Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.

Commit db61411

Browse files
authored
fix(Dropdown): add onClick override on DropdownItem (#1248)
* added override capability to DropdownItem * removed the mouse listeners * updated changelog * added code review changes * added unit tests * updated changelog again * Update packages/react/test/specs/components/Dropdown/Dropdown-test.tsx Co-Authored-By: silviuavram <silviu.avram91@gmail.com> * added another assertion to the tests * updated changelog * stop immediate propagation from native event * fixed tests by adding mock fn * fixed import issue * fixed a couple more tests
1 parent 3d4af74 commit db61411

File tree

3 files changed

+86
-33
lines changed

3 files changed

+86
-33
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
2121
- Rename `inputFocusBorderBottomColor` to `inputFocusBorderColor` in `InputVariables` @layershifter ([#1247](https://github.com/stardust-ui/react/pull/1247))
2222

2323
### Fixes
24+
- Fix onClick in `DropdownItem` to accept user callback and have its event propagation stopped @silviuavram ([#1248](https://github.com/stardust-ui/react/pull/1248))
2425
- Fix a11y message cleanup for add and remove items in `Dropdown` @silviuavram ([#1237](https://github.com/stardust-ui/react/pull/1237))
2526

2627
### Features

packages/react/src/components/Dropdown/Dropdown.tsx

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
} from '../../lib'
3333
import List from '../List/List'
3434
import Ref from '../Ref/Ref'
35-
import DropdownItem from './DropdownItem'
35+
import DropdownItem, { DropdownItemProps } from './DropdownItem'
3636
import DropdownSelectedItem, { DropdownSelectedItemProps } from './DropdownSelectedItem'
3737
import DropdownSearchInput, { DropdownSearchInputProps } from './DropdownSearchInput'
3838
import Button from '../Button/Button'
@@ -512,16 +512,14 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
512512
variables,
513513
inputRef: this.inputRef,
514514
},
515-
overrideProps: (predefinedProps: DropdownSearchInputProps) =>
516-
this.handleSearchInputOverrides(
517-
predefinedProps,
518-
highlightedIndex,
519-
rtl,
520-
selectItemAtIndex,
521-
toggleMenu,
522-
accessibilityComboboxProps,
523-
getInputProps,
524-
),
515+
overrideProps: this.handleSearchInputOverrides(
516+
highlightedIndex,
517+
rtl,
518+
selectItemAtIndex,
519+
toggleMenu,
520+
accessibilityComboboxProps,
521+
getInputProps,
522+
),
525523
})
526524
}
527525

@@ -598,7 +596,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
598596
key: (item as any).header,
599597
}),
600598
},
601-
overrideProps: () => this.handleItemOverrides(item, index, getItemProps),
599+
overrideProps: this.handleItemOverrides(item, index, getItemProps),
602600
render: renderItem,
603601
}),
604602
)
@@ -640,8 +638,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
640638
key: (item as any).header,
641639
}),
642640
},
643-
overrideProps: (predefinedProps: DropdownSelectedItemProps) =>
644-
this.handleSelectedItemOverrides(predefinedProps, item, rtl),
641+
overrideProps: this.handleSelectedItemOverrides(item, rtl),
645642
render: renderSelectedItem,
646643
}),
647644
)
@@ -724,12 +721,20 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
724721
item: ShorthandValue,
725722
index: number,
726723
getItemProps: (options: GetItemPropsOptions<ShorthandValue>) => any,
727-
) => ({ accessibilityItemProps: getItemProps({ item, index }) })
724+
) => (predefinedProps: DropdownItemProps) => ({
725+
accessibilityItemProps: getItemProps({
726+
item,
727+
index,
728+
onClick: e => {
729+
e.stopPropagation()
730+
e.nativeEvent.stopImmediatePropagation()
731+
_.invoke(predefinedProps, 'onClick', e, predefinedProps)
732+
},
733+
}),
734+
})
728735

