Skip to content
Open
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
20 changes: 4 additions & 16 deletions packages/react-dom-bindings/src/client/validateDOMNesting.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ const inScopeTags = [
'th',
'marquee',
'object',
'select',
'template',

// https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
Expand Down Expand Up @@ -306,22 +307,6 @@ function isTagValidWithParent(
): boolean {
// First, let's check if we're in an unusual parsing mode...
switch (parentTag) {
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
case 'select':
return (
tag === 'hr' ||
tag === 'option' ||
tag === 'optgroup' ||
tag === 'script' ||
tag === 'template' ||
tag === '#text'
);
case 'optgroup':
return tag === 'option' || tag === '#text';
// Strictly speaking, seeing an <option> doesn't mean we're in a <select>
// but
case 'option':
return tag === '#text';
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intd
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incaption
// No special behavior since these rules fall back to "in body" mode for
Expand Down Expand Up @@ -423,6 +408,9 @@ function isTagValidWithParent(
case 'caption':
case 'col':
case 'colgroup':
case 'input':
// <input> causes open <select> tags to close.
return parentTag !== 'select';
case 'frameset':
case 'frame':
case 'tbody':
Expand Down
25 changes: 2 additions & 23 deletions packages/react-dom/src/__tests__/ReactDOMOption-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,13 @@ describe('ReactDOMOption', () => {
expect(container.firstChild.innerHTML).toBe('1 foo');
});

it('should warn for invalid child tags', async () => {
it('should not warn for a child div tag', async () => {
const el = (
<option value="12">
{1} <div /> {2}
</option>
);
const container = await renderIntoDocument(el);
assertConsoleErrorDev([
'In HTML, <div> cannot be a child of <option>.\n' +
'This will cause a hydration error.\n' +
'\n' +
'> <option value="12">\n' +
'> <div>\n' +
' ...\n' +
'\n' +
' in div (at **)',
]);
expect(container.firstChild.innerHTML).toBe('1 <div></div> 2');
await renderIntoDocument(el);
});
Expand Down Expand Up @@ -239,7 +229,7 @@ describe('ReactDOMOption', () => {
expect(node.selectedIndex).toEqual(2);
});

it('generates a hydration error when an invalid nested tag is used as a child', async () => {
it('does not generate a hydration error when a div tag is used as a child', async () => {
const ref = React.createRef();
const children = (
<select readOnly={true} value="bar">
Expand Down Expand Up @@ -267,17 +257,6 @@ describe('ReactDOMOption', () => {
onRecoverableError: () => {},
});
});
assertConsoleErrorDev([
'In HTML, <div> cannot be a child of <option>.\n' +
'This will cause a hydration error.\n' +
'\n' +
' <select readOnly={true} value="bar">\n' +
'> <option value="bar">\n' +
'> <div ref={{current:null}}>\n' +
' ...\n' +
'\n' +
' in div (at **)',
]);
option = container.firstChild.firstChild;

expect(option.textContent).toBe('BarFooBaz');
Expand Down
11 changes: 11 additions & 0 deletions packages/react-dom/src/__tests__/validateDOMNesting-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ function expectWarnings(tags, warnings = [], withoutStack = 0) {
const Tag = tags.pop();
if (Tag === '#text') {
element = 'text';
} else if (Tag === 'option') {
element = <Tag value={'value'}>{element}</Tag>;
} else {
element = <Tag>{element}</Tag>;
}
Expand All @@ -77,6 +79,7 @@ describe('validateDOMNesting', () => {
expectWarnings(['div', 'p', 'button', 'p']);
expectWarnings(['p', 'svg', 'foreignObject', 'p']);
expectWarnings(['html', 'body', 'div']);
expectWarnings(['select', 'div', 'optgroup', 'div', 'option', 'span']);

// Invalid, but not changed by browser parsing so we allow them
expectWarnings(['div', 'ul', 'ul', 'li']);
Expand Down Expand Up @@ -205,6 +208,14 @@ describe('validateDOMNesting', () => {
' in body (at **)',
],
);
expectWarnings(
['select', 'input'],
[
'In HTML, <input> cannot be a child of <select>.\n' +
'This will cause a hydration error.\n' +
' in input (at **)',
],
);
});

it('relaxes the nesting rules at the root when the container is a singleton', () => {
Expand Down
Loading