Skip to content

Commit

Permalink
03-accessible-keyboard-navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
bahdcoder committed Jul 26, 2020
1 parent 49fabee commit 23477bc
Showing 1 changed file with 58 additions and 6 deletions.
64 changes: 58 additions & 6 deletions packages/react/src/molecules/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import Text from '../../atoms/Text'
const KEY_CODES = {
ENTER: 13,
SPACE: 32,
DOWN_ARROW: 40
DOWN_ARROW: 40,
ESC: 27,
UP_ARROW: 38
}

interface SelectOption {
Expand All @@ -26,6 +28,30 @@ interface SelectProps {
renderOption?: (props: RenderOptionProps) => React.ReactNode
}

const getPreviousOptionIndex = (currentIndex: number|null, options: Array<SelectOption>) => {
if (currentIndex === null) {
return 0
}

if (currentIndex === 0) {
return options.length - 1
}

return currentIndex - 1
}

const getNextOptionIndex = (currentIndex: number|null, options: Array<SelectOption>) => {
if (currentIndex === null) {
return 0
}

if (currentIndex === options.length - 1) {
return 0
}

return currentIndex + 1
}

const Select: React.FunctionComponent<SelectProps> = ({ options = [], label = 'Please select an option ...', onOptionSelected: handler, renderOption }) => {
const [isOpen, setIsOpen] = useState<boolean>(false)
const [selectedIndex, setSelectedIndex] = useState<null|number>(null)
Expand Down Expand Up @@ -59,7 +85,7 @@ const Select: React.FunctionComponent<SelectProps> = ({ options = [], label = 'P
selectedOption = options[selectedIndex]
}

const highlightItem = (optionIndex: number|null) => {
const highlightOption = (optionIndex: number|null) => {
setHighlightedIndex(optionIndex)
}

Expand All @@ -70,7 +96,7 @@ const Select: React.FunctionComponent<SelectProps> = ({ options = [], label = 'P
setIsOpen(true)

// set focus on the list item
highlightItem(0)
highlightOption(0)
}
}

Expand All @@ -86,7 +112,29 @@ const Select: React.FunctionComponent<SelectProps> = ({ options = [], label = 'P
ref.current.focus()
}
}
}, [isOpen])
}, [isOpen, highlightedIndex])

const onOptionKeyDown: KeyboardEventHandler = (event) => {
if (event.keyCode === KEY_CODES.ESC) {
setIsOpen(false)

return
}

if (event.keyCode === KEY_CODES.DOWN_ARROW) {
highlightOption(getNextOptionIndex(highlightedIndex, options))
}

if (event.keyCode === KEY_CODES.UP_ARROW) {
highlightOption(
getPreviousOptionIndex(highlightedIndex, options)
)
}

if (event.keyCode === KEY_CODES.ENTER) {
onOptionSelected(options[highlightedIndex!], highlightedIndex!)
}
}

return <div className='dse-select'>
<button onKeyDown={onButtonKeyDown} aria-controls='dse-select-list' aria-haspopup={true} aria-expanded={isOpen ? true: undefined} ref={labelRef} className='dse-select__label' onClick={() => onLabelClick()}>
Expand All @@ -109,9 +157,13 @@ const Select: React.FunctionComponent<SelectProps> = ({ options = [], label = 'P
isSelected,
getOptionRecommendedProps: (overrideProps = {}) => {return {
ref,
role: 'menuitemradio',
'aria-label': option.label,
'aria-checked': isSelected ? true : undefined,
onKeyDown: onOptionKeyDown,
tabIndex: isHighlighted ? -1 : 0,
onMouseEnter: () => highlightItem(optionIndex),
onMouseLeave: () => highlightItem(null),
onMouseEnter: () => highlightOption(optionIndex),
onMouseLeave: () => highlightOption(null),
className: `dse-select__option
${isSelected ? 'dse-select__option--selected' : ''}
${isHighlighted ? 'dse-select__option--highlighted' : ''}
Expand Down

0 comments on commit 23477bc

Please sign in to comment.