1- import React , { useCallback , useRef , useEffect } from 'react'
1+ import React , { useCallback , useRef , useEffect , useMemo } from 'react'
22import styled , { css } from 'styled-components'
33import { shape , spacing } from '../designparams'
44import { Typography } from '../Typography'
@@ -126,11 +126,15 @@ export const SelectList: React.FC<SelectListProps> = ({
126126 [ onSelect ]
127127 )
128128
129+ const selectedValueIndex : number = useMemo (
130+ ( ) => options . findIndex ( o => o . value === value ) ,
131+ [ options , value ]
132+ )
133+
129134 // At first render _only_, scroll to the position of the selected item.
130135 useEffect ( ( ) => {
131- const index = options . findIndex ( o => o . value === value )
132- if ( listRef . current !== null && index !== - 1 ) {
133- listRef . current . scrollTo ( 0 , index * LIST_ITEM_HEIGHT )
136+ if ( listRef . current !== null && selectedValueIndex !== - 1 ) {
137+ listRef . current . scrollTo ( 0 , selectedValueIndex * LIST_ITEM_HEIGHT )
134138 }
135139 } , [ ] ) // eslint-disable-line react-hooks/exhaustive-deps
136140
@@ -152,10 +156,53 @@ export const SelectList: React.FC<SelectListProps> = ({
152156 [ options , listRef ]
153157 )
154158
159+ // traversingKeys
160+ enum TraversingKey {
161+ ArrowUp = 'ArrowUp' ,
162+ ArrowDown = 'ArrowDown' ,
163+ }
164+
165+ const halfway = Math . round ( maxHeight / LIST_ITEM_HEIGHT / 2 )
166+
167+ const traverseSelection = useCallback (
168+ ( e : React . KeyboardEvent < HTMLUListElement > ) => {
169+ if ( ! ( e . key in TraversingKey ) ) {
170+ return
171+ }
172+
173+ e . preventDefault ( )
174+
175+ let nextSelectedValueIndex = selectedValueIndex
176+ let nextScrollTopCount = selectedValueIndex
177+ switch ( e . key ) {
178+ case TraversingKey . ArrowUp :
179+ if ( selectedValueIndex === 0 ) {
180+ return
181+ }
182+ nextSelectedValueIndex --
183+ nextScrollTopCount = nextSelectedValueIndex - halfway
184+ break
185+ case TraversingKey . ArrowDown :
186+ if ( selectedValueIndex + 1 === options . length ) {
187+ return
188+ }
189+ nextSelectedValueIndex ++
190+ nextScrollTopCount = nextSelectedValueIndex - halfway
191+ break
192+ }
193+ onSelect ( options [ nextSelectedValueIndex ] . value )
194+ if ( listRef . current !== null && selectedValueIndex !== - 1 ) {
195+ listRef . current . scrollTo ( 0 , nextScrollTopCount * LIST_ITEM_HEIGHT )
196+ }
197+ } ,
198+ [ options , value , selectedValueIndex ]
199+ )
200+
155201 return (
156202 < SelectListNative
157203 tabIndex = { 0 }
158204 onKeyUp = { searchForOption }
205+ onKeyDown = { traverseSelection }
159206 maxHeight = { `${ maxHeight } px` }
160207 ref = { listRef }
161208 { ...props }
0 commit comments