Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
fix: set initial value when setting UI value
  • Loading branch information
ph-fritsche committed Apr 10, 2022
commit cc28baecf3abea6228112b16667e3346c70becfb
21 changes: 13 additions & 8 deletions src/document/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import {dispatchUIEvent} from '../event'
import {Config} from '../setup'
import {prepareSelectionInterceptor} from './selection'
import {
clearInitialValue,
getInitialValue,
prepareValueInterceptor,
setInitialValue,
} from './value'

const isPrepared = Symbol('Node prepared with document state workarounds')
Expand Down Expand Up @@ -45,8 +45,11 @@ export function prepareDocument(document: Document) {
e => {
const el = e.target as HTMLInputElement
const initialValue = getInitialValue(el)
if (typeof initialValue === 'string' && el.value !== initialValue) {
dispatchUIEvent({} as Config, el, 'change')
if (initialValue !== undefined) {
if (el.value !== initialValue) {
dispatchUIEvent({} as Config, el, 'change')
}
clearInitialValue(el)
}
},
{
Expand All @@ -59,10 +62,6 @@ export function prepareDocument(document: Document) {
}

function prepareElement(el: Node | HTMLInputElement) {
if ('value' in el) {
setInitialValue(el)
}

if (el[isPrepared]) {
return
}
Expand All @@ -75,6 +74,12 @@ function prepareElement(el: Node | HTMLInputElement) {
el[isPrepared] = isPrepared
}

export {getUIValue, setUIValue, startTrackValue, endTrackValue} from './value'
export {
getUIValue,
setUIValue,
startTrackValue,
endTrackValue,
clearInitialValue,
} from './value'
export {getUISelection, setUISelection} from './selection'
export type {UISelectionRange} from './selection'
9 changes: 6 additions & 3 deletions src/document/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export function setUIValue(
element: HTMLInputElement | HTMLTextAreaElement,
value: string,
) {
if (element[InitialValue] === undefined) {
element[InitialValue] = element.value
}

element.value = {
[UIValue]: UIValue,
toString: () => value,
Expand All @@ -78,10 +82,10 @@ export function getUIValue(element: HTMLInputElement | HTMLTextAreaElement) {
: String(element[UIValue])
}

export function setInitialValue(
export function clearInitialValue(
element: HTMLInputElement | HTMLTextAreaElement,
) {
element[InitialValue] = element.value
element[InitialValue] = undefined
}

export function getInitialValue(
Expand Down Expand Up @@ -123,7 +127,6 @@ function setCleanValue(
v: string,
) {
element[UIValue] = undefined
element[InitialValue] = v

// Programmatically setting the value property
// moves the cursor to the end of the input.
Expand Down
2 changes: 2 additions & 0 deletions src/utils/edit/input.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
clearInitialValue,
endTrackValue,
getUIValue,
setUIValue,
Expand Down Expand Up @@ -169,6 +170,7 @@ function editInputElement(
if (isValidDateOrTimeValue(element, newValue)) {
commitInput(config, element, newOffset, {})
dispatchUIEvent(config, element, 'change')
clearInitialValue(element)
}
} else {
commitInput(config, element, newOffset, {
Expand Down
15 changes: 12 additions & 3 deletions tests/document/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ test('keep track of value in UI', async () => {
expect(getUIValue(element)).toBe('3.5')
})

test('trigger `change` event if value changed since focus/set', async () => {
test('trigger `change` event if value changed per user input', async () => {
const {element, getEvents} = render<HTMLInputElement>(
`<input type="number"/>`,
{focus: false},
Expand All @@ -66,16 +66,25 @@ test('trigger `change` event if value changed since focus/set', async () => {
expect(getEvents('change')).toHaveLength(0)

element.focus()
// Programmatically changing value sets initial value
// Programmatically changing value is ignored
element.value = '3'
// Value doesn't change
setUIValue(element, '3')
element.blur()

expect(getEvents('change')).toHaveLength(0)

element.focus()
setUIValue(element, '2')
// value is reset so there is no change in the end
element.value = '3'
element.blur()

expect(getEvents('change')).toHaveLength(0)

element.focus()
setUIValue(element, '2')
element.value = '2'
setUIValue(element, '3')
element.blur()

expect(getEvents('change')).toHaveLength(1)
Expand Down