Skip to content

Commit e844d6e

Browse files
authored
Fix dialogs close (#4707)
1 parent 566b4f5 commit e844d6e

File tree

2 files changed

+71
-51
lines changed

2 files changed

+71
-51
lines changed

packages/toolpad-core/src/useDialogs/DialogsProvider.tsx

+46-51
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client';
22
import invariant from 'invariant';
33
import * as React from 'react';
4+
import useEventCallback from '@mui/utils/useEventCallback';
45
import { DialogsContext } from './DialogsContext';
56
import type { DialogComponent, OpenDialog, OpenDialogOptions } from './useDialogs';
67

@@ -37,62 +38,56 @@ function DialogsProvider(props: DialogProviderProps) {
3738
const keyPrefix = React.useId();
3839
const nextId = React.useRef(0);
3940

40-
const requestDialog = React.useCallback<OpenDialog>(
41-
function open<P, R>(
42-
Component: DialogComponent<P, R>,
43-
payload: P,
44-
options: OpenDialogOptions<R> = {},
45-
) {
46-
const { onClose = async () => {} } = options;
47-
let resolve: ((result: R) => void) | undefined;
48-
const promise = new Promise<R>((resolveImpl) => {
49-
resolve = resolveImpl;
50-
});
51-
invariant(resolve, 'resolve not set');
41+
const requestDialog = useEventCallback<OpenDialog>(function open<P, R>(
42+
Component: DialogComponent<P, R>,
43+
payload: P,
44+
options: OpenDialogOptions<R> = {},
45+
) {
46+
const { onClose = async () => {} } = options;
47+
let resolve: ((result: R) => void) | undefined;
48+
const promise = new Promise<R>((resolveImpl) => {
49+
resolve = resolveImpl;
50+
});
51+
invariant(resolve, 'resolve not set');
5252

53-
const key = `${keyPrefix}-${nextId.current}`;
54-
nextId.current += 1;
53+
const key = `${keyPrefix}-${nextId.current}`;
54+
nextId.current += 1;
5555

56-
const newEntry: DialogStackEntry<P, R> = {
57-
key,
58-
open: true,
59-
promise,
60-
Component,
61-
payload,
62-
onClose,
63-
resolve,
64-
};
56+
const newEntry: DialogStackEntry<P, R> = {
57+
key,
58+
open: true,
59+
promise,
60+
Component,
61+
payload,
62+
onClose,
63+
resolve,
64+
};
6565

66-
setStack((prevStack) => [...prevStack, newEntry]);
67-
return promise;
68-
},
69-
[keyPrefix],
70-
);
66+
setStack((prevStack) => [...prevStack, newEntry]);
67+
return promise;
68+
});
7169

72-
const closeDialogUi = React.useCallback(
73-
function closeDialogUi<R>(dialog: Promise<R>) {
74-
setStack((prevStack) =>
75-
prevStack.map((entry) => (entry.promise === dialog ? { ...entry, open: false } : entry)),
76-
);
77-
setTimeout(() => {
78-
// wait for closing animation
79-
setStack((prevStack) => prevStack.filter((entry) => entry.promise !== dialog));
80-
}, unmountAfter);
81-
},
82-
[unmountAfter],
83-
);
70+
const closeDialogUi = useEventCallback(function closeDialogUi<R>(dialog: Promise<R>) {
71+
setStack((prevStack) =>
72+
prevStack.map((entry) => (entry.promise === dialog ? { ...entry, open: false } : entry)),
73+
);
74+
setTimeout(() => {
75+
// wait for closing animation
76+
setStack((prevStack) => prevStack.filter((entry) => entry.promise !== dialog));
77+
}, unmountAfter);
78+
});
8479

85-
const closeDialog = React.useCallback(
86-
async function closeDialog<R>(dialog: Promise<R>, result: R) {
87-
const entryToClose = stack.find((entry) => entry.promise === dialog);
88-
invariant(entryToClose, 'dialog not found');
89-
await entryToClose.onClose(result);
90-
entryToClose.resolve(result);
91-
closeDialogUi(dialog);
92-
return dialog;
93-
},
94-
[stack, closeDialogUi],
95-
);
80+
const closeDialog = useEventCallback(async function closeDialog<R>(
81+
dialog: Promise<R>,
82+
result: R,
83+
) {
84+
const entryToClose = stack.find((entry) => entry.promise === dialog);
85+
invariant(entryToClose, 'dialog not found');
86+
await entryToClose.onClose(result);
87+
entryToClose.resolve(result);
88+
closeDialogUi(dialog);
89+
return dialog;
90+
});
9691

9792
const contextValue = React.useMemo(
9893
() => ({ open: requestDialog, close: closeDialog }),

packages/toolpad-core/src/useDialogs/useDialogs.test.tsx

+25
Original file line numberDiff line numberDiff line change
@@ -195,5 +195,30 @@ describe('useDialogs', () => {
195195

196196
expect(await dialogResult).toBe('I am result');
197197
});
198+
199+
test('can close imperatively', async () => {
200+
function CustomDialog({ open, onClose }: DialogProps) {
201+
return open ? (
202+
<div role="dialog">
203+
Hello <button onClick={() => onClose()}>Close me</button>
204+
</div>
205+
) : null;
206+
}
207+
const { result, rerender } = renderHook(() => useDialogs(), { wrapper: TestWrapper });
208+
209+
const theDialog = result.current.open(CustomDialog);
210+
211+
const dialog = await screen.findByRole('dialog');
212+
213+
rerender();
214+
215+
expect(within(dialog).getByText('Hello')).toBeTruthy();
216+
217+
await result.current.close(theDialog, null);
218+
219+
rerender();
220+
221+
expect(screen.queryByRole('dialog')).toBeFalsy();
222+
});
198223
});
199224
});

0 commit comments

Comments
 (0)