Skip to content

Commit b8da713

Browse files
fix(Select templates): allowed custom keydown handling (#11608)
1 parent 420232c commit b8da713

File tree

6 files changed

+72
-6
lines changed

6 files changed

+72
-6
lines changed

packages/react-templates/src/components/Select/MultiTypeaheadSelect.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ export interface MultiTypeaheadSelectProps extends Omit<SelectProps, 'toggle' |
3737
onToggle?: (nextIsOpen: boolean) => void;
3838
/** Callback triggered when the text in the input field changes. */
3939
onInputChange?: (newValue: string) => void;
40+
/** Custom callback triggered when the input field has focus and a keyboard event is triggered.
41+
* This will override the default keydown behavior for the input field.
42+
*/
43+
onInputKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
4044
/** Placeholder text for the select input. */
4145
placeholder?: string;
4246
/** Message to display when no options match the filter. */
@@ -55,6 +59,7 @@ export const MultiTypeaheadSelectBase: React.FunctionComponent<MultiTypeaheadSel
5559
onSelectionChange,
5660
onToggle,
5761
onInputChange,
62+
onInputKeyDown: onInputKeyDownProp,
5863
placeholder = 'Select an option',
5964
noOptionsFoundMessage = (filter) => `No results found for "${filter}"`,
6065
isDisabled,
@@ -216,11 +221,12 @@ export const MultiTypeaheadSelectBase: React.FunctionComponent<MultiTypeaheadSel
216221
setActiveAndFocusedItem(indexToFocus);
217222
};
218223

219-
const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
224+
const defaultOnInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
220225
const focusedItem = focusedItemIndex !== null ? selectOptions[focusedItemIndex] : null;
221226

222227
switch (event.key) {
223228
case 'Enter':
229+
event.preventDefault();
224230
if (isOpen && focusedItem && focusedItem.value !== NO_RESULTS && !focusedItem.isAriaDisabled) {
225231
selectOption(event, focusedItem?.value);
226232
}
@@ -239,6 +245,14 @@ export const MultiTypeaheadSelectBase: React.FunctionComponent<MultiTypeaheadSel
239245
}
240246
};
241247

248+
const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
249+
if (onInputKeyDownProp) {
250+
onInputKeyDownProp(event);
251+
} else {
252+
defaultOnInputKeyDown(event);
253+
}
254+
};
255+
242256
const onToggleClick = () => {
243257
onToggle && onToggle(!isOpen);
244258
setIsOpen(!isOpen);

packages/react-templates/src/components/Select/TypeaheadSelect.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ export interface TypeaheadSelectProps extends Omit<SelectProps, 'toggle' | 'onSe
3636
onToggle?: (nextIsOpen: boolean) => void;
3737
/** Callback triggered when the text in the input field changes. */
3838
onInputChange?: (newValue: string) => void;
39+
/** Custom callback triggered when the input field has focus and a keyboard event is triggered.
40+
* This will override the default keydown behavior for the input field.
41+
*/
42+
onInputKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
3943
/** Callback triggered when the clear button is selected */
4044
onClearSelection?: () => void;
4145
/** Placeholder text for the select input. */
@@ -67,6 +71,7 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
6771
onSelect,
6872
onToggle,
6973
onInputChange,
74+
onInputKeyDown: onInputKeyDownProp,
7075
onClearSelection,
7176
placeholder = 'Select an option',
7277
noOptionsAvailableMessage = 'No options are available',
@@ -271,11 +276,12 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
271276
setActiveAndFocusedItem(indexToFocus);
272277
};
273278

274-
const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
279+
const defaultOnInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
275280
const focusedItem = focusedItemIndex !== null ? selectOptions[focusedItemIndex] : null;
276281

277282
switch (event.key) {
278283
case 'Enter':
284+
event.preventDefault();
279285
if (isOpen && focusedItem && focusedItem.value !== NO_RESULTS && !focusedItem.isAriaDisabled) {
280286
selectOption(event, focusedItem);
281287
}
@@ -291,6 +297,14 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
291297
}
292298
};
293299

