Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: onchange event with preact/compat enabled #72

Merged
merged 5 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/__tests__/events-compat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { h } from 'preact' // required by render
import { fireEvent, render } from '..'
import 'preact/compat'

test('calling `fireEvent` with `preact/compat` and onChange works too', () => {
const handler = jest.fn()

// Preact only matches React's aliasing of `onChange` when `preact/compat` is used
// This test ensures this is supported properly with `fireEvent.change()`
const {
container: { firstChild: input }
} = render(<input type="text" onChange={handler} />)

const targetProperties = { value: 'a' }
const otherProperties = { isComposing: true }
const init = {
target: targetProperties,
...otherProperties
}

expect(fireEvent.change(input, init)).toBe(true)

expect(handler).toHaveBeenCalledTimes(1)
expect(handler).toHaveBeenCalledWith(expect.objectContaining(otherProperties))
})
12 changes: 9 additions & 3 deletions src/__tests__/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const eventTypes = [
},
{
type: 'Focus',
events: ['input', 'invalid'],
events: ['input', 'invalid', 'change'],
elementType: 'input'
},
{
Expand Down Expand Up @@ -200,8 +200,14 @@ test('calling `fireEvent` directly works too', () => {
})

test('`fireEvent` returns false when prevented', () => {
const { container: { firstChild: button } } = render(
(<button onClick={(e) => { e.preventDefault() }} />)
const {
container: { firstChild: button }
} = render(
<button
onClick={(e) => {
e.preventDefault()
}}
/>
)

expect(fireEvent.click(button)).toBe(false)
Expand Down
27 changes: 25 additions & 2 deletions src/fire-event.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
import { fireEvent as domFireEvent, createEvent } from '@testing-library/dom'
import { options } from 'preact'

let isCompat = false

// Detects if preact/compat is used
const oldHook = options.vnode
options.vnode = (vnode) => {
if (vnode.$$typeof) isCompat = true
if (oldHook) oldHook(vnode)
}

// Renames event to match React (preact/compat) version
const renameEventCompat = (key) => {
return key === 'change' ? 'input' : key
}

// Similar to RTL we make are own fireEvent helper that just calls DTL's fireEvent with that
// we can that any specific behaviors to the helpers we need
Expand All @@ -11,8 +26,16 @@ Object.keys(domFireEvent).forEach((key) => {
// we hit the Preact listeners.
const eventName = `on${key.toLowerCase()}`
const isInElem = eventName in elem
// Preact changes all change events to input events when running 'preact/compat',
// making the event name out of sync.
// The problematic code is in: preact/compat/src/render.js > handleDomVNode()
const keyFiltered = !isCompat ? key : renameEventCompat(key)

return isInElem
? domFireEvent[key](elem, init)
: domFireEvent(elem, createEvent(key[0].toUpperCase() + key.slice(1), elem, init))
? domFireEvent[keyFiltered](elem, init)
: domFireEvent(
elem,
createEvent(keyFiltered[0].toUpperCase() + keyFiltered.slice(1), elem, init)
)
}
})
Loading