From 0c7416cf60e57c4d22ead4628527f3fd03aea0f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Connor=20B=C3=A4r?= Date: Wed, 15 Sep 2021 13:25:29 +0200 Subject: [PATCH] Fix useCollapsible not closing immediately (#1169) --- .changeset/proud-apples-attack.md | 5 +++++ .changeset/warm-meals-invite.md | 5 +++++ .../hooks/useCollapsible/useCollapsible.spec.ts | 14 +++++++++----- .../hooks/useCollapsible/useCollapsible.ts | 17 +++++++++-------- 4 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 .changeset/proud-apples-attack.md create mode 100644 .changeset/warm-meals-invite.md diff --git a/.changeset/proud-apples-attack.md b/.changeset/proud-apples-attack.md new file mode 100644 index 0000000000..8666a5133a --- /dev/null +++ b/.changeset/proud-apples-attack.md @@ -0,0 +1,5 @@ +--- +'@sumup/circuit-ui': minor +--- + +Exposed the `isAnimating` state from the useCollapsible hook. diff --git a/.changeset/warm-meals-invite.md b/.changeset/warm-meals-invite.md new file mode 100644 index 0000000000..2fa3300701 --- /dev/null +++ b/.changeset/warm-meals-invite.md @@ -0,0 +1,5 @@ +--- +'@sumup/circuit-ui': patch +--- + +Fixed a bug in the `useCollapsible` hook to start the closing animation immediately. diff --git a/packages/circuit-ui/hooks/useCollapsible/useCollapsible.spec.ts b/packages/circuit-ui/hooks/useCollapsible/useCollapsible.spec.ts index ed1262107c..50ee249a10 100644 --- a/packages/circuit-ui/hooks/useCollapsible/useCollapsible.spec.ts +++ b/packages/circuit-ui/hooks/useCollapsible/useCollapsible.spec.ts @@ -15,7 +15,7 @@ import { MouseEvent } from 'react'; -import { renderHook, actHook } from '../../util/test-utils'; +import { renderHook, actHook, waitFor } from '../../util/test-utils'; import { useCollapsible, getHeight } from './useCollapsible'; @@ -158,7 +158,7 @@ describe('useCollapsible', () => { }); describe('toggling', () => { - it('should toggle the open state when the button is clicked', () => { + it('should toggle the open state when the button is clicked', async () => { const event = ({ fizz: 'buzz' } as unknown) as MouseEvent; const { result } = renderHook(() => useCollapsible()); const { getButtonProps } = result.current; @@ -169,10 +169,12 @@ describe('useCollapsible', () => { getButtonProps().onClick(event); }); - expect(result.current.isOpen).toBeTruthy(); + await waitFor(() => { + expect(result.current.isOpen).toBeTruthy(); + }); }); - it('should toggle the open state when the callback is called', () => { + it('should toggle the open state when the callback is called', async () => { const { result } = renderHook(() => useCollapsible()); expect(result.current.isOpen).toBeFalsy(); @@ -181,7 +183,9 @@ describe('useCollapsible', () => { result.current.toggleOpen(); }); - expect(result.current.isOpen).toBeTruthy(); + await waitFor(() => { + expect(result.current.isOpen).toBeTruthy(); + }); }); }); diff --git a/packages/circuit-ui/hooks/useCollapsible/useCollapsible.ts b/packages/circuit-ui/hooks/useCollapsible/useCollapsible.ts index f6366ded5d..a8c0daf498 100644 --- a/packages/circuit-ui/hooks/useCollapsible/useCollapsible.ts +++ b/packages/circuit-ui/hooks/useCollapsible/useCollapsible.ts @@ -48,6 +48,7 @@ type ContentProps = { type Collapsible = { isOpen: boolean; toggleOpen: () => void; + isAnimating: boolean; getButtonProps: (props?: { onClick?: (event: ClickEvent) => void; }) => ButtonProps; @@ -69,29 +70,29 @@ export function useCollapsible({ const contentElement = useRef(null); const [isOpen, setOpen] = useState(initialOpen); const [height, setHeight] = useState(getHeight(contentElement)); - const [, setAnimating] = useAnimation(); + const [isAnimating, setAnimating] = useAnimation(); const toggleOpen = useCallback(() => { setAnimating({ duration, onStart: () => { setHeight(getHeight(contentElement)); - if (!isOpen) { - setOpen(true); - } + // Delaying the state update until the next animation frame ensures that + // the browsers renders the new height before the animation starts. + window.requestAnimationFrame(() => { + setOpen((prev) => !prev); + }); }, onEnd: () => { - if (isOpen) { - setOpen(false); - } setHeight(DEFAULT_HEIGHT); }, }); - }, [isOpen, setAnimating, duration]); + }, [setAnimating, duration]); return { isOpen, toggleOpen, + isAnimating, getButtonProps: (props = {}) => ({ 'onClick': (event: ClickEvent) => { if (props.onClick) {