Skip to content

Commit 168b56e

Browse files
committed
fix: scroll to selected option when filtering enabled
1 parent 8568662 commit 168b56e

File tree

2 files changed

+29
-18
lines changed

2 files changed

+29
-18
lines changed

src/select/__tests__/use-select.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ describe('useSelect', () => {
449449
});
450450
});
451451

452-
test('select with filter should open and NOT navigate to selected option', () => {
452+
test('select with filter should open and NOT focus the selected option', () => {
453453
const hook = renderHook(useSelect, {
454454
initialProps: { ...initialProps, selectedOptions: [{ value: 'child1' }] },
455455
});

src/select/utils/use-select.ts

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { usePrevious } from '../../internal/hooks/use-previous';
2121
import { FilterProps } from '../parts/filter';
2222
import { ItemProps } from '../parts/item';
2323
import { connectOptionsByValue } from './connect-options';
24+
import scrollToIndex from './scroll-to-index';
2425

2526
export type MenuProps = Omit<OptionsListProps, 'children'> & { ref: React.RefObject<HTMLDivElement> };
2627
export type GetOptionProps = (option: DropdownOption, index: number) => ItemProps;
@@ -305,24 +306,33 @@ export function useSelect({
305306

306307
const prevOpen = usePrevious<boolean>(isOpen);
307308
useEffect(() => {
308-
// highlight the first selected option, when opening the Select component without filter input
309-
// keep the focus in the filter input when opening, so that screenreader can recognize the combobox
310-
if (isOpen && !prevOpen && options.length > 0 && !hasFilter) {
311-
if (openedWithKeyboard) {
312-
if (__selectedOptions[0]) {
313-
highlightOptionWithKeyboard(__selectedOptions[0]);
314-
} else {
315-
goHomeWithKeyboard();
316-
}
317-
} else {
318-
if (!__selectedOptions[0] || !options.includes(__selectedOptions[0])) {
319-
highlightFirstOptionWithMouse();
320-
} else {
321-
const highlightedIndex = options.indexOf(__selectedOptions[0]);
322-
setHighlightedIndexWithMouse(highlightedIndex, true);
323-
}
324-
}
309+
if (!isOpen || prevOpen || options.length === 0) {
310+
return;
311+
}
312+
313+
const selectedOption = __selectedOptions[0];
314+
const hasSelectedOption = selectedOption && options.includes(selectedOption);
315+
const selectedIndex = hasSelectedOption ? options.indexOf(selectedOption) : -1;
316+
317+
// With filter: scroll selected option into view without highlighting to keep focus on filter input for accessibility
318+
if (hasFilter && hasSelectedOption && menuRef.current) {
319+
scrollToIndex({ index: selectedIndex, menuEl: menuRef.current });
320+
return;
321+
}
322+
323+
// With filter but no selected option: do nothing, keep focus on filter
324+
if (hasFilter) {
325+
return;
325326
}
327+
328+
// Without filter, opened with keyboard: highlight selected option or go to first option
329+
if (openedWithKeyboard) {
330+
hasSelectedOption ? highlightOptionWithKeyboard(selectedOption) : goHomeWithKeyboard();
331+
return;
332+
}
333+
334+
// Without filter, opened with mouse: highlight and focus selected option or first option
335+
hasSelectedOption ? setHighlightedIndexWithMouse(selectedIndex, true) : highlightFirstOptionWithMouse();
326336
}, [
327337
isOpen,
328338
__selectedOptions,
@@ -335,6 +345,7 @@ export function useSelect({
335345
options,
336346
prevOpen,
337347
hasFilter,
348+
menuRef,
338349
]);
339350

340351
useEffect(() => {

0 commit comments

Comments
 (0)