srcdev-nuxt-components 9.5.7 → 9.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/component-compliance-checklist.md +8 -2
- package/.claude/skills/component-dynamic-slots.md +3 -3
- package/.claude/skills/component-export-types.md +10 -16
- package/.claude/skills/component-naming.md +1 -1
- package/.claude/skills/components/alert-content.md +9 -1
- package/.claude/skills/components/breadcrumb.md +5 -0
- package/.claude/skills/components/container-glow.md +21 -8
- package/.claude/skills/components/display-avatar.md +13 -7
- package/.claude/skills/components/display-chip.md +37 -24
- package/.claude/skills/components/display-pill.md +58 -8
- package/.claude/skills/components/display-prompt.md +8 -5
- package/.claude/skills/components/display-toast.md +21 -4
- package/.claude/skills/components/glowing-border.md +20 -2
- package/.claude/skills/components/input-button.md +3 -0
- package/.claude/skills/components/responsive-header.md +59 -0
- package/.claude/skills/components/select-menu.md +54 -5
- package/.claude/skills/components/service-detail.md +32 -0
- package/.claude/skills/components/service-summary.md +8 -0
- package/.claude/skills/components/services-card-grid.md +5 -3
- package/.claude/skills/components/services-card.md +22 -12
- package/.claude/skills/components/site-header.md +2 -2
- package/.claude/skills/components/slider-gallery.md +10 -4
- package/.claude/skills/components/tabbed-content.md +300 -0
- package/.claude/skills/composable-anchor-scroll.md +16 -15
- package/.claude/skills/index.md +4 -5
- package/.claude/skills/storybook-add-story.md +53 -0
- package/.claude/skills/theming-status-tokens.md +60 -0
- package/.vscode/srcdev-component-display-avatar.code-snippets +2 -1
- package/.vscode/srcdev-component-display-chip.code-snippets +3 -2
- package/.vscode/srcdev-component-display-pill.code-snippets +10 -0
- package/.vscode/srcdev-component-responsive-header.code-snippets +16 -0
- package/.vscode/srcdev-component-select-menu.code-snippets +28 -0
- package/.vscode/srcdev-component-service-detail.code-snippets +1 -0
- package/.vscode/srcdev-component-service-summary.code-snippets +3 -1
- package/.vscode/srcdev-component-services-card.code-snippets +38 -36
- package/.vscode/srcdev-component-services-grids.code-snippets +2 -1
- package/.vscode/{srcdev-component-tabs-core.code-snippets → srcdev-component-tabbed-content.code-snippets} +35 -16
- package/app/assets/styles/setup/03.theming/_status.css +24 -0
- package/app/assets/styles/setup/03.theming/index.css +1 -0
- package/app/components/01.atoms/animations/container-glow/CONSUMER-STYLING.md +39 -35
- package/app/components/01.atoms/animations/container-glow/ContainerGlow.vue +25 -15
- package/app/components/01.atoms/animations/container-glow/stories/ContainerGlow.stories.ts +130 -24
- package/app/components/01.atoms/animations/container-glow/tests/ContainerGlow.spec.ts +45 -0
- package/app/components/01.atoms/animations/container-glow/tests/__snapshots__/ContainerGlow.spec.ts.snap +2 -2
- package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +76 -19
- package/app/components/01.atoms/animations/glowing-border/GlowingBorder.vue +12 -10
- package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +201 -42
- package/app/components/01.atoms/animations/glowing-border/tests/GlowingBorder.spec.ts +15 -4
- package/app/components/01.atoms/animations/glowing-border/tests/__snapshots__/GlowingBorder.spec.ts.snap +1 -1
- package/app/components/01.atoms/display-pill/CONSUMER-STYLING.md +78 -9
- package/app/components/01.atoms/display-pill/DisplayPill.vue +59 -23
- package/app/components/01.atoms/display-pill/stories/DisplayPill.stories.ts +240 -16
- package/app/components/01.atoms/display-pill/tests/DisplayPill.spec.ts +43 -14
- package/app/components/01.atoms/display-pill/tests/__snapshots__/DisplayPill.spec.ts.snap +3 -3
- package/app/components/01.atoms/navigation/breadcrumb/Breadcrumb.vue +16 -4
- package/app/components/01.atoms/navigation/breadcrumb/CONSUMER-STYLING.md +28 -5
- package/app/components/01.atoms/navigation/breadcrumb/stories/Breadcrumb.stories.ts +94 -0
- package/app/components/01.atoms/navigation/breadcrumb/tests/Breadcrumb.spec.ts +27 -0
- package/app/components/01.atoms/tabbed-content/CONSUMER-STYLING.md +201 -0
- package/app/components/01.atoms/tabbed-content/TabbedContent.vue +750 -0
- package/app/components/01.atoms/tabbed-content/stories/TabbedContent.stories.ts +279 -0
- package/app/components/01.atoms/tabbed-content/tests/TabbedContent.spec.ts +816 -0
- package/app/components/02.molecules/alert-content/AlertContent.vue +11 -2
- package/app/components/02.molecules/alert-content/AlertContentInner.vue +19 -0
- package/app/components/02.molecules/alert-content/CONSUMER-STYLING.md +17 -4
- package/app/components/02.molecules/alert-content/stories/AlertContent.stories.ts +200 -17
- package/app/components/02.molecules/alert-content/tests/AlertContent.spec.ts +33 -0
- package/app/components/02.molecules/display-avatar/CONSUMER-STYLING.md +7 -5
- package/app/components/02.molecules/display-avatar/DisplayAvatar.vue +6 -2
- package/app/components/02.molecules/display-avatar/stories/DisplayAvatar.stories.ts +24 -3
- package/app/components/02.molecules/display-avatar/tests/DisplayAvatar.spec.ts +19 -0
- package/app/components/02.molecules/display-avatar/tests/__snapshots__/DisplayAvatar.spec.ts.snap +2 -1
- package/app/components/02.molecules/display-chip/CONSUMER-STYLING.md +52 -19
- package/app/components/02.molecules/display-chip/DisplayChip.vue +72 -54
- package/app/components/02.molecules/display-chip/stories/DisplayChip.stories.ts +103 -6
- package/app/components/02.molecules/display-chip/tests/DisplayChip.spec.ts +98 -29
- package/app/components/02.molecules/display-chip/tests/__snapshots__/DisplayChip.spec.ts.snap +8 -4
- package/app/components/02.molecules/prompt/CONSUMER-STYLING.md +9 -1
- package/app/components/02.molecules/prompt/DisplayPrompt.vue +2 -2
- package/app/components/02.molecules/prompt/stories/DisplayPrompt.stories.ts +208 -71
- package/app/components/02.molecules/prompt/tests/DisplayPrompt.spec.ts +19 -6
- package/app/components/02.molecules/select-menu/CONSUMER-STYLING.md +66 -8
- package/app/components/02.molecules/select-menu/SelectMenu.vue +141 -18
- package/app/components/02.molecules/select-menu/stories/SelectMenu.stories.ts +232 -2
- package/app/components/02.molecules/select-menu/tests/SelectMenu.spec.ts +151 -0
- package/app/components/02.molecules/select-menu/tests/__snapshots__/SelectMenu.spec.ts.snap +6 -2
- package/app/components/02.molecules/toast/CONSUMER-STYLING.md +91 -27
- package/app/components/02.molecules/toast/DisplayToast.vue +109 -50
- package/app/components/02.molecules/toast/DisplayToastProvider.vue +109 -45
- package/app/components/02.molecules/toast/stories/DisplayToast.stories.ts +23 -0
- package/app/components/02.molecules/toast/stories/DisplayToastProvider.stories.ts +73 -0
- package/app/components/02.molecules/toast/tests/DisplayToast.spec.ts +132 -18
- package/app/components/02.molecules/toast/tests/DisplayToastProvider.spec.ts +105 -39
- package/app/components/03.organisms/image-galleries/slider-gallery/CONSUMER-STYLING.md +19 -1
- package/app/components/03.organisms/image-galleries/slider-gallery/SliderGallery.vue +65 -7
- package/app/components/03.organisms/image-galleries/slider-gallery/stories/SliderGallery.stories.ts +201 -63
- package/app/components/03.organisms/image-galleries/slider-gallery/tests/SliderGallery.spec.ts +41 -2
- package/app/components/03.organisms/image-galleries/slider-gallery/tests/__snapshots__/SliderGallery.spec.ts.snap +6 -6
- package/app/components/03.organisms/responsive-header/CONSUMER-STYLING.md +42 -11
- package/app/components/03.organisms/responsive-header/NavigationItems.vue +60 -16
- package/app/components/03.organisms/responsive-header/ResponsiveHeader.vue +86 -24
- package/app/components/03.organisms/responsive-header/stories/ResponsiveHeader.stories.ts +52 -0
- package/app/components/03.organisms/responsive-header/tests/NavigationItems.spec.ts +20 -0
- package/app/components/03.organisms/responsive-header/tests/ResponsiveHeader.spec.ts +40 -0
- package/app/components/03.organisms/services/service-detail/CONSUMER-STYLING.md +92 -41
- package/app/components/03.organisms/services/service-detail/ServiceDetail.vue +198 -73
- package/app/components/03.organisms/services/service-detail/stories/ServiceDetail.stories.ts +157 -9
- package/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +58 -0
- package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +5 -4
- package/app/components/03.organisms/services/service-summary/CONSUMER-STYLING.md +30 -0
- package/app/components/03.organisms/services/service-summary/ServiceSummary.vue +32 -8
- package/app/components/03.organisms/services/service-summary/stories/ServiceSummary.stories.ts +147 -0
- package/app/components/03.organisms/services/service-summary/tests/ServiceSummary.spec.ts +26 -0
- package/app/components/03.organisms/services/service-summary/tests/__snapshots__/ServiceSummary.spec.ts.snap +2 -2
- package/app/components/03.organisms/services/services-card/CONSUMER-STYLING.md +104 -63
- package/app/components/03.organisms/services/services-card/ServicesCard.vue +92 -66
- package/app/components/03.organisms/services/services-card/stories/ServicesCard.stories.ts +194 -27
- package/app/components/03.organisms/services/services-card/tests/ServicesCard.spec.ts +34 -8
- package/app/components/03.organisms/services/services-card/tests/__snapshots__/ServicesCard.spec.ts.snap +8 -8
- package/app/components/03.organisms/services/services-grids/CONSUMER-STYLING.md +9 -0
- package/app/components/03.organisms/services/services-grids/ServicesCardGrid.vue +2 -1
- package/app/components/03.organisms/services/services-grids/stories/ServiceSummaryGrid.stories.ts +46 -6
- package/app/components/03.organisms/services/services-grids/stories/ServicesCardGrid.stories.ts +89 -11
- package/app/components/03.organisms/services/services-grids/tests/ServicesCardGrid.spec.ts +8 -0
- package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServiceSummaryGrid.spec.ts.snap +6 -6
- package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesCardGrid.spec.ts.snap +24 -24
- package/app/components/05.forms/input-button/CONSUMER-STYLING.md +8 -0
- package/app/components/05.forms/input-button/InputButton.vue +3 -1
- package/app/composables/tests/useAnchorScroll.spec.ts +8 -0
- package/app/composables/useAnchorScroll.ts +1 -1
- package/app/composables/useTabs.ts +148 -206
- package/app/types/components/display-chip.d.ts +8 -4
- package/app/types/components/display-toast.d.ts +2 -0
- package/app/types/components/index.ts +0 -1
- package/app/types/components/select-menu.d.ts +2 -0
- package/app/utils/pausableTimeout.ts +41 -0
- package/nuxt.config.ts +1 -1
- package/package.json +1 -1
- package/.claude/skills/components/site-navigation.md +0 -145
- package/.claude/skills/components/tab-navigation.md +0 -272
- package/.claude/skills/components/tabs-core.md +0 -187
- package/.vscode/srcdev-component-site-navigation.code-snippets +0 -38
- package/.vscode/srcdev-component-tab-navigation.code-snippets +0 -30
- package/app/components/01.atoms/navigation/tabs/CONSUMER-STYLING.md +0 -120
- package/app/components/01.atoms/navigation/tabs/TabsCore.vue +0 -272
- package/app/components/01.atoms/navigation/tabs/stories/TabsCore.stories.ts +0 -199
- package/app/components/01.atoms/navigation/tabs/tests/TabsCore.spec.ts +0 -274
- package/app/components/02.molecules/navigation/site-navigation/CONSUMER-STYLING.md +0 -109
- package/app/components/02.molecules/navigation/site-navigation/SiteNavigation.vue +0 -751
- package/app/components/02.molecules/navigation/site-navigation/stories/SiteNavigation.stories.ts +0 -356
- package/app/components/02.molecules/navigation/site-navigation/tests/SiteNavigation.spec.ts +0 -367
- package/app/components/02.molecules/navigation/site-navigation/tests/__snapshots__/SiteNavigation.spec.ts.snap +0 -30
- package/app/components/02.molecules/navigation/tab-navigation/CONSUMER-STYLING.md +0 -150
- package/app/components/02.molecules/navigation/tab-navigation/TabNavigation.vue +0 -518
- package/app/components/02.molecules/navigation/tab-navigation/stories/TabNavigation.stories.ts +0 -340
- package/app/components/02.molecules/navigation/tab-navigation/tests/TabNavigation.spec.ts +0 -339
- package/app/components/02.molecules/navigation/tab-navigation/tests/__snapshots__/TabNavigation.spec.ts.snap +0 -33
- package/app/composables/useNavCollapse.ts +0 -86
- package/app/types/components/nav-item.d.ts +0 -11
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
import TabbedContent from "../TabbedContent.vue";
|
|
3
|
+
import CanvasSwitcher from "../../canvas-switcher/CanvasSwitcher.vue";
|
|
4
|
+
import type { Meta, StoryObj } from "@nuxtjs/storybook";
|
|
5
|
+
import type { MediaCanvas } from "~/types/components";
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof TabbedContent> = {
|
|
8
|
+
title: "Atoms/TabbedContent",
|
|
9
|
+
component: TabbedContent,
|
|
10
|
+
argTypes: {
|
|
11
|
+
axis: {
|
|
12
|
+
control: "radio",
|
|
13
|
+
options: ["x", "y"],
|
|
14
|
+
description: "Layout axis — horizontal tab row or vertical tab column",
|
|
15
|
+
},
|
|
16
|
+
itemCount: {
|
|
17
|
+
control: "inline-radio",
|
|
18
|
+
options: [1, 2, 3, 4, 5, 6],
|
|
19
|
+
description: "Number of tabs — drives the tab-{n}-trigger / tab-{n}-content indexed slots",
|
|
20
|
+
},
|
|
21
|
+
transitionDuration: {
|
|
22
|
+
control: { type: "range", min: 0, max: 600, step: 10 },
|
|
23
|
+
description: "Duration (ms) of the moving indicator's transition",
|
|
24
|
+
},
|
|
25
|
+
trackHover: {
|
|
26
|
+
control: "boolean",
|
|
27
|
+
description: "Shows a moving highlight behind the hovered tab",
|
|
28
|
+
},
|
|
29
|
+
trackActive: {
|
|
30
|
+
control: "boolean",
|
|
31
|
+
description: "Shows a moving highlight behind the active tab",
|
|
32
|
+
},
|
|
33
|
+
trackIndicator: {
|
|
34
|
+
control: "boolean",
|
|
35
|
+
description: "Shows a moving underline/sideline indicator beneath the active tab",
|
|
36
|
+
},
|
|
37
|
+
overflowMode: {
|
|
38
|
+
control: "radio",
|
|
39
|
+
options: ["menu", "scroll"],
|
|
40
|
+
description: "axis x only: tabs that don't fit move into a More menu, or the tab row scrolls sideways",
|
|
41
|
+
},
|
|
42
|
+
moreLabel: {
|
|
43
|
+
control: "text",
|
|
44
|
+
description: "Accessible name of the More button and its menu (override for localisation)",
|
|
45
|
+
},
|
|
46
|
+
ariaLabel: {
|
|
47
|
+
control: "text",
|
|
48
|
+
description: "aria-label on the tablist — override for localisation",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
args: {
|
|
52
|
+
axis: "x",
|
|
53
|
+
itemCount: 3,
|
|
54
|
+
transitionDuration: 200,
|
|
55
|
+
trackHover: true,
|
|
56
|
+
trackActive: true,
|
|
57
|
+
trackIndicator: true,
|
|
58
|
+
ariaLabel: "Tabs",
|
|
59
|
+
overflowMode: "menu",
|
|
60
|
+
moreLabel: "More tabs",
|
|
61
|
+
},
|
|
62
|
+
decorators: [
|
|
63
|
+
(story, context) => ({
|
|
64
|
+
components: { story, CanvasSwitcher },
|
|
65
|
+
setup() {
|
|
66
|
+
const canvasName = ref<MediaCanvas>(context.parameters.initialCanvas ?? "fullWidthCanvas");
|
|
67
|
+
return { canvasName };
|
|
68
|
+
},
|
|
69
|
+
template: `
|
|
70
|
+
<div style="padding: 1.2rem 1.6rem; border-block-end: 1px solid currentColor;">
|
|
71
|
+
<CanvasSwitcher v-model:canvas-name="canvasName" />
|
|
72
|
+
</div>
|
|
73
|
+
<div :class="canvasName" style="margin-inline: auto; padding: 2rem; outline: 1px dashed currentColor;">
|
|
74
|
+
<story />
|
|
75
|
+
</div>
|
|
76
|
+
`,
|
|
77
|
+
}),
|
|
78
|
+
],
|
|
79
|
+
parameters: {
|
|
80
|
+
layout: "fullscreen",
|
|
81
|
+
docs: {
|
|
82
|
+
description: {
|
|
83
|
+
component:
|
|
84
|
+
"Tabbed content: a tablist and its panels, built from indexed slots (tab-{n}-trigger / tab-{n}-content, driven by itemCount) with moving hover/active/underline indicators. Supports arrow-key navigation (Left/Right or Up/Down depending on axis, plus Home/End) per the WAI-ARIA Tabs pattern.",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export default meta;
|
|
91
|
+
type Story = StoryObj<typeof TabbedContent>;
|
|
92
|
+
|
|
93
|
+
// ─── Fixtures ─────────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
// Slot content for every tab the itemCount control allows (max 6), so raising itemCount never
|
|
96
|
+
// shows empty tabs. Each story only renders as many as its itemCount asks for.
|
|
97
|
+
const panels = [
|
|
98
|
+
{ label: "Overview", content: "A short summary of the product goes here." },
|
|
99
|
+
{ label: "Specs", content: "Technical specifications and dimensions." },
|
|
100
|
+
{ label: "Reviews", content: "Customer reviews and ratings." },
|
|
101
|
+
{ label: "Shipping", content: "Delivery estimates and carrier options." },
|
|
102
|
+
{ label: "Returns", content: "Return window and refund policy." },
|
|
103
|
+
{ label: "FAQ", content: "Answers to common questions." },
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const tabSlots = panels
|
|
107
|
+
.map(
|
|
108
|
+
(panel, index) => `
|
|
109
|
+
<template #tab-${index}-trigger>${panel.label}</template>
|
|
110
|
+
<template #tab-${index}-content>
|
|
111
|
+
<div style="padding: 1.6rem;">${panel.content}</div>
|
|
112
|
+
</template>`
|
|
113
|
+
)
|
|
114
|
+
.join("\n");
|
|
115
|
+
|
|
116
|
+
// ─── Stories ──────────────────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
export const Horizontal: Story = {
|
|
119
|
+
args: { itemCount: 3 },
|
|
120
|
+
render: (args) => ({
|
|
121
|
+
components: { TabbedContent },
|
|
122
|
+
setup() {
|
|
123
|
+
return { args };
|
|
124
|
+
},
|
|
125
|
+
template: `
|
|
126
|
+
<TabbedContent v-bind="args">
|
|
127
|
+
${tabSlots}
|
|
128
|
+
</TabbedContent>
|
|
129
|
+
`,
|
|
130
|
+
}),
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const Vertical: Story = {
|
|
134
|
+
args: { itemCount: 3, axis: "y" },
|
|
135
|
+
render: (args) => ({
|
|
136
|
+
components: { TabbedContent },
|
|
137
|
+
setup() {
|
|
138
|
+
return { args };
|
|
139
|
+
},
|
|
140
|
+
template: `
|
|
141
|
+
<TabbedContent v-bind="args">
|
|
142
|
+
${tabSlots}
|
|
143
|
+
</TabbedContent>
|
|
144
|
+
`,
|
|
145
|
+
}),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const IndicatorsOff: Story = {
|
|
149
|
+
name: "Indicators Off",
|
|
150
|
+
args: { itemCount: 3, trackHover: false, trackActive: false, trackIndicator: false },
|
|
151
|
+
render: (args) => ({
|
|
152
|
+
components: { TabbedContent },
|
|
153
|
+
setup() {
|
|
154
|
+
return { args };
|
|
155
|
+
},
|
|
156
|
+
template: `
|
|
157
|
+
<TabbedContent v-bind="args">
|
|
158
|
+
${tabSlots}
|
|
159
|
+
</TabbedContent>
|
|
160
|
+
`,
|
|
161
|
+
}),
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const FiveTabs: Story = {
|
|
165
|
+
name: "Five Tabs (regression check)",
|
|
166
|
+
args: { itemCount: 5 },
|
|
167
|
+
parameters: {
|
|
168
|
+
docs: {
|
|
169
|
+
description: {
|
|
170
|
+
story:
|
|
171
|
+
"itemCount raised to 5 as a regression check — the original hand-rolled version of this component had an indicator-positioning issue that only showed up with more than 3 tabs. Try hovering quickly across several tabs in a row, then clicking a tab several positions away from the active one (e.g. tab 1 → tab 5), to confirm the hover/active/underline indicators land in the right place without jumping or lagging behind.",
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
render: (args) => ({
|
|
176
|
+
components: { TabbedContent },
|
|
177
|
+
setup() {
|
|
178
|
+
return { args };
|
|
179
|
+
},
|
|
180
|
+
template: `
|
|
181
|
+
<TabbedContent v-bind="args">
|
|
182
|
+
${tabSlots}
|
|
183
|
+
</TabbedContent>
|
|
184
|
+
`,
|
|
185
|
+
}),
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export const IndicatorColours: Story = {
|
|
189
|
+
name: "Indicator Colours (demo)",
|
|
190
|
+
args: { itemCount: 3 },
|
|
191
|
+
parameters: {
|
|
192
|
+
docs: {
|
|
193
|
+
description: {
|
|
194
|
+
story:
|
|
195
|
+
"Each moving indicator given a distinct colour via its public CSS token, purely to make the three separate states (hover / active / underline) easy to tell apart. Hover a tab to see the green highlight, with a thin orange hover underline beneath it, move independently of the blue active highlight and its thick orange underline — the label text stays white against both backgrounds via the matching --tabs-hover-indicator-text-colour / --tabs-active-indicator-text-colour tokens (see CONSUMER-STYLING.md), which previously had no effect since they were wired to the wrong element.",
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
render: (args) => ({
|
|
200
|
+
components: { TabbedContent },
|
|
201
|
+
setup() {
|
|
202
|
+
const indicatorColourVars = {
|
|
203
|
+
"--tabs-hover-indicator-colour": "seagreen",
|
|
204
|
+
"--tabs-hover-indicator-text-colour": "white",
|
|
205
|
+
"--tabs-active-indicator-colour": "steelblue",
|
|
206
|
+
"--tabs-active-indicator-text-colour": "white",
|
|
207
|
+
"--tabs-underline-indicator-colour": "darkorange",
|
|
208
|
+
"--tabs-hover-underline-indicator-height": "0.2rem",
|
|
209
|
+
};
|
|
210
|
+
return { args, indicatorColourVars };
|
|
211
|
+
},
|
|
212
|
+
template: `
|
|
213
|
+
<div :style="indicatorColourVars">
|
|
214
|
+
<TabbedContent v-bind="args">
|
|
215
|
+
${tabSlots}
|
|
216
|
+
</TabbedContent>
|
|
217
|
+
</div>
|
|
218
|
+
`,
|
|
219
|
+
}),
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// ─── Stress test ──────────────────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
const stressTabs = [
|
|
225
|
+
{
|
|
226
|
+
label: "Datenschutzgrundverordnungskonformitätsbescheinigung",
|
|
227
|
+
content:
|
|
228
|
+
"Lange deutsche Überschrift ohne Leerzeichen, und ein langer Absatz: Die Verarbeitung personenbezogener Daten erfolgt ausschließlich im Rahmen der gesetzlichen Bestimmungen und wird regelmäßig überprüft.",
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
label: "https://example.com/a/very/long/unbroken/url/that/never/wraps/on/its/own",
|
|
232
|
+
content: "https://example.com/a/very/long/unbroken/url/that/never/wraps/on/its/own/and/keeps/going/and/going",
|
|
233
|
+
},
|
|
234
|
+
{ label: "🎉 Party", content: "Emoji at the start of the label." },
|
|
235
|
+
{ label: "<b>not bold</b>", content: "<script>alert('x')</script> must render as text." },
|
|
236
|
+
{ label: "المراجعات", content: "نص عربي من اليمين إلى اليسار في لوحة التبويب." },
|
|
237
|
+
{ label: "X", content: "Single-character label." },
|
|
238
|
+
{ label: "", content: "Empty trigger label: the tab still takes keyboard focus and is announced by its position." },
|
|
239
|
+
{ label: "Icon inside", icon: "lucide:star", content: "Clicking the icon inside the trigger must still switch tabs." },
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
export const StressTest: Story = {
|
|
243
|
+
name: "Stress Test (Worst-Case Data)",
|
|
244
|
+
args: { itemCount: stressTabs.length, ariaLabel: "Produktinformationen und rechtliche Hinweise" },
|
|
245
|
+
argTypes: {
|
|
246
|
+
itemCount: { control: "inline-radio", options: [0, 1, 2, 3, 4, 5, 6, 7, 8] },
|
|
247
|
+
},
|
|
248
|
+
parameters: {
|
|
249
|
+
initialCanvas: "mobileCanvas",
|
|
250
|
+
docs: {
|
|
251
|
+
description: {
|
|
252
|
+
story:
|
|
253
|
+
"Deliberately hostile data to find breakage: eight tabs, a long German compound label, an unbroken URL label " +
|
|
254
|
+
"and panel, emoji, HTML-like text (must render as text), right-to-left Arabic, a single character, an empty " +
|
|
255
|
+
"label, and an icon inside a trigger (clicking the icon must still switch tabs). Try itemCount below 8 " +
|
|
256
|
+
"(including 0) and switch axis to y. Check at every canvas width: the page never scrolls sideways (tabs that don't fit move into the More " +
|
|
257
|
+
"menu, or with overflowMode scroll the tab row scrolls inside itself), long labels wrap inside a tab or " +
|
|
258
|
+
"menu item rather than stretching it past --tabs-list-item-max-inline-size, picking a tab from the menu " +
|
|
259
|
+
"moves the indicators onto the More button, and panel text wraps inside the panel.",
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
render: (args) => ({
|
|
264
|
+
components: { TabbedContent },
|
|
265
|
+
setup() {
|
|
266
|
+
return { args, stressTabs };
|
|
267
|
+
},
|
|
268
|
+
template: `
|
|
269
|
+
<TabbedContent v-bind="args">
|
|
270
|
+
<template v-for="(tab, index) in stressTabs" :key="index" #[\`tab-\${index}-trigger\`]>
|
|
271
|
+
<Icon v-if="tab.icon" :name="tab.icon" style="vertical-align: middle; margin-inline-end: 0.6rem;" />{{ tab.label }}
|
|
272
|
+
</template>
|
|
273
|
+
<template v-for="(tab, index) in stressTabs" :key="'c' + index" #[\`tab-\${index}-content\`]>
|
|
274
|
+
<div style="padding: 1.6rem;">{{ tab.content }}</div>
|
|
275
|
+
</template>
|
|
276
|
+
</TabbedContent>
|
|
277
|
+
`,
|
|
278
|
+
}),
|
|
279
|
+
};
|