mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 06:55:50 +00:00
Merge branch 'main' into next
This commit is contained in:
commit
f6db64af30
@ -7,7 +7,6 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import _ from 'lodash';
|
|
||||||
import React, { createContext, useCallback, useMemo } from 'react';
|
import React, { createContext, useCallback, useMemo } from 'react';
|
||||||
import { AssociationSelect } from './AssociationSelect';
|
import { AssociationSelect } from './AssociationSelect';
|
||||||
import { InternalFileManager } from './FileManager';
|
import { InternalFileManager } from './FileManager';
|
||||||
@ -29,7 +28,7 @@ export enum AssociationFieldMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface AssociationFieldModeProviderProps {
|
interface AssociationFieldModeProviderProps {
|
||||||
modeToComponent: Partial<Record<AssociationFieldMode, React.FC | ((originalCom: React.FC) => React.FC)>>;
|
modeToComponent: Partial<Record<AssociationFieldMode, React.FC>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultModeToComponent = {
|
const defaultModeToComponent = {
|
||||||
@ -45,11 +44,15 @@ const defaultModeToComponent = {
|
|||||||
const AssociationFieldModeContext = createContext<{
|
const AssociationFieldModeContext = createContext<{
|
||||||
modeToComponent: AssociationFieldModeProviderProps['modeToComponent'];
|
modeToComponent: AssociationFieldModeProviderProps['modeToComponent'];
|
||||||
getComponent: (mode: AssociationFieldMode) => React.FC;
|
getComponent: (mode: AssociationFieldMode) => React.FC;
|
||||||
|
getDefaultComponent: (mode: AssociationFieldMode) => React.FC;
|
||||||
}>({
|
}>({
|
||||||
modeToComponent: defaultModeToComponent,
|
modeToComponent: defaultModeToComponent,
|
||||||
getComponent: (mode: AssociationFieldMode) => {
|
getComponent: (mode: AssociationFieldMode) => {
|
||||||
return defaultModeToComponent[mode];
|
return defaultModeToComponent[mode];
|
||||||
},
|
},
|
||||||
|
getDefaultComponent: (mode: AssociationFieldMode) => {
|
||||||
|
return defaultModeToComponent[mode];
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AssociationFieldModeProvider: React.FC<AssociationFieldModeProviderProps> = (props) => {
|
export const AssociationFieldModeProvider: React.FC<AssociationFieldModeProviderProps> = (props) => {
|
||||||
@ -61,18 +64,19 @@ export const AssociationFieldModeProvider: React.FC<AssociationFieldModeProvider
|
|||||||
|
|
||||||
const getComponent = useCallback(
|
const getComponent = useCallback(
|
||||||
(mode: AssociationFieldMode) => {
|
(mode: AssociationFieldMode) => {
|
||||||
const component = modeToComponent[mode];
|
return modeToComponent[mode] || defaultModeToComponent[mode];
|
||||||
|
|
||||||
if (_.isFunction(component)) {
|
|
||||||
return component(defaultModeToComponent[mode]) as React.FC;
|
|
||||||
}
|
|
||||||
|
|
||||||
return component || defaultModeToComponent[mode];
|
|
||||||
},
|
},
|
||||||
[modeToComponent],
|
[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 <AssociationFieldModeContext.Provider value={value}>{props.children}</AssociationFieldModeContext.Provider>;
|
return <AssociationFieldModeContext.Provider value={value}>{props.children}</AssociationFieldModeContext.Provider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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: () => <div>Association Select</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../InternalPicker', () => ({
|
||||||
|
InternalPicker: () => <div>Internal Picker</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('AssociationFieldModeProvider', () => {
|
||||||
|
it('should correctly provide the default modeToComponent mapping', () => {
|
||||||
|
const TestComponent = () => {
|
||||||
|
const { modeToComponent } = useAssociationFieldModeContext();
|
||||||
|
return <div>{Object.keys(modeToComponent).join(',')} </div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<AssociationFieldModeProvider modeToComponent={{}}>
|
||||||
|
<TestComponent />
|
||||||
|
</AssociationFieldModeProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Picker,Nester,PopoverNester,Select,SubTable,FileManager,CascadeSelect')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow overriding the default modeToComponent mapping', () => {
|
||||||
|
const CustomComponent = () => <div>Custom Component</div>;
|
||||||
|
const TestComponent = () => {
|
||||||
|
const { getComponent } = useAssociationFieldModeContext();
|
||||||
|
const Component = getComponent(AssociationFieldMode.Picker);
|
||||||
|
return <Component />;
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<AssociationFieldModeProvider modeToComponent={{ [AssociationFieldMode.Picker]: CustomComponent }}>
|
||||||
|
<TestComponent />
|
||||||
|
</AssociationFieldModeProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
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 <Component />;
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<AssociationFieldModeProvider modeToComponent={{}}>
|
||||||
|
<TestComponent />
|
||||||
|
</AssociationFieldModeProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Association Select')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getDefaultComponent should always return the default component', () => {
|
||||||
|
const CustomComponent = () => <div>Custom Component</div>;
|
||||||
|
const TestComponent = () => {
|
||||||
|
const { getDefaultComponent } = useAssociationFieldModeContext();
|
||||||
|
const Component = getDefaultComponent(AssociationFieldMode.Picker);
|
||||||
|
return <Component />;
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<AssociationFieldModeProvider modeToComponent={{ [AssociationFieldMode.Picker]: CustomComponent }}>
|
||||||
|
<TestComponent />
|
||||||
|
</AssociationFieldModeProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Internal Picker')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -15,7 +15,11 @@ import { Nester } from './Nester';
|
|||||||
import { ReadPretty } from './ReadPretty';
|
import { ReadPretty } from './ReadPretty';
|
||||||
import { SubTable } from './SubTable';
|
import { SubTable } from './SubTable';
|
||||||
|
|
||||||
export { AssociationFieldModeProvider } from './AssociationFieldModeProvider';
|
export {
|
||||||
|
AssociationFieldMode,
|
||||||
|
AssociationFieldModeProvider,
|
||||||
|
useAssociationFieldModeContext,
|
||||||
|
} from './AssociationFieldModeProvider';
|
||||||
export const AssociationField: any = connect(Editable, mapReadPretty(ReadPretty));
|
export const AssociationField: any = connect(Editable, mapReadPretty(ReadPretty));
|
||||||
|
|
||||||
AssociationField.SubTable = SubTable;
|
AssociationField.SubTable = SubTable;
|
||||||
|
@ -11,16 +11,17 @@ import {
|
|||||||
Action,
|
Action,
|
||||||
AdminProvider,
|
AdminProvider,
|
||||||
AntdAppProvider,
|
AntdAppProvider,
|
||||||
|
AssociationFieldMode,
|
||||||
AssociationFieldModeProvider,
|
AssociationFieldModeProvider,
|
||||||
BlockTemplateProvider,
|
BlockTemplateProvider,
|
||||||
GlobalThemeProvider,
|
GlobalThemeProvider,
|
||||||
OpenModeProvider,
|
OpenModeProvider,
|
||||||
|
useAssociationFieldModeContext,
|
||||||
usePlugin,
|
usePlugin,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { isDesktop } from 'react-device-detect';
|
import { isDesktop } from 'react-device-detect';
|
||||||
|
|
||||||
import _ from 'lodash';
|
|
||||||
import { ActionDrawerUsedInMobile, useToAdaptActionDrawerToMobile } from '../adaptor-of-desktop/ActionDrawer';
|
import { ActionDrawerUsedInMobile, useToAdaptActionDrawerToMobile } from '../adaptor-of-desktop/ActionDrawer';
|
||||||
import { BasicZIndexProvider } from '../adaptor-of-desktop/BasicZIndexProvider';
|
import { BasicZIndexProvider } from '../adaptor-of-desktop/BasicZIndexProvider';
|
||||||
import { useToAdaptFilterActionToMobile } from '../adaptor-of-desktop/FilterAction';
|
import { useToAdaptFilterActionToMobile } from '../adaptor-of-desktop/FilterAction';
|
||||||
@ -67,9 +68,11 @@ export const Mobile = () => {
|
|||||||
const DesktopComponent = mobilePlugin.desktopMode === false ? React.Fragment : DesktopMode;
|
const DesktopComponent = mobilePlugin.desktopMode === false ? React.Fragment : DesktopMode;
|
||||||
const modeToComponent = React.useMemo(() => {
|
const modeToComponent = React.useMemo(() => {
|
||||||
return {
|
return {
|
||||||
PopoverNester: _.memoize((OriginComponent) => (props) => (
|
PopoverNester: (props) => {
|
||||||
<InternalPopoverNesterUsedInMobile {...props} OriginComponent={OriginComponent} />
|
const { getDefaultComponent } = useAssociationFieldModeContext();
|
||||||
)),
|
const OriginComponent = getDefaultComponent(AssociationFieldMode.PopoverNester);
|
||||||
|
return <InternalPopoverNesterUsedInMobile {...props} OriginComponent={OriginComponent} />;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user