-
Notifications
You must be signed in to change notification settings - Fork 51
fix(Dropdown): reset highlighted index on input change #1168
Changes from all commits
6b96fa0
2591488
56444dc
2104798
963f3f7
0006711
40e3ef9
347cb8c
9e956d2
77ce9fd
6ae5c03
b61502c
5ff41cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ import * as React from 'react' | |
| import * as keyboardKey from 'keyboard-key' | ||
|
|
||
| import Dropdown from 'src/components/Dropdown/Dropdown' | ||
| import DropdownSearchInput from 'src/components/Dropdown/DropdownSearchInput' | ||
| import { isConformant } from 'test/specs/commonTests' | ||
| import { mountWithProvider } from 'test/utils' | ||
|
|
||
|
|
@@ -40,7 +41,7 @@ describe('Dropdown', () => { | |
| onOpenChange.mockReset() | ||
| }) | ||
|
|
||
| it('calls onOpen with highlightedIndex on click', () => { | ||
| it('has the provided prop value when opened by click', () => { | ||
| const highlightedIndex = 1 | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown highlightedIndex={highlightedIndex} onOpenChange={onOpenChange} items={items} />, | ||
|
|
@@ -57,7 +58,7 @@ describe('Dropdown', () => { | |
| ) | ||
| }) | ||
|
|
||
| it('calls onOpen with highlightedIndex + 1 on arrow down', () => { | ||
| it('has the (provided prop value + 1) when opened by ArrowDown', () => { | ||
| const highlightedIndex = 1 | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown highlightedIndex={highlightedIndex} onOpenChange={onOpenChange} items={items} />, | ||
|
|
@@ -77,7 +78,7 @@ describe('Dropdown', () => { | |
| ) | ||
| }) | ||
|
|
||
| it('calls onOpen with highlightedIndex - 1 on arrow down', () => { | ||
| it('has the provided (prop value - 1) when opened by ArrowUp', () => { | ||
| const highlightedIndex = 1 | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown highlightedIndex={highlightedIndex} onOpenChange={onOpenChange} items={items} />, | ||
|
|
@@ -97,7 +98,7 @@ describe('Dropdown', () => { | |
| ) | ||
| }) | ||
|
|
||
| it('calls onOpen with highlightedIndex wrapped to 0 on arrow down', () => { | ||
| it('is 0 when the provided prop value is last item index and opened by ArrowDown', () => { | ||
| const highlightedIndex = items.length - 1 | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown highlightedIndex={highlightedIndex} onOpenChange={onOpenChange} items={items} />, | ||
|
|
@@ -117,7 +118,7 @@ describe('Dropdown', () => { | |
| ) | ||
| }) | ||
|
|
||
| it('calls onOpen with highlightedIndex wrapped to items.length - 1 on arrow up', () => { | ||
| it('is last item index when the provided prop value is 0 and opened by ArrowUp', () => { | ||
| const highlightedIndex = 0 | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown highlightedIndex={highlightedIndex} onOpenChange={onOpenChange} items={items} />, | ||
|
|
@@ -137,7 +138,7 @@ describe('Dropdown', () => { | |
| ) | ||
| }) | ||
|
|
||
| it('calls onOpen with highlightedIndex set to defaultHighlightedIndex', () => { | ||
| it('is defaultHighlightedIndex prop value at first opening', () => { | ||
| const defaultHighlightedIndex = 1 | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown | ||
|
|
@@ -158,9 +159,28 @@ describe('Dropdown', () => { | |
| ) | ||
| }) | ||
|
|
||
| it('calls onOpen with highlightedIndex always set to 0', () => { | ||
| it('is null on second and subsequent open even though defaultHighlightedIndex prop is passed', () => { | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown highlightFirstItemOnOpen={true} onOpenChange={onOpenChange} items={items} />, | ||
| <Dropdown defaultHighlightedIndex={1} onOpenChange={onOpenChange} items={items} />, | ||
| ) | ||
| wrapper | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually, its not easy to see the reason for the event to happen this amount of times (which is requested by test). Could you, please, suggest why
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, this check, apparently, verifies that on the subsequent open events
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well, it is 3 because it opened a couple of times and closed once. I just want to make sure that on opening it the second time, it opens and there is no item highlighted. what do you think I should change?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it - I think that it would be more apparent if you would introduce a separate test case for that, the one that would follow a plan like:
Specifically to this example, it could be done the following way:
wrapper
.find(...)
. ...
.simulate('click')
.expect( .. ) // and this is where assertion phase startsIf there will be just single arrangement phase followed by assertion, it would be much easier to infer where the numbers come from :) currently the main problem is that there are several such phases within single test, so that the side effect of the previous arrangement affects the following one, which is not intuitive and harder to maintain
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. split the test into two. |
||
| .find({ className: Dropdown.slotClassNames.triggerButton }) | ||
| .simulate('click') | ||
| .simulate('click') | ||
| .simulate('click') | ||
| expect(onOpenChange).toBeCalledTimes(3) | ||
| expect(onOpenChange).toHaveBeenCalledWith( | ||
| null, | ||
| expect.objectContaining({ | ||
| highlightedIndex: null, | ||
| open: true, | ||
| }), | ||
| ) | ||
| }) | ||
|
|
||
| it('is 0 on first open when highlightFirstItemOnOpen prop is provided', () => { | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown highlightFirstItemOnOpen onOpenChange={onOpenChange} items={items} />, | ||
| ) | ||
| const triggerButton = wrapper.find({ className: Dropdown.slotClassNames.triggerButton }) | ||
|
|
||
|
|
@@ -173,7 +193,18 @@ describe('Dropdown', () => { | |
| open: true, | ||
| }), | ||
| ) | ||
| triggerButton.simulate('click').simulate('click') | ||
| }) | ||
|
|
||
| it('is 0 on second and subsequent open when highlightFirstItemOnOpen prop is provided', () => { | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown highlightFirstItemOnOpen onOpenChange={onOpenChange} items={items} />, | ||
| ) | ||
| const triggerButton = wrapper.find({ className: Dropdown.slotClassNames.triggerButton }) | ||
|
|
||
| triggerButton | ||
| .simulate('click') | ||
| .simulate('click') | ||
| .simulate('click') | ||
| expect(onOpenChange).toBeCalledTimes(3) | ||
| expect(onOpenChange).toHaveBeenCalledWith( | ||
| null, | ||
|
|
@@ -183,6 +214,124 @@ describe('Dropdown', () => { | |
| }), | ||
| ) | ||
| }) | ||
|
|
||
| it('is 0 on searchQuery first change and when highlightFirstItemOnOpen prop is provided', () => { | ||
| const onSearchQueryChange = jest.fn() | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown | ||
| search | ||
| highlightFirstItemOnOpen | ||
| onSearchQueryChange={onSearchQueryChange} | ||
| onOpenChange={onOpenChange} | ||
| items={items} | ||
| />, | ||
| ) | ||
| const searchInput = wrapper.find(`input.${DropdownSearchInput.slotClassNames.input}`) | ||
|
|
||
| searchInput.simulate('click').simulate('change', { target: { value: 'i' } }) | ||
| expect(onOpenChange).toBeCalledTimes(1) | ||
| expect(onOpenChange).toHaveBeenCalledWith( | ||
| null, | ||
| expect.objectContaining({ | ||
| highlightedIndex: 0, | ||
| open: true, | ||
| }), | ||
| ) | ||
| expect(onSearchQueryChange).toBeCalledTimes(1) | ||
| expect(onSearchQueryChange).toHaveBeenCalledWith( | ||
| null, | ||
| expect.objectContaining({ | ||
| searchQuery: 'i', | ||
| highlightedIndex: 0, | ||
| }), | ||
| ) | ||
| }) | ||
|
|
||
| it('is reset to 0 on searchQuery change and when highlightFirstItemOnOpen prop is provided', () => { | ||
| const onSearchQueryChange = jest.fn() | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown | ||
| search | ||
| highlightFirstItemOnOpen | ||
| onSearchQueryChange={onSearchQueryChange} | ||
| onOpenChange={onOpenChange} | ||
| items={items} | ||
| />, | ||
| ) | ||
| const searchInput = wrapper.find(`input.${DropdownSearchInput.slotClassNames.input}`) | ||
|
|
||
| searchInput | ||
| .simulate('click') | ||
| .simulate('change', { target: { value: 'i' } }) | ||
| .simulate('keydown', { keyCode: keyboardKey.ArrowUp, key: 'ArrowUp' }) // now it's on index 1. | ||
| .simulate('change', { target: { value: 'in' } }) // now it should reset to 0. | ||
| expect(onSearchQueryChange).toBeCalledTimes(2) | ||
| expect(onSearchQueryChange).toHaveBeenCalledWith( | ||
| null, | ||
| expect.objectContaining({ | ||
| searchQuery: 'in', | ||
| highlightedIndex: 0, | ||
| }), | ||
| ) | ||
| }) | ||
|
|
||
| it('is null on searchQuery first change and when highlightFirstItemOnOpen prop is not provided', () => { | ||
| const onSearchQueryChange = jest.fn() | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown | ||
| search | ||
| onSearchQueryChange={onSearchQueryChange} | ||
| onOpenChange={onOpenChange} | ||
| items={items} | ||
| />, | ||
| ) | ||
| const searchInput = wrapper.find(`input.${DropdownSearchInput.slotClassNames.input}`) | ||
|
|
||
| searchInput.simulate('click').simulate('change', { target: { value: 'i' } }) | ||
| expect(onOpenChange).toBeCalledTimes(1) | ||
| expect(onOpenChange).toHaveBeenCalledWith( | ||
| null, | ||
| expect.objectContaining({ | ||
| highlightedIndex: null, | ||
| open: true, | ||
| }), | ||
| ) | ||
| expect(onSearchQueryChange).toBeCalledTimes(1) | ||
| expect(onSearchQueryChange).toHaveBeenCalledWith( | ||
| null, | ||
| expect.objectContaining({ | ||
| searchQuery: 'i', | ||
| highlightedIndex: null, | ||
| }), | ||
| ) | ||
| }) | ||
|
|
||
| it('is reset to null on searchQuery change and when highlightFirstItemOnOpen prop is not provided', () => { | ||
| const onSearchQueryChange = jest.fn() | ||
| const wrapper = mountWithProvider( | ||
| <Dropdown | ||
| search | ||
| onSearchQueryChange={onSearchQueryChange} | ||
| onOpenChange={onOpenChange} | ||
| items={items} | ||
| />, | ||
| ) | ||
| const searchInput = wrapper.find(`input.${DropdownSearchInput.slotClassNames.input}`) | ||
|
|
||
| searchInput | ||
| .simulate('click') | ||
| .simulate('change', { target: { value: 'i' } }) // no item highlighted. | ||
| .simulate('keydown', { keyCode: keyboardKey.ArrowUp, key: 'ArrowUp' }) // highlight on index 0. | ||
| .simulate('change', { target: { value: 'in' } }) | ||
| expect(onSearchQueryChange).toBeCalledTimes(2) | ||
| expect(onSearchQueryChange).toHaveBeenCalledWith( | ||
| null, | ||
| expect.objectContaining({ | ||
| searchQuery: 'in', | ||
| highlightedIndex: null, | ||
| }), | ||
| ) | ||
| }) | ||
| }) | ||
|
|
||
| describe('getA11ySelectionMessage', () => { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.