From 14f15caa2dc6ea2aaa2a78a12822c205dcf89a0d Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 15:26:48 +0100 Subject: [PATCH 01/44] 1. disable focus trap --- src/ActionMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index fa07408c99e..3d7a098c19b 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -125,6 +125,7 @@ const Overlay: React.FC = ({children, align = 'start', ...over align={align} overlayProps={overlayProps} focusZoneSettings={{focusOutBehavior: 'wrap'}} + focusTrapSettings={{disabled: true}} >
Date: Fri, 18 Mar 2022 15:48:58 +0100 Subject: [PATCH 02/44] add failing tests --- src/__tests__/ActionMenu.test.tsx | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/__tests__/ActionMenu.test.tsx b/src/__tests__/ActionMenu.test.tsx index 7aa30c560ec..5385c18ffb8 100644 --- a/src/__tests__/ActionMenu.test.tsx +++ b/src/__tests__/ActionMenu.test.tsx @@ -136,6 +136,38 @@ describe('ActionMenu', () => { cleanup() }) + it('should select first element when ArrowDown is pressed after opening Menu', () => { + const component = HTMLRender() + + const button = component.getByText('Toggle Menu') + fireEvent.click(button) + expect(component.queryByRole('menu')).toBeInTheDocument() + + fireEvent.keyDown(button, {key: 'ArrowDown', code: 'ArrowDown'}) + expect(component.getAllByRole('menuitem')[0]).toEqual(document.activeElement) + + cleanup() + }) + + it('should close the menu if Tab is pressed and move to next element', () => { + const component = HTMLRender( + <> + + + + ) + const button = component.getByText('Toggle Menu') + fireEvent.click(button) + expect(component.queryByRole('menu')).toBeInTheDocument() + + fireEvent.keyDown(button, {key: 'Tab', code: 'Tab'}) + expect(component.queryByRole('menu')).not.toBeInTheDocument() + + expect(component.getByText('next focusable element')).toEqual(document.activeElement) + + cleanup() + }) + it('should have no axe violations', async () => { const {container} = HTMLRender() const results = await axe(container) From 8d13770634f027fc22fb261db3c21ba948f811be Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 15:50:27 +0100 Subject: [PATCH 03/44] add more failing tests --- src/__tests__/ActionMenu.test.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/__tests__/ActionMenu.test.tsx b/src/__tests__/ActionMenu.test.tsx index 5385c18ffb8..8eb3c2a04d5 100644 --- a/src/__tests__/ActionMenu.test.tsx +++ b/src/__tests__/ActionMenu.test.tsx @@ -136,6 +136,18 @@ describe('ActionMenu', () => { cleanup() }) + it('should keep focus on Button when menu is opened with click', () => { + const component = HTMLRender() + + const button = component.getByText('Toggle Menu') + fireEvent.click(button) + + expect(component.queryByRole('menu')).toBeInTheDocument() + expect(button).toEqual(document.activeElement) + + cleanup() + }) + it('should select first element when ArrowDown is pressed after opening Menu', () => { const component = HTMLRender() @@ -143,7 +155,8 @@ describe('ActionMenu', () => { fireEvent.click(button) expect(component.queryByRole('menu')).toBeInTheDocument() - fireEvent.keyDown(button, {key: 'ArrowDown', code: 'ArrowDown'}) + // button should be the active element + fireEvent.keyDown(document.activeElement!, {key: 'ArrowDown', code: 'ArrowDown'}) expect(component.getAllByRole('menuitem')[0]).toEqual(document.activeElement) cleanup() From 36d67381744961dfe6fe3f20f009b3343cb67a96 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 16:32:31 +0100 Subject: [PATCH 04/44] handle click in useMenuInitialFocus --- .../hooks/useMenuInitialFocus.test.tsx | 20 +++++++++++++-- src/hooks/useMenuInitialFocus.ts | 25 +++++++++++++------ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/__tests__/hooks/useMenuInitialFocus.test.tsx b/src/__tests__/hooks/useMenuInitialFocus.test.tsx index bc077603ddf..8dd76810065 100644 --- a/src/__tests__/hooks/useMenuInitialFocus.test.tsx +++ b/src/__tests__/hooks/useMenuInitialFocus.test.tsx @@ -7,11 +7,16 @@ const Component = () => { const onOpen = () => setOpen(!open) const containerRef = React.createRef() - const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef) + const anchorRef = React.createRef() + const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef, anchorRef) return ( <> - {open && ( @@ -83,4 +88,15 @@ describe('useMenuInitialFocus', () => { expect(document.body).toEqual(document.activeElement) }) }) + + it('should keep focus on trigger when opened with click', async () => { + const {getByText} = render() + + const button = getByText('open container') + fireEvent.click(button) + + await waitFor(() => { + expect(button).toEqual(document.activeElement) + }) + }) }) diff --git a/src/hooks/useMenuInitialFocus.ts b/src/hooks/useMenuInitialFocus.ts index e742e6b49fc..186571db027 100644 --- a/src/hooks/useMenuInitialFocus.ts +++ b/src/hooks/useMenuInitialFocus.ts @@ -5,18 +5,25 @@ import {useProvidedRefOrCreate} from './useProvidedRefOrCreate' type Gesture = 'anchor-click' | 'anchor-key-press' type Callback = (gesture: Gesture, event?: React.KeyboardEvent) => unknown -export const useMenuInitialFocus = (open: boolean, onOpen?: Callback, providedRef?: React.RefObject) => { - const containerRef = useProvidedRefOrCreate(providedRef) +export const useMenuInitialFocus = ( + open: boolean, + onOpen?: Callback, + providedContainerRef?: React.RefObject, + providedAnchorRef?: React.RefObject +) => { + const containerRef = useProvidedRefOrCreate(providedContainerRef) + const anchorRef = useProvidedRefOrCreate(providedAnchorRef) const [openingKey, setOpeningKey] = React.useState(undefined) const openWithFocus: Callback = (gesture, event) => { - if (gesture === 'anchor-key-press' && event) setOpeningKey(event.code) - else setOpeningKey(undefined) + if (gesture === 'anchor-click') setOpeningKey('mouse-click') + if (gesture === 'anchor-key-press' && event) setOpeningKey((event as React.KeyboardEvent).code) if (typeof onOpen === 'function') onOpen(gesture, event) } /** * Pick the first element to focus based on the key used to open the Menu + * Click: anchor * ArrowDown | Space | Enter: first element * ArrowUp: last element */ @@ -25,7 +32,11 @@ export const useMenuInitialFocus = (open: boolean, onOpen?: Callback, providedRe if (!openingKey || !containerRef.current) return const iterable = iterateFocusableElements(containerRef.current) - if (['ArrowDown', 'Space', 'Enter'].includes(openingKey)) { + + if (openingKey === 'mouse-click') { + if (anchorRef.current) anchorRef.current.focus() + else throw new Error('For focus management, please attach anchorRef') // TODO: improve error + } else if (['ArrowDown', 'Space', 'Enter'].includes(openingKey)) { const firstElement = iterable.next().value /** We push imperative focus to the next tick to prevent React's batching */ setTimeout(() => firstElement?.focus()) @@ -34,7 +45,7 @@ export const useMenuInitialFocus = (open: boolean, onOpen?: Callback, providedRe const lastElement = elements[elements.length - 1] setTimeout(() => lastElement.focus()) } - }, [open, openingKey, containerRef]) + }, [open, openingKey, containerRef, anchorRef]) - return {containerRef, openWithFocus} + return {containerRef, anchorRef, openWithFocus} } From 06c236cb93917e3953c3a331ede78898fcfb32cd Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 20:04:31 +0100 Subject: [PATCH 05/44] Fix useMenuInitialFocus test --- src/__tests__/hooks/useMenuInitialFocus.test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/__tests__/hooks/useMenuInitialFocus.test.tsx b/src/__tests__/hooks/useMenuInitialFocus.test.tsx index 8dd76810065..034f4d99bb9 100644 --- a/src/__tests__/hooks/useMenuInitialFocus.test.tsx +++ b/src/__tests__/hooks/useMenuInitialFocus.test.tsx @@ -93,6 +93,8 @@ describe('useMenuInitialFocus', () => { const {getByText} = render() const button = getByText('open container') + button.focus() // browsers do this automatically on click, but tests don't + expect(button).toEqual(document.activeElement) fireEvent.click(button) await waitFor(() => { From ac52a486c928c2d44dec3c020292eeb4bc24dcfb Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 20:04:44 +0100 Subject: [PATCH 06/44] Add test for click with ActionMenu --- src/__tests__/ActionMenu.test.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/__tests__/ActionMenu.test.tsx b/src/__tests__/ActionMenu.test.tsx index 8eb3c2a04d5..ab45278c920 100644 --- a/src/__tests__/ActionMenu.test.tsx +++ b/src/__tests__/ActionMenu.test.tsx @@ -136,14 +136,19 @@ describe('ActionMenu', () => { cleanup() }) - it('should keep focus on Button when menu is opened with click', () => { + it('should keep focus on Button when menu is opened with click', async () => { const component = HTMLRender() - const button = component.getByText('Toggle Menu') - fireEvent.click(button) + const button = component.getByRole('button') - expect(component.queryByRole('menu')).toBeInTheDocument() + button.focus() // browsers do this automatically on click, but tests don't expect(button).toEqual(document.activeElement) + fireEvent.click(button) + + /** We use waitFor because the hook uses an effect with setTimeout + * and we need to wait for that to happen in the next tick + */ + await waitFor(() => expect(document.activeElement).toEqual(button)) cleanup() }) @@ -152,6 +157,7 @@ describe('ActionMenu', () => { const component = HTMLRender() const button = component.getByText('Toggle Menu') + button.focus() // browsers do this automatically on click, but tests don't fireEvent.click(button) expect(component.queryByRole('menu')).toBeInTheDocument() From ca0781100bb933714769deb06a19fb020f09db1f Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 20:08:06 +0100 Subject: [PATCH 07/44] use updated menu focus --- src/ActionMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 3d7a098c19b..7b56ad39ab5 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -111,7 +111,7 @@ const Overlay: React.FC = ({children, align = 'start', ...over > const containerRef = React.createRef() - const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef) + const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef, anchorRef) useTypeaheadFocus(open, containerRef) return ( From 176256105ab466f928f12e87b62e0bcca3782ef1 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 20:08:13 +0100 Subject: [PATCH 08/44] add the option of rendering inline instead of portal --- src/ActionMenu.tsx | 2 +- src/Overlay.tsx | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 7b56ad39ab5..af2993aba0d 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -123,7 +123,7 @@ const Overlay: React.FC = ({children, align = 'start', ...over onOpen={openWithFocus} onClose={onClose} align={align} - overlayProps={overlayProps} + overlayProps={{usePortal: false, ...overlayProps}} focusZoneSettings={{focusOutBehavior: 'wrap'}} focusTrapSettings={{disabled: true}} > diff --git a/src/Overlay.tsx b/src/Overlay.tsx index cc9c721213c..444f72d53e7 100644 --- a/src/Overlay.tsx +++ b/src/Overlay.tsx @@ -100,6 +100,7 @@ type BaseOverlayProps = { preventFocusOnOpen?: boolean role?: AriaRole children?: React.ReactNode + usePortal?: boolean } type OwnOverlayProps = Merge @@ -137,6 +138,7 @@ const Overlay = React.forwardRef( anchorSide, portalContainerName, preventFocusOnOpen, + usePortal = true, ...rest }, forwardedRef @@ -179,8 +181,10 @@ const Overlay = React.forwardRef( ) }, [anchorSide, slideAnimationDistance, slideAnimationEasing, visibility]) + const Container = usePortal ? Portal : React.Fragment + return ( - + ( } as React.CSSProperties } /> - + ) } ) as PolymorphicForwardRefComponent<'div', OwnOverlayProps> From 5ce902bbc85ab3b14385f1e55470117babe0a736 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 20:08:42 +0100 Subject: [PATCH 09/44] handle tab press inside open menu --- src/ActionMenu.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index af2993aba0d..b578e91ed4b 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -72,6 +72,22 @@ const Anchor = React.forwardRef, onClose: MenuContextProps['onClose']) => { + React.useEffect(() => { + const handler = (event: React.KeyboardEvent) => { + if (event.code === 'Tab') { + onClose('escape') //TODO: Add tab out to the list + } + } + + const container = containerRef.current + if (!container) return + + container.addEventListener('keydown', handler) + return () => container.removeEventListener('keydown', handler) + }) +} + /** this component is syntactical sugar 🍭 */ export type ActionMenuButtonProps = ButtonProps const MenuButton = React.forwardRef( @@ -113,6 +129,7 @@ const Overlay: React.FC = ({children, align = 'start', ...over const containerRef = React.createRef() const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef, anchorRef) useTypeaheadFocus(open, containerRef) + useOnTabPress(containerRef, onClose) return ( Date: Fri, 18 Mar 2022 20:17:09 +0100 Subject: [PATCH 10/44] Repurpose openWithFocus for initialising keyboard interaction --- src/ActionMenu.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index b578e91ed4b..7d2f43165ec 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -131,6 +131,16 @@ const Overlay: React.FC = ({children, align = 'start', ...over useTypeaheadFocus(open, containerRef) useOnTabPress(containerRef, onClose) + React.useEffect(() => { + const handler = (event: React.KeyboardEvent) => { + // TODO: Refactor hook to be just about focus, not initial focus + openWithFocus('anchor-key-press', event) + } + + anchorRef.current.addEventListener('keydown', handler) + return () => anchorRef.current.removeEventListener('keydown', handler) + }) + return ( Date: Fri, 18 Mar 2022 20:18:12 +0100 Subject: [PATCH 11/44] Add anchorRef to useTypeaheadFocus --- src/ActionMenu.tsx | 2 +- src/hooks/useTypeaheadFocus.ts | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 7d2f43165ec..36f940704e0 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -128,7 +128,7 @@ const Overlay: React.FC = ({children, align = 'start', ...over const containerRef = React.createRef() const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef, anchorRef) - useTypeaheadFocus(open, containerRef) + useTypeaheadFocus(open, containerRef, anchorRef) useOnTabPress(containerRef, onClose) React.useEffect(() => { diff --git a/src/hooks/useTypeaheadFocus.ts b/src/hooks/useTypeaheadFocus.ts index 7cc20ab701f..20a67eeed97 100644 --- a/src/hooks/useTypeaheadFocus.ts +++ b/src/hooks/useTypeaheadFocus.ts @@ -4,7 +4,7 @@ import {useProvidedRefOrCreate} from './useProvidedRefOrCreate' export const TYPEAHEAD_TIMEOUT = 1000 -export const useTypeaheadFocus = (open: boolean, providedRef?: React.RefObject) => { +export const useTypeaheadFocus = (open: boolean, providedRef?: React.RefObject, anchorRef) => { const containerRef = useProvidedRefOrCreate(providedRef) React.useEffect(() => { @@ -83,8 +83,12 @@ export const useTypeaheadFocus = (open: boolean, providedRef?: React.RefObject container.removeEventListener('keydown', handler) - }, [open, containerRef]) + anchorRef.current.addEventListener('keydown', handler) + return () => { + container.removeEventListener('keydown', handler) + anchorRef.current.removeEventListener('keydown', handler) + } + }, [open, containerRef, anchorRef]) const isAlphabetKey = (event: KeyboardEvent) => { return event.key.length === 1 && /[a-z\d]/i.test(event.key) From b2585cc9afe852dbb48415316fe3e2aa6e01d68a Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 20:27:46 +0100 Subject: [PATCH 12/44] only activate keyboard activation if the menu is open --- src/ActionMenu.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 36f940704e0..8cf803e59da 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -132,9 +132,14 @@ const Overlay: React.FC = ({children, align = 'start', ...over useOnTabPress(containerRef, onClose) React.useEffect(() => { + // handle focus changes when keyboard navigation is activated const handler = (event: React.KeyboardEvent) => { + if (!open) return + // TODO: Refactor hook to be just about focus, not initial focus - openWithFocus('anchor-key-press', event) + if (['ArrowDown', 'Space', 'Enter', 'ArrowUp'].includes(event.code)) { + openWithFocus('anchor-key-press', event) + } } anchorRef.current.addEventListener('keydown', handler) From 5ee1c35c6a796b6bc7b5b8ed5388b70c81fc1316 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 18 Mar 2022 20:31:24 +0100 Subject: [PATCH 13/44] add tab handler to Anchor as well --- src/ActionMenu.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 8cf803e59da..de4d16ab090 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -72,7 +72,7 @@ const Anchor = React.forwardRef, onClose: MenuContextProps['onClose']) => { +const useOnTabPress = (containerRef: React.RefObject, anchorRef, onClose: MenuContextProps['onClose']) => { React.useEffect(() => { const handler = (event: React.KeyboardEvent) => { if (event.code === 'Tab') { @@ -84,7 +84,11 @@ const useOnTabPress = (containerRef: React.RefObject, onClose: Menu if (!container) return container.addEventListener('keydown', handler) - return () => container.removeEventListener('keydown', handler) + anchorRef.current.addEventListener('keydown', handler) + return () => { + container.removeEventListener('keydown', handler) + anchorRef.current.addEventListener('keydown', handler) + } }) } @@ -129,7 +133,7 @@ const Overlay: React.FC = ({children, align = 'start', ...over const containerRef = React.createRef() const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef, anchorRef) useTypeaheadFocus(open, containerRef, anchorRef) - useOnTabPress(containerRef, onClose) + useOnTabPress(containerRef, anchorRef, onClose) React.useEffect(() => { // handle focus changes when keyboard navigation is activated From 74b7a749b6762c68194fc5e835bd241441115162 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Mon, 21 Mar 2022 17:19:50 +0100 Subject: [PATCH 14/44] ActionMenu inside Overlay - overflow:visible, zIndex --- src/ActionMenu.tsx | 2 +- src/Overlay.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index de4d16ab090..9b3a12a12d6 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -159,7 +159,7 @@ const Overlay: React.FC = ({children, align = 'start', ...over onOpen={openWithFocus} onClose={onClose} align={align} - overlayProps={{usePortal: false, ...overlayProps}} + overlayProps={{usePortal: false, sx: {zIndex: 2}, ...overlayProps}} focusZoneSettings={{focusOutBehavior: 'wrap'}} focusTrapSettings={{disabled: true}} > diff --git a/src/Overlay.tsx b/src/Overlay.tsx index 444f72d53e7..71dd0b997d5 100644 --- a/src/Overlay.tsx +++ b/src/Overlay.tsx @@ -63,7 +63,7 @@ const StyledOverlay = styled.div` max-height: ${props => props.maxHeight && heightMap[props.maxHeight]}; width: ${props => widthMap[props.width || 'auto']}; border-radius: 12px; - overflow: hidden; + overflow: visible; animation: overlay-appear ${animationDuration}ms ${get('animation.easeOutCubic')}; @keyframes overlay-appear { From 7e23db7d8964bee81324afe70f033f6c4282a9ea Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Mon, 21 Mar 2022 17:21:17 +0100 Subject: [PATCH 15/44] merge sx prop on overlay props --- src/ActionMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 9b3a12a12d6..0c475abdbb5 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -122,7 +122,7 @@ type MenuOverlayProps = Partial & */ children: React.ReactElement[] | React.ReactElement } -const Overlay: React.FC = ({children, align = 'start', ...overlayProps}) => { +const Overlay: React.FC = ({children, align = 'start', sx: propsSx = {}, ...overlayProps}) => { // we typecast anchorRef as required instead of optional // because we know that we're setting it in context in Menu const {anchorRef, renderAnchor, anchorId, open, onOpen, onClose} = React.useContext(MenuContext) as MandateProps< @@ -159,7 +159,7 @@ const Overlay: React.FC = ({children, align = 'start', ...over onOpen={openWithFocus} onClose={onClose} align={align} - overlayProps={{usePortal: false, sx: {zIndex: 2}, ...overlayProps}} + overlayProps={{usePortal: false, sx: merge({zIndex: 2}, propsSx as SxProp), ...overlayProps}} focusZoneSettings={{focusOutBehavior: 'wrap'}} focusTrapSettings={{disabled: true}} > From c951b48f115ba5765ac8b0d625c89c09bb96694f Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Mon, 21 Mar 2022 17:37:43 +0100 Subject: [PATCH 16/44] update snapshot --- src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap b/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap index 82b17ad7de6..68e29b11175 100644 --- a/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap +++ b/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap @@ -175,7 +175,7 @@ exports[`AnchoredOverlay should render consistently when open 1`] = ` height: auto; width: auto; border-radius: 12px; - overflow: hidden; + overflow: visible; -webkit-animation: overlay-appear 200ms cubic-bezier(0.33,1,0.68,1); animation: overlay-appear 200ms cubic-bezier(0.33,1,0.68,1); visibility: var(--styled-overlay-visibility); From 0bd23cdfcb7edcaae107aea9c77fd7fa753d2e15 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Mon, 21 Mar 2022 17:40:06 +0100 Subject: [PATCH 17/44] make anchorRef optional for typeahead --- src/hooks/useTypeaheadFocus.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/hooks/useTypeaheadFocus.ts b/src/hooks/useTypeaheadFocus.ts index 20a67eeed97..5b2d6db7a6f 100644 --- a/src/hooks/useTypeaheadFocus.ts +++ b/src/hooks/useTypeaheadFocus.ts @@ -4,12 +4,18 @@ import {useProvidedRefOrCreate} from './useProvidedRefOrCreate' export const TYPEAHEAD_TIMEOUT = 1000 -export const useTypeaheadFocus = (open: boolean, providedRef?: React.RefObject, anchorRef) => { - const containerRef = useProvidedRefOrCreate(providedRef) +export const useTypeaheadFocus = ( + open: boolean, + providedContainerRef?: React.RefObject, + providedAnchorRef?: React.RefObject +) => { + const containerRef = useProvidedRefOrCreate(providedContainerRef) + const anchorRef = useProvidedRefOrCreate(providedAnchorRef) React.useEffect(() => { if (!open || !containerRef.current) return const container = containerRef.current + const anchor = anchorRef?.current let query = '' let timeout: number | undefined @@ -83,10 +89,10 @@ export const useTypeaheadFocus = (open: boolean, providedRef?: React.RefObject { container.removeEventListener('keydown', handler) - anchorRef.current.removeEventListener('keydown', handler) + anchor?.removeEventListener('keydown', handler) } }, [open, containerRef, anchorRef]) @@ -94,5 +100,5 @@ export const useTypeaheadFocus = (open: boolean, providedRef?: React.RefObject Date: Mon, 21 Mar 2022 18:42:47 +0100 Subject: [PATCH 18/44] use userEvent for tests --- src/__tests__/ActionMenu.test.tsx | 41 ++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/__tests__/ActionMenu.test.tsx b/src/__tests__/ActionMenu.test.tsx index ab45278c920..3e9991c7d08 100644 --- a/src/__tests__/ActionMenu.test.tsx +++ b/src/__tests__/ActionMenu.test.tsx @@ -1,4 +1,5 @@ import {cleanup, render as HTMLRender, waitFor, fireEvent} from '@testing-library/react' +import userEvent from '@testing-library/user-event' import 'babel-polyfill' import {axe, toHaveNoViolations} from 'jest-axe' import React from 'react' @@ -58,9 +59,7 @@ describe('ActionMenu', () => { const component = HTMLRender() const button = component.getByText('Toggle Menu') - // We pass keycode here to navigate a implementation detail in react-testing-library - // https://github.com/testing-library/react-testing-library/issues/269#issuecomment-455854112 - fireEvent.keyDown(button, {key: 'Enter', charCode: 13}) + fireEvent.keyDown(button, {key: 'Enter'}) expect(component.getByRole('menu')).toBeInTheDocument() cleanup() }) @@ -83,6 +82,9 @@ describe('ActionMenu', () => { fireEvent.click(button) const menuItems = await waitFor(() => component.getAllByRole('menuitem')) + + // We pass keycode here to navigate a implementation detail in react-testing-library + // https://github.com/testing-library/react-testing-library/issues/269#issuecomment-455854112 fireEvent.keyPress(menuItems[0], {key: 'Enter', charCode: 13}) expect(component.queryByRole('menu')).toBeNull() @@ -138,12 +140,13 @@ describe('ActionMenu', () => { it('should keep focus on Button when menu is opened with click', async () => { const component = HTMLRender() - const button = component.getByRole('button') - button.focus() // browsers do this automatically on click, but tests don't - expect(button).toEqual(document.activeElement) + userEvent.tab() // tab into the story, this should focus on the first button + expect(button).toEqual(document.activeElement) // trust, but verify + fireEvent.click(button) + expect(component.queryByRole('menu')).toBeInTheDocument() /** We use waitFor because the hook uses an effect with setTimeout * and we need to wait for that to happen in the next tick @@ -153,7 +156,7 @@ describe('ActionMenu', () => { cleanup() }) - it('should select first element when ArrowDown is pressed after opening Menu', () => { + it('should select first element when ArrowDown is pressed after opening Menu with click', async () => { const component = HTMLRender() const button = component.getByText('Toggle Menu') @@ -163,26 +166,34 @@ describe('ActionMenu', () => { // button should be the active element fireEvent.keyDown(document.activeElement!, {key: 'ArrowDown', code: 'ArrowDown'}) - expect(component.getAllByRole('menuitem')[0]).toEqual(document.activeElement) + + await waitFor(() => { + expect(component.getAllByRole('menuitem')[0]).toEqual(document.activeElement) + }) cleanup() }) - it('should close the menu if Tab is pressed and move to next element', () => { + it('should close the menu if Tab is pressed and move to next element', async () => { const component = HTMLRender( <> - + ) - const button = component.getByText('Toggle Menu') - fireEvent.click(button) + const anchor = component.getByRole('button') + + userEvent.tab() // tab into the story, this should focus on the first button + expect(anchor).toEqual(document.activeElement) // trust, but verify + + fireEvent.keyDown(anchor, {key: 'Enter'}) expect(component.queryByRole('menu')).toBeInTheDocument() - fireEvent.keyDown(button, {key: 'Tab', code: 'Tab'}) - expect(component.queryByRole('menu')).not.toBeInTheDocument() + expect(component.getAllByRole('menuitem')[0]).toEqual(document.activeElement) - expect(component.getByText('next focusable element')).toEqual(document.activeElement) + userEvent.tab() + expect(component.getByPlaceholderText('next focusable element')).toEqual(document.activeElement) + expect(component.queryByRole('menu')).not.toBeInTheDocument() cleanup() }) From 95df88d24a395d093611f09ac29cc03bd8e9fbad Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Mon, 21 Mar 2022 18:42:52 +0100 Subject: [PATCH 19/44] Add story for Tab testing --- src/stories/ActionMenu/fixtures.stories.tsx | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/stories/ActionMenu/fixtures.stories.tsx b/src/stories/ActionMenu/fixtures.stories.tsx index 46d960a9ad3..52c6ff685cd 100644 --- a/src/stories/ActionMenu/fixtures.stories.tsx +++ b/src/stories/ActionMenu/fixtures.stories.tsx @@ -657,3 +657,27 @@ export function TypeaheadTest(): JSX.Element { ) } + +export function TabTest(): JSX.Element { + return ( + <> +

Story to test Tab

+ + + Toggle Menu + + + New file + + Copy link + Edit file + event.preventDefault()}> + Delete file + + + + + + + ) +} From 9821bf002b37f826f46cb42712a6b47f0ad558a5 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Mon, 21 Mar 2022 18:45:31 +0100 Subject: [PATCH 20/44] improve types for handler --- src/ActionMenu.tsx | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 0c475abdbb5..8f7f63864bd 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -72,24 +72,29 @@ const Anchor = React.forwardRef, anchorRef, onClose: MenuContextProps['onClose']) => { +const useOnTabPress = ( + containerRef: React.RefObject, + anchorRef: React.RefObject, + onClose: MenuContextProps['onClose'] +) => { React.useEffect(() => { - const handler = (event: React.KeyboardEvent) => { - if (event.code === 'Tab') { + const handler = (event: KeyboardEvent) => { + if (event.key === 'Tab') { onClose('escape') //TODO: Add tab out to the list } } const container = containerRef.current - if (!container) return + const anchor = anchorRef.current + if (!container || !anchor) return container.addEventListener('keydown', handler) - anchorRef.current.addEventListener('keydown', handler) + anchor.addEventListener('keydown', handler) return () => { container.removeEventListener('keydown', handler) - anchorRef.current.addEventListener('keydown', handler) + anchor.addEventListener('keydown', handler) } - }) + }, [anchorRef, containerRef, onClose]) } /** this component is syntactical sugar 🍭 */ @@ -146,9 +151,11 @@ const Overlay: React.FC = ({children, align = 'start', sx: pro } } - anchorRef.current.addEventListener('keydown', handler) - return () => anchorRef.current.removeEventListener('keydown', handler) - }) + const anchor = anchorRef.current + + anchor?.addEventListener('keydown', handler) + return () => anchor?.removeEventListener('keydown', handler) + }, [open, openWithFocus, anchorRef]) return ( Date: Mon, 21 Mar 2022 18:52:58 +0100 Subject: [PATCH 21/44] continue to improve types --- src/ActionMenu.tsx | 8 +++----- src/AnchoredOverlay/AnchoredOverlay.tsx | 2 +- src/hooks/useMenuInitialFocus.ts | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 8f7f63864bd..0b242e2d4ed 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -79,9 +79,7 @@ const useOnTabPress = ( ) => { React.useEffect(() => { const handler = (event: KeyboardEvent) => { - if (event.key === 'Tab') { - onClose('escape') //TODO: Add tab out to the list - } + if (event.key === 'Tab' && typeof onClose === 'function') onClose('tab') } const container = containerRef.current @@ -142,12 +140,12 @@ const Overlay: React.FC = ({children, align = 'start', sx: pro React.useEffect(() => { // handle focus changes when keyboard navigation is activated - const handler = (event: React.KeyboardEvent) => { + const handler = (event: KeyboardEvent) => { if (!open) return // TODO: Refactor hook to be just about focus, not initial focus if (['ArrowDown', 'Space', 'Enter', 'ArrowUp'].includes(event.code)) { - openWithFocus('anchor-key-press', event) + openWithFocus('anchor-key-press') } } diff --git a/src/AnchoredOverlay/AnchoredOverlay.tsx b/src/AnchoredOverlay/AnchoredOverlay.tsx index 1dd255473c6..2ebbf7f1e1a 100644 --- a/src/AnchoredOverlay/AnchoredOverlay.tsx +++ b/src/AnchoredOverlay/AnchoredOverlay.tsx @@ -60,7 +60,7 @@ interface AnchoredOverlayBaseProps extends Pick unknown + onClose?: (gesture: 'anchor-click' | 'click-outside' | 'escape' | 'tab') => unknown /** * Props to be spread on the internal `Overlay` component. diff --git a/src/hooks/useMenuInitialFocus.ts b/src/hooks/useMenuInitialFocus.ts index 186571db027..e1de450d169 100644 --- a/src/hooks/useMenuInitialFocus.ts +++ b/src/hooks/useMenuInitialFocus.ts @@ -17,7 +17,7 @@ export const useMenuInitialFocus = ( const openWithFocus: Callback = (gesture, event) => { if (gesture === 'anchor-click') setOpeningKey('mouse-click') - if (gesture === 'anchor-key-press' && event) setOpeningKey((event as React.KeyboardEvent).code) + if (gesture === 'anchor-key-press' && event) setOpeningKey(event.code) if (typeof onOpen === 'function') onOpen(gesture, event) } From 7399216e1d4d0c1c3b9995185f9a4f908ea17b9a Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Mon, 21 Mar 2022 19:45:12 +0100 Subject: [PATCH 22/44] clean up hooks --- src/ActionMenu.tsx | 31 ++---------------- src/hooks/index.ts | 1 + src/hooks/useMenuKeyboardNavigation.ts | 45 ++++++++++++++++++++++++++ src/hooks/useTypeaheadFocus.ts | 8 +++-- 4 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 src/hooks/useMenuKeyboardNavigation.ts diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 0b242e2d4ed..72af06afb3d 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -3,14 +3,14 @@ import {useSSRSafeId} from '@react-aria/ssr' import {TriangleDownIcon} from '@primer/octicons-react' import {AnchoredOverlay, AnchoredOverlayProps} from './AnchoredOverlay' import {OverlayProps} from './Overlay' -import {useProvidedRefOrCreate, useProvidedStateOrCreate, useMenuInitialFocus, useTypeaheadFocus} from './hooks' +import {useProvidedRefOrCreate, useProvidedStateOrCreate, useMenuKeyboardNavigation} from './hooks' import {Divider} from './ActionList/Divider' import {ActionListContainerContext} from './ActionList/ActionListContainerContext' import {Button, ButtonProps} from './Button' import {MandateProps} from './utils/types' import {SxProp, merge} from './sx' -type MenuContextProps = Pick< +export type MenuContextProps = Pick< AnchoredOverlayProps, 'anchorRef' | 'renderAnchor' | 'open' | 'onOpen' | 'onClose' | 'anchorId' > @@ -72,29 +72,6 @@ const Anchor = React.forwardRef, - anchorRef: React.RefObject, - onClose: MenuContextProps['onClose'] -) => { - React.useEffect(() => { - const handler = (event: KeyboardEvent) => { - if (event.key === 'Tab' && typeof onClose === 'function') onClose('tab') - } - - const container = containerRef.current - const anchor = anchorRef.current - if (!container || !anchor) return - - container.addEventListener('keydown', handler) - anchor.addEventListener('keydown', handler) - return () => { - container.removeEventListener('keydown', handler) - anchor.addEventListener('keydown', handler) - } - }, [anchorRef, containerRef, onClose]) -} - /** this component is syntactical sugar 🍭 */ export type ActionMenuButtonProps = ButtonProps const MenuButton = React.forwardRef( @@ -134,9 +111,7 @@ const Overlay: React.FC = ({children, align = 'start', sx: pro > const containerRef = React.createRef() - const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef, anchorRef) - useTypeaheadFocus(open, containerRef, anchorRef) - useOnTabPress(containerRef, anchorRef, onClose) + const {openWithFocus} = useMenuKeyboardNavigation(open, onOpen, onClose, containerRef, anchorRef) React.useEffect(() => { // handle focus changes when keyboard navigation is activated diff --git a/src/hooks/index.ts b/src/hooks/index.ts index a6dd3f13016..5538e41402d 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -12,3 +12,4 @@ export {useRenderForcingRef} from './useRenderForcingRef' export {useProvidedStateOrCreate} from './useProvidedStateOrCreate' export {useMenuInitialFocus} from './useMenuInitialFocus' export {useTypeaheadFocus} from './useTypeaheadFocus' +export {useMenuKeyboardNavigation} from './useMenuKeyboardNavigation' diff --git a/src/hooks/useMenuKeyboardNavigation.ts b/src/hooks/useMenuKeyboardNavigation.ts new file mode 100644 index 00000000000..85689c7024c --- /dev/null +++ b/src/hooks/useMenuKeyboardNavigation.ts @@ -0,0 +1,45 @@ +import React from 'react' +import {useMenuInitialFocus} from './useMenuInitialFocus' +import {useTypeaheadFocus} from './useTypeaheadFocus' +import {MenuContextProps} from '../ActionMenu' + +/** + * Keyboard navigation is a mix of 3 hooks + * useMenuInitialFocus, useTypeaheadFocus and useTabPress + */ +export const useMenuKeyboardNavigation = ( + open: boolean, + onOpen: MenuContextProps['onOpen'], + onClose: MenuContextProps['onClose'], + containerRef: React.RefObject, + anchorRef: React.RefObject +) => { + const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef, anchorRef) + useTabPress(onClose, containerRef, anchorRef) + useTypeaheadFocus(open, containerRef, anchorRef) + + return {containerRef, anchorRef, openWithFocus} +} + +const useTabPress = ( + onClose: MenuContextProps['onClose'], + containerRef: React.RefObject, + anchorRef: React.RefObject +) => { + React.useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (event.key === 'Tab' && typeof onClose === 'function') onClose('tab') + } + + const container = containerRef.current + const anchor = anchorRef.current + if (!container || !anchor) return + + container.addEventListener('keydown', handler) + anchor.addEventListener('keydown', handler) + return () => { + container.removeEventListener('keydown', handler) + anchor.addEventListener('keydown', handler) + } + }, [onClose, anchorRef, containerRef]) +} diff --git a/src/hooks/useTypeaheadFocus.ts b/src/hooks/useTypeaheadFocus.ts index 5b2d6db7a6f..0e894621a64 100644 --- a/src/hooks/useTypeaheadFocus.ts +++ b/src/hooks/useTypeaheadFocus.ts @@ -6,16 +6,18 @@ export const TYPEAHEAD_TIMEOUT = 1000 export const useTypeaheadFocus = ( open: boolean, - providedContainerRef?: React.RefObject, + providedContainerRef: React.RefObject, providedAnchorRef?: React.RefObject ) => { const containerRef = useProvidedRefOrCreate(providedContainerRef) const anchorRef = useProvidedRefOrCreate(providedAnchorRef) React.useEffect(() => { - if (!open || !containerRef.current) return const container = containerRef.current - const anchor = anchorRef?.current + const anchor = anchorRef.current + + // anchor is optional, but container isn't + if (!open || !container) return let query = '' let timeout: number | undefined From 7f758274a926c3633a86a8e80fb56b29c3ac30cc Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Tue, 22 Mar 2022 10:23:57 +0100 Subject: [PATCH 23/44] move effects to one big hook --- src/ActionMenu.tsx | 17 ------- src/hooks/useMenuKeyboardNavigation.ts | 69 +++++++++++++++++++++----- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index 72af06afb3d..c4377effbdc 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -113,23 +113,6 @@ const Overlay: React.FC = ({children, align = 'start', sx: pro const containerRef = React.createRef() const {openWithFocus} = useMenuKeyboardNavigation(open, onOpen, onClose, containerRef, anchorRef) - React.useEffect(() => { - // handle focus changes when keyboard navigation is activated - const handler = (event: KeyboardEvent) => { - if (!open) return - - // TODO: Refactor hook to be just about focus, not initial focus - if (['ArrowDown', 'Space', 'Enter', 'ArrowUp'].includes(event.code)) { - openWithFocus('anchor-key-press') - } - } - - const anchor = anchorRef.current - - anchor?.addEventListener('keydown', handler) - return () => anchor?.removeEventListener('keydown', handler) - }, [open, openWithFocus, anchorRef]) - return ( ) => { const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef, anchorRef) - useTabPress(onClose, containerRef, anchorRef) useTypeaheadFocus(open, containerRef, anchorRef) + useCloseMenuOnTab(open, onClose, containerRef, anchorRef) + useMoveFocusToMenuItem(open, containerRef, anchorRef) - return {containerRef, anchorRef, openWithFocus} + return {openWithFocus} } -const useTabPress = ( +/** + * When Tab or Shift+Tab is pressed, the menu should close + * and the focus should naturally move to the next item + */ +const useCloseMenuOnTab = ( + open: boolean, onClose: MenuContextProps['onClose'], containerRef: React.RefObject, anchorRef: React.RefObject ) => { React.useEffect(() => { + const container = containerRef.current + const anchor = anchorRef.current + const handler = (event: KeyboardEvent) => { - if (event.key === 'Tab' && typeof onClose === 'function') onClose('tab') + if (open && event.key === 'Tab') onClose?.('tab') } + container?.addEventListener('keydown', handler) + anchor?.addEventListener('keydown', handler) + return () => { + container?.removeEventListener('keydown', handler) + anchor?.removeEventListener('keydown', handler) + } + }, [open, onClose, containerRef, anchorRef]) +} + +/** + * When Arrow Keys are pressed and the focus is on the anchor, + * focus should move to a menu item + */ +const useMoveFocusToMenuItem = ( + open: boolean, + containerRef: React.RefObject, + anchorRef: React.RefObject +) => { + React.useEffect(() => { const container = containerRef.current const anchor = anchorRef.current - if (!container || !anchor) return - container.addEventListener('keydown', handler) - anchor.addEventListener('keydown', handler) - return () => { - container.removeEventListener('keydown', handler) - anchor.addEventListener('keydown', handler) + const handler = (event: KeyboardEvent) => { + if (!open || !container) return + + const iterable = iterateFocusableElements(container) + + if (event.key === 'ArrowDown') { + const firstElement = iterable.next().value + /** We push imperative focus to the next tick to prevent React's batching */ + setTimeout(() => firstElement?.focus()) + } else if (event.key === 'ArrowUp') { + const elements = [...iterable] + const lastElement = elements[elements.length - 1] + setTimeout(() => lastElement.focus()) + } } - }, [onClose, anchorRef, containerRef]) + + anchor?.addEventListener('keydown', handler) + return () => anchor?.addEventListener('keydown', handler) + }, [open, containerRef, anchorRef]) } From f0b76141baf9730028588c62e4dfde3271d4a6a7 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Tue, 22 Mar 2022 10:27:02 +0100 Subject: [PATCH 24/44] gesture type should not be in AnchoredOverlay --- src/ActionMenu.tsx | 6 ++++-- src/AnchoredOverlay/AnchoredOverlay.tsx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index c4377effbdc..aa2c1013017 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -12,8 +12,10 @@ import {SxProp, merge} from './sx' export type MenuContextProps = Pick< AnchoredOverlayProps, - 'anchorRef' | 'renderAnchor' | 'open' | 'onOpen' | 'onClose' | 'anchorId' -> + 'anchorRef' | 'renderAnchor' | 'open' | 'onOpen' | 'anchorId' +> & { + onClose?: (gesture: 'anchor-click' | 'click-outside' | 'escape' | 'tab') => void +} const MenuContext = React.createContext({renderAnchor: null, open: false}) export type ActionMenuProps = { diff --git a/src/AnchoredOverlay/AnchoredOverlay.tsx b/src/AnchoredOverlay/AnchoredOverlay.tsx index 2ebbf7f1e1a..1dd255473c6 100644 --- a/src/AnchoredOverlay/AnchoredOverlay.tsx +++ b/src/AnchoredOverlay/AnchoredOverlay.tsx @@ -60,7 +60,7 @@ interface AnchoredOverlayBaseProps extends Pick unknown + onClose?: (gesture: 'anchor-click' | 'click-outside' | 'escape') => unknown /** * Props to be spread on the internal `Overlay` component. From 1be39c35f9fc01986deed4c7b7c4c7fc15b69724 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Tue, 22 Mar 2022 10:30:10 +0100 Subject: [PATCH 25/44] add changelog --- .changeset/actionmenu-remove-focus-trap.md | 5 +++++ .changeset/overlay-overflow.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/actionmenu-remove-focus-trap.md create mode 100644 .changeset/overlay-overflow.md diff --git a/.changeset/actionmenu-remove-focus-trap.md b/.changeset/actionmenu-remove-focus-trap.md new file mode 100644 index 00000000000..d43f6668bf0 --- /dev/null +++ b/.changeset/actionmenu-remove-focus-trap.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +ActionMenu: Remove focus trap to enable Tab and Shift+Tab behavior diff --git a/.changeset/overlay-overflow.md b/.changeset/overlay-overflow.md new file mode 100644 index 00000000000..bfea1341443 --- /dev/null +++ b/.changeset/overlay-overflow.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +Overlay: Make overflow visible From 345d90ab20300dc79febd62bc4a8a191820d17c1 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Tue, 22 Mar 2022 10:35:56 +0100 Subject: [PATCH 26/44] Add docs for usePortal --- .changeset/overlay-use-portal.md | 5 +++++ docs/content/Overlay.mdx | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100644 .changeset/overlay-use-portal.md diff --git a/.changeset/overlay-use-portal.md b/.changeset/overlay-use-portal.md new file mode 100644 index 00000000000..5adf6ad9a64 --- /dev/null +++ b/.changeset/overlay-use-portal.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +Overlay: Add the option of rendering inline instead of a portal. diff --git a/docs/content/Overlay.mdx b/docs/content/Overlay.mdx index 2d7dbba946c..b1b03b93372 100644 --- a/docs/content/Overlay.mdx +++ b/docs/content/Overlay.mdx @@ -240,6 +240,13 @@ See the W3C accessibility recommendations for modals [here](https://www.w3.org/T } /> + If set to false, Overlay will be rendered inline instead of a Portal} + /> + ## Status From 21a157a20099deda43eeaa525fc7debdda953313 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Tue, 22 Mar 2022 10:54:21 +0100 Subject: [PATCH 27/44] add comments to overlayProps --- src/ActionMenu.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index aa2c1013017..bc27ab302ef 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -124,7 +124,11 @@ const Overlay: React.FC = ({children, align = 'start', sx: pro onOpen={openWithFocus} onClose={onClose} align={align} - overlayProps={{usePortal: false, sx: merge({zIndex: 2}, propsSx as SxProp), ...overlayProps}} + overlayProps={{ + usePortal: false, // render inline for natural focus management + sx: merge({zIndex: 1}, propsSx as SxProp), // render above overlay contents (story/internal-components-overlay--memex-nested-overlays) + ...overlayProps + }} focusZoneSettings={{focusOutBehavior: 'wrap'}} focusTrapSettings={{disabled: true}} > From ff413d9b2d35ab3aac799ec28fd5f36d84019626 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Tue, 22 Mar 2022 10:58:08 +0100 Subject: [PATCH 28/44] remove todo --- src/hooks/useMenuInitialFocus.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useMenuInitialFocus.ts b/src/hooks/useMenuInitialFocus.ts index e1de450d169..a5db601efac 100644 --- a/src/hooks/useMenuInitialFocus.ts +++ b/src/hooks/useMenuInitialFocus.ts @@ -35,7 +35,7 @@ export const useMenuInitialFocus = ( if (openingKey === 'mouse-click') { if (anchorRef.current) anchorRef.current.focus() - else throw new Error('For focus management, please attach anchorRef') // TODO: improve error + else throw new Error('For focus management, please attach anchorRef') } else if (['ArrowDown', 'Space', 'Enter'].includes(openingKey)) { const firstElement = iterable.next().value /** We push imperative focus to the next tick to prevent React's batching */ From bfbd8da518abcac48d7d1449275e2eec98e516e9 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Tue, 22 Mar 2022 12:03:44 +0100 Subject: [PATCH 29/44] Add overlay story with overflow --- src/stories/Overlay.stories.tsx | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/stories/Overlay.stories.tsx b/src/stories/Overlay.stories.tsx index 5c2ac9e50aa..cd4502f3f28 100644 --- a/src/stories/Overlay.stories.tsx +++ b/src/stories/Overlay.stories.tsx @@ -492,3 +492,52 @@ export const MemexIssueOverlay = () => { ) } + +export const LongContentWithOverflow = ({anchorSide}: OverlayProps) => { + const [isOpen, setIsOpen] = useState(false) + const buttonRef = useRef(null) + const anchorRef = useRef(null) + const closeOverlay = () => setIsOpen(false) + + return ( + + + {isOpen ? ( + + + + Field 1 + + + + Field 2 + + + + Field 3 + + + + Field 4 + + + + Field 5 + + + + + ) : null} + + ) +} From ad927b7b269d4db943f89d6c2127ffe4829e7baf Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Tue, 22 Mar 2022 12:21:51 +0100 Subject: [PATCH 30/44] tiny bit more coverage --- src/__tests__/ActionMenu.test.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/__tests__/ActionMenu.test.tsx b/src/__tests__/ActionMenu.test.tsx index 3e9991c7d08..c9c196e3fbe 100644 --- a/src/__tests__/ActionMenu.test.tsx +++ b/src/__tests__/ActionMenu.test.tsx @@ -174,6 +174,24 @@ describe('ActionMenu', () => { cleanup() }) + it('should select last element when ArrowUp is pressed after opening Menu with click', async () => { + const component = HTMLRender() + + const button = component.getByText('Toggle Menu') + button.focus() // browsers do this automatically on click, but tests don't + fireEvent.click(button) + expect(component.queryByRole('menu')).toBeInTheDocument() + + // button should be the active element + fireEvent.keyDown(document.activeElement!, {key: 'ArrowUp', code: 'ArrowUp'}) + + await waitFor(() => { + expect(component.getAllByRole('menuitem').pop()).toEqual(document.activeElement) + }) + + cleanup() + }) + it('should close the menu if Tab is pressed and move to next element', async () => { const component = HTMLRender( <> From 342138d59ef893f5e8e251e3138168313dbce60a Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Wed, 23 Mar 2022 15:55:12 +0100 Subject: [PATCH 31/44] rename usePortal to renderInPortal --- docs/content/Overlay.mdx | 2 +- src/ActionMenu.tsx | 2 +- src/Overlay.tsx | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/content/Overlay.mdx b/docs/content/Overlay.mdx index b1b03b93372..dc05d555469 100644 --- a/docs/content/Overlay.mdx +++ b/docs/content/Overlay.mdx @@ -241,7 +241,7 @@ See the W3C accessibility recommendations for modals [here](https://www.w3.org/T /> If set to false, Overlay will be rendered inline instead of a Portal} diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index bc27ab302ef..c178e61793e 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -125,7 +125,7 @@ const Overlay: React.FC = ({children, align = 'start', sx: pro onClose={onClose} align={align} overlayProps={{ - usePortal: false, // render inline for natural focus management + renderInPortal: false, // render inline for natural focus management sx: merge({zIndex: 1}, propsSx as SxProp), // render above overlay contents (story/internal-components-overlay--memex-nested-overlays) ...overlayProps }} diff --git a/src/Overlay.tsx b/src/Overlay.tsx index 71dd0b997d5..486029b76b3 100644 --- a/src/Overlay.tsx +++ b/src/Overlay.tsx @@ -100,7 +100,7 @@ type BaseOverlayProps = { preventFocusOnOpen?: boolean role?: AriaRole children?: React.ReactNode - usePortal?: boolean + renderInPortal?: boolean } type OwnOverlayProps = Merge @@ -138,7 +138,7 @@ const Overlay = React.forwardRef( anchorSide, portalContainerName, preventFocusOnOpen, - usePortal = true, + renderInPortal = true, ...rest }, forwardedRef @@ -181,10 +181,10 @@ const Overlay = React.forwardRef( ) }, [anchorSide, slideAnimationDistance, slideAnimationEasing, visibility]) - const Container = usePortal ? Portal : React.Fragment + const Container = renderInPortal ? Portal : React.Fragment return ( - + Date: Wed, 23 Mar 2022 19:26:54 +0100 Subject: [PATCH 32/44] Add story for position relative + overflow --- src/stories/ActionMenu/fixtures.stories.tsx | 146 +++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) diff --git a/src/stories/ActionMenu/fixtures.stories.tsx b/src/stories/ActionMenu/fixtures.stories.tsx index 2f56c87fe40..7ad2f823585 100644 --- a/src/stories/ActionMenu/fixtures.stories.tsx +++ b/src/stories/ActionMenu/fixtures.stories.tsx @@ -11,7 +11,9 @@ import { ActionMenu, ActionList, Button, - IconButton + IconButton, + TabNav, + Checkbox } from '../..' import { ServerIcon, @@ -702,3 +704,145 @@ export function TabTest(): JSX.Element { ) } + +export function InsideContainer(): JSX.Element { + const [open, setOpen] = React.useState(false) + const [fixed, setFixed] = React.useState(false) + const anchorRef = React.createRef() + + return ( + <> +

Inside a container with overflow:hidden & position:relative

+ + + + + React + + + + + + + + + + + + + Rename view + + + + + + Save changes to new view + + + + + + Delete view + + + + + + + CSS + Rails + + + setFixed(!fixed)} /> + + fix with override: {`nav { overflow: initial; }`} + + +
+
+
+ alternatively, Do not render Menu.Overlay inside position:relative by using external anchor + + + + React + + + setOpen(!open)} + aria-expanded={open} + aria-haspopup="true" + /> + + + + + + + + + + + Rename view + + + + + + Save changes to new view + + + + + + Delete view + + + + + + CSS + Rails + + + ) +} From bc7923936e0c433ceb902da6b861c41c6fc97291 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 16:24:28 +0100 Subject: [PATCH 33/44] change z-index to 11, add comment why --- src/ActionMenu.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index c178e61793e..fb9a3b1f948 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -126,7 +126,16 @@ const Overlay: React.FC = ({children, align = 'start', sx: pro align={align} overlayProps={{ renderInPortal: false, // render inline for natural focus management - sx: merge({zIndex: 1}, propsSx as SxProp), // render above overlay contents (story/internal-components-overlay--memex-nested-overlays) + sx: merge( + { + // why 11? for a lack of a better z-index strategy in the repo, I am following the popular layers approach, + // which leaves room for the application to claim the z-index "in between layers". + // inspiration: https://getbootstrap.com/docs/5.0/layout/z-index + // story: https://primer.style/react/storybook?path=/story/internal-components-overlay--memex-nested-overlays + zIndex: 11 + }, + propsSx as SxProp + ), ...overlayProps }} focusZoneSettings={{focusOutBehavior: 'wrap'}} From 48cfaaa857f180ff3a702d2e2d433fdaf5bc9e0d Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:26:11 +0100 Subject: [PATCH 34/44] undo render inline --- docs/content/Overlay.mdx | 6 ------ src/ActionMenu.tsx | 15 +-------------- src/Overlay.tsx | 8 ++------ 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/docs/content/Overlay.mdx b/docs/content/Overlay.mdx index dc05d555469..12f45ef8064 100644 --- a/docs/content/Overlay.mdx +++ b/docs/content/Overlay.mdx @@ -240,12 +240,6 @@ See the W3C accessibility recommendations for modals [here](https://www.w3.org/T } /> - If set to false, Overlay will be rendered inline instead of a Portal} - /> diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index fb9a3b1f948..f861da0c461 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -124,20 +124,7 @@ const Overlay: React.FC = ({children, align = 'start', sx: pro onOpen={openWithFocus} onClose={onClose} align={align} - overlayProps={{ - renderInPortal: false, // render inline for natural focus management - sx: merge( - { - // why 11? for a lack of a better z-index strategy in the repo, I am following the popular layers approach, - // which leaves room for the application to claim the z-index "in between layers". - // inspiration: https://getbootstrap.com/docs/5.0/layout/z-index - // story: https://primer.style/react/storybook?path=/story/internal-components-overlay--memex-nested-overlays - zIndex: 11 - }, - propsSx as SxProp - ), - ...overlayProps - }} + overlayProps={overlayProps} focusZoneSettings={{focusOutBehavior: 'wrap'}} focusTrapSettings={{disabled: true}} > diff --git a/src/Overlay.tsx b/src/Overlay.tsx index 486029b76b3..58f5463a0e7 100644 --- a/src/Overlay.tsx +++ b/src/Overlay.tsx @@ -100,7 +100,6 @@ type BaseOverlayProps = { preventFocusOnOpen?: boolean role?: AriaRole children?: React.ReactNode - renderInPortal?: boolean } type OwnOverlayProps = Merge @@ -138,7 +137,6 @@ const Overlay = React.forwardRef( anchorSide, portalContainerName, preventFocusOnOpen, - renderInPortal = true, ...rest }, forwardedRef @@ -181,10 +179,8 @@ const Overlay = React.forwardRef( ) }, [anchorSide, slideAnimationDistance, slideAnimationEasing, visibility]) - const Container = renderInPortal ? Portal : React.Fragment - return ( - + ( } as React.CSSProperties } /> - + ) } ) as PolymorphicForwardRefComponent<'div', OwnOverlayProps> From f99b1974bb2f227a382b20ff4faea83379fdfe9a Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:30:52 +0100 Subject: [PATCH 35/44] remove story --- src/stories/ActionMenu/fixtures.stories.tsx | 142 -------------------- 1 file changed, 142 deletions(-) diff --git a/src/stories/ActionMenu/fixtures.stories.tsx b/src/stories/ActionMenu/fixtures.stories.tsx index 7ad2f823585..4757f1cd4ed 100644 --- a/src/stories/ActionMenu/fixtures.stories.tsx +++ b/src/stories/ActionMenu/fixtures.stories.tsx @@ -704,145 +704,3 @@ export function TabTest(): JSX.Element { ) } - -export function InsideContainer(): JSX.Element { - const [open, setOpen] = React.useState(false) - const [fixed, setFixed] = React.useState(false) - const anchorRef = React.createRef() - - return ( - <> -

Inside a container with overflow:hidden & position:relative

- - - - - React - - - - - - - - - - - - - Rename view - - - - - - Save changes to new view - - - - - - Delete view - - - - - - - CSS - Rails - - - setFixed(!fixed)} /> - - fix with override: {`nav { overflow: initial; }`} - - -
-
-
- alternatively, Do not render Menu.Overlay inside position:relative by using external anchor - - - - React - - - setOpen(!open)} - aria-expanded={open} - aria-haspopup="true" - /> - - - - - - - - - - - Rename view - - - - - - Save changes to new view - - - - - - Delete view - - - - - - CSS - Rails - - - ) -} From 872d84c53bf10e01a12c78c2708167e539eacdf1 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:41:48 +0100 Subject: [PATCH 36/44] wrap async test in waitFor --- src/__tests__/ActionMenu.test.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/__tests__/ActionMenu.test.tsx b/src/__tests__/ActionMenu.test.tsx index c9c196e3fbe..01324e6a99e 100644 --- a/src/__tests__/ActionMenu.test.tsx +++ b/src/__tests__/ActionMenu.test.tsx @@ -209,9 +209,11 @@ describe('ActionMenu', () => { expect(component.getAllByRole('menuitem')[0]).toEqual(document.activeElement) - userEvent.tab() - expect(component.getByPlaceholderText('next focusable element')).toEqual(document.activeElement) - expect(component.queryByRole('menu')).not.toBeInTheDocument() + await waitFor(() => { + userEvent.tab() + expect(document.activeElement).toEqual(component.getByPlaceholderText('next focusable element')) + expect(component.queryByRole('menu')).not.toBeInTheDocument() + }) cleanup() }) From 1ac46a47f752bdf47fc02aa3ecd2ee2270783eb6 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:45:32 +0100 Subject: [PATCH 37/44] unused variable --- src/ActionMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ActionMenu.tsx b/src/ActionMenu.tsx index f861da0c461..6f1a6574d35 100644 --- a/src/ActionMenu.tsx +++ b/src/ActionMenu.tsx @@ -104,7 +104,7 @@ type MenuOverlayProps = Partial & */ children: React.ReactElement[] | React.ReactElement } -const Overlay: React.FC = ({children, align = 'start', sx: propsSx = {}, ...overlayProps}) => { +const Overlay: React.FC = ({children, align = 'start', ...overlayProps}) => { // we typecast anchorRef as required instead of optional // because we know that we're setting it in context in Menu const {anchorRef, renderAnchor, anchorId, open, onOpen, onClose} = React.useContext(MenuContext) as MandateProps< From 7a65d7e634ad52f66bd883fe2d17db40b9601262 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:46:41 +0100 Subject: [PATCH 38/44] remove overlay overflow --- src/Overlay.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Overlay.tsx b/src/Overlay.tsx index 58f5463a0e7..cc9c721213c 100644 --- a/src/Overlay.tsx +++ b/src/Overlay.tsx @@ -63,7 +63,7 @@ const StyledOverlay = styled.div` max-height: ${props => props.maxHeight && heightMap[props.maxHeight]}; width: ${props => widthMap[props.width || 'auto']}; border-radius: 12px; - overflow: visible; + overflow: hidden; animation: overlay-appear ${animationDuration}ms ${get('animation.easeOutCubic')}; @keyframes overlay-appear { From 1b01aa91106bfaeae095c23106e6a560583e054f Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:47:43 +0100 Subject: [PATCH 39/44] Delete overlay-use-portal.md --- .changeset/overlay-use-portal.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/overlay-use-portal.md diff --git a/.changeset/overlay-use-portal.md b/.changeset/overlay-use-portal.md deleted file mode 100644 index 5adf6ad9a64..00000000000 --- a/.changeset/overlay-use-portal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -Overlay: Add the option of rendering inline instead of a portal. From 530774e01b31be21a9da1559dc14ab512178529c Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:48:03 +0100 Subject: [PATCH 40/44] Delete overlay-overflow.md --- .changeset/overlay-overflow.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/overlay-overflow.md diff --git a/.changeset/overlay-overflow.md b/.changeset/overlay-overflow.md deleted file mode 100644 index bfea1341443..00000000000 --- a/.changeset/overlay-overflow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -Overlay: Make overflow visible From ec0d9430ac8aedaef984c27482a2dc1b644897cd Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:48:28 +0100 Subject: [PATCH 41/44] remove duplicate sx row --- docs/content/Overlay.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/content/Overlay.mdx b/docs/content/Overlay.mdx index 12f45ef8064..2d7dbba946c 100644 --- a/docs/content/Overlay.mdx +++ b/docs/content/Overlay.mdx @@ -240,7 +240,6 @@ See the W3C accessibility recommendations for modals [here](https://www.w3.org/T } /> - ## Status From 26afdceafa14be26bbbdb0f71aaeee95b22d2f98 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:51:12 +0100 Subject: [PATCH 42/44] update snapshots --- src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap b/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap index 68e29b11175..82b17ad7de6 100644 --- a/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap +++ b/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap @@ -175,7 +175,7 @@ exports[`AnchoredOverlay should render consistently when open 1`] = ` height: auto; width: auto; border-radius: 12px; - overflow: visible; + overflow: hidden; -webkit-animation: overlay-appear 200ms cubic-bezier(0.33,1,0.68,1); animation: overlay-appear 200ms cubic-bezier(0.33,1,0.68,1); visibility: var(--styled-overlay-visibility); From 20013b2bdcf05a7dc59eabcc8126240e70d5ddf4 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 17:51:41 +0100 Subject: [PATCH 43/44] remove irrelevant story --- src/stories/Overlay.stories.tsx | 49 --------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/stories/Overlay.stories.tsx b/src/stories/Overlay.stories.tsx index cd4502f3f28..5c2ac9e50aa 100644 --- a/src/stories/Overlay.stories.tsx +++ b/src/stories/Overlay.stories.tsx @@ -492,52 +492,3 @@ export const MemexIssueOverlay = () => { ) } - -export const LongContentWithOverflow = ({anchorSide}: OverlayProps) => { - const [isOpen, setIsOpen] = useState(false) - const buttonRef = useRef(null) - const anchorRef = useRef(null) - const closeOverlay = () => setIsOpen(false) - - return ( - - - {isOpen ? ( - - - - Field 1 - - - - Field 2 - - - - Field 3 - - - - Field 4 - - - - Field 5 - - - - - ) : null} - - ) -} From 44867175a864b5f88bd0ea9e076e38f40651c744 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Fri, 25 Mar 2022 18:23:40 +0100 Subject: [PATCH 44/44] remove unused imports --- src/stories/ActionMenu/fixtures.stories.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/stories/ActionMenu/fixtures.stories.tsx b/src/stories/ActionMenu/fixtures.stories.tsx index 4757f1cd4ed..2f56c87fe40 100644 --- a/src/stories/ActionMenu/fixtures.stories.tsx +++ b/src/stories/ActionMenu/fixtures.stories.tsx @@ -11,9 +11,7 @@ import { ActionMenu, ActionList, Button, - IconButton, - TabNav, - Checkbox + IconButton } from '../..' import { ServerIcon,