300+
const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
301+
if (onInputKeyDownProp) {
302+
onInputKeyDownProp(event);
303+
} else {
304+
defaultOnInputKeyDown(event);
305+
}
306+
};
307+
294308
const onToggleClick = () => {
295309
onToggle && onToggle(!isOpen);
296310
setIsOpen(!isOpen);

packages/react-templates/src/components/Select/__tests__/MultiTypeaheadSelect.test.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,25 @@ describe('MultiTypeaheadSelect', () => {
394394
expect(onInputChangeMock).toHaveBeenCalledWith('1');
395395
});
396396

397+
it('calls the onInputKeyDown callback only when keydown event occurs', async () => {
398+
const initialOptions = [
399+
{ content: 'Option 1', value: 'option1' },
400+
{ content: 'Option 2', value: 'option2' },
401+
{ content: 'Option 3', value: 'option3' }
402+
];
403+
404+
const user = userEvent.setup();
405+
const onInputKeyDownMock = jest.fn();
406+
407+
render(<MultiTypeaheadSelect initialOptions={initialOptions} onInputKeyDown={onInputKeyDownMock} />);
408+
409+
const input = screen.getByRole('combobox');
410+
await user.click(input);
411+
expect(onInputKeyDownMock).not.toHaveBeenCalled();
412+
await user.keyboard('{Enter}');
413+
expect(onInputKeyDownMock).toHaveBeenCalledTimes(1);
414+
});
415+
397416
it('Matches snapshot', async () => {
398417
const initialOptions = [
399418
{ content: 'Option 1', value: 'option1' },

packages/react-templates/src/components/Select/__tests__/TypeaheadSelect.test.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,25 @@ test('typing in input triggers onInputChange callback', async () => {
389389
expect(onInputChangeMock).toHaveBeenCalledWith('1');
390390
});
391391

392+
it('calls the onInputKeyDown callback only when keydown event occurs', async () => {
393+
const initialOptions = [
394+
{ content: 'Option 1', value: 'option1' },
395+
{ content: 'Option 2', value: 'option2' },
396+
{ content: 'Option 3', value: 'option3' }
397+
];
398+
399+
const user = userEvent.setup();
400+
const onInputKeyDownMock = jest.fn();
401+
402+
render(<TypeaheadSelect initialOptions={initialOptions} onInputKeyDown={onInputKeyDownMock} />);
403+
404+
const input = screen.getByRole('combobox');
405+
await user.click(input);
406+
expect(onInputKeyDownMock).not.toHaveBeenCalled();
407+
await user.keyboard('{Enter}');
408+
expect(onInputKeyDownMock).toHaveBeenCalledTimes(1);
409+
});
410+
392411
test('Matches snapshot', async () => {
393412
const initialOptions = [
394413
{ content: 'Option 1', value: 'option1' },

packages/react-templates/src/components/Select/__tests__/__snapshots__/MultiTypeaheadSelect.test.tsx.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ exports[`MultiTypeaheadSelect Matches snapshot 1`] = `
3434
<button
3535
aria-label="Clear input value"
3636
class="pf-v6-c-button pf-m-plain"
37-
data-ouia-component-id="OUIA-Generated-Button-plain-17"
37+
data-ouia-component-id="OUIA-Generated-Button-plain-18"
3838
data-ouia-component-type="PF6/Button"
3939
data-ouia-safe="true"
4040
type="button"
@@ -63,7 +63,7 @@ exports[`MultiTypeaheadSelect Matches snapshot 1`] = `
6363
aria-expanded="true"
6464
aria-label="Multi select Typeahead menu toggle"
6565
class="pf-v6-c-menu-toggle__button"
66-
data-ouia-component-id="OUIA-Generated-MenuToggle-typeahead-17"
66+
data-ouia-component-id="OUIA-Generated-MenuToggle-typeahead-18"
6767
data-ouia-component-type="PF6/MenuToggle"
6868
data-ouia-safe="true"
6969
tabindex="-1"

packages/react-templates/src/components/Select/__tests__/__snapshots__/TypeaheadSelect.test.tsx.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ exports[`Matches snapshot 1`] = `
3434
<button
3535
aria-label="Clear input value"
3636
class="pf-v6-c-button pf-m-plain"
37-
data-ouia-component-id="OUIA-Generated-Button-plain-17"
37+
data-ouia-component-id="OUIA-Generated-Button-plain-18"
3838
data-ouia-component-type="PF6/Button"
3939
data-ouia-safe="true"
4040
type="button"
@@ -63,7 +63,7 @@ exports[`Matches snapshot 1`] = `
6363
aria-expanded="true"
6464
aria-label="Typeahead menu toggle"
6565
class="pf-v6-c-menu-toggle__button"
66-
data-ouia-component-id="OUIA-Generated-MenuToggle-typeahead-17"
66+
data-ouia-component-id="OUIA-Generated-MenuToggle-typeahead-18"
6767
data-ouia-component-type="PF6/MenuToggle"
6868
data-ouia-safe="true"
6969
tabindex="-1"

0 commit comments

Comments
 (0)