Skip to content

Commit ce5b27b

Browse files
authored
fix(core): fix SlotController.isEmpty() (#2906)
1 parent c9d5d23 commit ce5b27b

File tree

3 files changed

+232
-18
lines changed

3 files changed

+232
-18
lines changed

.changeset/shaky-things-beg.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@patternfly/pfe-core": patch
3+
---
4+
5+
`SlotController`: correctly report the state of the default slot in `isEmpty()` calls with no arguments
6+

core/pfe-core/controllers/slot-controller-server.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,17 @@ export class SlotController implements SlotControllerPublicAPI {
2626
.map(x => x.trim());
2727
}
2828

29-
getSlotted<T extends Element = Element>(..._: string[]): T[] {
29+
getSlotted<T extends Element = Element>(..._: (string | null)[]): T[] {
3030
return [];
3131
}
3232

3333
hasSlotted(...names: (string | null)[]): boolean {
3434
const attr = this.host.getAttribute(SlotController.attribute);
3535
const anon = this.host.hasAttribute(SlotController.anonymousAttribute);
3636
const hints = new Set(this.fromAttribute(attr));
37+
if (!names.length) {
38+
names.push(null);
39+
}
3740
return names.every(x => x === null ? anon : hints.has(x));
3841
}
3942

core/pfe-core/controllers/test/slot-controller.spec.ts

Lines changed: 222 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,28 @@
1-
import { expect, fixture, nextFrame } from '@open-wc/testing';
1+
import { expect, fixture } from '@open-wc/testing';
22

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

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

8-
@customElement('test-slot-controller-with-named-and-anonymous')
9-
class TestSlotControllerWithNamedAndAnonymous extends LitElement {
9+
@customElement('test-slot-controller')
10+
class TestSlotController extends LitElement {
1011
controller = new SlotController(this, 'a', null);
1112
render(): TemplateResult {
1213
return html`
1314
<slot name="a"></slot>
14-
<slot name="b"></slot>
15+
<slot></slot>
16+
`;
17+
}
18+
}
19+
20+
@customElement('test-slot-controller-server')
21+
class TestSlotControllerServer extends LitElement {
22+
controller = new SlotControllerServer(this, 'a', null);
23+
render(): TemplateResult {
24+
return html`
25+
<slot name="a"></slot>
1526
<slot></slot>
1627
`;
1728
}
@@ -20,10 +31,10 @@ class TestSlotControllerWithNamedAndAnonymous extends LitElement {
2031
describe('SlotController', function() {
2132
describe('with named and anonymous slots', function() {
2233
describe('with no content', function() {
23-
let element: TestSlotControllerWithNamedAndAnonymous;
34+
let element: TestSlotController;
2435
beforeEach(async function() {
2536
element = await fixture(html`
26-
<test-slot-controller-with-named-and-anonymous></test-slot-controller-with-named-and-anonymous>
37+
<test-slot-controller></test-slot-controller>
2738
`);
2839
});
2940
it('reports empty named slots', function() {
@@ -50,12 +61,12 @@ describe('SlotController', function() {
5061
});
5162

5263
describe('with element content in default slot', function() {
53-
let element: TestSlotControllerWithNamedAndAnonymous;
64+
let element: TestSlotController;
5465
beforeEach(async function() {
5566
element = await fixture(html`
56-
<test-slot-controller-with-named-and-anonymous>
67+
<test-slot-controller>
5768
<p>element</p>
58-
</test-slot-controller-with-named-and-anonymous>
69+
</test-slot-controller>
5970
`);
6071
});
6172
it('reports empty named slots', function() {
@@ -82,12 +93,12 @@ describe('SlotController', function() {
8293
});
8394

8495
describe('with element content in named slot', function() {
85-
let element: TestSlotControllerWithNamedAndAnonymous;
96+
let element: TestSlotController;
8697
beforeEach(async function() {
8798
element = await fixture(html`
88-
<test-slot-controller-with-named-and-anonymous>
99+
<test-slot-controller>
89100
<p slot="a">element</p>
90-
</test-slot-controller-with-named-and-anonymous>
101+
</test-slot-controller>
91102
`);
92103
});
93104
it('reports non-empty named slots', function() {
@@ -114,12 +125,12 @@ describe('SlotController', function() {
114125
});
115126

116127
describe('with text content in default slot', function() {
117-
let element: TestSlotControllerWithNamedAndAnonymous;
128+
let element: TestSlotController;
118129
beforeEach(async function() {
119130
element = await fixture(html`
120-
<test-slot-controller-with-named-and-anonymous>
131+
<test-slot-controller>
121132
text
122-
</test-slot-controller-with-named-and-anonymous>
133+
</test-slot-controller>
123134
`);
124135
});
125136
it('reports empty named slots', function() {
@@ -137,10 +148,204 @@ describe('SlotController', function() {
137148
it('returns empty list for getSlotted("a")', function() {
138149
expect(element.controller.getSlotted('a')).to.be.empty;
139150
});
140-
it('returns lengthy list for getSlotted(null)', function() {
151+
it('returns empty list for getSlotted(null)', function() {
141152
expect(element.controller.getSlotted(null)).to.be.empty;
142153
});
143-
it('returns lengthy list for getSlotted()', function() {
154+
it('returns empty list for getSlotted()', function() {
155+
expect(element.controller.getSlotted()).to.be.empty;
156+
});
157+
});
158+
159+
describe('with white space in default slot', function() {
160+
let element: TestSlotController;
161+
beforeEach(async function() {
162+
element = await fixture(html`
163+
<test-slot-controller-server>
164+
165+
</test-slot-controller-server>
166+
`);
167+
});
168+
it('reports empty named slots', function() {
169+
expect(element.controller.hasSlotted('a')).to.be.false;
170+
expect(element.controller.isEmpty('a')).to.be.true;
171+
});
172+
it('reports empty default slot', function() {
173+
expect(element.controller.hasSlotted(null)).to.be.false;
174+
expect(element.controller.isEmpty(null)).to.be.true;
175+
});
176+
it('reports empty default slot with no arguments', function() {
177+
expect(element.controller.hasSlotted()).to.be.false;
178+
expect(element.controller.isEmpty()).to.be.true;
179+
});
180+
it('returns empty list for getSlotted("a")', function() {
181+
expect(element.controller.getSlotted('a')).to.be.empty;
182+
});
183+
it('returns empty list for getSlotted(null)', function() {
184+
expect(element.controller.getSlotted(null)).to.be.empty;
185+
});
186+
it('returns empty list for getSlotted()', function() {
187+
expect(element.controller.getSlotted()).to.be.empty;
188+
});
189+
});
190+
});
191+
});
192+
193+
describe('SlotController (server)', function() {
194+
describe('with named and anonymous slots', function() {
195+
describe('with no ssr hint attrs', function() {
196+
let element: TestSlotControllerServer;
197+
beforeEach(async function() {
198+
element = await fixture(html`
199+
<test-slot-controller-server></test-slot-controller-server>
200+
`);
201+
});
202+
it('reports empty named slots', function() {
203+
expect(element.controller.hasSlotted('a')).to.be.false;
204+
expect(element.controller.isEmpty('a')).to.be.true;
205+
});
206+
it('reports empty default slot', function() {
207+
expect(element.controller.hasSlotted(null)).to.be.false;
208+
expect(element.controller.isEmpty(null)).to.be.true;
209+
});
210+
it('reports empty default slot with no arguments', function() {
211+
expect(element.controller.hasSlotted()).to.be.false;
212+
expect(element.controller.isEmpty()).to.be.true;
213+
});
214+
it('returns empty list for getSlotted("a")', function() {
215+
expect(element.controller.getSlotted('a')).to.be.empty;
216+
});
217+
it('returns empty list for getSlotted(null)', function() {
218+
expect(element.controller.getSlotted(null)).to.be.empty;
219+
});
220+
it('returns empty list for getSlotted()', function() {
221+
expect(element.controller.getSlotted()).to.be.empty;
222+
});
223+
});
224+
225+
describe('with ssr-hint-has-slotted-default attr', function() {
226+
let element: TestSlotController;
227+
beforeEach(async function() {
228+
element = await fixture(html`
229+
<test-slot-controller-server ssr-hint-has-slotted-default>
230+
<p>element</p>
231+
</test-slot-controller-server>
232+
`);
233+
});
234+
it('reports empty named slots', function() {
235+
expect(element.controller.hasSlotted('a')).to.be.false;
236+
expect(element.controller.isEmpty('a')).to.be.true;
237+
});
238+
it('reports non-empty default slot', function() {
239+
expect(element.controller.hasSlotted(null)).to.be.true;
240+
expect(element.controller.isEmpty(null)).to.be.false;
241+
});
242+
it('reports non-empty default slot with no arguments', function() {
243+
expect(element.controller.hasSlotted()).to.be.true;
244+
expect(element.controller.isEmpty()).to.be.false;
245+
});
246+
it('returns empty list for getSlotted("a")', function() {
247+
expect(element.controller.getSlotted('a')).to.be.empty;
248+
});
249+
it('returns empty list for getSlotted(null)', function() {
250+
expect(element.controller.getSlotted(null)).to.be.empty;
251+
});
252+
it('returns empty list for getSlotted()', function() {
253+
expect(element.controller.getSlotted()).to.be.empty;
254+
});
255+
});
256+
257+
describe('with ssr-hint-has-slotted="a" attr', function() {
258+
let element: TestSlotController;
259+
beforeEach(async function() {
260+
element = await fixture(html`
261+
<test-slot-controller-server ssr-hint-has-slotted="a">
262+
<p slot="a">element</p>
263+
</test-slot-controller-server>
264+
`);
265+
});
266+
it('reports non-empty named slots', function() {
267+
expect(element.controller.hasSlotted('a')).to.be.true;
268+
expect(element.controller.isEmpty('a')).to.be.false;
269+
});
270+
it('reports empty default slot', function() {
271+
expect(element.controller.hasSlotted(null)).to.be.false;
272+
expect(element.controller.isEmpty(null)).to.be.true;
273+
});
274+
it('reports empty default slot with no arguments', function() {
275+
expect(element.controller.hasSlotted()).to.be.false;
276+
expect(element.controller.isEmpty()).to.be.true;
277+
});
278+
it('returns empty list for getSlotted("a")', function() {
279+
expect(element.controller.getSlotted('a')).to.be.empty;
280+
});
281+
it('returns empty list for getSlotted(null)', function() {
282+
expect(element.controller.getSlotted(null)).to.be.empty;
283+
});
284+
it('returns empty list for getSlotted()', function() {
285+
expect(element.controller.getSlotted()).to.be.empty;
286+
});
287+
});
288+
289+
describe('with ssr-hint-has-slotted-default attr (text node)', function() {
290+
let element: TestSlotController;
291+
beforeEach(async function() {
292+
element = await fixture(html`
293+
<test-slot-controller-server ssr-hint-has-slotted-default>
294+
text
295+
</test-slot-controller-server>
296+
`);
297+
});
298+
it('reports empty named slots', function() {
299+
expect(element.controller.hasSlotted('a')).to.be.false;
300+
expect(element.controller.isEmpty('a')).to.be.true;
301+
});
302+
it('reports non-empty default slot', function() {
303+
expect(element.controller.hasSlotted(null)).to.be.true;
304+
expect(element.controller.isEmpty(null)).to.be.false;
305+
});
306+
it('reports non-empty default slot with no arguments', function() {
307+
expect(element.controller.hasSlotted()).to.be.true;
308+
expect(element.controller.isEmpty()).to.be.false;
309+
});
310+
it('returns empty list for getSlotted("a")', function() {
311+
expect(element.controller.getSlotted('a')).to.be.empty;
312+
});
313+
it('returns empty list for getSlotted(null)', function() {
314+
expect(element.controller.getSlotted(null)).to.be.empty;
315+
});
316+
it('returns empty list for getSlotted()', function() {
317+
expect(element.controller.getSlotted()).to.be.empty;
318+
});
319+
});
320+
321+
describe('with no ssr hint attrs (white space text node)', function() {
322+
let element: TestSlotController;
323+
beforeEach(async function() {
324+
element = await fixture(html`
325+
<test-slot-controller-server>
326+
327+
</test-slot-controller-server>
328+
`);
329+
});
330+
it('reports empty named slots', function() {
331+
expect(element.controller.hasSlotted('a')).to.be.false;
332+
expect(element.controller.isEmpty('a')).to.be.true;
333+
});
334+
it('reports empty default slot', function() {
335+
expect(element.controller.hasSlotted(null)).to.be.false;
336+
expect(element.controller.isEmpty(null)).to.be.true;
337+
});
338+
it('reports empty default slot with no arguments', function() {
339+
expect(element.controller.hasSlotted()).to.be.false;
340+
expect(element.controller.isEmpty()).to.be.true;
341+
});
342+
it('returns empty list for getSlotted("a")', function() {
343+
expect(element.controller.getSlotted('a')).to.be.empty;
344+
});
345+
it('returns empty list for getSlotted(null)', function() {
346+
expect(element.controller.getSlotted(null)).to.be.empty;
347+
});
348+
it('returns empty list for getSlotted()', function() {
144349
expect(element.controller.getSlotted()).to.be.empty;
145350
});
146351
});

0 commit comments

Comments
 (0)