Skip to content

Commit 828fad5

Browse files
XionWCFMjungpaeng
andauthored
feat: Add overlayAsync implementation (#59)
* feat: Add overlayAsync implementation * chore: useless code remove * fix: openAsync close test falsy positive fix * Create polite-pans-stare.md * Update polite-pans-stare.md --------- Co-authored-by: Yongbeen Im <been.im@toss.im> Co-authored-by: Yongbeen Im <meis1541@naver.com>
1 parent b5a5d64 commit 828fad5

File tree

4 files changed

+106
-4
lines changed

4 files changed

+106
-4
lines changed

.changeset/polite-pans-stare.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"overlay-kit": minor
3+
---
4+
5+
feat: Add overlayAsync implementation
6+
7+
This change implements the openAsync method for overly-kit's promise support discussed in #47.
8+
9+
**Related Issue:** Fixes #47

packages/src/context/provider.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ type OverlayControllerProps = {
4141
unmount: () => void;
4242
};
4343

44+
type OverlayAsyncControllerProps<T> = Omit<OverlayControllerProps, 'close'> & {
45+
close: (param: T) => void;
46+
};
47+
4448
export type OverlayControllerComponent = FC<OverlayControllerProps>;
49+
export type OverlayAsyncControllerComponent<T> = FC<OverlayAsyncControllerProps<T>>;
4550

4651
type ContentOverlayControllerProps = {
4752
isOpen: boolean;

packages/src/event.test.tsx

+70-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { render, screen } from '@testing-library/react';
1+
import { render, screen, waitFor } from '@testing-library/react';
22
import { act, useEffect, type PropsWithChildren } from 'react';
3-
import { afterEach, describe, expect, it } from 'vitest';
3+
import { afterEach, describe, expect, it, vi } from 'vitest';
44
import { OverlayProvider } from './context/provider';
55
import { overlay } from './event';
66

@@ -74,4 +74,72 @@ describe('overlay object', () => {
7474
expect(screen.queryByText(testContent3)).toBeInTheDocument();
7575
expect(screen.queryByText(testContent4)).toBeInTheDocument();
7676
});
77+
78+
it('The value passed as an argument to close is passed to resolve. overlay.openAsync', async () => {
79+
const wrapper = ({ children }: PropsWithChildren) => <OverlayProvider>{children}</OverlayProvider>;
80+
const testContent = 'context-modal-test-content';
81+
const dialogContent = 'context-modal-dialog-content';
82+
const mockFn = vi.fn();
83+
const Component = () => {
84+
const handleClick = async () => {
85+
const result = await overlay.openAsync<boolean>(({ close }) => (
86+
<button onClick={() => close(true)}>{dialogContent}</button>
87+
));
88+
89+
if (result) {
90+
mockFn(result);
91+
}
92+
};
93+
94+
return <button onClick={handleClick}>{testContent}</button>;
95+
};
96+
97+
const renderComponent = render(<Component />, { wrapper });
98+
const testContentElement = await renderComponent.findByText(testContent);
99+
100+
act(() => {
101+
testContentElement.click();
102+
});
103+
104+
const dialogContentElement = await renderComponent.findByText(dialogContent);
105+
106+
act(() => {
107+
dialogContentElement.click();
108+
});
109+
await waitFor(() => {
110+
expect(mockFn).toHaveBeenCalledWith(true);
111+
});
112+
});
113+
114+
it('should be able to turn off overlay through close overlay.openAsync', async () => {
115+
const wrapper = ({ children }: PropsWithChildren) => <OverlayProvider>{children}</OverlayProvider>;
116+
const testContent = 'context-modal-test-content';
117+
const dialogContent = 'context-modal-dialog-content';
118+
119+
const Component = () => {
120+
const handleClick = async () => {
121+
overlay.openAsync<boolean>(({ isOpen, close }) =>
122+
isOpen ? <button onClick={() => close(true)}>{dialogContent}</button> : null
123+
);
124+
};
125+
return <button onClick={handleClick}>{testContent}</button>;
126+
};
127+
128+
const renderComponent = render(<Component />, { wrapper });
129+
const testContentElement = await renderComponent.findByText(testContent);
130+
131+
act(() => {
132+
testContentElement.click();
133+
});
134+
135+
const dialogContentElement = await renderComponent.findByText(dialogContent);
136+
137+
act(() => {
138+
dialogContentElement.click();
139+
});
140+
141+
await waitFor(() => {
142+
expect(dialogContentElement).not.toBeInTheDocument();
143+
});
144+
});
77145
});

packages/src/event.ts

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type OverlayControllerComponent } from './context/provider';
1+
import { type OverlayAsyncControllerComponent, type OverlayControllerComponent } from './context/provider';
22
import { dispatchOverlay } from './context/store';
33
import { randomId } from './utils';
44

@@ -20,6 +20,26 @@ function open(controller: OverlayControllerComponent, options?: OpenOverlayOptio
2020

2121
return overlayId;
2222
}
23+
24+
async function openAsync<T>(controller: OverlayAsyncControllerComponent<T>, options?: OpenOverlayOptions) {
25+
return new Promise<T>((resolve) => {
26+
open((overlayProps, ...deprecatedLegacyContext) => {
27+
/**
28+
* @description close the overlay with resolve
29+
*/
30+
const close = (param: T) => {
31+
resolve(param as T);
32+
overlayProps.close();
33+
};
34+
/**
35+
* @description Passing overridden methods
36+
*/
37+
const props = { ...overlayProps, close };
38+
return controller(props, ...deprecatedLegacyContext);
39+
}, options);
40+
});
41+
}
42+
2343
function close(overlayId: string) {
2444
dispatchOverlay({ type: 'CLOSE', overlayId });
2545
}
@@ -33,4 +53,4 @@ function unmountAll() {
3353
dispatchOverlay({ type: 'REMOVE_ALL' });
3454
}
3555

36-
export const overlay = { open, close, unmount, closeAll, unmountAll };
56+
export const overlay = { open, close, unmount, closeAll, unmountAll, openAsync };

0 commit comments

Comments
 (0)