Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/shaky-things-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@patternfly/pfe-core": patch
---

`SlotController`: correctly report the state of the default slot in `isEmpty()` calls with no arguments

5 changes: 4 additions & 1 deletion core/pfe-core/controllers/slot-controller-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,17 @@ export class SlotController implements SlotControllerPublicAPI {
.map(x => x.trim());
}

getSlotted<T extends Element = Element>(..._: string[]): T[] {
getSlotted<T extends Element = Element>(..._: (string | null)[]): T[] {
return [];
}

hasSlotted(...names: (string | null)[]): boolean {
const attr = this.host.getAttribute(SlotController.attribute);
const anon = this.host.hasAttribute(SlotController.anonymousAttribute);
const hints = new Set(this.fromAttribute(attr));
if (!names.length) {
names.push(null);
}
return names.every(x => x === null ? anon : hints.has(x));
}

Expand Down
239 changes: 222 additions & 17 deletions core/pfe-core/controllers/test/slot-controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import { expect, fixture, nextFrame } from '@open-wc/testing';
import { expect, fixture } from '@open-wc/testing';

import { customElement } from 'lit/decorators/custom-element.js';
import { LitElement, html, type TemplateResult } from 'lit';

import { SlotController } from '../slot-controller.js';
import { SlotController as SlotControllerServer } from '../slot-controller-server.js';

@customElement('test-slot-controller-with-named-and-anonymous')
class TestSlotControllerWithNamedAndAnonymous extends LitElement {
@customElement('test-slot-controller')
class TestSlotController extends LitElement {
controller = new SlotController(this, 'a', null);
render(): TemplateResult {
return html`
<slot name="a"></slot>
<slot name="b"></slot>
<slot></slot>
`;
}
}

@customElement('test-slot-controller-server')
class TestSlotControllerServer extends LitElement {
controller = new SlotControllerServer(this, 'a', null);
render(): TemplateResult {
return html`
<slot name="a"></slot>
<slot></slot>
`;
}
Expand All @@ -20,10 +31,10 @@ class TestSlotControllerWithNamedAndAnonymous extends LitElement {
describe('SlotController', function() {
describe('with named and anonymous slots', function() {
describe('with no content', function() {
let element: TestSlotControllerWithNamedAndAnonymous;
let element: TestSlotController;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-with-named-and-anonymous></test-slot-controller-with-named-and-anonymous>
<test-slot-controller></test-slot-controller>
`);
});
it('reports empty named slots', function() {
Expand All @@ -50,12 +61,12 @@ describe('SlotController', function() {
});

describe('with element content in default slot', function() {
let element: TestSlotControllerWithNamedAndAnonymous;
let element: TestSlotController;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-with-named-and-anonymous>
<test-slot-controller>
<p>element</p>
</test-slot-controller-with-named-and-anonymous>
</test-slot-controller>
`);
});
it('reports empty named slots', function() {
Expand All @@ -82,12 +93,12 @@ describe('SlotController', function() {
});

describe('with element content in named slot', function() {
let element: TestSlotControllerWithNamedAndAnonymous;
let element: TestSlotController;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-with-named-and-anonymous>
<test-slot-controller>
<p slot="a">element</p>
</test-slot-controller-with-named-and-anonymous>
</test-slot-controller>
`);
});
it('reports non-empty named slots', function() {
Expand All @@ -114,12 +125,12 @@ describe('SlotController', function() {
});

describe('with text content in default slot', function() {
let element: TestSlotControllerWithNamedAndAnonymous;
let element: TestSlotController;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-with-named-and-anonymous>
<test-slot-controller>
text
</test-slot-controller-with-named-and-anonymous>
</test-slot-controller>
`);
});
it('reports empty named slots', function() {
Expand All @@ -137,10 +148,204 @@ describe('SlotController', function() {
it('returns empty list for getSlotted("a")', function() {
expect(element.controller.getSlotted('a')).to.be.empty;
});
it('returns lengthy list for getSlotted(null)', function() {
it('returns empty list for getSlotted(null)', function() {
expect(element.controller.getSlotted(null)).to.be.empty;
});
it('returns lengthy list for getSlotted()', function() {
it('returns empty list for getSlotted()', function() {
expect(element.controller.getSlotted()).to.be.empty;
});
});

describe('with white space in default slot', function() {
let element: TestSlotController;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-server>

</test-slot-controller-server>
`);
});
it('reports empty named slots', function() {
expect(element.controller.hasSlotted('a')).to.be.false;
expect(element.controller.isEmpty('a')).to.be.true;
});
it('reports empty default slot', function() {
expect(element.controller.hasSlotted(null)).to.be.false;
expect(element.controller.isEmpty(null)).to.be.true;
});
it('reports empty default slot with no arguments', function() {
expect(element.controller.hasSlotted()).to.be.false;
expect(element.controller.isEmpty()).to.be.true;
});
it('returns empty list for getSlotted("a")', function() {
expect(element.controller.getSlotted('a')).to.be.empty;
});
it('returns empty list for getSlotted(null)', function() {
expect(element.controller.getSlotted(null)).to.be.empty;
});
it('returns empty list for getSlotted()', function() {
expect(element.controller.getSlotted()).to.be.empty;
});
});
});
});

describe('SlotController (server)', function() {
describe('with named and anonymous slots', function() {
describe('with no ssr hint attrs', function() {
let element: TestSlotControllerServer;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-server></test-slot-controller-server>
`);
});
it('reports empty named slots', function() {
expect(element.controller.hasSlotted('a')).to.be.false;
expect(element.controller.isEmpty('a')).to.be.true;
});
it('reports empty default slot', function() {
expect(element.controller.hasSlotted(null)).to.be.false;
expect(element.controller.isEmpty(null)).to.be.true;
});
it('reports empty default slot with no arguments', function() {
expect(element.controller.hasSlotted()).to.be.false;
expect(element.controller.isEmpty()).to.be.true;
});
it('returns empty list for getSlotted("a")', function() {
expect(element.controller.getSlotted('a')).to.be.empty;
});
it('returns empty list for getSlotted(null)', function() {
expect(element.controller.getSlotted(null)).to.be.empty;
});
it('returns empty list for getSlotted()', function() {
expect(element.controller.getSlotted()).to.be.empty;
});
});

describe('with ssr-hint-has-slotted-default attr', function() {
let element: TestSlotController;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-server ssr-hint-has-slotted-default>
<p>element</p>
</test-slot-controller-server>
`);
});
it('reports empty named slots', function() {
expect(element.controller.hasSlotted('a')).to.be.false;
expect(element.controller.isEmpty('a')).to.be.true;
});
it('reports non-empty default slot', function() {
expect(element.controller.hasSlotted(null)).to.be.true;
expect(element.controller.isEmpty(null)).to.be.false;
});
it('reports non-empty default slot with no arguments', function() {
expect(element.controller.hasSlotted()).to.be.true;
expect(element.controller.isEmpty()).to.be.false;
});
it('returns empty list for getSlotted("a")', function() {
expect(element.controller.getSlotted('a')).to.be.empty;
});
it('returns empty list for getSlotted(null)', function() {
expect(element.controller.getSlotted(null)).to.be.empty;
});
it('returns empty list for getSlotted()', function() {
expect(element.controller.getSlotted()).to.be.empty;
});
});

describe('with ssr-hint-has-slotted="a" attr', function() {
let element: TestSlotController;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-server ssr-hint-has-slotted="a">
<p slot="a">element</p>
</test-slot-controller-server>
`);
});
it('reports non-empty named slots', function() {
expect(element.controller.hasSlotted('a')).to.be.true;
expect(element.controller.isEmpty('a')).to.be.false;
});
it('reports empty default slot', function() {
expect(element.controller.hasSlotted(null)).to.be.false;
expect(element.controller.isEmpty(null)).to.be.true;
});
it('reports empty default slot with no arguments', function() {
expect(element.controller.hasSlotted()).to.be.false;
expect(element.controller.isEmpty()).to.be.true;
});
it('returns empty list for getSlotted("a")', function() {
expect(element.controller.getSlotted('a')).to.be.empty;
});
it('returns empty list for getSlotted(null)', function() {
expect(element.controller.getSlotted(null)).to.be.empty;
});
it('returns empty list for getSlotted()', function() {
expect(element.controller.getSlotted()).to.be.empty;
});
});

describe('with ssr-hint-has-slotted-default attr (text node)', function() {
let element: TestSlotController;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-server ssr-hint-has-slotted-default>
text
</test-slot-controller-server>
`);
});
it('reports empty named slots', function() {
expect(element.controller.hasSlotted('a')).to.be.false;
expect(element.controller.isEmpty('a')).to.be.true;
});
it('reports non-empty default slot', function() {
expect(element.controller.hasSlotted(null)).to.be.true;
expect(element.controller.isEmpty(null)).to.be.false;
});
it('reports non-empty default slot with no arguments', function() {
expect(element.controller.hasSlotted()).to.be.true;
expect(element.controller.isEmpty()).to.be.false;
});
it('returns empty list for getSlotted("a")', function() {
expect(element.controller.getSlotted('a')).to.be.empty;
});
it('returns empty list for getSlotted(null)', function() {
expect(element.controller.getSlotted(null)).to.be.empty;
});
it('returns empty list for getSlotted()', function() {
expect(element.controller.getSlotted()).to.be.empty;
});
});

describe('with no ssr hint attrs (white space text node)', function() {
let element: TestSlotController;
beforeEach(async function() {
element = await fixture(html`
<test-slot-controller-server>

</test-slot-controller-server>
`);
});
it('reports empty named slots', function() {
expect(element.controller.hasSlotted('a')).to.be.false;
expect(element.controller.isEmpty('a')).to.be.true;
});
it('reports empty default slot', function() {
expect(element.controller.hasSlotted(null)).to.be.false;
expect(element.controller.isEmpty(null)).to.be.true;
});
it('reports empty default slot with no arguments', function() {
expect(element.controller.hasSlotted()).to.be.false;
expect(element.controller.isEmpty()).to.be.true;
});
it('returns empty list for getSlotted("a")', function() {
expect(element.controller.getSlotted('a')).to.be.empty;
});
it('returns empty list for getSlotted(null)', function() {
expect(element.controller.getSlotted(null)).to.be.empty;
});
it('returns empty list for getSlotted()', function() {
expect(element.controller.getSlotted()).to.be.empty;
});
});
Expand Down
Loading