(this._nestingContainer = n)} />;
}
}
const container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(
, container);
expect(selectNode.value).toBe('giraffe');
selectNode.value = 'gorilla';
let nativeEvent = document.createEvent('Event');
nativeEvent.initEvent('input', true, true);
selectNode.dispatchEvent(nativeEvent);
expect(selectNode.value).toEqual('gorilla');
nativeEvent = document.createEvent('Event');
nativeEvent.initEvent('change', true, true);
selectNode.dispatchEvent(nativeEvent);
expect(selectNode.value).toEqual('gorilla');
document.body.removeChild(container);
});
it('should not select first option by default when multiple is set and no defaultValue is set', async () => {
const stub = (
);
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(stub);
});
const node = container.firstChild;
expect(node.options[0].selected).toBe(false); // a
expect(node.options[1].selected).toBe(false); // b
expect(node.options[2].selected).toBe(false); // c
});
describe('When given a Symbol value', () => {
it('treats initial Symbol value as missing', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'Invalid value for prop `value` on
tag. ' +
'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' +
'For details, see https://react.dev/link/attribute-behavior \n' +
' in option (at **)',
]);
let node = container.firstChild;
expect(node.value).toBe('monkey');
await act(() => {
root.render(
,
);
});
node = container.firstChild;
expect(node.value).toBe('A Symbol!');
});
it('treats initial Symbol defaultValue as an empty string', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'Invalid value for prop `value` on tag. ' +
'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' +
'For details, see https://react.dev/link/attribute-behavior \n' +
' in option (at **)',
]);
const node = container.firstChild;
expect(node.value).toBe('A Symbol!');
});
it('treats updated Symbol defaultValue as an empty string', async () => {
let container = document.createElement('div');
let root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'Invalid value for prop `value` on tag. ' +
'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' +
'For details, see https://react.dev/link/attribute-behavior \n' +
' in option (at **)',
]);
let node = container.firstChild;
expect(node.value).toBe('monkey');
container = document.createElement('div');
root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
node = container.firstChild;
expect(node.value).toBe('A Symbol!');
});
});
describe('When given a function value', () => {
it('treats initial function value as missing', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'Invalid value for prop `value` on tag. ' +
'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' +
'For details, see https://react.dev/link/attribute-behavior \n' +
' in option (at **)',
]);
const node = container.firstChild;
expect(node.value).toBe('A function!');
});
it('treats initial function defaultValue as an empty string', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'Invalid value for prop `value` on tag. ' +
'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' +
'For details, see https://react.dev/link/attribute-behavior \n' +
' in option (at **)',
]);
const node = container.firstChild;
expect(node.value).toBe('A function!');
});
it('treats updated function value as an empty string', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'Invalid value for prop `value` on tag. ' +
'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' +
'For details, see https://react.dev/link/attribute-behavior \n' +
' in option (at **)',
]);
let node = container.firstChild;
expect(node.value).toBe('monkey');
await act(() => {
root.render(
,
);
});
node = container.firstChild;
expect(node.value).toBe('A function!');
});
it('treats updated function defaultValue as an empty string', async () => {
let container = document.createElement('div');
let root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'Invalid value for prop `value` on tag. ' +
'Either remove it from the element, or pass a string or number value to keep it in the DOM. ' +
'For details, see https://react.dev/link/attribute-behavior \n' +
' in option (at **)',
]);
let node = container.firstChild;
expect(node.value).toBe('monkey');
container = document.createElement('div');
root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
node = container.firstChild;
expect(node.value).toBe('A function!');
});
});
describe('When given a Temporal.PlainDate-like value', () => {
class TemporalLike {
valueOf() {
// Throwing here is the behavior of ECMAScript "Temporal" date/time API.
// See https://tc39.es/proposal-temporal/docs/plaindate.html#valueOf
throw new TypeError('prod message');
}
toString() {
return '2020-01-01';
}
}
it('throws when given a Temporal.PlainDate-like value (select)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.\n' +
' in select (at **)',
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.\n' +
' in select (at **)',
]);
});
it('throws when given a Temporal.PlainDate-like value (option)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
]);
});
it('throws when given a Temporal.PlainDate-like value (both)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
]);
});
it('throws with updated Temporal.PlainDate-like value (select)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.\n' +
' in select (at **)',
]);
});
it('throws with updated Temporal.PlainDate-like value (option)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
]);
});
it('throws with updated Temporal.PlainDate-like value (both)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(
new AggregateError([
new TypeError('prod message'),
new TypeError('prod message'),
]),
);
assertConsoleErrorDev([
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.\n' +
' in select (at **)',
]);
});
it('throws when given a Temporal.PlainDate-like defaultValue (select)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.\n' +
' in select (at **)',
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.\n' +
' in select (at **)',
]);
});
it('throws when given a Temporal.PlainDate-like defaultValue (option)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
]);
});
it('throws when given a Temporal.PlainDate-like defaultValue (both)', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
]);
});
it('throws with updated Temporal.PlainDate-like defaultValue (select)', async () => {
let container = document.createElement('div');
let root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
container = document.createElement('div');
root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.\n' +
' in select (at **)',
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.\n' +
' in select (at **)',
]);
});
it('throws with updated Temporal.PlainDate-like defaultValue (both)', async () => {
let container = document.createElement('div');
let root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
container = document.createElement('div');
root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).rejects.toThrowError(new TypeError('prod message'));
assertConsoleErrorDev([
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.\n' +
' in option (at **)',
]);
});
it('should not warn about missing onChange if value is not set', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).resolves.not.toThrow();
});
it('should not throw an error about missing onChange if value is undefined', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).resolves.not.toThrow();
});
it('should not warn about missing onChange if onChange is set', async () => {
const change = jest.fn();
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).resolves.not.toThrow();
});
it('should not warn about missing onChange if disabled is true', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(
act(() => {
root.render(
,
);
}),
).resolves.not.toThrow();
});
it('should warn about missing onChange if value is false', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'You provided a `value` prop to a form ' +
'field without an `onChange` handler. This will render a read-only ' +
'field. If the field should be mutable use `defaultValue`. ' +
'Otherwise, set `onChange`.\n in select (at **)',
]);
});
it('should warn about missing onChange if value is 0', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'You provided a `value` prop to a form ' +
'field without an `onChange` handler. This will render a read-only ' +
'field. If the field should be mutable use `defaultValue`. ' +
'Otherwise, set `onChange`.\n' +
' in select (at **)',
]);
});
it('should warn about missing onChange if value is "0"', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'You provided a `value` prop to a form ' +
'field without an `onChange` handler. This will render a read-only ' +
'field. If the field should be mutable use `defaultValue`. ' +
'Otherwise, set `onChange`.\n' +
' in select (at **)',
]);
});
it('should warn about missing onChange if value is ""', async () => {
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
,
);
});
assertConsoleErrorDev([
'You provided a `value` prop to a form ' +
'field without an `onChange` handler. This will render a read-only ' +
'field. If the field should be mutable use `defaultValue`. ' +
'Otherwise, set `onChange`.\n' +
' in select (at **)',
]);
});
});
});