@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.
Files changed (228) hide show
  1. package/dist/components/editor/molecules/EditorBorder/EditorBorder.vue.d.ts +12 -0
  2. package/dist/components/editor/molecules/EditorColor/_ThemeRoleSwatches.vue.d.ts +16 -0
  3. package/dist/index.js +1094 -1135
  4. package/dist/nuxt.js +0 -1
  5. package/package.json +38 -36
  6. package/src/attribute-contract.test.ts +151 -0
  7. package/src/components/atoms/AnimatedNumber/AnimatedNumber.stories.ts +36 -0
  8. package/src/components/atoms/AnimatedNumber/AnimatedNumber.test.ts +21 -0
  9. package/src/components/atoms/Avatar/Avatar.stories.ts +122 -0
  10. package/src/components/atoms/Avatar/Avatar.test.ts +134 -0
  11. package/src/components/atoms/Button/Button.stories.ts +290 -0
  12. package/src/components/atoms/Button/Button.test.ts +406 -0
  13. package/src/components/atoms/Card/Card.stories.ts +45 -0
  14. package/src/components/atoms/Card/Card.test.ts +46 -0
  15. package/src/components/atoms/Checkbox/Checkbox.stories.ts +35 -0
  16. package/src/components/atoms/Checkbox/Checkbox.test.ts +55 -0
  17. package/src/components/atoms/Chip/Chip.stories.ts +274 -0
  18. package/src/components/atoms/Chip/Chip.test.ts +230 -0
  19. package/src/components/atoms/EnergyLabel/EnergyLabel.stories.ts +189 -0
  20. package/src/components/atoms/EnergyLabel/EnergyLabel.test.ts +100 -0
  21. package/src/components/atoms/FloatingPanel/FloatingPanel.stories.ts +677 -0
  22. package/src/components/atoms/FloatingPanel/FloatingPanel.test.ts +864 -0
  23. package/src/components/atoms/Icon/Icon.stories.ts +63 -0
  24. package/src/components/atoms/Icon/Icon.test.ts +89 -0
  25. package/src/components/atoms/Loader/Loader.stories.ts +36 -0
  26. package/src/components/atoms/Loader/Loader.test.ts +40 -0
  27. package/src/components/atoms/PickerItem/PickerItem.stories.ts +172 -0
  28. package/src/components/atoms/PickerItem/PickerItem.test.ts +142 -0
  29. package/src/components/atoms/PickerPreview/PickerPreview.stories.ts +183 -0
  30. package/src/components/atoms/PickerPreview/PickerPreview.test.ts +137 -0
  31. package/src/components/atoms/PresenceIndicator/PresenceIndicator.stories.ts +21 -0
  32. package/src/components/atoms/PropertyListing/PropertyListing.stories.ts +38 -0
  33. package/src/components/atoms/PropertyListing/PropertyListing.test.ts +46 -0
  34. package/src/components/atoms/SelectableOption/SelectableOption.stories.ts +131 -0
  35. package/src/components/atoms/SelectableOption/SelectableOption.test.ts +96 -0
  36. package/src/components/atoms/Skeleton/Skeleton.stories.ts +27 -0
  37. package/src/components/atoms/Skeleton/Skeleton.test.ts +28 -0
  38. package/src/components/atoms/Slider/Slider.stories.ts +151 -0
  39. package/src/components/atoms/Slider/Slider.test.ts +191 -0
  40. package/src/components/atoms/Squircle/Squircle.stories.ts +114 -0
  41. package/src/components/atoms/Squircle/Squircle.test.ts +70 -0
  42. package/src/components/atoms/Switch/Switch.stories.ts +91 -0
  43. package/src/components/atoms/Switch/Switch.test.ts +183 -0
  44. package/src/components/atoms/Tooltip/Tooltip.stories.ts +158 -0
  45. package/src/components/atoms/Tooltip/Tooltip.test.ts +134 -0
  46. package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.stories.ts +124 -0
  47. package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.test.ts +464 -0
  48. package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.stories.ts +266 -0
  49. package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.test.ts +421 -0
  50. package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.stories.ts +365 -0
  51. package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.test.ts +484 -0
  52. package/src/components/editor/composables/useRadioGrid.test.ts +261 -0
  53. package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.stories.ts +73 -0
  54. package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.test.ts +189 -0
  55. package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.stories.ts +309 -0
  56. package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.test.ts +359 -0
  57. package/src/components/editor/molecules/EditorAnchor/EditorAnchor.stories.ts +138 -0
  58. package/src/components/editor/molecules/EditorAnchor/EditorAnchor.test.ts +344 -0
  59. package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.stories.ts +87 -0
  60. package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.test.ts +262 -0
  61. package/src/components/editor/molecules/EditorBlur/EditorBlur.stories.ts +115 -0
  62. package/src/components/editor/molecules/EditorBlur/EditorBlur.test.ts +243 -0
  63. package/src/components/editor/molecules/EditorBorder/EditorBorder.stories.ts +187 -0
  64. package/src/components/editor/molecules/EditorBorder/EditorBorder.test.ts +456 -0
  65. package/src/components/editor/molecules/EditorBorder/EditorBorder.vue +58 -2
  66. package/src/components/editor/molecules/EditorColor/EditorColor.stories.ts +563 -0
  67. package/src/components/editor/molecules/EditorColor/EditorColor.test.ts +689 -0
  68. package/src/components/editor/molecules/EditorColor/EditorColorGradient.test.ts +793 -0
  69. package/src/components/editor/molecules/EditorColor/_ColorField.vue +7 -29
  70. package/src/components/editor/molecules/EditorColor/_ThemeRoleSwatches.vue +57 -0
  71. package/src/components/editor/molecules/EditorColor/gradientClass.test.ts +303 -0
  72. package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.stories.ts +72 -0
  73. package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.test.ts +124 -0
  74. package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.stories.ts +46 -0
  75. package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.test.ts +97 -0
  76. package/src/components/editor/molecules/EditorFontSize/EditorFontSize.stories.ts +248 -0
  77. package/src/components/editor/molecules/EditorFontSize/EditorFontSize.test.ts +288 -0
  78. package/src/components/editor/molecules/EditorGap/EditorGap.stories.ts +129 -0
  79. package/src/components/editor/molecules/EditorGap/EditorGap.test.ts +253 -0
  80. package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.stories.ts +68 -0
  81. package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.test.ts +150 -0
  82. package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.stories.ts +71 -0
  83. package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.test.ts +161 -0
  84. package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.stories.ts +204 -0
  85. package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.test.ts +238 -0
  86. package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.stories.ts +257 -0
  87. package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.test.ts +286 -0
  88. package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.stories.ts +135 -0
  89. package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.test.ts +230 -0
  90. package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.stories.ts +154 -0
  91. package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.test.ts +112 -0
  92. package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.stories.ts +206 -0
  93. package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.test.ts +332 -0
  94. package/src/components/editor/molecules/EditorPadding/EditorPadding.stories.ts +374 -0
  95. package/src/components/editor/molecules/EditorPadding/EditorPadding.test.ts +712 -0
  96. package/src/components/editor/molecules/EditorRadius/EditorRadius.stories.ts +64 -0
  97. package/src/components/editor/molecules/EditorRadius/EditorRadius.test.ts +137 -0
  98. package/src/components/editor/molecules/EditorShadow/EditorShadow.stories.ts +103 -0
  99. package/src/components/editor/molecules/EditorShadow/EditorShadow.test.ts +168 -0
  100. package/src/components/editor/molecules/EditorSize/EditorSize.stories.ts +84 -0
  101. package/src/components/editor/molecules/EditorSize/EditorSize.test.ts +253 -0
  102. package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.stories.ts +49 -0
  103. package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.test.ts +43 -0
  104. package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.stories.ts +69 -0
  105. package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.test.ts +155 -0
  106. package/src/components/molecules/Announcement/Announcement.stories.ts +224 -0
  107. package/src/components/molecules/Announcement/Announcement.test.ts +161 -0
  108. package/src/components/molecules/ContactInfoCard/ContactInfoCard.stories.ts +99 -0
  109. package/src/components/molecules/ContactInfoCard/ContactInfoCard.test.ts +70 -0
  110. package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.stories.ts +148 -0
  111. package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.test.ts +271 -0
  112. package/src/components/molecules/DoughnutChart/DoughnutChart.stories.ts +45 -0
  113. package/src/components/molecules/DoughnutChart/DoughnutChart.test.ts +205 -0
  114. package/src/components/molecules/InfoBlock/InfoBlock.stories.ts +84 -0
  115. package/src/components/molecules/InfoBlock/InfoBlock.test.ts +101 -0
  116. package/src/components/molecules/InputField/InputField.stories.ts +117 -0
  117. package/src/components/molecules/InputField/InputField.test.ts +138 -0
  118. package/src/components/molecules/InputSelect/InputSelect.stories.ts +116 -0
  119. package/src/components/molecules/InputSelect/InputSelect.test.ts +127 -0
  120. package/src/components/molecules/ModalFooter/ModalFooter.stories.ts +204 -0
  121. package/src/components/molecules/ModalFooter/ModalFooter.test.ts +276 -0
  122. package/src/components/molecules/PageCard/PageCard.stories.ts +100 -0
  123. package/src/components/molecules/PageCard/PageCard.test.ts +91 -0
  124. package/src/components/molecules/PageContainer/PageContainer.stories.ts +90 -0
  125. package/src/components/molecules/PageContainer/PageContainer.test.ts +50 -0
  126. package/src/components/molecules/PageMockup/PageMockup.stories.ts +38 -0
  127. package/src/components/molecules/PageMockup/PageMockup.test.ts +20 -0
  128. package/src/components/molecules/PageTypeTile/PageTypeTile.stories.ts +63 -0
  129. package/src/components/molecules/PageTypeTile/PageTypeTile.test.ts +41 -0
  130. package/src/components/molecules/Pagination/Pagination.stories.ts +72 -0
  131. package/src/components/molecules/Pagination/Pagination.test.ts +130 -0
  132. package/src/components/molecules/PickerGroup/PickerGroup.stories.ts +90 -0
  133. package/src/components/molecules/PickerGroup/PickerGroup.test.ts +145 -0
  134. package/src/components/molecules/PickerList/PickerList.stories.ts +436 -0
  135. package/src/components/molecules/PickerList/PickerList.test.ts +870 -0
  136. package/src/components/molecules/PlaceholderInput/PlaceholderInput.stories.ts +48 -0
  137. package/src/components/molecules/PlaceholderInput/PlaceholderInput.test.ts +107 -0
  138. package/src/components/molecules/PropertyCard/PropertyCard.stories.ts +46 -0
  139. package/src/components/molecules/PropertyCard/PropertyCard.test.ts +59 -0
  140. package/src/components/molecules/Select/Select.stories.ts +264 -0
  141. package/src/components/molecules/Select/Select.test.ts +315 -0
  142. package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.stories.ts +84 -0
  143. package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.test.ts +103 -0
  144. package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.stories.ts +11 -0
  145. package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.test.ts +38 -0
  146. package/src/components/molecules/SocialIcons/SocialIcons.stories.ts +28 -0
  147. package/src/components/molecules/SocialIcons/SocialIcons.test.ts +58 -0
  148. package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.stories.ts +11 -0
  149. package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.test.ts +116 -0
  150. package/src/components/molecules/SocialMediaType/SocialMediaType.stories.ts +37 -0
  151. package/src/components/molecules/SocialMediaType/SocialMediaType.test.ts +126 -0
  152. package/src/components/molecules/Source/Source.stories.ts +35 -0
  153. package/src/components/molecules/Source/Source.test.ts +45 -0
  154. package/src/components/molecules/TabbedContent/TabbedContent.stories.ts +96 -0
  155. package/src/components/molecules/TabbedContent/TabbedContent.test.ts +109 -0
  156. package/src/components/molecules/Tabs/Tabs.stories.ts +230 -0
  157. package/src/components/molecules/Tabs/Tabs.test.ts +280 -0
  158. package/src/components/molecules/TemplateCard/TemplateCard.stories.ts +327 -0
  159. package/src/components/molecules/TemplateCard/TemplateCard.test.ts +339 -0
  160. package/src/components/molecules/Textarea/Textarea.stories.ts +216 -0
  161. package/src/components/molecules/Textarea/Textarea.test.ts +191 -0
  162. package/src/components/molecules/TimelineEvent/TimelineEvent.stories.ts +319 -0
  163. package/src/components/molecules/TimelineEvent/TimelineEvent.test.ts +224 -0
  164. package/src/components/molecules/TimelineLanes/TimelineLanes.stories.ts +29 -0
  165. package/src/components/molecules/TimelineLanes/TimelineLanes.test.ts +30 -0
  166. package/src/components/molecules/Toast/Toast.stories.ts +110 -0
  167. package/src/components/molecules/Toast/Toast.test.ts +103 -0
  168. package/src/components/molecules/ToggleGroup/ToggleGroup.stories.ts +297 -0
  169. package/src/components/molecules/ToggleGroup/ToggleGroup.test.ts +578 -0
  170. package/src/components/molecules/Tree/Tree.stories.ts +884 -0
  171. package/src/components/molecules/Tree/Tree.test.ts +1247 -0
  172. package/src/components/molecules/Tree/tree-drop.test.ts +223 -0
  173. package/src/components/organisms/Calendar/Calendar.stories.ts +108 -0
  174. package/src/components/organisms/Calendar/Calendar.test.ts +279 -0
  175. package/src/components/organisms/Calendar/_CalendarDayView.test.ts +104 -0
  176. package/src/components/organisms/Calendar/_CalendarHeader.test.ts +86 -0
  177. package/src/components/organisms/Calendar/_CalendarMonthView.test.ts +52 -0
  178. package/src/components/organisms/Calendar/_CalendarTimePicker.test.ts +89 -0
  179. package/src/components/organisms/Calendar/_CalendarYearView.test.ts +56 -0
  180. package/src/components/organisms/ColorPicker/ColorPicker.stories.ts +229 -0
  181. package/src/components/organisms/ColorPicker/ColorPicker.test.ts +638 -0
  182. package/src/components/organisms/ColorPicker/gradient.test.ts +67 -0
  183. package/src/components/organisms/ColorPicker/gradientGeometry.test.ts +206 -0
  184. package/src/components/organisms/Combobox/Combobox.stories.ts +234 -0
  185. package/src/components/organisms/Combobox/Combobox.test.ts +512 -0
  186. package/src/components/organisms/Combobox/Combobox.vue +1 -1
  187. package/src/components/organisms/ComboboxPanel/ComboboxPanel.stories.ts +158 -0
  188. package/src/components/organisms/ComboboxPanel/ComboboxPanel.test.ts +524 -0
  189. package/src/components/organisms/ComboboxPanel/ComboboxPanel.vue +1 -1
  190. package/src/components/organisms/CountrySelect/CountrySelect.stories.ts +139 -0
  191. package/src/components/organisms/CountrySelect/CountrySelect.test.ts +149 -0
  192. package/src/components/organisms/DatePicker/DatePicker.stories.ts +163 -0
  193. package/src/components/organisms/DatePicker/DatePicker.test.ts +214 -0
  194. package/src/components/organisms/DropdownButton/DropdownButton.stories.ts +123 -0
  195. package/src/components/organisms/DropdownButton/DropdownButton.test.ts +379 -0
  196. package/src/components/organisms/FontPicker/FontPicker.stories.ts +476 -0
  197. package/src/components/organisms/FontPicker/FontPicker.test.ts +873 -0
  198. package/src/components/organisms/Header/Header.stories.ts +75 -0
  199. package/src/components/organisms/Header/Header.test.ts +233 -0
  200. package/src/components/organisms/IconPicker/IconPicker.stories.ts +734 -0
  201. package/src/components/organisms/IconPicker/IconPicker.test.ts +669 -0
  202. package/src/components/organisms/IconPicker/_IconPickerGrid.test.ts +184 -0
  203. package/src/components/organisms/IconPicker/_IconPickerRail.test.ts +102 -0
  204. package/src/components/organisms/IconPicker/useIconPickerNavigation.test.ts +215 -0
  205. package/src/components/organisms/Modal/Modal.stories.ts +350 -0
  206. package/src/components/organisms/Modal/Modal.test.ts +275 -0
  207. package/src/components/organisms/PickerPanel/PickerPanel.stories.ts +452 -0
  208. package/src/components/organisms/PickerPanel/PickerPanel.test.ts +358 -0
  209. package/src/components/organisms/ProgressBar/ProgressBar.stories.ts +100 -0
  210. package/src/components/organisms/ProgressBar/ProgressBar.test.ts +417 -0
  211. package/src/components/organisms/ReferenceCard/ReferenceCard.stories.ts +191 -0
  212. package/src/components/organisms/ReferenceCard/ReferenceCard.test.ts +165 -0
  213. package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.stories.ts +65 -0
  214. package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.test.ts +466 -0
  215. package/src/components/organisms/StepperHeader/StepperHeader.stories.ts +91 -0
  216. package/src/components/organisms/StepperHeader/StepperHeader.test.ts +279 -0
  217. package/src/components/organisms/Table/Table.stories.ts +332 -0
  218. package/src/components/organisms/Table/Table.test.ts +320 -0
  219. package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.stories.ts +198 -0
  220. package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.test.ts +283 -0
  221. package/src/components/organisms/TimelineTrack/TimelineTrack.stories.ts +120 -0
  222. package/src/components/organisms/TimelineTrack/TimelineTrack.test.ts +91 -0
  223. package/src/components/organisms/Toaster/Toaster.stories.ts +51 -0
  224. package/src/components/organisms/Toaster/Toaster.test.ts +75 -0
  225. package/src/components/organisms/VideoControls/VideoControls.stories.ts +792 -0
  226. package/src/components/organisms/VideoControls/VideoControls.test.ts +1056 -0
  227. package/src/components/organisms/VideoPlayer/VideoPlayer.stories.ts +327 -0
  228. package/src/components/organisms/VideoPlayer/VideoPlayer.test.ts +439 -0
