srcdev-nuxt-components 9.6.3 → 9.6.5
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/rules/consumer/vue-no-self-closing.md +3 -2
- package/.claude/skills/component-compliance-checklist.md +17 -3
- package/.claude/skills/component-inline-action-button.md +33 -2
- package/.claude/skills/components/cookie-consent-banner.md +9 -6
- package/.claude/skills/components/input-copy.md +47 -100
- package/.claude/skills/components/progress-tracker.md +140 -0
- package/.claude/skills/components/stepper-list.md +10 -3
- package/.claude/skills/components/tabbed-content.md +4 -0
- package/.claude/skills/composable-cookie-consent.md +1 -1
- package/.claude/skills/index.md +4 -3
- package/.claude/skills/storybook-add-story.md +14 -0
- package/.claude/skills/testing-add-unit-test.md +19 -0
- package/.claude/skills/theming-component-token-pattern.md +1 -1
- package/.vscode/srcdev-component-cookie-consent-banner.code-snippets +7 -5
- package/.vscode/srcdev-component-input-copy.code-snippets +24 -42
- package/.vscode/srcdev-component-progress-tracker.code-snippets +45 -0
- package/.vscode/srcdev-component-stepper-list.code-snippets +3 -2
- package/.vscode/srcdev-component-tabbed-content.code-snippets +1 -0
- package/app/components/01.atoms/tabbed-content/CONSUMER-STYLING.md +5 -1
- package/app/components/01.atoms/tabbed-content/TabbedContent.vue +2 -2
- package/app/components/01.atoms/tabbed-content/stories/TabbedContent.stories.ts +26 -0
- package/app/components/02.molecules/contact-section/tests/__snapshots__/ContactSection.spec.ts.snap +28 -28
- package/app/components/02.molecules/cookie-consent-banner/CONSUMER-STYLING.md +13 -4
- package/app/components/02.molecules/cookie-consent-banner/CookieConsentBanner.vue +23 -14
- package/app/components/02.molecules/cookie-consent-banner/stories/CookieConsentBanner.stories.ts +106 -5
- package/app/components/02.molecules/cookie-consent-banner/tests/CookieConsentBanner.spec.ts +33 -11
- package/app/components/02.molecules/input-copy/CONSUMER-STYLING.md +82 -124
- package/app/components/02.molecules/input-copy/InputCopy.vue +110 -85
- package/app/components/02.molecules/input-copy/stories/InputCopy.stories.ts +204 -47
- package/app/components/02.molecules/input-copy/tests/InputCopy.spec.ts +147 -171
- package/app/components/02.molecules/progress-tracker/CONSUMER-STYLING.md +116 -0
- package/app/components/02.molecules/progress-tracker/ProgressTracker.vue +275 -0
- package/app/components/02.molecules/progress-tracker/stories/ProgressTracker.stories.ts +424 -0
- package/app/components/02.molecules/progress-tracker/tests/ProgressTracker.spec.ts +253 -0
- package/app/components/02.molecules/progress-tracker/tests/__snapshots__/ProgressTracker.spec.ts.snap +46 -0
- package/app/components/02.molecules/stepper-list/CONSUMER-STYLING.md +42 -7
- package/app/components/02.molecules/stepper-list/StepperList.vue +42 -19
- package/app/components/02.molecules/stepper-list/stories/StepperList.stories.ts +288 -2
- package/app/components/02.molecules/stepper-list/tests/StepperList.spec.ts +161 -34
- package/app/components/02.molecules/stepper-list/tests/__snapshots__/StepperList.spec.ts.snap +40 -40
- package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +7 -7
- package/app/types/components/cookie-consent-banner.d.ts +2 -0
- package/app/types/components/index.ts +1 -0
- package/app/types/components/progress-tracker.d.ts +6 -0
- package/package.json +1 -1
|
@@ -1,9 +1,26 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
1
2
|
import StepperList from "../StepperList.vue";
|
|
2
3
|
import type { Meta, StoryObj } from "@nuxtjs/storybook";
|
|
3
4
|
|
|
5
|
+
// Links in the canvas open in the Storybook manager (target="_top"), not inside the preview iframe.
|
|
6
|
+
const storyNoteStyle = "margin: 0 0 1.6rem; font-size: 1.4rem;";
|
|
7
|
+
const parentStoriesNote = `
|
|
8
|
+
<p style="${storyNoteStyle}">
|
|
9
|
+
StepperList in real use: ServiceDetail renders its process steps from CMS data (<a href="/?path=/story/organisms-services-service-detail--default" target="_top">ServiceDetail</a> story), and ProgressTracker adds done, current and upcoming states, a progress header and a focus card (<a href="/?path=/story/molecules-progresstracker--default" target="_top">ProgressTracker</a> story).
|
|
10
|
+
</p>
|
|
11
|
+
`;
|
|
12
|
+
|
|
4
13
|
const meta: Meta<typeof StepperList> = {
|
|
5
14
|
title: "Molecules/Lists/StepperList",
|
|
6
15
|
component: StepperList,
|
|
16
|
+
parameters: {
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component:
|
|
20
|
+
"A list of steps, each with an indicator and a content area. `itemCount` sets how many rows render; fill them with the zero-indexed `#item-{n}` slots. Each row shows an auto-numbered counter bubble (`indicatorVariant`: disc, circle or square; `indicatorSize`) unless an `#indicator-{n}` slot replaces it. `connected` draws a line between indicators. Used by [ServiceDetail](?path=/story/organisms-services-service-detail--default) for its process steps and by [ProgressTracker](?path=/story/molecules-progresstracker--default), which adds step states, a progress header and a focus card.",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
7
24
|
argTypes: {
|
|
8
25
|
tag: {
|
|
9
26
|
control: { type: "select" },
|
|
@@ -30,7 +47,7 @@ const meta: Meta<typeof StepperList> = {
|
|
|
30
47
|
},
|
|
31
48
|
connected: {
|
|
32
49
|
control: "boolean",
|
|
33
|
-
description: "
|
|
50
|
+
description: "Draws a vertical line between indicators, positioned from measured indicator sizes",
|
|
34
51
|
},
|
|
35
52
|
styleClassPassthrough: {
|
|
36
53
|
control: "object",
|
|
@@ -66,6 +83,7 @@ export const Default: Story = {
|
|
|
66
83
|
return { args };
|
|
67
84
|
},
|
|
68
85
|
template: `
|
|
86
|
+
${parentStoriesNote}
|
|
69
87
|
<StepperList v-bind="args">
|
|
70
88
|
<template #item-0><p class="page-body-normal">Plan your project goals and define what success looks like</p></template>
|
|
71
89
|
<template #item-1><p class="page-body-normal">Set up your development environment</p></template>
|
|
@@ -90,6 +108,7 @@ export const OrderedList: Story = {
|
|
|
90
108
|
return { args };
|
|
91
109
|
},
|
|
92
110
|
template: `
|
|
111
|
+
${parentStoriesNote}
|
|
93
112
|
<StepperList v-bind="args">
|
|
94
113
|
<template #item-0><p class="page-body-normal">Preheat the oven to 180°C</p></template>
|
|
95
114
|
<template #item-1><p class="page-body-normal">Sift together the flour, baking powder, and a pinch of salt into a large bowl</p></template>
|
|
@@ -115,6 +134,7 @@ export const WithCustomIndicators: Story = {
|
|
|
115
134
|
return { args };
|
|
116
135
|
},
|
|
117
136
|
template: `
|
|
137
|
+
${parentStoriesNote}
|
|
118
138
|
<StepperList v-bind="args">
|
|
119
139
|
<template #indicator-0>
|
|
120
140
|
<svg class="indicator-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -169,6 +189,7 @@ export const MixedIndicators: Story = {
|
|
|
169
189
|
return { args };
|
|
170
190
|
},
|
|
171
191
|
template: `
|
|
192
|
+
${parentStoriesNote}
|
|
172
193
|
<StepperList v-bind="args">
|
|
173
194
|
<template #indicator-0>
|
|
174
195
|
<svg class="indicator-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -208,6 +229,7 @@ export const AlignmentCenter: Story = {
|
|
|
208
229
|
return { args };
|
|
209
230
|
},
|
|
210
231
|
template: `
|
|
232
|
+
${parentStoriesNote}
|
|
211
233
|
<StepperList v-bind="args">
|
|
212
234
|
<template #item-0><p class="page-body-normal">Connect your data source</p></template>
|
|
213
235
|
<template #item-1><p class="page-body-normal">Configure sync settings</p></template>
|
|
@@ -219,6 +241,62 @@ export const AlignmentCenter: Story = {
|
|
|
219
241
|
}),
|
|
220
242
|
};
|
|
221
243
|
|
|
244
|
+
type ConnectorGapArgs = InstanceType<typeof StepperList>["$props"] & { connectorGap: string };
|
|
245
|
+
|
|
246
|
+
export const ConnectorGap: StoryObj<ConnectorGapArgs> = {
|
|
247
|
+
name: "Connector Gap",
|
|
248
|
+
argTypes: {
|
|
249
|
+
connectorGap: {
|
|
250
|
+
name: "Connector gap",
|
|
251
|
+
control: "select",
|
|
252
|
+
options: ["0", "0.2rem", "0.4rem", "0.6rem", "0.8rem", "1.2rem"],
|
|
253
|
+
description:
|
|
254
|
+
"**Story control, not a prop.** Sets the `--stepper-list-connector-gap` CSS token so you can try it here. " +
|
|
255
|
+
"To use it in an app, set the token in your own CSS, e.g. `.how-it-works { --stepper-list-connector-gap: 0.6rem; }`. " +
|
|
256
|
+
"`0` (the default) makes the line touch both indicators.",
|
|
257
|
+
table: { category: "CSS tokens (story only, set in your CSS)", defaultValue: { summary: "0" } },
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
args: {
|
|
261
|
+
tag: "ol",
|
|
262
|
+
itemCount: 5,
|
|
263
|
+
connected: true,
|
|
264
|
+
indicatorAlignment: "center",
|
|
265
|
+
connectorGap: "0.6rem",
|
|
266
|
+
},
|
|
267
|
+
parameters: {
|
|
268
|
+
docs: {
|
|
269
|
+
description: {
|
|
270
|
+
story:
|
|
271
|
+
"`--stepper-list-connector-gap` leaves a space between each end of the connector line and the indicators it joins. It defaults to `0`, where the line touches both indicators. The Connector gap control sets that CSS token on a wrapper; it isn't a prop.",
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
render: (args) => ({
|
|
276
|
+
components: { StepperList },
|
|
277
|
+
setup() {
|
|
278
|
+
const componentArgs = computed(() => {
|
|
279
|
+
const { connectorGap: _connectorGap, ...rest } = args;
|
|
280
|
+
return rest;
|
|
281
|
+
});
|
|
282
|
+
const tokenStyles = computed(() => ({ "--stepper-list-connector-gap": args.connectorGap }));
|
|
283
|
+
return { componentArgs, tokenStyles };
|
|
284
|
+
},
|
|
285
|
+
template: `
|
|
286
|
+
${parentStoriesNote}
|
|
287
|
+
<div :style="tokenStyles">
|
|
288
|
+
<StepperList v-bind="componentArgs">
|
|
289
|
+
<template #item-0><p class="page-body-normal">Connect your data source</p></template>
|
|
290
|
+
<template #item-1><p class="page-body-normal">Configure sync settings</p></template>
|
|
291
|
+
<template #item-2><p class="page-body-normal">Map your fields</p></template>
|
|
292
|
+
<template #item-3><p class="page-body-normal">Run a test import to check everything looks right</p></template>
|
|
293
|
+
<template #item-4><p class="page-body-normal">Publish</p></template>
|
|
294
|
+
</StepperList>
|
|
295
|
+
</div>
|
|
296
|
+
`,
|
|
297
|
+
}),
|
|
298
|
+
};
|
|
299
|
+
|
|
222
300
|
/** Disc variant — filled circle with solid background (default). */
|
|
223
301
|
export const VariantDisc: Story = {
|
|
224
302
|
name: "Variant — Disc",
|
|
@@ -233,6 +311,7 @@ export const VariantDisc: Story = {
|
|
|
233
311
|
return { args };
|
|
234
312
|
},
|
|
235
313
|
template: `
|
|
314
|
+
${parentStoriesNote}
|
|
236
315
|
<StepperList v-bind="args">
|
|
237
316
|
<template #item-0><p class="page-body-normal">Research and define requirements with stakeholders</p></template>
|
|
238
317
|
<template #item-1><p class="page-body-normal">Wireframe key user journeys</p></template>
|
|
@@ -258,6 +337,7 @@ export const VariantCircle: Story = {
|
|
|
258
337
|
return { args };
|
|
259
338
|
},
|
|
260
339
|
template: `
|
|
340
|
+
${parentStoriesNote}
|
|
261
341
|
<StepperList v-bind="args">
|
|
262
342
|
<template #item-0><p class="page-body-normal">Research and define requirements with stakeholders</p></template>
|
|
263
343
|
<template #item-1><p class="page-body-normal">Wireframe key user journeys</p></template>
|
|
@@ -283,6 +363,7 @@ export const VariantSquare: Story = {
|
|
|
283
363
|
return { args };
|
|
284
364
|
},
|
|
285
365
|
template: `
|
|
366
|
+
${parentStoriesNote}
|
|
286
367
|
<StepperList v-bind="args">
|
|
287
368
|
<template #item-0><p class="page-body-normal">Research and define requirements with stakeholders</p></template>
|
|
288
369
|
<template #item-1><p class="page-body-normal">Wireframe key user journeys</p></template>
|
|
@@ -310,6 +391,7 @@ export const CustomIndicatorSize: Story = {
|
|
|
310
391
|
return { args };
|
|
311
392
|
},
|
|
312
393
|
template: `
|
|
394
|
+
${parentStoriesNote}
|
|
313
395
|
<StepperList v-bind="args">
|
|
314
396
|
<template #item-0><p class="page-body-normal">Create your account</p></template>
|
|
315
397
|
<template #item-1><p class="page-body-normal">Verify your email and complete your profile with a photo, bio, and preferred contact details</p></template>
|
|
@@ -333,6 +415,7 @@ export const RichContent: Story = {
|
|
|
333
415
|
return { args };
|
|
334
416
|
},
|
|
335
417
|
template: `
|
|
418
|
+
${parentStoriesNote}
|
|
336
419
|
<StepperList v-bind="args">
|
|
337
420
|
<template #item-0>
|
|
338
421
|
<div>
|
|
@@ -381,6 +464,7 @@ export const SingleItem: Story = {
|
|
|
381
464
|
return { args };
|
|
382
465
|
},
|
|
383
466
|
template: `
|
|
467
|
+
${parentStoriesNote}
|
|
384
468
|
<StepperList v-bind="args">
|
|
385
469
|
<template #item-0><p class="page-body-normal">The only item</p></template>
|
|
386
470
|
</StepperList>
|
|
@@ -398,7 +482,7 @@ export const Empty: Story = {
|
|
|
398
482
|
setup() {
|
|
399
483
|
return { args };
|
|
400
484
|
},
|
|
401
|
-
template:
|
|
485
|
+
template: `${parentStoriesNote}<StepperList v-bind="args" />`,
|
|
402
486
|
}),
|
|
403
487
|
};
|
|
404
488
|
|
|
@@ -414,6 +498,7 @@ export const WithStyleClassPassthrough: Story = {
|
|
|
414
498
|
return { args };
|
|
415
499
|
},
|
|
416
500
|
template: `
|
|
501
|
+
${parentStoriesNote}
|
|
417
502
|
<StepperList v-bind="args">
|
|
418
503
|
<template #item-0><p class="page-body-normal">First item</p></template>
|
|
419
504
|
<template #item-1><p class="page-body-normal">Second item</p></template>
|
|
@@ -421,3 +506,204 @@ export const WithStyleClassPassthrough: Story = {
|
|
|
421
506
|
`,
|
|
422
507
|
}),
|
|
423
508
|
};
|
|
509
|
+
|
|
510
|
+
// ─── Styled demo ──────────────────────────────────────────────────────────────
|
|
511
|
+
|
|
512
|
+
type TimelineState = "done" | "current" | "upcoming";
|
|
513
|
+
|
|
514
|
+
const timelineStateLabels: Record<TimelineState, string> = {
|
|
515
|
+
done: "Done",
|
|
516
|
+
current: "In progress",
|
|
517
|
+
upcoming: "Up next",
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const timelineSteps: { state: TimelineState; title: string; body: string }[] = [
|
|
521
|
+
{ state: "done", title: "Create your account", body: "Enter your email and choose a password to get started." },
|
|
522
|
+
{
|
|
523
|
+
state: "current",
|
|
524
|
+
title: "Verify your email",
|
|
525
|
+
body: "Click the link we sent to your inbox. If you don't see it, check your spam folder: it should arrive within a few minutes.",
|
|
526
|
+
},
|
|
527
|
+
{ state: "upcoming", title: "Choose a plan", body: "Select from Starter, Pro, or Enterprise depending on your needs." },
|
|
528
|
+
{ state: "upcoming", title: "Invite your team", body: "Add colleagues by email so they can collaborate on your workspace." },
|
|
529
|
+
{ state: "upcoming", title: "Start building", body: "You're all set. Dive into the dashboard and start your first project." },
|
|
530
|
+
];
|
|
531
|
+
|
|
532
|
+
const timelineCss = `
|
|
533
|
+
.stepper-timeline-demo {
|
|
534
|
+
background: #16171b;
|
|
535
|
+
color: #e8e9ec;
|
|
536
|
+
padding: 4rem 3.2rem;
|
|
537
|
+
border-radius: 1.2rem;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.stepper-list.stepper-timeline {
|
|
541
|
+
--stepper-list-gap: 3.2rem;
|
|
542
|
+
--stepper-list-padding-block: 2rem;
|
|
543
|
+
--stepper-list-connector-width: 0.2rem;
|
|
544
|
+
--stepper-list-connector-color: transparent;
|
|
545
|
+
--stepper-list-connector-image: repeating-linear-gradient(to bottom, #3a3d45 0 0.2rem, transparent 0.2rem 0.6rem);
|
|
546
|
+
margin: 0;
|
|
547
|
+
|
|
548
|
+
.stepper-list-item:has(.stepper-timeline-marker.is-done) {
|
|
549
|
+
--stepper-list-connector-image: repeating-linear-gradient(to bottom, #00c8d7 0 0.4rem, transparent 0.4rem 0.7rem);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.stepper-timeline-marker {
|
|
553
|
+
display: grid;
|
|
554
|
+
place-items: center;
|
|
555
|
+
inline-size: 3.2rem;
|
|
556
|
+
block-size: 3.2rem;
|
|
557
|
+
margin-block-start: -0.6rem;
|
|
558
|
+
|
|
559
|
+
&::before {
|
|
560
|
+
content: "";
|
|
561
|
+
inline-size: 2.2rem;
|
|
562
|
+
block-size: 2.2rem;
|
|
563
|
+
rotate: 45deg;
|
|
564
|
+
border: 0.2rem solid #4a4d55;
|
|
565
|
+
background: transparent;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
&.is-done::before {
|
|
569
|
+
border-color: #00c8d7;
|
|
570
|
+
background: #00c8d7;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
&.is-current::before {
|
|
574
|
+
border-color: #00c8d7;
|
|
575
|
+
background: #16171b;
|
|
576
|
+
box-shadow: 0 0 0 0.4rem rgb(0 200 215 / 0.15), 0 0 1.6rem 0.2rem rgb(0 200 215 / 0.45);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.stepper-timeline-eyebrow {
|
|
581
|
+
margin: 0 0 0.6rem;
|
|
582
|
+
font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
|
|
583
|
+
font-size: var(--step-4);
|
|
584
|
+
font-weight: 600;
|
|
585
|
+
letter-spacing: 0.16em;
|
|
586
|
+
text-transform: uppercase;
|
|
587
|
+
color: #8a8d96;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.stepper-timeline-title {
|
|
591
|
+
margin: 0 0 0.8rem;
|
|
592
|
+
font-size: 2.2rem;
|
|
593
|
+
font-weight: 600;
|
|
594
|
+
line-height: 1.2;
|
|
595
|
+
color: #ffffff;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.stepper-timeline-body {
|
|
599
|
+
margin: 0;
|
|
600
|
+
font-size: 1.6rem;
|
|
601
|
+
line-height: 1.5;
|
|
602
|
+
color: #a6a9b2;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.stepper-list-item:has(.is-current) .stepper-timeline-eyebrow {
|
|
606
|
+
color: #00c8d7;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.stepper-list-item:has(.is-upcoming) .stepper-list-content {
|
|
610
|
+
opacity: 0.45;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
`;
|
|
614
|
+
|
|
615
|
+
export const TimelineDemo: Story = {
|
|
616
|
+
name: "Timeline (styled demo)",
|
|
617
|
+
args: {
|
|
618
|
+
tag: "ol",
|
|
619
|
+
itemCount: timelineSteps.length,
|
|
620
|
+
connected: true,
|
|
621
|
+
indicatorAlignment: "top",
|
|
622
|
+
indicatorSize: "3.2rem",
|
|
623
|
+
styleClassPassthrough: ["stepper-timeline"],
|
|
624
|
+
},
|
|
625
|
+
parameters: {
|
|
626
|
+
docs: {
|
|
627
|
+
description: {
|
|
628
|
+
story:
|
|
629
|
+
"A consumer-styled timeline built only from public tokens and slots, no extra props. Diamond markers are rotated squares in the `#indicator-{n}` slots, each with a state class (`is-done`, `is-current`, `is-upcoming`). `.stepper-list-item:has(.is-done)` swaps that item's connector to a cyan dashed line through `--stepper-list-connector-image`; the rest stay grey and dotted. The current step's eyebrow turns cyan and upcoming steps are dimmed. The state is also written in each eyebrow, so it never depends on colour alone. See CONSUMER-STYLING.md, \"Recipe: per-step states and dashed connectors\".",
|
|
630
|
+
},
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
render: (args) => ({
|
|
634
|
+
components: { StepperList },
|
|
635
|
+
setup() {
|
|
636
|
+
return { args, timelineSteps, timelineStateLabels, timelineCss };
|
|
637
|
+
},
|
|
638
|
+
template: `
|
|
639
|
+
${parentStoriesNote}
|
|
640
|
+
<div class="stepper-timeline-demo">
|
|
641
|
+
<component :is="'style'">{{ timelineCss }}</component>
|
|
642
|
+
<StepperList v-bind="args">
|
|
643
|
+
<template v-for="(step, i) in timelineSteps" :key="'indicator-' + i" #[\`indicator-\${i}\`]>
|
|
644
|
+
<span class="stepper-timeline-marker" :class="'is-' + step.state" aria-hidden="true"></span>
|
|
645
|
+
</template>
|
|
646
|
+
<template v-for="(step, i) in timelineSteps" :key="'item-' + i" #[\`item-\${i}\`]>
|
|
647
|
+
<p class="stepper-timeline-eyebrow">Step {{ String(i + 1).padStart(2, "0") }} · {{ timelineStateLabels[step.state] }}</p>
|
|
648
|
+
<h3 class="stepper-timeline-title" :aria-current="step.state === 'current' ? 'step' : undefined">{{ step.title }}</h3>
|
|
649
|
+
<p class="stepper-timeline-body">{{ step.body }}</p>
|
|
650
|
+
</template>
|
|
651
|
+
</StepperList>
|
|
652
|
+
</div>
|
|
653
|
+
`,
|
|
654
|
+
}),
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// ─── Stress test ──────────────────────────────────────────────────────────────
|
|
658
|
+
|
|
659
|
+
const unbroken = "Haarpflegebehandlungsterminvereinbarungsbestätigungsschreiben".repeat(3);
|
|
660
|
+
const longGerman =
|
|
661
|
+
"Bitte vereinbaren Sie vor der ersten Behandlung einen ausführlichen Beratungstermin, damit wir Ihre Haarstruktur, Ihre bisherigen Färbungen und Ihre Wünsche gemeinsam besprechen können.";
|
|
662
|
+
const longUrl = "https://example.com/a/very/long/url/without/any/spaces/that/keeps/going/and/going/and/going";
|
|
663
|
+
|
|
664
|
+
export const StressTest: Story = {
|
|
665
|
+
name: "Stress Test (Worst-Case Data)",
|
|
666
|
+
args: {
|
|
667
|
+
tag: "ol",
|
|
668
|
+
itemCount: 12,
|
|
669
|
+
connected: true,
|
|
670
|
+
indicatorVariant: "circle",
|
|
671
|
+
},
|
|
672
|
+
parameters: {
|
|
673
|
+
docs: {
|
|
674
|
+
description: {
|
|
675
|
+
story:
|
|
676
|
+
"Worst-case content in every row. Check that: unbroken strings and the long URL wrap inside the content column instead of widening the list; rows 10 to 12 (no slots provided) render as numbered rows with an empty content area; the two-digit counters still fit their bubble; the HTML-like text renders as text; the RTL row reads right to left; the wide custom indicator in row 2 still has the connector line running from its centre; and the second list (fractional `itemCount` 2.9) renders 2 rows rather than none.",
|
|
677
|
+
},
|
|
678
|
+
story: { inline: false, height: "1400px" },
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
render: (args) => ({
|
|
682
|
+
components: { StepperList },
|
|
683
|
+
setup() {
|
|
684
|
+
return { args, unbroken, longGerman, longUrl };
|
|
685
|
+
},
|
|
686
|
+
template: `
|
|
687
|
+
${parentStoriesNote}
|
|
688
|
+
<div style="max-inline-size: 48rem;">
|
|
689
|
+
<StepperList v-bind="args">
|
|
690
|
+
<template #item-0><p class="page-body-normal">{{ unbroken }}</p></template>
|
|
691
|
+
<template #indicator-1><span style="white-space: nowrap; padding-inline: 0.8rem; border: 2px solid currentColor; border-radius: 100vw;">Step 2 of 12 ✨</span></template>
|
|
692
|
+
<template #item-1><p class="page-body-normal">{{ longGerman }} {{ longGerman }}</p></template>
|
|
693
|
+
<template #item-2><p class="page-body-normal"><a :href="longUrl">{{ longUrl }}</a></p></template>
|
|
694
|
+
<template #item-3><p class="page-body-normal">🦄💇♀️ Emoji first, then text</p></template>
|
|
695
|
+
<template #item-4><p class="page-body-normal" dir="rtl">مرحبا بكم في صالوننا، نحن نقدم جميع خدمات الشعر</p></template>
|
|
696
|
+
<template #item-5><p class="page-body-normal"><script>alert(1)</script> renders as text</p></template>
|
|
697
|
+
<template #item-6><p class="page-body-normal">A</p></template>
|
|
698
|
+
<template #item-7></template>
|
|
699
|
+
<template #item-8><p class="page-body-normal">{{ longGerman }}</p><p class="page-body-normal">{{ unbroken }}</p></template>
|
|
700
|
+
</StepperList>
|
|
701
|
+
<StepperList :item-count="2.9" indicator-variant="square" style="margin-block-start: 3.2rem;">
|
|
702
|
+
<template #item-0><p class="page-body-normal">Fractional itemCount: first row</p></template>
|
|
703
|
+
<template #item-1><p class="page-body-normal">Second row (a third would be wrong)</p></template>
|
|
704
|
+
<template #item-2><p class="page-body-normal">Third row: should not render</p></template>
|
|
705
|
+
</StepperList>
|
|
706
|
+
</div>
|
|
707
|
+
`,
|
|
708
|
+
}),
|
|
709
|
+
};
|