From c036179c70a6bc26fa905134a2b4dbaff009589c Mon Sep 17 00:00:00 2001 From: adamviktora Date: Wed, 23 Oct 2024 18:18:41 +0200 Subject: [PATCH] feat(Select): default arrow key handling to focus items --- .../src/components/Select/Select.tsx | 19 +++++++++++++++++++ .../Select/examples/SelectTypeahead.tsx | 1 + .../src/components/Select/TypeaheadSelect.tsx | 1 + 3 files changed, 21 insertions(+) diff --git a/packages/react-core/src/components/Select/Select.tsx b/packages/react-core/src/components/Select/Select.tsx index ace74b9eeb1..f0eb6247943 100644 --- a/packages/react-core/src/components/Select/Select.tsx +++ b/packages/react-core/src/components/Select/Select.tsx @@ -62,6 +62,8 @@ export interface SelectProps extends MenuProps, OUIAProps { onOpenChange?: (isOpen: boolean) => void; /** Keys that trigger onOpenChange, defaults to tab and escape. It is highly recommended to include Escape in the array, while Tab may be omitted if the menu contains non-menu items that are focusable. */ onOpenChangeKeys?: string[]; + /** Custom callback to override the default behaviour when pressing up/down arrows. Defaults to navigating through the menu items (with focus on them). */ + onArrowUpDownKeyPress?: (event: KeyboardEvent) => void; /** Indicates if the select should be without the outer box-shadow */ isPlain?: boolean; /** @hide Forwarded ref */ @@ -95,6 +97,7 @@ const SelectBase: React.FunctionComponent = ({ shouldFocusFirstItemOnOpen = false, onOpenChange, onOpenChangeKeys = ['Escape', 'Tab'], + onArrowUpDownKeyPress, isPlain, innerRef, zIndex = 9999, @@ -131,6 +134,18 @@ const SelectBase: React.FunctionComponent = ({ }, [isOpen]); React.useEffect(() => { + const onArrowUpDownKeyPressDefault = (event: KeyboardEvent) => { + event.preventDefault(); + + const firstItemSelector = 'li button:not(:disabled),li input:not(:disabled)'; + const lastItemSelector = 'li:last-child button:not(:disabled),li:last-child input:not(:disabled)'; + + const elementToFocus = menuRef?.current?.querySelector( + event.key === 'ArrowDown' ? firstItemSelector : lastItemSelector + ); + elementToFocus && (elementToFocus as HTMLElement).focus(); + }; + const handleMenuKeys = (event: KeyboardEvent) => { // Close the menu on tab or escape if onOpenChange is provided if ( @@ -144,6 +159,10 @@ const SelectBase: React.FunctionComponent = ({ toggleRef.current?.focus(); } } + + if (event.key === 'ArrowDown' || event.key === 'ArrowUp') { + onArrowUpDownKeyPress ? onArrowUpDownKeyPress(event) : onArrowUpDownKeyPressDefault(event); + } }; const handleClick = (event: MouseEvent) => { diff --git a/packages/react-core/src/components/Select/examples/SelectTypeahead.tsx b/packages/react-core/src/components/Select/examples/SelectTypeahead.tsx index 12ca075992d..f6da5fff0ed 100644 --- a/packages/react-core/src/components/Select/examples/SelectTypeahead.tsx +++ b/packages/react-core/src/components/Select/examples/SelectTypeahead.tsx @@ -243,6 +243,7 @@ export const SelectTypeahead: React.FunctionComponent = () => { onOpenChange={(isOpen) => { !isOpen && closeMenu(); }} + onArrowUpDownKeyPress={(_event) => {}} toggle={toggle} shouldFocusFirstItemOnOpen={false} > diff --git a/packages/react-templates/src/components/Select/TypeaheadSelect.tsx b/packages/react-templates/src/components/Select/TypeaheadSelect.tsx index b674a227d5d..2a062df6a75 100644 --- a/packages/react-templates/src/components/Select/TypeaheadSelect.tsx +++ b/packages/react-templates/src/components/Select/TypeaheadSelect.tsx @@ -360,6 +360,7 @@ export const TypeaheadSelectBase: React.FunctionComponent onOpenChange={(isOpen) => { !isOpen && closeMenu(); }} + onArrowUpDownKeyPress={(_event) => {}} toggle={toggle} shouldFocusFirstItemOnOpen={false} ref={innerRef}