729-
private handleSelectedItemOverrides = (
736+
private handleSelectedItemOverrides = (item: ShorthandValue, rtl: boolean) => (
730737
predefinedProps: DropdownSelectedItemProps,
731-
item: ShorthandValue,
732-
rtl: boolean,
733738
) => ({
734739
onRemove: (e: React.SyntheticEvent, dropdownSelectedItemProps: DropdownSelectedItemProps) => {
735740
this.handleSelectedItemRemove(e, item, predefinedProps, dropdownSelectedItemProps)
@@ -747,7 +752,6 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
747752
})
748753

749754
private handleSearchInputOverrides = (
750-
predefinedProps: DropdownSearchInputProps,
751755
highlightedIndex: number,
752756
rtl: boolean,
753757
selectItemAtIndex: (
@@ -758,7 +762,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
758762
toggleMenu: () => void,
759763
accessibilityComboboxProps: Object,
760764
getInputProps: (options?: GetInputPropsOptions) => any,
761-
) => {
765+
) => (predefinedProps: DropdownSearchInputProps) => {
762766
const handleInputBlur = (
763767
e: React.SyntheticEvent,
764768
searchInputProps: DropdownSearchInputProps,
@@ -1027,7 +1031,6 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
10271031
this.removeItemFromValue(item)
10281032
this.tryFocusSearchInput()
10291033
this.tryFocusTriggerButton()
1030-
e.stopPropagation()
10311034
_.invoke(predefinedProps, 'onRemove', e, DropdownSelectedItemProps)
10321035
}
10331036

packages/react/test/specs/components/Dropdown/Dropdown-test.tsx

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ describe('Dropdown', () => {
104104

105105
triggerButton.simulate('click')
106106
const firstItem = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(0)
107-
firstItem.simulate('click')
107+
firstItem.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
108108

109109
expect(onOpenChange).toBeCalledTimes(2)
110110
expect(onOpenChange).toHaveBeenLastCalledWith(
@@ -630,7 +630,7 @@ describe('Dropdown', () => {
630630

631631
triggerButton.simulate('click')
632632
const item = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(itemSelectedIndex)
633-
item.simulate('click')
633+
item.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
634634

635635
expect(onSelectedChange).toHaveBeenCalledTimes(1)
636636
expect(onSelectedChange).toHaveBeenCalledWith(
@@ -716,10 +716,10 @@ describe('Dropdown', () => {
716716

717717
triggerButton.simulate('click')
718718
const firstItem = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(1)
719-
firstItem.simulate('click')
719+
firstItem.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
720720
triggerButton.simulate('click')
721721
const itemAtIndex = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(itemSelectedIndex)
722-
itemAtIndex.simulate('click')
722+
itemAtIndex.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
723723

724724
expect(onSelectedChange).toHaveBeenCalledTimes(2)
725725
expect(onSelectedChange).toHaveBeenLastCalledWith(
@@ -739,10 +739,10 @@ describe('Dropdown', () => {
739739

740740
triggerButton.simulate('click')
741741
const itemAtIndex1 = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(1)
742-
itemAtIndex1.simulate('click')
742+
itemAtIndex1.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
743743
triggerButton.simulate('click')
744744
const itemAtIndex2 = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(3)
745-
itemAtIndex2.simulate('click')
745+
itemAtIndex2.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
746746

747747
expect(onSelectedChange).toHaveBeenCalledTimes(2)
748748
expect(onSelectedChange).toHaveBeenLastCalledWith(
@@ -763,10 +763,10 @@ describe('Dropdown', () => {
763763

764764
toggleIndicator.simulate('click')
765765
let firstItem = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(0)
766-
firstItem.simulate('click')
766+
firstItem.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
767767
toggleIndicator.simulate('click')
768768
firstItem = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(0)
769-
firstItem.simulate('click')
769+
firstItem.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
770770
searchInput
771771
.simulate('click')
772772
.simulate('keydown', { keyCode: keyboardKey.Backspace, key: 'Backspace' })
@@ -808,7 +808,7 @@ describe('Dropdown', () => {
808808

809809
triggerButton.simulate('click')
810810
const firstItem = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(0)
811-
firstItem.simulate('click')
811+
firstItem.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
812812

813813
expect(dropdown.state('a11ySelectionStatus')).toBe('bla bla added')
814814

@@ -830,7 +830,7 @@ describe('Dropdown', () => {
830830

831831
triggerButton.simulate('click')
832832
const firstItem = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(0)
833-
firstItem.simulate('click')
833+
firstItem.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
834834
jest.runAllTimers()
835835
const removeIcon = wrapper.find(`span.${DropdownSelectedItem.slotClassNames.icon}`)
836836
removeIcon.simulate('click')
@@ -866,7 +866,7 @@ describe('Dropdown', () => {
866866

867867
toggleIndicator.simulate('click')
868868
const itemAtIndex = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(itemSelectedIndex)
869-
itemAtIndex.simulate('click')
869+
itemAtIndex.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
870870

871871
expect(onSelectedChange).toHaveBeenCalledTimes(1)
872872
expect(onSelectedChange).toHaveBeenCalledWith(
@@ -909,7 +909,7 @@ describe('Dropdown', () => {
909909

910910
toggleIndicator.simulate('click')
911911
const itemAtIndex = wrapper.find(`li.${Dropdown.slotClassNames.item}`).at(itemSelectedIndex)
912-
itemAtIndex.simulate('click')
912+
itemAtIndex.simulate('click', { nativeEvent: { stopImmediatePropagation: jest.fn() } })
913913

914914
expect(onSelectedChange).toHaveBeenCalledTimes(1)
915915
expect(onSelectedChange).toHaveBeenCalledWith(
@@ -1010,4 +1010,53 @@ describe('Dropdown', () => {
10101010
expect(preventDefault).not.toBeCalled()
10111011
})
10121012
})
1013+
1014+
describe('items', () => {
1015+
it('have onClick called when passed stop event from being propagated', () => {
1016+
const onClick = jest.fn()
1017+
const stopPropagation = jest.fn()
1018+
const stopImmediatePropagation = jest.fn()
1019+
const mockedEvent = { stopPropagation, nativeEvent: { stopImmediatePropagation } }
1020+
const items = [{ header: 'Venom', onClick }]
1021+
const wrapper = mountWithProvider(<Dropdown items={items} />)
1022+
const triggerButton = wrapper.find(`button.${Dropdown.slotClassNames.triggerButton}`)
1023+
1024+
triggerButton.simulate('click')
1025+
const firstItem = wrapper.find(`li.${Dropdown.slotClassNames.item}`)
1026+
firstItem.simulate('click', mockedEvent)
1027+
1028+
expect(onClick).toBeCalledTimes(1)
1029+
expect(onClick).toHaveBeenCalledWith(
1030+
expect.objectContaining(mockedEvent),
1031+
expect.objectContaining({
1032+
header: 'Venom',
1033+
}),
1034+
)
1035+
expect(stopPropagation).toBeCalledTimes(1)
1036+
expect(stopImmediatePropagation).toBeCalledTimes(1)
1037+
})
1038+
1039+
it('when selected have onClick called when passed stop event from being propagated', () => {
1040+
const onClick = jest.fn()
1041+
const stopPropagation = jest.fn()
1042+
const stopImmediatePropagation = jest.fn()
1043+
const mockedEvent = { stopPropagation, nativeEvent: { stopImmediatePropagation } }
1044+
const items = [{ header: 'Venom', onClick }]
1045+
const wrapper = mountWithProvider(<Dropdown items={items} value={items} multiple />)
1046+
const selectedItemHeaderAtIndex0 = wrapper
1047+
.find(`span.${DropdownSelectedItem.slotClassNames.header}`)
1048+
.at(0)
1049+
1050+
selectedItemHeaderAtIndex0.simulate('click', mockedEvent)
1051+
1052+
expect(onClick).toBeCalledTimes(1)
1053+
expect(onClick).toHaveBeenCalledWith(
1054+
expect.objectContaining(mockedEvent),
1055+
expect.objectContaining({
1056+
header: 'Venom',
1057+
}),
1058+
)
1059+
expect(stopPropagation).toBeCalledTimes(1)
1060+
})
1061+
})
10131062
})

0 commit comments

Comments
 (0)