Skip to content

Commit d12d306

Browse files
authored
Merge branch 'main' into ci/compressed-size
2 parents 4523933 + bfdd189 commit d12d306

File tree

2 files changed

+136
-27
lines changed

2 files changed

+136
-27
lines changed

debug/src/debug.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,13 @@ export function initDebug() {
353353
});
354354
}
355355

356-
if (typeof type === 'string' && (isTableElement(type) || type === 'p')) {
356+
if (
357+
typeof type === 'string' &&
358+
(isTableElement(type) ||
359+
type === 'p' ||
360+
type === 'a' ||
361+
type === 'button')
362+
) {
357363
// Avoid false positives when Preact only partially rendered the
358364
// HTML tree. Whilst we attempt to include the outer DOM in our
359365
// validation, this wouldn't work on the server for
@@ -387,11 +393,10 @@ export function initDebug() {
387393
type === 'tr' &&
388394
domParentName !== 'thead' &&
389395
domParentName !== 'tfoot' &&
390-
domParentName !== 'tbody' &&
391-
domParentName !== 'table'
396+
domParentName !== 'tbody'
392397
) {
393398
console.error(
394-
'Improper nesting of table. Your <tr> should have a <thead/tbody/tfoot/table> parent.' +
399+
'Improper nesting of table. Your <tr> should have a <thead/tbody/tfoot> parent.' +
395400
serializeVNode(vnode) +
396401
`\n\n${getOwnerStack(vnode)}`
397402
);
@@ -421,6 +426,16 @@ export function initDebug() {
421426
`\n\n${getOwnerStack(vnode)}`
422427
);
423428
}
429+
} else if (type === 'a' || type === 'button') {
430+
if (getDomChildren(vnode).indexOf(type) !== -1) {
431+
console.error(
432+
`Improper nesting of interactive content. Your <${type}>` +
433+
` should not have other ${type === 'a' ? 'anchor' : 'button'}` +
434+
' tags as child-elements.' +
435+
serializeVNode(vnode) +
436+
`\n\n${getOwnerStack(vnode)}`
437+
);
438+
}
424439
}
425440
}
426441

debug/test/browser/debug.test.js

Lines changed: 117 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -543,12 +543,14 @@ describe('debug', () => {
543543
it('Accepts minimal well formed table', () => {
544544
const Table = () => (
545545
<table>
546-
<tr>
547-
<th>Head</th>
548-
</tr>
549-
<tr>
550-
<td>Body</td>
551-
</tr>
546+
<tbody>
547+
<tr>
548+
<th>Head</th>
549+
</tr>
550+
<tr>
551+
<td>Body</td>
552+
</tr>
553+
</tbody>
552554
</table>
553555
);
554556
render(<Table />, scratch);
@@ -586,23 +588,27 @@ describe('debug', () => {
586588
it('accepts valid nested tables', () => {
587589
const Table = () => (
588590
<table>
589-
<tr>
590-
<th>foo</th>
591-
</tr>
592-
<tr>
593-
<td id="nested">
594-
<table>
595-
<tr>
596-
<td>cell1</td>
597-
<td>cell2</td>
598-
<td>cell3</td>
599-
</tr>
600-
</table>
601-
</td>
602-
</tr>
603-
<tr>
604-
<td>bar</td>
605-
</tr>
591+
<tbody>
592+
<tr>
593+
<th>foo</th>
594+
</tr>
595+
<tr>
596+
<td id="nested">
597+
<table>
598+
<tbody>
599+
<tr>
600+
<td>cell1</td>
601+
<td>cell2</td>
602+
<td>cell3</td>
603+
</tr>
604+
</tbody>
605+
</table>
606+
</td>
607+
</tr>
608+
<tr>
609+
<td>bar</td>
610+
</tr>
611+
</tbody>
606612
</table>
607613
);
608614

@@ -661,6 +667,94 @@ describe('debug', () => {
661667
});
662668
});
663669

670+
describe('button nesting', () => {
671+
it('should not warn on a regular button', () => {
672+
const Button = () => <button>Hello world</button>;
673+
674+
render(<Button />, scratch);
675+
expect(console.error).to.not.be.called;
676+
});
677+
678+
it('should warn for nesting illegal dom-nodes under a button', () => {
679+
const Button = () => (
680+
<button>
681+
<button>Hello world</button>
682+
</button>
683+
);
684+
685+
render(<Button />, scratch);
686+
expect(console.error).to.be.calledOnce;
687+
});
688+
689+
it('should warn for nesting illegal dom-nodes under a button as func', () => {
690+
const ButtonChild = ({ children }) => <button>{children}</button>;
691+
const Button = () => (
692+
<button>
693+
<ButtonChild>Hello world</ButtonChild>
694+
</button>
695+
);
696+
697+
render(<Button />, scratch);
698+
expect(console.error).to.be.calledOnce;
699+
});
700+
701+
it('should not warn for nesting non-interactive content under a button', () => {
702+
const Button = () => (
703+
<button>
704+
<span>Hello </span>
705+
<a>World</a>
706+
</button>
707+
);
708+
709+
render(<Button />, scratch);
710+
expect(console.error).to.not.be.called;
711+
});
712+
});
713+
714+
describe('anchor nesting', () => {
715+
it('should not warn a regular anchor', () => {
716+
const Anchor = () => <a>Hello world</a>;
717+
718+
render(<Anchor />, scratch);
719+
expect(console.error).to.not.be.called;
720+
});
721+
722+
it('should warn for nesting illegal dom-nodes under an anchor', () => {
723+
const Anchor = () => (
724+
<a>
725+
<a>Hello world</a>
726+
</a>
727+
);
728+
729+
render(<Anchor />, scratch);
730+
expect(console.error).to.be.calledOnce;
731+
});
732+
733+
it('should warn for nesting illegal dom-nodes under an anchor as func', () => {
734+
const AnchorChild = ({ children }) => <a>{children}</a>;
735+
const Anchor = () => (
736+
<a>
737+
<AnchorChild>Hello world</AnchorChild>
738+
</a>
739+
);
740+
741+
render(<Anchor />, scratch);
742+
expect(console.error).to.be.calledOnce;
743+
});
744+
745+
it('should not warn for nesting non-interactive content under an anchor', () => {
746+
const Anchor = () => (
747+
<a>
748+
<span>Hello </span>
749+
<button>World</button>
750+
</a>
751+
);
752+
753+
render(<Anchor />, scratch);
754+
expect(console.error).to.not.be.called;
755+
});
756+
});
757+
664758
describe('PropTypes', () => {
665759
beforeEach(() => {
666760
resetPropWarnings();

0 commit comments

Comments
 (0)