Skip to content

Commit

Permalink
fix(runtime): scoped: true slot fallback with forwarded slot (#6171)
Browse files Browse the repository at this point in the history
* fix(runtime): `scoped: true` slot fallback with forwarded slot

* chore:

---------

Co-authored-by: John Jenkins <[email protected]>
  • Loading branch information
johnjenkins and John Jenkins authored Feb 21, 2025
1 parent 30f2a09 commit 57e7e58
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 9 deletions.
4 changes: 2 additions & 2 deletions src/runtime/slot-polyfill-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const updateFallbackSlotVisibility = (elm: d.RenderNode) => {
getHostSlotNodes(childNodes as any, (elm as HTMLElement).tagName).forEach((slotNode) => {
if (slotNode.nodeType === NODE_TYPE.ElementNode && slotNode.tagName === 'SLOT-FB') {
// this is a slot fallback node
if (getSlotChildSiblings(slotNode, getSlotName(slotNode), false)?.length) {
if (getSlotChildSiblings(slotNode, getSlotName(slotNode), false).length) {
// has slotted nodes, hide fallback
slotNode.hidden = true;
} else {
Expand Down Expand Up @@ -108,7 +108,7 @@ export const getSlotChildSiblings = (slot: d.RenderNode, slotName: string, inclu
let node = slot;

while ((node = node.nextSibling as any)) {
if (getSlotName(node) === slotName) childNodes.push(node as any);
if (getSlotName(node) === slotName && (includeSlot || !node['s-sr'])) childNodes.push(node as any);
}
return childNodes;
};
Expand Down
14 changes: 7 additions & 7 deletions src/runtime/test/hydrate-slot-fallback.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ describe('hydrate, slot fallback', () => {
return (
<article>
<cmp-b>
<slot>Fallback content parent - should not be hidden</slot>
<slot>Fallback content parent - should be hidden</slot>
</cmp-b>
</article>
);
Expand All @@ -458,7 +458,7 @@ describe('hydrate, slot fallback', () => {
render() {
return (
<section>
<slot>Fallback content child - should be hidden</slot>
<slot>Fallback content child - should not be hidden</slot>
</section>
);
}
Expand All @@ -479,13 +479,13 @@ describe('hydrate, slot fallback', () => {
<!--r.2-->
<!--o.1.2.-->
<section c-id=\"2.0.0.0\">
<slot-fb c-id=\"2.1.1.0\" hidden=\"\" s-sn=\"\">
<slot-fb c-id=\"2.1.1.0\" s-sn=\"\">
<!--t.2.2.2.0-->
Fallback content child - should be hidden
Fallback content child - should not be hidden
</slot-fb>
<slot-fb c-id=\"1.2.2.0\" s-sn=\"\">
<!--t.1.3.3.0-->
Fallback content parent - should not be hidden
Fallback content parent - should be hidden
</slot-fb>
</section>
</cmp-b>
Expand All @@ -507,12 +507,12 @@ describe('hydrate, slot fallback', () => {
<mock:shadow-root>
<section>
<slot>
Fallback content child - should be hidden
Fallback content child - should not be hidden
</slot>
</section>
</mock:shadow-root>
<slot-fb class="sc-cmp-a">
Fallback content parent - should not be hidden
Fallback content parent - should be hidden
</slot-fb>
</cmp-b>
</article>
Expand Down
24 changes: 24 additions & 0 deletions test/wdio/slot-fallback-with-forwarded-slot/child-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Component, h, Host, Prop } from '@stencil/core';

@Component({
tag: 'slot-forward-child-fallback',
scoped: true,
styles: `
:host {
display: block;
}
`,
})
export class ChildComponent {
@Prop() label: string;

render() {
return (
<Host>
<div>
<slot name="label">{this.label}</slot>
</div>
</Host>
);
}
}
51 changes: 51 additions & 0 deletions test/wdio/slot-fallback-with-forwarded-slot/cmp.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { h } from '@stencil/core';
import { render } from '@wdio/browser-runner/stencil';

describe('slot-fallback-with-forwarded-slot', () => {
it('renders fallback via prop', async () => {
// @ts-expect-error - wdio complaining about missing prop
const { $root, root } = render({
template: () => <slot-forward-root label="Slot fallback via property"></slot-forward-root>,
});
await $root.$('slot-fb');
const fb: HTMLElement = document.querySelector('slot-fb');

expect(await $root.getText()).toBe('');
expect(fb.textContent).toBe('Slot fallback via property');
expect(fb.getAttribute('hidden')).toBe(null);
expect(fb.hidden).toBe(false);

const p = document.createElement('p');
p.textContent = 'Slot content via slot';
p.slot = 'label';
root.appendChild(p);

expect(await $root.getText()).toBe('Slot content via slot');
expect(fb.getAttribute('hidden')).toBe('');
expect(fb.hidden).toBe(true);
});

it('should hide slot-fb elements when slotted content exists', async () => {
// @ts-expect-error - wdio complaining about missing prop
const { $root, root } = render({
template: () => (
<slot-forward-root label="Slot fallback via property">
<div slot="label">Slot content via slot</div>
</slot-forward-root>
),
});
await $root.$('slot-fb');
const fb: HTMLElement = document.querySelector('slot-fb');

expect(await $root.getText()).toBe('Slot content via slot');
expect(fb.textContent).toBe('Slot fallback via property');
expect(fb.getAttribute('hidden')).toBe('');
expect(fb.hidden).toBe(true);

root.removeChild(root.childNodes[0]);

expect(await $root.getText()).toBe('');
expect(fb.getAttribute('hidden')).toBe(null);
expect(fb.hidden).toBe(false);
});
});
24 changes: 24 additions & 0 deletions test/wdio/slot-fallback-with-forwarded-slot/parent-component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Component, h, Host, Prop } from '@stencil/core';

@Component({
tag: 'slot-forward-root',
scoped: true,
styles: `
:host {
display: block;
}
`,
})
export class MyComponent {
@Prop() label: string;

render() {
return (
<Host>
<slot-forward-child-fallback label={this.label}>
<slot name="label" />
</slot-forward-child-fallback>
</Host>
);
}
}

0 comments on commit 57e7e58

Please sign in to comment.