@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,266 @@
|
|
|
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 EditorSettingsBlock from './EditorSettingsBlock.vue'
|
|
5
|
+
import EditorSettingsGroup from '../EditorSettingsGroup/EditorSettingsGroup.vue'
|
|
6
|
+
|
|
7
|
+
/** Rendered width of a `size="sm"` switch track (`w-9`), in pixels. */
|
|
8
|
+
const SM_TRACK_WIDTH = 36
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A wrapping label used to squash the switch, because the track sat in a flex
|
|
12
|
+
* row without `shrink-0`. Measured in a real browser: jsdom has no layout.
|
|
13
|
+
*/
|
|
14
|
+
const expectFullWidthTrack = (canvasElement: HTMLElement) => {
|
|
15
|
+
const track = canvasElement.querySelector('label > span.relative')
|
|
16
|
+
|
|
17
|
+
expect(track).not.toBeNull()
|
|
18
|
+
expect(track!.getBoundingClientRect().width).toBe(SM_TRACK_WIDTH)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The collapsing wrapper, found by the grid row it animates rather than by any
|
|
23
|
+
* styling class, so restyling the block cannot quietly break these assertions.
|
|
24
|
+
*/
|
|
25
|
+
const contentWrapper = (canvasElement: HTMLElement) =>
|
|
26
|
+
canvasElement.querySelector('[class*="grid-rows-"]') as HTMLElement
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
title: 'Components/Editor Components/Atoms/Editor Settings Block',
|
|
30
|
+
component: EditorSettingsBlock,
|
|
31
|
+
parameters: {
|
|
32
|
+
docs: {
|
|
33
|
+
description: {
|
|
34
|
+
component:
|
|
35
|
+
'One labelled setting in the editor sidebar, optionally collapsible via a switch. The block draws no separator of its own: the consumer groups and divides blocks on the wrapper, e.g. `divide-y divide-gray-200`.',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
decorators: [() => ({ template: '<div class="w-100 mx-auto"><story /></div>' })],
|
|
40
|
+
argTypes: {
|
|
41
|
+
label: { control: 'text' },
|
|
42
|
+
switchable: { control: 'boolean' },
|
|
43
|
+
modelValue: { control: 'boolean' },
|
|
44
|
+
},
|
|
45
|
+
args: {
|
|
46
|
+
label: 'Achtergrond',
|
|
47
|
+
switchable: false,
|
|
48
|
+
},
|
|
49
|
+
} satisfies Meta<typeof EditorSettingsBlock>
|
|
50
|
+
|
|
51
|
+
type Story = StoryObj<typeof EditorSettingsBlock>
|
|
52
|
+
|
|
53
|
+
export const Default: Story = {
|
|
54
|
+
render: (args) => ({
|
|
55
|
+
components: { EditorSettingsBlock },
|
|
56
|
+
setup: () => ({ args }),
|
|
57
|
+
template: `
|
|
58
|
+
<EditorSettingsBlock v-bind="args">
|
|
59
|
+
<p class="small-normal text-gray-700">Deze instelling staat altijd aan.</p>
|
|
60
|
+
</EditorSettingsBlock>
|
|
61
|
+
`,
|
|
62
|
+
}),
|
|
63
|
+
// The `-mx-1` / `px-1` pair that keeps a focus ring from being clipped must
|
|
64
|
+
// cancel out: slot content stays aligned with the label, and the widened
|
|
65
|
+
// clipping box may not push the block past its container.
|
|
66
|
+
play: ({ canvasElement }) => {
|
|
67
|
+
const label = canvasElement.querySelector('p')!
|
|
68
|
+
const block = label.parentElement!.parentElement!
|
|
69
|
+
const content = canvasElement.querySelectorAll('p')[1]
|
|
70
|
+
const clip = contentWrapper(canvasElement).firstElementChild!
|
|
71
|
+
|
|
72
|
+
expect(content.getBoundingClientRect().left).toBe(label.getBoundingClientRect().left)
|
|
73
|
+
expect(clip.getBoundingClientRect().width).toBe(block.getBoundingClientRect().width + 8)
|
|
74
|
+
expect(canvasElement.scrollWidth).toBe(canvasElement.clientWidth)
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const Switchable: Story = {
|
|
79
|
+
args: { switchable: true },
|
|
80
|
+
render: (args) => ({
|
|
81
|
+
components: { EditorSettingsBlock },
|
|
82
|
+
setup() {
|
|
83
|
+
const enabled = ref(true)
|
|
84
|
+
return { args, enabled }
|
|
85
|
+
},
|
|
86
|
+
template: `
|
|
87
|
+
<EditorSettingsBlock v-bind="args" v-model="enabled">
|
|
88
|
+
<p class="small-normal text-gray-700">Zet de switch uit om dit blok in te klappen.</p>
|
|
89
|
+
</EditorSettingsBlock>
|
|
90
|
+
`,
|
|
91
|
+
}),
|
|
92
|
+
play: async ({ canvasElement }) => {
|
|
93
|
+
expectFullWidthTrack(canvasElement)
|
|
94
|
+
|
|
95
|
+
const toggle = within(canvasElement).getByRole('switch')
|
|
96
|
+
const content = contentWrapper(canvasElement)
|
|
97
|
+
|
|
98
|
+
expect(content).toHaveClass('grid-rows-[1fr]')
|
|
99
|
+
expect(content).not.toHaveAttribute('inert')
|
|
100
|
+
|
|
101
|
+
await userEvent.click(toggle)
|
|
102
|
+
await waitFor(() => {
|
|
103
|
+
expect(content).toHaveClass('grid-rows-[0fr]')
|
|
104
|
+
expect(content).toHaveAttribute('inert')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
await userEvent.click(toggle)
|
|
108
|
+
await waitFor(() => {
|
|
109
|
+
expect(content).toHaveClass('grid-rows-[1fr]')
|
|
110
|
+
expect(content).not.toHaveAttribute('inert')
|
|
111
|
+
})
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const SwitchableClosed: Story = {
|
|
116
|
+
args: { switchable: true },
|
|
117
|
+
render: (args) => ({
|
|
118
|
+
components: { EditorSettingsBlock },
|
|
119
|
+
setup() {
|
|
120
|
+
const enabled = ref(false)
|
|
121
|
+
return { args, enabled }
|
|
122
|
+
},
|
|
123
|
+
template: `
|
|
124
|
+
<EditorSettingsBlock v-bind="args" v-model="enabled">
|
|
125
|
+
<p class="small-normal text-gray-700">Deze content is gemount maar ingeklapt.</p>
|
|
126
|
+
</EditorSettingsBlock>
|
|
127
|
+
`,
|
|
128
|
+
}),
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const LongLabel: Story = {
|
|
132
|
+
args: {
|
|
133
|
+
switchable: true,
|
|
134
|
+
label: 'Achtergrondafbeelding en overlaykleur van de headersectie',
|
|
135
|
+
},
|
|
136
|
+
render: (args) => ({
|
|
137
|
+
components: { EditorSettingsBlock },
|
|
138
|
+
setup() {
|
|
139
|
+
const enabled = ref(true)
|
|
140
|
+
return { args, enabled }
|
|
141
|
+
},
|
|
142
|
+
template: `
|
|
143
|
+
<EditorSettingsBlock v-bind="args" v-model="enabled">
|
|
144
|
+
<p class="small-normal text-gray-700">Het label loopt door over meerdere regels.</p>
|
|
145
|
+
</EditorSettingsBlock>
|
|
146
|
+
`,
|
|
147
|
+
}),
|
|
148
|
+
// Same assertion as Switchable: a wrapping label must not change the track.
|
|
149
|
+
play: ({ canvasElement }) => expectFullWidthTrack(canvasElement),
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The block draws no separator of its own; the wrapper divides the blocks,
|
|
153
|
+
// the way the app does.
|
|
154
|
+
export const Stacked: Story = {
|
|
155
|
+
render: () => ({
|
|
156
|
+
components: { EditorSettingsBlock },
|
|
157
|
+
setup() {
|
|
158
|
+
const logo = ref(true)
|
|
159
|
+
const footer = ref(false)
|
|
160
|
+
return { logo, footer }
|
|
161
|
+
},
|
|
162
|
+
template: `
|
|
163
|
+
<div class="divide-y divide-gray-200">
|
|
164
|
+
<EditorSettingsBlock label="Titel">
|
|
165
|
+
<p class="small-normal text-gray-700">Deze instelling staat altijd aan.</p>
|
|
166
|
+
</EditorSettingsBlock>
|
|
167
|
+
<EditorSettingsBlock label="Logo" switchable v-model="logo">
|
|
168
|
+
<p class="small-normal text-gray-700">Toon het makelaarslogo linksboven.</p>
|
|
169
|
+
</EditorSettingsBlock>
|
|
170
|
+
<EditorSettingsBlock label="Voettekst" switchable v-model="footer">
|
|
171
|
+
<p class="small-normal text-gray-700">Toon de contactgegevens onderaan.</p>
|
|
172
|
+
</EditorSettingsBlock>
|
|
173
|
+
</div>
|
|
174
|
+
`,
|
|
175
|
+
}),
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Two groups of blocks: the outer wrapper divides the groups, and the blocks
|
|
180
|
+
* inside a group share no separator.
|
|
181
|
+
*/
|
|
182
|
+
export const Grouped: Story = {
|
|
183
|
+
render: () => ({
|
|
184
|
+
components: { EditorSettingsBlock },
|
|
185
|
+
setup() {
|
|
186
|
+
const shadow = ref(false)
|
|
187
|
+
return { shadow }
|
|
188
|
+
},
|
|
189
|
+
template: `
|
|
190
|
+
<div class="divide-y divide-gray-200">
|
|
191
|
+
<div data-testid="group">
|
|
192
|
+
<EditorSettingsBlock label="Lettertype">
|
|
193
|
+
<p class="small-normal text-gray-700">Kies het lettertype van de kop.</p>
|
|
194
|
+
</EditorSettingsBlock>
|
|
195
|
+
<EditorSettingsBlock label="Regelhoogte">
|
|
196
|
+
<p class="small-normal text-gray-700">Ruimte tussen de regels.</p>
|
|
197
|
+
</EditorSettingsBlock>
|
|
198
|
+
</div>
|
|
199
|
+
<div data-testid="group">
|
|
200
|
+
<EditorSettingsBlock label="Achtergrond">
|
|
201
|
+
<p class="small-normal text-gray-700">Kleur achter de sectie.</p>
|
|
202
|
+
</EditorSettingsBlock>
|
|
203
|
+
<EditorSettingsBlock label="Schaduw" switchable v-model="shadow">
|
|
204
|
+
<p class="small-normal text-gray-700">Schaduw onder de sectie.</p>
|
|
205
|
+
</EditorSettingsBlock>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
`,
|
|
209
|
+
}),
|
|
210
|
+
// Measured in a real browser. Tailwind v4 draws `divide-y` as a bottom border
|
|
211
|
+
// on every child but the last, so only the first group carries one; no block
|
|
212
|
+
// inside either group draws a border.
|
|
213
|
+
play: ({ canvasElement }) => {
|
|
214
|
+
const groups = within(canvasElement).getAllByTestId('group')
|
|
215
|
+
const borderWidths = (element: Element) => {
|
|
216
|
+
const style = getComputedStyle(element)
|
|
217
|
+
return [style.borderTopWidth, style.borderBottomWidth]
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
expect(borderWidths(groups[0])).toEqual(['0px', '1px'])
|
|
221
|
+
expect(borderWidths(groups[1])).toEqual(['0px', '0px'])
|
|
222
|
+
for (const block of groups.flatMap((group) => [...group.children])) {
|
|
223
|
+
expect(borderWidths(block)).toEqual(['0px', '0px'])
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export const EmptyContent: Story = {
|
|
229
|
+
args: { switchable: true, label: 'Watermerk' },
|
|
230
|
+
render: (args) => ({
|
|
231
|
+
components: { EditorSettingsBlock },
|
|
232
|
+
setup() {
|
|
233
|
+
const enabled = ref(true)
|
|
234
|
+
return { args, enabled }
|
|
235
|
+
},
|
|
236
|
+
template: `<EditorSettingsBlock v-bind="args" v-model="enabled" />`,
|
|
237
|
+
}),
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Inside a group the block takes only `pt-4`: the group owns the line and the
|
|
242
|
+
* bottom space, so two settings sit 16px apart instead of 32.
|
|
243
|
+
*/
|
|
244
|
+
export const InGroup: Story = {
|
|
245
|
+
render: () => ({
|
|
246
|
+
components: { EditorSettingsBlock, EditorSettingsGroup },
|
|
247
|
+
template: `
|
|
248
|
+
<EditorSettingsGroup label="Achtergrond">
|
|
249
|
+
<EditorSettingsBlock label="Kleur">
|
|
250
|
+
<p class="small-normal text-gray-700">Kleurkiezer</p>
|
|
251
|
+
</EditorSettingsBlock>
|
|
252
|
+
<EditorSettingsBlock label="Afbeelding">
|
|
253
|
+
<p data-testid="second" class="small-normal text-gray-700">Afbeeldingkiezer</p>
|
|
254
|
+
</EditorSettingsBlock>
|
|
255
|
+
</EditorSettingsGroup>
|
|
256
|
+
`,
|
|
257
|
+
}),
|
|
258
|
+
play: ({ canvasElement }) => {
|
|
259
|
+
const canvas = within(canvasElement)
|
|
260
|
+
const firstContent = canvas.getByText('Kleurkiezer').getBoundingClientRect()
|
|
261
|
+
const secondLabel = canvas.getByText('Afbeelding').getBoundingClientRect()
|
|
262
|
+
|
|
263
|
+
// The content wrapper's pb-1 plus the next block's pt-4.
|
|
264
|
+
expect(secondLabel.top - firstContent.bottom).toBe(20)
|
|
265
|
+
},
|
|
266
|
+
}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { defineComponent } from 'vue'
|
|
4
|
+
import EditorSettingsBlock from './EditorSettingsBlock.vue'
|
|
5
|
+
import EditorSettingsGroup from '../EditorSettingsGroup/EditorSettingsGroup.vue'
|
|
6
|
+
|
|
7
|
+
const slots = { default: '<p>Block content</p>' }
|
|
8
|
+
|
|
9
|
+
describe('EditorSettingsBlock', () => {
|
|
10
|
+
// Deliberately no assertions on typography utilities anywhere in this file:
|
|
11
|
+
// the label is selected by structure or by text, so restyling the header
|
|
12
|
+
// never turns these tests red.
|
|
13
|
+
// The consumer groups and divides blocks on the wrapper, so the block itself
|
|
14
|
+
// may not draw a separator in either mode.
|
|
15
|
+
it.each([false, true])('puts no border classes on the root (switchable: %s)', (switchable) => {
|
|
16
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
17
|
+
props: { label: 'Achtergrond', switchable },
|
|
18
|
+
slots,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
expect(wrapper.classes().filter((name) => /(^|:)border(-|$)/.test(name))).toEqual([])
|
|
22
|
+
expect(wrapper.classes()).toContain('py-4')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('renders the label in a paragraph when it is not switchable', () => {
|
|
26
|
+
const wrapper = mount(EditorSettingsBlock, { props: { label: 'Achtergrond' }, slots })
|
|
27
|
+
|
|
28
|
+
expect(wrapper.find('p').text()).toBe('Achtergrond')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('renders the label inside the switch label when it is switchable', () => {
|
|
32
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
33
|
+
props: { label: 'Achtergrond', switchable: true },
|
|
34
|
+
slots,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
expect(wrapper.find('label span').text()).toBe('Achtergrond')
|
|
38
|
+
expect(wrapper.find('label').text()).toContain('Achtergrond')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('does not render a checkbox when it is not switchable', () => {
|
|
42
|
+
const wrapper = mount(EditorSettingsBlock, { props: { label: 'Achtergrond' }, slots })
|
|
43
|
+
|
|
44
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('keeps the content open and not inert when it is not switchable', () => {
|
|
48
|
+
const wrapper = mount(EditorSettingsBlock, { props: { label: 'Achtergrond' }, slots })
|
|
49
|
+
const content = wrapper.find('div.grid')
|
|
50
|
+
|
|
51
|
+
expect(content.classes()).toContain('grid-rows-[1fr]')
|
|
52
|
+
expect(content.attributes('inert')).toBeUndefined()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('ignores modelValue false when it is not switchable', () => {
|
|
56
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
57
|
+
props: { label: 'Achtergrond', modelValue: false },
|
|
58
|
+
slots,
|
|
59
|
+
})
|
|
60
|
+
const content = wrapper.find('div.grid')
|
|
61
|
+
|
|
62
|
+
expect(content.classes()).toContain('grid-rows-[1fr]')
|
|
63
|
+
expect(content.attributes('inert')).toBeUndefined()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('does not animate the content when it is not switchable', () => {
|
|
67
|
+
const wrapper = mount(EditorSettingsBlock, { props: { label: 'Achtergrond' }, slots })
|
|
68
|
+
|
|
69
|
+
expect(wrapper.find('div.grid').classes()).not.toContain('transition-[grid-template-rows]')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('animates the content when it is switchable', () => {
|
|
73
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
74
|
+
props: { label: 'Achtergrond', switchable: true },
|
|
75
|
+
slots,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
expect(wrapper.find('div.grid').classes()).toEqual(
|
|
79
|
+
expect.arrayContaining(['transition-[grid-template-rows]', 'motion-reduce:transition-none']),
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('checks the switch and opens the content when modelValue is true', () => {
|
|
84
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
85
|
+
props: { label: 'Achtergrond', switchable: true, modelValue: true },
|
|
86
|
+
slots,
|
|
87
|
+
})
|
|
88
|
+
const content = wrapper.find('div.grid')
|
|
89
|
+
|
|
90
|
+
expect((wrapper.find('input[type="checkbox"]').element as HTMLInputElement).checked).toBe(true)
|
|
91
|
+
expect(content.classes()).toContain('grid-rows-[1fr]')
|
|
92
|
+
expect(content.attributes('inert')).toBeUndefined()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('collapses and inerts the content when modelValue is false', () => {
|
|
96
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
97
|
+
props: { label: 'Achtergrond', switchable: true, modelValue: false },
|
|
98
|
+
slots,
|
|
99
|
+
})
|
|
100
|
+
const content = wrapper.find('div.grid')
|
|
101
|
+
|
|
102
|
+
expect((wrapper.find('input[type="checkbox"]').element as HTMLInputElement).checked).toBe(false)
|
|
103
|
+
expect(content.classes()).toContain('grid-rows-[0fr]')
|
|
104
|
+
expect(content.attributes('inert')).toBeDefined()
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('keeps collapsed slot content mounted', () => {
|
|
108
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
109
|
+
props: { label: 'Achtergrond', switchable: true, modelValue: false },
|
|
110
|
+
slots,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
expect(wrapper.find('div.grid').text()).toContain('Block content')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('emits update:modelValue when the switch is toggled', async () => {
|
|
117
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
118
|
+
props: { label: 'Achtergrond', switchable: true, modelValue: false },
|
|
119
|
+
slots,
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
await wrapper.find('input[type="checkbox"]').setValue(true)
|
|
123
|
+
|
|
124
|
+
expect(wrapper.emitted('update:modelValue')).toEqual([[true]])
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('defaults the model to true', () => {
|
|
128
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
129
|
+
props: { label: 'Achtergrond', switchable: true },
|
|
130
|
+
slots,
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
expect((wrapper.find('input[type="checkbox"]').element as HTMLInputElement).checked).toBe(true)
|
|
134
|
+
expect(wrapper.find('div.grid').attributes('inert')).toBeUndefined()
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('does not render a content wrapper without slot content', () => {
|
|
138
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
139
|
+
props: { label: 'Achtergrond', switchable: true },
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
expect(wrapper.find('div.grid').exists()).toBe(false)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('stretches the switch label across the header row', () => {
|
|
146
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
147
|
+
props: { label: 'Achtergrond', switchable: true },
|
|
148
|
+
slots,
|
|
149
|
+
})
|
|
150
|
+
const header = wrapper.find('label').element.parentElement!
|
|
151
|
+
|
|
152
|
+
expect(header.className).toContain('[&>label]:w-full')
|
|
153
|
+
expect(header.querySelector(':scope > label')).not.toBeNull()
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// The gap between label and track lives on the Switch's own <label>, not on
|
|
157
|
+
// the header: the header has exactly one child in both modes, so a `gap` on
|
|
158
|
+
// the header would never apply.
|
|
159
|
+
it('puts the header gap on the switch label instead of the header', () => {
|
|
160
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
161
|
+
props: { label: 'Achtergrond', switchable: true },
|
|
162
|
+
slots,
|
|
163
|
+
})
|
|
164
|
+
const header = wrapper.find('label').element.parentElement!
|
|
165
|
+
|
|
166
|
+
expect(header.className).toContain('[&>label]:gap-3')
|
|
167
|
+
expect(header.children).toHaveLength(1)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('reacts to modelValue changing after mount', async () => {
|
|
171
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
172
|
+
props: { label: 'Achtergrond', switchable: true, modelValue: true },
|
|
173
|
+
slots,
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
await wrapper.setProps({ modelValue: false })
|
|
177
|
+
|
|
178
|
+
expect(wrapper.find('div.grid').attributes('inert')).toBeDefined()
|
|
179
|
+
expect(wrapper.find('div.grid').classes()).toContain('grid-rows-[0fr]')
|
|
180
|
+
|
|
181
|
+
await wrapper.setProps({ modelValue: true })
|
|
182
|
+
|
|
183
|
+
expect(wrapper.find('div.grid').attributes('inert')).toBeUndefined()
|
|
184
|
+
expect(wrapper.find('div.grid').classes()).toContain('grid-rows-[1fr]')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('reacts to switchable changing after mount', async () => {
|
|
188
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
189
|
+
props: { label: 'Achtergrond', switchable: false, modelValue: false },
|
|
190
|
+
slots,
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
|
|
194
|
+
expect(wrapper.find('div.grid').attributes('inert')).toBeUndefined()
|
|
195
|
+
|
|
196
|
+
await wrapper.setProps({ switchable: true })
|
|
197
|
+
|
|
198
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true)
|
|
199
|
+
expect(wrapper.find('div.grid').attributes('inert')).toBeDefined()
|
|
200
|
+
expect(wrapper.find('div.grid').classes()).toContain('grid-rows-[0fr]')
|
|
201
|
+
|
|
202
|
+
await wrapper.setProps({ switchable: false })
|
|
203
|
+
|
|
204
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
|
|
205
|
+
expect(wrapper.find('div.grid').attributes('inert')).toBeUndefined()
|
|
206
|
+
expect(wrapper.find('div.grid').classes()).toContain('grid-rows-[1fr]')
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('sets describedby as aria-describedby on the switch input', () => {
|
|
210
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
211
|
+
props: { label: 'Achtergrond', switchable: true, describedby: 'hint' },
|
|
212
|
+
slots,
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
expect(wrapper.get('[role="switch"]').attributes('aria-describedby')).toBe('hint')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('renders no aria-describedby on the switch without describedby', () => {
|
|
219
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
220
|
+
props: { label: 'Achtergrond', switchable: true },
|
|
221
|
+
slots,
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
expect(wrapper.get('[role="switch"]').attributes('aria-describedby')).toBeUndefined()
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// Outside a NexxtEditorSettingsGroup the block must render exactly what it did
|
|
229
|
+
// before groups existed: other consumers of the library rely on its `py-4` and
|
|
230
|
+
// its markup. These snapshots were taken from the block before the group
|
|
231
|
+
// context was added, so any drift outside a group shows up here.
|
|
232
|
+
describe('EditorSettingsBlock outside a group', () => {
|
|
233
|
+
it('renders the stacked block unchanged', () => {
|
|
234
|
+
const wrapper = mount(EditorSettingsBlock, { props: { label: 'Achtergrond' }, slots })
|
|
235
|
+
|
|
236
|
+
expect(wrapper.html()).toMatchInlineSnapshot(`
|
|
237
|
+
"<div class="py-4">
|
|
238
|
+
<!--
|
|
239
|
+
Switch.vue sets \`inheritAttrs: false\` and binds \`$attrs\` on its sr-only
|
|
240
|
+
input, so a class on <Switch> never reaches its root <label>. The child
|
|
241
|
+
variant below styles that label from the header instead, which keeps the
|
|
242
|
+
whole row clickable and the accessible name on the real <label>. The gap
|
|
243
|
+
has to go on that label too: the header has a single child in both modes,
|
|
244
|
+
so its own \`gap\` would never apply and the spacing would silently fall
|
|
245
|
+
back to the \`gap-2\` that Switch.vue sets for itself.
|
|
246
|
+
-->
|
|
247
|
+
<div class="flex items-center [&>label]:flex [&>label]:w-full [&>label]:gap-3">
|
|
248
|
+
<p class="small-normal text-gray-900">Achtergrond</p>
|
|
249
|
+
</div>
|
|
250
|
+
<div class="grid grid-rows-[1fr]">
|
|
251
|
+
<!--
|
|
252
|
+
\`overflow-hidden\` is what makes the 0fr collapse clip, but it also clips
|
|
253
|
+
a focus ring on a full-width control in the slot, and the block has no
|
|
254
|
+
horizontal padding of its own. \`-mx-1\` widens the clipping box by 4px on
|
|
255
|
+
each side and the \`px-1\` below pushes the content back, so the ring has
|
|
256
|
+
room while the content keeps the same x position as the label.
|
|
257
|
+
-->
|
|
258
|
+
<div class="-mx-1 min-h-0 overflow-hidden opacity-100">
|
|
259
|
+
<!-- Padding sits on the inner element so nothing is left over at 0fr. -->
|
|
260
|
+
<div class="px-1 pt-1.5 pb-1">
|
|
261
|
+
<p>Block content</p>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>"
|
|
266
|
+
`)
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('renders the switchable block unchanged', () => {
|
|
270
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
271
|
+
props: { label: 'Achtergrond', switchable: true, modelValue: false },
|
|
272
|
+
slots,
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
expect(wrapper.html()).toMatchInlineSnapshot(`
|
|
276
|
+
"<div class="py-4">
|
|
277
|
+
<!--
|
|
278
|
+
Switch.vue sets \`inheritAttrs: false\` and binds \`$attrs\` on its sr-only
|
|
279
|
+
input, so a class on <Switch> never reaches its root <label>. The child
|
|
280
|
+
variant below styles that label from the header instead, which keeps the
|
|
281
|
+
whole row clickable and the accessible name on the real <label>. The gap
|
|
282
|
+
has to go on that label too: the header has a single child in both modes,
|
|
283
|
+
so its own \`gap\` would never apply and the spacing would silently fall
|
|
284
|
+
back to the \`gap-2\` that Switch.vue sets for itself.
|
|
285
|
+
-->
|
|
286
|
+
<div class="flex items-center [&>label]:flex [&>label]:w-full [&>label]:gap-3"><label class="inline-flex cursor-pointer items-center gap-2"><span class="grow text-left small-normal text-gray-900">Achtergrond</span>
|
|
287
|
+
<!--
|
|
288
|
+
\`shrink-0\`: the track has a fixed pixel size, so letting flex shrink it
|
|
289
|
+
squashes the toggle the moment a sibling label wraps onto a second line.
|
|
290
|
+
--><span class="relative inline-block shrink-0 h-5 w-9"><!--
|
|
291
|
+
\`role="switch"\` on a native checkbox: it is on/off rather than
|
|
292
|
+
selected/unselected, and the checked state is still the input's own, so
|
|
293
|
+
no \`aria-checked\` is set -- the browser derives it from the checkbox.
|
|
294
|
+
--><input type="checkbox" role="switch" class="peer sr-only"><span class="absolute inset-0 overflow-hidden rounded-[30px] border transition-colors duration-200 peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-cornflower-blue-500 border-gray-500 bg-gray-200"><span class="absolute rounded-full bg-white transition-transform duration-400 top-0.5 left-0.5 h-3.5 w-3.5"></span><span class="absolute flex items-center justify-center rounded-full bg-white transition-transform duration-300 top-0.5 left-0.5 h-3.5 w-3.5"><!--v-if--></span></span></span>
|
|
295
|
+
<!--v-if-->
|
|
296
|
+
</label></div>
|
|
297
|
+
<div inert="true" class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-300 ease-in-out motion-reduce:transition-none">
|
|
298
|
+
<!--
|
|
299
|
+
\`overflow-hidden\` is what makes the 0fr collapse clip, but it also clips
|
|
300
|
+
a focus ring on a full-width control in the slot, and the block has no
|
|
301
|
+
horizontal padding of its own. \`-mx-1\` widens the clipping box by 4px on
|
|
302
|
+
each side and the \`px-1\` below pushes the content back, so the ring has
|
|
303
|
+
room while the content keeps the same x position as the label.
|
|
304
|
+
-->
|
|
305
|
+
<div class="-mx-1 min-h-0 overflow-hidden opacity-0 transition-opacity duration-200 ease-in-out motion-reduce:transition-none">
|
|
306
|
+
<!-- Padding sits on the inner element so nothing is left over at 0fr. -->
|
|
307
|
+
<div class="px-1 pt-1.5 pb-1">
|
|
308
|
+
<p>Block content</p>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
</div>"
|
|
313
|
+
`)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it('renders a block without content unchanged', () => {
|
|
317
|
+
const wrapper = mount(EditorSettingsBlock, { props: { label: 'Achtergrond' } })
|
|
318
|
+
|
|
319
|
+
expect(wrapper.html()).toMatchInlineSnapshot(`
|
|
320
|
+
"<div class="py-4">
|
|
321
|
+
<!--
|
|
322
|
+
Switch.vue sets \`inheritAttrs: false\` and binds \`$attrs\` on its sr-only
|
|
323
|
+
input, so a class on <Switch> never reaches its root <label>. The child
|
|
324
|
+
variant below styles that label from the header instead, which keeps the
|
|
325
|
+
whole row clickable and the accessible name on the real <label>. The gap
|
|
326
|
+
has to go on that label too: the header has a single child in both modes,
|
|
327
|
+
so its own \`gap\` would never apply and the spacing would silently fall
|
|
328
|
+
back to the \`gap-2\` that Switch.vue sets for itself.
|
|
329
|
+
-->
|
|
330
|
+
<div class="flex items-center [&>label]:flex [&>label]:w-full [&>label]:gap-3">
|
|
331
|
+
<p class="small-normal text-gray-900">Achtergrond</p>
|
|
332
|
+
</div>
|
|
333
|
+
<!--v-if-->
|
|
334
|
+
</div>"
|
|
335
|
+
`)
|
|
336
|
+
})
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
describe('EditorSettingsBlock in a group', () => {
|
|
340
|
+
/** A block inside a group, as one app so the group context reaches it. */
|
|
341
|
+
const inGroup = (blockProps: Record<string, unknown>) =>
|
|
342
|
+
mount(
|
|
343
|
+
defineComponent({
|
|
344
|
+
components: { EditorSettingsGroup, EditorSettingsBlock },
|
|
345
|
+
setup: () => ({ blockProps }),
|
|
346
|
+
template: `
|
|
347
|
+
<EditorSettingsGroup label="Lijn">
|
|
348
|
+
<EditorSettingsBlock label="Uitlijning" v-bind="blockProps">
|
|
349
|
+
<button type="button">Optie</button>
|
|
350
|
+
</EditorSettingsBlock>
|
|
351
|
+
</EditorSettingsGroup>
|
|
352
|
+
`,
|
|
353
|
+
}),
|
|
354
|
+
).findComponent(EditorSettingsBlock)
|
|
355
|
+
|
|
356
|
+
it('stacks: label above, content in the collapsing wrapper, pt-4 on the root', () => {
|
|
357
|
+
const block = inGroup({})
|
|
358
|
+
|
|
359
|
+
expect(block.find('[class*="grid-rows-"]').element.contains(block.get('button').element)).toBe(
|
|
360
|
+
true,
|
|
361
|
+
)
|
|
362
|
+
expect((block.element as HTMLElement).classList).toEqual(
|
|
363
|
+
expect.objectContaining({ value: 'pt-4' }),
|
|
364
|
+
)
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
it('keeps an ordinary label gray-900 under a labelled group', () => {
|
|
368
|
+
const block = inGroup({})
|
|
369
|
+
|
|
370
|
+
expect(block.get('p').classes()).toContain('text-gray-900')
|
|
371
|
+
expect(block.get('p').classes()).not.toContain('text-cornflower-blue-700')
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
it('takes no layout prop: a stray one falls through as a plain attribute', () => {
|
|
375
|
+
const block = inGroup({ layout: 'inline' })
|
|
376
|
+
|
|
377
|
+
expect(block.props()).not.toHaveProperty('layout')
|
|
378
|
+
expect(block.find('[class*="grid-rows-"]').exists()).toBe(true)
|
|
379
|
+
})
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
describe('EditorSettingsBlock label colour', () => {
|
|
383
|
+
/** A block as the only setting of a group without a label, so its label is promoted. */
|
|
384
|
+
const promoted = (blockProps: Record<string, unknown> = {}) =>
|
|
385
|
+
mount(
|
|
386
|
+
defineComponent({
|
|
387
|
+
components: { EditorSettingsGroup, EditorSettingsBlock },
|
|
388
|
+
setup: () => ({ blockProps }),
|
|
389
|
+
template: `
|
|
390
|
+
<EditorSettingsGroup>
|
|
391
|
+
<EditorSettingsBlock label="Uitlijning" v-bind="blockProps">Optie</EditorSettingsBlock>
|
|
392
|
+
</EditorSettingsGroup>
|
|
393
|
+
`,
|
|
394
|
+
}),
|
|
395
|
+
).findComponent(EditorSettingsBlock)
|
|
396
|
+
|
|
397
|
+
it.each([false, true])('stays gray-900 outside a group (switchable: %s)', (switchable) => {
|
|
398
|
+
const wrapper = mount(EditorSettingsBlock, {
|
|
399
|
+
props: { label: 'Achtergrond', switchable },
|
|
400
|
+
slots,
|
|
401
|
+
})
|
|
402
|
+
const label = wrapper.get(switchable ? 'label span' : 'p')
|
|
403
|
+
|
|
404
|
+
expect(label.classes()).toContain('text-gray-900')
|
|
405
|
+
expect(label.classes()).not.toContain('text-cornflower-blue-700')
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
it('takes the group title colour when promoted to a heading', () => {
|
|
409
|
+
const heading = promoted().get('h3')
|
|
410
|
+
|
|
411
|
+
expect(heading.classes()).toContain('text-cornflower-blue-700')
|
|
412
|
+
expect(heading.classes()).not.toContain('text-gray-900')
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
it('takes the group title colour when promoted inside the switch label', () => {
|
|
416
|
+
const label = promoted({ switchable: true }).get('label span')
|
|
417
|
+
|
|
418
|
+
expect(label.classes()).toContain('text-cornflower-blue-700')
|
|
419
|
+
expect(label.classes()).not.toContain('text-gray-900')
|
|
420
|
+
})
|
|
421
|
+
})
|