Refactor dialog old component states (#13186)
This PR refactors dialog old component state management.
This commit is contained in:
@ -51,7 +51,7 @@ export const AppRouterProviders = () => {
|
||||
<UserThemeProviderEffect />
|
||||
<SnackBarProvider>
|
||||
<ErrorMessageEffect />
|
||||
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
||||
<DialogManagerScope dialogComponentInstanceId="dialog-manager">
|
||||
<DialogManager>
|
||||
<StrictMode>
|
||||
<PromiseRejectionEffect />
|
||||
|
||||
@ -62,7 +62,7 @@ const mockData = [
|
||||
];
|
||||
|
||||
export const Default = () => (
|
||||
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
||||
<DialogManagerScope dialogComponentInstanceId="dialog-manager">
|
||||
<ReactSpreadsheetImportContextProvider values={mockRsiValues}>
|
||||
<SpreadSheetImportModalWrapper
|
||||
modalId="match-columns-step"
|
||||
|
||||
@ -40,7 +40,7 @@ const meta: Meta<typeof SelectHeaderStep> = {
|
||||
|
||||
export default meta;
|
||||
export const Default = () => (
|
||||
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
||||
<DialogManagerScope dialogComponentInstanceId="dialog-manager">
|
||||
<ReactSpreadsheetImportContextProvider values={mockRsiValues}>
|
||||
<SpreadSheetImportModalWrapper
|
||||
modalId="select-header-step"
|
||||
|
||||
@ -40,7 +40,7 @@ export default meta;
|
||||
const sheetNames = ['Sheet1', 'Sheet2', 'Sheet3'];
|
||||
|
||||
export const Default = () => (
|
||||
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
||||
<DialogManagerScope dialogComponentInstanceId="dialog-manager">
|
||||
<ReactSpreadsheetImportContextProvider values={mockRsiValues}>
|
||||
<SpreadSheetImportModalWrapper
|
||||
modalId="select-sheet-step"
|
||||
|
||||
@ -44,7 +44,7 @@ const meta: Meta<typeof UploadStep> = {
|
||||
export default meta;
|
||||
|
||||
export const Default = () => (
|
||||
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
||||
<DialogManagerScope dialogComponentInstanceId="dialog-manager">
|
||||
<ReactSpreadsheetImportContextProvider values={mockRsiValues}>
|
||||
<SpreadSheetImportModalWrapper modalId="upload-step" onClose={() => null}>
|
||||
<UploadStep
|
||||
|
||||
@ -43,7 +43,7 @@ export default meta;
|
||||
const file = new File([''], 'file.csv');
|
||||
|
||||
export const Default = () => (
|
||||
<DialogManagerScope dialogManagerScopeId="dialog-manager">
|
||||
<DialogManagerScope dialogComponentInstanceId="dialog-manager">
|
||||
<ReactSpreadsheetImportContextProvider values={mockRsiValues}>
|
||||
<SpreadSheetImportModalWrapper
|
||||
modalId="validation-step"
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
import { useDialogManagerScopedStates } from '../hooks/internal/useDialogManagerScopedStates';
|
||||
import { dialogInternalComponentState } from '@/ui/feedback/dialog-manager/states/dialogInternalComponentState';
|
||||
import { useDialogManager } from '../hooks/useDialogManager';
|
||||
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { Dialog } from './Dialog';
|
||||
import { DialogManagerEffect } from './DialogManagerEffect';
|
||||
|
||||
export const DialogManager = ({ children }: React.PropsWithChildren) => {
|
||||
const { dialogInternal } = useDialogManagerScopedStates();
|
||||
const dialogInternal = useRecoilComponentValueV2(
|
||||
dialogInternalComponentState,
|
||||
);
|
||||
const { closeDialog } = useDialogManager();
|
||||
|
||||
return (
|
||||
|
||||
@ -3,10 +3,14 @@ import { useEffect } from 'react';
|
||||
import { DIALOG_FOCUS_ID } from '@/ui/feedback/dialog-manager/constants/DialogFocusId';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { useDialogManagerScopedStates } from '../hooks/internal/useDialogManagerScopedStates';
|
||||
|
||||
import { dialogInternalComponentState } from '@/ui/feedback/dialog-manager/states/dialogInternalComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const DialogManagerEffect = () => {
|
||||
const { dialogInternal } = useDialogManagerScopedStates();
|
||||
const dialogInternal = useRecoilComponentValueV2(
|
||||
dialogInternalComponentState,
|
||||
);
|
||||
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||
|
||||
export const DialogComponentInstanceContext = createComponentInstanceContext();
|
||||
@ -2,10 +2,11 @@ import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { useDialogManagerScopedStates } from '@/ui/feedback/dialog-manager/hooks/internal/useDialogManagerScopedStates';
|
||||
import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager';
|
||||
import { DialogManagerScope } from '@/ui/feedback/dialog-manager/scopes/DialogManagerScope';
|
||||
import { dialogInternalComponentState } from '@/ui/feedback/dialog-manager/states/dialogInternalComponentState';
|
||||
import { DialogOptions } from '@/ui/feedback/dialog-manager/types/DialogOptions';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
const mockedUuid = 'mocked-uuid';
|
||||
jest.mock('uuid');
|
||||
@ -15,7 +16,7 @@ jest.mock('uuid');
|
||||
const dialogManagerScopeId = 'dialog-manager';
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<DialogManagerScope dialogManagerScopeId={dialogManagerScopeId}>
|
||||
<DialogManagerScope dialogComponentInstanceId={dialogManagerScopeId}>
|
||||
{children}
|
||||
</DialogManagerScope>
|
||||
</RecoilRoot>
|
||||
@ -74,12 +75,8 @@ const dialogOptionsArray: DialogOptionsArray = [
|
||||
const renderHooks = () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
dialogManager: useDialogManager({
|
||||
dialogManagerScopeId: dialogManagerScopeId,
|
||||
}),
|
||||
internalState: useDialogManagerScopedStates({
|
||||
dialogManagerScopeId,
|
||||
}),
|
||||
dialogManager: useDialogManager(),
|
||||
dialogInternal: useRecoilComponentValueV2(dialogInternalComponentState),
|
||||
}),
|
||||
renderHookConfig,
|
||||
);
|
||||
@ -107,7 +104,7 @@ describe('useDialogManager', () => {
|
||||
result.current.dialogManager.enqueueDialog(dialogOptionsArray[0]);
|
||||
});
|
||||
|
||||
const { dialogInternal } = result.current.internalState;
|
||||
const { dialogInternal } = result.current;
|
||||
|
||||
expect(dialogInternal.maxQueue).toEqual(2);
|
||||
expect(dialogInternal.queue).toHaveLength(1);
|
||||
@ -127,7 +124,7 @@ describe('useDialogManager', () => {
|
||||
result.current.dialogManager.enqueueDialog(dialogOptionsArray[1]);
|
||||
});
|
||||
|
||||
const { dialogInternal } = result.current.internalState;
|
||||
const { dialogInternal } = result.current;
|
||||
|
||||
expect(dialogInternal.maxQueue).toEqual(2);
|
||||
expect(dialogInternal.queue).toHaveLength(2);
|
||||
@ -148,7 +145,7 @@ describe('useDialogManager', () => {
|
||||
result.current.dialogManager.enqueueDialog(dialogOptionsArray[2]);
|
||||
});
|
||||
|
||||
const { dialogInternal } = result.current.internalState;
|
||||
const { dialogInternal } = result.current;
|
||||
|
||||
expect(dialogInternal.maxQueue).toEqual(2);
|
||||
expect(dialogInternal.queue).toHaveLength(2);
|
||||
@ -170,8 +167,7 @@ describe('useDialogManager', () => {
|
||||
dialogOptionsArray[1],
|
||||
]);
|
||||
|
||||
const { dialogInternal: stateAfterEnqueue } =
|
||||
result.current.internalState;
|
||||
const { dialogInternal: stateAfterEnqueue } = result.current;
|
||||
|
||||
expect(stateAfterEnqueue.maxQueue).toEqual(2);
|
||||
expect(stateAfterEnqueue.queue).toHaveLength(2);
|
||||
@ -186,7 +182,7 @@ describe('useDialogManager', () => {
|
||||
queue: [],
|
||||
};
|
||||
|
||||
const { dialogInternal: stateAfterClose } = result.current.internalState;
|
||||
const { dialogInternal: stateAfterClose } = result.current;
|
||||
|
||||
expect(stateAfterClose).toEqual(expectReturnWhenClose);
|
||||
expect(stateAfterClose.maxQueue).toEqual(2);
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useDialogManagerScopedStates } from '@/ui/feedback/dialog-manager/hooks/internal/useDialogManagerScopedStates';
|
||||
import { DialogManagerScope } from '@/ui/feedback/dialog-manager/scopes/DialogManagerScope';
|
||||
|
||||
const dialogManagerScopeId = 'dialog-manager';
|
||||
|
||||
const defaultReturnDialogState = { maxQueue: 2, queue: [] };
|
||||
|
||||
const updatedReturnDialogState = {
|
||||
maxQueue: 5,
|
||||
queue: [{ id: 'fakeId', title: 'testTitle', message: 'testMessage' }],
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<RecoilRoot>
|
||||
<DialogManagerScope dialogManagerScopeId={dialogManagerScopeId}>
|
||||
{children}
|
||||
</DialogManagerScope>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
|
||||
describe('useDialogManagerScopedStates', () => {
|
||||
it('Should return a dialog state and a function to update the state', async () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useDialogManagerScopedStates({
|
||||
dialogManagerScopeId,
|
||||
}),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.dialogInternal).toEqual(defaultReturnDialogState);
|
||||
expect(result.current.setDialogInternal).toBeInstanceOf(Function);
|
||||
|
||||
await act(async () => {
|
||||
result.current.setDialogInternal(updatedReturnDialogState);
|
||||
});
|
||||
|
||||
expect(result.current.dialogInternal).toEqual(updatedReturnDialogState);
|
||||
});
|
||||
});
|
||||
@ -1,25 +0,0 @@
|
||||
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
import { DialogManagerScopeInternalContext } from '../../scopes/scope-internal-context/DialogManagerScopeInternalContext';
|
||||
import { dialogInternalScopedState } from '../../states/dialogInternalScopedState';
|
||||
|
||||
type useDialogManagerScopedStatesProps = {
|
||||
dialogManagerScopeId?: string;
|
||||
};
|
||||
|
||||
export const useDialogManagerScopedStates = (
|
||||
props?: useDialogManagerScopedStatesProps,
|
||||
) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
DialogManagerScopeInternalContext,
|
||||
props?.dialogManagerScopeId,
|
||||
);
|
||||
|
||||
const [dialogInternal, setDialogInternal] = useRecoilScopedStateV2(
|
||||
dialogInternalScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
return { dialogInternal, setDialogInternal };
|
||||
};
|
||||
@ -1,22 +1,17 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
import { DIALOG_FOCUS_ID } from '@/ui/feedback/dialog-manager/constants/DialogFocusId';
|
||||
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
|
||||
import { DialogManagerScopeInternalContext } from '../scopes/scope-internal-context/DialogManagerScopeInternalContext';
|
||||
import { dialogInternalScopedState } from '../states/dialogInternalScopedState';
|
||||
|
||||
import { DialogComponentInstanceContext } from '@/ui/feedback/dialog-manager/contexts/DialogComponentInstanceContext';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { dialogInternalComponentState } from '../states/dialogInternalComponentState';
|
||||
import { DialogOptions } from '../types/DialogOptions';
|
||||
|
||||
type useDialogManagerProps = {
|
||||
dialogManagerScopeId?: string;
|
||||
};
|
||||
|
||||
export const useDialogManager = (props?: useDialogManagerProps) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
DialogManagerScopeInternalContext,
|
||||
props?.dialogManagerScopeId,
|
||||
export const useDialogManager = () => {
|
||||
const componentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
DialogComponentInstanceContext,
|
||||
);
|
||||
|
||||
const { removeFocusItemFromFocusStackById } =
|
||||
@ -25,33 +20,43 @@ export const useDialogManager = (props?: useDialogManagerProps) => {
|
||||
const closeDialog = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(id: string) => {
|
||||
set(dialogInternalScopedState({ scopeId: scopeId }), (prevState) => ({
|
||||
...prevState,
|
||||
queue: prevState.queue.filter((dialog) => dialog.id !== id),
|
||||
}));
|
||||
set(
|
||||
dialogInternalComponentState.atomFamily({
|
||||
instanceId: componentInstanceId,
|
||||
}),
|
||||
(prevState) => ({
|
||||
...prevState,
|
||||
queue: prevState.queue.filter((dialog) => dialog.id !== id),
|
||||
}),
|
||||
);
|
||||
|
||||
removeFocusItemFromFocusStackById({ focusId: DIALOG_FOCUS_ID });
|
||||
},
|
||||
[removeFocusItemFromFocusStackById, scopeId],
|
||||
[componentInstanceId, removeFocusItemFromFocusStackById],
|
||||
);
|
||||
|
||||
const setDialogQueue = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(newValue) =>
|
||||
set(dialogInternalScopedState({ scopeId: scopeId }), (prev) => {
|
||||
if (prev.queue.length >= prev.maxQueue) {
|
||||
set(
|
||||
dialogInternalComponentState.atomFamily({
|
||||
instanceId: componentInstanceId,
|
||||
}),
|
||||
(prev) => {
|
||||
if (prev.queue.length >= prev.maxQueue) {
|
||||
return {
|
||||
...prev,
|
||||
queue: [...prev.queue.slice(1), newValue] as DialogOptions[],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...prev,
|
||||
queue: [...prev.queue.slice(1), newValue] as DialogOptions[],
|
||||
queue: [...prev.queue, newValue] as DialogOptions[],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...prev,
|
||||
queue: [...prev.queue, newValue] as DialogOptions[],
|
||||
};
|
||||
}),
|
||||
[scopeId],
|
||||
},
|
||||
),
|
||||
[componentInstanceId],
|
||||
);
|
||||
|
||||
const enqueueDialog = (options?: Omit<DialogOptions, 'id'>) => {
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { DialogManagerScopeInternalContext } from './scope-internal-context/DialogManagerScopeInternalContext';
|
||||
import { DialogComponentInstanceContext } from '@/ui/feedback/dialog-manager/contexts/DialogComponentInstanceContext';
|
||||
|
||||
type DialogManagerScopeProps = {
|
||||
children: ReactNode;
|
||||
dialogManagerScopeId: string;
|
||||
dialogComponentInstanceId: string;
|
||||
};
|
||||
|
||||
export const DialogManagerScope = ({
|
||||
children,
|
||||
dialogManagerScopeId,
|
||||
dialogComponentInstanceId,
|
||||
}: DialogManagerScopeProps) => {
|
||||
return (
|
||||
<DialogManagerScopeInternalContext.Provider
|
||||
value={{ scopeId: dialogManagerScopeId }}
|
||||
<DialogComponentInstanceContext.Provider
|
||||
value={{ instanceId: dialogComponentInstanceId }}
|
||||
>
|
||||
{children}
|
||||
</DialogManagerScopeInternalContext.Provider>
|
||||
</DialogComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
|
||||
import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey';
|
||||
|
||||
type DialogManagerScopeInternalContextProps = RecoilComponentStateKey;
|
||||
|
||||
export const DialogManagerScopeInternalContext =
|
||||
createScopeInternalContext<DialogManagerScopeInternalContextProps>();
|
||||
@ -0,0 +1,19 @@
|
||||
import { DialogComponentInstanceContext } from '@/ui/feedback/dialog-manager/contexts/DialogComponentInstanceContext';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
||||
import { DialogOptions } from '../types/DialogOptions';
|
||||
|
||||
type DialogState = {
|
||||
maxQueue: number;
|
||||
queue: DialogOptions[];
|
||||
};
|
||||
|
||||
export const dialogInternalComponentState = createComponentStateV2<DialogState>(
|
||||
{
|
||||
key: 'dialogInternalComponentState',
|
||||
defaultValue: {
|
||||
maxQueue: 2,
|
||||
queue: [],
|
||||
},
|
||||
componentInstanceContext: DialogComponentInstanceContext,
|
||||
},
|
||||
);
|
||||
@ -1,16 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
import { DialogOptions } from '../types/DialogOptions';
|
||||
|
||||
type DialogState = {
|
||||
maxQueue: number;
|
||||
queue: DialogOptions[];
|
||||
};
|
||||
|
||||
export const dialogInternalScopedState = createComponentState<DialogState>({
|
||||
key: 'dialog/internal-state',
|
||||
defaultValue: {
|
||||
maxQueue: 2,
|
||||
queue: [],
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user