Skip to content

Commit

Permalink
Update popup when props change in usePopup (#85)
Browse files Browse the repository at this point in the history
* Update popup when props change in `usePopup`

* Fixed eslint errors
  • Loading branch information
AlexShukel authored Nov 2, 2023
1 parent e7e08cf commit ba3eb4c
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 15 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
8 changes: 6 additions & 2 deletions src/hooks/usePopup.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -18,7 +18,7 @@ export const usePopup = <P, K extends keyof P>(
props: Pick<P, K>,
group: PopupGroup
): UsePopupBag<P, K> => {
const { mount, close: closePopup, unmount } = usePopupsContext();
const { mount, close: closePopup, unmount, update } = usePopupsContext();

const popupIdentifier = useRef<PopupIdentifier>({
id: nanoid(),
Expand Down Expand Up @@ -46,5 +46,9 @@ export const usePopup = <P, K extends keyof P>(
closePopup(popupIdentifier.current);
}, [closePopup]);

useEffect(() => {
update(popupIdentifier.current, props);
}, [props, update]);

return [open, close];
};
14 changes: 14 additions & 0 deletions src/hooks/usePopupsBag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -47,6 +60,7 @@ export const usePopupsBag = (): PopupsBag => {
return {
mount,
unmount,
update,
getPopup,
close,
popupsState,
Expand Down
1 change: 1 addition & 0 deletions src/types/PopupsBag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PopupsState } from '../utils/popupsReducer';
export type PopupsBag = {
mount: <P>(popup: Popup<P>) => PopupIdentifier;
unmount: (popupIdentifier: PopupIdentifier) => void;
update: (popupIdentifier: PopupIdentifier, props: object) => void;
close: (popupIdentifier: PopupIdentifier) => void;

popupsState: PopupsState;
Expand Down
34 changes: 29 additions & 5 deletions src/utils/popupsReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PopupsRegistry } from '../types/PopupsRegistry';
export enum ActionType {
MOUNT,
UNMOUNT,
UPDATE,
}

type MountAction = {
Expand All @@ -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;
Expand All @@ -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;

Expand Down
39 changes: 34 additions & 5 deletions test/hooks/usePopup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand All @@ -45,8 +46,9 @@ describe('usePopup', () => {
});

it('should close popup', () => {
const initialProps = {};
const { result } = renderHook(
() => usePopup(SimplePopupComponent, {}, group),
() => usePopup(SimplePopupComponent, initialProps, group),
{
wrapper: TestHookWrapper,
}
Expand All @@ -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 }
);

Expand All @@ -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,
}
Expand All @@ -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();
});
});

0 comments on commit ba3eb4c

Please sign in to comment.