From e6a95b3b9189753f2c95df2565fce6bc865d40ab Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Fri, 11 Oct 2024 07:13:47 +0800 Subject: [PATCH] fix(mobile): fix file field rendering error issue (#5387) * fix(mobile): fix file field rendering error issue * test(AssociationFieldModeProvider): add unit tests --- .../AssociationFieldModeProvider.tsx | 24 +++-- .../AssociationFieldModeProvider.test.tsx | 92 +++++++++++++++++++ .../antd/association-field/index.ts | 6 +- .../src/client/mobile/Mobile.tsx | 11 ++- 4 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 packages/core/client/src/schema-component/antd/association-field/__tests__/AssociationFieldModeProvider.test.tsx diff --git a/packages/core/client/src/schema-component/antd/association-field/AssociationFieldModeProvider.tsx b/packages/core/client/src/schema-component/antd/association-field/AssociationFieldModeProvider.tsx index 59cae720f0..b45faa3a0f 100644 --- a/packages/core/client/src/schema-component/antd/association-field/AssociationFieldModeProvider.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/AssociationFieldModeProvider.tsx @@ -7,7 +7,6 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import _ from 'lodash'; import React, { createContext, useCallback, useMemo } from 'react'; import { AssociationSelect } from './AssociationSelect'; import { InternalFileManager } from './FileManager'; @@ -29,7 +28,7 @@ export enum AssociationFieldMode { } interface AssociationFieldModeProviderProps { - modeToComponent: Partial React.FC)>>; + modeToComponent: Partial>; } const defaultModeToComponent = { @@ -45,11 +44,15 @@ const defaultModeToComponent = { const AssociationFieldModeContext = createContext<{ modeToComponent: AssociationFieldModeProviderProps['modeToComponent']; getComponent: (mode: AssociationFieldMode) => React.FC; + getDefaultComponent: (mode: AssociationFieldMode) => React.FC; }>({ modeToComponent: defaultModeToComponent, getComponent: (mode: AssociationFieldMode) => { return defaultModeToComponent[mode]; }, + getDefaultComponent: (mode: AssociationFieldMode) => { + return defaultModeToComponent[mode]; + }, }); export const AssociationFieldModeProvider: React.FC = (props) => { @@ -61,18 +64,19 @@ export const AssociationFieldModeProvider: React.FC { - const component = modeToComponent[mode]; - - if (_.isFunction(component)) { - return component(defaultModeToComponent[mode]) as React.FC; - } - - return component || defaultModeToComponent[mode]; + return modeToComponent[mode] || defaultModeToComponent[mode]; }, [modeToComponent], ); - const value = useMemo(() => ({ modeToComponent, getComponent }), [getComponent, modeToComponent]); + const getDefaultComponent = useCallback((mode: AssociationFieldMode) => { + return defaultModeToComponent[mode]; + }, []); + + const value = useMemo( + () => ({ modeToComponent, getComponent, getDefaultComponent }), + [getComponent, modeToComponent, getDefaultComponent], + ); return {props.children}; }; diff --git a/packages/core/client/src/schema-component/antd/association-field/__tests__/AssociationFieldModeProvider.test.tsx b/packages/core/client/src/schema-component/antd/association-field/__tests__/AssociationFieldModeProvider.test.tsx new file mode 100644 index 0000000000..1a46e0bcbc --- /dev/null +++ b/packages/core/client/src/schema-component/antd/association-field/__tests__/AssociationFieldModeProvider.test.tsx @@ -0,0 +1,92 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; +import { describe, expect, it, vi } from 'vitest'; +import { + AssociationFieldMode, + AssociationFieldModeProvider, + useAssociationFieldModeContext, +} from '../AssociationFieldModeProvider'; + +vi.mock('../AssociationSelect', () => ({ + AssociationSelect: () =>
Association Select
, +})); + +vi.mock('../InternalPicker', () => ({ + InternalPicker: () =>
Internal Picker
, +})); + +describe('AssociationFieldModeProvider', () => { + it('should correctly provide the default modeToComponent mapping', () => { + const TestComponent = () => { + const { modeToComponent } = useAssociationFieldModeContext(); + return
{Object.keys(modeToComponent).join(',')}
; + }; + + render( + + + , + ); + + expect(screen.getByText('Picker,Nester,PopoverNester,Select,SubTable,FileManager,CascadeSelect')).toBeTruthy(); + }); + + it('should allow overriding the default modeToComponent mapping', () => { + const CustomComponent = () =>
Custom Component
; + const TestComponent = () => { + const { getComponent } = useAssociationFieldModeContext(); + const Component = getComponent(AssociationFieldMode.Picker); + return ; + }; + + render( + + + , + ); + + expect(screen.getByText('Custom Component')).toBeTruthy(); + }); + + it('getComponent should return the default component if no custom component is found', () => { + const TestComponent = () => { + const { getComponent } = useAssociationFieldModeContext(); + const Component = getComponent(AssociationFieldMode.Select); + return ; + }; + + render( + + + , + ); + + expect(screen.getByText('Association Select')).toBeTruthy(); + }); + + it('getDefaultComponent should always return the default component', () => { + const CustomComponent = () =>
Custom Component
; + const TestComponent = () => { + const { getDefaultComponent } = useAssociationFieldModeContext(); + const Component = getDefaultComponent(AssociationFieldMode.Picker); + return ; + }; + + render( + + + , + ); + + expect(screen.getByText('Internal Picker')).toBeTruthy(); + }); +}); diff --git a/packages/core/client/src/schema-component/antd/association-field/index.ts b/packages/core/client/src/schema-component/antd/association-field/index.ts index 427391c679..91c922cd73 100644 --- a/packages/core/client/src/schema-component/antd/association-field/index.ts +++ b/packages/core/client/src/schema-component/antd/association-field/index.ts @@ -15,7 +15,11 @@ import { Nester } from './Nester'; import { ReadPretty } from './ReadPretty'; import { SubTable } from './SubTable'; -export { AssociationFieldModeProvider } from './AssociationFieldModeProvider'; +export { + AssociationFieldMode, + AssociationFieldModeProvider, + useAssociationFieldModeContext, +} from './AssociationFieldModeProvider'; export const AssociationField: any = connect(Editable, mapReadPretty(ReadPretty)); AssociationField.SubTable = SubTable; diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/mobile/Mobile.tsx b/packages/plugins/@nocobase/plugin-mobile/src/client/mobile/Mobile.tsx index 762da0a527..45506f07d6 100644 --- a/packages/plugins/@nocobase/plugin-mobile/src/client/mobile/Mobile.tsx +++ b/packages/plugins/@nocobase/plugin-mobile/src/client/mobile/Mobile.tsx @@ -11,16 +11,17 @@ import { Action, AdminProvider, AntdAppProvider, + AssociationFieldMode, AssociationFieldModeProvider, BlockTemplateProvider, GlobalThemeProvider, OpenModeProvider, + useAssociationFieldModeContext, usePlugin, } from '@nocobase/client'; import React from 'react'; import { isDesktop } from 'react-device-detect'; -import _ from 'lodash'; import { ActionDrawerUsedInMobile, useToAdaptActionDrawerToMobile } from '../adaptor-of-desktop/ActionDrawer'; import { BasicZIndexProvider } from '../adaptor-of-desktop/BasicZIndexProvider'; import { useToAdaptFilterActionToMobile } from '../adaptor-of-desktop/FilterAction'; @@ -65,9 +66,11 @@ export const Mobile = () => { const DesktopComponent = mobilePlugin.desktopMode === false ? React.Fragment : DesktopMode; const modeToComponent = React.useMemo(() => { return { - PopoverNester: _.memoize((OriginComponent) => (props) => ( - - )), + PopoverNester: (props) => { + const { getDefaultComponent } = useAssociationFieldModeContext(); + const OriginComponent = getDefaultComponent(AssociationFieldMode.PopoverNester); + return ; + }, }; }, []);