Skip to content

Commit

Permalink
feat: Add overlayAsync implementation (#59)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
Co-authored-by: Yongbeen Im <[email protected]>
  • Loading branch information
3 people authored Jul 15, 2024
1 parent b5a5d64 commit 828fad5
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 4 deletions.
9 changes: 9 additions & 0 deletions .changeset/polite-pans-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"overlay-kit": minor
---

feat: Add overlayAsync implementation

This change implements the openAsync method for overly-kit's promise support discussed in #47.

**Related Issue:** Fixes #47
5 changes: 5 additions & 0 deletions packages/src/context/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ type OverlayControllerProps = {
unmount: () => void;
};

type OverlayAsyncControllerProps<T> = Omit<OverlayControllerProps, 'close'> & {
close: (param: T) => void;
};

export type OverlayControllerComponent = FC<OverlayControllerProps>;
export type OverlayAsyncControllerComponent<T> = FC<OverlayAsyncControllerProps<T>>;

type ContentOverlayControllerProps = {
isOpen: boolean;
Expand Down
72 changes: 70 additions & 2 deletions packages/src/event.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { render, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import { act, useEffect, type PropsWithChildren } from 'react';
import { afterEach, describe, expect, it } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { OverlayProvider } from './context/provider';
import { overlay } from './event';

Expand Down Expand Up @@ -74,4 +74,72 @@ describe('overlay object', () => {
expect(screen.queryByText(testContent3)).toBeInTheDocument();
expect(screen.queryByText(testContent4)).toBeInTheDocument();
});

it('The value passed as an argument to close is passed to resolve. overlay.openAsync', async () => {
const wrapper = ({ children }: PropsWithChildren) => <OverlayProvider>{children}</OverlayProvider>;
const testContent = 'context-modal-test-content';
const dialogContent = 'context-modal-dialog-content';
const mockFn = vi.fn();
const Component = () => {
const handleClick = async () => {
const result = await overlay.openAsync<boolean>(({ close }) => (
<button onClick={() => close(true)}>{dialogContent}</button>
));

if (result) {
mockFn(result);
}
};

return <button onClick={handleClick}>{testContent}</button>;
};

const renderComponent = render(<Component />, { wrapper });
const testContentElement = await renderComponent.findByText(testContent);

act(() => {
testContentElement.click();
});

const dialogContentElement = await renderComponent.findByText(dialogContent);

act(() => {
dialogContentElement.click();
});
await waitFor(() => {
expect(mockFn).toHaveBeenCalledWith(true);
});
});

it('should be able to turn off overlay through close overlay.openAsync', async () => {
const wrapper = ({ children }: PropsWithChildren) => <OverlayProvider>{children}</OverlayProvider>;
const testContent = 'context-modal-test-content';
const dialogContent = 'context-modal-dialog-content';

const Component = () => {
const handleClick = async () => {
overlay.openAsync<boolean>(({ isOpen, close }) =>
isOpen ? <button onClick={() => close(true)}>{dialogContent}</button> : null
);
};
return <button onClick={handleClick}>{testContent}</button>;
};

const renderComponent = render(<Component />, { wrapper });
const testContentElement = await renderComponent.findByText(testContent);

act(() => {
testContentElement.click();
});

const dialogContentElement = await renderComponent.findByText(dialogContent);

act(() => {
dialogContentElement.click();
});

await waitFor(() => {
expect(dialogContentElement).not.toBeInTheDocument();
});
});
});
24 changes: 22 additions & 2 deletions packages/src/event.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type OverlayControllerComponent } from './context/provider';
import { type OverlayAsyncControllerComponent, type OverlayControllerComponent } from './context/provider';
import { dispatchOverlay } from './context/store';
import { randomId } from './utils';

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

return overlayId;
}

async function openAsync<T>(controller: OverlayAsyncControllerComponent<T>, options?: OpenOverlayOptions) {
return new Promise<T>((resolve) => {
open((overlayProps, ...deprecatedLegacyContext) => {
/**
* @description close the overlay with resolve
*/
const close = (param: T) => {
resolve(param as T);
overlayProps.close();
};
/**
* @description Passing overridden methods
*/
const props = { ...overlayProps, close };
return controller(props, ...deprecatedLegacyContext);
}, options);
});
}

function close(overlayId: string) {
dispatchOverlay({ type: 'CLOSE', overlayId });
}
Expand All @@ -33,4 +53,4 @@ function unmountAll() {
dispatchOverlay({ type: 'REMOVE_ALL' });
}

export const overlay = { open, close, unmount, closeAll, unmountAll };
export const overlay = { open, close, unmount, closeAll, unmountAll, openAsync };

0 comments on commit 828fad5

Please sign in to comment.