srcdev-nuxt-components 9.6.18 → 9.6.19
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/css-relative-colour-calc.md +15 -0
- package/.claude/rules/consumer/vue-dynamic-arg-no-spaces.md +16 -0
- package/.claude/skills/component-compliance-checklist.md +6 -1
- package/.claude/skills/component-dynamic-slots.md +2 -2
- package/.claude/skills/component-naming.md +1 -1
- package/.claude/skills/components/accordion-chat.md +99 -0
- package/.claude/skills/components/accordion-group.md +98 -0
- package/.claude/skills/components/action-menu.md +11 -5
- package/.claude/skills/components/expanding-panel-classic.md +9 -7
- package/.claude/skills/components/expanding-panel.md +9 -8
- package/.claude/skills/components/sparkle-effect.md +79 -0
- package/.claude/skills/index.md +4 -2
- package/.claude/skills/storybook-add-story.md +8 -0
- package/.claude/skills/using-component-skills.md +1 -1
- package/.vscode/srcdev-component-accordion-chat.code-snippets +42 -0
- package/.vscode/srcdev-component-accordion-group.code-snippets +38 -0
- package/.vscode/srcdev-component-action-menu.code-snippets +3 -3
- package/.vscode/srcdev-component-sparkle-effect.code-snippets +33 -0
- package/app/components/01.atoms/animations/sparkle-effect/CONSUMER-STYLING.md +75 -0
- package/app/components/01.atoms/animations/sparkle-effect/SparkleEffect.vue +213 -0
- package/app/components/01.atoms/animations/sparkle-effect/stories/SparkleEffect.stories.ts +303 -0
- package/app/components/01.atoms/animations/sparkle-effect/tests/SparkleEffect.spec.ts +150 -0
- package/app/components/01.atoms/content-wrappers/docs-pages/ContentDocs.vue +1 -1
- package/app/components/02.molecules/action-menu/ActionMenu.vue +6 -2
- package/app/components/02.molecules/action-menu/ActionMenuItem.vue +6 -3
- package/app/components/02.molecules/action-menu/CONSUMER-STYLING.md +11 -4
- package/app/components/02.molecules/action-menu/stories/ActionMenu.stories.ts +130 -18
- package/app/components/02.molecules/expandable/accordion-chat/AccordionChat.vue +227 -0
- package/app/components/02.molecules/expandable/accordion-chat/CONSUMER-STYLING.md +150 -0
- package/app/components/02.molecules/expandable/accordion-chat/stories/AccordionChat.stories.ts +347 -0
- package/app/components/02.molecules/expandable/accordion-chat/tests/AccordionChat.spec.ts +205 -0
- package/app/components/02.molecules/expandable/accordion-chat/tests/__snapshots__/AccordionChat.spec.ts.snap +40 -0
- package/app/components/02.molecules/expandable/{accordian/AccordianCore.vue → accordion-group/AccordionGroup.vue} +32 -20
- package/app/components/02.molecules/expandable/accordion-group/CONSUMER-STYLING.md +66 -0
- package/app/components/02.molecules/expandable/accordion-group/stories/AccordionGroup.stories.ts +238 -0
- package/app/components/02.molecules/expandable/accordion-group/tests/AccordionGroup.spec.ts +172 -0
- package/app/components/02.molecules/expandable/accordion-group/tests/__snapshots__/AccordionGroup.spec.ts.snap +45 -0
- package/app/components/02.molecules/expandable/expanding-panel/CONSUMER-STYLING.md +1 -0
- package/app/components/02.molecules/expandable/expanding-panel/ExpandingPanel.vue +11 -6
- package/app/components/02.molecules/expandable/expanding-panel/stories/ExpandingPanel.stories.ts +82 -1
- package/app/components/02.molecules/expandable/expanding-panel/tests/ExpandingPanel.spec.ts +24 -30
- package/app/components/02.molecules/expandable/expanding-panel/tests/__snapshots__/ExpandingPanel.spec.ts.snap +8 -8
- package/app/components/02.molecules/expandable/expanding-panel-classic/CONSUMER-STYLING.md +1 -0
- package/app/components/02.molecules/expandable/expanding-panel-classic/ExpandingPanelClassic.vue +10 -5
- package/app/components/02.molecules/expandable/expanding-panel-classic/stories/ExpandingPanelClassic.stories.ts +83 -1
- package/app/components/02.molecules/expandable/expanding-panel-classic/tests/ExpandingPanelClassic.spec.ts +24 -30
- package/app/components/02.molecules/expandable/expanding-panel-classic/tests/__snapshots__/ExpandingPanelClassic.spec.ts.snap +8 -8
- package/app/components/03.organisms/responsive-header/tests/__snapshots__/NavigationItems.spec.ts.snap +2 -2
- package/app/components/03.organisms/responsive-header/tests/__snapshots__/ResponsiveHeader.spec.ts.snap +2 -2
- package/package.json +1 -1
- package/.claude/skills/components/accordian-core.md +0 -180
- package/.vscode/srcdev-component-accordian-core.code-snippets +0 -74
- package/app/components/02.molecules/expandable/accordian/CONSUMER-STYLING.md +0 -100
- package/app/components/02.molecules/expandable/accordian/stories/AccordianCore.stories.ts +0 -272
- package/app/components/02.molecules/expandable/accordian/tests/AccordianCore.spec.ts +0 -257
- package/app/components/02.molecules/expandable/accordian/tests/__snapshots__/AccordianCore.spec.ts.snap +0 -95
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import AccordianCore from "../AccordianCore.vue";
|
|
2
|
-
import type { Meta, StoryObj } from "@nuxtjs/storybook";
|
|
3
|
-
|
|
4
|
-
const meta: Meta<typeof AccordianCore> = {
|
|
5
|
-
title: "Molecules/Expandable/Accordian Core",
|
|
6
|
-
component: AccordianCore,
|
|
7
|
-
argTypes: {
|
|
8
|
-
name: {
|
|
9
|
-
control: { type: "text" },
|
|
10
|
-
description:
|
|
11
|
-
"Shared name attribute passed to each ExpandingPanel — used to group panels so only one can be open at a time (native <details> behaviour)",
|
|
12
|
-
},
|
|
13
|
-
itemCount: {
|
|
14
|
-
control: { type: "number", min: 0, step: 1 },
|
|
15
|
-
description: "Number of accordion panels to render",
|
|
16
|
-
},
|
|
17
|
-
animationDuration: {
|
|
18
|
-
control: { type: "number", min: 0, step: 50 },
|
|
19
|
-
description: "Expand/collapse animation duration in milliseconds",
|
|
20
|
-
},
|
|
21
|
-
variant: {
|
|
22
|
-
control: { type: "radio" },
|
|
23
|
-
options: ["modern", "classic"],
|
|
24
|
-
description:
|
|
25
|
-
"Which panel implementation to render: 'modern' (ExpandingPanel, ::details-content based) or " +
|
|
26
|
-
"'classic' (ExpandingPanelClassic, grid-template-rows based) — use classic when the open/close " +
|
|
27
|
-
"animation must run identically in every browser today",
|
|
28
|
-
},
|
|
29
|
-
styleClassPassthrough: {
|
|
30
|
-
control: "object",
|
|
31
|
-
description: "Additional CSS classes applied to the root element",
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
args: {
|
|
35
|
-
name: undefined,
|
|
36
|
-
itemCount: 3,
|
|
37
|
-
animationDuration: 300,
|
|
38
|
-
variant: "modern",
|
|
39
|
-
styleClassPassthrough: [],
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export default meta;
|
|
44
|
-
type Story = StoryObj<typeof AccordianCore>;
|
|
45
|
-
|
|
46
|
-
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
47
|
-
|
|
48
|
-
/** Builds a slot object for N items with generic placeholder content. */
|
|
49
|
-
function buildGenericSlots(count: number): Record<string, string> {
|
|
50
|
-
const slots: Record<string, string> = {};
|
|
51
|
-
for (let i = 0; i < count; i++) {
|
|
52
|
-
slots[`accordian-${i}-summary`] = `<span>Panel ${i + 1} — Summary</span>`;
|
|
53
|
-
slots[`accordian-${i}-icon`] = `<span>▸</span>`;
|
|
54
|
-
slots[`accordian-${i}-content`] =
|
|
55
|
-
`<p style="margin:0;padding:0.5rem 0">This is the content for panel ${i + 1}. ` +
|
|
56
|
-
`Add whatever you need here — text, images, components.</p>`;
|
|
57
|
-
}
|
|
58
|
-
return slots;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ─── Stories ─────────────────────────────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
export const Default: Story = {
|
|
64
|
-
args: {
|
|
65
|
-
itemCount: 3,
|
|
66
|
-
animationDuration: 300,
|
|
67
|
-
},
|
|
68
|
-
render: (args) => ({
|
|
69
|
-
components: { AccordianCore },
|
|
70
|
-
setup() {
|
|
71
|
-
return { args, slots: buildGenericSlots(args.itemCount ?? 3) };
|
|
72
|
-
},
|
|
73
|
-
template: `
|
|
74
|
-
<AccordianCore v-bind="args">
|
|
75
|
-
<template v-for="(content, name) in slots" #[name] :key="name">
|
|
76
|
-
<span v-html="content" />
|
|
77
|
-
</template>
|
|
78
|
-
</AccordianCore>
|
|
79
|
-
`,
|
|
80
|
-
}),
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export const ClassicVariant: Story = {
|
|
84
|
-
name: "Classic Variant",
|
|
85
|
-
args: {
|
|
86
|
-
itemCount: 3,
|
|
87
|
-
animationDuration: 300,
|
|
88
|
-
variant: "classic",
|
|
89
|
-
},
|
|
90
|
-
render: (args) => ({
|
|
91
|
-
components: { AccordianCore },
|
|
92
|
-
setup() {
|
|
93
|
-
return { args, slots: buildGenericSlots(args.itemCount ?? 3) };
|
|
94
|
-
},
|
|
95
|
-
template: `
|
|
96
|
-
<AccordianCore v-bind="args">
|
|
97
|
-
<template v-for="(content, name) in slots" #[name] :key="name">
|
|
98
|
-
<span v-html="content" />
|
|
99
|
-
</template>
|
|
100
|
-
</AccordianCore>
|
|
101
|
-
`,
|
|
102
|
-
}),
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
export const SinglePanel: Story = {
|
|
106
|
-
args: {
|
|
107
|
-
itemCount: 1,
|
|
108
|
-
animationDuration: 300,
|
|
109
|
-
},
|
|
110
|
-
render: (args) => ({
|
|
111
|
-
components: { AccordianCore },
|
|
112
|
-
setup() {
|
|
113
|
-
return { args };
|
|
114
|
-
},
|
|
115
|
-
template: `
|
|
116
|
-
<AccordianCore v-bind="args">
|
|
117
|
-
<template #accordian-0-summary>
|
|
118
|
-
<span>The only panel</span>
|
|
119
|
-
</template>
|
|
120
|
-
<template #accordian-0-content>
|
|
121
|
-
<p style="margin:0;padding:0.5rem 0">Content for the single panel.</p>
|
|
122
|
-
</template>
|
|
123
|
-
</AccordianCore>
|
|
124
|
-
`,
|
|
125
|
-
}),
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/** Panels share a name so the browser enforces only one open at a time. */
|
|
129
|
-
export const ExclusiveOpen: Story = {
|
|
130
|
-
name: "Exclusive Open (name grouped)",
|
|
131
|
-
args: {
|
|
132
|
-
itemCount: 4,
|
|
133
|
-
name: "exclusive-group",
|
|
134
|
-
animationDuration: 300,
|
|
135
|
-
},
|
|
136
|
-
render: (args) => ({
|
|
137
|
-
components: { AccordianCore },
|
|
138
|
-
setup() {
|
|
139
|
-
return { args, slots: buildGenericSlots(args.itemCount ?? 4) };
|
|
140
|
-
},
|
|
141
|
-
template: `
|
|
142
|
-
<AccordianCore v-bind="args">
|
|
143
|
-
<template v-for="(content, name) in slots" #[name] :key="name">
|
|
144
|
-
<span v-html="content" />
|
|
145
|
-
</template>
|
|
146
|
-
</AccordianCore>
|
|
147
|
-
`,
|
|
148
|
-
}),
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
export const SlowAnimation: Story = {
|
|
152
|
-
name: "Slow Animation (800ms)",
|
|
153
|
-
args: {
|
|
154
|
-
itemCount: 3,
|
|
155
|
-
animationDuration: 800,
|
|
156
|
-
},
|
|
157
|
-
render: (args) => ({
|
|
158
|
-
components: { AccordianCore },
|
|
159
|
-
setup() {
|
|
160
|
-
return { args, slots: buildGenericSlots(args.itemCount ?? 3) };
|
|
161
|
-
},
|
|
162
|
-
template: `
|
|
163
|
-
<AccordianCore v-bind="args">
|
|
164
|
-
<template v-for="(content, name) in slots" #[name] :key="name">
|
|
165
|
-
<span v-html="content" />
|
|
166
|
-
</template>
|
|
167
|
-
</AccordianCore>
|
|
168
|
-
`,
|
|
169
|
-
}),
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
export const NoAnimation: Story = {
|
|
173
|
-
name: "No Animation (0ms)",
|
|
174
|
-
args: {
|
|
175
|
-
itemCount: 3,
|
|
176
|
-
animationDuration: 0,
|
|
177
|
-
},
|
|
178
|
-
render: (args) => ({
|
|
179
|
-
components: { AccordianCore },
|
|
180
|
-
setup() {
|
|
181
|
-
return { args, slots: buildGenericSlots(args.itemCount ?? 3) };
|
|
182
|
-
},
|
|
183
|
-
template: `
|
|
184
|
-
<AccordianCore v-bind="args">
|
|
185
|
-
<template v-for="(content, name) in slots" #[name] :key="name">
|
|
186
|
-
<span v-html="content" />
|
|
187
|
-
</template>
|
|
188
|
-
</AccordianCore>
|
|
189
|
-
`,
|
|
190
|
-
}),
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
export const WithStyleClassPassthrough: Story = {
|
|
194
|
-
name: "With styleClassPassthrough",
|
|
195
|
-
args: {
|
|
196
|
-
itemCount: 2,
|
|
197
|
-
styleClassPassthrough: ["custom-class", "another-class"],
|
|
198
|
-
},
|
|
199
|
-
render: (args) => ({
|
|
200
|
-
components: { AccordianCore },
|
|
201
|
-
setup() {
|
|
202
|
-
return { args, slots: buildGenericSlots(args.itemCount ?? 2) };
|
|
203
|
-
},
|
|
204
|
-
template: `
|
|
205
|
-
<AccordianCore v-bind="args">
|
|
206
|
-
<template v-for="(content, name) in slots" #[name] :key="name">
|
|
207
|
-
<span v-html="content" />
|
|
208
|
-
</template>
|
|
209
|
-
</AccordianCore>
|
|
210
|
-
`,
|
|
211
|
-
}),
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
export const RichContent: Story = {
|
|
215
|
-
name: "Rich Slot Content",
|
|
216
|
-
args: {
|
|
217
|
-
itemCount: 3,
|
|
218
|
-
animationDuration: 300,
|
|
219
|
-
},
|
|
220
|
-
render: (args) => ({
|
|
221
|
-
components: { AccordianCore },
|
|
222
|
-
setup() {
|
|
223
|
-
return { args };
|
|
224
|
-
},
|
|
225
|
-
template: `
|
|
226
|
-
<AccordianCore v-bind="args">
|
|
227
|
-
<template #accordian-0-summary>
|
|
228
|
-
<strong>Delivery & Returns</strong>
|
|
229
|
-
</template>
|
|
230
|
-
<template #accordian-0-content>
|
|
231
|
-
<p style="margin:0;padding:0.5rem 0">
|
|
232
|
-
Free standard delivery on orders over £50. Returns accepted within 30 days.
|
|
233
|
-
</p>
|
|
234
|
-
</template>
|
|
235
|
-
|
|
236
|
-
<template #accordian-1-summary>
|
|
237
|
-
<strong>Materials & Care</strong>
|
|
238
|
-
</template>
|
|
239
|
-
<template #accordian-1-content>
|
|
240
|
-
<ul style="margin:0;padding:0.5rem 0 0.5rem 1.2rem">
|
|
241
|
-
<li>100% organic cotton</li>
|
|
242
|
-
<li>Machine wash at 30°C</li>
|
|
243
|
-
<li>Do not tumble dry</li>
|
|
244
|
-
</ul>
|
|
245
|
-
</template>
|
|
246
|
-
|
|
247
|
-
<template #accordian-2-summary>
|
|
248
|
-
<strong>Sizing Guide</strong>
|
|
249
|
-
</template>
|
|
250
|
-
<template #accordian-2-content>
|
|
251
|
-
<p style="margin:0;padding:0.5rem 0">
|
|
252
|
-
Our pieces run true to size. If you're between sizes, we recommend sizing up.
|
|
253
|
-
</p>
|
|
254
|
-
</template>
|
|
255
|
-
</AccordianCore>
|
|
256
|
-
`,
|
|
257
|
-
}),
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
export const Empty: Story = {
|
|
261
|
-
name: "Zero Items",
|
|
262
|
-
args: {
|
|
263
|
-
itemCount: 0,
|
|
264
|
-
},
|
|
265
|
-
render: (args) => ({
|
|
266
|
-
components: { AccordianCore },
|
|
267
|
-
setup() {
|
|
268
|
-
return { args };
|
|
269
|
-
},
|
|
270
|
-
template: `<AccordianCore v-bind="args" />`,
|
|
271
|
-
}),
|
|
272
|
-
};
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { mountSuspended } from "@nuxt/test-utils/runtime";
|
|
3
|
-
import AccordianCore from "../AccordianCore.vue";
|
|
4
|
-
import type { ComponentPublicInstance } from "vue";
|
|
5
|
-
|
|
6
|
-
interface AccordianCoreInstance extends ComponentPublicInstance {
|
|
7
|
-
animationDurationStr: string;
|
|
8
|
-
name: string | undefined;
|
|
9
|
-
itemCount: number;
|
|
10
|
-
animationDuration: number;
|
|
11
|
-
variant: "modern" | "classic";
|
|
12
|
-
styleClassPassthrough: string | string[];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
describe("AccordianCore", () => {
|
|
16
|
-
// ─── Mount ───────────────────────────────────────────────────────────────
|
|
17
|
-
|
|
18
|
-
it("mounts without error", async () => {
|
|
19
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
20
|
-
props: { itemCount: 3 },
|
|
21
|
-
});
|
|
22
|
-
expect(wrapper.vm).toBeTruthy();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// ─── Snapshots ───────────────────────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
it("renders correct HTML structure with default props", async () => {
|
|
28
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
29
|
-
props: { itemCount: 2 },
|
|
30
|
-
});
|
|
31
|
-
expect(wrapper.html()).toMatchSnapshot();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("renders correct HTML structure with all props set", async () => {
|
|
35
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
36
|
-
props: {
|
|
37
|
-
itemCount: 3,
|
|
38
|
-
name: "snapshot-accordian",
|
|
39
|
-
animationDuration: 500,
|
|
40
|
-
styleClassPassthrough: ["extra-class"],
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
expect(wrapper.html()).toMatchSnapshot();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("renders correct HTML structure with populated slots", async () => {
|
|
47
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
48
|
-
props: { itemCount: 2 },
|
|
49
|
-
slots: {
|
|
50
|
-
"accordian-0-summary": "<span>Summary 0</span>",
|
|
51
|
-
"accordian-0-icon": "<span>Icon 0</span>",
|
|
52
|
-
"accordian-0-content": "<span>Content 0</span>",
|
|
53
|
-
"accordian-1-summary": "<span>Summary 1</span>",
|
|
54
|
-
"accordian-1-icon": "<span>Icon 1</span>",
|
|
55
|
-
"accordian-1-content": "<span>Content 1</span>",
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
expect(wrapper.html()).toMatchSnapshot();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("renders correctly with zero items", async () => {
|
|
62
|
-
const wrapper = await mountSuspended(AccordianCore);
|
|
63
|
-
expect(wrapper.html()).toMatchSnapshot();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// ─── Root element ─────────────────────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
it("renders the root .display-accordian element", async () => {
|
|
69
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
70
|
-
props: { itemCount: 1 },
|
|
71
|
-
});
|
|
72
|
-
expect(wrapper.find(".display-accordian").exists()).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// ─── Item count ───────────────────────────────────────────────────────────
|
|
76
|
-
|
|
77
|
-
it("renders the correct number of panel components", async () => {
|
|
78
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
79
|
-
props: { itemCount: 4 },
|
|
80
|
-
});
|
|
81
|
-
expect(wrapper.findAll(".accordian-item")).toHaveLength(4);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("renders no ExpandingPanel components when itemCount is 0", async () => {
|
|
85
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
86
|
-
props: { itemCount: 0 },
|
|
87
|
-
});
|
|
88
|
-
expect(wrapper.findAllComponents({ name: "ExpandingPanel" })).toHaveLength(0);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("renders no ExpandingPanel components when itemCount is omitted", async () => {
|
|
92
|
-
const wrapper = await mountSuspended(AccordianCore);
|
|
93
|
-
expect(wrapper.findAllComponents({ name: "ExpandingPanel" })).toHaveLength(0);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// ─── variant ──────────────────────────────────────────────────────────────
|
|
97
|
-
|
|
98
|
-
it("renders ExpandingPanelClassic by default (variant: classic)", async () => {
|
|
99
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
100
|
-
props: { itemCount: 2 },
|
|
101
|
-
});
|
|
102
|
-
expect(wrapper.findAllComponents({ name: "ExpandingPanelClassic" })).toHaveLength(2);
|
|
103
|
-
expect(wrapper.findAllComponents({ name: "ExpandingPanel" })).toHaveLength(0);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("renders ExpandingPanelClassic when variant is classic (explicit)", async () => {
|
|
107
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
108
|
-
props: { itemCount: 2, variant: "classic" },
|
|
109
|
-
});
|
|
110
|
-
expect(wrapper.findAllComponents({ name: "ExpandingPanelClassic" })).toHaveLength(2);
|
|
111
|
-
expect(wrapper.findAllComponents({ name: "ExpandingPanel" })).toHaveLength(0);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("renders ExpandingPanel when variant is modern", async () => {
|
|
115
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
116
|
-
props: { itemCount: 2, variant: "modern" },
|
|
117
|
-
});
|
|
118
|
-
expect(wrapper.findAllComponents({ name: "ExpandingPanel" })).toHaveLength(2);
|
|
119
|
-
expect(wrapper.findAllComponents({ name: "ExpandingPanelClassic" })).toHaveLength(0);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("forwards props identically to ExpandingPanelClassic when variant is classic", async () => {
|
|
123
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
124
|
-
props: { itemCount: 2, name: "classic-accordian", animationDuration: 500, variant: "classic" },
|
|
125
|
-
});
|
|
126
|
-
wrapper.findAllComponents({ name: "ExpandingPanelClassic" }).forEach((panel) => {
|
|
127
|
-
expect(panel.props("name")).toBe("classic-accordian");
|
|
128
|
-
expect(panel.props("animationDuration")).toBe(500);
|
|
129
|
-
expect(panel.props("styleClassPassthrough")).toEqual(["accordian-item"]);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// ─── Props forwarded to ExpandingPanel (variant: modern) ─────────────────
|
|
134
|
-
|
|
135
|
-
it("forwards animationDuration to every ExpandingPanel", async () => {
|
|
136
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
137
|
-
props: { itemCount: 3, animationDuration: 500, variant: "modern" },
|
|
138
|
-
});
|
|
139
|
-
wrapper.findAllComponents({ name: "ExpandingPanel" }).forEach((panel) => {
|
|
140
|
-
expect(panel.props("animationDuration")).toBe(500);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("forwards name to every ExpandingPanel", async () => {
|
|
145
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
146
|
-
props: { itemCount: 2, name: "test-accordian", variant: "modern" },
|
|
147
|
-
});
|
|
148
|
-
wrapper.findAllComponents({ name: "ExpandingPanel" }).forEach((panel) => {
|
|
149
|
-
expect(panel.props("name")).toBe("test-accordian");
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it("passes accordian-item styleClassPassthrough to every ExpandingPanel", async () => {
|
|
154
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
155
|
-
props: { itemCount: 2, variant: "modern" },
|
|
156
|
-
});
|
|
157
|
-
wrapper.findAllComponents({ name: "ExpandingPanel" }).forEach((panel) => {
|
|
158
|
-
expect(panel.props("styleClassPassthrough")).toEqual(["accordian-item"]);
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// ─── Default prop values ──────────────────────────────────────────────────
|
|
163
|
-
|
|
164
|
-
it("defaults animationDuration to 300 on ExpandingPanelClassic when not provided", async () => {
|
|
165
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
166
|
-
props: { itemCount: 1 },
|
|
167
|
-
});
|
|
168
|
-
const panel = wrapper.findComponent({ name: "ExpandingPanelClassic" });
|
|
169
|
-
expect(panel.props("animationDuration")).toBe(300);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("uses correct default prop values on the component instance", async () => {
|
|
173
|
-
const wrapper = await mountSuspended(AccordianCore);
|
|
174
|
-
const vm = wrapper.vm as AccordianCoreInstance;
|
|
175
|
-
expect(vm.name).toBeUndefined();
|
|
176
|
-
expect(vm.itemCount).toBe(0);
|
|
177
|
-
expect(vm.animationDuration).toBe(300);
|
|
178
|
-
expect(vm.variant).toBe("classic");
|
|
179
|
-
expect(vm.styleClassPassthrough).toEqual([]);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// ─── Computed ─────────────────────────────────────────────────────────────
|
|
183
|
-
|
|
184
|
-
it("computes animationDurationStr as '<value>ms'", async () => {
|
|
185
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
186
|
-
props: { itemCount: 1, animationDuration: 750 },
|
|
187
|
-
});
|
|
188
|
-
const vm = wrapper.vm as AccordianCoreInstance;
|
|
189
|
-
expect(vm.animationDurationStr).toBe("750ms");
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("computes animationDurationStr using the default duration", async () => {
|
|
193
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
194
|
-
props: { itemCount: 1 },
|
|
195
|
-
});
|
|
196
|
-
const vm = wrapper.vm as AccordianCoreInstance;
|
|
197
|
-
expect(vm.animationDurationStr).toBe("300ms");
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// ─── styleClassPassthrough ────────────────────────────────────────────────
|
|
201
|
-
|
|
202
|
-
it("applies a styleClassPassthrough array to the root element", async () => {
|
|
203
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
204
|
-
props: {
|
|
205
|
-
itemCount: 1,
|
|
206
|
-
styleClassPassthrough: ["custom-class", "another-class"],
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
const root = wrapper.find(".display-accordian");
|
|
210
|
-
expect(root.classes()).toContain("custom-class");
|
|
211
|
-
expect(root.classes()).toContain("another-class");
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it("applies a single styleClassPassthrough string to the root element", async () => {
|
|
215
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
216
|
-
props: { itemCount: 1, styleClassPassthrough: "single-class" },
|
|
217
|
-
});
|
|
218
|
-
expect(wrapper.find(".display-accordian").classes()).toContain("single-class");
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// ─── Slots ────────────────────────────────────────────────────────────────
|
|
222
|
-
|
|
223
|
-
it("renders summary, icon, and content slot content for each item", async () => {
|
|
224
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
225
|
-
props: { itemCount: 2 },
|
|
226
|
-
slots: {
|
|
227
|
-
"accordian-0-summary": '<div data-testid="summary-0">Summary 0</div>',
|
|
228
|
-
"accordian-0-icon": '<div data-testid="icon-0">Icon 0</div>',
|
|
229
|
-
"accordian-0-content": '<div data-testid="content-0">Content 0</div>',
|
|
230
|
-
"accordian-1-summary": '<div data-testid="summary-1">Summary 1</div>',
|
|
231
|
-
"accordian-1-icon": '<div data-testid="icon-1">Icon 1</div>',
|
|
232
|
-
"accordian-1-content": '<div data-testid="content-1">Content 1</div>',
|
|
233
|
-
},
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
for (let i = 0; i < 2; i++) {
|
|
237
|
-
expect(wrapper.find(`[data-testid="summary-${i}"]`).text()).toBe(`Summary ${i}`);
|
|
238
|
-
expect(wrapper.find(`[data-testid="icon-${i}"]`).text()).toBe(`Icon ${i}`);
|
|
239
|
-
expect(wrapper.find(`[data-testid="content-${i}"]`).text()).toBe(`Content ${i}`);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it("renders slot content in the correct panel (slots are not mixed up)", async () => {
|
|
244
|
-
const wrapper = await mountSuspended(AccordianCore, {
|
|
245
|
-
props: { itemCount: 3 },
|
|
246
|
-
slots: {
|
|
247
|
-
"accordian-0-summary": "<span>Alpha</span>",
|
|
248
|
-
"accordian-1-summary": "<span>Beta</span>",
|
|
249
|
-
"accordian-2-summary": "<span>Gamma</span>",
|
|
250
|
-
},
|
|
251
|
-
});
|
|
252
|
-
const panels = wrapper.findAllComponents({ name: "ExpandingPanelClassic" });
|
|
253
|
-
expect(panels[0]?.text()).toContain("Alpha");
|
|
254
|
-
expect(panels[1]?.text()).toContain("Beta");
|
|
255
|
-
expect(panels[2]?.text()).toContain("Gamma");
|
|
256
|
-
});
|
|
257
|
-
});
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
-
|
|
3
|
-
exports[`AccordianCore > renders correct HTML structure with all props set 1`] = `
|
|
4
|
-
"<div class="display-accordian extra-class">
|
|
5
|
-
<div class="expanding-panel-classic accordian-item" icon-size="medium">
|
|
6
|
-
<details class="expanding-panel-classic-details" name="snapshot-accordian">
|
|
7
|
-
<summary id="id-snapshot-accordian-trigger" class="expanding-panel-classic-summary" aria-controls="id-snapshot-accordian-content" aria-expanded="false">
|
|
8
|
-
<div class="label-wrapper"></div>
|
|
9
|
-
<div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
|
|
10
|
-
</summary>
|
|
11
|
-
</details>
|
|
12
|
-
<div id="id-snapshot-accordian-content" class="expanding-panel-classic-content" aria-labelledby="id-snapshot-accordian-trigger" role="region">
|
|
13
|
-
<div class="inner"></div>
|
|
14
|
-
</div>
|
|
15
|
-
</div>
|
|
16
|
-
<div class="expanding-panel-classic accordian-item" icon-size="medium">
|
|
17
|
-
<details class="expanding-panel-classic-details" name="snapshot-accordian">
|
|
18
|
-
<summary id="id-snapshot-accordian-trigger" class="expanding-panel-classic-summary" aria-controls="id-snapshot-accordian-content" aria-expanded="false">
|
|
19
|
-
<div class="label-wrapper"></div>
|
|
20
|
-
<div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
|
|
21
|
-
</summary>
|
|
22
|
-
</details>
|
|
23
|
-
<div id="id-snapshot-accordian-content" class="expanding-panel-classic-content" aria-labelledby="id-snapshot-accordian-trigger" role="region">
|
|
24
|
-
<div class="inner"></div>
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
<div class="expanding-panel-classic accordian-item" icon-size="medium">
|
|
28
|
-
<details class="expanding-panel-classic-details" name="snapshot-accordian">
|
|
29
|
-
<summary id="id-snapshot-accordian-trigger" class="expanding-panel-classic-summary" aria-controls="id-snapshot-accordian-content" aria-expanded="false">
|
|
30
|
-
<div class="label-wrapper"></div>
|
|
31
|
-
<div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
|
|
32
|
-
</summary>
|
|
33
|
-
</details>
|
|
34
|
-
<div id="id-snapshot-accordian-content" class="expanding-panel-classic-content" aria-labelledby="id-snapshot-accordian-trigger" role="region">
|
|
35
|
-
<div class="inner"></div>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
</div>"
|
|
39
|
-
`;
|
|
40
|
-
|
|
41
|
-
exports[`AccordianCore > renders correct HTML structure with default props 1`] = `
|
|
42
|
-
"<div class="display-accordian">
|
|
43
|
-
<div class="expanding-panel-classic accordian-item" icon-size="medium">
|
|
44
|
-
<details class="expanding-panel-classic-details" name="v-0-0-0">
|
|
45
|
-
<summary id="id-v-0-0-0-trigger" class="expanding-panel-classic-summary" aria-controls="id-v-0-0-0-content" aria-expanded="false">
|
|
46
|
-
<div class="label-wrapper"></div>
|
|
47
|
-
<div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
|
|
48
|
-
</summary>
|
|
49
|
-
</details>
|
|
50
|
-
<div id="id-v-0-0-0-content" class="expanding-panel-classic-content" aria-labelledby="id-v-0-0-0-trigger" role="region">
|
|
51
|
-
<div class="inner"></div>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
<div class="expanding-panel-classic accordian-item" icon-size="medium">
|
|
55
|
-
<details class="expanding-panel-classic-details" name="v-0-0-1">
|
|
56
|
-
<summary id="id-v-0-0-1-trigger" class="expanding-panel-classic-summary" aria-controls="id-v-0-0-1-content" aria-expanded="false">
|
|
57
|
-
<div class="label-wrapper"></div>
|
|
58
|
-
<div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
|
|
59
|
-
</summary>
|
|
60
|
-
</details>
|
|
61
|
-
<div id="id-v-0-0-1-content" class="expanding-panel-classic-content" aria-labelledby="id-v-0-0-1-trigger" role="region">
|
|
62
|
-
<div class="inner"></div>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
</div>"
|
|
66
|
-
`;
|
|
67
|
-
|
|
68
|
-
exports[`AccordianCore > renders correct HTML structure with populated slots 1`] = `
|
|
69
|
-
"<div class="display-accordian">
|
|
70
|
-
<div class="expanding-panel-classic accordian-item" icon-size="medium">
|
|
71
|
-
<details class="expanding-panel-classic-details" name="v-0-0-0">
|
|
72
|
-
<summary id="id-v-0-0-0-trigger" class="expanding-panel-classic-summary" aria-controls="id-v-0-0-0-content" aria-expanded="false">
|
|
73
|
-
<div class="label-wrapper"><span>Summary 0</span></div>
|
|
74
|
-
<div class="icon-wrapper"><span>Icon 0</span></div>
|
|
75
|
-
</summary>
|
|
76
|
-
</details>
|
|
77
|
-
<div id="id-v-0-0-0-content" class="expanding-panel-classic-content" aria-labelledby="id-v-0-0-0-trigger" role="region">
|
|
78
|
-
<div class="inner"><span>Content 0</span></div>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
<div class="expanding-panel-classic accordian-item" icon-size="medium">
|
|
82
|
-
<details class="expanding-panel-classic-details" name="v-0-0-1">
|
|
83
|
-
<summary id="id-v-0-0-1-trigger" class="expanding-panel-classic-summary" aria-controls="id-v-0-0-1-content" aria-expanded="false">
|
|
84
|
-
<div class="label-wrapper"><span>Summary 1</span></div>
|
|
85
|
-
<div class="icon-wrapper"><span>Icon 1</span></div>
|
|
86
|
-
</summary>
|
|
87
|
-
</details>
|
|
88
|
-
<div id="id-v-0-0-1-content" class="expanding-panel-classic-content" aria-labelledby="id-v-0-0-1-trigger" role="region">
|
|
89
|
-
<div class="inner"><span>Content 1</span></div>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
</div>"
|
|
93
|
-
`;
|
|
94
|
-
|
|
95
|
-
exports[`AccordianCore > renders correctly with zero items 1`] = `"<div class="display-accordian"></div>"`;
|