From 7cb3373ab8801231f070fb6e65e93dac0fdfb231 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Wed, 31 Jan 2024 01:29:23 +0530 Subject: [PATCH 1/3] add moveFocusToList --- src/drafts/SelectPanel2/SelectPanel.tsx | 27 +++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/drafts/SelectPanel2/SelectPanel.tsx b/src/drafts/SelectPanel2/SelectPanel.tsx index 82e122a4181..d26e2816525 100644 --- a/src/drafts/SelectPanel2/SelectPanel.tsx +++ b/src/drafts/SelectPanel2/SelectPanel.tsx @@ -37,6 +37,7 @@ const SelectPanelContext = React.createContext<{ searchQuery: string setSearchQuery: React.Dispatch> selectionVariant: ActionListProps['selectionVariant'] | 'instant' + moveFocusToList: () => void }>({ title: '', description: undefined, @@ -46,6 +47,7 @@ const SelectPanelContext = React.createContext<{ searchQuery: '', setSearchQuery: () => {}, selectionVariant: 'multiple', + moveFocusToList: () => {}, }) export type SelectPanelProps = { @@ -161,6 +163,12 @@ const Panel: React.FC = ({ [internalOpen], ) + // used in SelectPanel.SearchInput + const moveFocusToList = () => { + const firstListElement = dialogRef.current?.querySelector('ul[role=listbox] li') as HTMLLIElement | undefined + firstListElement?.focus() + } + /* Dialog */ const dialogRef = React.useRef(null) @@ -264,6 +272,7 @@ const Panel: React.FC = ({ searchQuery, setSearchQuery, selectionVariant, + moveFocusToList, }} > = ({children, ...prop ) } -const SelectPanelSearchInput: React.FC = ({onChange: propsOnChange, ...props}) => { +const SelectPanelSearchInput: React.FC = ({ + onChange: propsOnChange, + onKeyDown: propsOnKeyDown, + ...props +}) => { // TODO: use forwardedRef const inputRef = React.createRef() - const {setSearchQuery} = React.useContext(SelectPanelContext) + const {setSearchQuery, moveFocusToList} = React.useContext(SelectPanelContext) const internalOnChange = (event: React.ChangeEvent) => { // If props.onChange is given, the application controls search, @@ -385,6 +398,15 @@ const SelectPanelSearchInput: React.FC = ({onChange: propsOnChan else setSearchQuery(event.target.value) } + const internalKeyDown = (event: React.KeyboardEvent) => { + if (event.key === 'ArrowDown') { + event.preventDefault() // prevent scroll + moveFocusToList() + } + + if (typeof propsOnKeyDown === 'function') propsOnKeyDown(event) + } + return ( = ({onChange: propsOnChan } sx={{'&:has(input:placeholder-shown) .TextInput-action': {display: 'none'}}} onChange={internalOnChange} + onKeyDown={internalKeyDown} {...props} /> ) From 6f8c5be721bb5f17506af7aab63d3835bd123f4c Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Wed, 31 Jan 2024 01:37:51 +0530 Subject: [PATCH 2/3] Create fresh-parents-kiss.md --- .changeset/fresh-parents-kiss.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fresh-parents-kiss.md diff --git a/.changeset/fresh-parents-kiss.md b/.changeset/fresh-parents-kiss.md new file mode 100644 index 00000000000..c6ed658c5f0 --- /dev/null +++ b/.changeset/fresh-parents-kiss.md @@ -0,0 +1,5 @@ +--- +"@primer/react": patch +--- + +experimental/SelectPanel: Improve keyboard navigation from search input From bcc4bcf70da4ad6fc9adf149499387af082ff893 Mon Sep 17 00:00:00 2001 From: Siddharth Kshetrapal Date: Mon, 12 Feb 2024 17:16:01 +0100 Subject: [PATCH 3/3] make selector ignore groups --- packages/react/src/drafts/SelectPanel2/SelectPanel.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react/src/drafts/SelectPanel2/SelectPanel.tsx b/packages/react/src/drafts/SelectPanel2/SelectPanel.tsx index e6116fdcaa8..73d0e40be77 100644 --- a/packages/react/src/drafts/SelectPanel2/SelectPanel.tsx +++ b/packages/react/src/drafts/SelectPanel2/SelectPanel.tsx @@ -165,7 +165,9 @@ const Panel: React.FC = ({ // used in SelectPanel.SearchInput const moveFocusToList = () => { - const firstListElement = dialogRef.current?.querySelector('ul[role=listbox] li') as HTMLLIElement | undefined + const selector = 'ul[role=listbox] li:not([role=none])' + // being specific about roles because there can be another ul (tabs in header) and an ActionList.Group (li[role=none]) + const firstListElement = dialogRef.current?.querySelector(selector) as HTMLLIElement | undefined firstListElement?.focus() }