@@ -0,0 +1,224 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
+ import { ref, watch } from 'vue'
3
+ import { NexxtAnnouncement, NexxtButton, NexxtIcon } from '../../../index'
4
+
5
+ export default {
6
+ title: 'Components/Molecules/Announcement',
7
+ component: NexxtAnnouncement,
8
+ decorators: [() => ({ template: '<div class="bg-gray-100"><story /></div>' })],
9
+ } satisfies Meta<typeof NexxtAnnouncement>
10
+
11
+ type Story = StoryObj<typeof NexxtAnnouncement>
12
+
13
+ const makeRender = (slotText: string) => (args: Story['args']) => ({
14
+ components: { NexxtAnnouncement },
15
+ setup() {
16
+ const visible = ref(true)
17
+ watch(visible, (val) => {
18
+ if (!val)
19
+ setTimeout(() => {
20
+ visible.value = true
21
+ }, 1500)
22
+ })
23
+ return { visible, args }
24
+ },
25
+ template: `
26
+ <NexxtAnnouncement v-if="visible" v-bind="args" @hide="visible = false">
27
+ ${slotText}
28
+ </NexxtAnnouncement>
29
+ `,
30
+ })
31
+
32
+ export const Default: Story = {
33
+ args: {
34
+ color: 'var(--color-cornflower-blue-500)',
35
+ icon: 'megaphone',
36
+ },
37
+ render: makeRender('New features are available! Check the release notes.'),
38
+ }
39
+
40
+ export const Success: Story = {
41
+ args: {
42
+ color: 'var(--color-green-400)',
43
+ icon: 'circle-check',
44
+ },
45
+ render: makeRender('Your changes have been saved successfully.'),
46
+ }
47
+
48
+ export const Warning: Story = {
49
+ args: {
50
+ color: 'var(--color-orange-500)',
51
+ icon: 'triangle-exclamation',
52
+ },
53
+ render: makeRender('Your trial expires in 3 days. Upgrade to keep access.'),
54
+ }
55
+
56
+ export const Danger: Story = {
57
+ args: {
58
+ color: 'var(--color-brick-500)',
59
+ icon: 'circle-xmark',
60
+ },
61
+ render: makeRender('Scheduled maintenance on Sunday 02:00–04:00 UTC. Expect downtime.'),
62
+ }
63
+
64
+ export const RichContent: Story = {
65
+ render: () => ({
66
+ components: { NexxtAnnouncement, NexxtButton, NexxtIcon },
67
+ template: `
68
+ <NexxtAnnouncement color="white" text_color="var(--color-gray-900)" icon="triangle-exclamation" :show-hide-button="false" class="border border-gray-200">
69
+ <div class="flex items-center justify-between">
70
+ <div>
71
+ <p class="text-sm font-medium">Access request from <strong>John Doe</strong></p>
72
+ <p class="text-xs text-gray-500">Do you want to give John Doe access to your account?</p>
73
+ </div>
74
+ <div class="flex gap-2">
75
+ <NexxtButton size="xs" variant="success">Allow</NexxtButton>
76
+ <NexxtButton size="xs" variant="secondary">Deny</NexxtButton>
77
+ </div>
78
+ </div>
79
+ </NexxtAnnouncement>
80
+ `,
81
+ }),
82
+ }
83
+
84
+ export const CustomIcon: Story = {
85
+ args: {
86
+ color: 'var(--color-purple-600)',
87
+ icon: 'gift',
88
+ },
89
+ render: makeRender('You have a special offer waiting for you!'),
90
+ }
91
+
92
+ /*
93
+ * Announcement has two colouring paths and they are not interchangeable:
94
+ *
95
+ * - `variant` is the semantic one. Five values — info, success, warning, danger,
96
+ * neutral — resolved to design tokens inside the component. Use it whenever the
97
+ * colour is a decision the design system gets to make.
98
+ * - `color` / `text_color` take RAW CSS COLOUR VALUES (`#f59e0b`, `rgb(...)`,
99
+ * `var(--color-green-400)`), not Tailwind classes. They exist because the API
100
+ * hands us colours at runtime that no class can express.
101
+ *
102
+ * They are applied differently — variant as classes, color/text_color as inline
103
+ * style — so an explicitly passed colour WINS over a variant. That is deliberate:
104
+ * an API-supplied banner keeps rendering its own colours even if a variant is set
105
+ * alongside it. The stories below are ordered to make that precedence visible.
106
+ */
107
+
108
+ /** All five semantic variants. This is the set to review; no raw colours involved. */
109
+ export const SemanticVariants: Story = {
110
+ render: () => ({
111
+ components: { NexxtAnnouncement },
112
+ setup: () => ({
113
+ rows: [
114
+ {
115
+ variant: 'info',
116
+ icon: 'megaphone',
117
+ text: 'New features are available! Check the release notes.',
118
+ },
119
+ {
120
+ variant: 'success',
121
+ icon: 'circle-check',
122
+ text: 'Your changes have been saved successfully.',
123
+ },
124
+ {
125
+ variant: 'warning',
126
+ icon: 'triangle-exclamation',
127
+ text: 'Your trial expires in 3 days. Upgrade to keep access.',
128
+ },
129
+ {
130
+ variant: 'danger',
131
+ icon: 'circle-xmark',
132
+ text: 'Scheduled maintenance on Sunday 02:00–04:00 UTC.',
133
+ },
134
+ { variant: 'neutral', icon: 'circle-info', text: 'This account is in read-only mode.' },
135
+ ],
136
+ }),
137
+ template: `
138
+ <div style="display: flex; flex-direction: column; gap: 12px; padding: 16px;">
139
+ <div v-for="row in rows" :key="row.variant">
140
+ <p style="margin: 0 0 4px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: #374151;">
141
+ variant="{{ row.variant }}"
142
+ </p>
143
+ <NexxtAnnouncement :variant="row.variant" :icon="row.icon" :show-hide-button="false">
144
+ {{ row.text }}
145
+ </NexxtAnnouncement>
146
+ </div>
147
+ </div>
148
+ `,
149
+ }),
150
+ }
151
+
152
+ /** A single semantic variant, driven from the args table. */
153
+ export const VariantInfo: Story = {
154
+ args: { variant: 'info', icon: 'megaphone' },
155
+ render: makeRender('New features are available! Check the release notes.'),
156
+ }
157
+
158
+ export const VariantNeutral: Story = {
159
+ args: { variant: 'neutral', icon: 'circle-info' },
160
+ render: makeRender('This account is in read-only mode.'),
161
+ }
162
+
163
+ /**
164
+ * The raw-colour path, kept so the distinction stays documented. `color` and
165
+ * `text_color` are CSS colour values, not Tailwind classes — here a literal hex of
166
+ * the kind the API returns for a customer's own branding. There is no `variant`
167
+ * that expresses this, which is the whole reason the props exist.
168
+ */
169
+ export const RawColoursFromApi: Story = {
170
+ args: {
171
+ color: '#7c2d12',
172
+ text_color: '#fed7aa',
173
+ icon: 'megaphone',
174
+ },
175
+ render: makeRender('Brand colours supplied by the API at runtime, not by a variant.'),
176
+ }
177
+
178
+ /**
179
+ * Both paths on the same component. `variant="success"` would paint this green;
180
+ * the explicitly passed raw `color` wins, because variant resolves to a class and
181
+ * `color` is applied as inline style. An API-supplied colour therefore always
182
+ * survives a variant sitting next to it.
183
+ */
184
+ export const RawColourOverridesVariant: Story = {
185
+ render: () => ({
186
+ components: { NexxtAnnouncement },
187
+ template: `
188
+ <div style="display: flex; flex-direction: column; gap: 12px; padding: 16px;">
189
+ <div>
190
+ <p style="margin: 0 0 4px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: #374151;">
191
+ variant="success" — token colours
192
+ </p>
193
+ <NexxtAnnouncement variant="success" icon="circle-check" :show-hide-button="false">
194
+ Painted by the design system.
195
+ </NexxtAnnouncement>
196
+ </div>
197
+ <div>
198
+ <p style="margin: 0 0 4px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: #374151;">
199
+ variant="success" + color="#7c2d12" — the raw colour wins
200
+ </p>
201
+ <NexxtAnnouncement
202
+ variant="success"
203
+ color="#7c2d12"
204
+ text_color="#fed7aa"
205
+ icon="circle-check"
206
+ :show-hide-button="false"
207
+ >
208
+ Painted by the API-supplied colour, despite the variant.
209
+ </NexxtAnnouncement>
210
+ </div>
211
+ </div>
212
+ `,
213
+ }),
214
+ }
215
+
216
+ /**
217
+ * Neither `variant` nor `color` passed. The legacy inline defaults still apply
218
+ * (`background: blue`, `color: white`) rather than a semantic variant, so existing
219
+ * consumers see no change from this addition.
220
+ */
221
+ export const NoVariantNoColour: Story = {
222
+ args: { icon: 'megaphone' },
223
+ render: makeRender('Legacy defaults: raw "blue" background, white text.'),
224
+ }
@@ -0,0 +1,161 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import Announcement from './Announcement.vue'
4
+
5
+ describe('Announcement', () => {
6
+ it('renders slot content', () => {
7
+ const wrapper = mount(Announcement, {
8
+ slots: { default: 'Hello world' },
9
+ })
10
+
11
+ expect(wrapper.text()).toContain('Hello world')
12
+ })
13
+
14
+ it('applies color and text_color as inline styles', () => {
15
+ const wrapper = mount(Announcement, {
16
+ props: { color: 'red', text_color: 'black' },
17
+ })
18
+
19
+ const style = wrapper.element.getAttribute('style')
20
+ expect(style).toContain('background: red')
21
+ expect(style).toContain('color: black')
22
+ })
23
+
24
+ it('uses blue and white as defaults', () => {
25
+ const wrapper = mount(Announcement)
26
+
27
+ const style = wrapper.element.getAttribute('style')
28
+ expect(style).toContain('background: blue')
29
+ expect(style).toContain('color: white')
30
+ })
31
+
32
+ it('emits hide when the close button is clicked', async () => {
33
+ const wrapper = mount(Announcement)
34
+
35
+ await wrapper.find('button').trigger('click')
36
+
37
+ expect(wrapper.emitted('hide')).toHaveLength(1)
38
+ })
39
+
40
+ it('shows the close button by default', () => {
41
+ const wrapper = mount(Announcement)
42
+
43
+ expect(wrapper.find('button').exists()).toBe(true)
44
+ })
45
+
46
+ it('hides the close button when showHideButton is false', () => {
47
+ const wrapper = mount(Announcement, {
48
+ props: { showHideButton: false },
49
+ })
50
+
51
+ expect(wrapper.find('button').exists()).toBe(false)
52
+ })
53
+
54
+ it('uses a two-column grid when showHideButton is false', () => {
55
+ const wrapper = mount(Announcement, {
56
+ props: { showHideButton: false },
57
+ })
58
+
59
+ expect(wrapper.classes()).toContain('grid-cols-[auto_1fr]')
60
+ })
61
+
62
+ it('uses a three-column grid when showHideButton is true', () => {
63
+ const wrapper = mount(Announcement)
64
+
65
+ expect(wrapper.classes()).toContain('grid-cols-[auto_1fr_auto]')
66
+ })
67
+
68
+ it('renders the default megaphone icon', () => {
69
+ const wrapper = mount(Announcement)
70
+
71
+ expect(wrapper.find('i.fa-megaphone').exists()).toBe(true)
72
+ })
73
+
74
+ it('renders a custom icon when the icon prop is set', () => {
75
+ const wrapper = mount(Announcement, {
76
+ props: { icon: 'circle-check' },
77
+ })
78
+
79
+ expect(wrapper.find('i.fa-circle-check').exists()).toBe(true)
80
+ })
81
+ })
82
+
83
+ describe('Announcement variant', () => {
84
+ const root = (props: Record<string, unknown>) => mount(Announcement, { props })
85
+ const inlineStyle = (w: ReturnType<typeof mount>) => w.element.getAttribute('style') ?? ''
86
+
87
+ it('exposes the semantic variant on the root element', () => {
88
+ expect(root({ variant: 'success' }).attributes('data-variant')).toBe('success')
89
+ })
90
+
91
+ it('carries no variant marker when the prop is omitted', () => {
92
+ expect(root({}).attributes('data-variant')).toBeUndefined()
93
+ })
94
+
95
+ it.each(['info', 'success', 'warning', 'danger', 'neutral'] as const)(
96
+ 'variant "%s" styles the announcement without touching the inline style',
97
+ (variant) => {
98
+ const wrapper = root({ variant })
99
+
100
+ expect(wrapper.attributes('data-variant')).toBe(variant)
101
+ expect(inlineStyle(wrapper)).not.toContain('background')
102
+ expect(inlineStyle(wrapper)).not.toMatch(/(^|;)\s*color:/)
103
+ },
104
+ )
105
+
106
+ it('renders visibly different classes per variant', () => {
107
+ const seen = (['info', 'success', 'warning', 'danger', 'neutral'] as const).map((variant) =>
108
+ root({ variant }).classes().sort().join(' '),
109
+ )
110
+ const plain = root({}).classes().sort().join(' ')
111
+
112
+ expect(new Set(seen).size).toBe(seen.length)
113
+ expect(seen).not.toContain(plain)
114
+ })
115
+
116
+ it('lets an explicitly supplied color win over the variant', () => {
117
+ const wrapper = root({ variant: 'success', color: 'red' })
118
+
119
+ expect(inlineStyle(wrapper)).toContain('background: red')
120
+ expect(wrapper.attributes('data-variant')).toBe('success')
121
+ })
122
+
123
+ it('lets an explicitly supplied text_color win over the variant', () => {
124
+ const wrapper = root({ variant: 'danger', text_color: 'black' })
125
+
126
+ expect(inlineStyle(wrapper)).toContain('color: black')
127
+ expect(inlineStyle(wrapper)).not.toContain('background')
128
+ })
129
+
130
+ // jsdom serialises hex to rgb(), so the expectation is the CSSOM form of the
131
+ // value, not the string typed in — the point is that the value reaches CSS at all.
132
+ it.each([
133
+ { value: '#ff0000', serialised: 'background: rgb(255, 0, 0)' },
134
+ { value: 'rgb(1, 2, 3)', serialised: 'background: rgb(1, 2, 3)' },
135
+ { value: 'var(--brand)', serialised: 'background: var(--brand)' },
136
+ { value: 'linear-gradient(90deg, red, blue)', serialised: 'linear-gradient' },
137
+ ])('passes the raw CSS value $value through as the background', ({ value, serialised }) => {
138
+ const wrapper = root({ color: value })
139
+
140
+ expect(inlineStyle(wrapper)).toContain(serialised)
141
+ })
142
+
143
+ it('keeps the legacy inline defaults when no variant is given', () => {
144
+ const style = inlineStyle(root({}))
145
+
146
+ expect(style).toContain('background: blue')
147
+ expect(style).toContain('color: white')
148
+ })
149
+
150
+ it('keeps rendering slot content, the icon and the hide button under a variant', async () => {
151
+ const wrapper = mount(Announcement, {
152
+ props: { variant: 'warning' },
153
+ slots: { default: 'Heads up' },
154
+ })
155
+
156
+ expect(wrapper.text()).toContain('Heads up')
157
+ expect(wrapper.find('i.fa-megaphone').exists()).toBe(true)
158
+ await wrapper.find('button').trigger('click')
159
+ expect(wrapper.emitted('hide')).toHaveLength(1)
160
+ })
161
+ })
@@ -0,0 +1,99 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
+ import ContactInfoCard from './ContactInfoCard.vue'
3
+
4
+ export default {
5
+ title: 'Components/Molecules/Contact Info Card',
6
+ component: ContactInfoCard,
7
+ decorators: [
8
+ // The card now paints its own white background, which is invisible on
9
+ // Storybook's default white canvas — the border alone would be doing all the
10
+ // work and a regression to a transparent card would look identical. Every
11
+ // story therefore sits on the tinted page background the app actually uses.
12
+ () => ({
13
+ template:
14
+ '<div style="background: #f3f4f6; padding: 32px; border-radius: 8px;"><story /></div>',
15
+ }),
16
+ ],
17
+ } satisfies Meta<typeof ContactInfoCard>
18
+
19
+ type Story = StoryObj<typeof ContactInfoCard>
20
+
21
+ export const Default: Story = {
22
+ render: () => ({
23
+ components: { ContactInfoCard },
24
+ template: `
25
+ <ContactInfoCard>
26
+ <template #title><span class="heading-3">Contact information</span></template>
27
+ <template #content>
28
+ <p>john.doe@example.com</p>
29
+ <p>+31 6 12345678</p>
30
+ </template>
31
+ </ContactInfoCard>
32
+ `,
33
+ }),
34
+ }
35
+
36
+ /**
37
+ * The card on a tinted page against the same card on white, side by side. This is
38
+ * the story that shows what `bg-white` buys: on the left the card is a distinct
39
+ * surface lifted off the page, on the right it is defined only by its border.
40
+ */
41
+ export const OnTintedAndWhite: Story = {
42
+ render: () => ({
43
+ components: { ContactInfoCard },
44
+ template: `
45
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start;">
46
+ <div>
47
+ <p style="margin: 0 0 8px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: #374151;">On a tinted page</p>
48
+ <ContactInfoCard>
49
+ <template #title><span class="heading-3">Contact information</span></template>
50
+ <template #content>
51
+ <p>john.doe@example.com</p>
52
+ <p>+31 6 12345678</p>
53
+ </template>
54
+ </ContactInfoCard>
55
+ </div>
56
+ <div style="background: #ffffff; padding: 16px; border-radius: 8px;">
57
+ <p style="margin: 0 0 8px; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: #374151;">On a white page</p>
58
+ <ContactInfoCard>
59
+ <template #title><span class="heading-3">Contact information</span></template>
60
+ <template #content>
61
+ <p>john.doe@example.com</p>
62
+ <p>+31 6 12345678</p>
63
+ </template>
64
+ </ContactInfoCard>
65
+ </div>
66
+ </div>
67
+ `,
68
+ }),
69
+ }
70
+
71
+ /**
72
+ * The card over page content. The white background has to be opaque — anything
73
+ * showing through would read as a rendering fault rather than a design choice.
74
+ */
75
+ export const OverPageContent: Story = {
76
+ render: () => ({
77
+ components: { ContactInfoCard },
78
+ template: `
79
+ <div style="position: relative;">
80
+ <p style="position: absolute; inset: 0; margin: 0; font-size: 13px; line-height: 22px; color: #6b7280;">
81
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
82
+ incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
83
+ nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
84
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
85
+ fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.
86
+ </p>
87
+ <div style="position: relative; padding-top: 24px; max-width: 420px;">
88
+ <ContactInfoCard>
89
+ <template #title><span class="heading-3">Contact information</span></template>
90
+ <template #content>
91
+ <p>john.doe@example.com</p>
92
+ <p>+31 6 12345678</p>
93
+ </template>
94
+ </ContactInfoCard>
95
+ </div>
96
+ </div>
97
+ `,
98
+ }),
99
+ }
@@ -0,0 +1,70 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import ContactInfoCard from './ContactInfoCard.vue'
4
+
5
+ describe('ContactInfoCard', () => {
6
+ it('renders the default title fallback when no title slot is provided', () => {
7
+ const wrapper = mount(ContactInfoCard)
8
+ expect(wrapper.text()).toContain('Title')
9
+ })
10
+
11
+ it('renders custom title slot content', () => {
12
+ const wrapper = mount(ContactInfoCard, {
13
+ slots: {
14
+ title: 'Contact information',
15
+ },
16
+ })
17
+ expect(wrapper.text()).toContain('Contact information')
18
+ })
19
+
20
+ it('renders content slot content', () => {
21
+ const wrapper = mount(ContactInfoCard, {
22
+ slots: {
23
+ content: '<p>john.doe@example.com</p><p>+31 6 12345678</p>',
24
+ },
25
+ })
26
+ expect(wrapper.text()).toContain('john.doe@example.com')
27
+ expect(wrapper.text()).toContain('+31 6 12345678')
28
+ })
29
+
30
+ it('renders both slots together', () => {
31
+ const wrapper = mount(ContactInfoCard, {
32
+ slots: {
33
+ title: 'Contact information',
34
+ content: 'john.doe@example.com',
35
+ },
36
+ })
37
+ expect(wrapper.text()).toContain('Contact information')
38
+ expect(wrapper.text()).toContain('john.doe@example.com')
39
+ })
40
+
41
+ // Assembled at runtime: a complete utility class written as a literal under
42
+ // src/components/ is picked up by Tailwind's @source scan and generated, which
43
+ // makes a missing class look present. See CONTRIBUTING.md.
44
+ const tw = (...parts: string[]) => parts.join('-')
45
+
46
+ it('renders an opaque white surface on the root element by default', () => {
47
+ const wrapper = mount(ContactInfoCard)
48
+
49
+ expect(wrapper.classes()).toContain(tw('bg', 'white'))
50
+ })
51
+
52
+ it('keeps the white surface on the root, not on an inner section', () => {
53
+ const wrapper = mount(ContactInfoCard, {
54
+ slots: { title: 'Contact information', content: 'john.doe@example.com' },
55
+ })
56
+
57
+ const bg = tw('bg', 'white')
58
+ expect(wrapper.classes()).toContain(bg)
59
+ const inner = wrapper.element.querySelectorAll('div')
60
+ expect([...inner].some((el) => el.classList.contains(bg))).toBe(false)
61
+ })
62
+
63
+ it('still renders the rounded bordered card shell alongside the surface', () => {
64
+ const wrapper = mount(ContactInfoCard)
65
+
66
+ expect(wrapper.classes()).toContain('border')
67
+ expect(wrapper.classes()).toContain(tw('border', 'gray', '200'))
68
+ expect(wrapper.classes()).toContain(tw('rounded', 'xl'))
69
+ })
70
+ })
@@ -0,0 +1,148 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
+ import CustomerJourneyTile from './CustomerJourneyTile.vue'
3
+
4
+ export default {
5
+ title: 'Components/Molecules/Customer Journey Tile',
6
+ component: CustomerJourneyTile,
7
+ argTypes: {
8
+ variant: {
9
+ control: 'select',
10
+ options: ['information', 'display'],
11
+ },
12
+ },
13
+ } satisfies Meta<typeof CustomerJourneyTile>
14
+
15
+ type Story = StoryObj<typeof CustomerJourneyTile>
16
+
17
+ export const Default: Story = {
18
+ args: {
19
+ title: 'Latent',
20
+ amount: 270,
21
+ percentage: 14,
22
+ percentageExplanaition: 'vorige maand',
23
+ },
24
+ }
25
+
26
+ export const NegativePercentage: Story = {
27
+ args: {
28
+ title: 'Latent',
29
+ amount: 270,
30
+ percentage: -8,
31
+ percentageExplanaition: 'vorige maand',
32
+ },
33
+ }
34
+
35
+ export const ZeroPercentage: Story = {
36
+ args: {
37
+ title: 'Latent',
38
+ amount: 270,
39
+ percentage: 0,
40
+ percentageExplanaition: 'vorige maand',
41
+ },
42
+ }
43
+
44
+ export const WithoutPercentage: Story = {
45
+ args: {
46
+ title: 'Wonen',
47
+ amount: 54,
48
+ },
49
+ }
50
+
51
+ export const WithoutAmount: Story = {
52
+ args: {
53
+ title: 'Actief zoeken',
54
+ percentage: 5,
55
+ percentageExplanaition: 'vorige maand',
56
+ },
57
+ }
58
+
59
+ export const Orientatie: Story = {
60
+ args: {
61
+ title: 'Oriëntatie',
62
+ amount: 142,
63
+ percentage: 3,
64
+ percentageExplanaition: 'vorige maand',
65
+ },
66
+ }
67
+
68
+ export const Selectie: Story = {
69
+ args: {
70
+ title: 'Selectie',
71
+ amount: 89,
72
+ percentage: -2,
73
+ percentageExplanaition: 'vorige maand',
74
+ },
75
+ }
76
+
77
+ export const Transactie: Story = {
78
+ args: {
79
+ title: 'Transactie',
80
+ amount: 31,
81
+ percentage: 21,
82
+ percentageExplanaition: 'vorige maand',
83
+ },
84
+ }
85
+
86
+ export const WithoutPhase: Story = {
87
+ args: {
88
+ amount: 270,
89
+ percentage: 14,
90
+ percentageExplanaition: 'vorige maand',
91
+ },
92
+ }
93
+
94
+ export const AsButton: Story = {
95
+ args: {
96
+ as: 'button',
97
+ title: 'Latent',
98
+ amount: 270,
99
+ percentage: 14,
100
+ percentageExplanaition: 'vorige maand',
101
+ },
102
+ }
103
+
104
+ export const Animated: Story = {
105
+ args: {
106
+ title: 'Latent',
107
+ amount: 270,
108
+ animated: true,
109
+ percentage: 14,
110
+ percentageExplanaition: 'vorige maand',
111
+ },
112
+ }
113
+
114
+ export const AnimatedWithDelay: Story = {
115
+ args: {
116
+ title: 'Latent',
117
+ amount: 270,
118
+ animated: true,
119
+ delay: 500,
120
+ percentage: 14,
121
+ percentageExplanaition: 'vorige maand',
122
+ },
123
+ }
124
+
125
+ export const Display: Story = {
126
+ args: {
127
+ title: 'Latent',
128
+ variant: 'display',
129
+ },
130
+ }
131
+
132
+ export const DisplaySelected: Story = {
133
+ args: {
134
+ title: 'Latent',
135
+ variant: 'display',
136
+ selected: true,
137
+ },
138
+ }
139
+
140
+ export const InvertedPercentageColor: Story = {
141
+ args: {
142
+ title: 'Latent',
143
+ amount: 270,
144
+ percentage: 14,
145
+ percentageExplanaition: 'vorige maand',
146
+ invertPercentageColor: true,
147
+ },
148
+ }