@nexxtmove/ui 2.5.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/components/editor/molecules/EditorBorder/EditorBorder.vue.d.ts +12 -0
- package/dist/components/editor/molecules/EditorColor/_ThemeRoleSwatches.vue.d.ts +16 -0
- package/dist/index.js +1094 -1135
- 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/EditorBorder/EditorBorder.vue +58 -2
- 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/_ColorField.vue +7 -29
- package/src/components/editor/molecules/EditorColor/_ThemeRoleSwatches.vue +57 -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,689 @@
|
|
|
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 { colorToString, normalizeColor, setChannelValue } from 'reka-ui'
|
|
5
|
+
import EditorColor from './EditorColor.vue'
|
|
6
|
+
import type { EditorColorThemeRole, EditorColorValue } from './EditorColor.vue'
|
|
7
|
+
import NexxtColorPicker from '../../../organisms/ColorPicker/ColorPicker.vue'
|
|
8
|
+
import NexxtTooltip from '../../../atoms/Tooltip/Tooltip.vue'
|
|
9
|
+
|
|
10
|
+
// jsdom ships neither ResizeObserver, which reka-ui's colour primitives use
|
|
11
|
+
// internally, nor a layout engine for floating-ui to measure -- same setup as
|
|
12
|
+
// ColorPicker's own test.
|
|
13
|
+
beforeAll(() => {
|
|
14
|
+
global.ResizeObserver = class ResizeObserver {
|
|
15
|
+
observe() {}
|
|
16
|
+
unobserve() {}
|
|
17
|
+
disconnect() {}
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
vi.mock('@floating-ui/vue', async (importOriginal) => {
|
|
22
|
+
const actual = await importOriginal<typeof import('@floating-ui/vue')>()
|
|
23
|
+
return {
|
|
24
|
+
...actual,
|
|
25
|
+
useFloating: () => ({
|
|
26
|
+
floatingStyles: ref({}),
|
|
27
|
+
placement: ref('bottom-start'),
|
|
28
|
+
isPositioned: ref(true),
|
|
29
|
+
}),
|
|
30
|
+
autoUpdate: () => () => {},
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
/** The value the block hands to the picker, before the picker normalises it. */
|
|
35
|
+
const pickerValue = (wrapper: VueWrapper) =>
|
|
36
|
+
wrapper.findComponent(NexxtColorPicker).props('modelValue')
|
|
37
|
+
|
|
38
|
+
/** The collapsing wrapper, found by the grid row it animates, not by styling. */
|
|
39
|
+
const contentWrapper = (wrapper: VueWrapper) => wrapper.find('[class*="grid-rows-"]')
|
|
40
|
+
|
|
41
|
+
// Heading and text share a hex on purpose: only the role class may tell them apart.
|
|
42
|
+
const THEME_ROLES: EditorColorThemeRole[] = [
|
|
43
|
+
{ role: 'main', label: 'Hoofdkleur', hex: '#1777EA' },
|
|
44
|
+
{ role: 'accent', label: 'Accentkleur', hex: '#F59E0B' },
|
|
45
|
+
{ role: 'background', label: 'Achtergrond', hex: '#F0F4F8' },
|
|
46
|
+
{ role: 'heading', label: 'Koppen', hex: '#1F2937' },
|
|
47
|
+
{ role: 'text', label: 'Tekst', hex: '#1F2937' },
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
const trigger = (wrapper: VueWrapper) => wrapper.findComponent(NexxtColorPicker).find('button')
|
|
51
|
+
|
|
52
|
+
/** The panel content only exists while the picker is open. */
|
|
53
|
+
const open = async (wrapper: VueWrapper) => {
|
|
54
|
+
await trigger(wrapper).trigger('click')
|
|
55
|
+
await flushPromises()
|
|
56
|
+
await nextTick()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The role buttons are the only pressable buttons in the block. */
|
|
60
|
+
const roleButtons = (wrapper: VueWrapper) => wrapper.findAll('button[aria-pressed]')
|
|
61
|
+
|
|
62
|
+
/** A role is a bare swatch, so its label is its accessible name. */
|
|
63
|
+
const roleLabels = (wrapper: VueWrapper) =>
|
|
64
|
+
roleButtons(wrapper).map((button) => button.attributes('aria-label'))
|
|
65
|
+
|
|
66
|
+
const roleButton = (wrapper: VueWrapper, label: string) =>
|
|
67
|
+
roleButtons(wrapper).find((button) => button.attributes('aria-label') === label)!
|
|
68
|
+
|
|
69
|
+
const pressedLabels = (wrapper: VueWrapper) =>
|
|
70
|
+
roleButtons(wrapper)
|
|
71
|
+
.filter((button) => button.attributes('aria-pressed') === 'true')
|
|
72
|
+
.map((button) => button.attributes('aria-label'))
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The slider is named by its visible label, through `aria-labelledby`; it only
|
|
76
|
+
* exists while the panel is open.
|
|
77
|
+
*/
|
|
78
|
+
const alphaThumb = (wrapper: VueWrapper, label = 'Dekking') => {
|
|
79
|
+
const id = wrapper
|
|
80
|
+
.findAll('span')
|
|
81
|
+
.find((span) => span.text() === label)
|
|
82
|
+
?.attributes('id')
|
|
83
|
+
// Without the label, look for any labelled slider: only the alpha slider is
|
|
84
|
+
// one, so this is empty exactly when the slider is not rendered.
|
|
85
|
+
return wrapper.find(
|
|
86
|
+
id ? `[role="slider"][aria-labelledby="${id}"]` : '[role="slider"][aria-labelledby]',
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** The picker's own hex field: what it holds is the colour the picker works with. */
|
|
91
|
+
const hexField = (wrapper: VueWrapper) => wrapper.find<HTMLInputElement>('[placeholder="#000000"]')
|
|
92
|
+
|
|
93
|
+
describe('EditorColor', () => {
|
|
94
|
+
// No assertions on typography utilities: the label is found by text or by
|
|
95
|
+
// structure, so restyling the block or the picker cannot turn these red.
|
|
96
|
+
it('renders the default label', () => {
|
|
97
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
98
|
+
|
|
99
|
+
expect(wrapper.find('p').text()).toBe('Kleur')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('renders a custom label', () => {
|
|
103
|
+
const wrapper = mount(EditorColor, {
|
|
104
|
+
props: { modelValue: 'bg-[#1777EA]', label: 'Achtergrondkleur' },
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
expect(wrapper.find('p').text()).toBe('Achtergrondkleur')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('renders a switch when switchable is set', () => {
|
|
111
|
+
const wrapper = mount(EditorColor, {
|
|
112
|
+
props: { modelValue: 'bg-[#1777EA]', switchable: true },
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
expect(wrapper.find('[role="switch"], input[type="checkbox"]').exists()).toBe(true)
|
|
116
|
+
expect(wrapper.text()).toContain('Kleur')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('renders no switch by default', () => {
|
|
120
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
121
|
+
|
|
122
|
+
expect(wrapper.find('[role="switch"], input[type="checkbox"]').exists()).toBe(false)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('hands the hex of the model to the picker', () => {
|
|
126
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
127
|
+
|
|
128
|
+
expect(pickerValue(wrapper)).toBe('#1777EA')
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('reads an eight-digit hex as well', () => {
|
|
132
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA80]' } })
|
|
133
|
+
|
|
134
|
+
expect(pickerValue(wrapper)).toBe('#1777EA80')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// A stored value from before this component, a token class, or a property this
|
|
138
|
+
// component does not own -- `border` belongs to the border component, colour
|
|
139
|
+
// and width together -- must leave the picker empty (its placeholder shows)
|
|
140
|
+
// instead of being written back.
|
|
141
|
+
it.each(['red', 'bg-red-500', 'border-[#1777EA]', ''])(
|
|
142
|
+
'leaves the picker empty for %p',
|
|
143
|
+
(value) => {
|
|
144
|
+
const wrapper = mount(EditorColor, { props: { modelValue: value as 'bg-[#000000]' } })
|
|
145
|
+
|
|
146
|
+
expect(pickerValue(wrapper)).toBe('')
|
|
147
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
148
|
+
},
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
it('emits the picked hex as a bg class', async () => {
|
|
152
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
153
|
+
|
|
154
|
+
wrapper.findComponent(NexxtColorPicker).vm.$emit('update:modelValue', '#00FF00')
|
|
155
|
+
await wrapper.vm.$nextTick()
|
|
156
|
+
|
|
157
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-[#00FF00]']])
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('uses the property prop as the prefix of the class it writes', async () => {
|
|
161
|
+
const wrapper = mount(EditorColor, {
|
|
162
|
+
props: { modelValue: 'text-[#1777EA]', property: 'text' },
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
wrapper.findComponent(NexxtColorPicker).vm.$emit('update:modelValue', '#00FF00')
|
|
166
|
+
await wrapper.vm.$nextTick()
|
|
167
|
+
|
|
168
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['text-[#00FF00]']])
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('keeps the colour when the picker reports an empty value', async () => {
|
|
172
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
173
|
+
|
|
174
|
+
wrapper.findComponent(NexxtColorPicker).vm.$emit('update:modelValue', '')
|
|
175
|
+
await wrapper.vm.$nextTick()
|
|
176
|
+
|
|
177
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('rewrites the prefix when the property prop changes', async () => {
|
|
181
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
182
|
+
|
|
183
|
+
await wrapper.setProps({ property: 'text' })
|
|
184
|
+
|
|
185
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['text-[#1777EA]']])
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('writes nothing when the property changes without a parseable colour', async () => {
|
|
189
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-red-500' as 'bg-[#000000]' } })
|
|
190
|
+
|
|
191
|
+
await wrapper.setProps({ property: 'text' })
|
|
192
|
+
|
|
193
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('follows the model when the parent changes it', async () => {
|
|
197
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
198
|
+
|
|
199
|
+
await wrapper.setProps({ modelValue: 'bg-[#123456]' })
|
|
200
|
+
|
|
201
|
+
expect(pickerValue(wrapper)).toBe('#123456')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('emits update:enabled when the switch is toggled', async () => {
|
|
205
|
+
const wrapper = mount(EditorColor, {
|
|
206
|
+
props: { modelValue: 'bg-[#1777EA]', switchable: true, enabled: true },
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
await wrapper.find('input[type="checkbox"]').setValue(false)
|
|
210
|
+
|
|
211
|
+
expect(wrapper.emitted('update:enabled')).toEqual([[false]])
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('makes the content inert when enabled is false', () => {
|
|
215
|
+
const wrapper = mount(EditorColor, {
|
|
216
|
+
props: { modelValue: 'bg-[#1777EA]', switchable: true, enabled: false },
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
expect(contentWrapper(wrapper).attributes('inert')).toBeDefined()
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('keeps the content interactive when enabled is true', () => {
|
|
223
|
+
const wrapper = mount(EditorColor, {
|
|
224
|
+
props: { modelValue: 'bg-[#1777EA]', switchable: true, enabled: true },
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
expect(contentWrapper(wrapper).attributes('inert')).toBeUndefined()
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// The panel is sized against the trigger rather than the picker's own w-70;
|
|
231
|
+
// the rendered widths are asserted in Chromium, in the Default story.
|
|
232
|
+
it('opens the picker panel at the width of its trigger', () => {
|
|
233
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
234
|
+
|
|
235
|
+
expect(wrapper.findComponent(NexxtColorPicker).props('matchWidth')).toBe(true)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it('passes a custom placeholder to the picker', () => {
|
|
239
|
+
const wrapper = mount(EditorColor, {
|
|
240
|
+
props: { modelValue: 'bg-red-500' as 'bg-[#000000]', messages: { placeholder: 'Pick one' } },
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
expect(wrapper.text()).toContain('Pick one')
|
|
244
|
+
})
|
|
245
|
+
it('passes reusable colour suggestions to the picker', () => {
|
|
246
|
+
const wrapper = mount(EditorColor, {
|
|
247
|
+
props: { modelValue: 'bg-[#1777EA]', suggestions: ['#1777EA', '#F0F4F8'] },
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
expect(wrapper.findComponent(NexxtColorPicker).props('suggestions')).toEqual({
|
|
251
|
+
hex: ['#1777EA', '#F0F4F8'],
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
describe('theme roles', () => {
|
|
256
|
+
it('renders no theme section without roles', async () => {
|
|
257
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
258
|
+
|
|
259
|
+
await open(wrapper)
|
|
260
|
+
|
|
261
|
+
expect(wrapper.text()).not.toContain('Huisstijl')
|
|
262
|
+
expect(roleButtons(wrapper)).toHaveLength(0)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('offers the passed roles under the default section title', async () => {
|
|
266
|
+
const wrapper = mount(EditorColor, {
|
|
267
|
+
props: { modelValue: 'bg-[#1777EA]', themeRoles: THEME_ROLES },
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
expect(roleButtons(wrapper)).toHaveLength(0)
|
|
271
|
+
|
|
272
|
+
await open(wrapper)
|
|
273
|
+
|
|
274
|
+
expect(wrapper.text()).toContain('Huisstijl')
|
|
275
|
+
expect(roleLabels(wrapper)).toEqual(THEME_ROLES.map(({ label }) => label))
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
// The label is not printed next to the swatch; that it shows on hover is
|
|
279
|
+
// asserted in Chromium, in the WithThemeRoles story.
|
|
280
|
+
it('renders each role as a bare swatch inside a tooltip', async () => {
|
|
281
|
+
const wrapper = mount(EditorColor, {
|
|
282
|
+
props: { modelValue: 'bg-[#1777EA]', themeRoles: THEME_ROLES },
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
await open(wrapper)
|
|
286
|
+
|
|
287
|
+
expect(wrapper.findAllComponents(NexxtTooltip)).toHaveLength(THEME_ROLES.length)
|
|
288
|
+
roleButtons(wrapper).forEach((button) => expect(button.text()).toBe(''))
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('offers only the roles the consumer passes', async () => {
|
|
292
|
+
const wrapper = mount(EditorColor, {
|
|
293
|
+
props: { modelValue: 'bg-[#1777EA]', themeRoles: THEME_ROLES.slice(0, 2) },
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
await open(wrapper)
|
|
297
|
+
|
|
298
|
+
expect(roleLabels(wrapper)).toEqual(['Hoofdkleur', 'Accentkleur'])
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
it('renders the section above the suggestions', async () => {
|
|
302
|
+
const wrapper = mount(EditorColor, {
|
|
303
|
+
props: { modelValue: 'bg-[#1777EA]', themeRoles: THEME_ROLES, suggestions: ['#00FF00'] },
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
await open(wrapper)
|
|
307
|
+
|
|
308
|
+
const text = wrapper.text()
|
|
309
|
+
expect(text.indexOf('Huisstijl')).toBeGreaterThan(-1)
|
|
310
|
+
expect(text.indexOf('Huisstijl')).toBeLessThan(text.indexOf('Gebruikte kleuren'))
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('takes the section title from messages', async () => {
|
|
314
|
+
const wrapper = mount(EditorColor, {
|
|
315
|
+
props: {
|
|
316
|
+
modelValue: 'bg-[#1777EA]',
|
|
317
|
+
themeRoles: THEME_ROLES,
|
|
318
|
+
messages: { themeLabel: 'Brand' },
|
|
319
|
+
},
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
await open(wrapper)
|
|
323
|
+
|
|
324
|
+
expect(wrapper.text()).toContain('Brand')
|
|
325
|
+
expect(wrapper.text()).not.toContain('Huisstijl')
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
it('writes the role class for a bg property', async () => {
|
|
329
|
+
const wrapper = mount(EditorColor, {
|
|
330
|
+
props: { modelValue: 'bg-[#1777EA]', themeRoles: THEME_ROLES },
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
await open(wrapper)
|
|
334
|
+
await roleButton(wrapper, 'Hoofdkleur').trigger('click')
|
|
335
|
+
|
|
336
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-theme-main']])
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
it('writes the role class for a text property', async () => {
|
|
340
|
+
const wrapper = mount(EditorColor, {
|
|
341
|
+
props: { modelValue: 'text-[#1777EA]', property: 'text', themeRoles: THEME_ROLES },
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
await open(wrapper)
|
|
345
|
+
await roleButton(wrapper, 'Koppen').trigger('click')
|
|
346
|
+
|
|
347
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['text-theme-heading']])
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
// The hex reaches the picker in its 8-digit form; see `pickerHex` in the component.
|
|
351
|
+
it('reads a stored role back as selected, with its label and hex', async () => {
|
|
352
|
+
const wrapper = mount(EditorColor, {
|
|
353
|
+
props: { modelValue: 'text-theme-heading', property: 'text', themeRoles: THEME_ROLES },
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
expect(pickerValue(wrapper)).toBe('#1F2937ff')
|
|
357
|
+
expect(trigger(wrapper).text()).toBe('Koppen')
|
|
358
|
+
|
|
359
|
+
await open(wrapper)
|
|
360
|
+
|
|
361
|
+
// `text` has the same hex as `heading`; only the stored role is selected.
|
|
362
|
+
expect(pressedLabels(wrapper)).toEqual(['Koppen'])
|
|
363
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it.each(
|
|
367
|
+
(['bg', 'text'] as const).flatMap((prefix) =>
|
|
368
|
+
THEME_ROLES.map(({ role, label }) => [`${prefix}-theme-${role}`, label] as const),
|
|
369
|
+
),
|
|
370
|
+
)('reads %s as %s', (value, label) => {
|
|
371
|
+
const wrapper = mount(EditorColor, { props: { modelValue: value, themeRoles: THEME_ROLES } })
|
|
372
|
+
|
|
373
|
+
expect(trigger(wrapper).text()).toBe(label)
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
it('keeps a literal that equals the hex of a role a literal', async () => {
|
|
377
|
+
const wrapper = mount(EditorColor, {
|
|
378
|
+
props: { modelValue: 'bg-[#1777EA]', themeRoles: THEME_ROLES },
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
expect(trigger(wrapper).text()).not.toContain('Hoofdkleur')
|
|
382
|
+
|
|
383
|
+
await open(wrapper)
|
|
384
|
+
|
|
385
|
+
expect(pressedLabels(wrapper)).toEqual([])
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
it('writes a literal when a colour is picked while a role is selected', async () => {
|
|
389
|
+
const wrapper = mount(EditorColor, {
|
|
390
|
+
props: { modelValue: 'bg-theme-main', themeRoles: THEME_ROLES },
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
wrapper.findComponent(NexxtColorPicker).vm.$emit('update:modelValue', '#00FF00')
|
|
394
|
+
await wrapper.vm.$nextTick()
|
|
395
|
+
|
|
396
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-[#00FF00]']])
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
// The way to detach a colour from the theme without changing how it looks.
|
|
400
|
+
it('writes a literal when the suggestion with the hex of the selected role is picked', async () => {
|
|
401
|
+
const wrapper = mount(EditorColor, {
|
|
402
|
+
props: {
|
|
403
|
+
modelValue: 'bg-theme-main',
|
|
404
|
+
themeRoles: [{ role: 'main', label: 'Hoofdkleur', hex: '#1777ea' }],
|
|
405
|
+
suggestions: ['#1777ea'],
|
|
406
|
+
},
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
await open(wrapper)
|
|
410
|
+
await wrapper.find('button[aria-label="Kies kleur #1777ea"]').trigger('click')
|
|
411
|
+
|
|
412
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-[#1777ea]']])
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
it('follows the model from a role to a literal and back', async () => {
|
|
416
|
+
const wrapper = mount(EditorColor, {
|
|
417
|
+
props: { modelValue: 'bg-theme-main', themeRoles: THEME_ROLES },
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
await wrapper.setProps({ modelValue: 'bg-[#123456]' })
|
|
421
|
+
|
|
422
|
+
expect(pickerValue(wrapper)).toBe('#123456')
|
|
423
|
+
expect(trigger(wrapper).text()).not.toContain('Hoofdkleur')
|
|
424
|
+
|
|
425
|
+
await wrapper.setProps({ modelValue: 'bg-theme-accent' })
|
|
426
|
+
|
|
427
|
+
expect(pickerValue(wrapper)).toBe('#F59E0Bff')
|
|
428
|
+
expect(trigger(wrapper).text()).toBe('Accentkleur')
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
it('rewrites the prefix of a role when the property prop changes', async () => {
|
|
432
|
+
const wrapper = mount(EditorColor, {
|
|
433
|
+
props: { modelValue: 'bg-theme-main', themeRoles: THEME_ROLES },
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
await wrapper.setProps({ property: 'text' })
|
|
437
|
+
|
|
438
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['text-theme-main']])
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
// No label and no hex to show it with, so it reads like any other value the
|
|
442
|
+
// component cannot show -- but it is a valid class and must not be replaced.
|
|
443
|
+
it.each([
|
|
444
|
+
['a role that is not offered', THEME_ROLES.slice(1)],
|
|
445
|
+
['no roles at all', undefined],
|
|
446
|
+
])('shows the placeholder for a stored role with %s', async (_, themeRoles) => {
|
|
447
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-theme-main', themeRoles } })
|
|
448
|
+
|
|
449
|
+
expect(pickerValue(wrapper)).toBe('')
|
|
450
|
+
expect(trigger(wrapper).text()).toBe('Kies kleur...')
|
|
451
|
+
|
|
452
|
+
await open(wrapper)
|
|
453
|
+
|
|
454
|
+
expect(pressedLabels(wrapper)).toEqual([])
|
|
455
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
it.each([
|
|
459
|
+
'bg-theme-main/10',
|
|
460
|
+
'bg-theme-secondary',
|
|
461
|
+
'bg-theme-on-main',
|
|
462
|
+
'border-theme-main',
|
|
463
|
+
'theme-main',
|
|
464
|
+
])('leaves the picker empty for %p', async (value) => {
|
|
465
|
+
const wrapper = mount(EditorColor, {
|
|
466
|
+
props: { modelValue: value as 'bg-theme-main', themeRoles: THEME_ROLES },
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
expect(pickerValue(wrapper)).toBe('')
|
|
470
|
+
|
|
471
|
+
await open(wrapper)
|
|
472
|
+
|
|
473
|
+
expect(pressedLabels(wrapper)).toEqual([])
|
|
474
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
// The picker throws on a colour it cannot read, so such a role never reaches it.
|
|
478
|
+
it.each(['', 'red', '#FFF', 'var(--nm-theme-main)'])(
|
|
479
|
+
'leaves out a role whose hex is %p',
|
|
480
|
+
async (hex) => {
|
|
481
|
+
const wrapper = mount(EditorColor, {
|
|
482
|
+
props: {
|
|
483
|
+
modelValue: 'bg-theme-main',
|
|
484
|
+
themeRoles: [
|
|
485
|
+
{ role: 'main', label: 'Hoofdkleur', hex },
|
|
486
|
+
{ role: 'accent', label: 'Accentkleur', hex: '#F59E0B' },
|
|
487
|
+
],
|
|
488
|
+
},
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
expect(pickerValue(wrapper)).toBe('')
|
|
492
|
+
|
|
493
|
+
await open(wrapper)
|
|
494
|
+
|
|
495
|
+
expect(roleLabels(wrapper)).toEqual(['Accentkleur'])
|
|
496
|
+
},
|
|
497
|
+
)
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
describe('opacity', () => {
|
|
501
|
+
// The 8-digit output below comes straight from reka; nothing in the
|
|
502
|
+
// component converts alpha to hex itself. This pins that behaviour.
|
|
503
|
+
it('relies on reka writing #rrggbbaa below full opacity and #rrggbb at full', () => {
|
|
504
|
+
const opaque = normalizeColor('#1777EA')
|
|
505
|
+
|
|
506
|
+
expect(colorToString(opaque, 'hex')).toBe('#1777ea')
|
|
507
|
+
expect(colorToString(setChannelValue(opaque, 'alpha', 50), 'hex')).toBe('#1777ea80')
|
|
508
|
+
expect(colorToString(normalizeColor('#1777EA80'), 'hex')).toBe('#1777ea80')
|
|
509
|
+
expect(colorToString(normalizeColor('#1777EAff'), 'hex')).toBe('#1777ea')
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
it('opens the picker without an alpha channel by default', async () => {
|
|
513
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]' } })
|
|
514
|
+
|
|
515
|
+
expect(wrapper.findComponent(NexxtColorPicker).props('showAlpha')).toBe(false)
|
|
516
|
+
|
|
517
|
+
await open(wrapper)
|
|
518
|
+
|
|
519
|
+
expect(alphaThumb(wrapper).exists()).toBe(false)
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
it('offers the slider when showAlpha is set', async () => {
|
|
523
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]', showAlpha: true } })
|
|
524
|
+
|
|
525
|
+
await open(wrapper)
|
|
526
|
+
|
|
527
|
+
expect(alphaThumb(wrapper).attributes('aria-valuetext')).toBe('100%')
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
it('takes the slider label from messages', async () => {
|
|
531
|
+
const wrapper = mount(EditorColor, {
|
|
532
|
+
props: {
|
|
533
|
+
modelValue: 'bg-[#1777EA]',
|
|
534
|
+
showAlpha: true,
|
|
535
|
+
messages: { alphaLabel: 'Transparantie' },
|
|
536
|
+
},
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
await open(wrapper)
|
|
540
|
+
|
|
541
|
+
expect(alphaThumb(wrapper, 'Transparantie').exists()).toBe(true)
|
|
542
|
+
expect(wrapper.text()).not.toContain('Dekking')
|
|
543
|
+
})
|
|
544
|
+
|
|
545
|
+
it('writes an 8-digit hex when the slider is moved', async () => {
|
|
546
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA]', showAlpha: true } })
|
|
547
|
+
|
|
548
|
+
await open(wrapper)
|
|
549
|
+
await alphaThumb(wrapper).trigger('keydown', { key: 'ArrowLeft' })
|
|
550
|
+
|
|
551
|
+
// 99% of 255 rounds to 0xfc.
|
|
552
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-[#1777eafc]']])
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
it.each([
|
|
556
|
+
['#1777ea80', 'bg-[#1777ea80]'],
|
|
557
|
+
['#1777ea', 'bg-[#1777ea]'],
|
|
558
|
+
// An alpha that rounds to ff is opaque; the pair is dropped.
|
|
559
|
+
['#1777eaff', 'bg-[#1777ea]'],
|
|
560
|
+
])('writes the picked %s as %s', async (picked, written) => {
|
|
561
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#000000]', showAlpha: true } })
|
|
562
|
+
|
|
563
|
+
wrapper.findComponent(NexxtColorPicker).vm.$emit('update:modelValue', picked)
|
|
564
|
+
await nextTick()
|
|
565
|
+
|
|
566
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([[written]])
|
|
567
|
+
})
|
|
568
|
+
|
|
569
|
+
it('loads a stored 8-digit hex with the slider at its opacity', async () => {
|
|
570
|
+
const wrapper = mount(EditorColor, {
|
|
571
|
+
props: { modelValue: 'bg-[#1777EA80]', showAlpha: true },
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
expect(trigger(wrapper).text()).toBe('#1777ea80')
|
|
575
|
+
|
|
576
|
+
await open(wrapper)
|
|
577
|
+
|
|
578
|
+
expect(alphaThumb(wrapper).attributes('aria-valuetext')).toBe('50%')
|
|
579
|
+
expect(hexField(wrapper).element.value).toBe('#1777ea80')
|
|
580
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
581
|
+
})
|
|
582
|
+
|
|
583
|
+
// Without the slider the picker strips the alpha on read instead of
|
|
584
|
+
// throwing; the stored value is left alone until a colour is picked.
|
|
585
|
+
it('shows a stored 8-digit hex opaque without showAlpha, and keeps it', async () => {
|
|
586
|
+
const wrapper = mount(EditorColor, { props: { modelValue: 'bg-[#1777EA80]' } })
|
|
587
|
+
|
|
588
|
+
expect(trigger(wrapper).text()).toBe('#1777ea')
|
|
589
|
+
|
|
590
|
+
await open(wrapper)
|
|
591
|
+
|
|
592
|
+
expect(alphaThumb(wrapper).exists()).toBe(false)
|
|
593
|
+
expect(hexField(wrapper).element.value).toBe('#1777ea')
|
|
594
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
595
|
+
|
|
596
|
+
wrapper.findComponent(NexxtColorPicker).vm.$emit('update:modelValue', '#00ff00')
|
|
597
|
+
await nextTick()
|
|
598
|
+
|
|
599
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-[#00ff00]']])
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
it('writes the opaque hex at once when showAlpha is turned off', async () => {
|
|
603
|
+
const wrapper = mount(EditorColor, {
|
|
604
|
+
props: { modelValue: 'bg-[#1777EA80]', showAlpha: true },
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
await wrapper.setProps({ showAlpha: false })
|
|
608
|
+
|
|
609
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-[#1777ea]']])
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
// A role carries no opacity, so its hex reaches the picker opaque; otherwise
|
|
613
|
+
// the picker drops the alpha itself and writes a literal over the role.
|
|
614
|
+
it('keeps a role whose own hex is translucent', async () => {
|
|
615
|
+
const translucent: EditorColorValue = 'bg-[#00FF0080]'
|
|
616
|
+
const themeRoles: EditorColorThemeRole[] = [
|
|
617
|
+
{ role: 'main', label: 'Hoofdkleur', hex: '#1777EA80' },
|
|
618
|
+
]
|
|
619
|
+
const wrapper = mount(EditorColor, {
|
|
620
|
+
props: {
|
|
621
|
+
modelValue: translucent,
|
|
622
|
+
themeRoles,
|
|
623
|
+
showAlpha: true,
|
|
624
|
+
'onUpdate:modelValue': (value: EditorColorValue) =>
|
|
625
|
+
wrapper.setProps({ modelValue: value }),
|
|
626
|
+
},
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
await open(wrapper)
|
|
630
|
+
await roleButton(wrapper, 'Hoofdkleur').trigger('click')
|
|
631
|
+
await flushPromises()
|
|
632
|
+
|
|
633
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-theme-main']])
|
|
634
|
+
expect(pickerValue(wrapper)).toBe('#1777EAff')
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
it('hides the slider while a theme role is selected', async () => {
|
|
638
|
+
const wrapper = mount(EditorColor, {
|
|
639
|
+
props: { modelValue: 'bg-theme-main', themeRoles: THEME_ROLES, showAlpha: true },
|
|
640
|
+
})
|
|
641
|
+
|
|
642
|
+
expect(wrapper.findComponent(NexxtColorPicker).props('showAlpha')).toBe(false)
|
|
643
|
+
|
|
644
|
+
await open(wrapper)
|
|
645
|
+
|
|
646
|
+
expect(alphaThumb(wrapper).exists()).toBe(false)
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
// Picking the role flips the picker's value and its alpha channel at once;
|
|
650
|
+
// the picker must not write the old colour back, stripped, over the role.
|
|
651
|
+
it('writes only the role when one is picked over a translucent literal', async () => {
|
|
652
|
+
// Typed up front: the handler refers to `wrapper`, which stops inference
|
|
653
|
+
// from narrowing an inline literal to the model's type.
|
|
654
|
+
const translucent: EditorColorValue = 'bg-[#00FF0080]'
|
|
655
|
+
const wrapper = mount(EditorColor, {
|
|
656
|
+
props: {
|
|
657
|
+
modelValue: translucent,
|
|
658
|
+
themeRoles: THEME_ROLES,
|
|
659
|
+
showAlpha: true,
|
|
660
|
+
'onUpdate:modelValue': (value: EditorColorValue) =>
|
|
661
|
+
wrapper.setProps({ modelValue: value }),
|
|
662
|
+
},
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
await open(wrapper)
|
|
666
|
+
expect(alphaThumb(wrapper).exists()).toBe(true)
|
|
667
|
+
|
|
668
|
+
await roleButton(wrapper, 'Hoofdkleur').trigger('click')
|
|
669
|
+
await flushPromises()
|
|
670
|
+
|
|
671
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-theme-main']])
|
|
672
|
+
expect(alphaThumb(wrapper).exists()).toBe(false)
|
|
673
|
+
expect(trigger(wrapper).text()).toBe('Hoofdkleur')
|
|
674
|
+
expect(hexField(wrapper).element.value).toBe('#1777ea')
|
|
675
|
+
})
|
|
676
|
+
|
|
677
|
+
it('offers the slider again once a literal replaces the role', async () => {
|
|
678
|
+
const wrapper = mount(EditorColor, {
|
|
679
|
+
props: { modelValue: 'bg-theme-main', themeRoles: THEME_ROLES, showAlpha: true },
|
|
680
|
+
})
|
|
681
|
+
|
|
682
|
+
await wrapper.setProps({ modelValue: 'bg-[#1777EA80]' })
|
|
683
|
+
|
|
684
|
+
expect(wrapper.findComponent(NexxtColorPicker).props('showAlpha')).toBe(true)
|
|
685
|
+
expect(trigger(wrapper).text()).toBe('#1777ea80')
|
|
686
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
687
|
+
})
|
|
688
|
+
})
|
|
689
|
+
})
|