Skip to content

Commit 62e2304

Browse files
ojj1123jungpaeng
andauthored
feat: enhance overlay async controller with reject handler (#199)
* feat: enhance overlay async controller with reject handler * test: add tests for reject handling in overlay.openAsync * Create loud-houses-hear.md --------- Co-authored-by: Yongbeen Im <[email protected]> Co-authored-by: Yongbeen Im <[email protected]>
1 parent 697f389 commit 62e2304

File tree

4 files changed

+86
-5
lines changed

4 files changed

+86
-5
lines changed

.changeset/loud-houses-hear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"overlay-kit": minor
3+
---
4+
5+
feat: enhance overlay async controller with reject handler

packages/src/context/provider/content-overlay-controller.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import { type FC, type ActionDispatch, memo, useEffect } from 'react';
22
import { type OverlayReducerAction } from '../reducer';
33

4-
type OverlayControllerProps = {
4+
export type OverlayControllerProps = {
55
overlayId: string;
66
isOpen: boolean;
77
close: () => void;
88
unmount: () => void;
99
};
1010

11-
type OverlayAsyncControllerProps<T> = Omit<OverlayControllerProps, 'close'> & {
11+
export type OverlayAsyncControllerProps<T> = Omit<OverlayControllerProps, 'close'> & {
1212
close: (param: T) => void;
13+
reject: (reason?: unknown) => void;
1314
};
1415

1516
export type OverlayControllerComponent = FC<OverlayControllerProps>;

packages/src/event.test.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,71 @@ describe('overlay object', () => {
134134
});
135135
});
136136

137+
it('The reason passed as an argument to reject is passed to reject. overlay.openAsync', async () => {
138+
const overlayDialogContent = 'context-modal-dialog-content';
139+
const overlayTriggerContent = 'context-modal-overlay-trigger-content';
140+
const rejectedReason = 'rejected';
141+
const mockFn = vi.fn();
142+
143+
function Component() {
144+
return (
145+
<button
146+
onClick={async () => {
147+
try {
148+
await overlay.openAsync<boolean>(
149+
({ isOpen, reject }) =>
150+
isOpen && <button onClick={() => reject(rejectedReason)}>{overlayDialogContent}</button>
151+
);
152+
} catch (error) {
153+
mockFn(error);
154+
}
155+
}}
156+
>
157+
{overlayTriggerContent}
158+
</button>
159+
);
160+
}
161+
162+
const { user } = renderWithUser(<Component />);
163+
await user.click(await screen.findByRole('button', { name: overlayTriggerContent }));
164+
await user.click(await screen.findByRole('button', { name: overlayDialogContent }));
165+
166+
await waitFor(() => {
167+
expect(mockFn).toHaveBeenCalledWith(rejectedReason);
168+
});
169+
});
170+
171+
it('should be able to turn off overlay through reject overlay.openAsync', async () => {
172+
const overlayTriggerContent = 'context-modal-test-content';
173+
const overlayDialogContent = 'context-modal-dialog-content';
174+
175+
function Component() {
176+
return (
177+
<button
178+
onClick={async () => {
179+
try {
180+
await overlay.openAsync<boolean>(
181+
({ isOpen, reject }) =>
182+
isOpen && <button onClick={() => reject('rejected')}>{overlayDialogContent}</button>
183+
);
184+
} catch (error) {
185+
//
186+
}
187+
}}
188+
>
189+
{overlayTriggerContent}
190+
</button>
191+
);
192+
}
193+
194+
const { user } = renderWithUser(<Component />, { wrapper });
195+
await user.click(await screen.findByRole('button', { name: overlayTriggerContent }));
196+
await user.click(await screen.findByRole('button', { name: overlayDialogContent }));
197+
198+
await waitFor(() => {
199+
expect(screen.queryByRole('button', { name: overlayDialogContent })).not.toBeInTheDocument();
200+
});
201+
});
137202
it('should handle current overlay correctly when unmounting overlays in different orders', async () => {
138203
const contents = {
139204
first: 'overlay-content-1',

packages/src/event.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
type OverlayAsyncControllerProps,
23
type OverlayAsyncControllerComponent,
34
type OverlayControllerComponent,
45
} from './context/provider/content-overlay-controller';
@@ -30,19 +31,28 @@ export function createOverlay(overlayId: string) {
3031
};
3132

3233
const openAsync = async <T>(controller: OverlayAsyncControllerComponent<T>, options?: OpenOverlayOptions) => {
33-
return new Promise<T>((resolve) => {
34+
return new Promise<T>((_resolve, _reject) => {
3435
open((overlayProps, ...deprecatedLegacyContext) => {
3536
/**
3637
* @description close the overlay with resolve
3738
*/
3839
const close = (param: T) => {
39-
resolve(param);
40+
_resolve(param);
4041
overlayProps.close();
4142
};
43+
44+
/**
45+
* @description close the overlay with reject
46+
*/
47+
const reject = (reason?: unknown) => {
48+
_reject(reason);
49+
overlayProps.close();
50+
};
51+
4252
/**
4353
* @description Passing overridden methods
4454
*/
45-
const props = { ...overlayProps, close };
55+
const props: OverlayAsyncControllerProps<T> = { ...overlayProps, close, reject };
4656
return controller(props, ...deprecatedLegacyContext);
4757
}, options);
4858
});

0 commit comments

Comments
 (0)