diff --git a/package.json b/package.json index 31c133e..b839f41 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,9 @@ "lint": "aqu lint", "lint:fix": "aqu lint --fix", "test": "aqu test", - "test:watch": "npm test -- --watch", - "test:coverage": "npm test -- --coverage --json --silent --ci --outputFile=\"report.json\"", - "test:log-coverage": "npm test -- --coverage --silent --ci --coverageReporters=text", + "test:watch": "pnpm test -- --watch", + "test:coverage": "pnpm test -- --coverage --json --silent --ci --outputFile=\"report.json\"", + "test:log-coverage": "pnpm test -- --coverage --silent --ci --coverageReporters=text", "release": "np", "prepublishOnly": "aqu build" }, diff --git a/src/hooks/usePopup.ts b/src/hooks/usePopup.ts index 857be22..46fe0ed 100644 --- a/src/hooks/usePopup.ts +++ b/src/hooks/usePopup.ts @@ -1,4 +1,4 @@ -import { ComponentType, useCallback, useRef } from 'react'; +import { ComponentType, useCallback, useEffect, useRef } from 'react'; import { nanoid } from 'nanoid/non-secure'; import { useEvent } from './useEvent'; @@ -18,7 +18,7 @@ export const usePopup =
( props: Pick
, group: PopupGroup ): UsePopupBag
=> {
- const { mount, close: closePopup, unmount } = usePopupsContext();
+ const { mount, close: closePopup, unmount, update } = usePopupsContext();
const popupIdentifier = useRef (
closePopup(popupIdentifier.current);
}, [closePopup]);
+ useEffect(() => {
+ update(popupIdentifier.current, props);
+ }, [props, update]);
+
return [open, close];
};
diff --git a/src/hooks/usePopupsBag.ts b/src/hooks/usePopupsBag.ts
index 54508d2..6147f4a 100644
--- a/src/hooks/usePopupsBag.ts
+++ b/src/hooks/usePopupsBag.ts
@@ -36,6 +36,19 @@ export const usePopupsBag = (): PopupsBag => {
return popup.popupIdentifier;
}, []);
+ const update = useCallback(
+ (popupIdentifier: PopupIdentifier, props: object) => {
+ dispatch({
+ type: ActionType.UPDATE,
+ payload: {
+ popupIdentifier,
+ props,
+ },
+ });
+ },
+ []
+ );
+
const close = useCallback(
(popupIdentifier: PopupIdentifier) => {
const popup = getPopup(popupIdentifier);
@@ -47,6 +60,7 @@ export const usePopupsBag = (): PopupsBag => {
return {
mount,
unmount,
+ update,
getPopup,
close,
popupsState,
diff --git a/src/types/PopupsBag.ts b/src/types/PopupsBag.ts
index 50e9341..d822a5b 100644
--- a/src/types/PopupsBag.ts
+++ b/src/types/PopupsBag.ts
@@ -5,6 +5,7 @@ import { PopupsState } from '../utils/popupsReducer';
export type PopupsBag = {
mount: (popup: Popup ) => PopupIdentifier;
unmount: (popupIdentifier: PopupIdentifier) => void;
+ update: (popupIdentifier: PopupIdentifier, props: object) => void;
close: (popupIdentifier: PopupIdentifier) => void;
popupsState: PopupsState;
diff --git a/src/utils/popupsReducer.ts b/src/utils/popupsReducer.ts
index 4f1d285..1458c37 100644
--- a/src/utils/popupsReducer.ts
+++ b/src/utils/popupsReducer.ts
@@ -5,6 +5,7 @@ import { PopupsRegistry } from '../types/PopupsRegistry';
export enum ActionType {
MOUNT,
UNMOUNT,
+ UPDATE,
}
type MountAction = {
@@ -21,14 +22,21 @@ type UnmountAction = {
};
};
-export type PopupsAction = MountAction | UnmountAction;
+type UpdateAction = {
+ type: ActionType.UPDATE;
+ payload: {
+ popupIdentifier: PopupIdentifier;
+ props: object;
+ };
+};
+
+export type PopupsAction = MountAction | UnmountAction | UpdateAction;
export type PopupsState = { popups: PopupsRegistry };
-export const popupsReducer = (
- { popups }: PopupsState,
- action: PopupsAction
-) => {
+export const popupsReducer = (prevState: PopupsState, action: PopupsAction) => {
+ const { popups } = prevState;
+
switch (action.type) {
case ActionType.MOUNT: {
const { popup } = action.payload;
@@ -47,6 +55,22 @@ export const popupsReducer = (
};
}
+ case ActionType.UPDATE: {
+ const { popupIdentifier, props } = action.payload;
+
+ const { groupId, id } = popupIdentifier;
+
+ if (!popups[groupId]?.[id]) {
+ return prevState;
+ }
+
+ popups[groupId][id].props = props;
+
+ return {
+ popups,
+ };
+ }
+
case ActionType.UNMOUNT: {
const { groupId, id } = action.payload.popupIdentifier;
diff --git a/test/hooks/usePopup.test.tsx b/test/hooks/usePopup.test.tsx
index 74c1cd2..09d356d 100644
--- a/test/hooks/usePopup.test.tsx
+++ b/test/hooks/usePopup.test.tsx
@@ -24,8 +24,9 @@ const PopupComponentWithProps: React.FC<{
describe('usePopup', () => {
it('should render only one popup', () => {
+ const initialProps = {};
const { result } = renderHook(
- () => usePopup(SimplePopupComponent, {}, group),
+ () => usePopup(SimplePopupComponent, initialProps, group),
{
wrapper: TestHookWrapper,
}
@@ -45,8 +46,9 @@ describe('usePopup', () => {
});
it('should close popup', () => {
+ const initialProps = {};
const { result } = renderHook(
- () => usePopup(SimplePopupComponent, {}, group),
+ () => usePopup(SimplePopupComponent, initialProps, group),
{
wrapper: TestHookWrapper,
}
@@ -65,12 +67,13 @@ describe('usePopup', () => {
expect(() => screen.getByText('simple popup')).toThrow();
});
- it('should update popup', () => {
+ it('should reopen popup with new props', () => {
const initialMessage = 'initial message';
const updatedMessage = 'updated message';
+ const initialProps = {};
const { result } = renderHook(
- () => usePopup(CustomizablePopupComponent, {}, group),
+ () => usePopup(CustomizablePopupComponent, initialProps, group),
{ wrapper: TestHookWrapper }
);
@@ -90,8 +93,9 @@ describe('usePopup', () => {
});
it('should merge props', () => {
+ const initialProps = { prop1: 42 };
const { result } = renderHook(
- () => usePopup(PopupComponentWithProps, { prop1: 42 }, group),
+ () => usePopup(PopupComponentWithProps, initialProps, group),
{
wrapper: TestHookWrapper,
}
@@ -108,4 +112,29 @@ describe('usePopup', () => {
prop2: 'hello',
});
});
+
+ it('should update popup when props changing', () => {
+ const { result, rerender } = renderHook(
+ (props: { message: string }) =>
+ usePopup(CustomizablePopupComponent, props, group),
+ {
+ wrapper: TestHookWrapper,
+ initialProps: {
+ message: 'initial',
+ },
+ }
+ );
+
+ act(() => {
+ result.current[0]();
+ });
+
+ expect(screen.getByText('initial')).toBeDefined();
+
+ rerender({
+ message: 'updated',
+ });
+
+ expect(screen.getByText('updated')).toBeDefined();
+ });
});