@nexxtmove/ui 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/dist/index.js +49 -126
  2. package/dist/nuxt.js +0 -1
  3. package/package.json +38 -36
  4. package/src/attribute-contract.test.ts +151 -0
  5. package/src/components/atoms/AnimatedNumber/AnimatedNumber.stories.ts +36 -0
  6. package/src/components/atoms/AnimatedNumber/AnimatedNumber.test.ts +21 -0
  7. package/src/components/atoms/Avatar/Avatar.stories.ts +122 -0
  8. package/src/components/atoms/Avatar/Avatar.test.ts +134 -0
  9. package/src/components/atoms/Button/Button.stories.ts +290 -0
  10. package/src/components/atoms/Button/Button.test.ts +406 -0
  11. package/src/components/atoms/Card/Card.stories.ts +45 -0
  12. package/src/components/atoms/Card/Card.test.ts +46 -0
  13. package/src/components/atoms/Checkbox/Checkbox.stories.ts +35 -0
  14. package/src/components/atoms/Checkbox/Checkbox.test.ts +55 -0
  15. package/src/components/atoms/Chip/Chip.stories.ts +274 -0
  16. package/src/components/atoms/Chip/Chip.test.ts +230 -0
  17. package/src/components/atoms/EnergyLabel/EnergyLabel.stories.ts +189 -0
  18. package/src/components/atoms/EnergyLabel/EnergyLabel.test.ts +100 -0
  19. package/src/components/atoms/FloatingPanel/FloatingPanel.stories.ts +677 -0
  20. package/src/components/atoms/FloatingPanel/FloatingPanel.test.ts +864 -0
  21. package/src/components/atoms/Icon/Icon.stories.ts +63 -0
  22. package/src/components/atoms/Icon/Icon.test.ts +89 -0
  23. package/src/components/atoms/Loader/Loader.stories.ts +36 -0
  24. package/src/components/atoms/Loader/Loader.test.ts +40 -0
  25. package/src/components/atoms/PickerItem/PickerItem.stories.ts +172 -0
  26. package/src/components/atoms/PickerItem/PickerItem.test.ts +142 -0
  27. package/src/components/atoms/PickerPreview/PickerPreview.stories.ts +183 -0
  28. package/src/components/atoms/PickerPreview/PickerPreview.test.ts +137 -0
  29. package/src/components/atoms/PresenceIndicator/PresenceIndicator.stories.ts +21 -0
  30. package/src/components/atoms/PropertyListing/PropertyListing.stories.ts +38 -0
  31. package/src/components/atoms/PropertyListing/PropertyListing.test.ts +46 -0
  32. package/src/components/atoms/SelectableOption/SelectableOption.stories.ts +131 -0
  33. package/src/components/atoms/SelectableOption/SelectableOption.test.ts +96 -0
  34. package/src/components/atoms/Skeleton/Skeleton.stories.ts +27 -0
  35. package/src/components/atoms/Skeleton/Skeleton.test.ts +28 -0
  36. package/src/components/atoms/Slider/Slider.stories.ts +151 -0
  37. package/src/components/atoms/Slider/Slider.test.ts +191 -0
  38. package/src/components/atoms/Squircle/Squircle.stories.ts +114 -0
  39. package/src/components/atoms/Squircle/Squircle.test.ts +70 -0
  40. package/src/components/atoms/Switch/Switch.stories.ts +91 -0
  41. package/src/components/atoms/Switch/Switch.test.ts +183 -0
  42. package/src/components/atoms/Tooltip/Tooltip.stories.ts +158 -0
  43. package/src/components/atoms/Tooltip/Tooltip.test.ts +134 -0
  44. package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.stories.ts +124 -0
  45. package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.test.ts +464 -0
  46. package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.stories.ts +266 -0
  47. package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.test.ts +421 -0
  48. package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.stories.ts +365 -0
  49. package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.test.ts +484 -0
  50. package/src/components/editor/composables/useRadioGrid.test.ts +261 -0
  51. package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.stories.ts +73 -0
  52. package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.test.ts +189 -0
  53. package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.stories.ts +309 -0
  54. package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.test.ts +359 -0
  55. package/src/components/editor/molecules/EditorAnchor/EditorAnchor.stories.ts +138 -0
  56. package/src/components/editor/molecules/EditorAnchor/EditorAnchor.test.ts +344 -0
  57. package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.stories.ts +87 -0
  58. package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.test.ts +262 -0
  59. package/src/components/editor/molecules/EditorBlur/EditorBlur.stories.ts +115 -0
  60. package/src/components/editor/molecules/EditorBlur/EditorBlur.test.ts +243 -0
  61. package/src/components/editor/molecules/EditorBorder/EditorBorder.stories.ts +187 -0
  62. package/src/components/editor/molecules/EditorBorder/EditorBorder.test.ts +456 -0
  63. package/src/components/editor/molecules/EditorColor/EditorColor.stories.ts +563 -0
  64. package/src/components/editor/molecules/EditorColor/EditorColor.test.ts +689 -0
  65. package/src/components/editor/molecules/EditorColor/EditorColorGradient.test.ts +793 -0
  66. package/src/components/editor/molecules/EditorColor/gradientClass.test.ts +303 -0
  67. package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.stories.ts +72 -0
  68. package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.test.ts +124 -0
  69. package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.stories.ts +46 -0
  70. package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.test.ts +97 -0
  71. package/src/components/editor/molecules/EditorFontSize/EditorFontSize.stories.ts +248 -0
  72. package/src/components/editor/molecules/EditorFontSize/EditorFontSize.test.ts +288 -0
  73. package/src/components/editor/molecules/EditorGap/EditorGap.stories.ts +129 -0
  74. package/src/components/editor/molecules/EditorGap/EditorGap.test.ts +253 -0
  75. package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.stories.ts +68 -0
  76. package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.test.ts +150 -0
  77. package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.stories.ts +71 -0
  78. package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.test.ts +161 -0
  79. package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.stories.ts +204 -0
  80. package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.test.ts +238 -0
  81. package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.stories.ts +257 -0
  82. package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.test.ts +286 -0
  83. package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.stories.ts +135 -0
  84. package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.test.ts +230 -0
  85. package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.stories.ts +154 -0
  86. package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.test.ts +112 -0
  87. package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.stories.ts +206 -0
  88. package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.test.ts +332 -0
  89. package/src/components/editor/molecules/EditorPadding/EditorPadding.stories.ts +374 -0
  90. package/src/components/editor/molecules/EditorPadding/EditorPadding.test.ts +712 -0
  91. package/src/components/editor/molecules/EditorRadius/EditorRadius.stories.ts +64 -0
  92. package/src/components/editor/molecules/EditorRadius/EditorRadius.test.ts +137 -0
  93. package/src/components/editor/molecules/EditorShadow/EditorShadow.stories.ts +103 -0
  94. package/src/components/editor/molecules/EditorShadow/EditorShadow.test.ts +168 -0
  95. package/src/components/editor/molecules/EditorSize/EditorSize.stories.ts +84 -0
  96. package/src/components/editor/molecules/EditorSize/EditorSize.test.ts +253 -0
  97. package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.stories.ts +49 -0
  98. package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.test.ts +43 -0
  99. package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.stories.ts +69 -0
  100. package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.test.ts +155 -0
  101. package/src/components/molecules/Announcement/Announcement.stories.ts +224 -0
  102. package/src/components/molecules/Announcement/Announcement.test.ts +161 -0
  103. package/src/components/molecules/ContactInfoCard/ContactInfoCard.stories.ts +99 -0
  104. package/src/components/molecules/ContactInfoCard/ContactInfoCard.test.ts +70 -0
  105. package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.stories.ts +148 -0
  106. package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.test.ts +271 -0
  107. package/src/components/molecules/DoughnutChart/DoughnutChart.stories.ts +45 -0
  108. package/src/components/molecules/DoughnutChart/DoughnutChart.test.ts +205 -0
  109. package/src/components/molecules/InfoBlock/InfoBlock.stories.ts +84 -0
  110. package/src/components/molecules/InfoBlock/InfoBlock.test.ts +101 -0
  111. package/src/components/molecules/InputField/InputField.stories.ts +117 -0
  112. package/src/components/molecules/InputField/InputField.test.ts +138 -0
  113. package/src/components/molecules/InputSelect/InputSelect.stories.ts +116 -0
  114. package/src/components/molecules/InputSelect/InputSelect.test.ts +127 -0
  115. package/src/components/molecules/ModalFooter/ModalFooter.stories.ts +204 -0
  116. package/src/components/molecules/ModalFooter/ModalFooter.test.ts +276 -0
  117. package/src/components/molecules/PageCard/PageCard.stories.ts +100 -0
  118. package/src/components/molecules/PageCard/PageCard.test.ts +91 -0
  119. package/src/components/molecules/PageContainer/PageContainer.stories.ts +90 -0
  120. package/src/components/molecules/PageContainer/PageContainer.test.ts +50 -0
  121. package/src/components/molecules/PageMockup/PageMockup.stories.ts +38 -0
  122. package/src/components/molecules/PageMockup/PageMockup.test.ts +20 -0
  123. package/src/components/molecules/PageTypeTile/PageTypeTile.stories.ts +63 -0
  124. package/src/components/molecules/PageTypeTile/PageTypeTile.test.ts +41 -0
  125. package/src/components/molecules/Pagination/Pagination.stories.ts +72 -0
  126. package/src/components/molecules/Pagination/Pagination.test.ts +130 -0
  127. package/src/components/molecules/PickerGroup/PickerGroup.stories.ts +90 -0
  128. package/src/components/molecules/PickerGroup/PickerGroup.test.ts +145 -0
  129. package/src/components/molecules/PickerList/PickerList.stories.ts +436 -0
  130. package/src/components/molecules/PickerList/PickerList.test.ts +870 -0
  131. package/src/components/molecules/PlaceholderInput/PlaceholderInput.stories.ts +48 -0
  132. package/src/components/molecules/PlaceholderInput/PlaceholderInput.test.ts +107 -0
  133. package/src/components/molecules/PropertyCard/PropertyCard.stories.ts +46 -0
  134. package/src/components/molecules/PropertyCard/PropertyCard.test.ts +59 -0
  135. package/src/components/molecules/Select/Select.stories.ts +264 -0
  136. package/src/components/molecules/Select/Select.test.ts +315 -0
  137. package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.stories.ts +84 -0
  138. package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.test.ts +103 -0
  139. package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.stories.ts +11 -0
  140. package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.test.ts +38 -0
  141. package/src/components/molecules/SocialIcons/SocialIcons.stories.ts +28 -0
  142. package/src/components/molecules/SocialIcons/SocialIcons.test.ts +58 -0
  143. package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.stories.ts +11 -0
  144. package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.test.ts +116 -0
  145. package/src/components/molecules/SocialMediaType/SocialMediaType.stories.ts +37 -0
  146. package/src/components/molecules/SocialMediaType/SocialMediaType.test.ts +126 -0
  147. package/src/components/molecules/Source/Source.stories.ts +35 -0
  148. package/src/components/molecules/Source/Source.test.ts +45 -0
  149. package/src/components/molecules/TabbedContent/TabbedContent.stories.ts +96 -0
  150. package/src/components/molecules/TabbedContent/TabbedContent.test.ts +109 -0
  151. package/src/components/molecules/Tabs/Tabs.stories.ts +230 -0
  152. package/src/components/molecules/Tabs/Tabs.test.ts +280 -0
  153. package/src/components/molecules/TemplateCard/TemplateCard.stories.ts +327 -0
  154. package/src/components/molecules/TemplateCard/TemplateCard.test.ts +339 -0
  155. package/src/components/molecules/Textarea/Textarea.stories.ts +216 -0
  156. package/src/components/molecules/Textarea/Textarea.test.ts +191 -0
  157. package/src/components/molecules/TimelineEvent/TimelineEvent.stories.ts +319 -0
  158. package/src/components/molecules/TimelineEvent/TimelineEvent.test.ts +224 -0
  159. package/src/components/molecules/TimelineLanes/TimelineLanes.stories.ts +29 -0
  160. package/src/components/molecules/TimelineLanes/TimelineLanes.test.ts +30 -0
  161. package/src/components/molecules/Toast/Toast.stories.ts +110 -0
  162. package/src/components/molecules/Toast/Toast.test.ts +103 -0
  163. package/src/components/molecules/ToggleGroup/ToggleGroup.stories.ts +297 -0
  164. package/src/components/molecules/ToggleGroup/ToggleGroup.test.ts +578 -0
  165. package/src/components/molecules/Tree/Tree.stories.ts +884 -0
  166. package/src/components/molecules/Tree/Tree.test.ts +1247 -0
  167. package/src/components/molecules/Tree/tree-drop.test.ts +223 -0
  168. package/src/components/organisms/Calendar/Calendar.stories.ts +108 -0
  169. package/src/components/organisms/Calendar/Calendar.test.ts +279 -0
  170. package/src/components/organisms/Calendar/_CalendarDayView.test.ts +104 -0
  171. package/src/components/organisms/Calendar/_CalendarHeader.test.ts +86 -0
  172. package/src/components/organisms/Calendar/_CalendarMonthView.test.ts +52 -0
  173. package/src/components/organisms/Calendar/_CalendarTimePicker.test.ts +89 -0
  174. package/src/components/organisms/Calendar/_CalendarYearView.test.ts +56 -0
  175. package/src/components/organisms/ColorPicker/ColorPicker.stories.ts +229 -0
  176. package/src/components/organisms/ColorPicker/ColorPicker.test.ts +638 -0
  177. package/src/components/organisms/ColorPicker/gradient.test.ts +67 -0
  178. package/src/components/organisms/ColorPicker/gradientGeometry.test.ts +206 -0
  179. package/src/components/organisms/Combobox/Combobox.stories.ts +234 -0
  180. package/src/components/organisms/Combobox/Combobox.test.ts +512 -0
  181. package/src/components/organisms/Combobox/Combobox.vue +1 -1
  182. package/src/components/organisms/ComboboxPanel/ComboboxPanel.stories.ts +158 -0
  183. package/src/components/organisms/ComboboxPanel/ComboboxPanel.test.ts +524 -0
  184. package/src/components/organisms/ComboboxPanel/ComboboxPanel.vue +1 -1
  185. package/src/components/organisms/CountrySelect/CountrySelect.stories.ts +139 -0
  186. package/src/components/organisms/CountrySelect/CountrySelect.test.ts +149 -0
  187. package/src/components/organisms/DatePicker/DatePicker.stories.ts +163 -0
  188. package/src/components/organisms/DatePicker/DatePicker.test.ts +214 -0
  189. package/src/components/organisms/DropdownButton/DropdownButton.stories.ts +123 -0
  190. package/src/components/organisms/DropdownButton/DropdownButton.test.ts +379 -0
  191. package/src/components/organisms/FontPicker/FontPicker.stories.ts +476 -0
  192. package/src/components/organisms/FontPicker/FontPicker.test.ts +873 -0
  193. package/src/components/organisms/Header/Header.stories.ts +75 -0
  194. package/src/components/organisms/Header/Header.test.ts +233 -0
  195. package/src/components/organisms/IconPicker/IconPicker.stories.ts +734 -0
  196. package/src/components/organisms/IconPicker/IconPicker.test.ts +669 -0
  197. package/src/components/organisms/IconPicker/_IconPickerGrid.test.ts +184 -0
  198. package/src/components/organisms/IconPicker/_IconPickerRail.test.ts +102 -0
  199. package/src/components/organisms/IconPicker/useIconPickerNavigation.test.ts +215 -0
  200. package/src/components/organisms/Modal/Modal.stories.ts +350 -0
  201. package/src/components/organisms/Modal/Modal.test.ts +275 -0
  202. package/src/components/organisms/PickerPanel/PickerPanel.stories.ts +452 -0
  203. package/src/components/organisms/PickerPanel/PickerPanel.test.ts +358 -0
  204. package/src/components/organisms/ProgressBar/ProgressBar.stories.ts +100 -0
  205. package/src/components/organisms/ProgressBar/ProgressBar.test.ts +417 -0
  206. package/src/components/organisms/ReferenceCard/ReferenceCard.stories.ts +191 -0
  207. package/src/components/organisms/ReferenceCard/ReferenceCard.test.ts +165 -0
  208. package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.stories.ts +65 -0
  209. package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.test.ts +466 -0
  210. package/src/components/organisms/StepperHeader/StepperHeader.stories.ts +91 -0
  211. package/src/components/organisms/StepperHeader/StepperHeader.test.ts +279 -0
  212. package/src/components/organisms/Table/Table.stories.ts +332 -0
  213. package/src/components/organisms/Table/Table.test.ts +320 -0
  214. package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.stories.ts +198 -0
  215. package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.test.ts +283 -0
  216. package/src/components/organisms/TimelineTrack/TimelineTrack.stories.ts +120 -0
  217. package/src/components/organisms/TimelineTrack/TimelineTrack.test.ts +91 -0
  218. package/src/components/organisms/Toaster/Toaster.stories.ts +51 -0
  219. package/src/components/organisms/Toaster/Toaster.test.ts +75 -0
  220. package/src/components/organisms/VideoControls/VideoControls.stories.ts +792 -0
  221. package/src/components/organisms/VideoControls/VideoControls.test.ts +1056 -0
  222. package/src/components/organisms/VideoPlayer/VideoPlayer.stories.ts +327 -0
  223. package/src/components/organisms/VideoPlayer/VideoPlayer.test.ts +439 -0
