{
it('should open Menu on MenuButton click', async () => {
const component = HTMLRender(
)
- const button = component.getByText('Toggle Menu')
- fireEvent.click(button)
+ const button = component.getByRole('button')
+
+ const user = userEvent.setup()
+ await user.click(button)
+
expect(component.getByRole('menu')).toBeInTheDocument()
cleanup()
})
it('should open Menu on MenuButton keypress', async () => {
const component = HTMLRender(
)
- const button = component.getByText('Toggle Menu')
+ const button = component.getByRole('button')
+
+ const user = userEvent.setup()
+ button.focus()
+ await user.keyboard('{Enter}')
- // 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})
expect(component.getByRole('menu')).toBeInTheDocument()
cleanup()
})
it('should close Menu on selecting an action with click', async () => {
const component = HTMLRender(
)
- const button = component.getByText('Toggle Menu')
+ const button = component.getByRole('button')
- fireEvent.click(button)
- const menuItems = await waitFor(() => component.getAllByRole('menuitem'))
- fireEvent.click(menuItems[0])
- expect(component.queryByRole('menu')).toBeNull()
+ const user = userEvent.setup()
+ await user.click(button)
+
+ const menuItems = component.getAllByRole('menuitem')
+ await user.click(menuItems[0])
+ expect(component.queryByRole('menu')).toBeNull()
cleanup()
})
it('should close Menu on selecting an action with Enter', async () => {
const component = HTMLRender(
)
- const button = component.getByText('Toggle Menu')
+ const button = component.getByRole('button')
- fireEvent.click(button)
- const menuItems = await waitFor(() => component.getAllByRole('menuitem'))
- fireEvent.keyPress(menuItems[0], {key: 'Enter', charCode: 13})
- expect(component.queryByRole('menu')).toBeNull()
+ const user = userEvent.setup()
+ await user.click(button)
+
+ const menuItems = component.getAllByRole('menuitem')
+ menuItems[0].focus()
+ await user.keyboard('{Enter}')
+ expect(component.queryByRole('menu')).toBeNull()
cleanup()
})
it('should not close Menu if event is prevented', async () => {
const component = HTMLRender(
)
- const button = component.getByText('Toggle Menu')
+ const button = component.getByRole('button')
+
+ const user = userEvent.setup()
+ await user.click(button)
+
+ const menuItems = component.getAllByRole('menuitem')
+ await user.click(menuItems[3])
- fireEvent.click(button)
- const menuItems = await waitFor(() => component.getAllByRole('menuitem'))
- fireEvent.click(menuItems[3])
// menu should still be open
expect(component.getByRole('menu')).toBeInTheDocument()
@@ -109,14 +122,16 @@ describe('ActionMenu', () => {
)
const button = component.getByLabelText('Field type')
- fireEvent.click(button)
+
+ const user = userEvent.setup()
+ await user.click(button)
// select first item by role, that would close the menu
- fireEvent.click(component.getAllByRole('menuitemradio')[0])
+ await user.click(component.getAllByRole('menuitemradio')[0])
expect(component.queryByRole('menu')).not.toBeInTheDocument()
// open menu again and check if the first option is checked
- fireEvent.click(button)
+ await user.click(button)
expect(component.getAllByRole('menuitemradio')[0]).toHaveAttribute('aria-checked', 'true')
cleanup()
})
@@ -128,7 +143,9 @@ describe('ActionMenu', () => {
)
const button = component.getByLabelText('Group by')
- fireEvent.click(button)
+
+ const user = userEvent.setup()
+ await user.click(button)
expect(component.getByLabelText('Status')).toHaveAttribute('role', 'menuitemradio')
expect(component.getByLabelText('Clear Group by')).toHaveAttribute('role', 'menuitem')
@@ -136,6 +153,82 @@ describe('ActionMenu', () => {
cleanup()
})
+ it('should keep focus on Button when menu is opened with click', async () => {
+ const component = HTMLRender(
)
+ const button = component.getByRole('button')
+
+ const user = userEvent.setup()
+ await user.tab() // tab into the story, this should focus on the first button
+ expect(button).toEqual(document.activeElement) // trust, but verify
+
+ await user.click(button)
+ expect(component.queryByRole('menu')).toBeInTheDocument()
+ expect(document.activeElement).toEqual(button)
+
+ cleanup()
+ })
+
+ it('should select first element when ArrowDown is pressed after opening Menu with click', async () => {
+ const component = HTMLRender(
)
+ const button = component.getByRole('button')
+
+ const user = userEvent.setup()
+ await user.click(button)
+
+ expect(component.queryByRole('menu')).toBeInTheDocument()
+
+ // assumes button is the active element at this point
+ await user.keyboard('{ArrowDown}')
+
+ expect(component.getAllByRole('menuitem')[0]).toEqual(document.activeElement)
+ cleanup()
+ })
+
+ it('should select last element when ArrowUp is pressed after opening Menu with click', async () => {
+ const component = HTMLRender(
)
+
+ const button = component.getByRole('button')
+
+ const user = userEvent.setup()
+ await user.click(button)
+
+ expect(component.queryByRole('menu')).toBeInTheDocument()
+
+ // button should be the active element
+ // assumes button is the active element at this point
+ await user.keyboard('{ArrowUp}')
+
+ 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(
+ <>
+
+
+ >
+ )
+ const anchor = component.getByRole('button')
+
+ const user = userEvent.setup()
+ await user.tab() // tab into the story, this should focus on the first button
+ expect(anchor).toEqual(document.activeElement) // trust, but verify
+
+ await user.keyboard('{Enter}')
+ expect(component.queryByRole('menu')).toBeInTheDocument()
+ expect(component.getAllByRole('menuitem')[0]).toEqual(document.activeElement)
+
+ // TODO: revisit why we need this waitFor
+ await waitFor(async () => {
+ await user.tab()
+ expect(document.activeElement).toEqual(component.getByPlaceholderText('next focusable element'))
+ expect(component.queryByRole('menu')).not.toBeInTheDocument()
+ })
+
+ cleanup()
+ })
+
it('should have no axe violations', async () => {
const {container} = HTMLRender(
)
const results = await axe(container)
diff --git a/src/__tests__/ConfirmationDialog.test.tsx b/src/__tests__/ConfirmationDialog.test.tsx
index 7de379edf25..7b62f0b8290 100644
--- a/src/__tests__/ConfirmationDialog.test.tsx
+++ b/src/__tests__/ConfirmationDialog.test.tsx
@@ -15,7 +15,7 @@ import {behavesAsComponent, checkExports} from '../utils/testing'
expect.extend(toHaveNoViolations)
-const Basic = () => {
+const Basic = ({confirmButtonType}: Pick
, 'confirmButtonType'>) => {
const [isOpen, setIsOpen] = useState(false)
const buttonRef = useRef(null)
const onDialogClose = useCallback(() => setIsOpen(false), [])
@@ -32,6 +32,7 @@ const Basic = () => {
onClose={onDialogClose}
cancelButtonContent="Secondary"
confirmButtonContent="Primary"
+ confirmButtonType={confirmButtonType}
>
Lorem ipsum dolor sit Pippin good dog.
@@ -95,7 +96,7 @@ describe('ConfirmationDialog', () => {
cleanup()
})
- it('focuses the primary action when opened', async () => {
+ it('focuses the primary action when opened and the confirmButtonType is not set', async () => {
const {getByText} = HTMLRender()
act(() => {
fireEvent.click(getByText('Show dialog'))
@@ -105,6 +106,26 @@ describe('ConfirmationDialog', () => {
cleanup()
})
+ it('focuses the primary action when opened and the confirmButtonType is not danger', async () => {
+ const {getByText} = HTMLRender()
+ act(() => {
+ fireEvent.click(getByText('Show dialog'))
+ })
+ expect(getByText('Primary')).toEqual(document.activeElement)
+ expect(getByText('Secondary')).not.toEqual(document.activeElement)
+ cleanup()
+ })
+
+ it('focuses the secondary action when opened and the confirmButtonType is danger', async () => {
+ const {getByText} = HTMLRender()
+ act(() => {
+ fireEvent.click(getByText('Show dialog'))
+ })
+ expect(getByText('Primary')).not.toEqual(document.activeElement)
+ expect(getByText('Secondary')).toEqual(document.activeElement)
+ cleanup()
+ })
+
it('supports nested `focusTrap`s', async () => {
const {getByText} = HTMLRender()
act(() => {
diff --git a/src/__tests__/hooks/useMenuInitialFocus.test.tsx b/src/__tests__/hooks/useMenuInitialFocus.test.tsx
index bc077603ddf..009698a94e4 100644
--- a/src/__tests__/hooks/useMenuInitialFocus.test.tsx
+++ b/src/__tests__/hooks/useMenuInitialFocus.test.tsx
@@ -7,11 +7,12 @@ const Component = () => {
const onOpen = () => setOpen(!open)
const containerRef = React.createRef()
- const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef)
+ const anchorRef = React.createRef()
+ useMenuInitialFocus(open, containerRef, anchorRef)
return (
<>
-