11import React from 'react'
22import { render , fireEvent , cleanup } from '@testing-library/react'
3- import { useTypeaheadFocus } from '../../hooks'
4- import { TYPEAHEAD_TIMEOUT } from '../../hooks/useTypeaheadFocus'
3+ import { useMnemonics } from '../../hooks'
54
6- const Component = ( {
5+ const Fixture = ( {
76 onSelect = ( ) => null ,
87 hasInput = false ,
98 refNotAttached = false
@@ -13,7 +12,7 @@ const Component = ({
1312 refNotAttached ?: boolean
1413} ) => {
1514 const containerRef = React . createRef < HTMLDivElement > ( )
16- useTypeaheadFocus ( true , containerRef ) // hard coding open=true for test
15+ useMnemonics ( true , containerRef ) // hard coding open=true for test
1716
1817 return (
1918 < >
@@ -22,9 +21,12 @@ const Component = ({
2221 { hasInput && < input autoFocus type = "text" placeholder = "Filter options" /> }
2322 < button onKeyDown = { onSelect } > button 1</ button >
2423 < button onKeyDown = { onSelect } > Button 2</ button >
25- < button disabled > button 3 is disabled</ button >
26- < button onKeyDown = { onSelect } > button 4</ button >
2724 < button onKeyDown = { onSelect } > third button</ button >
25+ < button disabled > fourth button is disabled</ button >
26+ < button onKeyDown = { onSelect } > button 5</ button >
27+ < button onKeyDown = { onSelect } aria-keyshortcuts = "6 E" >
28+ button 6
29+ </ button >
2830 < span > not focusable</ span >
2931 </ div >
3032 </ >
@@ -35,31 +37,31 @@ describe('useTypeaheadFocus', () => {
3537 afterEach ( cleanup )
3638
3739 it ( 'First element: when b is pressed, it should move focus the "b"utton 1' , ( ) => {
38- const { getByTestId, getByText} = render ( < Component /> )
40+ const { getByTestId, getByText} = render ( < Fixture /> )
3941 const container = getByTestId ( 'container' )
4042
4143 fireEvent . keyDown ( container , { key : 'b' , code : 'b' } )
4244 expect ( getByText ( 'button 1' ) ) . toEqual ( document . activeElement )
4345 } )
4446
4547 it ( 'Not first element: when t is pressed, it should move focus the "t"hird button' , ( ) => {
46- const { getByTestId, getByText} = render ( < Component /> )
48+ const { getByTestId, getByText} = render ( < Fixture /> )
4749 const container = getByTestId ( 'container' )
4850
4951 fireEvent . keyDown ( container , { key : 't' , code : 't' } )
5052 expect ( getByText ( 'third button' ) ) . toEqual ( document . activeElement )
5153 } )
5254
5355 it ( 'Case insensitive: when B is pressed, it should move focus the "b"utton 1' , ( ) => {
54- const { getByTestId, getByText} = render ( < Component /> )
56+ const { getByTestId, getByText} = render ( < Fixture /> )
5557 const container = getByTestId ( 'container' )
5658
5759 fireEvent . keyDown ( container , { key : 'B' , code : 'B' } )
5860 expect ( getByText ( 'button 1' ) ) . toEqual ( document . activeElement )
5961 } )
6062
6163 it ( 'Repeating letter: when b is pressed repeatedly, it should wrap through the buttons starting with "b", skipping the disabled button' , ( ) => {
62- const { getByTestId, getByText} = render ( < Component /> )
64+ const { getByTestId, getByText} = render ( < Fixture /> )
6365 const container = getByTestId ( 'container' )
6466
6567 fireEvent . keyDown ( container , { key : 'b' , code : 'b' } )
@@ -69,65 +71,61 @@ describe('useTypeaheadFocus', () => {
6971 expect ( getByText ( 'Button 2' ) ) . toEqual ( document . activeElement )
7072
7173 fireEvent . keyDown ( container , { key : 'b' , code : 'b' } )
72- expect ( getByText ( 'button 4 ' ) ) . toEqual ( document . activeElement )
74+ expect ( getByText ( 'button 5 ' ) ) . toEqual ( document . activeElement )
7375
76+ // should cycle back to start of the list
7477 fireEvent . keyDown ( container , { key : 'b' , code : 'b' } )
7578 expect ( getByText ( 'button 1' ) ) . toEqual ( document . activeElement )
7679 } )
7780
78- it ( 'Timeout: when presses b, waits and presses t, it should move focus the "t"hird button' , async ( ) => {
79- jest . useFakeTimers ( )
80- const { getByTestId, getByText} = render ( < Component /> )
81+ it ( 'User defined aria-keyshortcuts: Should catch all shortcuts defined by user' , ( ) => {
82+ const { getByTestId, getByText} = render ( < Fixture /> )
8183 const container = getByTestId ( 'container' )
8284
83- fireEvent . keyDown ( container , { key : 'b' , code : 'b' } )
84- expect ( getByText ( 'button 1' ) ) . toEqual ( document . activeElement )
85-
86- // if we press t now, the query would be "bt",
87- // and focus will stay wherever it is
88- fireEvent . keyDown ( container , { key : 't' , code : 't' } )
89- expect ( getByText ( 'button 1' ) ) . toEqual ( document . activeElement )
85+ fireEvent . keyDown ( container , { key : '6' , code : '6' } )
86+ expect ( getByText ( 'button 6' ) ) . toEqual ( document . activeElement )
9087
91- // but, if we simulate typeahead timeout, then type t
92- // it should jump to the third button
93- jest . advanceTimersByTime ( TYPEAHEAD_TIMEOUT )
88+ // send focus elsewhere
9489 fireEvent . keyDown ( container , { key : 't' , code : 't' } )
9590 expect ( getByText ( 'third button' ) ) . toEqual ( document . activeElement )
91+
92+ fireEvent . keyDown ( container , { key : 'e' , code : 'e' } )
93+ expect ( getByText ( 'button 6' ) ) . toEqual ( document . activeElement )
9694 } )
9795
98- it ( 'Space: when user is typing and presses Space, it should not select the option' , ( ) => {
99- const mockFunction = jest . fn ( )
100- const { getByTestId, getByText} = render ( < Component onSelect = { mockFunction } /> )
96+ it ( 'aria-keyshortcuts: it should add aria-keyshortcuts to focusable items' , ( ) => {
97+ const { getByText} = render ( < Fixture /> )
10198
102- const container = getByTestId ( 'container' )
103- fireEvent . keyDown ( container , { key : 't' , code : 't' } )
99+ expect ( getByText ( 'button 1' ) ) . toHaveAttribute ( 'aria-keyshortcuts' , 'b' )
100+ expect ( getByText ( 'Button 2' ) ) . toHaveAttribute ( 'aria-keyshortcuts' , 'b' )
101+ expect ( getByText ( 'third button' ) ) . toHaveAttribute ( 'aria-keyshortcuts' , 't' )
102+ expect ( getByText ( 'button 5' ) ) . toHaveAttribute ( 'aria-keyshortcuts' , 'b' )
104103
105- const thirdButton = getByText ( 'third button' )
106- expect ( thirdButton ) . toEqual ( document . activeElement )
107- fireEvent . keyDown ( thirdButton , { key : ' ' , code : 'Space' } )
108- expect ( mockFunction ) . not . toHaveBeenCalled ( )
104+ // don't overwrite aria-keyshortcuts if it's already defined
105+ expect ( getByText ( 'button 6' ) ) . toHaveAttribute ( 'aria-keyshortcuts' , '6 E' )
106+
107+ // not focusable items should not have aria-keyshortcuts
108+ expect ( getByText ( 'fourth button is disabled' ) ) . not . toHaveAttribute ( 'aria-keyshortcuts' )
109+ expect ( getByText ( 'not focusable' ) ) . not . toHaveAttribute ( 'aria-keyshortcuts' )
109110 } )
110111
111- it ( 'Space after timeout: when user is presses Space after waiting, it should select the option' , ( ) => {
112- jest . useFakeTimers ( )
112+ it ( 'Space: when user presses Space, it should select the option' , ( ) => {
113113 const mockFunction = jest . fn ( )
114- const { getByTestId, getByText} = render ( < Component onSelect = { mockFunction } /> )
114+ const { getByTestId, getByText} = render ( < Fixture onSelect = { mockFunction } /> )
115115
116116 const container = getByTestId ( 'container' )
117117 fireEvent . keyDown ( container , { key : 't' , code : 't' } )
118118
119119 const thirdButton = getByText ( 'third button' )
120120 expect ( thirdButton ) . toEqual ( document . activeElement )
121-
122- jest . advanceTimersByTime ( TYPEAHEAD_TIMEOUT )
123121 fireEvent . keyDown ( thirdButton , { key : ' ' , code : 'Space' } )
124122 expect ( mockFunction ) . toHaveBeenCalled ( )
125123 } )
126124
127- it ( 'Enter: when user is presses Enter, it should instantly select the option' , ( ) => {
125+ it ( 'Enter: when user is presses Enter, it should select the option' , ( ) => {
128126 jest . useFakeTimers ( )
129127 const mockFunction = jest . fn ( )
130- const { getByTestId, getByText} = render ( < Component onSelect = { mockFunction } /> )
128+ const { getByTestId, getByText} = render ( < Fixture onSelect = { mockFunction } /> )
131129
132130 const container = getByTestId ( 'container' )
133131 fireEvent . keyDown ( container , { key : 't' , code : 't' } )
@@ -139,44 +137,16 @@ describe('useTypeaheadFocus', () => {
139137 expect ( mockFunction ) . toHaveBeenCalled ( )
140138 } )
141139
142- it ( 'Long string: when user starts typing a longer string "button 4", focus should jump to closest match' , async ( ) => {
143- jest . useFakeTimers ( )
144- const mockFunction = jest . fn ( )
145- const { getByTestId, getByText} = render ( < Component onSelect = { mockFunction } /> )
146- const container = getByTestId ( 'container' )
147-
148- fireEvent . keyDown ( container , { key : 'b' , code : 'b' } )
149- expect ( getByText ( 'button 1' ) ) . toEqual ( document . activeElement )
150-
151- fireEvent . keyDown ( container , { key : 'u' , code : 'u' } )
152- expect ( getByText ( 'button 1' ) ) . toEqual ( document . activeElement )
153-
154- fireEvent . keyDown ( container , { key : 't' , code : 't' } )
155- fireEvent . keyDown ( container , { key : 't' , code : 't' } )
156- fireEvent . keyDown ( container , { key : 'o' , code : 'o' } )
157- fireEvent . keyDown ( container , { key : 'n' , code : 'n' } )
158-
159- // pressing Space should be treated as part of query
160- // and shouldn't select the option
161- fireEvent . keyDown ( container , { key : ' ' , code : 'Space' } )
162- expect ( mockFunction ) . not . toHaveBeenCalled ( )
163- expect ( getByText ( 'button 1' ) ) . toEqual ( document . activeElement )
164-
165- fireEvent . keyDown ( container , { key : '4' , code : '4' } )
166- // the query is now "button 4" and should move focus
167- expect ( getByText ( 'button 4' ) ) . toEqual ( document . activeElement )
168- } )
169-
170140 it ( 'Shortcuts: when a modifier is used, typeahead should not do anything' , ( ) => {
171- const { getByTestId, getByText} = render ( < Component /> )
141+ const { getByTestId, getByText} = render ( < Fixture /> )
172142 const container = getByTestId ( 'container' )
173143
174144 fireEvent . keyDown ( container , { metaKey : true , key : 'b' , code : 'b' } )
175145 expect ( getByText ( 'button 1' ) ) . not . toEqual ( document . activeElement )
176146 } )
177147
178148 it ( 'TextInput: when an textinput has focus, typeahead should not do anything' , async ( ) => {
179- const { getByTestId, getByPlaceholderText} = render ( < Component hasInput = { true } /> )
149+ const { getByTestId, getByPlaceholderText} = render ( < Fixture hasInput = { true } /> )
180150 const container = getByTestId ( 'container' )
181151
182152 const input = getByPlaceholderText ( 'Filter options' )
@@ -187,7 +157,7 @@ describe('useTypeaheadFocus', () => {
187157 } )
188158
189159 it ( 'Missing ref: when a ref is not attached, typeahead should break the component' , async ( ) => {
190- const { getByTestId, getByText} = render ( < Component refNotAttached = { true } /> )
160+ const { getByTestId, getByText} = render ( < Fixture refNotAttached = { true } /> )
191161 const container = getByTestId ( 'container' )
192162
193163 fireEvent . keyDown ( container , { key : 'b' , code : 'b' } )
0 commit comments