Skip to content

Commit 08365bc

Browse files
authored
fix: Compat change aliases (#77)
* fix: Only alias 'change' events to 'input' when the element is an '<input>' or '<textarea>' and not if the 'type' attr is 'file', 'checkbox', or 'radio' * test: Add test case
1 parent 3e5394e commit 08365bc

2 files changed

Lines changed: 38 additions & 9 deletions

File tree

src/__tests__/events-compat.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,23 @@ test('calling `fireEvent` with `preact/compat` and onChange works too', () => {
2323
expect(handler).toHaveBeenCalledTimes(1)
2424
expect(handler).toHaveBeenCalledWith(expect.objectContaining(otherProperties))
2525
})
26+
27+
test('should not alias `change` event to `input` for file, checkbox, or radio inputs', () => {
28+
for (const type of ['file', 'checkbox', 'radio']) {
29+
const inputHandler = jest.fn()
30+
const changeHandler = jest.fn()
31+
32+
const {
33+
container: { firstChild: input }
34+
} = render(<input type={type} onChange={changeHandler} onInput={inputHandler} />)
35+
36+
fireEvent.change(input, {
37+
target: input.type === 'file'
38+
? { files: [new File(['Hello World!'], 'foo.txt')] }
39+
: { checked: true }
40+
})
41+
42+
expect(inputHandler).toHaveBeenCalledTimes(0)
43+
expect(changeHandler).toHaveBeenCalledTimes(1)
44+
}
45+
})

src/fire-event.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,19 @@ options.vnode = (vnode) => {
1010
if (oldHook) oldHook(vnode)
1111
}
1212

13-
// Renames event to match React (preact/compat) version
14-
const renameEventCompat = (key) => {
15-
return key === 'change' ? 'input' : key
13+
// Matches the behavior of `preact/compat`:
14+
// https://github.com/preactjs/preact/blob/2459326755dea9ad6184b42bda1128c5004b8544/compat/src/render.js#L173-L175
15+
// https://github.com/preactjs/preact/blob/2459326755dea9ad6184b42bda1128c5004b8544/compat/src/render.js#L36-L37
16+
const maybeAliasKey = (key, elem) => {
17+
if (
18+
key === 'change' &&
19+
(elem.tagName === 'INPUT' || elem.tagName === 'TEXTAREA') &&
20+
!/fil|che|rad/.test(elem.type)
21+
) {
22+
return 'input'
23+
}
24+
25+
return key;
1626
}
1727

1828
// Similar to RTL we make are own fireEvent helper that just calls DTL's fireEvent with that
@@ -26,16 +36,15 @@ Object.keys(domFireEvent).forEach((key) => {
2636
// we hit the Preact listeners.
2737
const eventName = `on${key.toLowerCase()}`
2838
const isInElem = eventName in elem
29-
// Preact changes all change events to input events when running 'preact/compat',
30-
// making the event name out of sync.
31-
// The problematic code is in: preact/compat/src/render.js > handleDomVNode()
32-
const keyFiltered = !isCompat ? key : renameEventCompat(key)
39+
40+
// Preact aliases some change events when using `preact/compat` to mirror React's behavior
41+
const maybeAliasedKey = !isCompat ? key : maybeAliasKey(key, elem)
3342

3443
return isInElem
35-
? domFireEvent[keyFiltered](elem, init)
44+
? domFireEvent[maybeAliasedKey](elem, init)
3645
: domFireEvent(
3746
elem,
38-
createEvent(keyFiltered[0].toUpperCase() + keyFiltered.slice(1), elem, init)
47+
createEvent(maybeAliasedKey[0].toUpperCase() + maybeAliasedKey.slice(1), elem, init)
3948
)
4049
}
4150
})

0 commit comments

Comments
 (0)