@@ -0,0 +1,712 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { flushPromises, mount, type DOMWrapper, type VueWrapper } from '@vue/test-utils'
3
+ import EditorPadding, { EDITOR_PADDING_STEPS } from './EditorPadding.vue'
4
+ import PaddingIcon from './_PaddingIcon.vue'
5
+ import type { PaddingIconSide } from './_PaddingIcon.vue'
6
+
7
+ const render = (modelValue: string, props: Record<string, unknown> = {}) =>
8
+ mount(EditorPadding, { props: { modelValue, ...props } })
9
+
10
+ /**
11
+ * Every field, including the two in the collapsed second row: that row stays
12
+ * mounted so its height can be animated.
13
+ */
14
+ const inputs = (wrapper: VueWrapper) => wrapper.findAll('input')
15
+
16
+ /** The fields a user can actually reach -- the collapsed row is `inert`. */
17
+ const activeInputs = (wrapper: VueWrapper) =>
18
+ inputs(wrapper).filter((input) => input.element.closest('[inert]') === null)
19
+
20
+ const secondaryRow = (wrapper: VueWrapper) =>
21
+ wrapper.get('[data-testid="editor-padding-secondary-row"]')
22
+
23
+ /** Value in px, as the user sees it; a 0 shows as an empty field with a `0` placeholder. */
24
+ const values = (wrapper: VueWrapper) =>
25
+ activeInputs(wrapper).map((input) => (input.element as HTMLInputElement).value)
26
+
27
+ const toggle = (wrapper: VueWrapper) => wrapper.get('button')
28
+
29
+ /** The last class string the component wrote back. */
30
+ const lastEmit = (wrapper: VueWrapper) => wrapper.emitted('update:modelValue')?.at(-1)?.[0]
31
+
32
+ /** Type without confirming: the field keeps the text to itself until it commits. */
33
+ const type = async (input: DOMWrapper<Element>, typed: string) => {
34
+ await input.setValue(typed)
35
+ await flushPromises()
36
+ }
37
+
38
+ /** Type and confirm, the way leaving the field does. */
39
+ const commit = async (input: DOMWrapper<Element>, typed: string) => {
40
+ await type(input, typed)
41
+ await input.trigger('blur')
42
+ await flushPromises()
43
+ }
44
+
45
+ const press = async (input: DOMWrapper<Element>, key: string) => {
46
+ await input.trigger('keydown', { key })
47
+ await flushPromises()
48
+ }
49
+
50
+ /** Keydowns that make it out of the block, so a surrounding popover would see them. */
51
+ const bubbled = (wrapper: VueWrapper) => {
52
+ const seen: KeyboardEvent[] = []
53
+ wrapper.element.addEventListener('keydown', (event: KeyboardEvent) => seen.push(event))
54
+
55
+ return seen
56
+ }
57
+
58
+ describe('EditorPadding', () => {
59
+ // No assertions on typography utilities: fields are found by role/structure
60
+ // and by their accessible name, so restyling cannot turn these red.
61
+ describe('parsing', () => {
62
+ it('shows one field per axis for a value that both axes share', () => {
63
+ const wrapper = render('p-4')
64
+
65
+ expect(values(wrapper)).toEqual(['16', '16'])
66
+ expect(toggle(wrapper).attributes('aria-pressed')).toBe('false')
67
+ })
68
+
69
+ it('splits px/py over the two collapsed fields', () => {
70
+ const wrapper = render('px-4 py-2')
71
+
72
+ expect(values(wrapper)).toEqual(['16', '8'])
73
+ })
74
+
75
+ it('opens the four fields when the two axes are not symmetrical', () => {
76
+ const wrapper = render('pt-4 pr-2 pb-6 pl-2')
77
+
78
+ expect(values(wrapper)).toEqual(['16', '8', '24', '8'])
79
+ expect(toggle(wrapper).attributes('aria-pressed')).toBe('true')
80
+ })
81
+
82
+ it('shows the top of the scale in px', () => {
83
+ expect(values(render('p-96'))).toEqual(['384', '384'])
84
+ })
85
+
86
+ it('falls back to zero on all sides for a string it cannot read', () => {
87
+ const wrapper = render('rounded-lg p-13 nonsense')
88
+
89
+ expect(values(wrapper)).toEqual(['', ''])
90
+ })
91
+
92
+ it('lets a later token win over an earlier one', () => {
93
+ const wrapper = render('p-4 pt-2')
94
+
95
+ expect(values(wrapper)).toEqual(['8', '16', '16', '16'])
96
+ })
97
+
98
+ it('re-reads a value that changes from outside', async () => {
99
+ const wrapper = render('p-4')
100
+
101
+ await wrapper.setProps({ modelValue: 'p-8' })
102
+
103
+ expect(values(wrapper)).toEqual(['32', '32'])
104
+ })
105
+ })
106
+
107
+ describe('toggle', () => {
108
+ it('opens the four fields without touching the value', async () => {
109
+ const wrapper = render('p-4')
110
+
111
+ await toggle(wrapper).trigger('click')
112
+
113
+ expect(values(wrapper)).toEqual(['16', '16', '16', '16'])
114
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
115
+ })
116
+
117
+ it('keeps the second row mounted but collapsed and inert while closed', () => {
118
+ const wrapper = render('p-4')
119
+
120
+ expect(inputs(wrapper)).toHaveLength(4)
121
+ expect(secondaryRow(wrapper).classes()).toContain('grid-rows-[0fr]')
122
+ expect(secondaryRow(wrapper).attributes('inert')).toBeDefined()
123
+ })
124
+
125
+ it('expands the second row and hands it back to the tab order', async () => {
126
+ const wrapper = render('p-4')
127
+
128
+ await toggle(wrapper).trigger('click')
129
+
130
+ expect(secondaryRow(wrapper).classes()).toContain('grid-rows-[1fr]')
131
+ expect(secondaryRow(wrapper).attributes('inert')).toBeUndefined()
132
+ })
133
+
134
+ it('collapses back to two fields without touching a symmetrical value', async () => {
135
+ const wrapper = render('p-4')
136
+
137
+ await toggle(wrapper).trigger('click')
138
+ await toggle(wrapper).trigger('click')
139
+
140
+ expect(values(wrapper)).toEqual(['16', '16'])
141
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
142
+ })
143
+
144
+ // The two fields that stay on screen win, and the value follows at once:
145
+ // what is shown and what is on the element must not disagree.
146
+ it('rewrites the value from the two visible fields when it closes', async () => {
147
+ const wrapper = render('pt-4 pr-2 pb-6 pl-2')
148
+
149
+ await toggle(wrapper).trigger('click')
150
+
151
+ expect(lastEmit(wrapper)).toBe('px-2 py-4')
152
+ // Horizontal now reads what Rechts said, vertical what Boven said.
153
+ expect(values(wrapper)).toEqual(['8', '16'])
154
+ })
155
+
156
+ it('leaves the value alone when it opens', async () => {
157
+ const wrapper = render('px-2 py-4')
158
+
159
+ await toggle(wrapper).trigger('click')
160
+
161
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
162
+ expect(values(wrapper)).toEqual(['16', '8', '16', '8'])
163
+ })
164
+ })
165
+
166
+ describe('editing', () => {
167
+ it('writes both horizontal sides from the collapsed field', async () => {
168
+ const wrapper = render('p-4')
169
+
170
+ await commit(activeInputs(wrapper)[0], '24')
171
+
172
+ expect(lastEmit(wrapper)).toBe('px-6 py-4')
173
+ })
174
+
175
+ it('writes both vertical sides from the collapsed field', async () => {
176
+ const wrapper = render('p-4')
177
+
178
+ await commit(activeInputs(wrapper)[1], '24')
179
+
180
+ expect(lastEmit(wrapper)).toBe('px-4 py-6')
181
+ })
182
+
183
+ it('writes a single side from the expanded fields', async () => {
184
+ const wrapper = render('p-4')
185
+
186
+ await toggle(wrapper).trigger('click')
187
+ await commit(activeInputs(wrapper)[1], '8')
188
+
189
+ expect(lastEmit(wrapper)).toBe('pt-4 pr-2 pb-4 pl-4')
190
+ })
191
+
192
+ it('collapses back to the short class once all four sides match again', async () => {
193
+ const wrapper = render('pt-4 pr-2 pb-4 pl-4')
194
+
195
+ await commit(activeInputs(wrapper)[1], '16')
196
+
197
+ expect(lastEmit(wrapper)).toBe('p-4')
198
+ })
199
+
200
+ it('keeps the typed text and the value to itself until the field is left', async () => {
201
+ const wrapper = render('p-4')
202
+
203
+ await type(activeInputs(wrapper)[0], '30')
204
+
205
+ expect(values(wrapper)).toEqual(['30', '16'])
206
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
207
+ })
208
+
209
+ it('snaps on commit and shows the value it landed on', async () => {
210
+ const wrapper = render('p-4')
211
+
212
+ await commit(activeInputs(wrapper)[0], '30')
213
+
214
+ expect(lastEmit(wrapper)).toBe('px-7 py-4')
215
+ expect(values(wrapper)).toEqual(['28', '16'])
216
+ })
217
+
218
+ it.each([
219
+ // A tie goes down: 14 px sits between 12 and 16.
220
+ ['14', 'p-3', '12'],
221
+ ['26', 'p-6', '24'],
222
+ // Above the ceiling, and onto a step the scale skips.
223
+ ['500', 'p-96', '384'],
224
+ ['100', 'p-24', '96'],
225
+ // Clearing the field is how a zero is entered.
226
+ ['', 'p-0', ''],
227
+ ])('commits %s as %s', async (typed, expected, shown) => {
228
+ const wrapper = render('p-4')
229
+
230
+ await commit(activeInputs(wrapper)[0], typed)
231
+ await commit(activeInputs(wrapper)[1], typed)
232
+
233
+ expect(lastEmit(wrapper)).toBe(expected)
234
+ expect(values(wrapper)).toEqual([shown, shown])
235
+ })
236
+
237
+ it.each([
238
+ ['-3'],
239
+ // Not Number.parseInt: that reads "1e2" as 1 and "12abc" as 12.
240
+ ['1e2'],
241
+ ['12abc'],
242
+ ])('drops %s and shows the value from before', async (typed) => {
243
+ const wrapper = render('p-4')
244
+
245
+ await commit(activeInputs(wrapper)[0], typed)
246
+
247
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
248
+ expect(values(wrapper)).toEqual(['16', '16'])
249
+ })
250
+
251
+ it('commits on Enter and leaves the value selected for the next one', async () => {
252
+ const wrapper = render('p-4')
253
+ const input = activeInputs(wrapper)[0]
254
+
255
+ await type(input, '24')
256
+ await press(input, 'Enter')
257
+
258
+ expect(lastEmit(wrapper)).toBe('px-6 py-4')
259
+
260
+ const element = input.element as HTMLInputElement
261
+ expect([element.selectionStart, element.selectionEnd]).toEqual([0, element.value.length])
262
+ })
263
+
264
+ it('restores the value on Escape without writing anything', async () => {
265
+ const wrapper = render('p-4')
266
+ const seen = bubbled(wrapper)
267
+
268
+ await type(activeInputs(wrapper)[0], '30')
269
+ await press(activeInputs(wrapper)[0], 'Escape')
270
+
271
+ expect(values(wrapper)).toEqual(['16', '16'])
272
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
273
+ // Swallowed, so the popover the field sits in does not close on the undo.
274
+ expect(seen).toHaveLength(0)
275
+ })
276
+
277
+ it('leaves Escape to the surrounding UI when there is nothing to undo', async () => {
278
+ const wrapper = render('p-4')
279
+ const seen = bubbled(wrapper)
280
+
281
+ await press(activeInputs(wrapper)[0], 'Escape')
282
+
283
+ expect(seen).toHaveLength(1)
284
+ })
285
+
286
+ it('lets a change from outside overwrite text that is still being typed', async () => {
287
+ const wrapper = render('p-4')
288
+
289
+ await type(activeInputs(wrapper)[0], '30')
290
+ await wrapper.setProps({ modelValue: 'p-8' })
291
+
292
+ // Deliberate: the model is the source of truth, and an editor that moves
293
+ // the value under the caret has already taken the field off the user.
294
+ expect(values(wrapper)).toEqual(['32', '32'])
295
+ })
296
+
297
+ it('animates the number that landed on another step than the one typed', async () => {
298
+ const wrapper = render('p-4')
299
+
300
+ await commit(activeInputs(wrapper)[0], '30')
301
+
302
+ expect(activeInputs(wrapper)[0].attributes('data-snap')).toBe('down')
303
+ })
304
+
305
+ it('does not animate a value that only looks different', async () => {
306
+ const wrapper = render('p-4')
307
+
308
+ await commit(activeInputs(wrapper)[0], '016')
309
+
310
+ expect(activeInputs(wrapper)[0].attributes('data-snap')).toBeUndefined()
311
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
312
+ })
313
+
314
+ it('writes nothing when a commit lands back on the same step', async () => {
315
+ const wrapper = render('p-7')
316
+
317
+ await commit(activeInputs(wrapper)[0], '30')
318
+
319
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
320
+ expect(values(wrapper)).toEqual(['28', '28'])
321
+ })
322
+ })
323
+
324
+ describe('arrow keys', () => {
325
+ it('steps up the list straight away', async () => {
326
+ const wrapper = render('p-6')
327
+
328
+ await press(activeInputs(wrapper)[0], 'ArrowUp')
329
+
330
+ expect(lastEmit(wrapper)).toBe('px-7 py-6')
331
+ expect(values(wrapper)).toEqual(['28', '24'])
332
+ })
333
+
334
+ it('steps down the list straight away', async () => {
335
+ const wrapper = render('p-6')
336
+
337
+ await press(activeInputs(wrapper)[0], 'ArrowDown')
338
+
339
+ expect(lastEmit(wrapper)).toBe('px-5 py-6')
340
+ })
341
+
342
+ it('follows the gaps in the scale instead of counting in ones', async () => {
343
+ const wrapper = render('p-16')
344
+
345
+ await press(activeInputs(wrapper)[0], 'ArrowUp')
346
+
347
+ expect(lastEmit(wrapper)).toBe('px-20 py-16')
348
+ expect(values(wrapper)).toEqual(['80', '64'])
349
+ })
350
+
351
+ it('stops at the top of the list instead of wrapping', async () => {
352
+ const wrapper = render('p-96')
353
+
354
+ await press(activeInputs(wrapper)[0], 'ArrowUp')
355
+
356
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
357
+ expect(values(wrapper)).toEqual(['384', '384'])
358
+ })
359
+
360
+ it('stops at the bottom of the list instead of wrapping', async () => {
361
+ const wrapper = render('p-0')
362
+
363
+ await press(activeInputs(wrapper)[0], 'ArrowDown')
364
+
365
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
366
+ expect(values(wrapper)).toEqual(['', ''])
367
+ })
368
+
369
+ it('replaces uncommitted text with the value it stepped to', async () => {
370
+ const wrapper = render('p-6')
371
+
372
+ await type(activeInputs(wrapper)[0], '30')
373
+ await press(activeInputs(wrapper)[0], 'ArrowUp')
374
+
375
+ expect(values(wrapper)).toEqual(['28', '24'])
376
+ })
377
+ })
378
+
379
+ describe('accessible names', () => {
380
+ const names = (wrapper: VueWrapper) =>
381
+ activeInputs(wrapper).map((input) => input.attributes('aria-label'))
382
+
383
+ // The unit is only in the input as an `aria-hidden` suffix, so it has to be
384
+ // in the accessible name for the field to say what it measures.
385
+ it('names the collapsed fields in Dutch, with the unit', () => {
386
+ expect(names(render('p-4'))).toEqual(['Horizontaal (px)', 'Verticaal (px)'])
387
+ })
388
+
389
+ it('names the expanded fields in Dutch, with the unit', () => {
390
+ expect(names(render('pt-4 pr-2 pb-6 pl-2'))).toEqual([
391
+ 'Boven (px)',
392
+ 'Rechts (px)',
393
+ 'Onder (px)',
394
+ 'Links (px)',
395
+ ])
396
+ })
397
+
398
+ it('takes an overridden unit into the names and the suffix', () => {
399
+ const wrapper = render('p-4', { messages: { unit: 'pixels' } })
400
+
401
+ expect(names(wrapper)).toEqual(['Horizontaal (pixels)', 'Verticaal (pixels)'])
402
+ expect(wrapper.get('input ~ span').text()).toBe('pixels')
403
+ })
404
+
405
+ it('names the toggle in Dutch', () => {
406
+ expect(toggle(render('p-4')).attributes('aria-label')).toBe('Padding per zijde')
407
+ })
408
+
409
+ it('keeps the untouched defaults when one message is overridden', () => {
410
+ const wrapper = render('p-4', { messages: { horizontal: 'Horizontal' } })
411
+
412
+ expect(names(wrapper)).toEqual(['Horizontal (px)', 'Verticaal (px)'])
413
+ expect(toggle(wrapper).attributes('aria-label')).toBe('Padding per zijde')
414
+ })
415
+
416
+ it('renders the Dutch default block label', () => {
417
+ expect(render('p-4').find('p').text()).toBe('Padding')
418
+ })
419
+
420
+ it('renders a custom block label', () => {
421
+ expect(render('p-4', { label: 'Binnenmarge' }).find('p').text()).toBe('Binnenmarge')
422
+ })
423
+ })
424
+ })
425
+
426
+ describe("EditorPadding with sides='vertical'", () => {
427
+ const vertical = (modelValue: string) => render(modelValue, { sides: 'vertical' })
428
+
429
+ const names = (wrapper: VueWrapper) =>
430
+ activeInputs(wrapper).map((input) => input.attributes('aria-label'))
431
+
432
+ it('shows one vertical field and the toggle while collapsed', () => {
433
+ const wrapper = vertical('py-4')
434
+
435
+ expect(names(wrapper)).toEqual(['Verticaal (px)'])
436
+ expect(values(wrapper)).toEqual(['16'])
437
+ expect(toggle(wrapper).attributes('aria-label')).toBe('Padding per zijde')
438
+ })
439
+
440
+ it('renders no second row at all', () => {
441
+ const wrapper = vertical('py-4')
442
+
443
+ expect(wrapper.find('[data-testid="editor-padding-secondary-row"]').exists()).toBe(false)
444
+ expect(inputs(wrapper)).toHaveLength(1)
445
+ })
446
+
447
+ it('lets the single field span both field columns', () => {
448
+ const wrapper = vertical('py-4')
449
+
450
+ expect(inputs(wrapper)[0].element.parentElement!.classList).toContain('col-span-2')
451
+ })
452
+
453
+ it('writes py-N from the collapsed field', async () => {
454
+ const wrapper = vertical('py-4')
455
+
456
+ await commit(inputs(wrapper)[0], '32')
457
+
458
+ expect(lastEmit(wrapper)).toBe('py-8')
459
+ })
460
+
461
+ it('shows Boven and Onder in the same row when expanded', async () => {
462
+ const wrapper = vertical('py-4')
463
+
464
+ await toggle(wrapper).trigger('click')
465
+
466
+ expect(names(wrapper)).toEqual(['Boven (px)', 'Onder (px)'])
467
+ expect(values(wrapper)).toEqual(['16', '16'])
468
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
469
+ })
470
+
471
+ it('writes pt-N pb-N from the expanded fields, even when they agree', async () => {
472
+ const wrapper = vertical('py-4')
473
+ await toggle(wrapper).trigger('click')
474
+
475
+ await commit(inputs(wrapper)[1], '24')
476
+ expect(lastEmit(wrapper)).toBe('pt-4 pb-6')
477
+
478
+ await wrapper.setProps({ modelValue: 'pt-4 pb-6' })
479
+ await commit(inputs(wrapper)[1], '16')
480
+ expect(lastEmit(wrapper)).toBe('pt-4 pb-4')
481
+ })
482
+
483
+ it('opens expanded when top and bottom differ', () => {
484
+ const wrapper = vertical('pt-2 pb-6')
485
+
486
+ expect(toggle(wrapper).attributes('aria-pressed')).toBe('true')
487
+ expect(values(wrapper)).toEqual(['8', '24'])
488
+ })
489
+
490
+ it('collapses onto the top value and writes py-N', async () => {
491
+ const wrapper = vertical('pt-2 pb-6')
492
+
493
+ await toggle(wrapper).trigger('click')
494
+
495
+ expect(lastEmit(wrapper)).toBe('py-2')
496
+ expect(values(wrapper)).toEqual(['8'])
497
+ })
498
+
499
+ it('ignores horizontal padding and drops it on the first write', async () => {
500
+ const wrapper = vertical('px-6 py-4')
501
+
502
+ // Not opened by the asymmetry of left/right: those sides are not edited here.
503
+ expect(toggle(wrapper).attributes('aria-pressed')).toBe('false')
504
+ expect(values(wrapper)).toEqual(['16'])
505
+
506
+ await press(inputs(wrapper)[0], 'ArrowUp')
507
+
508
+ expect(lastEmit(wrapper)).toBe('py-5')
509
+ })
510
+
511
+ it('reads p-N as its vertical part', async () => {
512
+ const wrapper = vertical('p-3')
513
+
514
+ expect(values(wrapper)).toEqual(['12'])
515
+
516
+ await commit(inputs(wrapper)[0], '12')
517
+ // Nothing changed on the sides it edits, so nothing is written.
518
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
519
+ })
520
+
521
+ it("leaves the default 'all' layout alone", () => {
522
+ const wrapper = render('px-6 py-4', { sides: 'all' })
523
+
524
+ expect(values(wrapper)).toEqual(['24', '16'])
525
+ expect(inputs(wrapper)).toHaveLength(4)
526
+ })
527
+ })
528
+
529
+ describe("EditorPadding with sides='horizontal'", () => {
530
+ const horizontal = (modelValue: string, props: Record<string, unknown> = {}) =>
531
+ render(modelValue, { sides: 'horizontal', ...props })
532
+
533
+ const names = (wrapper: VueWrapper) =>
534
+ activeInputs(wrapper).map((input) => input.attributes('aria-label'))
535
+
536
+ it('shows one horizontal field and the toggle while collapsed', () => {
537
+ const wrapper = horizontal('px-4')
538
+
539
+ expect(names(wrapper)).toEqual(['Horizontaal (px)'])
540
+ expect(values(wrapper)).toEqual(['16'])
541
+ expect(toggle(wrapper).attributes('aria-label')).toBe('Padding per zijde')
542
+ })
543
+
544
+ it('names the collapsed field with messages.horizontal', () => {
545
+ const wrapper = horizontal('px-4', {
546
+ messages: { horizontal: 'Links en rechts', toggle: 'Links en rechts apart' },
547
+ })
548
+
549
+ expect(names(wrapper)).toEqual(['Links en rechts (px)'])
550
+ expect(toggle(wrapper).attributes('aria-label')).toBe('Links en rechts apart')
551
+ })
552
+
553
+ it('renders no second row, and lets the single field span both columns', () => {
554
+ const wrapper = horizontal('px-4')
555
+
556
+ expect(wrapper.find('[data-testid="editor-padding-secondary-row"]').exists()).toBe(false)
557
+ expect(inputs(wrapper)).toHaveLength(1)
558
+ expect(inputs(wrapper)[0].element.parentElement!.classList).toContain('col-span-2')
559
+ })
560
+
561
+ it('marks the left and right sides on the field icon', () => {
562
+ const wrapper = horizontal('px-4')
563
+
564
+ expect(wrapper.findAllComponents(PaddingIcon)[0].props('sides')).toEqual(['left', 'right'])
565
+ })
566
+
567
+ it('writes px-N from the collapsed field', async () => {
568
+ const wrapper = horizontal('px-4')
569
+
570
+ await commit(inputs(wrapper)[0], '32')
571
+
572
+ expect(lastEmit(wrapper)).toBe('px-8')
573
+ })
574
+
575
+ it('shows Links and Rechts in the same row when expanded', async () => {
576
+ const wrapper = horizontal('px-4')
577
+
578
+ await toggle(wrapper).trigger('click')
579
+
580
+ expect(names(wrapper)).toEqual(['Links (px)', 'Rechts (px)'])
581
+ expect(values(wrapper)).toEqual(['16', '16'])
582
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
583
+ })
584
+
585
+ it('writes pl-N pr-N from the expanded fields, even when they agree', async () => {
586
+ const wrapper = horizontal('px-4')
587
+ await toggle(wrapper).trigger('click')
588
+
589
+ await commit(inputs(wrapper)[1], '24')
590
+ expect(lastEmit(wrapper)).toBe('pl-4 pr-6')
591
+
592
+ await wrapper.setProps({ modelValue: 'pl-4 pr-6' })
593
+ await commit(inputs(wrapper)[1], '16')
594
+ expect(lastEmit(wrapper)).toBe('pl-4 pr-4')
595
+ })
596
+
597
+ it('opens expanded when left and right differ', () => {
598
+ const wrapper = horizontal('pl-2 pr-6')
599
+
600
+ expect(toggle(wrapper).attributes('aria-pressed')).toBe('true')
601
+ expect(values(wrapper)).toEqual(['8', '24'])
602
+ })
603
+
604
+ it('collapses onto the left value and writes px-N', async () => {
605
+ const wrapper = horizontal('pl-2 pr-6')
606
+
607
+ await toggle(wrapper).trigger('click')
608
+
609
+ expect(lastEmit(wrapper)).toBe('px-2')
610
+ expect(values(wrapper)).toEqual(['8'])
611
+ })
612
+
613
+ it('ignores vertical padding and drops it on the first write', async () => {
614
+ const wrapper = horizontal('pt-2 pb-8 px-4')
615
+
616
+ // Not opened by the asymmetry of top/bottom: those sides are not edited here.
617
+ expect(toggle(wrapper).attributes('aria-pressed')).toBe('false')
618
+ expect(values(wrapper)).toEqual(['16'])
619
+
620
+ await press(inputs(wrapper)[0], 'ArrowUp')
621
+
622
+ expect(lastEmit(wrapper)).toBe('px-5')
623
+ })
624
+
625
+ it('reads py-N and p-N without writing anything back', async () => {
626
+ expect(values(horizontal('py-6'))).toEqual([''])
627
+
628
+ const wrapper = horizontal('p-3')
629
+ expect(values(wrapper)).toEqual(['12'])
630
+
631
+ await commit(inputs(wrapper)[0], '12')
632
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
633
+ })
634
+ })
635
+
636
+ describe('PaddingIcon', () => {
637
+ const lines = (sides: PaddingIconSide[]) =>
638
+ mount(PaddingIcon, { props: { sides } }).findAll('line')
639
+
640
+ /** Opacity per line, in the fixed draw order top, right, bottom, left. */
641
+ const opacities = (sides: PaddingIconSide[]) =>
642
+ lines(sides).map((line) => (line.classes().includes('opacity-100') ? 1 : 0))
643
+
644
+ it('always draws four lines and only fades the inactive ones out', () => {
645
+ // All four stay in the DOM, so swapping a field's icon animates instead of
646
+ // replacing one drawing with another.
647
+ expect(lines(['left', 'right'])).toHaveLength(4)
648
+ expect(opacities(['left', 'right'])).toEqual([0, 1, 0, 1])
649
+ expect(opacities(['top'])).toEqual([1, 0, 0, 0])
650
+ expect(opacities(['top', 'right', 'bottom', 'left'])).toEqual([1, 1, 1, 1])
651
+ })
652
+
653
+ it('transitions the opacity, with a reduced-motion fallback', () => {
654
+ const [line] = lines(['top'])
655
+
656
+ expect(line.classes()).toContain('transition-opacity')
657
+ expect(line.classes()).toContain('motion-reduce:transition-none')
658
+ })
659
+
660
+ it('draws shorter lines when all four sides are lit, so they do not touch', () => {
661
+ const [two] = lines(['left', 'right'])
662
+ const [four] = lines(['top', 'right', 'bottom', 'left'])
663
+
664
+ expect(two.attributes('y1')).toBe('4')
665
+ expect(four.attributes('x1')).toBe('6')
666
+ })
667
+
668
+ it('is hidden from the accessibility tree', () => {
669
+ const wrapper = mount(PaddingIcon, { props: { sides: ['top'] } })
670
+
671
+ expect(wrapper.get('svg').attributes('aria-hidden')).toBe('true')
672
+ })
673
+ })
674
+
675
+ // The consumer derives its class matchers from this list, so the list is a
676
+ // contract: any change here changes what the app recognises.
677
+ describe('EDITOR_PADDING_STEPS', () => {
678
+ it('offers exactly the agreed steps', () => {
679
+ expect(EDITOR_PADDING_STEPS).toEqual([
680
+ { token: 0, px: 0 },
681
+ { token: 1, px: 4 },
682
+ { token: 2, px: 8 },
683
+ { token: 3, px: 12 },
684
+ { token: 4, px: 16 },
685
+ { token: 5, px: 20 },
686
+ { token: 6, px: 24 },
687
+ { token: 7, px: 28 },
688
+ { token: 8, px: 32 },
689
+ { token: 9, px: 36 },
690
+ { token: 10, px: 40 },
691
+ { token: 11, px: 44 },
692
+ { token: 12, px: 48 },
693
+ { token: 14, px: 56 },
694
+ { token: 16, px: 64 },
695
+ { token: 20, px: 80 },
696
+ { token: 24, px: 96 },
697
+ { token: 28, px: 112 },
698
+ { token: 32, px: 128 },
699
+ { token: 36, px: 144 },
700
+ { token: 40, px: 160 },
701
+ { token: 44, px: 176 },
702
+ { token: 48, px: 192 },
703
+ { token: 52, px: 208 },
704
+ { token: 56, px: 224 },
705
+ { token: 60, px: 240 },
706
+ { token: 64, px: 256 },
707
+ { token: 72, px: 288 },
708
+ { token: 80, px: 320 },
709
+ { token: 96, px: 384 },
710
+ ])
711
+ })
712
+ })