srcdev-nuxt-components 9.6.18 → 9.6.20
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/ids-from-useid.md +3 -2
- package/.claude/rules/consumer/vue-dynamic-arg-no-spaces.md +16 -0
- package/.claude/skills/component-compliance-checklist.md +11 -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/auto-grid.md +3 -1
- package/.claude/skills/components/capture-qr-code.md +9 -5
- package/.claude/skills/components/carousel-flip.md +41 -30
- package/.claude/skills/components/column-flow-grid.md +2 -0
- package/.claude/skills/components/dashboard-quad-grid.md +7 -2
- package/.claude/skills/components/dashboard-stats-grid.md +4 -0
- package/.claude/skills/components/decode-qr-code.md +7 -2
- package/.claude/skills/components/deep-expanding-menu-classic.md +31 -30
- package/.claude/skills/components/deep-expanding-menu.md +22 -21
- package/.claude/skills/components/expanding-panel-classic.md +9 -7
- package/.claude/skills/components/expanding-panel.md +9 -8
- package/.claude/skills/components/grid-stack.md +2 -0
- package/.claude/skills/components/layout-grid-by-cols.md +2 -0
- package/.claude/skills/components/masonry-grid.md +9 -1
- package/.claude/skills/components/page-row.md +3 -0
- package/.claude/skills/components/samaritan-prompt.md +24 -8
- package/.claude/skills/components/site-header.md +16 -2
- package/.claude/skills/components/sparkle-effect.md +79 -0
- package/.claude/skills/index.md +16 -14
- package/.claude/skills/storybook-add-story.md +33 -0
- package/.claude/skills/testing-add-unit-test.md +26 -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-capture-decode-qr-code.code-snippets +11 -2
- package/.vscode/srcdev-component-carousel-flip.code-snippets +22 -4
- package/.vscode/srcdev-component-deep-expanding-menu-classic.code-snippets +32 -2
- package/.vscode/srcdev-component-deep-expanding-menu.code-snippets +31 -1
- package/.vscode/srcdev-component-samaritan-prompt.code-snippets +5 -4
- package/.vscode/srcdev-component-site-header.code-snippets +13 -1
- 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/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/CONSUMER-STYLING.md +12 -2
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/DashboardQuadGrid.vue +19 -17
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/stories/DashboardQuadGrid.stories.ts +42 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/tests/DashboardQuadGrid.spec.ts +31 -44
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/CONSUMER-STYLING.md +13 -1
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/DashboardStatsGrid.vue +40 -31
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/stories/DashboardStatsGrid.stories.ts +47 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/tests/DashboardStatsGrid.spec.ts +51 -53
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/CONSUMER-STYLING.md +3 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/LayoutGridByCols.vue +4 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/stories/LayoutGridByCols.stories.ts +47 -2
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/tests/LayoutGridByCols.spec.ts +36 -23
- package/app/components/01.atoms/grids/auto-grid/AutoGrid.vue +7 -3
- package/app/components/01.atoms/grids/auto-grid/CONSUMER-STYLING.md +1 -1
- package/app/components/01.atoms/grids/auto-grid/stories/AutoGrid.stories.ts +46 -1
- package/app/components/01.atoms/grids/auto-grid/tests/AutoGrid.spec.ts +40 -27
- package/app/components/01.atoms/grids/auto-grid/tests/__snapshots__/AutoGrid.spec.ts.snap +1 -1
- package/app/components/01.atoms/grids/column-flow-grid/CONSUMER-STYLING.md +9 -0
- package/app/components/01.atoms/grids/column-flow-grid/ColumnFlowGrid.vue +3 -2
- package/app/components/01.atoms/grids/column-flow-grid/stories/ColumnFlowGrid.stories.ts +46 -0
- package/app/components/01.atoms/grids/column-flow-grid/tests/ColumnFlowGrid.spec.ts +24 -11
- package/app/components/01.atoms/grids/grid-stack/CONSUMER-STYLING.md +3 -0
- package/app/components/01.atoms/grids/grid-stack/GridStack.vue +2 -0
- package/app/components/01.atoms/grids/grid-stack/stories/GridStack.stories.ts +41 -0
- package/app/components/01.atoms/grids/grid-stack/tests/GridStack.spec.ts +26 -13
- package/app/components/01.atoms/grids/masonry-grid/CONSUMER-STYLING.md +15 -0
- package/app/components/01.atoms/grids/masonry-grid/MasonryGrid.vue +51 -31
- package/app/components/01.atoms/grids/masonry-grid/stories/MasonryGrid.stories.ts +48 -0
- package/app/components/01.atoms/grids/masonry-grid/tests/MasonryGrid.spec.ts +84 -29
- package/app/components/01.atoms/page-row/CONSUMER-STYLING.md +6 -1
- package/app/components/01.atoms/page-row/PageRow.vue +16 -15
- package/app/components/01.atoms/page-row/stories/PageRow.stories.ts +23 -0
- package/app/components/01.atoms/page-row/tests/PageRow.spec.ts +6 -5
- 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/02.molecules/navigation/deep-expanding-menu/CONSUMER-STYLING.md +63 -44
- package/app/components/02.molecules/navigation/deep-expanding-menu/DeepExpandingMenu.vue +74 -37
- package/app/components/02.molecules/navigation/deep-expanding-menu/stories/DeepExpandingMenu.stories.ts +139 -19
- package/app/components/02.molecules/navigation/deep-expanding-menu/tests/DeepExpandingMenu.spec.ts +90 -27
- package/app/components/02.molecules/navigation/deep-expanding-menu/tests/__snapshots__/DeepExpandingMenu.spec.ts.snap +8 -8
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/CONSUMER-STYLING.md +58 -26
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/DeepExpandingMenuClassic.vue +113 -57
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/stories/DeepExpandingMenuClassic.stories.ts +139 -19
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/tests/DeepExpandingMenuClassic.spec.ts +140 -22
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/tests/__snapshots__/DeepExpandingMenuClassic.spec.ts.snap +9 -9
- package/app/components/02.molecules/qr-code/CONSUMER-STYLING.md +22 -7
- package/app/components/02.molecules/qr-code/CaptureQrCode.vue +63 -71
- package/app/components/02.molecules/qr-code/DecodeQrCode.vue +47 -10
- package/app/components/02.molecules/qr-code/stories/CaptureQrCode.stories.ts +114 -26
- package/app/components/02.molecules/qr-code/stories/DecodeQrCode.stories.ts +115 -24
- package/app/components/02.molecules/qr-code/tests/CaptureQrCode.spec.ts +124 -51
- package/app/components/02.molecules/qr-code/tests/DecodeQrCode.spec.ts +74 -25
- package/app/components/02.molecules/samaritan-prompt/CONSUMER-STYLING.md +22 -10
- package/app/components/02.molecules/samaritan-prompt/SamaritanPrompt.vue +30 -12
- package/app/components/02.molecules/samaritan-prompt/SamaritanPromptMixed.vue +34 -10
- package/app/components/02.molecules/samaritan-prompt/stories/SamaritanPrompt.stories.ts +55 -4
- package/app/components/02.molecules/samaritan-prompt/stories/SamaritanPromptMixed.stories.ts +67 -7
- package/app/components/02.molecules/samaritan-prompt/tests/SamaritanPrompt.spec.ts +113 -29
- package/app/components/02.molecules/samaritan-prompt/tests/SamaritanPromptMixed.spec.ts +92 -45
- package/app/components/03.organisms/image-galleries/carousel-flip/CONSUMER-STYLING.md +82 -29
- package/app/components/03.organisms/image-galleries/carousel-flip/CarouselFlip.vue +176 -113
- package/app/components/03.organisms/image-galleries/carousel-flip/stories/CarouselFlip.stories.ts +153 -23
- package/app/components/03.organisms/image-galleries/carousel-flip/tests/CarouselFlip.spec.ts +119 -63
- 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/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +8 -8
- package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +4 -4
- package/app/components/03.organisms/site-header/CONSUMER-STYLING.md +41 -23
- package/app/components/03.organisms/site-header/SiteHeader.vue +16 -21
- package/app/components/03.organisms/site-header/stories/SiteHeader.stories.ts +138 -0
- package/app/components/03.organisms/site-header/tests/SiteHeader.spec.ts +53 -26
- package/app/components/03.organisms/site-header/tests/__snapshots__/SiteHeader.spec.ts.snap +2 -2
- package/app/types/components/qr-code.d.ts +12 -0
- package/app/utils/graphemes.ts +8 -0
- 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/01.atoms/animations/entry/.nuxt/app.config.mjs +0 -21
- package/app/components/01.atoms/animations/entry/.nuxt/components.d.ts +0 -66
- package/app/components/01.atoms/animations/entry/.nuxt/imports.d.ts +0 -30
- package/app/components/01.atoms/animations/entry/.nuxt/manifest/meta/test.json +0 -1
- package/app/components/01.atoms/animations/entry/.nuxt/types/app.config.d.ts +0 -35
- package/app/components/01.atoms/animations/entry/.nuxt/types/build.d.ts +0 -23
- package/app/components/01.atoms/animations/entry/.nuxt/types/builder-env.d.ts +0 -1
- package/app/components/01.atoms/animations/entry/.nuxt/types/components.d.ts +0 -71
- package/app/components/01.atoms/animations/entry/.nuxt/types/imports.d.ts +0 -386
- package/app/components/01.atoms/animations/entry/.nuxt/types/middleware.d.ts +0 -11
- package/app/components/01.atoms/animations/entry/.nuxt/types/modules.d.ts +0 -79
- package/app/components/01.atoms/animations/entry/.nuxt/types/nitro-layouts.d.ts +0 -17
- package/app/components/01.atoms/animations/entry/.nuxt/types/nitro-nuxt.d.ts +0 -64
- package/app/components/01.atoms/animations/entry/.nuxt/types/plugins.d.ts +0 -29
- package/app/components/01.atoms/animations/entry/.nuxt/types/runtime-config.d.ts +0 -32
- package/app/components/01.atoms/animations/entry/.nuxt/types/shared-imports.d.ts +0 -10
- package/app/components/01.atoms/animations/entry/.nuxt/types/vue-shim.d.ts +0 -0
- package/app/components/01.atoms/animations/entry/.nuxtrc +0 -1
- package/app/components/01.atoms/animations/entry/node_modules/.cache/nuxt/chrome-workspace.json +0 -1
- package/app/components/01.atoms/animations/entry/node_modules/.cache/vite/client/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- 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
- package/app/components/03.organisms/responsive-header/tests/.nuxt/app.config.mjs +0 -21
- package/app/components/03.organisms/responsive-header/tests/.nuxt/components.d.ts +0 -66
- package/app/components/03.organisms/responsive-header/tests/.nuxt/imports.d.ts +0 -30
- package/app/components/03.organisms/responsive-header/tests/.nuxt/manifest/meta/test.json +0 -1
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/app.config.d.ts +0 -35
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/build.d.ts +0 -23
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/builder-env.d.ts +0 -1
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/components.d.ts +0 -71
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/imports.d.ts +0 -386
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/middleware.d.ts +0 -11
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/modules.d.ts +0 -79
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-layouts.d.ts +0 -17
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-nuxt.d.ts +0 -64
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/plugins.d.ts +0 -29
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/runtime-config.d.ts +0 -32
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/shared-imports.d.ts +0 -10
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/vue-shim.d.ts +0 -0
- package/app/components/03.organisms/responsive-header/tests/node_modules/.cache/nuxt/chrome-workspace.json +0 -1
package/app/components/03.organisms/image-galleries/carousel-flip/tests/CarouselFlip.spec.ts
CHANGED
|
@@ -77,13 +77,6 @@ vi.mock("@vueuse/core", () => ({
|
|
|
77
77
|
}),
|
|
78
78
|
}));
|
|
79
79
|
|
|
80
|
-
// Mock Icon component
|
|
81
|
-
vi.mock("#imports", () => ({
|
|
82
|
-
useStyleClassPassthrough: vi.fn((classes) => ({
|
|
83
|
-
elementClasses: Array.isArray(classes) ? classes.join(" ") : classes || "",
|
|
84
|
-
})),
|
|
85
|
-
}));
|
|
86
|
-
|
|
87
80
|
describe("CarouselFlip", () => {
|
|
88
81
|
let wrapper: ReturnType<typeof mountSuspended>;
|
|
89
82
|
let component: CarouselFlipInstance;
|
|
@@ -137,7 +130,7 @@ describe("CarouselFlip", () => {
|
|
|
137
130
|
it("renders with required accessibility attributes", async () => {
|
|
138
131
|
await createWrapper();
|
|
139
132
|
|
|
140
|
-
const carousel = wrapper.find('
|
|
133
|
+
const carousel = wrapper.find('section.carousel-flip');
|
|
141
134
|
expect(carousel.exists()).toBe(true);
|
|
142
135
|
expect(carousel.attributes("aria-label")).toBe("Image carousel");
|
|
143
136
|
|
|
@@ -157,7 +150,7 @@ describe("CarouselFlip", () => {
|
|
|
157
150
|
it("renders all carousel items with correct slots", async () => {
|
|
158
151
|
await createWrapper();
|
|
159
152
|
|
|
160
|
-
const items = wrapper.findAll(".item");
|
|
153
|
+
const items = wrapper.findAll(".carousel-flip-item");
|
|
161
154
|
expect(items).toHaveLength(6);
|
|
162
155
|
|
|
163
156
|
for (let i = 0; i < 6; i++) {
|
|
@@ -170,7 +163,7 @@ describe("CarouselFlip", () => {
|
|
|
170
163
|
it("renders marker buttons for each item", async () => {
|
|
171
164
|
await createWrapper();
|
|
172
165
|
|
|
173
|
-
const markers = wrapper.findAll(".
|
|
166
|
+
const markers = wrapper.findAll(".carousel-flip-marker");
|
|
174
167
|
expect(markers).toHaveLength(6);
|
|
175
168
|
|
|
176
169
|
markers.forEach((marker: ReturnType<typeof wrapper.find>, index: number) => {
|
|
@@ -182,8 +175,8 @@ describe("CarouselFlip", () => {
|
|
|
182
175
|
it("renders navigation buttons with correct icons and labels", async () => {
|
|
183
176
|
await createWrapper();
|
|
184
177
|
|
|
185
|
-
const prevButton = wrapper.find('
|
|
186
|
-
const nextButton = wrapper.find('
|
|
178
|
+
const prevButton = wrapper.find('.carousel-flip-button-prev');
|
|
179
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
187
180
|
|
|
188
181
|
expect(prevButton.exists()).toBe(true);
|
|
189
182
|
expect(nextButton.exists()).toBe(true);
|
|
@@ -201,7 +194,7 @@ describe("CarouselFlip", () => {
|
|
|
201
194
|
carouselDataIds: customData,
|
|
202
195
|
});
|
|
203
196
|
|
|
204
|
-
const items = wrapper.findAll(".item");
|
|
197
|
+
const items = wrapper.findAll(".carousel-flip-item");
|
|
205
198
|
expect(items).toHaveLength(3);
|
|
206
199
|
expect(component.itemCount).toBe(3);
|
|
207
200
|
});
|
|
@@ -223,11 +216,11 @@ describe("CarouselFlip", () => {
|
|
|
223
216
|
// The transition speed is used in computed property transitionSpeedStr
|
|
224
217
|
});
|
|
225
218
|
|
|
226
|
-
it("
|
|
219
|
+
it("sets data-allow-overflow on the track when allowCarouselOverflow is on", async () => {
|
|
227
220
|
await createWrapper({ allowCarouselOverflow: true });
|
|
228
221
|
|
|
229
|
-
const container = wrapper.find(".
|
|
230
|
-
expect(container.
|
|
222
|
+
const container = wrapper.find(".carousel-flip-track");
|
|
223
|
+
expect(container.attributes("data-allow-overflow")).toBe("");
|
|
231
224
|
});
|
|
232
225
|
|
|
233
226
|
it("handles useFlipAnimation prop in calculations", async () => {
|
|
@@ -248,29 +241,29 @@ describe("CarouselFlip", () => {
|
|
|
248
241
|
it("shows controls container by default", async () => {
|
|
249
242
|
await createWrapper();
|
|
250
243
|
|
|
251
|
-
expect(wrapper.find(".controls
|
|
252
|
-
expect(wrapper.findAll(".
|
|
244
|
+
expect(wrapper.find(".carousel-flip-controls").exists()).toBe(true);
|
|
245
|
+
expect(wrapper.findAll(".carousel-flip-marker").length).toBe(6);
|
|
253
246
|
});
|
|
254
247
|
|
|
255
248
|
it("hides controls container when showControls is false", async () => {
|
|
256
249
|
await createWrapper({ showControls: false });
|
|
257
250
|
|
|
258
|
-
expect(wrapper.find(".controls
|
|
259
|
-
expect(wrapper.findAll(".
|
|
251
|
+
expect(wrapper.find(".carousel-flip-controls").exists()).toBe(false);
|
|
252
|
+
expect(wrapper.findAll(".carousel-flip-marker").length).toBe(0);
|
|
260
253
|
});
|
|
261
254
|
|
|
262
255
|
it("still renders navigation buttons when showControls is false", async () => {
|
|
263
256
|
await createWrapper({ showControls: false });
|
|
264
257
|
|
|
265
|
-
expect(wrapper.find('
|
|
266
|
-
expect(wrapper.find('
|
|
258
|
+
expect(wrapper.find('.carousel-flip-button-prev').exists()).toBe(true);
|
|
259
|
+
expect(wrapper.find('.carousel-flip-button-next').exists()).toBe(true);
|
|
267
260
|
});
|
|
268
261
|
|
|
269
262
|
it("navigation still works when showControls is false", async () => {
|
|
270
263
|
await createWrapper({ showControls: false });
|
|
271
264
|
await nextTick();
|
|
272
265
|
|
|
273
|
-
const nextButton = wrapper.find('
|
|
266
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
274
267
|
await nextButton.trigger("click");
|
|
275
268
|
await nextTick();
|
|
276
269
|
|
|
@@ -292,10 +285,10 @@ describe("CarouselFlip", () => {
|
|
|
292
285
|
jumpToItemLabel: "View look",
|
|
293
286
|
});
|
|
294
287
|
|
|
295
|
-
expect(wrapper.find('
|
|
288
|
+
expect(wrapper.find('section.carousel-flip').attributes("aria-label")).toBe("Featured looks");
|
|
296
289
|
expect(wrapper.find('[role="group"]').attributes("aria-label")).toBe("Look slides");
|
|
297
|
-
expect(wrapper.find('
|
|
298
|
-
expect(wrapper.find('
|
|
290
|
+
expect(wrapper.find('.carousel-flip-button-prev').exists()).toBe(true);
|
|
291
|
+
expect(wrapper.find('.carousel-flip-button-next').exists()).toBe(true);
|
|
299
292
|
expect(wrapper.find('[aria-label="View look 1"]').exists()).toBe(true);
|
|
300
293
|
});
|
|
301
294
|
|
|
@@ -310,8 +303,8 @@ describe("CarouselFlip", () => {
|
|
|
310
303
|
it("overrides prev/next icons via props", async () => {
|
|
311
304
|
await createWrapper({ prevIcon: "mdi:chevron-left", nextIcon: "mdi:chevron-right" });
|
|
312
305
|
|
|
313
|
-
const prevButton = wrapper.find('
|
|
314
|
-
const nextButton = wrapper.find('
|
|
306
|
+
const prevButton = wrapper.find('.carousel-flip-button-prev');
|
|
307
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
315
308
|
|
|
316
309
|
expect(prevButton.html()).toContain("i-mdi:chevron-left");
|
|
317
310
|
expect(nextButton.html()).toContain("i-mdi:chevron-right");
|
|
@@ -345,7 +338,7 @@ describe("CarouselFlip", () => {
|
|
|
345
338
|
await createWrapper();
|
|
346
339
|
await nextTick();
|
|
347
340
|
|
|
348
|
-
const nextButton = wrapper.find('
|
|
341
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
349
342
|
await nextButton.trigger("click");
|
|
350
343
|
await nextTick();
|
|
351
344
|
|
|
@@ -361,15 +354,16 @@ describe("CarouselFlip", () => {
|
|
|
361
354
|
expect(component.currentActiveIndex).toBe(0);
|
|
362
355
|
expect(component.displayActiveIndex).toBe(0);
|
|
363
356
|
|
|
364
|
-
const firstMarker = wrapper.find(".
|
|
365
|
-
expect(firstMarker.
|
|
357
|
+
const firstMarker = wrapper.find(".carousel-flip-marker");
|
|
358
|
+
expect(firstMarker.attributes("data-active")).toBe("");
|
|
359
|
+
expect(firstMarker.attributes("aria-current")).toBe("true");
|
|
366
360
|
});
|
|
367
361
|
|
|
368
362
|
it("sets up item order and z-index correctly", async () => {
|
|
369
363
|
await createWrapper();
|
|
370
364
|
await nextTick();
|
|
371
365
|
|
|
372
|
-
const items = wrapper.findAll(".item");
|
|
366
|
+
const items = wrapper.findAll(".carousel-flip-item");
|
|
373
367
|
// After initialization, items are reordered by the component
|
|
374
368
|
// Just verify that all items have order and z-index set
|
|
375
369
|
items.forEach((item: ReturnType<typeof wrapper.find>) => {
|
|
@@ -378,12 +372,12 @@ describe("CarouselFlip", () => {
|
|
|
378
372
|
});
|
|
379
373
|
});
|
|
380
374
|
|
|
381
|
-
it("
|
|
375
|
+
it("sets data-mounted after initialization", async () => {
|
|
382
376
|
await createWrapper();
|
|
383
377
|
await nextTick();
|
|
384
378
|
await nextTick(); // Wait for nextTick in component
|
|
385
379
|
|
|
386
|
-
expect(wrapper.
|
|
380
|
+
expect(wrapper.attributes("data-mounted")).toBe("");
|
|
387
381
|
});
|
|
388
382
|
|
|
389
383
|
it("marks carousel as initialized", async () => {
|
|
@@ -401,7 +395,7 @@ describe("CarouselFlip", () => {
|
|
|
401
395
|
});
|
|
402
396
|
|
|
403
397
|
it("navigates to next item when next button is clicked", async () => {
|
|
404
|
-
const nextButton = wrapper.find('
|
|
398
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
405
399
|
await nextButton.trigger("click");
|
|
406
400
|
await nextTick();
|
|
407
401
|
|
|
@@ -410,7 +404,7 @@ describe("CarouselFlip", () => {
|
|
|
410
404
|
});
|
|
411
405
|
|
|
412
406
|
it("navigates to previous item when previous button is clicked", async () => {
|
|
413
|
-
const prevButton = wrapper.find('
|
|
407
|
+
const prevButton = wrapper.find('.carousel-flip-button-prev');
|
|
414
408
|
await prevButton.trigger("click");
|
|
415
409
|
await nextTick();
|
|
416
410
|
|
|
@@ -423,7 +417,7 @@ describe("CarouselFlip", () => {
|
|
|
423
417
|
component.currentActiveIndex = 5;
|
|
424
418
|
await nextTick();
|
|
425
419
|
|
|
426
|
-
const nextButton = wrapper.find('
|
|
420
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
427
421
|
await nextButton.trigger("click");
|
|
428
422
|
await nextTick();
|
|
429
423
|
|
|
@@ -431,7 +425,7 @@ describe("CarouselFlip", () => {
|
|
|
431
425
|
});
|
|
432
426
|
|
|
433
427
|
it("wraps around when navigating before first item", async () => {
|
|
434
|
-
const prevButton = wrapper.find('
|
|
428
|
+
const prevButton = wrapper.find('.carousel-flip-button-prev');
|
|
435
429
|
await prevButton.trigger("click");
|
|
436
430
|
await nextTick();
|
|
437
431
|
|
|
@@ -439,7 +433,7 @@ describe("CarouselFlip", () => {
|
|
|
439
433
|
});
|
|
440
434
|
|
|
441
435
|
it("jumps to specific item when marker is clicked", async () => {
|
|
442
|
-
const thirdMarker = wrapper.findAll(".
|
|
436
|
+
const thirdMarker = wrapper.findAll(".carousel-flip-marker")[2];
|
|
443
437
|
await thirdMarker.trigger("click");
|
|
444
438
|
await nextTick();
|
|
445
439
|
|
|
@@ -448,17 +442,18 @@ describe("CarouselFlip", () => {
|
|
|
448
442
|
});
|
|
449
443
|
|
|
450
444
|
it("updates active marker visually", async () => {
|
|
451
|
-
const thirdMarker = wrapper.findAll(".
|
|
445
|
+
const thirdMarker = wrapper.findAll(".carousel-flip-marker")[2];
|
|
452
446
|
await thirdMarker.trigger("click");
|
|
453
447
|
await nextTick();
|
|
454
448
|
|
|
455
|
-
const markers = wrapper.findAll(".
|
|
456
|
-
expect(markers[0].
|
|
457
|
-
expect(markers[2].
|
|
449
|
+
const markers = wrapper.findAll(".carousel-flip-marker");
|
|
450
|
+
expect(markers[0].attributes("data-active")).toBeUndefined();
|
|
451
|
+
expect(markers[2].attributes("data-active")).toBe("");
|
|
452
|
+
expect(markers[2].attributes("aria-current")).toBe("true");
|
|
458
453
|
});
|
|
459
454
|
|
|
460
455
|
it("updates screen reader announcement when navigating", async () => {
|
|
461
|
-
const nextButton = wrapper.find('
|
|
456
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
462
457
|
await nextButton.trigger("click");
|
|
463
458
|
await nextTick();
|
|
464
459
|
|
|
@@ -467,12 +462,12 @@ describe("CarouselFlip", () => {
|
|
|
467
462
|
});
|
|
468
463
|
|
|
469
464
|
it("updates aria-current for active item", async () => {
|
|
470
|
-
const nextButton = wrapper.find('
|
|
465
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
471
466
|
await nextButton.trigger("click");
|
|
472
467
|
await nextTick();
|
|
473
468
|
|
|
474
|
-
const items = wrapper.findAll(".item");
|
|
475
|
-
expect(items[0].attributes("aria-current")).
|
|
469
|
+
const items = wrapper.findAll(".carousel-flip-item");
|
|
470
|
+
expect(items[0].attributes("aria-current")).toBeUndefined();
|
|
476
471
|
expect(items[1].attributes("aria-current")).toBe("true");
|
|
477
472
|
});
|
|
478
473
|
});
|
|
@@ -506,7 +501,7 @@ describe("CarouselFlip", () => {
|
|
|
506
501
|
});
|
|
507
502
|
|
|
508
503
|
it("ignores other keys", async () => {
|
|
509
|
-
const container = wrapper.find(".
|
|
504
|
+
const container = wrapper.find(".carousel-flip-track");
|
|
510
505
|
const initialIndex = component.currentActiveIndex;
|
|
511
506
|
|
|
512
507
|
await container.trigger("keydown", { key: "Space" });
|
|
@@ -557,16 +552,16 @@ describe("CarouselFlip", () => {
|
|
|
557
552
|
await nextTick();
|
|
558
553
|
});
|
|
559
554
|
|
|
560
|
-
it("
|
|
561
|
-
const items = wrapper.findAll(".item");
|
|
562
|
-
expect(items[0].
|
|
555
|
+
it("sets data-loaded after user interaction", async () => {
|
|
556
|
+
const items = wrapper.findAll(".carousel-flip-item");
|
|
557
|
+
expect(items[0].attributes("data-loaded")).toBeUndefined();
|
|
563
558
|
|
|
564
|
-
const nextButton = wrapper.find('
|
|
559
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
565
560
|
await nextButton.trigger("click");
|
|
566
561
|
await nextTick();
|
|
567
562
|
|
|
568
|
-
const updatedItems = wrapper.findAll(".item");
|
|
569
|
-
expect(updatedItems[0].
|
|
563
|
+
const updatedItems = wrapper.findAll(".carousel-flip-item");
|
|
564
|
+
expect(updatedItems[0].attributes("data-loaded")).toBe("");
|
|
570
565
|
});
|
|
571
566
|
|
|
572
567
|
it("calls reorderItems without animation during initial setup", async () => {
|
|
@@ -580,7 +575,7 @@ describe("CarouselFlip", () => {
|
|
|
580
575
|
// Test that navigation works which implicitly tests reorderItems is called
|
|
581
576
|
const initialIndex = component.currentActiveIndex;
|
|
582
577
|
|
|
583
|
-
const nextButton = wrapper.find('
|
|
578
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
584
579
|
await nextButton.trigger("click");
|
|
585
580
|
await nextTick();
|
|
586
581
|
|
|
@@ -594,30 +589,31 @@ describe("CarouselFlip", () => {
|
|
|
594
589
|
it("handles empty carousel data", async () => {
|
|
595
590
|
await createWrapper({ carouselDataIds: [] });
|
|
596
591
|
|
|
597
|
-
const items = wrapper.findAll(".item");
|
|
592
|
+
const items = wrapper.findAll(".carousel-flip-item");
|
|
598
593
|
expect(items).toHaveLength(0);
|
|
599
594
|
expect(component.itemCount).toBe(0);
|
|
600
595
|
|
|
601
|
-
const markers = wrapper.findAll(".
|
|
596
|
+
const markers = wrapper.findAll(".carousel-flip-marker");
|
|
602
597
|
expect(markers).toHaveLength(0);
|
|
603
598
|
});
|
|
604
599
|
|
|
605
600
|
it("handles single item carousel", async () => {
|
|
606
601
|
await createWrapper({ carouselDataIds: ["single-item"] });
|
|
607
602
|
|
|
608
|
-
const items = wrapper.findAll(".item");
|
|
603
|
+
const items = wrapper.findAll(".carousel-flip-item");
|
|
609
604
|
expect(items).toHaveLength(1);
|
|
610
605
|
|
|
611
|
-
|
|
612
|
-
expect(
|
|
613
|
-
expect(
|
|
606
|
+
expect(wrapper.findAll(".carousel-flip-marker")).toHaveLength(0);
|
|
607
|
+
expect(wrapper.find(".carousel-flip-button-prev").exists()).toBe(false);
|
|
608
|
+
expect(wrapper.find(".carousel-flip-button-next").exists()).toBe(false);
|
|
609
|
+
expect(items[0]!.attributes("aria-current")).toBe("true");
|
|
614
610
|
});
|
|
615
611
|
|
|
616
612
|
it("prevents navigation when carousel is not initialized", async () => {
|
|
617
613
|
await createWrapper();
|
|
618
614
|
component.carouselInitComplete = false;
|
|
619
615
|
|
|
620
|
-
const nextButton = wrapper.find('
|
|
616
|
+
const nextButton = wrapper.find('.carousel-flip-button-next');
|
|
621
617
|
await nextButton.trigger("click");
|
|
622
618
|
await nextTick();
|
|
623
619
|
|
|
@@ -655,7 +651,7 @@ describe("CarouselFlip", () => {
|
|
|
655
651
|
it("applies transition speed as CSS custom property", async () => {
|
|
656
652
|
await createWrapper({ transitionSpeed: 300 });
|
|
657
653
|
|
|
658
|
-
const items = wrapper.findAll(".item");
|
|
654
|
+
const items = wrapper.findAll(".carousel-flip-item");
|
|
659
655
|
if (items.length > 0) {
|
|
660
656
|
// CSS custom properties are handled by Vue's reactivity system
|
|
661
657
|
expect(component).toBeDefined();
|
|
@@ -672,4 +668,64 @@ describe("CarouselFlip", () => {
|
|
|
672
668
|
expect(component).toBeDefined();
|
|
673
669
|
});
|
|
674
670
|
});
|
|
671
|
+
|
|
672
|
+
describe("Worst-case data", () => {
|
|
673
|
+
it("mounts with no items without hanging, and renders no buttons or markers", async () => {
|
|
674
|
+
await createWrapper({ carouselDataIds: [] });
|
|
675
|
+
await nextTick();
|
|
676
|
+
expect(wrapper.findAll(".carousel-flip-item")).toHaveLength(0);
|
|
677
|
+
expect(wrapper.find(".carousel-flip-buttons").exists()).toBe(false);
|
|
678
|
+
expect(wrapper.find(".carousel-flip-controls").exists()).toBe(false);
|
|
679
|
+
expect(wrapper.find("[aria-live]").text()).toBe("");
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it("keeps the item count in step when carouselDataIds changes after mount", async () => {
|
|
683
|
+
await createWrapper({ carouselDataIds: ["item-1", "item-2"] });
|
|
684
|
+
expect(wrapper.findAll(".carousel-flip-marker")).toHaveLength(2);
|
|
685
|
+
|
|
686
|
+
await wrapper.setProps({ carouselDataIds: ["item-1", "item-2", "item-3", "item-4"] });
|
|
687
|
+
expect(wrapper.findAll(".carousel-flip-marker")).toHaveLength(4);
|
|
688
|
+
expect(wrapper.find("[aria-live]").text()).toContain("of 4");
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
it("resets to the first item when the active one is removed", async () => {
|
|
692
|
+
await createWrapper();
|
|
693
|
+
component.jumpToFrame(5);
|
|
694
|
+
await nextTick();
|
|
695
|
+
await wrapper.setProps({ carouselDataIds: ["item-1", "item-2"] });
|
|
696
|
+
await nextTick();
|
|
697
|
+
expect(component.currentActiveIndex).toBe(0);
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
it("renders items whose ids repeat without dropping one", async () => {
|
|
701
|
+
await createWrapper({ carouselDataIds: ["item-1", "item-1", "item-2"] });
|
|
702
|
+
expect(wrapper.findAll(".carousel-flip-item")).toHaveLength(3);
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
it("gives the controls bar no tab stop of its own", async () => {
|
|
706
|
+
await createWrapper();
|
|
707
|
+
expect(wrapper.find(".carousel-flip-controls").attributes("tabindex")).toBeUndefined();
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
it("replaces styleClassPassthrough classes when the prop changes", async () => {
|
|
711
|
+
await createWrapper({ styleClassPassthrough: ["first"] });
|
|
712
|
+
await wrapper.setProps({ styleClassPassthrough: ["second"] });
|
|
713
|
+
expect(wrapper.classes()).not.toContain("first");
|
|
714
|
+
expect(wrapper.classes()).toContain("second");
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
it("renders both buttons inside the buttons group in the track-edges layout", async () => {
|
|
718
|
+
await createWrapper({ buttonLayout: "track-edges" });
|
|
719
|
+
expect(wrapper.attributes("data-button-layout")).toBe("track-edges");
|
|
720
|
+
const group = wrapper.find(".carousel-flip-buttons");
|
|
721
|
+
expect(group.find(".carousel-flip-button-prev").exists()).toBe(true);
|
|
722
|
+
expect(group.find(".carousel-flip-button-next").exists()).toBe(true);
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it("names the prev and next buttons with their visually hidden text", async () => {
|
|
726
|
+
await createWrapper({ previousLabel: "Previous look", nextLabel: "Next look" });
|
|
727
|
+
expect(wrapper.find(".carousel-flip-button-prev").text()).toContain("Previous look");
|
|
728
|
+
expect(wrapper.find(".carousel-flip-button-next").text()).toContain("Next look");
|
|
729
|
+
});
|
|
730
|
+
});
|
|
675
731
|
});
|
|
@@ -8,12 +8,12 @@ exports[`NavigationItems > renders correct HTML structure with default props 1`]
|
|
|
8
8
|
<li class="overflow-navigation-item visible" style="--_main-navigation-item-width: 0px;" data-group-key="firstNav" data-local-index="2" role="none">
|
|
9
9
|
<div class="expanding-panel-classic overflow-navigation-details">
|
|
10
10
|
<details class="expanding-panel-classic-details" name="overflow-navigation-group">
|
|
11
|
-
<summary id="
|
|
11
|
+
<summary id="v-0-0-1-trigger" class="expanding-panel-classic-summary" aria-controls="v-0-0-1-content" aria-expanded="false">
|
|
12
12
|
<div class="label-wrapper"><span class="overflow-navigation-text" aria-label="UI Components submenu" role="menuitem" aria-haspopup="true">UI Components</span></div>
|
|
13
13
|
<div class="icon-wrapper"><span class="iconify i-mdi:chevron-down icon" aria-hidden="true"></span></div>
|
|
14
14
|
</summary>
|
|
15
15
|
</details>
|
|
16
|
-
<div id="
|
|
16
|
+
<div id="v-0-0-1-content" class="expanding-panel-classic-content" aria-labelledby="v-0-0-1-trigger" role="region">
|
|
17
17
|
<div class="inner">
|
|
18
18
|
<div class="overflow-navigation-sub-nav-inner">
|
|
19
19
|
<ul class="overflow-navigation-sub-nav-list">
|
|
@@ -41,12 +41,12 @@ exports[`ResponsiveHeader > renders correct HTML structure with default props 1`
|
|
|
41
41
|
<li class="overflow-navigation-item visible" data-group-key="firstNav" data-local-index="2" role="none">
|
|
42
42
|
<div class="expanding-panel-classic overflow-navigation-details">
|
|
43
43
|
<details class="expanding-panel-classic-details" name="overflow-navigation-group">
|
|
44
|
-
<summary id="
|
|
44
|
+
<summary id="v-0-0-1-trigger" class="expanding-panel-classic-summary" aria-controls="v-0-0-1-content" aria-expanded="false">
|
|
45
45
|
<div class="label-wrapper"><span class="overflow-navigation-text" aria-label="UI Components submenu" role="menuitem" aria-haspopup="true">UI Components</span></div>
|
|
46
46
|
<div class="icon-wrapper"><span class="iconify i-mdi:chevron-down icon" aria-hidden="true"></span></div>
|
|
47
47
|
</summary>
|
|
48
48
|
</details>
|
|
49
|
-
<div id="
|
|
49
|
+
<div id="v-0-0-1-content" class="expanding-panel-classic-content" aria-labelledby="v-0-0-1-trigger" role="region">
|
|
50
50
|
<div class="inner">
|
|
51
51
|
<div class="overflow-navigation-sub-nav-inner">
|
|
52
52
|
<ul class="overflow-navigation-sub-nav-list">
|
|
@@ -129,38 +129,38 @@ describe("ServiceDetail", () => {
|
|
|
129
129
|
|
|
130
130
|
it("defaults heroImageVariant to full, heroContentVariant and bodyVariant to content", async () => {
|
|
131
131
|
const wrapper = await mountSuspended(ServiceDetail, { props: { serviceData: mockService } });
|
|
132
|
-
expect(wrapper.find(".service-detail__hero-image-row").
|
|
133
|
-
expect(wrapper.find(".service-detail__hero-overlay").
|
|
134
|
-
expect(wrapper.find(".service-detail__body-row").
|
|
132
|
+
expect(wrapper.find(".service-detail__hero-image-row").attributes("data-variant")).toBe("full");
|
|
133
|
+
expect(wrapper.find(".service-detail__hero-overlay").attributes("data-variant")).toBe("content");
|
|
134
|
+
expect(wrapper.find(".service-detail__body-row").attributes("data-variant")).toBe("content");
|
|
135
135
|
});
|
|
136
136
|
|
|
137
137
|
it("applies the heroImageVariant prop to the hero image PageRow", async () => {
|
|
138
138
|
const wrapper = await mountSuspended(ServiceDetail, {
|
|
139
139
|
props: { serviceData: mockService, heroImageVariant: "popout" },
|
|
140
140
|
});
|
|
141
|
-
expect(wrapper.find(".service-detail__hero-image-row").
|
|
141
|
+
expect(wrapper.find(".service-detail__hero-image-row").attributes("data-variant")).toBe("popout");
|
|
142
142
|
});
|
|
143
143
|
|
|
144
144
|
it("applies the heroContentVariant prop to the hero content PageRow", async () => {
|
|
145
145
|
const wrapper = await mountSuspended(ServiceDetail, {
|
|
146
146
|
props: { serviceData: mockService, heroContentVariant: "inset-content" },
|
|
147
147
|
});
|
|
148
|
-
expect(wrapper.find(".service-detail__hero-overlay").
|
|
148
|
+
expect(wrapper.find(".service-detail__hero-overlay").attributes("data-variant")).toBe("inset-content");
|
|
149
149
|
});
|
|
150
150
|
|
|
151
151
|
it("applies the bodyVariant prop to the body PageRow", async () => {
|
|
152
152
|
const wrapper = await mountSuspended(ServiceDetail, {
|
|
153
153
|
props: { serviceData: mockService, bodyVariant: "popout" },
|
|
154
154
|
});
|
|
155
|
-
expect(wrapper.find(".service-detail__body-row").
|
|
155
|
+
expect(wrapper.find(".service-detail__body-row").attributes("data-variant")).toBe("popout");
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
it("defaults finalCtaVariant to content and applies the prop to the final CTA PageRow", async () => {
|
|
159
159
|
const wrapper = await mountSuspended(ServiceDetail, { props: { serviceData: mockService } });
|
|
160
|
-
expect(wrapper.find(".service-detail__final-cta-row").
|
|
160
|
+
expect(wrapper.find(".service-detail__final-cta-row").attributes("data-variant")).toBe("content");
|
|
161
161
|
|
|
162
162
|
await wrapper.setProps({ finalCtaVariant: "full" });
|
|
163
|
-
expect(wrapper.find(".service-detail__final-cta-row").
|
|
163
|
+
expect(wrapper.find(".service-detail__final-cta-row").attributes("data-variant")).toBe("full");
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
// ─── Service data ───────────────────────────────────────────────────────
|
|
@@ -4,10 +4,10 @@ exports[`ServiceDetail > renders correct HTML structure 1`] = `
|
|
|
4
4
|
"<div class="service-detail">
|
|
5
5
|
<div class="grid-stack service-detail__hero">
|
|
6
6
|
<div class="grid-stack__layer">
|
|
7
|
-
<div class="page-row
|
|
7
|
+
<div class="page-row service-detail__hero-image-row" data-variant="full"><img width="1920" height="1080" data-nuxt-img="" srcset="/_ipx/s_1920x1080/images/balayage.jpg 1x, /_ipx/s_3840x2160/images/balayage.jpg 2x" alt="Balayage" loading="eager" fetchpriority="high" class="service-detail__hero-image" src="/_ipx/s_1920x1080/images/balayage.jpg"></div>
|
|
8
8
|
</div>
|
|
9
9
|
<div class="grid-stack__layer">
|
|
10
|
-
<div class="page-row
|
|
10
|
+
<div class="page-row service-detail__hero-overlay" data-variant="content">
|
|
11
11
|
<div class="service-detail__hero-content">
|
|
12
12
|
<nav class="breadcrumb service-detail__breadcrumb" aria-label="Breadcrumb">
|
|
13
13
|
<ol class="breadcrumb__list">
|
|
@@ -26,7 +26,7 @@ exports[`ServiceDetail > renders correct HTML structure 1`] = `
|
|
|
26
26
|
</div>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
|
-
<div class="page-row
|
|
29
|
+
<div class="page-row service-detail__body-row" data-variant="content">
|
|
30
30
|
<div class="service-detail__body">
|
|
31
31
|
<div class="service-detail__main">
|
|
32
32
|
<p class="page-body-normal">Balayage is a freehand hair colouring technique that creates a soft gradient.</p>
|
|
@@ -95,7 +95,7 @@ exports[`ServiceDetail > renders correct HTML structure 1`] = `
|
|
|
95
95
|
</aside>
|
|
96
96
|
</div>
|
|
97
97
|
</div>
|
|
98
|
-
<div class="page-row
|
|
98
|
+
<div class="page-row service-detail__final-cta-row" data-variant="content">
|
|
99
99
|
<div class="service-detail__final-cta">
|
|
100
100
|
<div class="service-detail__final-cta-copy">
|
|
101
101
|
<h2 class="hero-text" data-font-size="subheading" data-axis="horizontal">
|
|
@@ -2,43 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
## Public token API
|
|
4
4
|
|
|
5
|
-
All `--site-header-*` tokens style the
|
|
6
|
-
|
|
7
|
-
component itself.
|
|
5
|
+
All `--site-header-*` tokens style the header row. They're the stable override surface: set them
|
|
6
|
+
at any scope without touching the component itself.
|
|
8
7
|
|
|
9
8
|
| Token | Default | Controls |
|
|
10
9
|
|---|---|---|
|
|
11
10
|
| `--site-header-gap` | `2.4rem` | Gap between the branding column and the nav column |
|
|
11
|
+
| `--site-header-branding-max-width` | `50%` | Widest the branding column may grow (`fit-content()`); past it, the branding wraps |
|
|
12
12
|
| `--site-header-padding-inline` | `2.4rem` | Horizontal padding on `.site-header` |
|
|
13
13
|
| `--site-header-padding-block` | `0px` | Vertical padding on `.site-header` |
|
|
14
|
-
| `--site-header-bg` | `transparent` | Background colour of `.site-header` |
|
|
14
|
+
| `--site-header-bg` | `transparent` | Background colour of `.site-header` (the content column, not the full-width row) |
|
|
15
15
|
| `--site-header-position` | `static` | Set to `sticky` to pin the header, paired with `--site-header-sticky-offset` |
|
|
16
16
|
| `--site-header-sticky-offset` | `0px` | `top` offset when `--site-header-position: sticky` |
|
|
17
|
-
| `--site-header-z-index` | `9` | Stacking order of
|
|
17
|
+
| `--site-header-z-index` | `9` | Stacking order of the row |
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
`
|
|
19
|
+
> Changed 2026-10-21: `--site-header-position`, `--site-header-sticky-offset` and
|
|
20
|
+
> `--site-header-z-index` now apply to the outer row (`.site-header-row`, the `PageRow`) instead of
|
|
21
|
+
> the inner `<header>`. On the inner header, `sticky` never stuck: its parent is exactly its own
|
|
22
|
+
> height, so there was nowhere for it to move. The branding column was `auto` and is now capped by
|
|
23
|
+
> `--site-header-branding-max-width`, so a long brand can't squeeze the nav to nothing.
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
- `navStyleClassPassthrough` → forwarded to the nested `ResponsiveHeader` (targets its own
|
|
24
|
-
`--responsive-header-*` and `--overflow-nav-*` tokens — see
|
|
25
|
-
[../../responsive-header/CONSUMER-STYLING.md](../../responsive-header/CONSUMER-STYLING.md) if present,
|
|
26
|
-
or the `ResponsiveHeader` skill doc)
|
|
25
|
+
No private `--_` tokens.
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
`SiteHeader` composes `PageRow` + `SkipLinks` + `ResponsiveHeader`. Two separate
|
|
28
|
+
`styleClassPassthrough` props reach the composed components independently:
|
|
29
|
+
|
|
30
|
+
- `styleClassPassthrough` → the root `PageRow` (set `--site-header-*` tokens there)
|
|
31
|
+
- `navStyleClassPassthrough` → the nested `ResponsiveHeader` (its own `--responsive-header-*` and
|
|
32
|
+
`--overflow-nav-*` tokens; see `ResponsiveHeader`'s `CONSUMER-STYLING.md`)
|
|
33
|
+
|
|
34
|
+
## State hooks
|
|
35
|
+
|
|
36
|
+
No state attributes. Element classes:
|
|
37
|
+
|
|
38
|
+
| Class | Element |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `.site-header-row` | Root `PageRow` (with `.page-row` and the variant class); carries the sticky position |
|
|
41
|
+
| `.site-header` | The `<header>` grid: branding column + nav column |
|
|
42
|
+
| `.site-header-home-nav` | The branding `<nav>` wrapping `SkipLinks` and the `#branding` slot |
|
|
43
|
+
|
|
44
|
+
> Changed 2026-10-21: `.site-header-home-nav` was `.home-navigation`, and `.site-header-row` is new.
|
|
29
45
|
|
|
30
46
|
## Global theming
|
|
31
47
|
|
|
32
|
-
|
|
33
|
-
on a scope class matching the one passed via `styleClassPassthrough`:
|
|
48
|
+
Set tokens on the scope class passed via `styleClassPassthrough`:
|
|
34
49
|
|
|
35
50
|
```css
|
|
36
|
-
/* assets/styles/setup/07.components/site-header.css */
|
|
37
51
|
.header {
|
|
38
52
|
--site-header-gap: 1.6rem;
|
|
39
53
|
--site-header-padding-inline: 1.2rem;
|
|
40
54
|
--site-header-position: sticky;
|
|
41
|
-
--site-header-bg:
|
|
55
|
+
--site-header-bg: var(--theme-surface-subtle);
|
|
42
56
|
}
|
|
43
57
|
```
|
|
44
58
|
|
|
@@ -50,8 +64,6 @@ on a scope class matching the one passed via `styleClassPassthrough`:
|
|
|
50
64
|
</SiteHeader>
|
|
51
65
|
```
|
|
52
66
|
|
|
53
|
-
---
|
|
54
|
-
|
|
55
67
|
## Local overrides
|
|
56
68
|
|
|
57
69
|
Set the tokens above on an element you own (a page or section class, or a class added with
|
|
@@ -60,12 +72,18 @@ Set the tokens above on an element you own (a page or section class, or a class
|
|
|
60
72
|
`<style scoped>`, tokens set on your element still work, but selectors that reach inside the component need
|
|
61
73
|
`:deep()`. Patterns and examples: `.claude/skills/component-local-style-override.md`.
|
|
62
74
|
|
|
63
|
-
|
|
75
|
+
**Caveat:** sticky positioning needs the row's own parent to be taller than the row (normally the
|
|
76
|
+
page layout) and no `overflow` other than `visible` on the ancestors between it and the scroller.
|
|
77
|
+
|
|
78
|
+
## Class passthrough
|
|
79
|
+
|
|
80
|
+
`style-class-passthrough` adds classes to the root `PageRow`; `nav-style-class-passthrough` to
|
|
81
|
+
`ResponsiveHeader`'s root.
|
|
64
82
|
|
|
65
83
|
## Notes
|
|
66
84
|
|
|
67
85
|
- The branding markup (logo image, wordmark, etc.) is entirely consumer-authored via the
|
|
68
|
-
`#branding` slot
|
|
86
|
+
`#branding` slot; `SiteHeader` has no opinion on brand styling. Long branding wraps
|
|
87
|
+
(`overflow-wrap: anywhere`) once it reaches `--site-header-branding-max-width`.
|
|
69
88
|
- `SkipLinks`' hardcoded anchors (`#main-content`, `#footer-content`) expect the consuming
|
|
70
|
-
layout's main/footer regions to carry those `id`s
|
|
71
|
-
|
|
89
|
+
layout's main/footer regions to carry those `id`s.
|