Skip to content

Commit

Permalink
[MenuList] Fix to allow conditional rendering for a menu item under L…
Browse files Browse the repository at this point in the history
…istSubheader (#36890)

Co-authored-by: ZeeshanTamboli <[email protected]>
  • Loading branch information
danielplewes and ZeeshanTamboli committed May 30, 2023
1 parent ea0228b commit d00243f
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 0 deletions.
7 changes: 7 additions & 0 deletions packages/mui-material/src/MenuList/MenuList.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,13 @@ const MenuList = React.forwardRef(function MenuList(props, ref) {
// item and use the first valid item as a fallback
React.Children.forEach(children, (child, index) => {
if (!React.isValidElement(child)) {
if (activeItemIndex === index) {
activeItemIndex += 1;
if (activeItemIndex >= children.length) {
// there are no focusable items within the list.
activeItemIndex = -1;
}
}
return;
}

Expand Down
56 changes: 56 additions & 0 deletions packages/mui-material/src/Select/Select.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,62 @@ describe('<Select />', () => {
expect(options[5]).to.have.attribute('aria-selected', 'true');
});
});

describe('when the second child is null', () => {
it('first selectable option is focused to use the arrow', () => {
const { getAllByRole } = render(
<Select defaultValue="" open>
<ListSubheader>Category 1</ListSubheader>
{null}
<MenuItem value={1}>Option 1</MenuItem>
<MenuItem value={2}>Option 2</MenuItem>
<ListSubheader>Category 2</ListSubheader>
<MenuItem value={3}>Option 3</MenuItem>
<MenuItem value={4}>Option 4</MenuItem>
</Select>,
);

const options = getAllByRole('option');
expect(options[1]).to.have.attribute('tabindex', '0');

act(() => {
fireEvent.keyDown(options[1], { key: 'ArrowDown' });
fireEvent.keyDown(options[2], { key: 'ArrowDown' });
fireEvent.keyDown(options[4], { key: 'Enter' });
});

expect(options[4]).to.have.attribute('aria-selected', 'true');
});
});

['', 0, false, undefined, NaN].forEach((value) =>
describe(`when the second child is conditionally rendering with "${value}"`, () => {
it('first selectable option is focused to use the arrow', () => {
const { getAllByRole } = render(
<Select defaultValue="" open>
<ListSubheader>Category 1</ListSubheader>
{value && <MenuItem value={1}>One</MenuItem>}
<MenuItem value={1}>Option 1</MenuItem>
<MenuItem value={2}>Option 2</MenuItem>
<ListSubheader>Category 2</ListSubheader>
<MenuItem value={3}>Option 3</MenuItem>
<MenuItem value={4}>Option 4</MenuItem>
</Select>,
);

const options = getAllByRole('option');
expect(options[1]).to.have.attribute('tabindex', '0');

act(() => {
fireEvent.keyDown(options[1], { key: 'ArrowDown' });
fireEvent.keyDown(options[2], { key: 'ArrowDown' });
fireEvent.keyDown(options[4], { key: 'Enter' });
});

expect(options[4]).to.have.attribute('aria-selected', 'true');
});
}),
);
});

describe('when the first child is a ListSubheader wrapped in a custom component', () => {
Expand Down

0 comments on commit d00243f

Please sign in to comment.