Skip to content

Commit ec182e5

Browse files
authored
fix: allow keyboard navigation on selected options when maxCount is reached (#1088)
1 parent 27ac66f commit ec182e5

File tree

2 files changed

+78
-12
lines changed

2 files changed

+78
-12
lines changed

src/OptionList.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
8585
listRef.current?.scrollTo(typeof args === 'number' ? { index: args } : args);
8686
};
8787

88+
// https://github.com/ant-design/ant-design/issues/34975
89+
const isSelected = React.useCallback(
90+
(value: RawValueType) => {
91+
if (mode === 'combobox') {
92+
return false;
93+
}
94+
return rawValues.has(value);
95+
},
96+
[mode, [...rawValues].toString(), rawValues.size],
97+
);
98+
8899
// ========================== Active ==========================
89100
const getEnabledActiveIndex = (index: number, offset: number = 1): number => {
90101
const len = memoFlattenOptions.length;
@@ -94,7 +105,7 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
94105

95106
const { group, data } = memoFlattenOptions[current] || {};
96107

97-
if (!group && !data?.disabled && !overMaxCount) {
108+
if (!group && !data?.disabled && (isSelected(data.value) || !overMaxCount)) {
98109
return current;
99110
}
100111
}
@@ -122,17 +133,6 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
122133
setActive(defaultActiveFirstOption !== false ? getEnabledActiveIndex(0) : -1);
123134
}, [memoFlattenOptions.length, searchValue]);
124135

125-
// https://github.com/ant-design/ant-design/issues/34975
126-
const isSelected = React.useCallback(
127-
(value: RawValueType) => {
128-
if (mode === 'combobox') {
129-
return false;
130-
}
131-
return rawValues.has(value);
132-
},
133-
[mode, [...rawValues].toString(), rawValues.size],
134-
);
135-
136136
// https://github.com/ant-design/ant-design/issues/48036
137137
const isAriaSelected = React.useCallback(
138138
(value: RawValueType) => {

tests/OptionList.test.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,70 @@ describe('OptionList', () => {
393393
});
394394
expect(global.scrollToArgs).toEqual({ index: 1 });
395395
});
396+
397+
// Test keyboard navigation behavior when maxCount limit is reached
398+
// Verifies that:
399+
// 1. Can navigate between already selected options
400+
// 2. Cannot navigate to unselected options when maxCount is reached
401+
// 3. Navigation wraps around between selected options
402+
it('should allow keyboard navigation on selected options when reach maxCount', () => {
403+
const onActiveValue = jest.fn();
404+
const listRef = React.createRef<RefOptionListProps>();
405+
406+
render(
407+
generateList({
408+
multiple: true,
409+
maxCount: 2,
410+
options: [
411+
{ value: '1', label: '1' },
412+
{ value: '2', label: '2' },
413+
{ value: '3', label: '3' },
414+
],
415+
values: new Set(['1', '2']), // Pre-select first two options
416+
onActiveValue,
417+
ref: listRef,
418+
}),
419+
);
420+
421+
onActiveValue.mockReset();
422+
423+
// Press down key - should move to option '2'
424+
act(() => {
425+
listRef.current.onKeyDown({ which: KeyCode.DOWN } as any);
426+
});
427+
expect(onActiveValue).toHaveBeenCalledWith(
428+
'2',
429+
expect.anything(),
430+
expect.objectContaining({ source: 'keyboard' }),
431+
);
432+
433+
// Press down key again - should wrap to option '1'
434+
onActiveValue.mockReset();
435+
act(() => {
436+
listRef.current.onKeyDown({ which: KeyCode.DOWN } as any);
437+
});
438+
expect(onActiveValue).toHaveBeenCalledWith(
439+
'1',
440+
expect.anything(),
441+
expect.objectContaining({ source: 'keyboard' }),
442+
);
443+
444+
// Press up key - should move back to option '2'
445+
onActiveValue.mockReset();
446+
act(() => {
447+
listRef.current.onKeyDown({ which: KeyCode.UP } as any);
448+
});
449+
expect(onActiveValue).toHaveBeenCalledWith(
450+
'2',
451+
expect.anything(),
452+
expect.objectContaining({ source: 'keyboard' }),
453+
);
454+
455+
// Press down key - should not activate option '3' since maxCount is reached
456+
onActiveValue.mockReset();
457+
act(() => {
458+
listRef.current.onKeyDown({ which: KeyCode.DOWN } as any);
459+
});
460+
expect(onActiveValue).not.toHaveBeenCalledWith('3', expect.anything(), expect.anything());
461+
});
396462
});

0 commit comments

Comments
 (0)