@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,262 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
+
import { mount, type VueWrapper } from '@vue/test-utils'
|
|
3
|
+
import EditorBackgroundSize, { EDITOR_BACKGROUND_SIZE_OPTIONS } from './EditorBackgroundSize.vue'
|
|
4
|
+
|
|
5
|
+
// ToggleGroup awaits document.fonts.ready on mount, which jsdom does not ship.
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
Object.defineProperty(document, 'fonts', {
|
|
8
|
+
value: { ready: Promise.resolve() },
|
|
9
|
+
configurable: true,
|
|
10
|
+
})
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The options are icon-only, so their copy lives on the button as the
|
|
15
|
+
* accessible name rather than as visible text.
|
|
16
|
+
*/
|
|
17
|
+
const optionNames = (wrapper: VueWrapper) =>
|
|
18
|
+
wrapper.findAll('button').map((button) => button.attributes('aria-label'))
|
|
19
|
+
|
|
20
|
+
describe('EditorBackgroundSize', () => {
|
|
21
|
+
// The options sit in a ToggleGroup toolbar; its name is the block label, so
|
|
22
|
+
// a screen reader announces which setting the pressed option belongs to.
|
|
23
|
+
it('names the options toolbar with the block label', async () => {
|
|
24
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
25
|
+
expect(wrapper.get('[role="toolbar"]').attributes('aria-label')).toBe('Vulling')
|
|
26
|
+
|
|
27
|
+
await wrapper.setProps({ label: 'Custom' })
|
|
28
|
+
expect(wrapper.get('[role="toolbar"]').attributes('aria-label')).toBe('Custom')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// No assertions on typography utilities: copy is found by structure or by
|
|
32
|
+
// accessible name, so restyling the block or the toggle group cannot turn
|
|
33
|
+
// these red.
|
|
34
|
+
it('renders the Dutch default label', () => {
|
|
35
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
36
|
+
|
|
37
|
+
expect(wrapper.find('p').text()).toBe('Vulling')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('renders a button for each of the three options', () => {
|
|
41
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
42
|
+
|
|
43
|
+
expect(wrapper.findAll('button')).toHaveLength(3)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('names the options with the Dutch defaults', () => {
|
|
47
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
48
|
+
|
|
49
|
+
expect(optionNames(wrapper)).toEqual(['Opvullen', 'Passend', 'Origineel'])
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('renders a custom label', () => {
|
|
53
|
+
const wrapper = mount(EditorBackgroundSize, {
|
|
54
|
+
props: { modelValue: 'bg-cover', label: 'Fit' },
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
expect(wrapper.find('p').text()).toBe('Fit')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('names the options with custom messages', () => {
|
|
61
|
+
const wrapper = mount(EditorBackgroundSize, {
|
|
62
|
+
props: {
|
|
63
|
+
modelValue: 'bg-cover',
|
|
64
|
+
messages: { cover: 'Cover', contain: 'Contain', auto: 'Original' },
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
expect(optionNames(wrapper)).toEqual(['Cover', 'Contain', 'Original'])
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('keeps the untouched defaults when only one message is overridden', () => {
|
|
72
|
+
const wrapper = mount(EditorBackgroundSize, {
|
|
73
|
+
props: { modelValue: 'bg-cover', messages: { contain: 'Contain' } },
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
expect(optionNames(wrapper)).toEqual(['Opvullen', 'Contain', 'Origineel'])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('emits update:modelValue with the Tailwind class of the clicked option', async () => {
|
|
80
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
81
|
+
|
|
82
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
83
|
+
|
|
84
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-contain']])
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('emits bg-auto when the last option is clicked', async () => {
|
|
88
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
89
|
+
|
|
90
|
+
await wrapper.findAll('button')[2].trigger('click')
|
|
91
|
+
|
|
92
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-auto']])
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('renders only icons, no visible option text', () => {
|
|
96
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
97
|
+
|
|
98
|
+
// Each icon is rendered twice: once per toggle group layer.
|
|
99
|
+
expect(wrapper.findAll('i')).toHaveLength(6)
|
|
100
|
+
wrapper.findAll('button').forEach((button) => expect(button.text()).toBe(''))
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('renders no tooltip: the copy is the accessible name, not a hover overlay', () => {
|
|
104
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
105
|
+
|
|
106
|
+
// ToggleGroup only mounts NexxtTooltip -- and with it reka-ui's
|
|
107
|
+
// TooltipProvider and the trigger's data attribute -- for options that
|
|
108
|
+
// carry a `tooltip`.
|
|
109
|
+
expect(wrapper.findAllComponents({ name: 'TooltipProvider' })).toHaveLength(0)
|
|
110
|
+
expect(wrapper.find('[data-grace-area-trigger]').exists()).toBe(false)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// Recorded against the component before `options` and `bare` existed: without
|
|
114
|
+
// either prop the markup has to stay byte for byte what consumers already ship.
|
|
115
|
+
it('renders unchanged without the new props', () => {
|
|
116
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
117
|
+
|
|
118
|
+
expect(wrapper.html()).toMatchInlineSnapshot(`
|
|
119
|
+
"<div class="py-4">
|
|
120
|
+
<!--
|
|
121
|
+
Switch.vue sets \`inheritAttrs: false\` and binds \`$attrs\` on its sr-only
|
|
122
|
+
input, so a class on <Switch> never reaches its root <label>. The child
|
|
123
|
+
variant below styles that label from the header instead, which keeps the
|
|
124
|
+
whole row clickable and the accessible name on the real <label>. The gap
|
|
125
|
+
has to go on that label too: the header has a single child in both modes,
|
|
126
|
+
so its own \`gap\` would never apply and the spacing would silently fall
|
|
127
|
+
back to the \`gap-2\` that Switch.vue sets for itself.
|
|
128
|
+
-->
|
|
129
|
+
<div class="flex items-center [&>label]:flex [&>label]:w-full [&>label]:gap-3">
|
|
130
|
+
<p class="small-normal text-gray-900">Vulling</p>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="grid grid-rows-[1fr]">
|
|
133
|
+
<!--
|
|
134
|
+
\`overflow-hidden\` is what makes the 0fr collapse clip, but it also clips
|
|
135
|
+
a focus ring on a full-width control in the slot, and the block has no
|
|
136
|
+
horizontal padding of its own. \`-mx-1\` widens the clipping box by 4px on
|
|
137
|
+
each side and the \`px-1\` below pushes the content back, so the ring has
|
|
138
|
+
room while the content keeps the same x position as the label.
|
|
139
|
+
-->
|
|
140
|
+
<div class="-mx-1 min-h-0 overflow-hidden opacity-100">
|
|
141
|
+
<!-- Padding sits on the inner element so nothing is left over at 0fr. -->
|
|
142
|
+
<div class="px-1 pt-1.5 pb-1">
|
|
143
|
+
<div class="flex flex-col gap-1" role="toolbar" aria-label="Vulling">
|
|
144
|
+
<!--v-if-->
|
|
145
|
+
<div class="relative isolate flex h-10 gap-1 rounded-xl bg-gray-100 p-1">
|
|
146
|
+
<!-- slider pill -->
|
|
147
|
+
<div aria-hidden="true" class="absolute inset-y-1 left-0 rounded-lg bg-white" style="width: 0px; transform: translateX(0px);"></div>
|
|
148
|
+
<!--
|
|
149
|
+
buttons with gray text underneath.
|
|
150
|
+
|
|
151
|
+
An icon-only option renders a button with no text at all, so its
|
|
152
|
+
accessible name has to come from somewhere: the tooltip is a sibling in
|
|
153
|
+
a portal, not a label, and axe flags these buttons with \`button-name\`.
|
|
154
|
+
\`ariaLabel\` is the explicit way to name such a button. Falling back to
|
|
155
|
+
the tooltip keeps the consumers that were already relying on it named,
|
|
156
|
+
and neither applies once there is a visible label -- an \`aria-label\`
|
|
157
|
+
would override the text the user can actually read.
|
|
158
|
+
--><button type="button" tabindex="0" aria-pressed="true" aria-label="Opvullen" class="relative z-10 flex cursor-pointer items-center justify-center gap-1.5 rounded-lg px-4 py-1.5 whitespace-nowrap text-gray-700 hover:text-gray-900 flex-1"><i class="fa-light fa-arrows-maximize items-center justify-center"></i>
|
|
159
|
+
<!--v-if-->
|
|
160
|
+
</button><button type="button" tabindex="-1" aria-pressed="false" aria-label="Passend" class="relative z-10 flex cursor-pointer items-center justify-center gap-1.5 rounded-lg px-4 py-1.5 whitespace-nowrap text-gray-700 hover:text-gray-900 flex-1"><i class="fa-light fa-arrows-minimize items-center justify-center"></i>
|
|
161
|
+
<!--v-if-->
|
|
162
|
+
</button><button type="button" tabindex="-1" aria-pressed="false" aria-label="Origineel" class="relative z-10 flex cursor-pointer items-center justify-center gap-1.5 rounded-lg px-4 py-1.5 whitespace-nowrap text-gray-700 hover:text-gray-900 flex-1"><i class="fa-light fa-image items-center justify-center"></i>
|
|
163
|
+
<!--v-if-->
|
|
164
|
+
</button>
|
|
165
|
+
<div aria-hidden="true" class="pointer-events-none absolute inset-0 z-20 flex gap-1 p-1" style="clip-path: inset(0 100% 0 0 round 8px);"><span class="flex items-center justify-center gap-1.5 rounded-lg px-4 py-1.5 whitespace-nowrap flex-1 text-cornflower-blue-600"><i class="fa-light fa-arrows-maximize items-center justify-center"></i><!--v-if--></span><span class="flex items-center justify-center gap-1.5 rounded-lg px-4 py-1.5 whitespace-nowrap flex-1 text-cornflower-blue-600"><i class="fa-light fa-arrows-minimize items-center justify-center"></i><!--v-if--></span><span class="flex items-center justify-center gap-1.5 rounded-lg px-4 py-1.5 whitespace-nowrap flex-1 text-cornflower-blue-600"><i class="fa-light fa-image items-center justify-center"></i><!--v-if--></span></div>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>"
|
|
172
|
+
`)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('offers only the given subset', () => {
|
|
176
|
+
const wrapper = mount(EditorBackgroundSize, {
|
|
177
|
+
props: { modelValue: 'bg-cover', options: ['bg-cover', 'bg-contain'] },
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
expect(optionNames(wrapper)).toEqual(['Opvullen', 'Passend'])
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
// The subset is always shown in the canonical order, like the other editor
|
|
184
|
+
// molecules with `options`, whatever order the consumer passes.
|
|
185
|
+
it('shows the subset in the canonical order', () => {
|
|
186
|
+
const wrapper = mount(EditorBackgroundSize, {
|
|
187
|
+
props: { modelValue: 'bg-cover', options: ['bg-contain', 'bg-cover'] },
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
expect(optionNames(wrapper)).toEqual(['Opvullen', 'Passend'])
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('ignores duplicate and unknown values', () => {
|
|
194
|
+
const wrapper = mount(EditorBackgroundSize, {
|
|
195
|
+
props: {
|
|
196
|
+
modelValue: 'bg-cover',
|
|
197
|
+
// An unknown value can still arrive from untyped consumer code.
|
|
198
|
+
options: ['bg-auto', 'bg-cover', 'bg-auto', 'bg-fill' as 'bg-auto'],
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
expect(optionNames(wrapper)).toEqual(['Opvullen', 'Origineel'])
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('emits the class of a clicked option in a subset', async () => {
|
|
206
|
+
const wrapper = mount(EditorBackgroundSize, {
|
|
207
|
+
props: { modelValue: 'bg-cover', options: ['bg-auto', 'bg-cover'] },
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
211
|
+
|
|
212
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-auto']])
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
describe('bare', () => {
|
|
216
|
+
it('renders only the toolbar, named by the label, without a header', () => {
|
|
217
|
+
const wrapper = mount(EditorBackgroundSize, {
|
|
218
|
+
props: { modelValue: 'bg-cover', bare: true, label: 'Weergave' },
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
expect(wrapper.element.getAttribute('role')).toBe('toolbar')
|
|
222
|
+
expect(wrapper.attributes('aria-label')).toBe('Weergave')
|
|
223
|
+
expect(wrapper.find('p').exists()).toBe(false)
|
|
224
|
+
expect(wrapper.text()).toBe('')
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('keeps the Dutch default as the toolbar name', () => {
|
|
228
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover', bare: true } })
|
|
229
|
+
|
|
230
|
+
expect(wrapper.attributes('aria-label')).toBe('Vulling')
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('still emits the clicked option', async () => {
|
|
234
|
+
const wrapper = mount(EditorBackgroundSize, {
|
|
235
|
+
props: { modelValue: 'bg-cover', bare: true, options: ['bg-cover', 'bg-auto'] },
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
expect(wrapper.findAll('button')).toHaveLength(2)
|
|
239
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
240
|
+
|
|
241
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['bg-auto']])
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('is always visible, so it renders no switch', () => {
|
|
246
|
+
const wrapper = mount(EditorBackgroundSize, { props: { modelValue: 'bg-cover' } })
|
|
247
|
+
|
|
248
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
// The consumer derives its class matchers from this list, so the list is a
|
|
253
|
+
// contract: any change here changes what the app recognises.
|
|
254
|
+
describe('EDITOR_BACKGROUND_SIZE_OPTIONS', () => {
|
|
255
|
+
it('offers exactly the agreed options', () => {
|
|
256
|
+
expect(EDITOR_BACKGROUND_SIZE_OPTIONS).toEqual([
|
|
257
|
+
{ token: 'cover' },
|
|
258
|
+
{ token: 'contain' },
|
|
259
|
+
{ token: 'auto' },
|
|
260
|
+
])
|
|
261
|
+
})
|
|
262
|
+
})
|
|
@@ -0,0 +1,115 @@
|
|
|
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 EditorBlur from './EditorBlur.vue'
|
|
5
|
+
import type { EditorBlurValue } from './EditorBlur.vue'
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: 'Components/Editor Components/Molecules/Editor Blur',
|
|
9
|
+
component: EditorBlur,
|
|
10
|
+
decorators: [() => ({ template: '<div class="w-100 mx-auto"><story /></div>' })],
|
|
11
|
+
argTypes: {
|
|
12
|
+
label: { control: 'text' },
|
|
13
|
+
switchable: { control: 'boolean' },
|
|
14
|
+
describedby: { control: 'text' },
|
|
15
|
+
},
|
|
16
|
+
} satisfies Meta<typeof EditorBlur>
|
|
17
|
+
|
|
18
|
+
type Story = StoryObj<typeof EditorBlur>
|
|
19
|
+
|
|
20
|
+
export const Default: Story = {
|
|
21
|
+
render: (args) => ({
|
|
22
|
+
components: { EditorBlur },
|
|
23
|
+
setup() {
|
|
24
|
+
const blur = ref<EditorBlurValue>('backdrop-blur-md')
|
|
25
|
+
const enabled = ref(true)
|
|
26
|
+
return { args, blur, enabled }
|
|
27
|
+
},
|
|
28
|
+
// The value is shown as-is: it is the Tailwind class the editor applies.
|
|
29
|
+
template: `
|
|
30
|
+
<div>
|
|
31
|
+
<EditorBlur v-bind="args" v-model="blur" v-model:enabled="enabled" />
|
|
32
|
+
<p class="small-normal text-gray-700">{{ blur }}</p>
|
|
33
|
+
<p class="small-normal text-gray-700">{{ enabled }}</p>
|
|
34
|
+
</div>
|
|
35
|
+
`,
|
|
36
|
+
}),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The group label is hidden but still names the steps. Checked in Chromium,
|
|
41
|
+
* where `sr-only` actually takes the label out of view.
|
|
42
|
+
*/
|
|
43
|
+
export const GroupLabelHidden: Story = {
|
|
44
|
+
render: Default.render,
|
|
45
|
+
play: async ({ canvasElement }) => {
|
|
46
|
+
const canvas = within(canvasElement)
|
|
47
|
+
const toolbar = canvas.getByRole('toolbar', { name: 'Sterkte' })
|
|
48
|
+
const groupLabel = within(toolbar).getByText('Sterkte')
|
|
49
|
+
|
|
50
|
+
const { width, height } = groupLabel.getBoundingClientRect()
|
|
51
|
+
await expect(width * height).toBeLessThanOrEqual(1)
|
|
52
|
+
await expect(getComputedStyle(groupLabel).position).toBe('absolute')
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const NotSwitchable: Story = {
|
|
57
|
+
args: { switchable: false },
|
|
58
|
+
render: Default.render,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The consumer renders the hint itself and passes its id as `describedby`, so
|
|
63
|
+
* both the switch and the group of steps announce it.
|
|
64
|
+
*/
|
|
65
|
+
export const WithHint: Story = {
|
|
66
|
+
args: { describedby: 'blur-hint' },
|
|
67
|
+
render: (args) => ({
|
|
68
|
+
components: { EditorBlur },
|
|
69
|
+
setup() {
|
|
70
|
+
const blur = ref<EditorBlurValue>('backdrop-blur-md')
|
|
71
|
+
const enabled = ref(true)
|
|
72
|
+
return { args, blur, enabled }
|
|
73
|
+
},
|
|
74
|
+
template: `
|
|
75
|
+
<div>
|
|
76
|
+
<EditorBlur v-bind="args" v-model="blur" v-model:enabled="enabled" />
|
|
77
|
+
<p id="blur-hint" class="extra-small-normal text-gray-700">
|
|
78
|
+
Alleen zichtbaar als de groep een doorschijnende achtergrond heeft.
|
|
79
|
+
</p>
|
|
80
|
+
</div>
|
|
81
|
+
`,
|
|
82
|
+
}),
|
|
83
|
+
play: async ({ canvasElement }) => {
|
|
84
|
+
const canvas = within(canvasElement)
|
|
85
|
+
const hint = 'Alleen zichtbaar als de groep een doorschijnende achtergrond heeft.'
|
|
86
|
+
|
|
87
|
+
await expect(canvas.getByRole('switch')).toHaveAccessibleDescription(hint)
|
|
88
|
+
await expect(canvas.getByRole('toolbar')).toHaveAccessibleDescription(hint)
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Off and without a value: switching on writes `backdrop-blur-md`. */
|
|
93
|
+
export const StartsOnMd: Story = {
|
|
94
|
+
render: (args) => ({
|
|
95
|
+
components: { EditorBlur },
|
|
96
|
+
setup() {
|
|
97
|
+
const blur = ref<EditorBlurValue>()
|
|
98
|
+
const enabled = ref(false)
|
|
99
|
+
return { args, blur, enabled }
|
|
100
|
+
},
|
|
101
|
+
template: `
|
|
102
|
+
<div>
|
|
103
|
+
<EditorBlur v-bind="args" v-model="blur" v-model:enabled="enabled" />
|
|
104
|
+
<p class="small-normal text-gray-700">{{ blur ?? 'geen waarde' }}</p>
|
|
105
|
+
</div>
|
|
106
|
+
`,
|
|
107
|
+
}),
|
|
108
|
+
play: async ({ canvasElement }) => {
|
|
109
|
+
const canvas = within(canvasElement)
|
|
110
|
+
|
|
111
|
+
await userEvent.click(canvas.getByRole('switch'))
|
|
112
|
+
|
|
113
|
+
await waitFor(() => expect(canvas.getByText('backdrop-blur-md')).toBeInTheDocument())
|
|
114
|
+
},
|
|
115
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
+
import { mount, type VueWrapper } from '@vue/test-utils'
|
|
3
|
+
import EditorBlur, { EDITOR_BLUR_OPTIONS } from './EditorBlur.vue'
|
|
4
|
+
import type { EditorBlurValue, NexxtEditorBlurProps } from './EditorBlur.vue'
|
|
5
|
+
import NexxtToggleGroup from '../../../molecules/ToggleGroup/ToggleGroup.vue'
|
|
6
|
+
|
|
7
|
+
// ToggleGroup awaits document.fonts.ready on mount, which jsdom does not ship.
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
Object.defineProperty(document, 'fonts', {
|
|
10
|
+
value: { ready: Promise.resolve() },
|
|
11
|
+
configurable: true,
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
/** The names of the options, which are their visible text. */
|
|
16
|
+
const optionNames = (wrapper: VueWrapper) =>
|
|
17
|
+
wrapper.findAll('button').map((button) => button.text())
|
|
18
|
+
|
|
19
|
+
/** The option the toggle group is told to show as selected. */
|
|
20
|
+
const selectedOption = (wrapper: VueWrapper) =>
|
|
21
|
+
wrapper.findComponent(NexxtToggleGroup).props('modelValue')
|
|
22
|
+
|
|
23
|
+
/** The collapsing wrapper, found by the grid row it animates, not by styling. */
|
|
24
|
+
const contentWrapper = (wrapper: VueWrapper) => wrapper.find('[class*="grid-rows-"]')
|
|
25
|
+
|
|
26
|
+
/** The group of steps: ToggleGroup puts `role="toolbar"` on its root. */
|
|
27
|
+
const toolbar = (wrapper: VueWrapper) => wrapper.get('[role="toolbar"]')
|
|
28
|
+
|
|
29
|
+
describe('EditorBlur', () => {
|
|
30
|
+
// No assertions on typography utilities: copy is found by text or structure.
|
|
31
|
+
it('renders the Dutch default label', () => {
|
|
32
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-md' } })
|
|
33
|
+
|
|
34
|
+
expect(wrapper.text()).toContain('Vervagen')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('renders a button for each of the five steps, in order', () => {
|
|
38
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-md' } })
|
|
39
|
+
|
|
40
|
+
expect(optionNames(wrapper)).toEqual(['XS', 'S', 'M', 'L', 'XL'])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('names the group of steps with the hidden Dutch default label', () => {
|
|
44
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-md' } })
|
|
45
|
+
|
|
46
|
+
const labelId = toolbar(wrapper).attributes('aria-labelledby')
|
|
47
|
+
const groupLabel = wrapper.get(`[id="${labelId}"]`)
|
|
48
|
+
expect(groupLabel.text()).toBe('Sterkte')
|
|
49
|
+
// Hiding it is ToggleGroup's `labelHidden`; visibility is checked in
|
|
50
|
+
// Chromium, in the GroupLabelHidden story.
|
|
51
|
+
expect(wrapper.findComponent(NexxtToggleGroup).props('labelHidden')).toBe(true)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('renders a custom label and merges custom messages per key', () => {
|
|
55
|
+
const wrapper = mount(EditorBlur, {
|
|
56
|
+
props: {
|
|
57
|
+
modelValue: 'backdrop-blur-md',
|
|
58
|
+
label: 'Blur',
|
|
59
|
+
messages: { strength: 'Strength', xl: 'Max' },
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
expect(wrapper.text()).toContain('Blur')
|
|
64
|
+
expect(wrapper.text()).not.toContain('Vervagen')
|
|
65
|
+
expect(wrapper.text()).toContain('Strength')
|
|
66
|
+
expect(optionNames(wrapper)).toEqual(['XS', 'S', 'M', 'L', 'Max'])
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('shows the class of the model as the selected one', () => {
|
|
70
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-xl' } })
|
|
71
|
+
|
|
72
|
+
expect(selectedOption(wrapper)).toBe('backdrop-blur-xl')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('emits update:modelValue with the Tailwind class of the clicked step', async () => {
|
|
76
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-md' } })
|
|
77
|
+
|
|
78
|
+
await wrapper.findAll('button')[0].trigger('click')
|
|
79
|
+
await wrapper.findAll('button')[4].trigger('click')
|
|
80
|
+
|
|
81
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([
|
|
82
|
+
['backdrop-blur-xs'],
|
|
83
|
+
['backdrop-blur-xl'],
|
|
84
|
+
])
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('follows the model when the parent changes it', async () => {
|
|
88
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-sm' } })
|
|
89
|
+
|
|
90
|
+
await wrapper.setProps({ modelValue: 'backdrop-blur-lg' })
|
|
91
|
+
|
|
92
|
+
expect(selectedOption(wrapper)).toBe('backdrop-blur-lg')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('shows md for a missing or unknown class', () => {
|
|
96
|
+
// A stored bare step or `backdrop-blur-2xl` gets past the types; the
|
|
97
|
+
// runtime fallback is md.
|
|
98
|
+
const bareStep = 'md' as unknown as EditorBlurValue
|
|
99
|
+
const unknown = 'backdrop-blur-2xl' as unknown as EditorBlurValue
|
|
100
|
+
|
|
101
|
+
expect(selectedOption(mount(EditorBlur))).toBe('backdrop-blur-md')
|
|
102
|
+
expect(selectedOption(mount(EditorBlur, { props: { modelValue: unknown } }))).toBe(
|
|
103
|
+
'backdrop-blur-md',
|
|
104
|
+
)
|
|
105
|
+
expect(selectedOption(mount(EditorBlur, { props: { modelValue: bareStep } }))).toBe(
|
|
106
|
+
'backdrop-blur-md',
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('emits update:enabled and leaves the step alone when switched off', async () => {
|
|
111
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-lg', enabled: true } })
|
|
112
|
+
|
|
113
|
+
await wrapper.find('input[type="checkbox"]').setValue(false)
|
|
114
|
+
|
|
115
|
+
expect(wrapper.emitted('update:enabled')).toEqual([[false]])
|
|
116
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('starts on md when switched on without a valid step', async () => {
|
|
120
|
+
const wrapper = mount(EditorBlur, { props: { enabled: false } })
|
|
121
|
+
|
|
122
|
+
await wrapper.find('input[type="checkbox"]').setValue(true)
|
|
123
|
+
|
|
124
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['backdrop-blur-md']])
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('writes nothing when enabled flips on while not switchable', async () => {
|
|
128
|
+
const wrapper = mount(EditorBlur, { props: { enabled: false, switchable: false } })
|
|
129
|
+
|
|
130
|
+
await wrapper.setProps({ enabled: true })
|
|
131
|
+
|
|
132
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('starts on md when the parent switches it on without a valid step', async () => {
|
|
136
|
+
const wrapper = mount(EditorBlur, { props: { enabled: false } })
|
|
137
|
+
|
|
138
|
+
await wrapper.setProps({ enabled: true })
|
|
139
|
+
|
|
140
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([['backdrop-blur-md']])
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
/** The props of a mount, models included, so every case in the table shares one type. */
|
|
144
|
+
type MountProps = NexxtEditorBlurProps & { modelValue?: EditorBlurValue; enabled?: boolean }
|
|
145
|
+
|
|
146
|
+
const unknownStep = 'backdrop-blur-2xl' as unknown as EditorBlurValue
|
|
147
|
+
const mountCases: [string, MountProps][] = [
|
|
148
|
+
['missing', { enabled: true }],
|
|
149
|
+
['unknown', { enabled: true, modelValue: unknownStep }],
|
|
150
|
+
['missing, not switchable', { switchable: false }],
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
it.each(mountCases)('writes nothing on mount with a %s step', async (_, props) => {
|
|
154
|
+
const wrapper = mount(EditorBlur, { props })
|
|
155
|
+
await wrapper.vm.$nextTick()
|
|
156
|
+
|
|
157
|
+
expect(selectedOption(wrapper)).toBe('backdrop-blur-md')
|
|
158
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('keeps the steps active when not switchable, even with enabled false', () => {
|
|
162
|
+
const wrapper = mount(EditorBlur, {
|
|
163
|
+
props: { modelValue: 'backdrop-blur-md', switchable: false, enabled: false },
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
expect(contentWrapper(wrapper).attributes('inert')).toBeUndefined()
|
|
167
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('keeps a valid step when switched on', async () => {
|
|
171
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-xs', enabled: false } })
|
|
172
|
+
|
|
173
|
+
await wrapper.setProps({ enabled: true })
|
|
174
|
+
|
|
175
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('makes the steps inert when enabled is false', () => {
|
|
179
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-md', enabled: false } })
|
|
180
|
+
|
|
181
|
+
expect(contentWrapper(wrapper).attributes('inert')).toBeDefined()
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('keeps the steps interactive when enabled is true', () => {
|
|
185
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-md', enabled: true } })
|
|
186
|
+
|
|
187
|
+
expect(contentWrapper(wrapper).attributes('inert')).toBeUndefined()
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('renders the switch by default', () => {
|
|
191
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-md' } })
|
|
192
|
+
|
|
193
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('renders no switch when switchable is false', () => {
|
|
197
|
+
const wrapper = mount(EditorBlur, {
|
|
198
|
+
props: { modelValue: 'backdrop-blur-md', switchable: false },
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
describe('describedby', () => {
|
|
205
|
+
it('sets aria-describedby on the switch and on the group of steps', () => {
|
|
206
|
+
const wrapper = mount(EditorBlur, {
|
|
207
|
+
props: { modelValue: 'backdrop-blur-md', describedby: 'blur-hint' },
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
expect(wrapper.get('[role="switch"]').attributes('aria-describedby')).toBe('blur-hint')
|
|
211
|
+
// On the element that carries the role, not on a wrapper around it.
|
|
212
|
+
expect(toolbar(wrapper).attributes('aria-describedby')).toBe('blur-hint')
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('still describes the group when the control is not switchable', () => {
|
|
216
|
+
const wrapper = mount(EditorBlur, {
|
|
217
|
+
props: { modelValue: 'backdrop-blur-md', describedby: 'blur-hint', switchable: false },
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
expect(toolbar(wrapper).attributes('aria-describedby')).toBe('blur-hint')
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('renders no aria-describedby anywhere without the prop', () => {
|
|
224
|
+
const wrapper = mount(EditorBlur, { props: { modelValue: 'backdrop-blur-md' } })
|
|
225
|
+
|
|
226
|
+
expect(wrapper.find('[aria-describedby]').exists()).toBe(false)
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
// The consumer derives its class matchers (`backdrop-blur-<token>`) from this
|
|
232
|
+
// list, so the list is a contract: any change here changes what the app recognises.
|
|
233
|
+
describe('EDITOR_BLUR_OPTIONS', () => {
|
|
234
|
+
it('offers exactly the agreed steps, in order', () => {
|
|
235
|
+
expect(EDITOR_BLUR_OPTIONS).toEqual([
|
|
236
|
+
{ token: 'xs' },
|
|
237
|
+
{ token: 'sm' },
|
|
238
|
+
{ token: 'md' },
|
|
239
|
+
{ token: 'lg' },
|
|
240
|
+
{ token: 'xl' },
|
|
241
|
+
])
|
|
242
|
+
})
|
|
243
|
+
})
|