@nexxtmove/ui 2.6.0 → 2.8.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/components/editor/molecules/EditorColor/EditorColor.vue.d.ts +7 -5
- package/dist/components/editor/molecules/EditorColor/_ColorField.vue.d.ts +4 -1
- package/dist/components/organisms/ColorPicker/ColorPicker.vue.d.ts +3 -1
- package/dist/index.js +95 -172
- 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 +633 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.test.ts +776 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.vue +7 -5
- package/src/components/editor/molecules/EditorColor/EditorColorGradient.test.ts +832 -0
- package/src/components/editor/molecules/EditorColor/_ColorField.vue +20 -15
- 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 +696 -0
- package/src/components/organisms/ColorPicker/ColorPicker.vue +12 -3
- 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,187 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import { expect, userEvent, waitFor, within } from 'storybook/test'
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
import EditorBorder from './EditorBorder.vue'
|
|
5
|
+
import type { EditorColorThemeRole } from '../EditorColor/colorClass'
|
|
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, with the hex each role has in the
|
|
16
|
+
* customer's theme right now. Heading and text share a hex, so both swatches
|
|
17
|
+
* are pressed by that one colour.
|
|
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 Border',
|
|
29
|
+
component: EditorBorder,
|
|
30
|
+
decorators: [() => ({ template: '<div class="w-100 mx-auto"><story /></div>' })],
|
|
31
|
+
argTypes: {
|
|
32
|
+
label: { control: 'text' },
|
|
33
|
+
switchable: { control: 'boolean' },
|
|
34
|
+
},
|
|
35
|
+
} satisfies Meta<typeof EditorBorder>
|
|
36
|
+
|
|
37
|
+
type Story = StoryObj<typeof EditorBorder>
|
|
38
|
+
|
|
39
|
+
export const Default: Story = {
|
|
40
|
+
render: (args) => ({
|
|
41
|
+
components: { EditorBorder },
|
|
42
|
+
setup() {
|
|
43
|
+
const border = ref('border border-[#1777EA]')
|
|
44
|
+
const enabled = ref(true)
|
|
45
|
+
return { args, border, enabled }
|
|
46
|
+
},
|
|
47
|
+
// The value is shown as-is: it is the Tailwind classes the editor applies.
|
|
48
|
+
template: `
|
|
49
|
+
<div>
|
|
50
|
+
<EditorBorder v-bind="args" v-model="border" v-model:enabled="enabled" />
|
|
51
|
+
<p class="small-normal text-gray-700">{{ border }}</p>
|
|
52
|
+
<p class="small-normal text-gray-700">{{ enabled }}</p>
|
|
53
|
+
</div>
|
|
54
|
+
`,
|
|
55
|
+
}),
|
|
56
|
+
// Two things jsdom cannot see: the collapse leaves the value alone, and the
|
|
57
|
+
// panel is sized against its trigger rather than the picker's fixed w-70.
|
|
58
|
+
play: async ({ canvasElement }) => {
|
|
59
|
+
const canvas = within(canvasElement)
|
|
60
|
+
const content = contentWrapper(canvasElement)
|
|
61
|
+
// The picker trigger is the only button; the switch has the switch role.
|
|
62
|
+
const trigger = canvas.getByRole('button')
|
|
63
|
+
|
|
64
|
+
await userEvent.click(trigger)
|
|
65
|
+
|
|
66
|
+
// The floating container is found by the inline position it is given, not by
|
|
67
|
+
// a styling class: it is the only fixed-positioned ancestor of the panel.
|
|
68
|
+
await waitFor(() => {
|
|
69
|
+
const panel = canvasElement
|
|
70
|
+
.querySelector('[placeholder="#000000"]')!
|
|
71
|
+
.closest<HTMLElement>('div[style*="position: fixed"]')!
|
|
72
|
+
|
|
73
|
+
expect(panel.getBoundingClientRect().width).toBe(trigger.getBoundingClientRect().width)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
await userEvent.keyboard('{Escape}')
|
|
77
|
+
|
|
78
|
+
expect(content).not.toHaveAttribute('inert')
|
|
79
|
+
|
|
80
|
+
await userEvent.click(canvas.getByRole('switch'))
|
|
81
|
+
|
|
82
|
+
await waitFor(() => {
|
|
83
|
+
expect(content).toHaveAttribute('inert')
|
|
84
|
+
expect(canvas.getByText('false')).toBeInTheDocument()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Width and colour survive the collapse: only the switch state changed.
|
|
88
|
+
expect(canvas.getByText('border border-[#1777EA]')).toBeInTheDocument()
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const Disabled: Story = {
|
|
93
|
+
render: (args) => ({
|
|
94
|
+
components: { EditorBorder },
|
|
95
|
+
setup() {
|
|
96
|
+
const border = ref('border-2 border-[#1777EA]')
|
|
97
|
+
const enabled = ref(false)
|
|
98
|
+
return { args, border, enabled }
|
|
99
|
+
},
|
|
100
|
+
template: `
|
|
101
|
+
<div>
|
|
102
|
+
<EditorBorder v-bind="args" v-model="border" v-model:enabled="enabled" />
|
|
103
|
+
<p class="small-normal text-gray-700">{{ border }}</p>
|
|
104
|
+
</div>
|
|
105
|
+
`,
|
|
106
|
+
}),
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** A value without a colour class leaves the picker on its placeholder. */
|
|
110
|
+
export const NoColor: Story = {
|
|
111
|
+
render: (args) => ({
|
|
112
|
+
components: { EditorBorder },
|
|
113
|
+
setup() {
|
|
114
|
+
const border = ref('border-2')
|
|
115
|
+
return { args, border }
|
|
116
|
+
},
|
|
117
|
+
template: `
|
|
118
|
+
<div>
|
|
119
|
+
<EditorBorder v-bind="args" v-model="border" />
|
|
120
|
+
<p class="small-normal text-gray-700">{{ border }}</p>
|
|
121
|
+
</div>
|
|
122
|
+
`,
|
|
123
|
+
}),
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const CustomMessages: Story = {
|
|
127
|
+
args: {
|
|
128
|
+
label: 'Border',
|
|
129
|
+
messages: { unit: 'pixels', placeholder: 'Pick a colour...', width: 'Thickness' },
|
|
130
|
+
},
|
|
131
|
+
render: (args) => ({
|
|
132
|
+
components: { EditorBorder },
|
|
133
|
+
setup() {
|
|
134
|
+
const border = ref('border-4')
|
|
135
|
+
return { args, border }
|
|
136
|
+
},
|
|
137
|
+
template: `<EditorBorder v-bind="args" v-model="border" />`,
|
|
138
|
+
}),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The theme roles are a shortcut to a hex: picking one writes `border-[#hex]`,
|
|
143
|
+
* exactly as a colour picked by hand does, and a swatch is pressed while the
|
|
144
|
+
* border has its hex.
|
|
145
|
+
*/
|
|
146
|
+
export const WithThemeRoles: Story = {
|
|
147
|
+
args: { themeRoles: THEME_ROLES },
|
|
148
|
+
render: (args) => ({
|
|
149
|
+
components: { EditorBorder },
|
|
150
|
+
setup() {
|
|
151
|
+
const border = ref('border-2 border-[#1777ea]')
|
|
152
|
+
return { args, border }
|
|
153
|
+
},
|
|
154
|
+
template: `
|
|
155
|
+
<div>
|
|
156
|
+
<EditorBorder v-bind="args" v-model="border" />
|
|
157
|
+
<p class="small-normal text-gray-700">{{ border }}</p>
|
|
158
|
+
</div>
|
|
159
|
+
`,
|
|
160
|
+
}),
|
|
161
|
+
// Reached and picked with the keyboard alone, through the panel's real tab order.
|
|
162
|
+
play: async ({ canvasElement }) => {
|
|
163
|
+
const canvas = within(canvasElement)
|
|
164
|
+
|
|
165
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Kies kleur...' }))
|
|
166
|
+
|
|
167
|
+
const main = await canvas.findByRole('button', { name: 'Hoofdkleur' })
|
|
168
|
+
const accent = canvas.getByRole('button', { name: 'Accentkleur' })
|
|
169
|
+
|
|
170
|
+
// The stored hex is lowercase, the role's uppercase: still the same colour.
|
|
171
|
+
expect(main).toHaveAttribute('aria-pressed', 'true')
|
|
172
|
+
expect(accent).toHaveAttribute('aria-pressed', 'false')
|
|
173
|
+
|
|
174
|
+
for (let step = 0; step < 20 && document.activeElement !== accent; step++) {
|
|
175
|
+
await userEvent.tab()
|
|
176
|
+
}
|
|
177
|
+
expect(accent).toHaveFocus()
|
|
178
|
+
|
|
179
|
+
await userEvent.keyboard('{Enter}')
|
|
180
|
+
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(canvas.getByText('border-2 border-[#F59E0B]')).toBeInTheDocument()
|
|
183
|
+
expect(accent).toHaveAttribute('aria-pressed', 'true')
|
|
184
|
+
expect(main).toHaveAttribute('aria-pressed', 'false')
|
|
185
|
+
})
|
|
186
|
+
},
|
|
187
|
+
}
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeAll } from 'vitest'
|
|
2
|
+
import { flushPromises, mount, type VueWrapper } from '@vue/test-utils'
|
|
3
|
+
import { nextTick, ref } from 'vue'
|
|
4
|
+
import EditorBorder, { EDITOR_BORDER_STEPS } from './EditorBorder.vue'
|
|
5
|
+
import NexxtSlider from '../../../atoms/Slider/Slider.vue'
|
|
6
|
+
import NexxtColorPicker from '../../../organisms/ColorPicker/ColorPicker.vue'
|
|
7
|
+
import NexxtTooltip from '../../../atoms/Tooltip/Tooltip.vue'
|
|
8
|
+
import type { EditorColorThemeRole } from '../EditorColor/colorClass'
|
|
9
|
+
|
|
10
|
+
// jsdom ships neither ResizeObserver -- reka-ui's slider observes its track and
|
|
11
|
+
// its colour primitives use it internally -- nor a layout engine for
|
|
12
|
+
// floating-ui to measure, nor pointer capture, which the slider thumb takes on
|
|
13
|
+
// pointerdown.
|
|
14
|
+
beforeAll(() => {
|
|
15
|
+
global.ResizeObserver = class ResizeObserver {
|
|
16
|
+
observe() {}
|
|
17
|
+
unobserve() {}
|
|
18
|
+
disconnect() {}
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
Element.prototype.setPointerCapture ??= () => {}
|
|
23
|
+
Element.prototype.releasePointerCapture ??= () => {}
|
|
24
|
+
Element.prototype.hasPointerCapture ??= () => false
|
|
25
|
+
|
|
26
|
+
vi.mock('@floating-ui/vue', async (importOriginal) => {
|
|
27
|
+
const actual = await importOriginal<typeof import('@floating-ui/vue')>()
|
|
28
|
+
return {
|
|
29
|
+
...actual,
|
|
30
|
+
useFloating: () => ({
|
|
31
|
+
floatingStyles: ref({}),
|
|
32
|
+
placement: ref('bottom-start'),
|
|
33
|
+
isPositioned: ref(true),
|
|
34
|
+
}),
|
|
35
|
+
autoUpdate: () => () => {},
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
/** The index the slider sits on, read from the model handed to the slider. */
|
|
40
|
+
const sliderIndex = (wrapper: VueWrapper) => wrapper.findComponent(NexxtSlider).props('modelValue')
|
|
41
|
+
|
|
42
|
+
/** The value the block hands to the picker, before the picker normalises it. */
|
|
43
|
+
const pickerValue = (wrapper: VueWrapper) =>
|
|
44
|
+
wrapper.findComponent(NexxtColorPicker).props('modelValue')
|
|
45
|
+
|
|
46
|
+
/** The collapsing wrapper, found by the grid row it animates, not by styling. */
|
|
47
|
+
const contentWrapper = (wrapper: VueWrapper) => wrapper.find('[class*="grid-rows-"]')
|
|
48
|
+
|
|
49
|
+
// Heading and text share a hex on purpose: a role is a shortcut to its hex here,
|
|
50
|
+
// so both swatches are pressed by the same colour.
|
|
51
|
+
const THEME_ROLES: EditorColorThemeRole[] = [
|
|
52
|
+
{ role: 'main', label: 'Hoofdkleur', hex: '#1777EA' },
|
|
53
|
+
{ role: 'accent', label: 'Accentkleur', hex: '#F59E0B' },
|
|
54
|
+
{ role: 'heading', label: 'Koppen', hex: '#1F2937' },
|
|
55
|
+
{ role: 'text', label: 'Tekst', hex: '#1F2937' },
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
/** The panel content only exists while the picker is open. */
|
|
59
|
+
const open = async (wrapper: VueWrapper) => {
|
|
60
|
+
await wrapper.findComponent(NexxtColorPicker).find('button').trigger('click')
|
|
61
|
+
await flushPromises()
|
|
62
|
+
await nextTick()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The role buttons are the only pressable buttons in the block. */
|
|
66
|
+
const roleButtons = (wrapper: VueWrapper) => wrapper.findAll('button[aria-pressed]')
|
|
67
|
+
|
|
68
|
+
/** A role is a bare swatch, so its label is its accessible name. */
|
|
69
|
+
const roleLabels = (wrapper: VueWrapper) =>
|
|
70
|
+
roleButtons(wrapper).map((button) => button.attributes('aria-label'))
|
|
71
|
+
|
|
72
|
+
const roleButton = (wrapper: VueWrapper, label: string) =>
|
|
73
|
+
roleButtons(wrapper).find((button) => button.attributes('aria-label') === label)!
|
|
74
|
+
|
|
75
|
+
const pressedLabels = (wrapper: VueWrapper) =>
|
|
76
|
+
roleButtons(wrapper)
|
|
77
|
+
.filter((button) => button.attributes('aria-pressed') === 'true')
|
|
78
|
+
.map((button) => button.attributes('aria-label'))
|
|
79
|
+
|
|
80
|
+
describe('EditorBorder', () => {
|
|
81
|
+
// No assertions on typography utilities: the label and the readout are found
|
|
82
|
+
// by text or by structure, so restyling the block, the slider or the picker
|
|
83
|
+
// cannot turn these red.
|
|
84
|
+
it('renders the Dutch default label', () => {
|
|
85
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border border-[#1777EA]' } })
|
|
86
|
+
|
|
87
|
+
expect(wrapper.text()).toContain('Rand')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('renders a custom label', () => {
|
|
91
|
+
const wrapper = mount(EditorBorder, {
|
|
92
|
+
props: { modelValue: 'border border-[#1777EA]', label: 'Border' },
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
expect(wrapper.text()).toContain('Border')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('reads the width and the colour out of the model', () => {
|
|
99
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-2 border-[#1777EA]' } })
|
|
100
|
+
|
|
101
|
+
expect(sliderIndex(wrapper)).toEqual([1])
|
|
102
|
+
expect(pickerValue(wrapper)).toBe('#1777EA')
|
|
103
|
+
expect(wrapper.text()).toContain('2 px')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// The two halves are independent tokens, so a consumer -- or an earlier
|
|
107
|
+
// version of this component -- may have stored them the other way round.
|
|
108
|
+
it('reads the halves in either order', () => {
|
|
109
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-[#1777EA] border-4' } })
|
|
110
|
+
|
|
111
|
+
expect(sliderIndex(wrapper)).toEqual([2])
|
|
112
|
+
expect(pickerValue(wrapper)).toBe('#1777EA')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('leaves the picker empty when the model carries no colour', () => {
|
|
116
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-2' } })
|
|
117
|
+
|
|
118
|
+
expect(sliderIndex(wrapper)).toEqual([1])
|
|
119
|
+
expect(pickerValue(wrapper)).toBe('')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
// Unparseable tokens are not reported as a problem: the thumb sits on the
|
|
123
|
+
// first step and the first change writes a valid string back.
|
|
124
|
+
it('falls back to the first step for a string it cannot parse', () => {
|
|
125
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'rubbish' } })
|
|
126
|
+
|
|
127
|
+
expect(sliderIndex(wrapper)).toEqual([0])
|
|
128
|
+
expect(pickerValue(wrapper)).toBe('')
|
|
129
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('spans the whole width scale', () => {
|
|
133
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border' } })
|
|
134
|
+
const slider = wrapper.findComponent(NexxtSlider)
|
|
135
|
+
|
|
136
|
+
expect(slider.props('min')).toBe(0)
|
|
137
|
+
expect(slider.props('max')).toBe(3)
|
|
138
|
+
expect(slider.props('step')).toBe(1)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('writes the new width and keeps the colour when the slider moves', async () => {
|
|
142
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-2 border-[#1777EA]' } })
|
|
143
|
+
|
|
144
|
+
wrapper.findComponent(NexxtSlider).vm.$emit('update:modelValue', [3])
|
|
145
|
+
await wrapper.vm.$nextTick()
|
|
146
|
+
|
|
147
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['border-8 border-[#1777EA]']])
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('writes the picked colour and keeps the width', async () => {
|
|
151
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-2 border-[#1777EA]' } })
|
|
152
|
+
|
|
153
|
+
wrapper.findComponent(NexxtColorPicker).vm.$emit('update:modelValue', '#00FF00')
|
|
154
|
+
await wrapper.vm.$nextTick()
|
|
155
|
+
|
|
156
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['border-2 border-[#00FF00]']])
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// Without a colour the width stands alone, rather than gaining an empty
|
|
160
|
+
// `border-[]` that Tailwind would not understand.
|
|
161
|
+
it('writes the width alone when there is no colour', async () => {
|
|
162
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border' } })
|
|
163
|
+
|
|
164
|
+
wrapper.findComponent(NexxtSlider).vm.$emit('update:modelValue', [2])
|
|
165
|
+
await wrapper.vm.$nextTick()
|
|
166
|
+
|
|
167
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['border-4']])
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('keeps the colour when the picker reports an empty value', async () => {
|
|
171
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-2 border-[#1777EA]' } })
|
|
172
|
+
|
|
173
|
+
wrapper.findComponent(NexxtColorPicker).vm.$emit('update:modelValue', '')
|
|
174
|
+
await wrapper.vm.$nextTick()
|
|
175
|
+
|
|
176
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('follows the model when the parent changes it', async () => {
|
|
180
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-2 border-[#1777EA]' } })
|
|
181
|
+
|
|
182
|
+
await wrapper.setProps({ modelValue: 'border-8' })
|
|
183
|
+
|
|
184
|
+
expect(sliderIndex(wrapper)).toEqual([3])
|
|
185
|
+
expect(pickerValue(wrapper)).toBe('')
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('emits update:enabled when the switch is toggled', async () => {
|
|
189
|
+
const wrapper = mount(EditorBorder, {
|
|
190
|
+
props: { modelValue: 'border-2 border-[#1777EA]', enabled: true },
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
await wrapper.find('input[type="checkbox"]').setValue(false)
|
|
194
|
+
|
|
195
|
+
expect(wrapper.emitted('update:enabled')).toEqual([[false]])
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('makes the content inert when enabled is false', () => {
|
|
199
|
+
const wrapper = mount(EditorBorder, {
|
|
200
|
+
props: { modelValue: 'border-2 border-[#1777EA]', enabled: false },
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
expect(contentWrapper(wrapper).attributes('inert')).toBeDefined()
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('keeps the content interactive when enabled is true', () => {
|
|
207
|
+
const wrapper = mount(EditorBorder, {
|
|
208
|
+
props: { modelValue: 'border-2 border-[#1777EA]', enabled: true },
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
expect(contentWrapper(wrapper).attributes('inert')).toBeUndefined()
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
// Switchable by default here, unlike the other blocks: a border is off as
|
|
215
|
+
// often as it is on.
|
|
216
|
+
it('renders the switch by default', () => {
|
|
217
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-2 border-[#1777EA]' } })
|
|
218
|
+
|
|
219
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('renders no switch when switchable is false', () => {
|
|
223
|
+
const wrapper = mount(EditorBorder, {
|
|
224
|
+
props: { modelValue: 'border-2 border-[#1777EA]', switchable: false },
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// aria-valuenow is the step index, which is meaningless read aloud; the text
|
|
231
|
+
// the readout shows is what the thumb announces instead. The header names the
|
|
232
|
+
// whole setting, so the thumb is named after the width alone.
|
|
233
|
+
it('announces the width in pixels on the thumb', () => {
|
|
234
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-4' } })
|
|
235
|
+
const thumb = wrapper.find('[role="slider"]')
|
|
236
|
+
|
|
237
|
+
expect(thumb.attributes('aria-valuetext')).toBe('4 px')
|
|
238
|
+
expect(thumb.attributes('aria-label')).toBe('Dikte')
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('renders custom messages', () => {
|
|
242
|
+
const wrapper = mount(EditorBorder, {
|
|
243
|
+
props: {
|
|
244
|
+
modelValue: 'border-4',
|
|
245
|
+
messages: { unit: 'pixels', placeholder: 'Pick one', width: 'Thickness' },
|
|
246
|
+
},
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
expect(wrapper.text()).toContain('4 pixels')
|
|
250
|
+
expect(wrapper.text()).toContain('Pick one')
|
|
251
|
+
expect(wrapper.find('[role="slider"]').attributes('aria-label')).toBe('Thickness')
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
describe('EditorBorder theme roles', () => {
|
|
256
|
+
it('renders no theme section without roles', async () => {
|
|
257
|
+
const wrapper = mount(EditorBorder, { props: { modelValue: 'border-2 border-[#1777EA]' } })
|
|
258
|
+
|
|
259
|
+
await open(wrapper)
|
|
260
|
+
|
|
261
|
+
expect(wrapper.text()).not.toContain('Huisstijl')
|
|
262
|
+
expect(roleButtons(wrapper)).toHaveLength(0)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
// An empty list is the default, so it must render what no prop renders.
|
|
266
|
+
it('renders the same markup for an empty list as without the prop', async () => {
|
|
267
|
+
const without = mount(EditorBorder, { props: { modelValue: 'border-2 border-[#1777EA]' } })
|
|
268
|
+
const empty = mount(EditorBorder, {
|
|
269
|
+
props: { modelValue: 'border-2 border-[#1777EA]', themeRoles: [] },
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
expect(empty.html()).toBe(without.html())
|
|
273
|
+
|
|
274
|
+
await open(without)
|
|
275
|
+
await open(empty)
|
|
276
|
+
|
|
277
|
+
expect(empty.html()).toBe(without.html())
|
|
278
|
+
expect(roleButtons(empty)).toHaveLength(0)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('offers the passed roles under the default section title', async () => {
|
|
282
|
+
const wrapper = mount(EditorBorder, {
|
|
283
|
+
props: { modelValue: 'border-2 border-[#1777EA]', themeRoles: THEME_ROLES },
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
await open(wrapper)
|
|
287
|
+
|
|
288
|
+
expect(wrapper.text()).toContain('Huisstijl')
|
|
289
|
+
expect(roleLabels(wrapper)).toEqual(THEME_ROLES.map(({ label }) => label))
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('renders each role as a bare swatch inside a tooltip', async () => {
|
|
293
|
+
const wrapper = mount(EditorBorder, {
|
|
294
|
+
props: { modelValue: 'border-2', themeRoles: THEME_ROLES },
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
await open(wrapper)
|
|
298
|
+
|
|
299
|
+
expect(wrapper.findAllComponents(NexxtTooltip)).toHaveLength(THEME_ROLES.length)
|
|
300
|
+
roleButtons(wrapper).forEach((button) => expect(button.text()).toBe(''))
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('takes the section title from messages', async () => {
|
|
304
|
+
const wrapper = mount(EditorBorder, {
|
|
305
|
+
props: {
|
|
306
|
+
modelValue: 'border-2',
|
|
307
|
+
themeRoles: THEME_ROLES,
|
|
308
|
+
messages: { themeLabel: 'Brand' },
|
|
309
|
+
},
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
await open(wrapper)
|
|
313
|
+
|
|
314
|
+
expect(wrapper.text()).toContain('Brand')
|
|
315
|
+
expect(wrapper.text()).not.toContain('Huisstijl')
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
// The picker throws on a colour it cannot read.
|
|
319
|
+
it('leaves out a role with an unreadable hex', async () => {
|
|
320
|
+
const wrapper = mount(EditorBorder, {
|
|
321
|
+
props: {
|
|
322
|
+
modelValue: 'border-2',
|
|
323
|
+
themeRoles: [
|
|
324
|
+
{ role: 'main', label: 'Hoofdkleur', hex: '#1777EA' },
|
|
325
|
+
{ role: 'accent', label: 'Accentkleur', hex: 'orange' },
|
|
326
|
+
{ role: 'background', label: 'Achtergrond', hex: '#FFF' },
|
|
327
|
+
{ role: 'heading', label: 'Koppen', hex: '#1F293780' },
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
await open(wrapper)
|
|
333
|
+
|
|
334
|
+
expect(roleLabels(wrapper)).toEqual(['Hoofdkleur', 'Koppen'])
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
// The picker has no alpha channel here, so a role's alpha would be stored
|
|
338
|
+
// but never shown: the role stands for its opaque colour, both ways.
|
|
339
|
+
it('writes a role without its alpha, and matches it by its opaque colour', async () => {
|
|
340
|
+
const roles: EditorColorThemeRole[] = [
|
|
341
|
+
{ role: 'heading', label: 'Koppen', hex: '#1F293780' },
|
|
342
|
+
{ role: 'main', label: 'Hoofdkleur', hex: '#1777EA' },
|
|
343
|
+
]
|
|
344
|
+
const wrapper = mount(EditorBorder, {
|
|
345
|
+
props: {
|
|
346
|
+
modelValue: 'border-2 border-[#000000]',
|
|
347
|
+
'onUpdate:modelValue': (value: string) => wrapper.setProps({ modelValue: value }),
|
|
348
|
+
themeRoles: roles,
|
|
349
|
+
},
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
await open(wrapper)
|
|
353
|
+
await roleButton(wrapper, 'Koppen').trigger('click')
|
|
354
|
+
await nextTick()
|
|
355
|
+
|
|
356
|
+
// Role to border: six digits, and the role with alpha shows as selected.
|
|
357
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['border-2 border-[#1F2937]']])
|
|
358
|
+
expect(pressedLabels(wrapper)).toEqual(['Koppen'])
|
|
359
|
+
|
|
360
|
+
// Border to role: a hand-picked opaque colour selects the role with alpha.
|
|
361
|
+
await wrapper.setProps({ modelValue: 'border-2 border-[#000000]' })
|
|
362
|
+
expect(pressedLabels(wrapper)).toEqual([])
|
|
363
|
+
wrapper.findComponent(NexxtColorPicker).vm.$emit('update:modelValue', '#1f2937')
|
|
364
|
+
await nextTick()
|
|
365
|
+
|
|
366
|
+
expect(wrapper.props('modelValue')).toBe('border-2 border-[#1f2937]')
|
|
367
|
+
expect(pressedLabels(wrapper)).toEqual(['Koppen'])
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('renders no theme section when every role is unreadable', async () => {
|
|
371
|
+
const wrapper = mount(EditorBorder, {
|
|
372
|
+
props: {
|
|
373
|
+
modelValue: 'border-2',
|
|
374
|
+
themeRoles: [{ role: 'main', label: 'Hoofdkleur', hex: '' }],
|
|
375
|
+
},
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
await open(wrapper)
|
|
379
|
+
|
|
380
|
+
expect(wrapper.text()).not.toContain('Huisstijl')
|
|
381
|
+
expect(roleButtons(wrapper)).toHaveLength(0)
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
// A role is a shortcut to its hex: the back-end only takes a hex for a
|
|
385
|
+
// border, so no role class is ever written.
|
|
386
|
+
it('writes the role hex as a picked colour, keeping the width', async () => {
|
|
387
|
+
const wrapper = mount(EditorBorder, {
|
|
388
|
+
props: { modelValue: 'border-4 border-[#000000]', themeRoles: THEME_ROLES },
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
await open(wrapper)
|
|
392
|
+
await roleButton(wrapper, 'Accentkleur').trigger('click')
|
|
393
|
+
|
|
394
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['border-4 border-[#F59E0B]']])
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
it('writes the first step with the role hex when the model has no width', async () => {
|
|
398
|
+
const wrapper = mount(EditorBorder, {
|
|
399
|
+
props: { modelValue: '', themeRoles: THEME_ROLES },
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
await open(wrapper)
|
|
403
|
+
await roleButton(wrapper, 'Hoofdkleur').trigger('click')
|
|
404
|
+
|
|
405
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['border border-[#1777EA]']])
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
it('presses the roles whose hex equals the border colour, whatever the case', async () => {
|
|
409
|
+
const wrapper = mount(EditorBorder, {
|
|
410
|
+
props: { modelValue: 'border-2 border-[#1f2937]', themeRoles: THEME_ROLES },
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
await open(wrapper)
|
|
414
|
+
|
|
415
|
+
expect(pressedLabels(wrapper)).toEqual(['Koppen', 'Tekst'])
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
it('presses no role for a colour no role has', async () => {
|
|
419
|
+
const wrapper = mount(EditorBorder, {
|
|
420
|
+
props: { modelValue: 'border-2 border-[#00FF00]', themeRoles: THEME_ROLES },
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
await open(wrapper)
|
|
424
|
+
|
|
425
|
+
expect(pressedLabels(wrapper)).toEqual([])
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
it('moves the pressed state with the model', async () => {
|
|
429
|
+
const wrapper = mount(EditorBorder, {
|
|
430
|
+
props: {
|
|
431
|
+
modelValue: 'border-2 border-[#000000]',
|
|
432
|
+
'onUpdate:modelValue': (value: string) => wrapper.setProps({ modelValue: value }),
|
|
433
|
+
themeRoles: THEME_ROLES,
|
|
434
|
+
},
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
await open(wrapper)
|
|
438
|
+
await roleButton(wrapper, 'Hoofdkleur').trigger('click')
|
|
439
|
+
await nextTick()
|
|
440
|
+
|
|
441
|
+
expect(pressedLabels(wrapper)).toEqual(['Hoofdkleur'])
|
|
442
|
+
})
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
// The consumer derives its class matchers from this list, so the list is a
|
|
446
|
+
// contract: any change here changes what the app recognises.
|
|
447
|
+
describe('EDITOR_BORDER_STEPS', () => {
|
|
448
|
+
it('offers exactly the agreed steps', () => {
|
|
449
|
+
expect(EDITOR_BORDER_STEPS).toEqual([
|
|
450
|
+
{ token: 'border', px: 1 },
|
|
451
|
+
{ token: 'border-2', px: 2 },
|
|
452
|
+
{ token: 'border-4', px: 4 },
|
|
453
|
+
{ token: 'border-8', px: 8 },
|
|
454
|
+
])
|
|
455
|
+
})
|
|
456
|
+
})
|