Skip to content

Commit

Permalink
[MenuUnstyled] Fix keyboard accessibility of menu items (#33145)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaldudak committed Jun 23, 2022
1 parent 9cfcd8c commit cb74465
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 12 deletions.
73 changes: 71 additions & 2 deletions packages/mui-base/src/ListboxUnstyled/useListbox.test.tsx
@@ -1,7 +1,8 @@
import * as React from 'react';
import { useListbox } from '@mui/base/ListboxUnstyled';
import { expect } from 'chai';
import { createRenderer } from 'test/utils';
import { SinonSpy, spy } from 'sinon';
import { useListbox } from '@mui/base/ListboxUnstyled';
import { createRenderer, createEvent, fireEvent } from 'test/utils';

describe('useListbox', () => {
const { render } = createRenderer();
Expand Down Expand Up @@ -56,4 +57,72 @@ describe('useListbox', () => {
expect(listboxes[0].id).not.to.equal(listboxes[1].id);
});
});

describe('preventing default behavior on keyDown', () => {
['ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown', 'Enter', ' '].forEach((key) =>
it(`prevents default behavior when ${key} is pressed in activeDescendant focus management mode`, () => {
const Listbox = () => {
const { getRootProps } = useListbox({ options: [], focusManagement: 'activeDescendant' });
return <div {...getRootProps()} />;
};

const { getByRole } = render(<Listbox />);
const listbox = getByRole('listbox');
listbox.focus();

const event = createEvent.keyDown(listbox, {
key,
});

event.preventDefault = spy();
fireEvent(listbox, event);

expect((event.preventDefault as SinonSpy).calledOnce).to.equal(true);
}),
);

['ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown'].forEach((key) =>
it(`prevents default behavior when ${key} is pressed in DOM focus management mode`, () => {
const Listbox = () => {
const { getRootProps } = useListbox({ options: [], focusManagement: 'DOM' });
return <div {...getRootProps()} />;
};

const { getByRole } = render(<Listbox />);
const listbox = getByRole('listbox');
listbox.focus();

const event = createEvent.keyDown(listbox, {
key,
});

event.preventDefault = spy();
fireEvent(listbox, event);

expect((event.preventDefault as SinonSpy).calledOnce).to.equal(true);
}),
);

['Enter', ' '].forEach((key) =>
it(`does not prevent default behavior when ${key} is pressed in DOM focus management mode`, () => {
const Listbox = () => {
const { getRootProps } = useListbox({ options: [], focusManagement: 'DOM' });
return <div {...getRootProps()} />;
};

const { getByRole } = render(<Listbox />);
const listbox = getByRole('listbox');
listbox.focus();

const event = createEvent.keyDown(listbox, {
key,
});

event.preventDefault = spy();
fireEvent(listbox, event);

expect((event.preventDefault as SinonSpy).notCalled).to.equal(true);
}),
);
});
});
19 changes: 9 additions & 10 deletions packages/mui-base/src/ListboxUnstyled/useListbox.ts
Expand Up @@ -160,16 +160,15 @@ export default function useListbox<TOption>(props: UseListboxParameters<TOption>
return;
}

const keysToPreventDefault = [
' ',
'Enter',
'ArrowUp',
'ArrowDown',
'Home',
'End',
'PageUp',
'PageDown',
];
const keysToPreventDefault = ['ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown'];

if (focusManagement === 'activeDescendant') {
// When the child element is focused using the activeDescendant attribute,
// the listbox handles keyboard events on its behalf.
// We have to `preventDefault()` is this case to prevent the browser from
// scrolling the view when space is pressed or submitting forms when enter is pressed.
keysToPreventDefault.push(' ', 'Enter');
}

if (keysToPreventDefault.includes(event.key)) {
event.preventDefault();
Expand Down

0 comments on commit cb74465

Please sign in to comment.