Skip to content

Commit 434fcc3

Browse files
authored
Support nested Tab Groups (#721)
1 parent b343d86 commit 434fcc3

6 files changed

+150
-79
lines changed

.changeset/quiet-moose-tickle.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@crowdstrike/glide-core': patch
3+
---
4+
5+
Tab Groups can now be nested.

custom-elements.json

+17-26
Original file line numberDiff line numberDiff line change
@@ -9423,15 +9423,6 @@
94239423
"name": "selectedTab",
94249424
"privacy": "private"
94259425
},
9426-
{
9427-
"kind": "field",
9428-
"name": "isAfterFirstUpdated",
9429-
"type": {
9430-
"text": "boolean"
9431-
},
9432-
"privacy": "private",
9433-
"default": "false"
9434-
},
94359426
{
94369427
"kind": "field",
94379428
"name": "isDisableOverflowEndButton",
@@ -9535,7 +9526,7 @@
95359526
},
95369527
{
95379528
"kind": "method",
9538-
"name": "#onClick",
9529+
"name": "#onComponentClick",
95399530
"privacy": "private",
95409531
"parameters": [
95419532
{
@@ -9548,35 +9539,35 @@
95489539
},
95499540
{
95509541
"kind": "method",
9551-
"name": "#onClickOverflowEndButton",
9552-
"privacy": "private"
9542+
"name": "#onComponentKeydown",
9543+
"privacy": "private",
9544+
"parameters": [
9545+
{
9546+
"name": "event",
9547+
"type": {
9548+
"text": "KeyboardEvent"
9549+
}
9550+
}
9551+
]
95539552
},
95549553
{
95559554
"kind": "method",
9556-
"name": "#onClickOverflowStartButton",
9555+
"name": "#onNavSlotChange",
95579556
"privacy": "private"
95589557
},
95599558
{
95609559
"kind": "method",
9561-
"name": "#onFocusout",
9560+
"name": "#onOverflowEndButtonClick",
95629561
"privacy": "private"
95639562
},
95649563
{
95659564
"kind": "method",
9566-
"name": "#onKeydown",
9567-
"privacy": "private",
9568-
"parameters": [
9569-
{
9570-
"name": "event",
9571-
"type": {
9572-
"text": "KeyboardEvent"
9573-
}
9574-
}
9575-
]
9565+
"name": "#onOverflowStartButtonClick",
9566+
"privacy": "private"
95769567
},
95779568
{
95789569
"kind": "method",
9579-
"name": "#onNavSlotChange",
9570+
"name": "#onTabListFocusout",
95809571
"privacy": "private"
95819572
},
95829573
{
@@ -9619,7 +9610,7 @@
96199610
},
96209611
{
96219612
"kind": "method",
9622-
"name": "#setupTabs",
9613+
"name": "#setUpTabs",
96239614
"privacy": "private"
96249615
},
96259616
{

src/library/final.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
export default function <Type extends new (...arguments_: any[]) => object>(
33
constructor: Type,
44
) {
5-
return class Final extends constructor {
5+
class Final extends constructor {
66
// eslint-disable-next-line @typescript-eslint/no-explicit-any
77
constructor(...arguments_: any[]) {
88
if (new.target !== Final) {
@@ -12,5 +12,12 @@ export default function <Type extends new (...arguments_: any[]) => object>(
1212
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
1313
super(...arguments_);
1414
}
15-
};
15+
}
16+
17+
// Particularly helpful when dealing with `event.target`. Consumers
18+
// expect that property's name to be that of the original constructor
19+
// and not "Final".
20+
Object.defineProperty(Final, 'name', { value: constructor.name });
21+
22+
return Final;
1623
}

src/tab.group.stories.ts

+27-14
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,18 @@ const meta: Meta = {
6565
'<glide-core-tab-panel>[--padding-inline-start]': '',
6666
},
6767
play(context) {
68-
const tabs = context.canvasElement.querySelectorAll('glide-core-tab');
69-
70-
for (const tab of tabs) {
71-
tab.addEventListener('click', (event: Event) => {
68+
context.canvasElement
69+
.querySelector('glide-core-tab-group')
70+
?.addEventListener('selected', (event: Event) => {
7271
if (event.target instanceof GlideCoreTab) {
73-
for (const tab of tabs) {
74-
addons.getChannel().emit(UPDATE_STORY_ARGS, {
75-
storyId: context.id,
76-
updatedArgs: {
77-
[`<glide-core-tab>.${tab.panel}.selected`]:
78-
tab === event.target,
79-
},
80-
});
81-
}
72+
addons.getChannel().emit(UPDATE_STORY_ARGS, {
73+
storyId: context.id,
74+
updatedArgs: {
75+
[`<glide-core-tab>.${event.target.panel}.selected`]: true,
76+
},
77+
});
8278
}
8379
});
84-
}
8580
},
8681
render(arguments_) {
8782
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment */
@@ -132,6 +127,24 @@ const meta: Meta = {
132127
})}
133128
>
134129
${unsafeHTML(arguments_['<glide-core-tab-panel>[slot="default"]'])}
130+
131+
<glide-core-tab-group>
132+
<glide-core-tab slot="nav" panel="1"> test </glide-core-tab>
133+
134+
<glide-core-tab slot="nav" panel="2">
135+
With Icon test
136+
137+
<glide-core-example-icon
138+
slot="icon"
139+
name="checkmark"
140+
></glide-core-example-icon>
141+
</glide-core-tab>
142+
143+
<glide-core-tab-panel name="1"> </glide-core-tab-panel>
144+
<glide-core-tab-panel name="2">
145+
With Icon test
146+
</glide-core-tab-panel>
147+
</glide-core-tab-group>
135148
</glide-core-tab-panel>
136149
<glide-core-tab-panel name="2"> With Icon </glide-core-tab-panel>
137150
</glide-core-tab-group>

src/tab.group.test.interactions.ts

+50
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,53 @@ it('sets the selected tab as tabbable on tab blur', async () => {
355355
expect(tabs[1]?.selected).to.be.false;
356356
expect(tabs[1]?.tabIndex).to.equal(-1);
357357
});
358+
359+
it('can be nested', async () => {
360+
const host = await fixture<GlideCoreTabGroup>(html`
361+
<glide-core-tab-group>
362+
<glide-core-tab panel="1" slot="nav">One</glide-core-tab>
363+
<glide-core-tab panel="2" slot="nav">Two</glide-core-tab>
364+
365+
<glide-core-tab-panel name="1">One</glide-core-tab-panel>
366+
367+
<glide-core-tab-panel name="2">
368+
<glide-core-tab-group>
369+
<glide-core-tab panel="1" slot="nav">One</glide-core-tab>
370+
<glide-core-tab panel="2" slot="nav">Two</glide-core-tab>
371+
372+
<glide-core-tab-panel name="1">One</glide-core-tab-panel>
373+
<glide-core-tab-panel name="2">Two</glide-core-tab-panel>
374+
</glide-core-tab-group>
375+
</glide-core-tab-panel>
376+
</glide-core-tab-group>
377+
`);
378+
379+
const tabs = host.querySelectorAll('glide-core-tab');
380+
const panels = host.querySelectorAll('glide-core-tab-panel');
381+
382+
tabs[1]?.click();
383+
await host.updateComplete;
384+
385+
expect(tabs[0]?.selected).to.be.false;
386+
expect(tabs[1]?.selected).to.be.true;
387+
expect(tabs[2]?.selected).to.be.true;
388+
expect(tabs[3]?.selected).to.be.false;
389+
390+
expect(panels[0]?.ariaHidden).to.equal('true');
391+
expect(panels[1]?.ariaHidden).to.equal('false');
392+
expect(panels[2]?.ariaHidden).to.equal('false');
393+
expect(panels[3]?.ariaHidden).to.equal('true');
394+
395+
tabs[0]?.click();
396+
await host.updateComplete;
397+
398+
expect(tabs[0]?.selected).to.be.true;
399+
expect(tabs[1]?.selected).to.be.false;
400+
expect(tabs[2]?.selected).to.be.true;
401+
expect(tabs[3]?.selected).to.be.false;
402+
403+
expect(panels[0]?.ariaHidden).to.equal('false');
404+
expect(panels[1]?.ariaHidden).to.equal('true');
405+
expect(panels[2]?.ariaHidden).to.equal('false');
406+
expect(panels[3]?.ariaHidden).to.equal('true');
407+
});

0 commit comments

Comments
 (0)