Skip to content

Commit bfdd189

Browse files
authored
Merge pull request #4376 from preactjs/debug/button-anchor-nesting
debug: Provide error for illegal nesting of <button> and <a>
2 parents c29caa3 + 06a2df1 commit bfdd189

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

debug/src/debug.js

Lines changed: 17 additions & 1 deletion
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
@@ -420,6 +426,16 @@ export function initDebug() {
420426
`\n\n${getOwnerStack(vnode)}`
421427
);
422428
}
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+
}
423439
}
424440
}
425441

debug/test/browser/debug.test.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,94 @@ describe('debug', () => {
667667
});
668668
});
669669

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+
670758
describe('PropTypes', () => {
671759
beforeEach(() => {
672760
resetPropWarnings();

0 commit comments

Comments
 (0)