Skip to content

Commit

Permalink
[Autocomplete] Fix navigation issue on mouse hover (#35196)
Browse files Browse the repository at this point in the history
  • Loading branch information
sai6855 committed Apr 4, 2023
1 parent d24ac60 commit 6776cb2
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 9 deletions.
17 changes: 10 additions & 7 deletions packages/mui-base/src/useAutocomplete/useAutocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -965,12 +965,15 @@ export default function useAutocomplete(props) {
}
};

const handleOptionMouseOver = (event) => {
setHighlightedIndex({
event,
index: Number(event.currentTarget.getAttribute('data-option-index')),
reason: 'mouse',
});
const handleOptionMouseMove = (event) => {
const index = Number(event.currentTarget.getAttribute('data-option-index'));
if (highlightedIndexRef.current !== index) {
setHighlightedIndex({
event,
index,
reason: 'mouse',
});
}
};

const handleOptionTouchStart = (event) => {
Expand Down Expand Up @@ -1144,7 +1147,7 @@ export default function useAutocomplete(props) {
tabIndex: -1,
role: 'option',
id: `${id}-option-${index}`,
onMouseOver: handleOptionMouseOver,
onMouseMove: handleOptionMouseMove,
onClick: handleOptionClick,
onTouchStart: handleOptionTouchStart,
'data-option-index': index,
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-joy/src/Autocomplete/Autocomplete.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2049,7 +2049,7 @@ describe('Joy <Autocomplete />', () => {
<Autocomplete onHighlightChange={handleHighlightChange} options={options} open autoFocus />,
);
const firstOption = getAllByRole('option')[0];
fireEvent.mouseOver(firstOption);
fireEvent.mouseMove(firstOption);
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
React.version.startsWith('18') ? 4 : 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2520,7 +2520,7 @@ describe('<Autocomplete />', () => {
/>,
);
const firstOption = getAllByRole('option')[0];
fireEvent.mouseOver(firstOption);
fireEvent.mouseMove(firstOption);
expect(handleHighlightChange.callCount).to.equal(
// FIXME: highlighted index implementation should be implemented using React not the DOM.
React.version.startsWith('18') ? 4 : 3,
Expand Down
15 changes: 15 additions & 0 deletions test/e2e/fixtures/Autocomplete/HoverJoyAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as React from 'react';
import Autocomplete from '@mui/joy/Autocomplete';

function HoverJoyAutocomplete() {
return (
<Autocomplete
open
options={['one', 'two', 'three', 'four', 'five']}
sx={{ width: 300 }}
slotProps={{ listbox: { sx: { height: '100px' } } }}
/>
);
}

export default HoverJoyAutocomplete;
17 changes: 17 additions & 0 deletions test/e2e/fixtures/Autocomplete/HoverMaterialAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import Autocomplete from '@mui/material/Autocomplete';
import TextField from '@mui/material/TextField';

function HoverMaterialAutocomplete() {
return (
<Autocomplete
open
options={['one', 'two', 'three', 'four', 'five']}
sx={{ width: 300 }}
ListboxProps={{ sx: { height: '100px' } }}
renderInput={(params) => <TextField {...params} />}
/>
);
}

export default HoverMaterialAutocomplete;
46 changes: 46 additions & 0 deletions test/e2e/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,52 @@ describe('e2e', () => {
});
});

describe('<Autocomplete/>', () => {
it('[Material Autocomplete] should highlight correct option when initial navigation through options starts from mouse move', async () => {
await renderFixture('Autocomplete/HoverMaterialAutocomplete');

const combobox = (await screen.getByRole('combobox'))!;
await combobox.click();

const firstOption = (await screen.getByText('one'))!;

const dimensions = (await firstOption.boundingBox())!;

await page.mouse.move(dimensions.x + 10, dimensions.y + 10); // moves to 1st option
await page.keyboard.down('ArrowDown'); // moves to 2nd option
await page.keyboard.down('ArrowDown'); // moves to 3rd option
await page.keyboard.down('ArrowDown'); // moves to 4th option

const listbox = (await screen.getByRole('listbox'))!;
const focusedOption = (await listbox.$('.Mui-focused'))!;
const focusedOptionText = await focusedOption.innerHTML();

expect(focusedOptionText).to.equal('four');
});

it('[Joy Autocomplete] should highlight correct option when initial navigation through options starts from mouse move', async () => {
await renderFixture('Autocomplete/HoverJoyAutocomplete');

const combobox = (await screen.getByRole('combobox'))!;
await combobox.click();

const firstOption = (await screen.getByText('one'))!;

const dimensions = (await firstOption.boundingBox())!;

await page.mouse.move(dimensions.x + 10, dimensions.y + 10); // moves to 1st option
await page.keyboard.down('ArrowDown'); // moves to 2nd option
await page.keyboard.down('ArrowDown'); // moves to 3rd option
await page.keyboard.down('ArrowDown'); // moves to 4th option

const listbox = (await screen.getByRole('listbox'))!;
const focusedOption = (await listbox.$('.Joy-focused'))!;
const focusedOptionText = await focusedOption.innerHTML();

expect(focusedOptionText).to.equal('four');
});
});

describe('<TextareaAutosize />', () => {
// https://github.com/mui/material-ui/issues/32640
it('should handle suspense without error', async () => {
Expand Down

0 comments on commit 6776cb2

Please sign in to comment.