downshift-js / downshift
Showing 9 of 39 files from the diff.
Other files ignored by Codecov
package.json has changed.

@@ -1,16 +1,11 @@
Loading
1 1
import * as React from 'react'
2 -
import {render, fireEvent, screen} from '@testing-library/react'
2 +
import {render} from '@testing-library/react'
3 3
import {renderHook} from '@testing-library/react-hooks'
4 -
import userEvent from '@testing-library/user-event'
5 4
import {defaultProps} from '../utils'
6 -
import {items} from '../testUtils'
5 +
import {dataTestIds, items, user, getInput} from '../testUtils'
7 6
import useCombobox from '.'
8 7
9 -
const dataTestIds = {
10 -
  toggleButton: 'toggle-button-id',
11 -
  item: index => `item-id-${index}`,
12 -
  input: 'input-id',
13 -
}
8 +
export * from '../testUtils'
14 9
15 10
jest.mock('../../utils', () => {
16 11
  const utils = jest.requireActual('../../utils')
@@ -34,74 +29,25 @@
Loading
34 29
beforeEach(jest.resetAllMocks)
35 30
afterAll(jest.restoreAllMocks)
36 31
37 -
const renderCombobox = (props, uiCallback) => {
32 +
export async function changeInputValue(inputValue) {
33 +
  await user.type(getInput(), inputValue)
34 +
}
35 +
36 +
export const renderCombobox = (props, uiCallback) => {
38 37
  const renderSpy = jest.fn()
39 38
  const ui = <DropdownCombobox renderSpy={renderSpy} {...props} />
40 39
  const utils = render(uiCallback ? uiCallback(ui) : ui)
41 40
  const rerender = newProps =>
42 41
    utils.rerender(<DropdownCombobox renderSpy={renderSpy} {...newProps} />)
43 -
  const label = screen.getByText(/choose an element/i)
44 -
  const menu = screen.getByRole('listbox')
45 -
  const toggleButton = screen.getByTestId(dataTestIds.toggleButton)
46 -
  const input = screen.getByTestId(dataTestIds.input)
47 -
  const combobox = screen.getByRole('combobox')
48 -
  const getItemAtIndex = index => screen.getByTestId(dataTestIds.item(index))
49 -
  const getItems = () => screen.queryAllByRole('option')
50 -
  const clickOnItemAtIndex = index => {
51 -
    // keeping fireEvent so we don't trigger input blur via user event
52 -
    fireEvent.click(getItemAtIndex(index))
53 -
  }
54 -
  const clickOnToggleButton = () => {
55 -
    userEvent.click(toggleButton)
56 -
  }
57 -
  const mouseMoveItemAtIndex = index => {
58 -
    userEvent.hover(getItemAtIndex(index))
59 -
  }
60 -
  const getA11yStatusContainer = () => screen.queryByRole('status')
61 -
  const mouseLeaveMenu = () => {
62 -
    userEvent.unhover(menu)
63 -
  }
64 -
  const changeInputValue = inputValue => {
65 -
    userEvent.type(input, inputValue)
66 -
  }
67 -
  const focusInput = () => {
68 -
    fireEvent.focus(input)
69 -
  }
70 -
  const keyDownOnInput = (key, options = {}) => {
71 -
    if (document.activeElement !== input) {
72 -
      focusInput()
73 -
    }
74 -
75 -
    fireEvent.keyDown(input, {key, ...options})
76 -
  }
77 -
  const blurInput = () => {
78 -
    fireEvent.blur(input)
79 -
  }
80 42
81 43
  return {
82 44
    ...utils,
83 45
    renderSpy,
84 46
    rerender,
85 -
    label,
86 -
    menu,
87 -
    toggleButton,
88 -
    getItemAtIndex,
89 -
    clickOnItemAtIndex,
90 -
    mouseMoveItemAtIndex,
91 -
    getItems,
92 -
    clickOnToggleButton,
93 -
    getA11yStatusContainer,
94 -
    mouseLeaveMenu,
95 -
    input,
96 -
    combobox,
97 -
    changeInputValue,
98 -
    keyDownOnInput,
99 -
    blurInput,
100 -
    focusInput,
101 47
  }
102 48
}
103 49
104 -
const DropdownCombobox = ({renderSpy, renderItem, ...props}) => {
50 +
function DropdownCombobox({renderSpy, renderItem, ...props}) {
105 51
  const {
106 52
    isOpen,
107 53
    getToggleButtonProps,
@@ -133,7 +79,7 @@
Loading
133 79
            const stringItem =
134 80
              item instanceof Object ? itemToString(item) : item
135 81
            return renderItem ? (
136 -
              renderItem({index, item, getItemProps, dataTestIds, stringItem})
82 +
              renderItem({index, item, getItemProps, stringItem})
137 83
            ) : (
138 84
              <li
139 85
                data-testid={dataTestIds.item(index)}
@@ -149,8 +95,6 @@
Loading
149 95
  )
150 96
}
151 97
152 -
const renderUseCombobox = props => {
98 +
export const renderUseCombobox = props => {
153 99
  return renderHook(() => useCombobox({items, ...props}))
154 100
}
155 -
156 -
export {renderUseCombobox, dataTestIds, renderCombobox}

@@ -1,7 +1,8 @@
Loading
1 1
import React from 'react'
2 -
import {act} from '@testing-library/react'
2 +
import {screen, act} from '@testing-library/react'
3 +
import userEvent from '@testing-library/user-event'
3 4
4 -
const items = [
5 +
export const items = [
5 6
  'Neptunium',
6 7
  'Plutonium',
7 8
  'Americium',
@@ -30,7 +31,16 @@
Loading
30 31
  'Oganesson',
31 32
]
32 33
33 -
const defaultIds = {
34 +
export const dataTestIds = {
35 +
  toggleButton: 'toggle-button-id',
36 +
  menu: 'menu-id',
37 +
  item: index => `item-id-${index}`,
38 +
  input: 'input-id',
39 +
  selectedItemPrefix: 'selected-item-id',
40 +
  selectedItem: index => `selected-item-id-${index}`,
41 +
}
42 +
43 +
export const defaultIds = {
34 44
  labelId: 'downshift-test-id-label',
35 45
  menuId: 'downshift-test-id-menu',
36 46
  getItemId: index => `downshift-test-id-item-${index}`,
@@ -38,14 +48,13 @@
Loading
38 48
  inputId: 'downshift-test-id-input',
39 49
}
40 50
41 -
const waitForDebouncedA11yStatusUpdate = () =>
51 +
export const waitForDebouncedA11yStatusUpdate = () =>
42 52
  act(() => jest.advanceTimersByTime(200))
43 53
44 -
const MemoizedItem = React.memo(function Item({
54 +
export const MemoizedItem = React.memo(function Item({
45 55
  index,
46 56
  item,
47 57
  getItemProps,
48 -
  dataTestIds,
49 58
  stringItem,
50 59
  ...rest
51 60
}) {
@@ -60,4 +69,55 @@
Loading
60 69
  )
61 70
})
62 71
63 -
export {items, defaultIds, waitForDebouncedA11yStatusUpdate, MemoizedItem}
72 +
export const user = userEvent.setup({delay: null})
73 +
74 +
export function getLabel() {
75 +
  return screen.getByText(/choose an element/i)
76 +
}
77 +
export function getMenu() {
78 +
  return screen.getByRole('listbox')
79 +
}
80 +
export function getToggleButton() {
81 +
  return screen.getByTestId(dataTestIds.toggleButton)
82 +
}
83 +
export function getItemAtIndex(index) {
84 +
  return getItems()[index]
85 +
}
86 +
export function getItems() {
87 +
  return screen.queryAllByRole('option')
88 +
}
89 +
export function getInput() {
90 +
  return screen.getByRole('textbox')
91 +
}
92 +
export async function clickOnItemAtIndex(index) {
93 +
  await user.click(getItemAtIndex(index))
94 +
}
95 +
export async function clickOnToggleButton() {
96 +
  await user.click(getToggleButton())
97 +
}
98 +
export async function mouseMoveItemAtIndex(index) {
99 +
  await user.hover(getItemAtIndex(index))
100 +
}
101 +
export async function mouseLeaveItemAtIndex(index) {
102 +
  await user.unhover(getItemAtIndex(index))
103 +
}
104 +
export async function keyDownOnToggleButton(keys) {
105 +
  if (document.activeElement !== getToggleButton()) {
106 +
    getToggleButton().focus()
107 +
  }
108 +
109 +
  await user.keyboard(keys)
110 +
}
111 +
export async function keyDownOnInput(keys) {
112 +
  if (document.activeElement !== getInput()) {
113 +
    getInput().focus()
114 +
  }
115 +
116 +
  await user.keyboard(keys)
117 +
}
118 +
export function getA11yStatusContainer() {
119 +
  return screen.queryByRole('status')
120 +
}
121 +
export async function tab(shiftKey = false) {
122 +
  await user.tab({shift: shiftKey})
123 +
}

@@ -1,11 +1,22 @@
Loading
1 1
import * as React from 'react'
2 -
import {render, fireEvent, screen} from '@testing-library/react'
2 +
import {render, act} from '@testing-library/react'
3 3
import {renderHook} from '@testing-library/react-hooks'
4 -
import userEvent from '@testing-library/user-event'
5 4
import {defaultProps} from '../utils'
6 -
import {items} from '../testUtils'
5 +
import {
6 +
  clickOnItemAtIndex,
7 +
  clickOnToggleButton,
8 +
  dataTestIds,
9 +
  items,
10 +
  keyDownOnToggleButton,
11 +
  mouseLeaveItemAtIndex,
12 +
  mouseMoveItemAtIndex,
13 +
  tab,
14 +
} from '../testUtils'
15 +
import * as stateChangeTypes from './stateChangeTypes'
7 16
import useSelect from '.'
8 17
18 +
export * from '../testUtils'
19 +
9 20
jest.mock('../../utils', () => {
10 21
  const utils = jest.requireActual('../../utils')
11 22
@@ -28,75 +39,24 @@
Loading
28 39
beforeEach(jest.resetAllMocks)
29 40
afterAll(jest.restoreAllMocks)
30 41
31 -
const dataTestIds = {
32 -
  toggleButton: 'toggle-button-id',
33 -
  menu: 'menu-id',
34 -
  item: index => `item-id-${index}`,
35 -
}
36 -
37 -
const renderUseSelect = props => {
42 +
export function renderUseSelect(props) {
38 43
  return renderHook(() => useSelect({items, ...props}))
39 44
}
40 -
41 -
const renderSelect = (props, uiCallback) => {
45 +
export function renderSelect(props, uiCallback) {
42 46
  const renderSpy = jest.fn()
43 47
  const ui = <DropdownSelect renderSpy={renderSpy} {...props} />
44 48
  const utils = render(uiCallback ? uiCallback(ui) : ui)
45 49
  const rerender = p =>
46 50
    utils.rerender(<DropdownSelect renderSpy={renderSpy} {...p} />)
47 -
  const label = screen.getByText(/choose an element/i)
48 -
  const menu = screen.getByRole('listbox')
49 -
  const toggleButton = screen.getByTestId(dataTestIds.toggleButton)
50 -
  const getItemAtIndex = index => screen.getByTestId(dataTestIds.item(index))
51 -
  const getItems = () => screen.queryAllByRole('option')
52 -
  const clickOnItemAtIndex = index => {
53 -
    fireEvent.click(getItemAtIndex(index))
54 -
  }
55 -
  const clickOnToggleButton = () => {
56 -
    fireEvent.click(toggleButton)
57 -
  }
58 -
  const mouseMoveItemAtIndex = index => {
59 -
    fireEvent.mouseMove(getItemAtIndex(index))
60 -
  }
61 -
  const keyDownOnToggleButton = (key, options = {}) => {
62 -
    fireEvent.keyDown(toggleButton, {key, ...options})
63 -
  }
64 -
  const keyDownOnMenu = (key, options = {}) => {
65 -
    fireEvent.keyDown(menu, {key, ...options})
66 -
  }
67 -
  const blurMenu = () => {
68 -
    fireEvent.blur(menu)
69 -
  }
70 -
  const getA11yStatusContainer = () => screen.queryByRole('status')
71 -
  const mouseLeaveMenu = () => {
72 -
    userEvent.unhover(menu)
73 -
  }
74 -
  const tab = (shiftKey = false) => {
75 -
    userEvent.tab({shift: shiftKey})
76 -
  }
77 51
78 52
  return {
79 53
    ...utils,
80 54
    renderSpy,
81 55
    rerender,
82 -
    label,
83 -
    menu,
84 -
    toggleButton,
85 -
    getItemAtIndex,
86 -
    clickOnItemAtIndex,
87 -
    mouseMoveItemAtIndex,
88 -
    getItems,
89 -
    keyDownOnToggleButton,
90 -
    clickOnToggleButton,
91 -
    blurMenu,
92 -
    getA11yStatusContainer,
93 -
    mouseLeaveMenu,
94 -
    keyDownOnMenu,
95 -
    tab,
96 56
  }
97 57
}
98 58
99 -
const DropdownSelect = ({renderSpy, renderItem, ...props}) => {
59 +
export function DropdownSelect({renderSpy, renderItem, ...props}) {
100 60
  const {
101 61
    isOpen,
102 62
    selectedItem,
@@ -105,7 +65,7 @@
Loading
105 65
    getMenuProps,
106 66
    getItemProps,
107 67
  } = useSelect({items, ...props})
108 -
  const {itemToString} = props.itemToString ? props : defaultProps
68 +
  const itemToString = props?.itemToString ?? defaultProps.itemToString
109 69
110 70
  renderSpy()
111 71
@@ -126,7 +86,7 @@
Loading
126 86
            const stringItem =
127 87
              item instanceof Object ? itemToString(item) : item
128 88
            return renderItem ? (
129 -
              renderItem({index, item, getItemProps, dataTestIds, stringItem})
89 +
              renderItem({index, item, getItemProps, stringItem})
130 90
            ) : (
131 91
              <li
132 92
                data-testid={dataTestIds.item(index)}
@@ -142,4 +102,210 @@
Loading
142 102
  )
143 103
}
144 104
145 -
export {items, renderUseSelect, renderSelect, DropdownSelect}
105 +
/**
106 +
 * Return the id of the item that strats with the caracter.
107 +
 * @param {string} character The start of the item string.
108 +
 * @param {number} startIndex The index to start searching.
109 +
 * @returns number The index of the item.
110 +
 */
111 +
export function getItemIndexByCharacter(character, startIndex = 0) {
112 +
  return (
113 +
    items.slice(startIndex).findIndex(item => {
114 +
      // console.log(item.toLowerCase(), character.toLowerCase(), item.toLowerCase().startsWith(character.toLowerCase()))
115 +
116 +
      return item.toLowerCase().startsWith(character.toLowerCase())
117 +
    }) + startIndex
118 +
  )
119 +
}
120 +
121 +
export const stateChangeTestCases = [
122 +
  {
123 +
    step: keyDownOnToggleButton,
124 +
    arg: '{ArrowDown}',
125 +
    state: {
126 +
      isOpen: true,
127 +
      highlightedIndex: 0,
128 +
      inputValue: '',
129 +
      selectedItem: null,
130 +
    },
131 +
    type: stateChangeTypes.ToggleButtonKeyDownArrowDown,
132 +
  },
133 +
  {
134 +
    step: keyDownOnToggleButton,
135 +
    arg: '{Enter}',
136 +
    state: {
137 +
      isOpen: false,
138 +
      highlightedIndex: -1,
139 +
      inputValue: '',
140 +
      selectedItem: items[0],
141 +
    },
142 +
    type: stateChangeTypes.ToggleButtonKeyDownEnter,
143 +
  },
144 +
  {
145 +
    step: keyDownOnToggleButton,
146 +
    arg: '{End}',
147 +
    state: {
148 +
      isOpen: true,
149 +
      highlightedIndex: items.length - 1,
150 +
      inputValue: '',
151 +
      selectedItem: items[0],
152 +
    },
153 +
    type: stateChangeTypes.ToggleButtonKeyDownEnd,
154 +
  },
155 +
  {
156 +
    step: keyDownOnToggleButton,
157 +
    arg: ' ',
158 +
    state: {
159 +
      isOpen: false,
160 +
      highlightedIndex: -1,
161 +
      inputValue: '',
162 +
      selectedItem: items[items.length - 1],
163 +
    },
164 +
    type: stateChangeTypes.ToggleButtonKeyDownSpaceButton,
165 +
  },
166 +
  {
167 +
    step: clickOnToggleButton,
168 +
    state: {
169 +
      isOpen: true,
170 +
      highlightedIndex: items.length - 1,
171 +
      inputValue: '',
172 +
      selectedItem: items[items.length - 1],
173 +
    },
174 +
    type: stateChangeTypes.ToggleButtonClick,
175 +
  },
176 +
  {
177 +
    step: keyDownOnToggleButton,
178 +
    arg: '{PageUp}',
179 +
    state: {
180 +
      isOpen: true,
181 +
      highlightedIndex: items.length - 11,
182 +
      inputValue: '',
183 +
      selectedItem: items[items.length - 1],
184 +
    },
185 +
    type: stateChangeTypes.ToggleButtonKeyDownPageUp,
186 +
  },
187 +
  {
188 +
    step: keyDownOnToggleButton,
189 +
    arg: '{PageDown}',
190 +
    state: {
191 +
      isOpen: true,
192 +
      highlightedIndex: items.length - 1,
193 +
      inputValue: '',
194 +
      selectedItem: items[items.length - 1],
195 +
    },
196 +
    type: stateChangeTypes.ToggleButtonKeyDownPageDown,
197 +
  },
198 +
  {
199 +
    step: mouseMoveItemAtIndex,
200 +
    arg: items.length - 2,
201 +
    state: {
202 +
      isOpen: true,
203 +
      highlightedIndex: items.length - 2,
204 +
      inputValue: '',
205 +
      selectedItem: items[items.length - 1],
206 +
    },
207 +
    type: stateChangeTypes.ItemMouseMove,
208 +
  },
209 +
  {
210 +
    step: mouseLeaveItemAtIndex,
211 +
    arg: items.length - 2,
212 +
    state: {
213 +
      isOpen: true,
214 +
      highlightedIndex: -1,
215 +
      inputValue: '',
216 +
      selectedItem: items[items.length - 1],
217 +
    },
218 +
    type: stateChangeTypes.MenuMouseLeave,
219 +
  },
220 +
  {
221 +
    step: mouseMoveItemAtIndex,
222 +
    arg: 2,
223 +
    state: {
224 +
      isOpen: true,
225 +
      highlightedIndex: 2,
226 +
      inputValue: '',
227 +
      selectedItem: items[items.length - 1],
228 +
    },
229 +
    type: stateChangeTypes.ItemMouseMove,
230 +
  },
231 +
  {
232 +
    step: clickOnItemAtIndex,
233 +
    arg: 2,
234 +
    state: {
235 +
      isOpen: false,
236 +
      highlightedIndex: -1,
237 +
      inputValue: '',
238 +
      selectedItem: items[2],
239 +
    },
240 +
    type: stateChangeTypes.ItemClick,
241 +
  },
242 +
  {
243 +
    step: keyDownOnToggleButton,
244 +
    arg: '{Alt>}{ArrowDown}{/Alt}',
245 +
    state: {
246 +
      isOpen: true,
247 +
      highlightedIndex: 2,
248 +
      inputValue: '',
249 +
      selectedItem: items[2],
250 +
    },
251 +
    type: stateChangeTypes.ToggleButtonKeyDownArrowDown,
252 +
  },
253 +
  {
254 +
    step: keyDownOnToggleButton,
255 +
    arg: '{Alt>}{ArrowDown}{/Alt}',
256 +
    state: {
257 +
      isOpen: true,
258 +
      highlightedIndex: 3,
259 +
      inputValue: '',
260 +
      selectedItem: items[2],
261 +
    },
262 +
    type: stateChangeTypes.ToggleButtonKeyDownArrowDown,
263 +
  },
264 +
  {
265 +
    step: keyDownOnToggleButton,
266 +
    arg: '{Alt>}{ArrowUp}{/Alt}',
267 +
    state: {
268 +
      isOpen: false,
269 +
      highlightedIndex: -1,
270 +
      inputValue: '',
271 +
      selectedItem: items[3],
272 +
    },
273 +
    type: stateChangeTypes.ToggleButtonKeyDownArrowUp,
274 +
  },
275 +
  {
276 +
    step: keyDownOnToggleButton,
277 +
    arg: 'c',
278 +
    state: {
279 +
      isOpen: true,
280 +
      highlightedIndex: 5,
281 +
      inputValue: 'c',
282 +
      selectedItem: items[3],
283 +
    },
284 +
    type: stateChangeTypes.ToggleButtonKeyDownCharacter,
285 +
  },
286 +
  {
287 +
    step: () =>
288 +
      // just to have all steps async.
289 +
      new Promise(resolve => {
290 +
        act(() => jest.runAllTimers())
291 +
        resolve()
292 +
      }),
293 +
    state: {
294 +
      isOpen: true,
295 +
      highlightedIndex: 5,
296 +
      inputValue: '',
297 +
      selectedItem: items[3],
298 +
    },
299 +
    type: stateChangeTypes.FunctionSetInputValue,
300 +
  },
301 +
  {
302 +
    step: tab,
303 +
    state: {
304 +
      isOpen: false,
305 +
      highlightedIndex: -1,
306 +
      inputValue: '',
307 +
      selectedItem: items[5],
308 +
    },
309 +
    type: stateChangeTypes.ToggleButtonBlur,
310 +
  },
311 +
]

@@ -1,6 +1,5 @@
Loading
1 1
import {getHighlightedIndexOnOpen, getDefaultValue} from './utils'
2 2
3 -
/* eslint-disable complexity */
4 3
export default function downshiftCommonReducer(
5 4
  state,
6 5
  action,

@@ -1,13 +1,14 @@
Loading
1 1
import * as React from 'react'
2 2
3 -
import {render, fireEvent, screen} from '@testing-library/react'
3 +
import {render, screen} from '@testing-library/react'
4 4
import {renderHook} from '@testing-library/react-hooks'
5 -
import userEvent from '@testing-library/user-event'
6 5
import {defaultProps} from '../utils'
7 -
import {items} from '../testUtils'
6 +
import {items, user, getInput, dataTestIds} from '../testUtils'
8 7
import useCombobox from '../useCombobox'
9 8
import useMultipleSelection from '.'
10 9
10 +
export * from '../testUtils'
11 +
11 12
jest.mock('../../utils', () => {
12 13
  const utils = jest.requireActual('../../utils')
13 14
@@ -30,21 +31,41 @@
Loading
30 31
beforeEach(jest.resetAllMocks)
31 32
afterAll(jest.restoreAllMocks)
32 33
33 -
export const dataTestIds = {
34 -
  selectedItemPrefix: 'selected-item-id',
35 -
  selectedItem: index => `selected-item-id-${index}`,
36 -
  input: 'input-id',
34 +
export function getSelectedItemAtIndex(index) {
35 +
  return screen.getByTestId(dataTestIds.selectedItem(index))
36 +
}
37 +
38 +
export function getSelectedItems() {
39 +
  return screen.queryAllByTestId(new RegExp(dataTestIds.selectedItemPrefix))
40 +
}
41 +
42 +
export async function clickOnSelectedItemAtIndex(index) {
43 +
  await user.click(getSelectedItemAtIndex(index))
44 +
}
45 +
export async function keyDownOnSelectedItemAtIndex(index, key) {
46 +
  const selectedItem = getSelectedItemAtIndex(index)
47 +
48 +
  if (document.activeElement !== selectedItem) {
49 +
    selectedItem.focus()
50 +
  }
51 +
52 +
  await user.keyboard(key)
53 +
}
54 +
55 +
export function focusSelectedItemAtIndex(index) {
56 +
  getSelectedItemAtIndex(index).focus()
57 +
}
58 +
59 +
export async function clickOnInput() {
60 +
  await user.click(getInput())
37 61
}
38 62
39 63
const DropdownMultipleCombobox = ({
40 64
  multipleSelectionProps = {},
41 65
  comboboxProps = {},
42 66
}) => {
43 -
  const {
44 -
    getSelectedItemProps,
45 -
    getDropdownProps,
46 -
    selectedItems,
47 -
  } = useMultipleSelection(multipleSelectionProps)
67 +
  const {getSelectedItemProps, getDropdownProps, selectedItems} =
68 +
    useMultipleSelection(multipleSelectionProps)
48 69
  const {
49 70
    getToggleButtonProps,
50 71
    getLabelProps,
@@ -87,54 +108,12 @@
Loading
87 108
88 109
export const renderMultipleCombobox = props => {
89 110
  const utils = render(<DropdownMultipleCombobox {...props} />)
90 -
  const label = screen.getByText(/choose an element/i)
91 -
  const menu = screen.getByRole('listbox')
92 -
  const input = screen.getByTestId(dataTestIds.input)
93 111
  const rerender = newProps =>
94 112
    utils.rerender(<DropdownMultipleCombobox {...newProps} />)
95 -
  const getSelectedItemAtIndex = index =>
96 -
    screen.getByTestId(dataTestIds.selectedItem(index))
97 -
  const getSelectedItems = () =>
98 -
    screen.queryAllByTestId(new RegExp(dataTestIds.selectedItemPrefix))
99 -
  const clickOnSelectedItemAtIndex = index => {
100 -
    fireEvent.click(getSelectedItemAtIndex(index))
101 -
  }
102 -
  const keyDownOnSelectedItemAtIndex = (index, key, options = {}) => {
103 -
    fireEvent.keyDown(getSelectedItemAtIndex(index), {key, ...options})
104 -
  }
105 -
  const focusSelectedItemAtIndex = index => {
106 -
    getSelectedItemAtIndex(index).focus()
107 -
  }
108 -
  const getA11yStatusContainer = () => screen.queryByRole('status')
109 -
  const focusInput = () => {
110 -
    input.focus()
111 -
  }
112 -
  const keyDownOnInput = (key, options = {}) => {
113 -
    if (document.activeElement !== input) {
114 -
      focusInput()
115 -
    }
116 -
117 -
    fireEvent.keyDown(input, {key, ...options})
118 -
  }
119 -
  const clickOnInput = () => {
120 -
    userEvent.click(input)
121 -
  }
122 113
123 114
  return {
124 115
    ...utils,
125 -
    label,
126 -
    menu,
127 116
    rerender,
128 -
    getSelectedItemAtIndex,
129 -
    clickOnSelectedItemAtIndex,
130 -
    keyDownOnSelectedItemAtIndex,
131 -
    focusSelectedItemAtIndex,
132 -
    getSelectedItems,
133 -
    getA11yStatusContainer,
134 -
    input,
135 -
    keyDownOnInput,
136 -
    focusInput,
137 -
    clickOnInput,
138 117
  }
139 118
}
140 119

@@ -8,13 +8,12 @@
Loading
8 8
import {isReactNative} from '../is.macro'
9 9
import {
10 10
  scrollIntoView,
11 -
  getNextWrappingIndex,
12 11
  getState,
13 12
  generateId,
14 13
  debounce,
15 -
  targetWithinDownshift,
16 14
  validateControlledUnchanged,
17 15
  noop,
16 +
  targetWithinDownshift,
18 17
} from '../utils'
19 18
import setStatus from '../set-a11y-status'
20 19
@@ -214,7 +213,6 @@
Loading
214 213
  stateReducer,
215 214
  getA11ySelectionMessage,
216 215
  scrollIntoView,
217 -
  circularNavigation: false,
218 216
  environment:
219 217
    /* istanbul ignore next (ssr) */
220 218
    typeof window === 'undefined' ? {} : window,
@@ -271,7 +269,7 @@
Loading
271 269
  }
272 270
}
273 271
274 -
function getHighlightedIndexOnOpen(props, state, offset, getItemNodeFromIndex) {
272 +
function getHighlightedIndexOnOpen(props, state, offset) {
275 273
  const {items, initialHighlightedIndex, defaultHighlightedIndex} = props
276 274
  const {selectedItem, highlightedIndex} = state
277 275
@@ -290,16 +288,7 @@
Loading
290 288
    return defaultHighlightedIndex
291 289
  }
292 290
  if (selectedItem) {
293 -
    if (offset === 0) {
294 -
      return items.indexOf(selectedItem)
295 -
    }
296 -
    return getNextWrappingIndex(
297 -
      offset,
298 -
      items.indexOf(selectedItem),
299 -
      items.length,
300 -
      getItemNodeFromIndex,
301 -
      false,
302 -
    )
291 +
    return items.indexOf(selectedItem)
303 292
  }
304 293
  if (offset === 0) {
305 294
    return -1
@@ -316,7 +305,7 @@
Loading
316 305
 * @param {Function} handleBlur Handler on blur from mouse or touch.
317 306
 * @returns {Object} Ref containing whether mouseDown or touchMove event is happening
318 307
 */
319 -
function useMouseAndTouchTracker(
308 +
 function useMouseAndTouchTracker(
320 309
  isOpen,
321 310
  downshiftElementRefs,
322 311
  environment,

@@ -1,17 +1,16 @@
Loading
1 -
/* eslint-disable max-statements */
2 1
import {useRef, useEffect, useCallback, useMemo} from 'react'
3 2
import {
4 3
  getItemIndex,
5 4
  isAcceptedCharacterKey,
6 5
  useControlledReducer,
7 6
  getInitialState,
8 -
  useMouseAndTouchTracker,
9 7
  useGetterPropsCalledChecker,
10 8
  useLatestRef,
11 9
  useA11yMessageSetter,
12 10
  useScrollIntoView,
13 11
  useControlPropsValidator,
14 12
  useElementIds,
13 +
  useMouseAndTouchTracker,
15 14
} from '../utils'
16 15
import {
17 16
  callAllEventHandlers,
@@ -36,8 +35,6 @@
Loading
36 35
    items,
37 36
    scrollIntoView,
38 37
    environment,
39 -
    initialIsOpen,
40 -
    defaultIsOpen,
41 38
    itemToString,
42 39
    getA11ySelectionMessage,
43 40
    getA11yStatusMessage,
@@ -55,8 +52,6 @@
Loading
55 52
  const toggleButtonRef = useRef(null)
56 53
  const menuRef = useRef(null)
57 54
  const itemRefs = useRef({})
58 -
  // used not to trigger menu blur action in some scenarios.
59 -
  const shouldBlurRef = useRef(true)
60 55
  // used to keep the inputValue clearTimeout object between renders.
61 56
  const clearTimeoutRef = useRef(null)
62 57
  // prevent id re-generation between renders.
@@ -139,34 +134,6 @@
Loading
139 134
    props,
140 135
    state,
141 136
  })
142 -
  /* Controls the focus on the menu or the toggle button. */
143 -
  useEffect(() => {
144 -
    // Don't focus menu on first render.
145 -
    if (isInitialMountRef.current) {
146 -
      // Unless it was initialised as open.
147 -
      if ((initialIsOpen || defaultIsOpen || isOpen) && menuRef.current) {
148 -
        menuRef.current.focus()
149 -
      }
150 -
      return
151 -
    }
152 -
    // Focus menu on open.
153 -
    if (isOpen) {
154 -
      // istanbul ignore else
155 -
      if (menuRef.current) {
156 -
        menuRef.current.focus()
157 -
      }
158 -
      return
159 -
    }
160 -
    // Focus toggleButton on close, but not if it was closed with (Shift+)Tab.
161 -
    if (environment.document.activeElement === menuRef.current) {
162 -
      // istanbul ignore else
163 -
      if (toggleButtonRef.current) {
164 -
        shouldBlurRef.current = false
165 -
        toggleButtonRef.current.focus()
166 -
      }
167 -
    }
168 -
    // eslint-disable-next-line react-hooks/exhaustive-deps
169 -
  }, [isOpen])
170 137
  useEffect(() => {
171 138
    if (isInitialMountRef.current) {
172 139
      return
@@ -181,7 +148,7 @@
Loading
181 148
    environment,
182 149
    () => {
183 150
      dispatch({
184 -
        type: stateChangeTypes.MenuBlur,
151 +
        type: stateChangeTypes.ToggleButtonBlur,
185 152
      })
186 153
    },
187 154
  )
@@ -209,7 +176,7 @@
Loading
209 176
        dispatch({
210 177
          type: stateChangeTypes.ToggleButtonKeyDownArrowDown,
211 178
          getItemNodeFromIndex,
212 -
          shiftKey: event.shiftKey,
179 +
          altKey: event.altKey,
213 180
        })
214 181
      },
215 182
      ArrowUp(event) {
@@ -218,37 +185,14 @@
Loading
218 185
        dispatch({
219 186
          type: stateChangeTypes.ToggleButtonKeyDownArrowUp,
220 187
          getItemNodeFromIndex,
221 -
          shiftKey: event.shiftKey,
222 -
        })
223 -
      },
224 -
    }),
225 -
    [dispatch, getItemNodeFromIndex],
226 -
  )
227 -
  const menuKeyDownHandlers = useMemo(
228 -
    () => ({
229 -
      ArrowDown(event) {
230 -
        event.preventDefault()
231 -
232 -
        dispatch({
233 -
          type: stateChangeTypes.MenuKeyDownArrowDown,
234 -
          getItemNodeFromIndex,
235 -
          shiftKey: event.shiftKey,
236 -
        })
237 -
      },
238 -
      ArrowUp(event) {
239 -
        event.preventDefault()
240 -
241 -
        dispatch({
242 -
          type: stateChangeTypes.MenuKeyDownArrowUp,
243 -
          getItemNodeFromIndex,
244 -
          shiftKey: event.shiftKey,
188 +
          altKey: event.altKey,
245 189
        })
246 190
      },
247 191
      Home(event) {
248 192
        event.preventDefault()
249 193
250 194
        dispatch({
251 -
          type: stateChangeTypes.MenuKeyDownHome,
195 +
          type: stateChangeTypes.ToggleButtonKeyDownHome,
252 196
          getItemNodeFromIndex,
253 197
        })
254 198
      },
@@ -256,31 +200,57 @@
Loading
256 200
        event.preventDefault()
257 201
258 202
        dispatch({
259 -
          type: stateChangeTypes.MenuKeyDownEnd,
203 +
          type: stateChangeTypes.ToggleButtonKeyDownEnd,
260 204
          getItemNodeFromIndex,
261 205
        })
262 206
      },
263 207
      Escape() {
264 -
        dispatch({
265 -
          type: stateChangeTypes.MenuKeyDownEscape,
266 -
        })
208 +
        if (latest.current.state.isOpen) {
209 +
          dispatch({
210 +
            type: stateChangeTypes.ToggleButtonKeyDownEscape,
211 +
          })
212 +
        }
267 213
      },
268 214
      Enter(event) {
269 -
        event.preventDefault()
215 +
        if (latest.current.state.isOpen) {
216 +
          event.preventDefault()
270 217
271 -
        dispatch({
272 -
          type: stateChangeTypes.MenuKeyDownEnter,
273 -
        })
218 +
          dispatch({
219 +
            type: stateChangeTypes.ToggleButtonKeyDownEnter,
220 +
          })
221 +
        }
222 +
      },
223 +
      PageUp(event) {
224 +
        if (latest.current.state.isOpen) {
225 +
          event.preventDefault()
226 +
227 +
          dispatch({
228 +
            type: stateChangeTypes.ToggleButtonKeyDownPageUp,
229 +
            getItemNodeFromIndex,
230 +
          })
231 +
        }
232 +
      },
233 +
      PageDown(event) {
234 +
        if (latest.current.state.isOpen) {
235 +
          event.preventDefault()
236 +
237 +
          dispatch({
238 +
            type: stateChangeTypes.ToggleButtonKeyDownPageDown,
239 +
            getItemNodeFromIndex,
240 +
          })
241 +
        }
274 242
      },
275 243
      ' '(event) {
276 -
        event.preventDefault()
244 +
        if (latest.current.state.isOpen) {
245 +
          event.preventDefault()
277 246
278 -
        dispatch({
279 -
          type: stateChangeTypes.MenuKeyDownSpaceButton,
280 -
        })
247 +
          dispatch({
248 +
            type: stateChangeTypes.ToggleButtonKeyDownSpaceButton,
249 +
          })
250 +
        }
281 251
      },
282 252
    }),
283 -
    [dispatch, getItemNodeFromIndex],
253 +
    [dispatch, getItemNodeFromIndex, latest],
284 254
  )
285 255
286 256
  // Action functions.
@@ -345,32 +315,6 @@
Loading
345 315
      {onMouseLeave, refKey = 'ref', onKeyDown, onBlur, ref, ...rest} = {},
346 316
      {suppressRefError = false} = {},
347 317
    ) => {
348 -
      const latestState = latest.current.state
349 -
      const menuHandleKeyDown = event => {
350 -
        const key = normalizeArrowKey(event)
351 -
        if (key && menuKeyDownHandlers[key]) {
352 -
          menuKeyDownHandlers[key](event)
353 -
        } else if (isAcceptedCharacterKey(key)) {
354 -
          dispatch({
355 -
            type: stateChangeTypes.MenuKeyDownCharacter,
356 -
            key,
357 -
            getItemNodeFromIndex,
358 -
          })
359 -
        }
360 -
      }
361 -
      const menuHandleBlur = () => {
362 -
        // if the blur was a result of selection, we don't trigger this action.
363 -
        if (shouldBlurRef.current === false) {
364 -
          shouldBlurRef.current = true
365 -
          return
366 -
        }
367 -
368 -
        const shouldBlur = !mouseAndTouchTrackersRef.current.isMouseDown
369 -
        /* istanbul ignore else */
370 -
        if (shouldBlur) {
371 -
          dispatch({type: stateChangeTypes.MenuBlur})
372 -
        }
373 -
      }
374 318
      const menuHandleMouseLeave = () => {
375 319
        dispatch({
376 320
          type: stateChangeTypes.MenuMouseLeave,
@@ -387,38 +331,33 @@
Loading
387 331
        role: 'listbox',
388 332
        'aria-labelledby': elementIds.labelId,
389 333
        tabIndex: -1,
390 -
        ...(latestState.isOpen &&
391 -
          latestState.highlightedIndex > -1 && {
392 -
            'aria-activedescendant': elementIds.getItemId(
393 -
              latestState.highlightedIndex,
394 -
            ),
395 -
          }),
396 334
        onMouseLeave: callAllEventHandlers(onMouseLeave, menuHandleMouseLeave),
397 -
        onKeyDown: callAllEventHandlers(onKeyDown, menuHandleKeyDown),
398 -
        onBlur: callAllEventHandlers(onBlur, menuHandleBlur),
399 335
        ...rest,
400 336
      }
401 337
    },
402 -
    [
403 -
      dispatch,
404 -
      latest,
405 -
      menuKeyDownHandlers,
406 -
      mouseAndTouchTrackersRef,
407 -
      setGetterPropCallInfo,
408 -
      elementIds,
409 -
      getItemNodeFromIndex,
410 -
    ],
338 +
    [dispatch, setGetterPropCallInfo, elementIds],
411 339
  )
412 340
  const getToggleButtonProps = useCallback(
413 341
    (
414 -
      {onClick, onKeyDown, refKey = 'ref', ref, ...rest} = {},
342 +
      {onBlur, onClick, onKeyDown, refKey = 'ref', ref, ...rest} = {},
415 343
      {suppressRefError = false} = {},
416 344
    ) => {
345 +
      const latestState = latest.current.state
417 346
      const toggleButtonHandleClick = () => {
418 347
        dispatch({
419 348
          type: stateChangeTypes.ToggleButtonClick,
420 349
        })
421 350
      }
351 +
      const toggleButtonHandleBlur = () => {
352 +
        if (
353 +
          latestState.isOpen &&
354 +
          !mouseAndTouchTrackersRef.current.isMouseDown
355 +
        ) {
356 +
          dispatch({
357 +
            type: stateChangeTypes.ToggleButtonBlur,
358 +
          })
359 +
        }
360 +
      }
422 361
      const toggleButtonHandleKeyDown = event => {
423 362
        const key = normalizeArrowKey(event)
424 363
        if (key && toggleButtonKeyDownHandlers[key]) {
@@ -435,10 +374,18 @@
Loading
435 374
        [refKey]: handleRefs(ref, toggleButtonNode => {
436 375
          toggleButtonRef.current = toggleButtonNode
437 376
        }),
438 -
        id: elementIds.toggleButtonId,
439 -
        'aria-haspopup': 'listbox',
377 +
        'aria-activedescendant':
378 +
          latestState.isOpen && latestState.highlightedIndex > -1
379 +
            ? elementIds.getItemId(latestState.highlightedIndex)
380 +
            : '',
381 +
        'aria-controls': elementIds.menuId,
440 382
        'aria-expanded': latest.current.state.isOpen,
383 +
        'aria-haspopup': 'listbox',
441 384
        'aria-labelledby': `${elementIds.labelId} ${elementIds.toggleButtonId}`,
385 +
        id: elementIds.toggleButtonId,
386 +
        role: 'combobox',
387 +
        tabIndex: 0,
388 +
        onBlur: callAllEventHandlers(onBlur, toggleButtonHandleBlur),
442 389
        ...rest,
443 390
      }
444 391
@@ -463,18 +410,19 @@
Loading
463 410
      return toggleProps
464 411
    },
465 412
    [
466 -
      dispatch,
467 413
      latest,
468 -
      toggleButtonKeyDownHandlers,
469 -
      setGetterPropCallInfo,
470 414
      elementIds,
415 +
      setGetterPropCallInfo,
416 +
      dispatch,
417 +
      mouseAndTouchTrackersRef,
418 +
      toggleButtonKeyDownHandlers,
471 419
      getItemNodeFromIndex,
472 420
    ],
473 421
  )
474 422
  const getItemProps = useCallback(
475 423
    ({
476 -
      item,
477 -
      index,
424 +
      item: itemProp,
425 +
      index: indexProp,
478 426
      onMouseMove,
479 427
      onClick,
480 428
      refKey = 'ref',
@@ -483,6 +431,9 @@
Loading
483 431
      ...rest
484 432
    } = {}) => {
485 433
      const {state: latestState, props: latestProps} = latest.current
434 +
      const item = itemProp ?? items[indexProp]
435 +
      const index = getItemIndex(indexProp, item, latestProps.items)
436 +
486 437
      const itemHandleMouseMove = () => {
487 438
        if (index === latestState.highlightedIndex) {
488 439
          return
@@ -508,7 +459,7 @@
Loading
508 459
      const itemProps = {
509 460
        disabled,
510 461
        role: 'option',
511 -
        'aria-selected': `${itemIndex === latestState.highlightedIndex}`,
462 +
        'aria-selected': `${item === selectedItem}`,
512 463
        id: elementIds.getItemId(itemIndex),
513 464
        [refKey]: handleRefs(ref, itemNode => {
514 465
          if (itemNode) {
@@ -529,7 +480,7 @@
Loading
529 480
530 481
      return itemProps
531 482
    },
532 -
    [dispatch, latest, shouldScrollRef, elementIds],
483 +
    [latest, items, selectedItem, elementIds, shouldScrollRef, dispatch],
533 484
  )
534 485
535 486
  return {

@@ -6,7 +6,7 @@
Loading
6 6
7 7
/* eslint-disable complexity */
8 8
export default function downshiftSelectReducer(state, action) {
9 -
  const {type, props, shiftKey} = action
9 +
  const {type, props, altKey} = action
10 10
  let changes
11 11
12 12
  switch (type) {
@@ -22,11 +22,13 @@
Loading
22 22
      {
23 23
        const lowercasedKey = action.key
24 24
        const inputValue = `${state.inputValue}${lowercasedKey}`
25 -
        const itemIndex = getItemIndexByCharacterKey({
26 -
          keysSoFar: inputValue,
27 -
          highlightedIndex: state.selectedItem
25 +
        const prevHighlightedIndex =
26 +
          !state.isOpen && state.selectedItem
28 27
            ? props.items.indexOf(state.selectedItem)
29 -
            : -1,
28 +
            : state.highlightedIndex
29 +
        const highlightedIndex = getItemIndexByCharacterKey({
30 +
          keysSoFar: inputValue,
31 +
          highlightedIndex: prevHighlightedIndex,
30 32
          items: props.items,
31 33
          itemToString: props.itemToString,
32 34
          getItemNodeFromIndex: action.getItemNodeFromIndex,
@@ -34,39 +36,61 @@
Loading
34 36
35 37
        changes = {
36 38
          inputValue,
37 -
          ...(itemIndex >= 0 && {
38 -
            selectedItem: props.items[itemIndex],
39 -
          }),
39 +
          highlightedIndex,
40 +
          isOpen: true,
40 41
        }
41 42
      }
42 43
43 44
      break
44 45
    case stateChangeTypes.ToggleButtonKeyDownArrowDown:
45 -
      changes = {
46 -
        highlightedIndex: getHighlightedIndexOnOpen(
47 -
          props,
48 -
          state,
49 -
          1,
50 -
          action.getItemNodeFromIndex,
51 -
        ),
52 -
        isOpen: true,
46 +
      {
47 +
        const highlightedIndex = state.isOpen
48 +
          ? getNextWrappingIndex(
49 +
              1,
50 +
              state.highlightedIndex,
51 +
              props.items.length,
52 +
              action.getItemNodeFromIndex,
53 +
              false,
54 +
            )
55 +
          : altKey && state.selectedItem == null
56 +
          ? -1
57 +
          : getHighlightedIndexOnOpen(props, state, 1)
58 +
        changes = {
59 +
          highlightedIndex,
60 +
          isOpen: true,
61 +
        }
53 62
      }
54 63
55 64
      break
56 65
    case stateChangeTypes.ToggleButtonKeyDownArrowUp:
57 -
      changes = {
58 -
        highlightedIndex: getHighlightedIndexOnOpen(
59 -
          props,
60 -
          state,
61 -
          -1,
62 -
          action.getItemNodeFromIndex,
63 -
        ),
64 -
        isOpen: true,
66 +
      if (state.isOpen && altKey) {
67 +
        changes = {
68 +
          isOpen: getDefaultValue(props, 'isOpen'),
69 +
          highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
70 +
          ...(state.highlightedIndex >= 0 && {
71 +
            selectedItem: props.items[state.highlightedIndex],
72 +
          }),
73 +
        }
74 +
      } else {
75 +
        const highlightedIndex = state.isOpen
76 +
          ? getNextWrappingIndex(
77 +
              -1,
78 +
              state.highlightedIndex,
79 +
              props.items.length,
80 +
              action.getItemNodeFromIndex,
81 +
              false,
82 +
            )
83 +
          : getHighlightedIndexOnOpen(props, state, -1)
84 +
        changes = {
85 +
          highlightedIndex,
86 +
          isOpen: true,
87 +
        }
65 88
      }
66 89
67 90
      break
68 -
    case stateChangeTypes.MenuKeyDownEnter:
69 -
    case stateChangeTypes.MenuKeyDownSpaceButton:
91 +
    // only triggered when menu is open.
92 +
    case stateChangeTypes.ToggleButtonKeyDownEnter:
93 +
    case stateChangeTypes.ToggleButtonKeyDownSpaceButton:
70 94
      changes = {
71 95
        isOpen: getDefaultValue(props, 'isOpen'),
72 96
        highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
@@ -76,7 +100,7 @@
Loading
76 100
      }
77 101
78 102
      break
79 -
    case stateChangeTypes.MenuKeyDownHome:
103 +
    case stateChangeTypes.ToggleButtonKeyDownHome:
80 104
      changes = {
81 105
        highlightedIndex: getNextNonDisabledIndex(
82 106
          1,
@@ -85,10 +109,11 @@
Loading
85 109
          action.getItemNodeFromIndex,
86 110
          false,
87 111
        ),
112 +
        isOpen: true,
88 113
      }
89 114
90 115
      break
91 -
    case stateChangeTypes.MenuKeyDownEnd:
116 +
    case stateChangeTypes.ToggleButtonKeyDownEnd:
92 117
      changes = {
93 118
        highlightedIndex: getNextNonDisabledIndex(
94 119
          -1,
@@ -97,67 +122,49 @@
Loading
97 122
          action.getItemNodeFromIndex,
98 123
          false,
99 124
        ),
125 +
        isOpen: true,
100 126
      }
101 127
102 128
      break
103 -
    case stateChangeTypes.MenuKeyDownEscape:
104 -
      changes = {
105 -
        isOpen: false,
106 -
        highlightedIndex: -1,
107 -
      }
108 -
109 -
      break
110 -
    case stateChangeTypes.MenuBlur:
111 -
      changes = {
112 -
        isOpen: false,
113 -
        highlightedIndex: -1,
114 -
      }
115 -
116 -
      break
117 -
    case stateChangeTypes.MenuKeyDownCharacter:
118 -
      {
119 -
        const lowercasedKey = action.key
120 -
        const inputValue = `${state.inputValue}${lowercasedKey}`
121 -
        const highlightedIndex = getItemIndexByCharacterKey({
122 -
          keysSoFar: inputValue,
123 -
          highlightedIndex: state.highlightedIndex,
124 -
          items: props.items,
125 -
          itemToString: props.itemToString,
126 -
          getItemNodeFromIndex: action.getItemNodeFromIndex,
127 -
        })
128 -
129 -
        changes = {
130 -
          inputValue,
131 -
          ...(highlightedIndex >= 0 && {
132 -
            highlightedIndex,
133 -
          }),
134 -
        }
135 -
      }
136 -
      break
137 -
    case stateChangeTypes.MenuKeyDownArrowDown:
129 +
    case stateChangeTypes.ToggleButtonKeyDownPageUp:
138 130
      changes = {
139 131
        highlightedIndex: getNextWrappingIndex(
140 -
          shiftKey ? 5 : 1,
132 +
          -10,
141 133
          state.highlightedIndex,
142 134
          props.items.length,
143 135
          action.getItemNodeFromIndex,
144 -
          props.circularNavigation,
136 +
          false,
145 137
        ),
146 138
      }
147 -
148 139
      break
149 -
    case stateChangeTypes.MenuKeyDownArrowUp:
140 +
    case stateChangeTypes.ToggleButtonKeyDownPageDown:
150 141
      changes = {
151 142
        highlightedIndex: getNextWrappingIndex(
152 -
          shiftKey ? -5 : -1,
143 +
          10,
153 144
          state.highlightedIndex,
154 145
          props.items.length,
155 146
          action.getItemNodeFromIndex,
156 -
          props.circularNavigation,
147 +
          false,
157 148
        ),
158 149
      }
159 150
      break
151 +
    case stateChangeTypes.ToggleButtonKeyDownEscape:
152 +
      changes = {
153 +
        isOpen: false,
154 +
        highlightedIndex: -1,
155 +
      }
160 156
157 +
      break
158 +
    case stateChangeTypes.ToggleButtonBlur:
159 +
      changes = {
160 +
        isOpen: false,
161 +
        highlightedIndex: -1,
162 +
        ...(state.highlightedIndex >= 0 && {
163 +
          selectedItem: props.items[state.highlightedIndex],
164 +
        }),
165 +
      }
166 +
167 +
      break
161 168
    case stateChangeTypes.FunctionSelectItem:
162 169
      changes = {
163 170
        selectedItem: action.selectedItem,

@@ -39,7 +39,6 @@
Loading
39 39
  itemToString: PropTypes.func,
40 40
  getA11yStatusMessage: PropTypes.func,
41 41
  getA11ySelectionMessage: PropTypes.func,
42 -
  circularNavigation: PropTypes.bool,
43 42
  highlightedIndex: PropTypes.number,
44 43
  defaultHighlightedIndex: PropTypes.number,
45 44
  initialHighlightedIndex: PropTypes.number,
Files Coverage
src 100.00%
Project Totals (18 files) 100.00%

No yaml found.

Create your codecov.yml to customize your Codecov experience

Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading