Skip to content

Commit

Permalink
SelectPanel2: Improve keyboard navigation from search input (#4199)
Browse files Browse the repository at this point in the history
* add moveFocusToList

* Create fresh-parents-kiss.md

* make selector ignore groups
  • Loading branch information
siddharthkp committed Feb 14, 2024
1 parent 1ba0bdd commit b6e5807
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/fresh-parents-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

experimental/SelectPanel: Improve keyboard navigation from search input
29 changes: 27 additions & 2 deletions packages/react/src/drafts/SelectPanel2/SelectPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const SelectPanelContext = React.createContext<{
searchQuery: string
setSearchQuery: React.Dispatch<React.SetStateAction<string>>
selectionVariant: ActionListProps['selectionVariant'] | 'instant'
moveFocusToList: () => void
}>({
title: '',
description: undefined,
Expand All @@ -46,6 +47,7 @@ const SelectPanelContext = React.createContext<{
searchQuery: '',
setSearchQuery: () => {},
selectionVariant: 'multiple',
moveFocusToList: () => {},
})

export type SelectPanelProps = {
Expand Down Expand Up @@ -166,6 +168,14 @@ const Panel: React.FC<SelectPanelProps> = ({
[internalOpen],
)

// used in SelectPanel.SearchInput
const moveFocusToList = () => {
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()
}

/* Dialog */
const dialogRef = React.useRef<HTMLDialogElement>(null)

Expand Down Expand Up @@ -272,6 +282,7 @@ const Panel: React.FC<SelectPanelProps> = ({
searchQuery,
setSearchQuery,
selectionVariant,
moveFocusToList,
}}
>
<Box
Expand Down Expand Up @@ -380,11 +391,15 @@ const SelectPanelHeader: React.FC<React.PropsWithChildren> = ({children, ...prop
)
}

const SelectPanelSearchInput: React.FC<TextInputProps> = ({onChange: propsOnChange, ...props}) => {
const SelectPanelSearchInput: React.FC<TextInputProps> = ({
onChange: propsOnChange,
onKeyDown: propsOnKeyDown,
...props
}) => {
// TODO: use forwardedRef
const inputRef = React.createRef<HTMLInputElement>()

const {setSearchQuery} = React.useContext(SelectPanelContext)
const {setSearchQuery, moveFocusToList} = React.useContext(SelectPanelContext)

const internalOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// If props.onChange is given, the application controls search,
Expand All @@ -393,6 +408,15 @@ const SelectPanelSearchInput: React.FC<TextInputProps> = ({onChange: propsOnChan
else setSearchQuery(event.target.value)
}

const internalKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'ArrowDown') {
event.preventDefault() // prevent scroll
moveFocusToList()
}

if (typeof propsOnKeyDown === 'function') propsOnKeyDown(event)
}

return (
<TextInput
ref={inputRef}
Expand All @@ -416,6 +440,7 @@ const SelectPanelSearchInput: React.FC<TextInputProps> = ({onChange: propsOnChan
}
sx={{'&:has(input:placeholder-shown) .TextInput-action': {display: 'none'}}}
onChange={internalOnChange}
onKeyDown={internalKeyDown}
{...props}
/>
)
Expand Down

0 comments on commit b6e5807

Please sign in to comment.