srcdev-nuxt-components 9.4.9 → 9.4.11
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/commands/migrate-component.md +38 -6
- package/.claude/component-ledger/audit.json +1 -1
- package/.claude/component-ledger/build.mjs +26 -0
- package/.claude/component-ledger/output.html +8 -3
- package/.claude/component-ledger/template.html +7 -2
- package/.claude/skills/component-dynamic-slots.md +1 -1
- package/.claude/skills/components/alert-mask-core.md +84 -0
- package/.claude/skills/components/canvas-switcher.md +1 -1
- package/.claude/skills/components/carousel-flip.md +112 -168
- package/.claude/skills/components/container-glow.md +123 -0
- package/.claude/skills/components/dashboard-quad-grid.md +98 -0
- package/.claude/skills/components/dashboard-stats-grid.md +103 -0
- package/.claude/skills/components/display-theme-switch.md +60 -0
- package/.claude/skills/components/glowing-border.md +66 -0
- package/.claude/skills/components/input-number-core.md +214 -0
- package/.claude/skills/components/input-range-core.md +142 -0
- package/.claude/skills/components/input-select-core.md +151 -0
- package/.claude/skills/components/input-text-core.md +16 -0
- package/.claude/skills/components/input-textarea-core.md +154 -0
- package/.claude/skills/components/section-parallax.md +32 -10
- package/.claude/skills/components/select-menu.md +195 -0
- package/.claude/skills/components/toggle-switch-core.md +150 -0
- package/.claude/skills/components/ui-block-decorated.md +56 -0
- package/.claude/skills/components/wipe-away-vertical.md +98 -0
- package/.claude/skills/icon-sets.md +1 -1
- package/.claude/skills/index.md +15 -1
- package/.vscode/css-custom-data.json +38 -0
- package/.vscode/settings.json +3 -0
- package/.vscode/srcdev-component-alert-mask-core.code-snippets +31 -0
- package/.vscode/srcdev-component-carousel-flip.code-snippets +41 -0
- package/.vscode/srcdev-component-container-glow.code-snippets +33 -0
- package/.vscode/srcdev-component-dashboard-quad-grid.code-snippets +36 -0
- package/.vscode/srcdev-component-dashboard-stats-grid.code-snippets +35 -0
- package/.vscode/srcdev-component-display-theme-switch.code-snippets +27 -0
- package/.vscode/srcdev-component-glowing-border.code-snippets +23 -0
- package/.vscode/srcdev-component-input-number.code-snippets +65 -0
- package/.vscode/srcdev-component-input-range.code-snippets +85 -0
- package/.vscode/srcdev-component-input-select.code-snippets +42 -0
- package/.vscode/srcdev-component-input-textarea.code-snippets +40 -0
- package/.vscode/srcdev-component-section-parallax.code-snippets +27 -0
- package/.vscode/srcdev-component-select-menu.code-snippets +65 -0
- package/.vscode/srcdev-component-toggle-switch.code-snippets +49 -0
- package/.vscode/srcdev-component-ui-block-decorated.code-snippets +24 -0
- package/.vscode/srcdev-component-wipe-away-vertical.code-snippets +25 -0
- package/README.md +18 -37
- package/app/components/{alert-mask → 01.atoms/alert-mask}/AlertMaskCore.vue +16 -17
- package/app/components/01.atoms/alert-mask/CONSUMER-STYLING.md +46 -0
- package/app/components/01.atoms/alert-mask/stories/AlertMaskCore.stories.ts +79 -0
- package/app/components/{alert-mask → 01.atoms/alert-mask}/tests/AlertMaskCore.spec.ts +10 -10
- package/app/components/01.atoms/animations/container-glow/CONSUMER-STYLING.md +86 -0
- package/app/components/01.atoms/animations/container-glow/ContainerGlow.vue +299 -0
- package/app/components/01.atoms/animations/container-glow/stories/ContainerGlow.stories.ts +175 -0
- package/app/components/01.atoms/animations/container-glow/tests/ContainerGlow.spec.ts +131 -0
- package/app/components/01.atoms/animations/container-glow/tests/__snapshots__/ContainerGlow.spec.ts.snap +14 -0
- package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +61 -0
- package/app/components/01.atoms/animations/glowing-border/GlowingBorder.vue +132 -0
- package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +165 -0
- package/app/components/01.atoms/animations/glowing-border/tests/GlowingBorder.spec.ts +94 -0
- package/app/components/01.atoms/animations/glowing-border/tests/__snapshots__/GlowingBorder.spec.ts.snap +3 -0
- package/app/components/01.atoms/animations/marquee-scroller/CONSUMER-STYLING.md +1 -1
- package/app/components/01.atoms/animations/marquee-scroller/MarqueeScroller.vue +1 -1
- package/app/components/01.atoms/animations/marquee-scroller/stories/MarqueeScroller.stories.ts +1 -1
- package/app/components/01.atoms/animations/section-parallax/CONSUMER-STYLING.md +44 -0
- package/app/components/{parallax → 01.atoms/animations/section-parallax}/SectionParallax.vue +11 -10
- package/app/components/01.atoms/animations/section-parallax/stories/SectionParallax.stories.ts +98 -0
- package/app/components/01.atoms/animations/section-parallax/tests/SectionParallax.spec.ts +102 -0
- package/app/components/01.atoms/animations/section-parallax/tests/__snapshots__/SectionParallax.spec.ts.snap +9 -0
- package/app/components/01.atoms/animations/view-timeline/CONSUMER-STYLING.md +70 -0
- package/app/components/01.atoms/animations/view-timeline/WipeAwayVertical.vue +181 -0
- package/app/components/01.atoms/animations/view-timeline/stories/WipeAwayVertical.stories.ts +269 -0
- package/app/components/01.atoms/animations/view-timeline/tests/WipeAwayVertical.spec.ts +127 -0
- package/app/components/01.atoms/canvas-switcher/CONSUMER-STYLING.md +3 -3
- package/app/components/01.atoms/canvas-switcher/CanvasSwitcher.vue +2 -2
- package/app/components/01.atoms/canvas-switcher/stories/CanvasSwitcher.stories.ts +1 -1
- package/app/components/01.atoms/card/CardCore.vue +4 -4
- package/app/components/01.atoms/clipped-panel/CONSUMER-STYLING.md +2 -2
- package/app/components/01.atoms/clipped-panel/ClippedPanel.vue +2 -2
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/CONSUMER-STYLING.md +42 -0
- package/app/components/{layout-grids/LayoutGridA.vue → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/DashboardQuadGrid.vue} +23 -13
- package/app/components/{layout-grids/stories/LayoutGridA.stories.ts → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/stories/DashboardQuadGrid.stories.ts} +151 -23
- package/app/components/{layout-grids/tests/LayoutGridA.spec.ts → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/tests/DashboardQuadGrid.spec.ts} +25 -25
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/CONSUMER-STYLING.md +41 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/DashboardStatsGrid.vue +133 -0
- package/app/components/{layout-grids/stories/LayoutGridB.stories.ts → 01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/stories/DashboardStatsGrid.stories.ts} +24 -24
- package/app/components/{layout-grids/tests/LayoutGridB.spec.ts → 01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/tests/DashboardStatsGrid.spec.ts} +23 -23
- package/app/components/01.atoms/display-tooltip/CONSUMER-STYLING.md +8 -8
- package/app/components/01.atoms/display-tooltip/DisplayTooltip.vue +8 -8
- package/app/components/01.atoms/pop-over/CONSUMER-STYLING.md +6 -6
- package/app/components/01.atoms/pop-over/PopOver.vue +6 -6
- package/app/components/01.atoms/ui-block-decorated/CONSUMER-STYLING.md +39 -0
- package/app/components/01.atoms/ui-block-decorated/UiBlockDecorated.vue +141 -0
- package/app/components/01.atoms/ui-block-decorated/stories/UiBlockDecorated.stories.ts +148 -0
- package/app/components/01.atoms/ui-block-decorated/tests/UiBlockDecorated.spec.ts +80 -0
- package/app/components/01.atoms/ui-block-decorated/tests/__snapshots__/UiBlockDecorated.spec.ts.snap +3 -0
- package/app/components/02.molecules/alert-masked-content/tests/AlertMaskedContent.spec.ts +6 -6
- package/app/components/02.molecules/display-theme-switch/CONSUMER-STYLING.md +34 -0
- package/app/components/02.molecules/display-theme-switch/DisplayThemeSwitch.vue +102 -0
- package/app/components/02.molecules/display-theme-switch/stories/DisplayThemeSwitch.stories.ts +115 -0
- package/app/components/02.molecules/display-theme-switch/tests/DisplayThemeSwitch.spec.ts +86 -0
- package/app/components/02.molecules/navigation/deep-expanding-menu/CONSUMER-STYLING.md +1 -1
- package/app/components/02.molecules/navigation/deep-expanding-menu/DeepExpandingMenu.vue +1 -1
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/CONSUMER-STYLING.md +1 -1
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/DeepExpandingMenuClassic.vue +1 -1
- package/app/components/02.molecules/select-menu/CONSUMER-STYLING.md +168 -0
- package/app/components/02.molecules/select-menu/SelectMenu.vue +384 -0
- package/app/components/02.molecules/select-menu/stories/SelectMenu.stories.ts +359 -0
- package/app/components/02.molecules/select-menu/tests/SelectMenu.spec.ts +476 -0
- package/app/components/02.molecules/select-menu/tests/__snapshots__/SelectMenu.spec.ts.snap +42 -0
- package/app/components/03.organisms/image-galleries/carousel-flip/CONSUMER-STYLING.md +46 -0
- package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/CarouselFlip.vue +63 -42
- package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/stories/CarouselFlip.stories.ts +114 -29
- package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/tests/CarouselFlip.spec.ts +80 -0
- package/app/components/05.forms/form-field/FormField.vue +1 -1
- package/app/components/05.forms/form-wrapper/FormWrapper.vue +1 -1
- package/app/components/05.forms/input-button/CONSUMER-STYLING.md +2 -7
- package/app/components/05.forms/input-button/InputButtonCore.vue +1 -5
- package/app/components/05.forms/input-number/CONSUMER-STYLING.md +26 -6
- package/app/components/05.forms/input-number/InputNumberCore.vue +125 -85
- package/app/components/05.forms/input-number/stories/InputNumberCore.stories.ts +92 -0
- package/app/components/05.forms/input-number/tests/InputNumberCore.spec.ts +119 -0
- package/app/components/05.forms/input-number/variants/InputNumberDefault.vue +30 -22
- package/app/components/05.forms/input-number/variants/stories/InputNumberDefault.stories.ts +123 -0
- package/app/components/05.forms/input-number/variants/tests/InputNumberDefault.spec.ts +143 -0
- package/app/components/05.forms/input-range/CONSUMER-STYLING.md +45 -0
- package/app/components/05.forms/input-range/InputRangeCore.vue +79 -143
- package/app/components/05.forms/input-range/stories/InputRangeCore.stories.ts +126 -0
- package/app/components/05.forms/input-range/tests/InputRangeCore.spec.ts +124 -0
- package/app/components/05.forms/input-range/variants/InputRangeDefault.vue +9 -32
- package/app/components/05.forms/input-range/variants/stories/InputRangeDefault.stories.ts +108 -0
- package/app/components/05.forms/input-range/variants/tests/InputRangeDefault.spec.ts +140 -0
- package/app/components/05.forms/input-select/InputSelectCore.vue +23 -30
- package/app/components/05.forms/input-select/stories/InputSelectCore.stories.ts +2 -2
- package/app/components/05.forms/input-select/stories/InputSelectWithLabel.stories.ts +115 -0
- package/app/components/05.forms/input-select/tests/InputSelectCore.spec.ts +129 -0
- package/app/components/05.forms/input-select/variants/InputSelectWithLabel.vue +1 -1
- package/app/components/05.forms/input-select/variants/tests/InputSelectWithLabel.spec.ts +87 -0
- package/app/components/05.forms/input-text/tests/InputTextAsNumberWithLabel.spec.ts +14 -0
- package/app/components/05.forms/input-text/variants/InputTextAsNumberWithLabel.vue +1 -1
- package/app/components/05.forms/input-textarea/CONSUMER-STYLING.md +55 -0
- package/app/components/05.forms/input-textarea/InputTextareaCore.vue +24 -16
- package/app/components/05.forms/input-textarea/stories/InputTextareaWithLabel.stories.ts +1 -1
- package/app/components/05.forms/input-textarea/tests/InputTextareaCore.spec.ts +119 -0
- package/app/components/05.forms/input-textarea/variants/InputTextareaWithLabel.vue +3 -13
- package/app/components/05.forms/input-textarea/variants/tests/InputTextareaWithLabel.spec.ts +93 -0
- package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +220 -0
- package/app/components/05.forms/toggle-switch/CONSUMER-STYLING.md +29 -0
- package/app/components/05.forms/toggle-switch/ToggleSwitchCore.vue +120 -119
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchCore.stories.ts +1 -1
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabel.stories.ts +1 -1
- package/app/components/05.forms/toggle-switch/stories/ToggleSwitchWithLabelInline.stories.ts +2 -2
- package/app/components/05.forms/toggle-switch/tests/ToggleSwitchCore.spec.ts +101 -0
- package/app/components/05.forms/toggle-switch/variants/ToggleSwitchWithLabel.vue +3 -5
- package/app/components/05.forms/toggle-switch/variants/ToggleSwitchWithLabelInline.vue +1 -2
- package/app/components/05.forms/toggle-switch/variants/tests/ToggleSwitchWithLabel.spec.ts +77 -0
- package/app/components/05.forms/toggle-switch/variants/tests/ToggleSwitchWithLabelInline.spec.ts +66 -0
- package/app/components/05.forms/triple-toggle-switch/CONSUMER-STYLING.md +20 -6
- package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitchCore.vue +12 -10
- package/app/types/components/container-glow.d.ts +8 -0
- package/app/types/components/index.ts +2 -1
- package/app/types/components/select-menu.d.ts +5 -0
- package/package.json +3 -2
- package/.vscode/srcdev-component-toggle-switch-core.code-snippets +0 -13
- package/app/components/05.forms/input-range-fancy/InputRangeFancyCore.vue +0 -426
- package/app/components/05.forms/input-range-fancy/InputRangeFancyWithLabel.vue +0 -94
- package/app/components/carousels/CarouselBasic.vue +0 -317
- package/app/components/carousels/CarouselInfinite.vue +0 -358
- package/app/components/container-glow/ContainerGlowCore.vue +0 -296
- package/app/components/container-glow/stories/ContainerGlowCore.stories.ts +0 -337
- package/app/components/display-theme-switch/DisplayThemeSwitch.vue +0 -189
- package/app/components/display-theme-switch/stories/DisplayThemeSwitch.stories.ts +0 -154
- package/app/components/glowing-border/GlowingBorder.vue +0 -142
- package/app/components/glowing-border/stories/GlowingBorder.stories.ts +0 -141
- package/app/components/layout-grids/LayoutGridB.vue +0 -134
- package/app/components/test-storybook/TestStorybook.vue +0 -49
- package/app/components/test-storybook/stories/TestStorybook.stories.ts +0 -28
- package/app/components/ui-block-decorated/UiBlockDecorated.vue +0 -167
- package/app/components/view-timeline/WipeAwayVertical.vue +0 -180
- package/app/types/components/carousel-basic.d.ts +0 -19
package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/CarouselFlip.vue
RENAMED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
:class="[elementClasses]"
|
|
6
6
|
:data-button-layout="buttonLayout"
|
|
7
7
|
role="region"
|
|
8
|
-
aria-label="
|
|
8
|
+
:aria-label="ariaLabel"
|
|
9
9
|
>
|
|
10
10
|
<div aria-live="polite" aria-atomic="true" class="sr-only">
|
|
11
|
-
|
|
11
|
+
{{ props.itemPositionAnnouncement(currentActiveIndex + 1, itemCount) }}
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
14
|
<div
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
class="item-container"
|
|
18
18
|
:class="{ 'allow-overflow': allowCarouselOverflow }"
|
|
19
19
|
role="group"
|
|
20
|
-
aria-label="
|
|
20
|
+
:aria-label="itemsAriaLabel"
|
|
21
21
|
>
|
|
22
22
|
<div
|
|
23
23
|
v-for="(item, index) in carouselDataIds"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
<button
|
|
40
40
|
class="btn-marker"
|
|
41
41
|
:class="[{ active: displayActiveIndex === index - 1 }]"
|
|
42
|
-
:aria-label="
|
|
42
|
+
:aria-label="`${jumpToItemLabel} ${index}`"
|
|
43
43
|
@click.prevent="jumpToFrame(index - 1)"
|
|
44
44
|
></button>
|
|
45
45
|
</li>
|
|
@@ -48,16 +48,15 @@
|
|
|
48
48
|
</div>
|
|
49
49
|
|
|
50
50
|
<div class="buttons-container">
|
|
51
|
-
<button
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@click.prevent="actionPrevious()"
|
|
56
|
-
>
|
|
57
|
-
<Icon name="ic:outline-keyboard-arrow-left" class="arrows-icon" />
|
|
51
|
+
<button type="button" class="btn-action btn-prev" :aria-label="previousLabel" @click.prevent="actionPrevious()">
|
|
52
|
+
<slot name="prev-icon">
|
|
53
|
+
<Icon :name="prevIcon" class="arrows-icon" />
|
|
54
|
+
</slot>
|
|
58
55
|
</button>
|
|
59
|
-
<button type="button" class="btn-action btn-next" aria-label="
|
|
60
|
-
<
|
|
56
|
+
<button type="button" class="btn-action btn-next" :aria-label="nextLabel" @click.prevent="actionNext()">
|
|
57
|
+
<slot name="next-icon">
|
|
58
|
+
<Icon :name="nextIcon" class="arrows-icon" />
|
|
59
|
+
</slot>
|
|
61
60
|
</button>
|
|
62
61
|
</div>
|
|
63
62
|
</section>
|
|
@@ -75,6 +74,14 @@ interface Props {
|
|
|
75
74
|
useSpringEffect?: boolean;
|
|
76
75
|
buttonLayout?: "sides" | "controls-flanking" | "controls-grouped-right" | "overlay";
|
|
77
76
|
showControls?: boolean;
|
|
77
|
+
ariaLabel?: string;
|
|
78
|
+
itemsAriaLabel?: string;
|
|
79
|
+
previousLabel?: string;
|
|
80
|
+
nextLabel?: string;
|
|
81
|
+
jumpToItemLabel?: string;
|
|
82
|
+
itemPositionAnnouncement?: (current: number, total: number) => string;
|
|
83
|
+
prevIcon?: string;
|
|
84
|
+
nextIcon?: string;
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -86,8 +93,17 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
86
93
|
useSpringEffect: false,
|
|
87
94
|
buttonLayout: "sides",
|
|
88
95
|
showControls: true,
|
|
96
|
+
ariaLabel: "Image carousel",
|
|
97
|
+
itemsAriaLabel: "Carousel items",
|
|
98
|
+
previousLabel: "Go to previous item",
|
|
99
|
+
nextLabel: "Go to next item",
|
|
100
|
+
jumpToItemLabel: "Jump to item",
|
|
101
|
+
itemPositionAnnouncement: (current: number, total: number) => `Item ${current} of ${total}`,
|
|
102
|
+
prevIcon: "ic:outline-keyboard-arrow-left",
|
|
103
|
+
nextIcon: "ic:outline-keyboard-arrow-right",
|
|
89
104
|
});
|
|
90
105
|
|
|
106
|
+
|
|
91
107
|
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
92
108
|
|
|
93
109
|
const carouselWrapperRef = ref<HTMLDivElement | null>(null);
|
|
@@ -107,19 +123,11 @@ const transitionSpeedStr = props.transitionSpeed + "ms";
|
|
|
107
123
|
|
|
108
124
|
const itemWidth = ref(0);
|
|
109
125
|
const itemWidthOffsetStr = computed(() => {
|
|
110
|
-
// if (props.allowCarouselOverflow) {
|
|
111
126
|
if (props.useFlipAnimation) {
|
|
112
|
-
return `calc(-${initialItemOffset.value} * ${itemWidth.value}px - var(--_carousel-item-track-gap))`;
|
|
127
|
+
return `calc(-${initialItemOffset.value} * ${itemWidth.value}px - var(--_carousel-item-track-gap))`;
|
|
113
128
|
} else {
|
|
114
|
-
return `calc(-${initialItemOffset.value} * ${itemWidth.value}px - (2 * var(--_carousel-item-track-gap)))`;
|
|
129
|
+
return `calc(-${initialItemOffset.value} * ${itemWidth.value}px - (2 * var(--_carousel-item-track-gap)))`;
|
|
115
130
|
}
|
|
116
|
-
// } else {
|
|
117
|
-
// if (props.useFlipAnimation) {
|
|
118
|
-
// return `calc(-${initialItemOffset.value} * ${itemWidth.value}px - var(--_carousel-item-track-gap))` // Goof
|
|
119
|
-
// } else {
|
|
120
|
-
// return `calc(-${initialItemOffset.value} * ${itemWidth.value}px - (2 * var(--_carousel-item-track-gap)))` // Good
|
|
121
|
-
// }
|
|
122
|
-
// }
|
|
123
131
|
});
|
|
124
132
|
const currentActiveIndex = ref(0);
|
|
125
133
|
|
|
@@ -256,7 +264,10 @@ const reorderItems = (skipAnimation: boolean = false) => {
|
|
|
256
264
|
item.style.transform = `translateX(${deltaX}px)`;
|
|
257
265
|
|
|
258
266
|
requestAnimationFrame(() => {
|
|
259
|
-
const shouldTransition =
|
|
267
|
+
const shouldTransition =
|
|
268
|
+
carouselInitComplete.value &&
|
|
269
|
+
userHasInteracted.value &&
|
|
270
|
+
!window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
260
271
|
let transitionProperties = "none";
|
|
261
272
|
|
|
262
273
|
if (shouldTransition) {
|
|
@@ -416,7 +427,7 @@ onMounted(() => {
|
|
|
416
427
|
<style lang="css">
|
|
417
428
|
@layer components {
|
|
418
429
|
.carousel-flip {
|
|
419
|
-
--_carousel-item-track-gap:
|
|
430
|
+
--_carousel-item-track-gap: var(--carousel-flip-gap, 1rem);
|
|
420
431
|
|
|
421
432
|
display: grid;
|
|
422
433
|
grid-template-columns: auto 1fr auto;
|
|
@@ -424,7 +435,7 @@ onMounted(() => {
|
|
|
424
435
|
grid-template-areas:
|
|
425
436
|
"prev carousel next"
|
|
426
437
|
". controls . ";
|
|
427
|
-
gap:
|
|
438
|
+
gap: var(--carousel-flip-layout-gap, 1rem);
|
|
428
439
|
opacity: 0;
|
|
429
440
|
|
|
430
441
|
&.mounted {
|
|
@@ -451,7 +462,7 @@ onMounted(() => {
|
|
|
451
462
|
position: relative;
|
|
452
463
|
isolation: isolate;
|
|
453
464
|
|
|
454
|
-
max-inline-size: var(--
|
|
465
|
+
max-inline-size: var(--carousel-flip-display-max-width, 80rem);
|
|
455
466
|
margin-inline: auto;
|
|
456
467
|
|
|
457
468
|
&.allow-overflow {
|
|
@@ -466,17 +477,20 @@ onMounted(() => {
|
|
|
466
477
|
margin-inline: auto;
|
|
467
478
|
|
|
468
479
|
max-inline-size: calc(
|
|
469
|
-
var(--
|
|
470
|
-
(2 * var(--
|
|
480
|
+
var(--carousel-flip-item-max-width, 80rem) + var(--_carousel-item-track-gap) -
|
|
481
|
+
(2 * var(--carousel-flip-edge-preview-width, 4rem))
|
|
471
482
|
);
|
|
472
483
|
|
|
473
484
|
translate: calc(
|
|
474
|
-
v-bind(itemWidthOffsetStr) - var(--_carousel-item-track-gap) +
|
|
485
|
+
v-bind(itemWidthOffsetStr) - var(--_carousel-item-track-gap) +
|
|
486
|
+
var(--carousel-flip-edge-preview-width, 4rem)
|
|
475
487
|
)
|
|
476
488
|
0;
|
|
477
489
|
|
|
478
490
|
&.loaded {
|
|
479
|
-
|
|
491
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
492
|
+
transition: transform v-bind(transitionSpeedStr) ease;
|
|
493
|
+
}
|
|
480
494
|
}
|
|
481
495
|
}
|
|
482
496
|
}
|
|
@@ -486,14 +500,14 @@ onMounted(() => {
|
|
|
486
500
|
display: flex;
|
|
487
501
|
align-items: center;
|
|
488
502
|
justify-content: center;
|
|
489
|
-
max-inline-size: var(--
|
|
503
|
+
max-inline-size: var(--carousel-flip-display-max-width, 80rem);
|
|
490
504
|
margin-inline: auto;
|
|
491
505
|
|
|
492
506
|
.markers-container {
|
|
493
507
|
.markers-list {
|
|
494
508
|
display: flex;
|
|
495
509
|
flex-direction: row;
|
|
496
|
-
gap:
|
|
510
|
+
gap: var(--carousel-flip-marker-gap, 1rem);
|
|
497
511
|
list-style-type: none;
|
|
498
512
|
margin: unset;
|
|
499
513
|
padding: unset;
|
|
@@ -507,7 +521,13 @@ onMounted(() => {
|
|
|
507
521
|
transition: background-color v-bind(transitionSpeedStr) linear;
|
|
508
522
|
|
|
509
523
|
&.active {
|
|
510
|
-
background-color:
|
|
524
|
+
background-color: var(--carousel-flip-marker-active-colour, var(--slate-10));
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
&:focus-visible {
|
|
528
|
+
outline: var(--carousel-flip-focus-outline-width, 0.2rem) solid
|
|
529
|
+
var(--carousel-flip-focus-outline-colour, var(--theme-ring));
|
|
530
|
+
outline-offset: var(--carousel-flip-focus-outline-offset, 0.2rem);
|
|
511
531
|
}
|
|
512
532
|
}
|
|
513
533
|
}
|
|
@@ -538,14 +558,15 @@ onMounted(() => {
|
|
|
538
558
|
cursor: pointer;
|
|
539
559
|
height: fit-content;
|
|
540
560
|
|
|
541
|
-
background-color: white;
|
|
542
|
-
border:
|
|
543
|
-
|
|
544
|
-
|
|
561
|
+
background-color: var(--carousel-flip-button-background-colour, white);
|
|
562
|
+
border: var(--carousel-flip-button-border-width, 0.1rem) solid
|
|
563
|
+
var(--carousel-flip-button-border-colour, hsl(0, 29%, 3%));
|
|
564
|
+
border-radius: var(--carousel-flip-button-border-radius, 100vw);
|
|
565
|
+
padding: var(--carousel-flip-button-padding, 0.8rem);
|
|
545
566
|
|
|
546
567
|
.arrows-icon {
|
|
547
|
-
width:
|
|
548
|
-
height:
|
|
568
|
+
width: var(--carousel-flip-button-icon-size, 2.4rem);
|
|
569
|
+
height: var(--carousel-flip-button-icon-size, 2.4rem);
|
|
549
570
|
}
|
|
550
571
|
}
|
|
551
572
|
|
|
@@ -574,7 +595,7 @@ onMounted(() => {
|
|
|
574
595
|
.buttons-container {
|
|
575
596
|
display: flex;
|
|
576
597
|
align-items: center;
|
|
577
|
-
gap:
|
|
598
|
+
gap: var(--carousel-flip-buttons-gap, 1rem);
|
|
578
599
|
grid-area: controls;
|
|
579
600
|
justify-self: end;
|
|
580
601
|
}
|
|
@@ -586,7 +607,7 @@ onMounted(() => {
|
|
|
586
607
|
grid-area: carousel;
|
|
587
608
|
align-self: end;
|
|
588
609
|
z-index: 1;
|
|
589
|
-
padding-block-end: 1rem;
|
|
610
|
+
padding-block-end: var(--carousel-flip-overlay-controls-offset, 1rem);
|
|
590
611
|
}
|
|
591
612
|
}
|
|
592
613
|
}
|
|
@@ -19,6 +19,13 @@ interface CarouselFlipStoryArgs {
|
|
|
19
19
|
buttonLayout: "sides" | "controls-flanking" | "controls-grouped-right" | "overlay";
|
|
20
20
|
showControls: boolean;
|
|
21
21
|
styleClassPassthrough: string[];
|
|
22
|
+
ariaLabel: string;
|
|
23
|
+
itemsAriaLabel: string;
|
|
24
|
+
previousLabel: string;
|
|
25
|
+
nextLabel: string;
|
|
26
|
+
jumpToItemLabel: string;
|
|
27
|
+
prevIcon: string;
|
|
28
|
+
nextIcon: string;
|
|
22
29
|
// Mock data for stories
|
|
23
30
|
carouselData: CarouselItem[];
|
|
24
31
|
}
|
|
@@ -68,7 +75,7 @@ const sampleCarouselItems: CarouselItem[] = [
|
|
|
68
75
|
];
|
|
69
76
|
|
|
70
77
|
export default {
|
|
71
|
-
title: "
|
|
78
|
+
title: "Organisms/Image Galleries/Carousel Flip",
|
|
72
79
|
component: StorybookComponent,
|
|
73
80
|
argTypes: {
|
|
74
81
|
// Carousel Configuration
|
|
@@ -129,6 +136,56 @@ export default {
|
|
|
129
136
|
category: "Layout",
|
|
130
137
|
},
|
|
131
138
|
},
|
|
139
|
+
// Accessibility / localisation
|
|
140
|
+
ariaLabel: {
|
|
141
|
+
control: { type: "text" },
|
|
142
|
+
description: "Accessible name for the carousel region",
|
|
143
|
+
table: {
|
|
144
|
+
category: "Accessibility",
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
itemsAriaLabel: {
|
|
148
|
+
control: { type: "text" },
|
|
149
|
+
description: "Accessible name for the item group",
|
|
150
|
+
table: {
|
|
151
|
+
category: "Accessibility",
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
previousLabel: {
|
|
155
|
+
control: { type: "text" },
|
|
156
|
+
description: "Accessible label for the previous button",
|
|
157
|
+
table: {
|
|
158
|
+
category: "Accessibility",
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
nextLabel: {
|
|
162
|
+
control: { type: "text" },
|
|
163
|
+
description: "Accessible label for the next button",
|
|
164
|
+
table: {
|
|
165
|
+
category: "Accessibility",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
jumpToItemLabel: {
|
|
169
|
+
control: { type: "text" },
|
|
170
|
+
description: "Accessible label prefix for marker buttons (index is appended)",
|
|
171
|
+
table: {
|
|
172
|
+
category: "Accessibility",
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
prevIcon: {
|
|
176
|
+
control: { type: "text" },
|
|
177
|
+
description: "Iconify name for the previous button icon",
|
|
178
|
+
table: {
|
|
179
|
+
category: "Icons",
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
nextIcon: {
|
|
183
|
+
control: { type: "text" },
|
|
184
|
+
description: "Iconify name for the next button icon",
|
|
185
|
+
table: {
|
|
186
|
+
category: "Icons",
|
|
187
|
+
},
|
|
188
|
+
},
|
|
132
189
|
// Display data for reference
|
|
133
190
|
carouselData: {
|
|
134
191
|
control: { type: "object" },
|
|
@@ -147,6 +204,13 @@ export default {
|
|
|
147
204
|
buttonLayout: "sides",
|
|
148
205
|
showControls: true,
|
|
149
206
|
styleClassPassthrough: ["carousel-flip-demo"],
|
|
207
|
+
ariaLabel: "Image carousel",
|
|
208
|
+
itemsAriaLabel: "Carousel items",
|
|
209
|
+
previousLabel: "Go to previous item",
|
|
210
|
+
nextLabel: "Go to next item",
|
|
211
|
+
jumpToItemLabel: "Jump to item",
|
|
212
|
+
prevIcon: "ic:outline-keyboard-arrow-left",
|
|
213
|
+
nextIcon: "ic:outline-keyboard-arrow-right",
|
|
150
214
|
carouselData: sampleCarouselItems,
|
|
151
215
|
},
|
|
152
216
|
} as Meta<CarouselFlipStoryArgs>;
|
|
@@ -180,22 +244,22 @@ const Template: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
180
244
|
}
|
|
181
245
|
|
|
182
246
|
.carousel-flip-demo.carousel-flip {
|
|
183
|
-
--
|
|
184
|
-
--
|
|
185
|
-
--
|
|
247
|
+
--carousel-flip-gap: 12px;
|
|
248
|
+
--carousel-flip-item-max-width: 800px;
|
|
249
|
+
--carousel-flip-edge-preview-width: 12px;
|
|
186
250
|
}
|
|
187
251
|
|
|
188
252
|
@media (width >= 768px) {
|
|
189
253
|
.carousel-flip-demo.carousel-flip {
|
|
190
|
-
--
|
|
191
|
-
--
|
|
254
|
+
--carousel-flip-gap: 24px;
|
|
255
|
+
--carousel-flip-edge-preview-width: 80px;
|
|
192
256
|
}
|
|
193
257
|
}
|
|
194
258
|
|
|
195
259
|
.carousel-flip-demo.carousel-flip .item-container {
|
|
196
260
|
max-inline-size: 800px;
|
|
197
261
|
margin-inline: auto;
|
|
198
|
-
outline: 1px solid
|
|
262
|
+
outline: 1px solid var(--slate-08, #666);
|
|
199
263
|
padding-block: 6px;
|
|
200
264
|
padding-inline: 6px;
|
|
201
265
|
}
|
|
@@ -208,12 +272,12 @@ const Template: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
208
272
|
}
|
|
209
273
|
|
|
210
274
|
.carousel-flip-demo.carousel-flip .item {
|
|
211
|
-
background-color:
|
|
275
|
+
background-color: var(--slate-05, #e5e5e5);
|
|
212
276
|
contain: layout style paint;
|
|
213
277
|
}
|
|
214
278
|
|
|
215
279
|
.carousel-flip-demo.carousel-flip .item:nth-child(odd) {
|
|
216
|
-
background-color:
|
|
280
|
+
background-color: var(--slate-06, #999);
|
|
217
281
|
}
|
|
218
282
|
|
|
219
283
|
.custom-carousel-item {
|
|
@@ -223,11 +287,11 @@ const Template: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
223
287
|
justify-content: center;
|
|
224
288
|
aspect-ratio: 4 / 3;
|
|
225
289
|
inline-size: 100%;
|
|
226
|
-
color:
|
|
290
|
+
color: #aaa;
|
|
227
291
|
padding-block: 10px;
|
|
228
292
|
padding-inline: 10px;
|
|
229
293
|
border-radius: 4px;
|
|
230
|
-
outline: 1px solid
|
|
294
|
+
outline: 1px solid #00000090;
|
|
231
295
|
}
|
|
232
296
|
|
|
233
297
|
.carousel-flip-demo.carousel-flip .controls-container {
|
|
@@ -259,7 +323,7 @@ const Template: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
259
323
|
width: var(--marker-width);
|
|
260
324
|
height: var(--marker-height);
|
|
261
325
|
border-radius: var(--marker-border-radius);
|
|
262
|
-
background-color:
|
|
326
|
+
background-color: var(--slate-05, #e5e5e5);
|
|
263
327
|
line-height: 3px;
|
|
264
328
|
transition: all 0.3s linear;
|
|
265
329
|
border: 1px solid transparent;
|
|
@@ -267,13 +331,13 @@ const Template: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
267
331
|
}
|
|
268
332
|
|
|
269
333
|
.carousel-flip-demo.carousel-flip .btn-marker.active {
|
|
270
|
-
background-color:
|
|
334
|
+
background-color: var(--slate-012, #000);
|
|
271
335
|
will-change: background-color;
|
|
272
336
|
}
|
|
273
337
|
|
|
274
338
|
.carousel-flip-demo.carousel-flip .btn-marker:hover,
|
|
275
339
|
.carousel-flip-demo.carousel-flip .btn-marker:focus-visible {
|
|
276
|
-
outline-color:
|
|
340
|
+
outline-color: #000;
|
|
277
341
|
will-change: background-color, outline-color;
|
|
278
342
|
}
|
|
279
343
|
`;
|
|
@@ -290,13 +354,13 @@ const Template: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
290
354
|
Displaying {{ args.carouselDataIds.length }} carousel items with {{ args.transitionSpeed }}ms transition speed
|
|
291
355
|
</p>
|
|
292
356
|
<div style="display: flex; gap: 12px; margin-bottom: 20px; flex-wrap: wrap;">
|
|
293
|
-
<div style="padding: 4px 8px; border: 1px solid
|
|
357
|
+
<div style="padding: 4px 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 12px; color: #333;">
|
|
294
358
|
Overflow: {{ args.allowCarouselOverflow ? 'Enabled' : 'Disabled' }}
|
|
295
359
|
</div>
|
|
296
|
-
<div style="padding: 4px 8px; border: 1px solid
|
|
360
|
+
<div style="padding: 4px 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 12px; color: #333;">
|
|
297
361
|
Flip Animation: {{ args.useFlipAnimation ? 'Enabled' : 'Disabled' }}
|
|
298
362
|
</div>
|
|
299
|
-
<div style="padding: 4px 8px; border: 1px solid
|
|
363
|
+
<div style="padding: 4px 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 12px; color: #333;">
|
|
300
364
|
Spring Effect: {{ args.useSpringEffect ? 'Enabled' : 'Disabled' }}
|
|
301
365
|
</div>
|
|
302
366
|
</div>
|
|
@@ -311,6 +375,13 @@ const Template: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
311
375
|
:button-layout="args.buttonLayout"
|
|
312
376
|
:show-controls="args.showControls"
|
|
313
377
|
:style-class-passthrough="args.styleClassPassthrough"
|
|
378
|
+
:aria-label="args.ariaLabel"
|
|
379
|
+
:items-aria-label="args.itemsAriaLabel"
|
|
380
|
+
:previous-label="args.previousLabel"
|
|
381
|
+
:next-label="args.nextLabel"
|
|
382
|
+
:jump-to-item-label="args.jumpToItemLabel"
|
|
383
|
+
:prev-icon="args.prevIcon"
|
|
384
|
+
:next-icon="args.nextIcon"
|
|
314
385
|
>
|
|
315
386
|
<template v-for="(item, index) in filteredCarouselData" #[item.id] :key="index">
|
|
316
387
|
<div class="custom-carousel-item">
|
|
@@ -427,22 +498,22 @@ const RichContentTemplate: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
427
498
|
style.id = styleId;
|
|
428
499
|
style.textContent = `
|
|
429
500
|
.carousel-flip-demo.carousel-flip {
|
|
430
|
-
--
|
|
431
|
-
--
|
|
432
|
-
--
|
|
501
|
+
--carousel-flip-gap: 12px;
|
|
502
|
+
--carousel-flip-item-max-width: 800px;
|
|
503
|
+
--carousel-flip-edge-preview-width: 12px;
|
|
433
504
|
}
|
|
434
505
|
|
|
435
506
|
@media (width >= 768px) {
|
|
436
507
|
.carousel-flip-demo.carousel-flip {
|
|
437
|
-
--
|
|
438
|
-
--
|
|
508
|
+
--carousel-flip-gap: 24px;
|
|
509
|
+
--carousel-flip-edge-preview-width: 80px;
|
|
439
510
|
}
|
|
440
511
|
}
|
|
441
512
|
|
|
442
513
|
.carousel-flip-demo.carousel-flip .item-container {
|
|
443
514
|
max-inline-size: 800px;
|
|
444
515
|
margin-inline: auto;
|
|
445
|
-
outline: 1px solid
|
|
516
|
+
outline: 1px solid var(--slate-08, #666);
|
|
446
517
|
padding-block: 6px;
|
|
447
518
|
padding-inline: 6px;
|
|
448
519
|
}
|
|
@@ -455,12 +526,12 @@ const RichContentTemplate: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
455
526
|
}
|
|
456
527
|
|
|
457
528
|
.carousel-flip-demo.carousel-flip .item {
|
|
458
|
-
background-color:
|
|
529
|
+
background-color: var(--slate-05, #e5e5e5);
|
|
459
530
|
contain: layout style paint;
|
|
460
531
|
}
|
|
461
532
|
|
|
462
533
|
.carousel-flip-demo.carousel-flip .item:nth-child(odd) {
|
|
463
|
-
background-color:
|
|
534
|
+
background-color: var(--slate-06, #999);
|
|
464
535
|
}
|
|
465
536
|
`;
|
|
466
537
|
document.head.appendChild(style);
|
|
@@ -486,6 +557,13 @@ const RichContentTemplate: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
486
557
|
:button-layout="args.buttonLayout"
|
|
487
558
|
:show-controls="args.showControls"
|
|
488
559
|
:style-class-passthrough="args.styleClassPassthrough"
|
|
560
|
+
:aria-label="args.ariaLabel"
|
|
561
|
+
:items-aria-label="args.itemsAriaLabel"
|
|
562
|
+
:previous-label="args.previousLabel"
|
|
563
|
+
:next-label="args.nextLabel"
|
|
564
|
+
:jump-to-item-label="args.jumpToItemLabel"
|
|
565
|
+
:prev-icon="args.prevIcon"
|
|
566
|
+
:next-icon="args.nextIcon"
|
|
489
567
|
>
|
|
490
568
|
<template v-for="(item, index) in filteredCarouselData" #[item.id] :key="index">
|
|
491
569
|
<div
|
|
@@ -604,16 +682,16 @@ const AnimationShowcaseTemplate: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
604
682
|
Testing different animation combinations and timing
|
|
605
683
|
</p>
|
|
606
684
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-bottom: 20px;">
|
|
607
|
-
<div style="padding: 8px; border: 1px solid
|
|
685
|
+
<div style="padding: 8px; border: 1px solid #ccc; border-radius: 6px; text-align: center; font-size: 12px; color: #333; background: args.useFlipAnimation ? '#e7f5e7' : 'transparent';">
|
|
608
686
|
Flip: {{ args.useFlipAnimation ? '✓' : '✗' }}
|
|
609
687
|
</div>
|
|
610
|
-
<div style="padding: 8px; border: 1px solid
|
|
688
|
+
<div style="padding: 8px; border: 1px solid #ccc; border-radius: 6px; text-align: center; font-size: 12px; color: #333; background: args.useSpringEffect ? '#e7f5e7' : 'transparent';">
|
|
611
689
|
Spring: {{ args.useSpringEffect ? '✓' : '✗' }}
|
|
612
690
|
</div>
|
|
613
|
-
<div style="padding: 8px; border: 1px solid
|
|
691
|
+
<div style="padding: 8px; border: 1px solid #ccc; border-radius: 6px; text-align: center; font-size: 12px; color: #333; background: args.allowCarouselOverflow ? '#e7f5e7' : 'transparent';">
|
|
614
692
|
Overflow: {{ args.allowCarouselOverflow ? '✓' : '✗' }}
|
|
615
693
|
</div>
|
|
616
|
-
<div style="padding: 8px; border: 1px solid
|
|
694
|
+
<div style="padding: 8px; border: 1px solid #ccc; border-radius: 6px; text-align: center; font-size: 12px; color: #333;">
|
|
617
695
|
Speed: {{ args.transitionSpeed }}ms
|
|
618
696
|
</div>
|
|
619
697
|
</div>
|
|
@@ -628,6 +706,13 @@ const AnimationShowcaseTemplate: StoryFn<CarouselFlipStoryArgs> = (args) => ({
|
|
|
628
706
|
:button-layout="args.buttonLayout"
|
|
629
707
|
:show-controls="args.showControls"
|
|
630
708
|
:style-class-passthrough="args.styleClassPassthrough"
|
|
709
|
+
:aria-label="args.ariaLabel"
|
|
710
|
+
:items-aria-label="args.itemsAriaLabel"
|
|
711
|
+
:previous-label="args.previousLabel"
|
|
712
|
+
:next-label="args.nextLabel"
|
|
713
|
+
:jump-to-item-label="args.jumpToItemLabel"
|
|
714
|
+
:prev-icon="args.prevIcon"
|
|
715
|
+
:next-icon="args.nextIcon"
|
|
631
716
|
>
|
|
632
717
|
<template v-for="(item, index) in filteredCarouselData" #[item.id] :key="index">
|
|
633
718
|
<div
|
|
@@ -3,6 +3,16 @@ import { mountSuspended } from "@nuxt/test-utils/runtime";
|
|
|
3
3
|
import { nextTick } from "vue";
|
|
4
4
|
import CarouselFlip from "../CarouselFlip.vue";
|
|
5
5
|
|
|
6
|
+
const stubMatchMedia = (prefersReducedMotion: boolean) => {
|
|
7
|
+
vi.stubGlobal(
|
|
8
|
+
"matchMedia",
|
|
9
|
+
vi.fn().mockReturnValue({
|
|
10
|
+
matches: prefersReducedMotion,
|
|
11
|
+
addEventListener: vi.fn(),
|
|
12
|
+
})
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
6
16
|
interface CarouselFlipInstance {
|
|
7
17
|
carouselWrapperRef: HTMLDivElement | null;
|
|
8
18
|
carouselContainerRef: HTMLDivElement | null;
|
|
@@ -115,6 +125,7 @@ describe("CarouselFlip", () => {
|
|
|
115
125
|
beforeEach(() => {
|
|
116
126
|
vi.clearAllMocks();
|
|
117
127
|
mockDirection.value = "";
|
|
128
|
+
stubMatchMedia(false);
|
|
118
129
|
});
|
|
119
130
|
|
|
120
131
|
afterEach(() => {
|
|
@@ -271,6 +282,75 @@ describe("CarouselFlip", () => {
|
|
|
271
282
|
|
|
272
283
|
expect(wrapper.attributes("data-button-layout")).toBe("overlay");
|
|
273
284
|
});
|
|
285
|
+
|
|
286
|
+
it("overrides accessible labels via props", async () => {
|
|
287
|
+
await createWrapper({
|
|
288
|
+
ariaLabel: "Featured looks",
|
|
289
|
+
itemsAriaLabel: "Look slides",
|
|
290
|
+
previousLabel: "Previous look",
|
|
291
|
+
nextLabel: "Next look",
|
|
292
|
+
jumpToItemLabel: "View look",
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
expect(wrapper.find('[role="region"]').attributes("aria-label")).toBe("Featured looks");
|
|
296
|
+
expect(wrapper.find('[role="group"]').attributes("aria-label")).toBe("Look slides");
|
|
297
|
+
expect(wrapper.find('[aria-label="Previous look"]').exists()).toBe(true);
|
|
298
|
+
expect(wrapper.find('[aria-label="Next look"]').exists()).toBe(true);
|
|
299
|
+
expect(wrapper.find('[aria-label="View look 1"]').exists()).toBe(true);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("overrides the screen-reader position announcement via prop", async () => {
|
|
303
|
+
await createWrapper({
|
|
304
|
+
itemPositionAnnouncement: (current: number, total: number) => `Slide ${current} / ${total}`,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
expect(wrapper.find('[aria-live="polite"]').text()).toBe("Slide 1 / 6");
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("overrides prev/next icons via props", async () => {
|
|
311
|
+
await createWrapper({ prevIcon: "mdi:chevron-left", nextIcon: "mdi:chevron-right" });
|
|
312
|
+
|
|
313
|
+
const prevButton = wrapper.find('[aria-label="Go to previous item"]');
|
|
314
|
+
const nextButton = wrapper.find('[aria-label="Go to next item"]');
|
|
315
|
+
|
|
316
|
+
expect(prevButton.html()).toContain("i-mdi:chevron-left");
|
|
317
|
+
expect(nextButton.html()).toContain("i-mdi:chevron-right");
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("allows overriding the prev/next icon via slots", async () => {
|
|
321
|
+
wrapper = await mountSuspended(CarouselFlip, {
|
|
322
|
+
props: { carouselDataIds: mockCarouselData },
|
|
323
|
+
slots: {
|
|
324
|
+
"prev-icon": '<span data-testid="custom-prev">Prev</span>',
|
|
325
|
+
"next-icon": '<span data-testid="custom-next">Next</span>',
|
|
326
|
+
},
|
|
327
|
+
global: {
|
|
328
|
+
components: {
|
|
329
|
+
Icon: {
|
|
330
|
+
template: '<svg data-testid="icon" :class="$attrs.class"><title>{{ name }}</title></svg>',
|
|
331
|
+
props: ["name"],
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
expect(wrapper.find('[data-testid="custom-prev"]').exists()).toBe(true);
|
|
338
|
+
expect(wrapper.find('[data-testid="custom-next"]').exists()).toBe(true);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
describe("Reduced Motion", () => {
|
|
343
|
+
it("skips the JS-driven transition when prefers-reduced-motion is set", async () => {
|
|
344
|
+
stubMatchMedia(true);
|
|
345
|
+
await createWrapper();
|
|
346
|
+
await nextTick();
|
|
347
|
+
|
|
348
|
+
const nextButton = wrapper.find('[aria-label="Go to next item"]');
|
|
349
|
+
await nextButton.trigger("click");
|
|
350
|
+
await nextTick();
|
|
351
|
+
|
|
352
|
+
expect(component.currentActiveIndex).toBe(1);
|
|
353
|
+
});
|
|
274
354
|
});
|
|
275
355
|
|
|
276
356
|
describe("Initial Setup", () => {
|