Skip to content

Commit 03cc974

Browse files
aMolluskKent C. Dodds
authored andcommitted
fix: resolve iOS touch blur issue (#429)
* fix: resolve iOS touch blur issue * fix: update even stateChangeType and tests
1 parent 204d179 commit 03cc974

File tree

3 files changed

+54
-2
lines changed

3 files changed

+54
-2
lines changed

src/__tests__/downshift.lifecycle.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,42 @@ test('handles mouse events properly to reset state', () => {
4646
expect(handleStateChange).toHaveBeenCalledTimes(1)
4747
})
4848

49+
test('handles state change for touchevent events', () => {
50+
const handleStateChange = jest.fn()
51+
const renderSpy = jest.fn(({getToggleButtonProps}) => (
52+
<button {...getToggleButtonProps({'data-testid': 'button'})} />
53+
))
54+
55+
const MyComponent = () => (
56+
<Downshift onStateChange={handleStateChange} render={renderSpy} />
57+
)
58+
const {queryByTestId, container, unmount} = render(<MyComponent />)
59+
document.body.appendChild(container)
60+
61+
const button = queryByTestId('button')
62+
63+
// touch outside for coverage
64+
document.body.dispatchEvent(
65+
new window.TouchEvent('touchstart', {bubbles: true}),
66+
)
67+
68+
// open menu
69+
Simulate.click(button)
70+
jest.runAllTimers()
71+
72+
expect(handleStateChange).toHaveBeenCalledTimes(1)
73+
74+
// touch outside downshift
75+
document.body.dispatchEvent(
76+
new window.TouchEvent('touchstart', {bubbles: true}),
77+
)
78+
79+
jest.runAllTimers()
80+
expect(handleStateChange).toHaveBeenCalledTimes(2)
81+
82+
unmount()
83+
})
84+
4985
test('props update causes the a11y status to be updated', () => {
5086
setA11yStatus.mockReset()
5187
const MyComponent = () => (

src/__tests__/downshift.props.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ test('uses given environment', () => {
127127
const {unmount, setHighlightedIndex} = setup({environment})
128128
setHighlightedIndex()
129129
unmount()
130-
expect(environment.addEventListener).toHaveBeenCalledTimes(2)
131-
expect(environment.removeEventListener).toHaveBeenCalledTimes(2)
130+
expect(environment.addEventListener).toHaveBeenCalledTimes(3)
131+
expect(environment.removeEventListener).toHaveBeenCalledTimes(3)
132132
})
133133

134134
test('can override onOuterClick callback to maintain isOpen state', () => {

src/downshift.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class Downshift extends Component {
116116
blurButton: '__autocomplete_blur_button__',
117117
controlledPropUpdatedSelectedItem:
118118
'__autocomplete_controlled_prop_updated_selected_item__',
119+
touchStart: '__autocomplete_touchstart__',
119120
}
120121

121122
constructor(...args) {
@@ -881,13 +882,28 @@ class Downshift extends Component {
881882
)
882883
}
883884
}
885+
// Touching an element in iOS gives focus and hover states, but touching out of
886+
// the element will remove hover, and persist the focus state, resulting in the
887+
// blur event not being triggered.
888+
const onTouchStart = event => {
889+
const targetInDownshift =
890+
this._rootNode && isOrContainsNode(this._rootNode, event.target)
891+
if (!targetInDownshift && this.getState().isOpen) {
892+
this.reset({type: Downshift.stateChangeTypes.touchStart}, () =>
893+
this.props.onOuterClick(this.getStateAndHelpers()),
894+
)
895+
}
896+
}
897+
884898
this.props.environment.addEventListener('mousedown', onMouseDown)
885899
this.props.environment.addEventListener('mouseup', onMouseUp)
900+
this.props.environment.addEventListener('touchstart', onTouchStart)
886901

887902
this.cleanup = () => {
888903
this._isMounted = false
889904
this.props.environment.removeEventListener('mousedown', onMouseDown)
890905
this.props.environment.removeEventListener('mouseup', onMouseUp)
906+
this.props.environment.removeEventListener('touchstart', onTouchStart)
891907
}
892908
}
893909
}

0 commit comments

Comments
 (0)