Skip to content

Commit 4bf894b

Browse files
authored
fix(highlightedIndex): do not highlight disabled items (#1601)
1 parent 602ee51 commit 4bf894b

14 files changed

+531
-80
lines changed

src/hooks/reducer.js

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import {getHighlightedIndexOnOpen, getDefaultValue} from './utils'
1+
import {
2+
getHighlightedIndexOnOpen,
3+
getDefaultValue,
4+
getDefaultHighlightedIndex,
5+
} from './utils'
26

37
export default function downshiftCommonReducer(
48
state,
@@ -46,7 +50,12 @@ export default function downshiftCommonReducer(
4650
break
4751
case stateChangeTypes.FunctionSetHighlightedIndex:
4852
changes = {
49-
highlightedIndex: action.highlightedIndex,
53+
highlightedIndex: props.isItemDisabled(
54+
props.items[action.highlightedIndex],
55+
action.highlightedIndex,
56+
)
57+
? -1
58+
: action.highlightedIndex,
5059
}
5160

5261
break
@@ -58,7 +67,7 @@ export default function downshiftCommonReducer(
5867
break
5968
case stateChangeTypes.FunctionReset:
6069
changes = {
61-
highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
70+
highlightedIndex: getDefaultHighlightedIndex(props),
6271
isOpen: getDefaultValue(props, 'isOpen'),
6372
selectedItem: getDefaultValue(props, 'selectedItem'),
6473
inputValue: getDefaultValue(props, 'inputValue'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`getInputProps event handlers on key down arrow down defaultHighlightedIndex is ignored if item is disabled 1`] = `
4+
[
5+
[
6+
Americium,
7+
2,
8+
],
9+
[
10+
Americium,
11+
2,
12+
],
13+
[
14+
Neptunium,
15+
0,
16+
],
17+
]
18+
`;
19+
20+
exports[`getInputProps event handlers on key down arrow down initialHighlightedIndex is ignored if item is disabled 1`] = `
21+
[
22+
[
23+
Americium,
24+
2,
25+
],
26+
[
27+
Neptunium,
28+
0,
29+
],
30+
]
31+
`;
32+
33+
exports[`getInputProps event handlers on key down arrow up defaultHighlightedIndex is ignored if item is disabled 1`] = `
34+
[
35+
[
36+
Americium,
37+
2,
38+
],
39+
[
40+
Americium,
41+
2,
42+
],
43+
[
44+
Oganesson,
45+
25,
46+
],
47+
]
48+
`;
49+
50+
exports[`getInputProps event handlers on key down arrow up initialHighlightedIndex is ignored if item is disabled 1`] = `
51+
[
52+
[
53+
Americium,
54+
2,
55+
],
56+
[
57+
Oganesson,
58+
25,
59+
],
60+
]
61+
`;

src/hooks/useCombobox/__tests__/getInputProps.test.js

+97-50
Original file line numberDiff line numberDiff line change
@@ -436,47 +436,64 @@ describe('getInputProps', () => {
436436
})
437437

438438
describe('event handlers', () => {
439-
test('on change should open the menu and keep the input value', async () => {
440-
renderCombobox()
439+
describe('on change', () => {
440+
test('should open the menu and keep the input value', async () => {
441+
renderCombobox()
441442

442-
await changeInputValue('california')
443+
await changeInputValue('california')
443444

444-
expect(getItems()).toHaveLength(items.length)
445-
expect(getInput()).toHaveValue('california')
446-
})
445+
expect(getItems()).toHaveLength(items.length)
446+
expect(getInput()).toHaveValue('california')
447+
})
447448

448-
test('on change should remove the highlightedIndex', async () => {
449-
renderCombobox({initialHighlightedIndex: 2})
449+
test('should remove the highlightedIndex', async () => {
450+
renderCombobox({initialHighlightedIndex: 2})
450451

451-
await changeInputValue('california')
452+
await changeInputValue('california')
452453

453-
expect(getInput()).toHaveAttribute('aria-activedescendant', '')
454-
})
454+
expect(getInput()).toHaveAttribute('aria-activedescendant', '')
455+
})
455456

456-
test('on change should reset to defaultHighlightedIndex', async () => {
457-
const defaultHighlightedIndex = 2
458-
renderCombobox({defaultHighlightedIndex})
457+
test('should reset to defaultHighlightedIndex', async () => {
458+
const defaultHighlightedIndex = 2
459+
renderCombobox({defaultHighlightedIndex})
459460

460-
await changeInputValue('a')
461+
await changeInputValue('a')
461462

462-
expect(getInput()).toHaveAttribute(
463-
'aria-activedescendant',
464-
defaultIds.getItemId(defaultHighlightedIndex),
465-
)
463+
expect(getInput()).toHaveAttribute(
464+
'aria-activedescendant',
465+
defaultIds.getItemId(defaultHighlightedIndex),
466+
)
466467

467-
await keyDownOnInput('{ArrowDown}')
468+
await keyDownOnInput('{ArrowDown}')
468469

469-
expect(getInput()).toHaveAttribute(
470-
'aria-activedescendant',
471-
defaultIds.getItemId(defaultHighlightedIndex + 1),
472-
)
470+
expect(getInput()).toHaveAttribute(
471+
'aria-activedescendant',
472+
defaultIds.getItemId(defaultHighlightedIndex + 1),
473+
)
473474

474-
await changeInputValue('a')
475+
await changeInputValue('a')
475476

476-
expect(getInput()).toHaveAttribute(
477-
'aria-activedescendant',
478-
defaultIds.getItemId(defaultHighlightedIndex),
479-
)
477+
expect(getInput()).toHaveAttribute(
478+
'aria-activedescendant',
479+
defaultIds.getItemId(defaultHighlightedIndex),
480+
)
481+
})
482+
483+
test('should not reset to defaultHighlightedIndex if disabled', async () => {
484+
const defaultHighlightedIndex = 2
485+
renderCombobox({
486+
defaultHighlightedIndex,
487+
isItemDisabled(_item, index) {
488+
return index === defaultHighlightedIndex
489+
},
490+
})
491+
492+
await keyDownOnInput('{ArrowDown}')
493+
await changeInputValue('a')
494+
495+
expect(getInput()).toHaveAttribute('aria-activedescendant', '')
496+
})
480497
})
481498

482499
describe('on key down', () => {
@@ -579,11 +596,14 @@ describe('getInputProps', () => {
579596

580597
test('initialHighlightedIndex is ignored if item is disabled', async () => {
581598
const initialHighlightedIndex = 2
599+
const isItemDisabled = jest
600+
.fn()
601+
.mockImplementation(
602+
item => items.indexOf(item) === initialHighlightedIndex,
603+
)
582604
renderCombobox({
583605
initialHighlightedIndex,
584-
isItemDisabled(item) {
585-
return items.indexOf(item) === initialHighlightedIndex
586-
},
606+
isItemDisabled,
587607
})
588608

589609
await keyDownOnInput('{ArrowUp}')
@@ -592,6 +612,7 @@ describe('getInputProps', () => {
592612
'aria-activedescendant',
593613
defaultIds.getItemId(items.length - 1),
594614
)
615+
expect(isItemDisabled.mock.calls.slice(0, 2)).toMatchSnapshot()
595616
})
596617

597618
test('initialHighlightedIndex is ignored and defaultHighlightedIndex is chosen if enabled', async () => {
@@ -615,11 +636,14 @@ describe('getInputProps', () => {
615636

616637
test('defaultHighlightedIndex is ignored if item is disabled', async () => {
617638
const defaultHighlightedIndex = 2
639+
const isItemDisabled = jest
640+
.fn()
641+
.mockImplementation(
642+
item => items.indexOf(item) === defaultHighlightedIndex,
643+
)
618644
renderCombobox({
619645
defaultHighlightedIndex,
620-
isItemDisabled(item) {
621-
return items.indexOf(item) === defaultHighlightedIndex
622-
},
646+
isItemDisabled,
623647
})
624648

625649
await keyDownOnInput('{ArrowUp}')
@@ -628,6 +652,7 @@ describe('getInputProps', () => {
628652
'aria-activedescendant',
629653
defaultIds.getItemId(items.length - 1),
630654
)
655+
expect(isItemDisabled.mock.calls.slice(0, 3)).toMatchSnapshot()
631656
})
632657

633658
test('both defaultHighlightedIndex and initialHighlightedIndex are ignored if items are disabled', async () => {
@@ -901,11 +926,14 @@ describe('getInputProps', () => {
901926

902927
test('initialHighlightedIndex is ignored if item is disabled', async () => {
903928
const initialHighlightedIndex = 2
929+
const isItemDisabled = jest
930+
.fn()
931+
.mockImplementation(
932+
item => items.indexOf(item) === initialHighlightedIndex,
933+
)
904934
renderCombobox({
905935
initialHighlightedIndex,
906-
isItemDisabled(item) {
907-
return items.indexOf(item) === initialHighlightedIndex
908-
},
936+
isItemDisabled,
909937
})
910938

911939
await keyDownOnInput('{ArrowDown}')
@@ -914,6 +942,7 @@ describe('getInputProps', () => {
914942
'aria-activedescendant',
915943
defaultIds.getItemId(0),
916944
)
945+
expect(isItemDisabled.mock.calls.slice(0, 2)).toMatchSnapshot()
917946
})
918947

919948
test('initialHighlightedIndex is ignored and defaultHighlightedIndex is chosen if enabled', async () => {
@@ -937,11 +966,14 @@ describe('getInputProps', () => {
937966

938967
test('defaultHighlightedIndex is ignored if item is disabled', async () => {
939968
const defaultHighlightedIndex = 2
969+
const isItemDisabled = jest
970+
.fn()
971+
.mockImplementation(
972+
item => items.indexOf(item) === defaultHighlightedIndex,
973+
)
940974
renderCombobox({
941975
defaultHighlightedIndex,
942-
isItemDisabled(item) {
943-
return items.indexOf(item) === defaultHighlightedIndex
944-
},
976+
isItemDisabled,
945977
})
946978

947979
await keyDownOnInput('{ArrowDown}')
@@ -950,6 +982,7 @@ describe('getInputProps', () => {
950982
'aria-activedescendant',
951983
defaultIds.getItemId(0),
952984
)
985+
expect(isItemDisabled.mock.calls.slice(0, 3)).toMatchSnapshot()
953986
})
954987

955988
test('both defaultHighlightedIndex and initialHighlightedIndex are ignored if items are disabled', async () => {
@@ -1859,7 +1892,7 @@ describe('getInputProps', () => {
18591892
renderCombobox({
18601893
initialIsOpen: true,
18611894
initialHighlightedIndex,
1862-
environment: undefined
1895+
environment: undefined,
18631896
})
18641897

18651898
await tab()
@@ -1967,19 +2000,26 @@ describe('getInputProps', () => {
19672000
)
19682001
})
19692002

1970-
19712003
test('initialHighlightedIndex is ignored if item is disabled', async () => {
19722004
const initialHighlightedIndex = 2
2005+
const isItemDisabled = jest
2006+
.fn()
2007+
.mockImplementation(
2008+
item => items.indexOf(item) === initialHighlightedIndex,
2009+
)
19732010
renderCombobox({
19742011
initialHighlightedIndex,
1975-
isItemDisabled(item) {
1976-
return items.indexOf(item) === initialHighlightedIndex
1977-
},
2012+
isItemDisabled,
19782013
})
19792014

19802015
await clickOnInput()
19812016

19822017
expect(getInput()).toHaveAttribute('aria-activedescendant', '')
2018+
expect(isItemDisabled).toHaveBeenNthCalledWith(
2019+
1,
2020+
items[initialHighlightedIndex],
2021+
initialHighlightedIndex,
2022+
)
19832023
})
19842024

19852025
test('initialHighlightedIndex is ignored and defaultHighlightedIndex is chosen if enabled', async () => {
@@ -2003,16 +2043,24 @@ describe('getInputProps', () => {
20032043

20042044
test('defaultHighlightedIndex is ignored if item is disabled', async () => {
20052045
const defaultHighlightedIndex = 2
2046+
const isItemDisabled = jest
2047+
.fn()
2048+
.mockImplementation(
2049+
item => items.indexOf(item) === defaultHighlightedIndex,
2050+
)
20062051
renderCombobox({
20072052
defaultHighlightedIndex,
2008-
isItemDisabled(item) {
2009-
return items.indexOf(item) === defaultHighlightedIndex
2010-
},
2053+
isItemDisabled,
20112054
})
20122055

20132056
await clickOnInput()
20142057

20152058
expect(getInput()).toHaveAttribute('aria-activedescendant', '')
2059+
expect(isItemDisabled).toHaveBeenNthCalledWith(
2060+
1,
2061+
items[defaultHighlightedIndex],
2062+
defaultHighlightedIndex,
2063+
)
20162064
})
20172065

20182066
test('both defaultHighlightedIndex and initialHighlightedIndex are ignored if items are disabled', async () => {
@@ -2032,7 +2080,6 @@ describe('getInputProps', () => {
20322080

20332081
expect(getInput()).toHaveAttribute('aria-activedescendant', '')
20342082
})
2035-
20362083
})
20372084
})
20382085

src/hooks/useCombobox/__tests__/getItemProps.test.js

+19
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,25 @@ describe('getItemProps', () => {
346346
defaultIds.getItemId(defaultHighlightedIndex),
347347
)
348348
})
349+
350+
test('it selects the item and resets to user defined defaults, but considers disabled status for an item', async () => {
351+
const index = 1
352+
const defaultHighlightedIndex = 2
353+
renderCombobox({
354+
defaultIsOpen: true,
355+
defaultHighlightedIndex,
356+
isItemDisabled(_item, idx) {
357+
return idx === defaultHighlightedIndex
358+
},
359+
})
360+
const input = getInput()
361+
362+
await clickOnItemAtIndex(index)
363+
364+
expect(input).toHaveValue(items[index])
365+
expect(getItems()).toHaveLength(items.length)
366+
expect(input).toHaveAttribute('aria-activedescendant', '')
367+
})
349368
})
350369
})
351370

0 commit comments

Comments
 (0)