@nexxtmove/ui 2.6.0 → 2.7.0
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/dist/index.js +49 -126
- package/dist/nuxt.js +0 -1
- package/package.json +38 -36
- package/src/attribute-contract.test.ts +151 -0
- package/src/components/atoms/AnimatedNumber/AnimatedNumber.stories.ts +36 -0
- package/src/components/atoms/AnimatedNumber/AnimatedNumber.test.ts +21 -0
- package/src/components/atoms/Avatar/Avatar.stories.ts +122 -0
- package/src/components/atoms/Avatar/Avatar.test.ts +134 -0
- package/src/components/atoms/Button/Button.stories.ts +290 -0
- package/src/components/atoms/Button/Button.test.ts +406 -0
- package/src/components/atoms/Card/Card.stories.ts +45 -0
- package/src/components/atoms/Card/Card.test.ts +46 -0
- package/src/components/atoms/Checkbox/Checkbox.stories.ts +35 -0
- package/src/components/atoms/Checkbox/Checkbox.test.ts +55 -0
- package/src/components/atoms/Chip/Chip.stories.ts +274 -0
- package/src/components/atoms/Chip/Chip.test.ts +230 -0
- package/src/components/atoms/EnergyLabel/EnergyLabel.stories.ts +189 -0
- package/src/components/atoms/EnergyLabel/EnergyLabel.test.ts +100 -0
- package/src/components/atoms/FloatingPanel/FloatingPanel.stories.ts +677 -0
- package/src/components/atoms/FloatingPanel/FloatingPanel.test.ts +864 -0
- package/src/components/atoms/Icon/Icon.stories.ts +63 -0
- package/src/components/atoms/Icon/Icon.test.ts +89 -0
- package/src/components/atoms/Loader/Loader.stories.ts +36 -0
- package/src/components/atoms/Loader/Loader.test.ts +40 -0
- package/src/components/atoms/PickerItem/PickerItem.stories.ts +172 -0
- package/src/components/atoms/PickerItem/PickerItem.test.ts +142 -0
- package/src/components/atoms/PickerPreview/PickerPreview.stories.ts +183 -0
- package/src/components/atoms/PickerPreview/PickerPreview.test.ts +137 -0
- package/src/components/atoms/PresenceIndicator/PresenceIndicator.stories.ts +21 -0
- package/src/components/atoms/PropertyListing/PropertyListing.stories.ts +38 -0
- package/src/components/atoms/PropertyListing/PropertyListing.test.ts +46 -0
- package/src/components/atoms/SelectableOption/SelectableOption.stories.ts +131 -0
- package/src/components/atoms/SelectableOption/SelectableOption.test.ts +96 -0
- package/src/components/atoms/Skeleton/Skeleton.stories.ts +27 -0
- package/src/components/atoms/Skeleton/Skeleton.test.ts +28 -0
- package/src/components/atoms/Slider/Slider.stories.ts +151 -0
- package/src/components/atoms/Slider/Slider.test.ts +191 -0
- package/src/components/atoms/Squircle/Squircle.stories.ts +114 -0
- package/src/components/atoms/Squircle/Squircle.test.ts +70 -0
- package/src/components/atoms/Switch/Switch.stories.ts +91 -0
- package/src/components/atoms/Switch/Switch.test.ts +183 -0
- package/src/components/atoms/Tooltip/Tooltip.stories.ts +158 -0
- package/src/components/atoms/Tooltip/Tooltip.test.ts +134 -0
- package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.stories.ts +124 -0
- package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.test.ts +464 -0
- package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.stories.ts +266 -0
- package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.test.ts +421 -0
- package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.stories.ts +365 -0
- package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.test.ts +484 -0
- package/src/components/editor/composables/useRadioGrid.test.ts +261 -0
- package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.stories.ts +73 -0
- package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.test.ts +189 -0
- package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.stories.ts +309 -0
- package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.test.ts +359 -0
- package/src/components/editor/molecules/EditorAnchor/EditorAnchor.stories.ts +138 -0
- package/src/components/editor/molecules/EditorAnchor/EditorAnchor.test.ts +344 -0
- package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.stories.ts +87 -0
- package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.test.ts +262 -0
- package/src/components/editor/molecules/EditorBlur/EditorBlur.stories.ts +115 -0
- package/src/components/editor/molecules/EditorBlur/EditorBlur.test.ts +243 -0
- package/src/components/editor/molecules/EditorBorder/EditorBorder.stories.ts +187 -0
- package/src/components/editor/molecules/EditorBorder/EditorBorder.test.ts +456 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.stories.ts +563 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.test.ts +689 -0
- package/src/components/editor/molecules/EditorColor/EditorColorGradient.test.ts +793 -0
- package/src/components/editor/molecules/EditorColor/gradientClass.test.ts +303 -0
- package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.stories.ts +72 -0
- package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.test.ts +124 -0
- package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.stories.ts +46 -0
- package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.test.ts +97 -0
- package/src/components/editor/molecules/EditorFontSize/EditorFontSize.stories.ts +248 -0
- package/src/components/editor/molecules/EditorFontSize/EditorFontSize.test.ts +288 -0
- package/src/components/editor/molecules/EditorGap/EditorGap.stories.ts +129 -0
- package/src/components/editor/molecules/EditorGap/EditorGap.test.ts +253 -0
- package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.stories.ts +68 -0
- package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.test.ts +150 -0
- package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.stories.ts +71 -0
- package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.test.ts +161 -0
- package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.stories.ts +204 -0
- package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.test.ts +238 -0
- package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.stories.ts +257 -0
- package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.test.ts +286 -0
- package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.stories.ts +135 -0
- package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.test.ts +230 -0
- package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.stories.ts +154 -0
- package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.test.ts +112 -0
- package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.stories.ts +206 -0
- package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.test.ts +332 -0
- package/src/components/editor/molecules/EditorPadding/EditorPadding.stories.ts +374 -0
- package/src/components/editor/molecules/EditorPadding/EditorPadding.test.ts +712 -0
- package/src/components/editor/molecules/EditorRadius/EditorRadius.stories.ts +64 -0
- package/src/components/editor/molecules/EditorRadius/EditorRadius.test.ts +137 -0
- package/src/components/editor/molecules/EditorShadow/EditorShadow.stories.ts +103 -0
- package/src/components/editor/molecules/EditorShadow/EditorShadow.test.ts +168 -0
- package/src/components/editor/molecules/EditorSize/EditorSize.stories.ts +84 -0
- package/src/components/editor/molecules/EditorSize/EditorSize.test.ts +253 -0
- package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.stories.ts +49 -0
- package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.test.ts +43 -0
- package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.stories.ts +69 -0
- package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.test.ts +155 -0
- package/src/components/molecules/Announcement/Announcement.stories.ts +224 -0
- package/src/components/molecules/Announcement/Announcement.test.ts +161 -0
- package/src/components/molecules/ContactInfoCard/ContactInfoCard.stories.ts +99 -0
- package/src/components/molecules/ContactInfoCard/ContactInfoCard.test.ts +70 -0
- package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.stories.ts +148 -0
- package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.test.ts +271 -0
- package/src/components/molecules/DoughnutChart/DoughnutChart.stories.ts +45 -0
- package/src/components/molecules/DoughnutChart/DoughnutChart.test.ts +205 -0
- package/src/components/molecules/InfoBlock/InfoBlock.stories.ts +84 -0
- package/src/components/molecules/InfoBlock/InfoBlock.test.ts +101 -0
- package/src/components/molecules/InputField/InputField.stories.ts +117 -0
- package/src/components/molecules/InputField/InputField.test.ts +138 -0
- package/src/components/molecules/InputSelect/InputSelect.stories.ts +116 -0
- package/src/components/molecules/InputSelect/InputSelect.test.ts +127 -0
- package/src/components/molecules/ModalFooter/ModalFooter.stories.ts +204 -0
- package/src/components/molecules/ModalFooter/ModalFooter.test.ts +276 -0
- package/src/components/molecules/PageCard/PageCard.stories.ts +100 -0
- package/src/components/molecules/PageCard/PageCard.test.ts +91 -0
- package/src/components/molecules/PageContainer/PageContainer.stories.ts +90 -0
- package/src/components/molecules/PageContainer/PageContainer.test.ts +50 -0
- package/src/components/molecules/PageMockup/PageMockup.stories.ts +38 -0
- package/src/components/molecules/PageMockup/PageMockup.test.ts +20 -0
- package/src/components/molecules/PageTypeTile/PageTypeTile.stories.ts +63 -0
- package/src/components/molecules/PageTypeTile/PageTypeTile.test.ts +41 -0
- package/src/components/molecules/Pagination/Pagination.stories.ts +72 -0
- package/src/components/molecules/Pagination/Pagination.test.ts +130 -0
- package/src/components/molecules/PickerGroup/PickerGroup.stories.ts +90 -0
- package/src/components/molecules/PickerGroup/PickerGroup.test.ts +145 -0
- package/src/components/molecules/PickerList/PickerList.stories.ts +436 -0
- package/src/components/molecules/PickerList/PickerList.test.ts +870 -0
- package/src/components/molecules/PlaceholderInput/PlaceholderInput.stories.ts +48 -0
- package/src/components/molecules/PlaceholderInput/PlaceholderInput.test.ts +107 -0
- package/src/components/molecules/PropertyCard/PropertyCard.stories.ts +46 -0
- package/src/components/molecules/PropertyCard/PropertyCard.test.ts +59 -0
- package/src/components/molecules/Select/Select.stories.ts +264 -0
- package/src/components/molecules/Select/Select.test.ts +315 -0
- package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.stories.ts +84 -0
- package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.test.ts +103 -0
- package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.stories.ts +11 -0
- package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.test.ts +38 -0
- package/src/components/molecules/SocialIcons/SocialIcons.stories.ts +28 -0
- package/src/components/molecules/SocialIcons/SocialIcons.test.ts +58 -0
- package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.stories.ts +11 -0
- package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.test.ts +116 -0
- package/src/components/molecules/SocialMediaType/SocialMediaType.stories.ts +37 -0
- package/src/components/molecules/SocialMediaType/SocialMediaType.test.ts +126 -0
- package/src/components/molecules/Source/Source.stories.ts +35 -0
- package/src/components/molecules/Source/Source.test.ts +45 -0
- package/src/components/molecules/TabbedContent/TabbedContent.stories.ts +96 -0
- package/src/components/molecules/TabbedContent/TabbedContent.test.ts +109 -0
- package/src/components/molecules/Tabs/Tabs.stories.ts +230 -0
- package/src/components/molecules/Tabs/Tabs.test.ts +280 -0
- package/src/components/molecules/TemplateCard/TemplateCard.stories.ts +327 -0
- package/src/components/molecules/TemplateCard/TemplateCard.test.ts +339 -0
- package/src/components/molecules/Textarea/Textarea.stories.ts +216 -0
- package/src/components/molecules/Textarea/Textarea.test.ts +191 -0
- package/src/components/molecules/TimelineEvent/TimelineEvent.stories.ts +319 -0
- package/src/components/molecules/TimelineEvent/TimelineEvent.test.ts +224 -0
- package/src/components/molecules/TimelineLanes/TimelineLanes.stories.ts +29 -0
- package/src/components/molecules/TimelineLanes/TimelineLanes.test.ts +30 -0
- package/src/components/molecules/Toast/Toast.stories.ts +110 -0
- package/src/components/molecules/Toast/Toast.test.ts +103 -0
- package/src/components/molecules/ToggleGroup/ToggleGroup.stories.ts +297 -0
- package/src/components/molecules/ToggleGroup/ToggleGroup.test.ts +578 -0
- package/src/components/molecules/Tree/Tree.stories.ts +884 -0
- package/src/components/molecules/Tree/Tree.test.ts +1247 -0
- package/src/components/molecules/Tree/tree-drop.test.ts +223 -0
- package/src/components/organisms/Calendar/Calendar.stories.ts +108 -0
- package/src/components/organisms/Calendar/Calendar.test.ts +279 -0
- package/src/components/organisms/Calendar/_CalendarDayView.test.ts +104 -0
- package/src/components/organisms/Calendar/_CalendarHeader.test.ts +86 -0
- package/src/components/organisms/Calendar/_CalendarMonthView.test.ts +52 -0
- package/src/components/organisms/Calendar/_CalendarTimePicker.test.ts +89 -0
- package/src/components/organisms/Calendar/_CalendarYearView.test.ts +56 -0
- package/src/components/organisms/ColorPicker/ColorPicker.stories.ts +229 -0
- package/src/components/organisms/ColorPicker/ColorPicker.test.ts +638 -0
- package/src/components/organisms/ColorPicker/gradient.test.ts +67 -0
- package/src/components/organisms/ColorPicker/gradientGeometry.test.ts +206 -0
- package/src/components/organisms/Combobox/Combobox.stories.ts +234 -0
- package/src/components/organisms/Combobox/Combobox.test.ts +512 -0
- package/src/components/organisms/Combobox/Combobox.vue +1 -1
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.stories.ts +158 -0
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.test.ts +524 -0
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.vue +1 -1
- package/src/components/organisms/CountrySelect/CountrySelect.stories.ts +139 -0
- package/src/components/organisms/CountrySelect/CountrySelect.test.ts +149 -0
- package/src/components/organisms/DatePicker/DatePicker.stories.ts +163 -0
- package/src/components/organisms/DatePicker/DatePicker.test.ts +214 -0
- package/src/components/organisms/DropdownButton/DropdownButton.stories.ts +123 -0
- package/src/components/organisms/DropdownButton/DropdownButton.test.ts +379 -0
- package/src/components/organisms/FontPicker/FontPicker.stories.ts +476 -0
- package/src/components/organisms/FontPicker/FontPicker.test.ts +873 -0
- package/src/components/organisms/Header/Header.stories.ts +75 -0
- package/src/components/organisms/Header/Header.test.ts +233 -0
- package/src/components/organisms/IconPicker/IconPicker.stories.ts +734 -0
- package/src/components/organisms/IconPicker/IconPicker.test.ts +669 -0
- package/src/components/organisms/IconPicker/_IconPickerGrid.test.ts +184 -0
- package/src/components/organisms/IconPicker/_IconPickerRail.test.ts +102 -0
- package/src/components/organisms/IconPicker/useIconPickerNavigation.test.ts +215 -0
- package/src/components/organisms/Modal/Modal.stories.ts +350 -0
- package/src/components/organisms/Modal/Modal.test.ts +275 -0
- package/src/components/organisms/PickerPanel/PickerPanel.stories.ts +452 -0
- package/src/components/organisms/PickerPanel/PickerPanel.test.ts +358 -0
- package/src/components/organisms/ProgressBar/ProgressBar.stories.ts +100 -0
- package/src/components/organisms/ProgressBar/ProgressBar.test.ts +417 -0
- package/src/components/organisms/ReferenceCard/ReferenceCard.stories.ts +191 -0
- package/src/components/organisms/ReferenceCard/ReferenceCard.test.ts +165 -0
- package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.stories.ts +65 -0
- package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.test.ts +466 -0
- package/src/components/organisms/StepperHeader/StepperHeader.stories.ts +91 -0
- package/src/components/organisms/StepperHeader/StepperHeader.test.ts +279 -0
- package/src/components/organisms/Table/Table.stories.ts +332 -0
- package/src/components/organisms/Table/Table.test.ts +320 -0
- package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.stories.ts +198 -0
- package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.test.ts +283 -0
- package/src/components/organisms/TimelineTrack/TimelineTrack.stories.ts +120 -0
- package/src/components/organisms/TimelineTrack/TimelineTrack.test.ts +91 -0
- package/src/components/organisms/Toaster/Toaster.stories.ts +51 -0
- package/src/components/organisms/Toaster/Toaster.test.ts +75 -0
- package/src/components/organisms/VideoControls/VideoControls.stories.ts +792 -0
- package/src/components/organisms/VideoControls/VideoControls.test.ts +1056 -0
- package/src/components/organisms/VideoPlayer/VideoPlayer.stories.ts +327 -0
- package/src/components/organisms/VideoPlayer/VideoPlayer.test.ts +439 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import { expect, screen, userEvent, waitFor, within } from 'storybook/test'
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
import EditorColor from './EditorColor.vue'
|
|
5
|
+
import type { EditorColorThemeRole, EditorColorValue } from './EditorColor.vue'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The collapsing wrapper, found by the grid row it animates rather than by any
|
|
9
|
+
* styling class, so restyling the block cannot quietly break the assertions.
|
|
10
|
+
*/
|
|
11
|
+
const contentWrapper = (canvasElement: HTMLElement) =>
|
|
12
|
+
canvasElement.querySelector('[class*="grid-rows-"]') as HTMLElement
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The roles as a consumer would pass them: its own labels, and the hex each role
|
|
16
|
+
* has in the customer's theme right now. Heading and text share a hex, so only
|
|
17
|
+
* the tooltip tells their swatches apart.
|
|
18
|
+
*/
|
|
19
|
+
const THEME_ROLES: EditorColorThemeRole[] = [
|
|
20
|
+
{ role: 'main', label: 'Hoofdkleur', hex: '#1777EA' },
|
|
21
|
+
{ role: 'accent', label: 'Accentkleur', hex: '#F59E0B' },
|
|
22
|
+
{ role: 'background', label: 'Achtergrond', hex: '#F0F4F8' },
|
|
23
|
+
{ role: 'heading', label: 'Koppen', hex: '#1F2937' },
|
|
24
|
+
{ role: 'text', label: 'Tekst', hex: '#1F2937' },
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
export default {
|
|
28
|
+
title: 'Components/Editor Components/Molecules/Editor Color',
|
|
29
|
+
component: EditorColor,
|
|
30
|
+
decorators: [() => ({ template: '<div class="w-100 mx-auto"><story /></div>' })],
|
|
31
|
+
argTypes: {
|
|
32
|
+
label: { control: 'text' },
|
|
33
|
+
switchable: { control: 'boolean' },
|
|
34
|
+
property: { control: 'select', options: ['bg', 'text'] },
|
|
35
|
+
showAlpha: { control: 'boolean' },
|
|
36
|
+
},
|
|
37
|
+
} satisfies Meta<typeof EditorColor>
|
|
38
|
+
|
|
39
|
+
type Story = StoryObj<typeof EditorColor>
|
|
40
|
+
|
|
41
|
+
export const Default: Story = {
|
|
42
|
+
render: (args) => ({
|
|
43
|
+
components: { EditorColor },
|
|
44
|
+
setup() {
|
|
45
|
+
const color = ref<EditorColorValue>('bg-[#1777EA]')
|
|
46
|
+
return { args, color }
|
|
47
|
+
},
|
|
48
|
+
// The value is shown as-is: it is the Tailwind class the editor applies.
|
|
49
|
+
template: `
|
|
50
|
+
<div>
|
|
51
|
+
<EditorColor v-bind="args" v-model="color" />
|
|
52
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
53
|
+
</div>
|
|
54
|
+
`,
|
|
55
|
+
}),
|
|
56
|
+
// The panel defaults to a fixed w-70, which is narrower than the full-width
|
|
57
|
+
// trigger in a settings column. jsdom has no layout, so the two widths can
|
|
58
|
+
// only be compared in a real browser.
|
|
59
|
+
play: async ({ canvasElement }) => {
|
|
60
|
+
const canvas = within(canvasElement)
|
|
61
|
+
const trigger = canvas.getByRole('button')
|
|
62
|
+
|
|
63
|
+
await userEvent.click(trigger)
|
|
64
|
+
|
|
65
|
+
// The floating container is found by the inline position it is given, not by
|
|
66
|
+
// a styling class: it is the only fixed-positioned ancestor of the panel.
|
|
67
|
+
await waitFor(() => {
|
|
68
|
+
const panel = canvasElement
|
|
69
|
+
.querySelector('[placeholder="#000000"]')!
|
|
70
|
+
.closest<HTMLElement>('div[style*="position: fixed"]')!
|
|
71
|
+
|
|
72
|
+
expect(panel.getBoundingClientRect().width).toBe(trigger.getBoundingClientRect().width)
|
|
73
|
+
})
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
export const WithSuggestions: Story = {
|
|
77
|
+
args: {
|
|
78
|
+
suggestions: ['#1777EA', '#F0F4F8', '#1F2937'],
|
|
79
|
+
},
|
|
80
|
+
render: (args) => ({
|
|
81
|
+
components: { EditorColor },
|
|
82
|
+
setup() {
|
|
83
|
+
const color = ref<EditorColorValue>('bg-[#1777EA]')
|
|
84
|
+
return { args, color }
|
|
85
|
+
},
|
|
86
|
+
template: `
|
|
87
|
+
<EditorColor v-bind="args" v-model="color" />
|
|
88
|
+
`,
|
|
89
|
+
}),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Picking a role writes the role class for the current property, not its hex. */
|
|
93
|
+
export const WithThemeRoles: Story = {
|
|
94
|
+
args: { themeRoles: THEME_ROLES },
|
|
95
|
+
render: (args) => ({
|
|
96
|
+
components: { EditorColor },
|
|
97
|
+
setup() {
|
|
98
|
+
const color = ref<EditorColorValue>('bg-[#1777EA]')
|
|
99
|
+
return { args, color }
|
|
100
|
+
},
|
|
101
|
+
template: `
|
|
102
|
+
<div>
|
|
103
|
+
<EditorColor v-bind="args" v-model="color" />
|
|
104
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
105
|
+
</div>
|
|
106
|
+
`,
|
|
107
|
+
}),
|
|
108
|
+
play: async ({ canvasElement }) => {
|
|
109
|
+
const canvas = within(canvasElement)
|
|
110
|
+
|
|
111
|
+
// The trigger is named after its placeholder; a role button after its label.
|
|
112
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Kies kleur...' }))
|
|
113
|
+
|
|
114
|
+
const main = await canvas.findByRole('button', { name: 'Hoofdkleur' })
|
|
115
|
+
|
|
116
|
+
// The literal has the hex of the main colour, and that selects nothing.
|
|
117
|
+
expect(main).toHaveAttribute('aria-pressed', 'false')
|
|
118
|
+
|
|
119
|
+
// The swatch carries no visible label; hovering it names the role. The
|
|
120
|
+
// tooltip is portalled to the body, outside the canvas, and reka hides the
|
|
121
|
+
// node that carries the role from the accessibility tree: the bubble around
|
|
122
|
+
// it is what the user sees.
|
|
123
|
+
await userEvent.hover(main)
|
|
124
|
+
const bubble = (await screen.findByRole('tooltip', { hidden: true })).parentElement
|
|
125
|
+
expect(bubble).toBeVisible()
|
|
126
|
+
expect(bubble).toHaveTextContent('Hoofdkleur')
|
|
127
|
+
|
|
128
|
+
await userEvent.click(main)
|
|
129
|
+
|
|
130
|
+
await waitFor(() => {
|
|
131
|
+
expect(canvas.getByText('bg-theme-main')).toBeInTheDocument()
|
|
132
|
+
expect(main).toHaveAttribute('aria-pressed', 'true')
|
|
133
|
+
expect(canvas.getByRole('button', { name: 'Kies kleur...' })).toHaveTextContent('Hoofdkleur')
|
|
134
|
+
})
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** A stored role class is shown by its label, with the swatch of the role's current hex. */
|
|
139
|
+
export const ThemeRoleSelected: Story = {
|
|
140
|
+
args: { themeRoles: THEME_ROLES, property: 'text', label: 'Tekstkleur' },
|
|
141
|
+
render: (args) => ({
|
|
142
|
+
components: { EditorColor },
|
|
143
|
+
setup() {
|
|
144
|
+
const color = ref<EditorColorValue>('text-theme-heading')
|
|
145
|
+
return { args, color }
|
|
146
|
+
},
|
|
147
|
+
template: `
|
|
148
|
+
<div>
|
|
149
|
+
<EditorColor v-bind="args" v-model="color" />
|
|
150
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
151
|
+
</div>
|
|
152
|
+
`,
|
|
153
|
+
}),
|
|
154
|
+
play: async ({ canvasElement }) => {
|
|
155
|
+
const canvas = within(canvasElement)
|
|
156
|
+
const trigger = canvas.getByRole('button', { name: 'Kies kleur...' })
|
|
157
|
+
|
|
158
|
+
expect(trigger).toHaveTextContent('Koppen')
|
|
159
|
+
|
|
160
|
+
await userEvent.click(trigger)
|
|
161
|
+
|
|
162
|
+
// `Tekst` has the same hex as `Koppen`; only the stored role is selected.
|
|
163
|
+
expect(await canvas.findByRole('button', { name: 'Koppen' })).toHaveAttribute(
|
|
164
|
+
'aria-pressed',
|
|
165
|
+
'true',
|
|
166
|
+
)
|
|
167
|
+
expect(canvas.getByRole('button', { name: 'Tekst' })).toHaveAttribute('aria-pressed', 'false')
|
|
168
|
+
|
|
169
|
+
// The picker is handed the role's hex with an alpha pair; the field must not show it.
|
|
170
|
+
expect(canvasElement.querySelector('[placeholder="#000000"]')).toHaveValue('#1f2937')
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The theme section sits above the suggestions; a suggestion still writes a literal colour. */
|
|
175
|
+
export const ThemeRolesWithSuggestions: Story = {
|
|
176
|
+
args: {
|
|
177
|
+
themeRoles: THEME_ROLES,
|
|
178
|
+
suggestions: ['#0F766E', '#F0F4F8', '#1F2937'],
|
|
179
|
+
},
|
|
180
|
+
render: (args) => ({
|
|
181
|
+
components: { EditorColor },
|
|
182
|
+
setup() {
|
|
183
|
+
const color = ref<EditorColorValue>('bg-theme-main')
|
|
184
|
+
return { args, color }
|
|
185
|
+
},
|
|
186
|
+
template: `
|
|
187
|
+
<div>
|
|
188
|
+
<EditorColor v-bind="args" v-model="color" />
|
|
189
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
190
|
+
</div>
|
|
191
|
+
`,
|
|
192
|
+
}),
|
|
193
|
+
play: async ({ canvasElement }) => {
|
|
194
|
+
const canvas = within(canvasElement)
|
|
195
|
+
|
|
196
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Kies kleur...' }))
|
|
197
|
+
|
|
198
|
+
const title = await canvas.findByText('Huisstijl')
|
|
199
|
+
const suggestionsTitle = canvas.getByText('Gebruikte kleuren')
|
|
200
|
+
|
|
201
|
+
expect(
|
|
202
|
+
title.compareDocumentPosition(suggestionsTitle) & Node.DOCUMENT_POSITION_FOLLOWING,
|
|
203
|
+
).toBeTruthy()
|
|
204
|
+
|
|
205
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Kies kleur #0f766e' }))
|
|
206
|
+
|
|
207
|
+
await waitFor(() => {
|
|
208
|
+
expect(canvas.getByText('bg-[#0f766e]')).toBeInTheDocument()
|
|
209
|
+
expect(canvas.getByRole('button', { name: 'Hoofdkleur' })).toHaveAttribute(
|
|
210
|
+
'aria-pressed',
|
|
211
|
+
'false',
|
|
212
|
+
)
|
|
213
|
+
})
|
|
214
|
+
},
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* With `showAlpha` the picker offers an opacity slider. Below full opacity the
|
|
219
|
+
* class carries an 8-digit hex; a theme role has no opacity, so picking one
|
|
220
|
+
* hides the slider.
|
|
221
|
+
*/
|
|
222
|
+
export const WithAlpha: Story = {
|
|
223
|
+
args: { showAlpha: true, themeRoles: THEME_ROLES },
|
|
224
|
+
render: (args) => ({
|
|
225
|
+
components: { EditorColor },
|
|
226
|
+
setup() {
|
|
227
|
+
const color = ref<EditorColorValue>('bg-[#1777EA80]')
|
|
228
|
+
return { args, color }
|
|
229
|
+
},
|
|
230
|
+
template: `
|
|
231
|
+
<div>
|
|
232
|
+
<EditorColor v-bind="args" v-model="color" />
|
|
233
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
234
|
+
</div>
|
|
235
|
+
`,
|
|
236
|
+
}),
|
|
237
|
+
play: async ({ canvasElement }) => {
|
|
238
|
+
const canvas = within(canvasElement)
|
|
239
|
+
|
|
240
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Kies kleur...' }))
|
|
241
|
+
|
|
242
|
+
// The slider is named by its visible label and reads the opacity out as a percentage.
|
|
243
|
+
const alpha = await canvas.findByRole('slider', { name: 'Dekking' })
|
|
244
|
+
expect(alpha).toHaveAttribute('aria-valuetext', '50%')
|
|
245
|
+
|
|
246
|
+
alpha.focus()
|
|
247
|
+
await userEvent.keyboard('{ArrowRight}')
|
|
248
|
+
|
|
249
|
+
// 51% of 255 rounds to 0x82; the picker writes hex in lower case.
|
|
250
|
+
await waitFor(() => expect(canvas.getByText('bg-[#1777ea82]')).toBeInTheDocument())
|
|
251
|
+
|
|
252
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Hoofdkleur' }))
|
|
253
|
+
|
|
254
|
+
await waitFor(() => {
|
|
255
|
+
expect(canvas.getByText('bg-theme-main')).toBeInTheDocument()
|
|
256
|
+
expect(canvas.queryByRole('slider', { name: 'Dekking' })).not.toBeInTheDocument()
|
|
257
|
+
})
|
|
258
|
+
},
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* The flow from the editor: a colour picked, made translucent with the slider,
|
|
263
|
+
* then replaced by a theme role in one click.
|
|
264
|
+
*/
|
|
265
|
+
export const WithAlphaRolePicked: Story = {
|
|
266
|
+
args: { showAlpha: true, switchable: true, themeRoles: THEME_ROLES, suggestions: ['#00aa00'] },
|
|
267
|
+
render: (args) => ({
|
|
268
|
+
components: { EditorColor },
|
|
269
|
+
setup() {
|
|
270
|
+
const color = ref<EditorColorValue>('bg-[#1777EA]')
|
|
271
|
+
const enabled = ref(true)
|
|
272
|
+
return { args, color, enabled }
|
|
273
|
+
},
|
|
274
|
+
template: `
|
|
275
|
+
<div>
|
|
276
|
+
<EditorColor v-bind="args" v-model="color" v-model:enabled="enabled" />
|
|
277
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
278
|
+
</div>
|
|
279
|
+
`,
|
|
280
|
+
}),
|
|
281
|
+
play: async ({ canvasElement }) => {
|
|
282
|
+
const canvas = within(canvasElement)
|
|
283
|
+
|
|
284
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Kies kleur...' }))
|
|
285
|
+
|
|
286
|
+
await userEvent.click(await canvas.findByRole('button', { name: 'Kies kleur #00aa00' }))
|
|
287
|
+
await waitFor(() => expect(canvas.getByText('bg-[#00aa00]')).toBeInTheDocument())
|
|
288
|
+
|
|
289
|
+
const alpha = canvas.getByRole('slider', { name: 'Dekking' })
|
|
290
|
+
alpha.focus()
|
|
291
|
+
for (let step = 0; step < 10; step++) await userEvent.keyboard('{ArrowLeft}')
|
|
292
|
+
await waitFor(() => expect(canvas.getByText('bg-[#00aa00e6]')).toBeInTheDocument())
|
|
293
|
+
|
|
294
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Hoofdkleur' }))
|
|
295
|
+
|
|
296
|
+
await waitFor(() => {
|
|
297
|
+
expect(canvas.getByText('bg-theme-main')).toBeInTheDocument()
|
|
298
|
+
expect(canvas.queryByRole('slider', { name: 'Dekking' })).not.toBeInTheDocument()
|
|
299
|
+
})
|
|
300
|
+
},
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export const Switchable: Story = {
|
|
304
|
+
args: { switchable: true },
|
|
305
|
+
render: (args) => ({
|
|
306
|
+
components: { EditorColor },
|
|
307
|
+
setup() {
|
|
308
|
+
const color = ref<EditorColorValue>('bg-[#1777EA]')
|
|
309
|
+
const enabled = ref(true)
|
|
310
|
+
return { args, color, enabled }
|
|
311
|
+
},
|
|
312
|
+
template: `
|
|
313
|
+
<div>
|
|
314
|
+
<EditorColor v-bind="args" v-model="color" v-model:enabled="enabled" />
|
|
315
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
316
|
+
<p class="small-normal text-gray-700">{{ enabled }}</p>
|
|
317
|
+
</div>
|
|
318
|
+
`,
|
|
319
|
+
}),
|
|
320
|
+
play: async ({ canvasElement }) => {
|
|
321
|
+
const canvas = within(canvasElement)
|
|
322
|
+
const content = contentWrapper(canvasElement)
|
|
323
|
+
|
|
324
|
+
expect(content).not.toHaveAttribute('inert')
|
|
325
|
+
|
|
326
|
+
await userEvent.click(canvas.getByRole('switch'))
|
|
327
|
+
|
|
328
|
+
await waitFor(() => {
|
|
329
|
+
expect(content).toHaveAttribute('inert')
|
|
330
|
+
expect(canvas.getByText('false')).toBeInTheDocument()
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
// The colour survives the collapse: only the switch state changed.
|
|
334
|
+
expect(canvas.getByText('bg-[#1777EA]')).toBeInTheDocument()
|
|
335
|
+
},
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export const SwitchableOff: Story = {
|
|
339
|
+
args: { switchable: true },
|
|
340
|
+
render: (args) => ({
|
|
341
|
+
components: { EditorColor },
|
|
342
|
+
setup() {
|
|
343
|
+
const color = ref<EditorColorValue>('bg-[#1777EA]')
|
|
344
|
+
const enabled = ref(false)
|
|
345
|
+
return { args, color, enabled }
|
|
346
|
+
},
|
|
347
|
+
template: `
|
|
348
|
+
<div>
|
|
349
|
+
<EditorColor v-bind="args" v-model="color" v-model:enabled="enabled" />
|
|
350
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
351
|
+
</div>
|
|
352
|
+
`,
|
|
353
|
+
}),
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export const TextProperty: Story = {
|
|
357
|
+
args: { property: 'text', label: 'Tekstkleur' },
|
|
358
|
+
render: (args) => ({
|
|
359
|
+
components: { EditorColor },
|
|
360
|
+
setup() {
|
|
361
|
+
const color = ref<EditorColorValue>('text-[#1777EA]')
|
|
362
|
+
return { args, color }
|
|
363
|
+
},
|
|
364
|
+
template: `
|
|
365
|
+
<div>
|
|
366
|
+
<EditorColor v-bind="args" v-model="color" />
|
|
367
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
368
|
+
</div>
|
|
369
|
+
`,
|
|
370
|
+
}),
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** A stored value the component cannot parse shows the placeholder instead. */
|
|
374
|
+
export const Empty: Story = {
|
|
375
|
+
render: (args) => ({
|
|
376
|
+
components: { EditorColor },
|
|
377
|
+
setup() {
|
|
378
|
+
const color = ref('bg-red-500' as EditorColorValue)
|
|
379
|
+
return { args, color }
|
|
380
|
+
},
|
|
381
|
+
template: `
|
|
382
|
+
<div>
|
|
383
|
+
<EditorColor v-bind="args" v-model="color" />
|
|
384
|
+
<p class="small-normal text-gray-700">{{ color }}</p>
|
|
385
|
+
</div>
|
|
386
|
+
`,
|
|
387
|
+
}),
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* The gradient stories render in a 256px column, the width of the editor's
|
|
392
|
+
* settings column, so the panel's content is the 224px the layout is made for.
|
|
393
|
+
*/
|
|
394
|
+
const gradientRender = (initial: EditorColorValue) => (args: Story['args']) => ({
|
|
395
|
+
components: { EditorColor },
|
|
396
|
+
setup() {
|
|
397
|
+
const color = ref<EditorColorValue>(initial)
|
|
398
|
+
return { args, color }
|
|
399
|
+
},
|
|
400
|
+
template: `
|
|
401
|
+
<div class="w-64">
|
|
402
|
+
<EditorColor v-bind="args" v-model="color" />
|
|
403
|
+
<p class="mt-2 extra-small-normal break-all text-gray-700">{{ color }}</p>
|
|
404
|
+
</div>
|
|
405
|
+
`,
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
/** The panel, found by the stop bar it always contains in the gradient mode. */
|
|
409
|
+
const gradientPanel = (canvasElement: HTMLElement) =>
|
|
410
|
+
canvasElement
|
|
411
|
+
.querySelector('[data-gradient-bar]')!
|
|
412
|
+
.closest<HTMLElement>('div[style*="position: fixed"]')!
|
|
413
|
+
|
|
414
|
+
/** Opens the picker and checks that the panel fits: no scrolling, nothing sticking out. */
|
|
415
|
+
const openAndMeasure = async (canvasElement: HTMLElement) => {
|
|
416
|
+
const trigger = within(canvasElement).getAllByRole('button')[0]!
|
|
417
|
+
await userEvent.click(trigger)
|
|
418
|
+
await waitFor(() => expect(canvasElement.querySelector('[data-gradient-bar]')).not.toBeNull())
|
|
419
|
+
const panel = gradientPanel(canvasElement)
|
|
420
|
+
const surface = panel.querySelector<HTMLElement>('.rounded-xl')!
|
|
421
|
+
// Layout pixels, not the bounding box: the test canvas may be zoomed. The
|
|
422
|
+
// spec expects ~560px at a content width of 224px, well within 720px; the
|
|
423
|
+
// measured height is the guard, so the panel cannot quietly grow.
|
|
424
|
+
expect(surface.offsetHeight).toBeLessThanOrEqual(PANEL_HEIGHT)
|
|
425
|
+
expect(surface.scrollHeight).toBeLessThanOrEqual(surface.clientHeight)
|
|
426
|
+
// The panel is exactly as wide as the trigger: nothing in it is wider.
|
|
427
|
+
expect(surface.offsetWidth).toBe(trigger.offsetWidth)
|
|
428
|
+
expect(surface.scrollWidth).toBeLessThanOrEqual(surface.clientWidth)
|
|
429
|
+
// The preview square takes the full content width: 224px in this column.
|
|
430
|
+
const square = panel.querySelector<HTMLElement>('[data-gradient-preview]')!
|
|
431
|
+
expect(square.offsetWidth).toBe(224)
|
|
432
|
+
expect(square.offsetHeight).toBe(224)
|
|
433
|
+
// Every handle lies in the square; half a handle may cross its edge.
|
|
434
|
+
const box = square.getBoundingClientRect()
|
|
435
|
+
square
|
|
436
|
+
.querySelectorAll<HTMLElement>('[data-stop-point], [data-gradient-center]')
|
|
437
|
+
.forEach((handle) => {
|
|
438
|
+
const r = handle.getBoundingClientRect()
|
|
439
|
+
const x = r.left + r.width / 2
|
|
440
|
+
const y = r.top + r.height / 2
|
|
441
|
+
expect(x).toBeGreaterThanOrEqual(box.left - 0.5)
|
|
442
|
+
expect(x).toBeLessThanOrEqual(box.right + 0.5)
|
|
443
|
+
expect(y).toBeGreaterThanOrEqual(box.top - 0.5)
|
|
444
|
+
expect(y).toBeLessThanOrEqual(box.bottom + 0.5)
|
|
445
|
+
})
|
|
446
|
+
// No field in a row overflows or clips its value. A role name longer than
|
|
447
|
+
// "Hoofdkleur" -- the one the layout is measured with -- may truncate.
|
|
448
|
+
const fields = panel.querySelectorAll<HTMLInputElement>('[data-gradient-row] input')
|
|
449
|
+
const measured = [...fields].filter((input) => !input.readOnly || input.value === 'Hoofdkleur')
|
|
450
|
+
measured.forEach((input) => {
|
|
451
|
+
expect(input.scrollWidth, input.getAttribute('aria-label') ?? '').toBeLessThanOrEqual(
|
|
452
|
+
input.clientWidth,
|
|
453
|
+
)
|
|
454
|
+
})
|
|
455
|
+
return panel
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** Measured height of the tallest gradient panel (three stops, a role) in the 224px column. */
|
|
459
|
+
const PANEL_HEIGHT = 544
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* A linear gradient: the to-point in the preview sets the angle, two stops in
|
|
463
|
+
* the bar and the list.
|
|
464
|
+
*/
|
|
465
|
+
export const GradientLinear: Story = {
|
|
466
|
+
// Axe runs on the open panel, after the play function.
|
|
467
|
+
parameters: { a11y: { test: 'error' } },
|
|
468
|
+
args: {
|
|
469
|
+
modes: ['solid', 'gradient'],
|
|
470
|
+
themeRoles: THEME_ROLES,
|
|
471
|
+
suggestions: ['#7F56D9', '#432E73'],
|
|
472
|
+
},
|
|
473
|
+
render: gradientRender('bg-linear-135 from-[#7f56d9] to-[#432e73cc]'),
|
|
474
|
+
play: async ({ canvasElement }) => {
|
|
475
|
+
const panel = await openAndMeasure(canvasElement)
|
|
476
|
+
const angle = within(panel).getByRole('slider', { name: 'Hoek' })
|
|
477
|
+
expect(angle).toHaveAttribute('aria-valuetext', '135 graden')
|
|
478
|
+
expect(within(panel).getAllByRole('group', { name: /^Kleurstop \d van 2$/ })).toHaveLength(2)
|
|
479
|
+
},
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/** A radial gradient with its centre off the middle: the centre point and the stops on one ray. */
|
|
483
|
+
export const GradientRadial: Story = {
|
|
484
|
+
// Axe runs on the open panel, after the play function.
|
|
485
|
+
parameters: { a11y: { test: 'error' } },
|
|
486
|
+
args: { modes: ['solid', 'gradient'], themeRoles: THEME_ROLES },
|
|
487
|
+
render: gradientRender('bg-radial-[at_30%_70%] from-theme-accent to-[#ffffff00]'),
|
|
488
|
+
play: async ({ canvasElement }) => {
|
|
489
|
+
const panel = await openAndMeasure(canvasElement)
|
|
490
|
+
expect(within(panel).getByRole('slider', { name: 'Middelpunt' })).toHaveAttribute(
|
|
491
|
+
'aria-valuetext',
|
|
492
|
+
'30 % van links, 70 % van boven',
|
|
493
|
+
)
|
|
494
|
+
// The first stop is a theme role: in its popover the opacity is locked, with the hint.
|
|
495
|
+
const [swatch] = within(panel).getAllByRole('button', { name: 'Kleur kiezen' })
|
|
496
|
+
await userEvent.click(swatch!)
|
|
497
|
+
const dialog = await within(panel).findByRole('dialog', { name: 'Kleur van kleurstop 1' })
|
|
498
|
+
const opacity = within(dialog).getByRole('slider', { name: 'Dekking' })
|
|
499
|
+
expect(opacity).toHaveAttribute('aria-disabled', 'true')
|
|
500
|
+
expect(opacity).toHaveAccessibleDescription('Huisstijlkleuren zijn altijd dekkend.')
|
|
501
|
+
},
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Three stops, the most the gradient takes: adding is unavailable, with the
|
|
506
|
+
* reason, and removing is possible. The middle stop is a theme role and the
|
|
507
|
+
* last one translucent, so its hex has eight digits. This is the tallest panel,
|
|
508
|
+
* and it carries the height check. The play also opens a stop's colour popover
|
|
509
|
+
* and closes it with Escape.
|
|
510
|
+
*/
|
|
511
|
+
export const GradientThreeStops: Story = {
|
|
512
|
+
// Axe runs on the open panel, after the play function.
|
|
513
|
+
parameters: { a11y: { test: 'error' } },
|
|
514
|
+
args: {
|
|
515
|
+
modes: ['solid', 'gradient'],
|
|
516
|
+
themeRoles: THEME_ROLES,
|
|
517
|
+
suggestions: ['#7F56D9', '#432E73', '#F0F4F8', '#1F2937', '#F59E0B', '#1777EA'],
|
|
518
|
+
},
|
|
519
|
+
render: gradientRender('bg-linear-90 from-[#7f56d9] via-theme-main via-40% to-[#432e73cc]'),
|
|
520
|
+
play: async ({ canvasElement }) => {
|
|
521
|
+
const panel = await openAndMeasure(canvasElement)
|
|
522
|
+
const add = within(panel).getByRole('button', { name: 'Kleurstop toevoegen' })
|
|
523
|
+
expect(add).toHaveAttribute('aria-disabled', 'true')
|
|
524
|
+
expect(add).toHaveAccessibleDescription('Een verloop heeft maximaal 3 kleurstops.')
|
|
525
|
+
within(panel)
|
|
526
|
+
.getAllByRole('button', { name: 'Verwijderen' })
|
|
527
|
+
.forEach((remove) => expect(remove).toBeEnabled())
|
|
528
|
+
expect(within(panel).getAllByRole('slider', { name: /^Kleurstop \d van 3$/ })).toHaveLength(3)
|
|
529
|
+
|
|
530
|
+
const [swatch] = within(panel).getAllByRole('button', { name: 'Kleur kiezen' })
|
|
531
|
+
await userEvent.click(swatch!)
|
|
532
|
+
const dialog = await within(panel).findByRole('dialog', { name: 'Kleur van kleurstop 1' })
|
|
533
|
+
expect(within(dialog).getByRole('textbox', { name: 'Hex' })).toBeInTheDocument()
|
|
534
|
+
await userEvent.keyboard('{Escape}')
|
|
535
|
+
await waitFor(() => expect(within(panel).queryByRole('dialog')).toBeNull())
|
|
536
|
+
expect(swatch).toHaveFocus()
|
|
537
|
+
expect(canvasElement.querySelector('[data-gradient-bar]')).not.toBeNull()
|
|
538
|
+
},
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Only the gradient, without the Effen | Verloop switch. The stored value is a
|
|
543
|
+
* solid colour, so the panel opens on its fade -- the colour to the same hex
|
|
544
|
+
* at `00` -- and nothing is written until the first change.
|
|
545
|
+
*/
|
|
546
|
+
export const GradientOnly: Story = {
|
|
547
|
+
// Axe runs on the open panel, after the play function.
|
|
548
|
+
parameters: { a11y: { test: 'error' } },
|
|
549
|
+
args: { modes: ['gradient'], themeRoles: THEME_ROLES },
|
|
550
|
+
render: gradientRender('bg-[#7f56d9]'),
|
|
551
|
+
play: async ({ canvasElement }) => {
|
|
552
|
+
const panel = await openAndMeasure(canvasElement)
|
|
553
|
+
expect(within(panel).queryByRole('button', { name: 'Effen' })).toBeNull()
|
|
554
|
+
const hexes = within(panel).getAllByRole('textbox', { name: 'Hex' })
|
|
555
|
+
expect(hexes.map((input) => (input as HTMLInputElement).value)).toEqual([
|
|
556
|
+
'#7f56d9',
|
|
557
|
+
'#7f56d900',
|
|
558
|
+
])
|
|
559
|
+
expect(canvasElement).toHaveTextContent('bg-[#7f56d9]')
|
|
560
|
+
// Until the first change the trigger shows the stored colour, not the placeholder.
|
|
561
|
+
expect(within(canvasElement).getAllByRole('button')[0]).toHaveTextContent('#7f56d9')
|
|
562
|
+
},
|
|
563
|
+
}
|