Skip to content
27 changes: 25 additions & 2 deletions lib/checks/lists/invalid-children-evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,32 @@ function getInvalidSelector(
const role = getExplicitRole(vChild);
if (role) {
return validRoles.includes(role) ? false : selector + `[role=${role}]`;
} else {
return validNodeNames.includes(nodeName) ? false : selector + nodeName;
}

if (validNodeNames.includes(nodeName)) {
return false;
}

// Check if a shadow host's shadow DOM root element is a valid child.
// This handles web components that wrap a valid element in their shadow DOM
// (e.g. <my-list-item> whose shadow DOM renders <li><slot></slot></li>).
if (vChild.actualNode?.shadowRoot) {
const shadowValid = vChild.children.some(shadowChild => {
if (shadowChild.actualNode?.nodeType !== 1) {
return false;
}
const shadowRole = getExplicitRole(shadowChild);
if (shadowRole) {
return validRoles.includes(shadowRole);
}
return validNodeNames.includes(shadowChild.props.nodeName);
});
if (shadowValid) {
return false;
}
}
Comment on lines +73 to +88
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalid-children-evaluate is also used by the only-dlitems check (not just only-listitems). This new shadow-host unwrapping logic changes only-dlitems behavior for custom elements with shadow DOM (e.g., <my-term> rendering <dt>), but this PR only adds tests for only-listitems. Add equivalent unit tests in test/checks/lists/only-dlitems.js to cover valid/invalid shadow DOM wrappers and prevent regressions.

Copilot uses AI. Check for mistakes.

return selector + nodeName;
}

function isDivGroup(vNode) {
Expand Down
21 changes: 21 additions & 0 deletions test/checks/lists/only-listitems.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,27 @@ describe('only-listitems', () => {
});

describe('shadow DOM', () => {
it('should return false when custom elements with shadow DOM <li> are slotted into a list', () => {
// Simulate <my-list-item> web components whose shadow DOM renders <li>
const item1 = document.createElement('my-list-item');
item1.attachShadow({ mode: 'open' }).innerHTML = '<li><slot></slot></li>';
item1.textContent = 'Item 1';

const item2 = document.createElement('my-list-item');
item2.attachShadow({ mode: 'open' }).innerHTML = '<li><slot></slot></li>';
item2.textContent = 'Item 2';

// Use a div (in possibleShadowRoots) as the outer shadow host,
// matching the pattern of the other shadow DOM tests in this suite
const host = document.createElement('div');
host.appendChild(item1);
host.appendChild(item2);
host.attachShadow({ mode: 'open' }).innerHTML = '<ul><slot></slot></ul>';

const checkArgs = checkSetup(host, 'ul');
assert.isFalse(checkEvaluate.apply(checkContext, checkArgs));
});
Comment on lines +224 to +243
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new shadow-DOM coverage here only exercises

    , but the bug/behavior applies equally to
      (only-listitems.json applies to both). Adding an equivalent test for
        with slotted custom elements wrapping
      1. would help prevent regressions for ordered lists.

Copilot uses AI. Check for mistakes.

it('should return false in a shadow DOM pass', () => {
const node = document.createElement('div');
node.innerHTML = '<li>My list item </li>';
Expand Down
Loading