@nexxtmove/ui 2.6.0 → 2.8.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 (229) hide show
  1. package/dist/components/editor/molecules/EditorColor/EditorColor.vue.d.ts +7 -5
  2. package/dist/components/editor/molecules/EditorColor/_ColorField.vue.d.ts +4 -1
  3. package/dist/components/organisms/ColorPicker/ColorPicker.vue.d.ts +3 -1
  4. package/dist/index.js +95 -172
  5. package/dist/nuxt.js +0 -1
  6. package/package.json +38 -36
  7. package/src/attribute-contract.test.ts +151 -0
  8. package/src/components/atoms/AnimatedNumber/AnimatedNumber.stories.ts +36 -0
  9. package/src/components/atoms/AnimatedNumber/AnimatedNumber.test.ts +21 -0
  10. package/src/components/atoms/Avatar/Avatar.stories.ts +122 -0
  11. package/src/components/atoms/Avatar/Avatar.test.ts +134 -0
  12. package/src/components/atoms/Button/Button.stories.ts +290 -0
  13. package/src/components/atoms/Button/Button.test.ts +406 -0
  14. package/src/components/atoms/Card/Card.stories.ts +45 -0
  15. package/src/components/atoms/Card/Card.test.ts +46 -0
  16. package/src/components/atoms/Checkbox/Checkbox.stories.ts +35 -0
  17. package/src/components/atoms/Checkbox/Checkbox.test.ts +55 -0
  18. package/src/components/atoms/Chip/Chip.stories.ts +274 -0
  19. package/src/components/atoms/Chip/Chip.test.ts +230 -0
  20. package/src/components/atoms/EnergyLabel/EnergyLabel.stories.ts +189 -0
  21. package/src/components/atoms/EnergyLabel/EnergyLabel.test.ts +100 -0
  22. package/src/components/atoms/FloatingPanel/FloatingPanel.stories.ts +677 -0
  23. package/src/components/atoms/FloatingPanel/FloatingPanel.test.ts +864 -0
  24. package/src/components/atoms/Icon/Icon.stories.ts +63 -0
  25. package/src/components/atoms/Icon/Icon.test.ts +89 -0
  26. package/src/components/atoms/Loader/Loader.stories.ts +36 -0
  27. package/src/components/atoms/Loader/Loader.test.ts +40 -0
  28. package/src/components/atoms/PickerItem/PickerItem.stories.ts +172 -0
  29. package/src/components/atoms/PickerItem/PickerItem.test.ts +142 -0
  30. package/src/components/atoms/PickerPreview/PickerPreview.stories.ts +183 -0
  31. package/src/components/atoms/PickerPreview/PickerPreview.test.ts +137 -0
  32. package/src/components/atoms/PresenceIndicator/PresenceIndicator.stories.ts +21 -0
  33. package/src/components/atoms/PropertyListing/PropertyListing.stories.ts +38 -0
  34. package/src/components/atoms/PropertyListing/PropertyListing.test.ts +46 -0
  35. package/src/components/atoms/SelectableOption/SelectableOption.stories.ts +131 -0
  36. package/src/components/atoms/SelectableOption/SelectableOption.test.ts +96 -0
  37. package/src/components/atoms/Skeleton/Skeleton.stories.ts +27 -0
  38. package/src/components/atoms/Skeleton/Skeleton.test.ts +28 -0
  39. package/src/components/atoms/Slider/Slider.stories.ts +151 -0
  40. package/src/components/atoms/Slider/Slider.test.ts +191 -0
  41. package/src/components/atoms/Squircle/Squircle.stories.ts +114 -0
  42. package/src/components/atoms/Squircle/Squircle.test.ts +70 -0
  43. package/src/components/atoms/Switch/Switch.stories.ts +91 -0
  44. package/src/components/atoms/Switch/Switch.test.ts +183 -0
  45. package/src/components/atoms/Tooltip/Tooltip.stories.ts +158 -0
  46. package/src/components/atoms/Tooltip/Tooltip.test.ts +134 -0
  47. package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.stories.ts +124 -0
  48. package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.test.ts +464 -0
  49. package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.stories.ts +266 -0
  50. package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.test.ts +421 -0
  51. package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.stories.ts +365 -0
  52. package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.test.ts +484 -0
  53. package/src/components/editor/composables/useRadioGrid.test.ts +261 -0
  54. package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.stories.ts +73 -0
  55. package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.test.ts +189 -0
  56. package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.stories.ts +309 -0
  57. package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.test.ts +359 -0
  58. package/src/components/editor/molecules/EditorAnchor/EditorAnchor.stories.ts +138 -0
  59. package/src/components/editor/molecules/EditorAnchor/EditorAnchor.test.ts +344 -0
  60. package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.stories.ts +87 -0
  61. package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.test.ts +262 -0
  62. package/src/components/editor/molecules/EditorBlur/EditorBlur.stories.ts +115 -0
  63. package/src/components/editor/molecules/EditorBlur/EditorBlur.test.ts +243 -0
  64. package/src/components/editor/molecules/EditorBorder/EditorBorder.stories.ts +187 -0
  65. package/src/components/editor/molecules/EditorBorder/EditorBorder.test.ts +456 -0
  66. package/src/components/editor/molecules/EditorColor/EditorColor.stories.ts +633 -0
  67. package/src/components/editor/molecules/EditorColor/EditorColor.test.ts +776 -0
  68. package/src/components/editor/molecules/EditorColor/EditorColor.vue +7 -5
  69. package/src/components/editor/molecules/EditorColor/EditorColorGradient.test.ts +832 -0
  70. package/src/components/editor/molecules/EditorColor/_ColorField.vue +20 -15
  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 +696 -0
  182. package/src/components/organisms/ColorPicker/ColorPicker.vue +12 -3
  183. package/src/components/organisms/ColorPicker/gradient.test.ts +67 -0
  184. package/src/components/organisms/ColorPicker/gradientGeometry.test.ts +206 -0
  185. package/src/components/organisms/Combobox/Combobox.stories.ts +234 -0
  186. package/src/components/organisms/Combobox/Combobox.test.ts +512 -0
  187. package/src/components/organisms/Combobox/Combobox.vue +1 -1
  188. package/src/components/organisms/ComboboxPanel/ComboboxPanel.stories.ts +158 -0
  189. package/src/components/organisms/ComboboxPanel/ComboboxPanel.test.ts +524 -0
  190. package/src/components/organisms/ComboboxPanel/ComboboxPanel.vue +1 -1
  191. package/src/components/organisms/CountrySelect/CountrySelect.stories.ts +139 -0
  192. package/src/components/organisms/CountrySelect/CountrySelect.test.ts +149 -0
  193. package/src/components/organisms/DatePicker/DatePicker.stories.ts +163 -0
  194. package/src/components/organisms/DatePicker/DatePicker.test.ts +214 -0
  195. package/src/components/organisms/DropdownButton/DropdownButton.stories.ts +123 -0
  196. package/src/components/organisms/DropdownButton/DropdownButton.test.ts +379 -0
  197. package/src/components/organisms/FontPicker/FontPicker.stories.ts +476 -0
  198. package/src/components/organisms/FontPicker/FontPicker.test.ts +873 -0
  199. package/src/components/organisms/Header/Header.stories.ts +75 -0
  200. package/src/components/organisms/Header/Header.test.ts +233 -0
  201. package/src/components/organisms/IconPicker/IconPicker.stories.ts +734 -0
  202. package/src/components/organisms/IconPicker/IconPicker.test.ts +669 -0
  203. package/src/components/organisms/IconPicker/_IconPickerGrid.test.ts +184 -0
  204. package/src/components/organisms/IconPicker/_IconPickerRail.test.ts +102 -0
  205. package/src/components/organisms/IconPicker/useIconPickerNavigation.test.ts +215 -0
  206. package/src/components/organisms/Modal/Modal.stories.ts +350 -0
  207. package/src/components/organisms/Modal/Modal.test.ts +275 -0
  208. package/src/components/organisms/PickerPanel/PickerPanel.stories.ts +452 -0
  209. package/src/components/organisms/PickerPanel/PickerPanel.test.ts +358 -0
  210. package/src/components/organisms/ProgressBar/ProgressBar.stories.ts +100 -0
  211. package/src/components/organisms/ProgressBar/ProgressBar.test.ts +417 -0
  212. package/src/components/organisms/ReferenceCard/ReferenceCard.stories.ts +191 -0
  213. package/src/components/organisms/ReferenceCard/ReferenceCard.test.ts +165 -0
  214. package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.stories.ts +65 -0
  215. package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.test.ts +466 -0
  216. package/src/components/organisms/StepperHeader/StepperHeader.stories.ts +91 -0
  217. package/src/components/organisms/StepperHeader/StepperHeader.test.ts +279 -0
  218. package/src/components/organisms/Table/Table.stories.ts +332 -0
  219. package/src/components/organisms/Table/Table.test.ts +320 -0
  220. package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.stories.ts +198 -0
  221. package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.test.ts +283 -0
  222. package/src/components/organisms/TimelineTrack/TimelineTrack.stories.ts +120 -0
  223. package/src/components/organisms/TimelineTrack/TimelineTrack.test.ts +91 -0
  224. package/src/components/organisms/Toaster/Toaster.stories.ts +51 -0
  225. package/src/components/organisms/Toaster/Toaster.test.ts +75 -0
  226. package/src/components/organisms/VideoControls/VideoControls.stories.ts +792 -0
  227. package/src/components/organisms/VideoControls/VideoControls.test.ts +1056 -0
  228. package/src/components/organisms/VideoPlayer/VideoPlayer.stories.ts +327 -0
  229. package/src/components/organisms/VideoPlayer/VideoPlayer.test.ts +439 -0
@@ -0,0 +1,116 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, it, expect, vi } from 'vitest'
3
+ import SocialMediaCustomTemplate from './SocialMediaCustomTemplate.vue'
4
+
5
+ describe('SocialMediaCustomTemplate', () => {
6
+ it('renders correctly with video and poster', () => {
7
+ const wrapper = mount(SocialMediaCustomTemplate)
8
+ const video = wrapper.find('video')
9
+
10
+ expect(wrapper.element.tagName).toBe('BUTTON')
11
+ expect(video.exists()).toBe(true)
12
+ // Check video properties directly
13
+ const videoElement = video.element as HTMLVideoElement
14
+ expect(videoElement.muted).toBe(true)
15
+ expect(video.attributes('poster')).toContain('Template_aanvragen.jpg')
16
+ })
17
+
18
+ it('handles video ended event correctly', async () => {
19
+ const wrapper = mount(SocialMediaCustomTemplate)
20
+ const videoElement = wrapper.find('video').element as HTMLVideoElement
21
+ const loadSpy = vi.spyOn(videoElement, 'load').mockImplementation(() => {})
22
+
23
+ // Simulate video ending
24
+ await wrapper.find('video').trigger('ended')
25
+
26
+ expect(videoElement.currentTime).toBe(0)
27
+ expect(loadSpy).toHaveBeenCalled()
28
+ })
29
+
30
+ it('plays video on mouseenter after first play', async () => {
31
+ const wrapper = mount(SocialMediaCustomTemplate)
32
+ const videoElement = wrapper.find('video').element as HTMLVideoElement
33
+ const playSpy = vi.spyOn(videoElement, 'play').mockImplementation(() => Promise.resolve())
34
+
35
+ // Set state to hasPlayedOnce
36
+ await wrapper.find('video').trigger('ended')
37
+
38
+ // Trigger mouseenter
39
+ await wrapper.trigger('mouseenter')
40
+
41
+ expect(playSpy).toHaveBeenCalled()
42
+ })
43
+
44
+ it('pauses video on mouseleave after first play', async () => {
45
+ const wrapper = mount(SocialMediaCustomTemplate)
46
+ const videoElement = wrapper.find('video').element as HTMLVideoElement
47
+ const pauseSpy = vi.spyOn(videoElement, 'pause')
48
+
49
+ // Set state to hasPlayedOnce
50
+ await wrapper.find('video').trigger('ended')
51
+
52
+ // Trigger mouseleave
53
+ await wrapper.trigger('mouseleave')
54
+
55
+ expect(pauseSpy).toHaveBeenCalled()
56
+ })
57
+
58
+ it('sets loop to true after first play', async () => {
59
+ const wrapper = mount(SocialMediaCustomTemplate)
60
+ const video = wrapper.find('video')
61
+ const videoElement = video.element as HTMLVideoElement
62
+
63
+ expect(videoElement.loop).toBe(false)
64
+
65
+ // Simulate video ending
66
+ await video.trigger('ended')
67
+
68
+ expect(videoElement.loop).toBe(true)
69
+ })
70
+
71
+ it('does not play on mouseenter before first play has ended', async () => {
72
+ const wrapper = mount(SocialMediaCustomTemplate)
73
+ const videoElement = wrapper.find('video').element as HTMLVideoElement
74
+ const playSpy = vi.spyOn(videoElement, 'play')
75
+
76
+ await wrapper.trigger('mouseenter')
77
+
78
+ expect(playSpy).not.toHaveBeenCalled()
79
+ })
80
+
81
+ it('does not pause on mouseleave before first play has ended', async () => {
82
+ const wrapper = mount(SocialMediaCustomTemplate)
83
+ const videoElement = wrapper.find('video').element as HTMLVideoElement
84
+ const pauseSpy = vi.spyOn(videoElement, 'pause')
85
+
86
+ await wrapper.trigger('mouseleave')
87
+
88
+ expect(pauseSpy).not.toHaveBeenCalled()
89
+ })
90
+
91
+ it('internal methods do nothing if hasPlayedOnce is false', async () => {
92
+ const wrapper = mount(SocialMediaCustomTemplate)
93
+ const videoElement = wrapper.find('video').element as HTMLVideoElement
94
+ const playSpy = vi.spyOn(videoElement, 'play').mockImplementation(() => Promise.resolve())
95
+ const pauseSpy = vi.spyOn(videoElement, 'pause')
96
+
97
+ // @ts-expect-error - calling internal method
98
+ wrapper.vm.handleMouseEnter()
99
+ // @ts-expect-error - calling internal method
100
+ wrapper.vm.handleMouseLeave()
101
+
102
+ expect(playSpy).not.toHaveBeenCalled()
103
+ expect(pauseSpy).not.toHaveBeenCalled()
104
+ })
105
+
106
+ it('plays onMounted if videoRef is valid', async () => {
107
+ const wrapper = mount(SocialMediaCustomTemplate)
108
+ const videoElement = wrapper.find('video').element as HTMLVideoElement
109
+ const playSpy = vi.spyOn(videoElement, 'play').mockImplementation(() => Promise.resolve())
110
+
111
+ // Simulate onMounted behavior by calling it again (it already ran once on mount)
112
+ // @ts-expect-error - manually calling onMounted setup code
113
+ wrapper.vm.handleMouseEnter() // hasPlayedOnce is false, should not play
114
+ expect(playSpy).not.toHaveBeenCalled()
115
+ })
116
+ })
@@ -0,0 +1,37 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
+ import SocialMediaType from './SocialMediaType.vue'
3
+
4
+ export default {
5
+ title: 'Components/Molecules/Social Media Type',
6
+ component: SocialMediaType,
7
+ } satisfies Meta<typeof SocialMediaType>
8
+
9
+ type Story = StoryObj<typeof SocialMediaType>
10
+
11
+ export const Post: Story = {
12
+ args: {
13
+ variant: 'post',
14
+ icons: ['facebook', 'instagram', 'linkedin', 'x', 'google'],
15
+ },
16
+ }
17
+
18
+ export const Reel: Story = {
19
+ args: {
20
+ variant: 'reel',
21
+ chip: 'Nieuw!',
22
+ },
23
+ }
24
+
25
+ export const Story: Story = {
26
+ args: {
27
+ variant: 'story',
28
+ icons: ['instagram'],
29
+ },
30
+ }
31
+
32
+ export const ReelSelected: Story = {
33
+ args: {
34
+ variant: 'reel',
35
+ selected: true,
36
+ },
37
+ }
@@ -0,0 +1,126 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, it, expect } from 'vitest'
3
+ import SocialMediaType from './SocialMediaType.vue'
4
+ import SocialIcons, { type NexxtSocialIconsProps } from '../SocialIcons/SocialIcons.vue'
5
+
6
+ type Variant = 'post' | 'reel' | 'story'
7
+
8
+ describe('SocialMediaType', () => {
9
+ it.each([
10
+ ['post', 'Post', { facebook: true, instagram: true, linkedin: true, x: true, google: true }],
11
+ ['reel', 'TikTok & Reel', { instagram: true, tiktok: true }],
12
+ ['story', 'Story', { instagram: true }],
13
+ ])('renders correctly for variant: %s', (variant, expectedLabel, expectedIcons) => {
14
+ const wrapper = mount(SocialMediaType, {
15
+ props: {
16
+ variant: variant as Variant,
17
+ },
18
+ })
19
+
20
+ // Check label
21
+ expect(wrapper.text()).toContain(expectedLabel)
22
+
23
+ // Check SocialIcons props
24
+ const socialIcons = wrapper.findComponent(SocialIcons)
25
+ expect(socialIcons.exists()).toBe(true)
26
+
27
+ Object.entries(expectedIcons).forEach(([prop, value]) => {
28
+ expect(socialIcons.props(prop as keyof NexxtSocialIconsProps)).toBe(value)
29
+ })
30
+ })
31
+
32
+ it('renders the chip when provided', () => {
33
+ const wrapper = mount(SocialMediaType, {
34
+ props: {
35
+ variant: 'post',
36
+ chip: 'Featured',
37
+ },
38
+ })
39
+ expect(wrapper.text()).toContain('Featured')
40
+ })
41
+
42
+ it('emits click event when clicked', async () => {
43
+ const wrapper = mount(SocialMediaType, {
44
+ props: {
45
+ variant: 'reel',
46
+ },
47
+ })
48
+ await wrapper.trigger('click')
49
+ expect(wrapper.emitted('click')).toBeTruthy()
50
+ })
51
+
52
+ it('normalizes twitter to x in custom icons', () => {
53
+ const wrapper = mount(SocialMediaType, {
54
+ props: {
55
+ variant: 'post',
56
+ icons: ['twitter', 'facebook'],
57
+ },
58
+ })
59
+
60
+ const socialIcons = wrapper.findComponent(SocialIcons)
61
+ expect(socialIcons.props('x')).toBe(true)
62
+ expect(socialIcons.props('facebook')).toBe(true)
63
+ })
64
+
65
+ it('handles custom icons array correctly', () => {
66
+ const wrapper = mount(SocialMediaType, {
67
+ props: {
68
+ variant: 'reel',
69
+ icons: ['instagram', 'tiktok', 'linkedin'],
70
+ },
71
+ })
72
+
73
+ const socialIcons = wrapper.findComponent(SocialIcons)
74
+ expect(socialIcons.props('instagram')).toBe(true)
75
+ expect(socialIcons.props('tiktok')).toBe(true)
76
+ expect(socialIcons.props('linkedin')).toBe(true)
77
+ expect(socialIcons.props('facebook')).toBe(false)
78
+ })
79
+
80
+ it('renders the selected state correctly', () => {
81
+ const wrapper = mount(SocialMediaType, {
82
+ props: {
83
+ variant: 'story',
84
+ selected: true,
85
+ },
86
+ })
87
+ expect(wrapper.classes()).toContain('bg-gray-50')
88
+ const icons = wrapper.findAllComponents({ name: 'NexxtIcon' })
89
+ const checkIcon = icons.find((icon) => icon.props('name') === 'check')
90
+ expect(checkIcon).toBeDefined()
91
+ })
92
+
93
+ it('renders a custom label', () => {
94
+ const wrapper = mount(SocialMediaType, {
95
+ props: {
96
+ variant: 'post',
97
+ label: 'My Custom Post',
98
+ },
99
+ })
100
+ expect(wrapper.text()).toContain('My Custom Post')
101
+ })
102
+
103
+ it('handles default branch for variant (if TS bypassed)', () => {
104
+ const wrapper = mount(SocialMediaType, {
105
+ props: {
106
+ // @ts-expect-error - testing invalid variant bypass
107
+ variant: 'invalid',
108
+ },
109
+ })
110
+ const socialIcons = wrapper.findComponent(SocialIcons)
111
+ // Should hit default: return {}
112
+ expect(socialIcons.exists()).toBe(true)
113
+ })
114
+
115
+ it('handles empty icons array', () => {
116
+ const wrapper = mount(SocialMediaType, {
117
+ props: {
118
+ variant: 'post',
119
+ icons: [],
120
+ },
121
+ })
122
+ const socialIcons = wrapper.findComponent(SocialIcons)
123
+ // Should have no icons
124
+ expect(socialIcons.exists()).toBe(true)
125
+ })
126
+ })
@@ -0,0 +1,35 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
+ import Source, { SOURCE_MAP } from './Source.vue'
3
+
4
+ export default {
5
+ title: 'Components/Molecules/Source',
6
+ component: Source,
7
+ argTypes: {
8
+ source: {
9
+ control: 'select',
10
+ options: Object.keys(SOURCE_MAP),
11
+ },
12
+ },
13
+ } satisfies Meta<typeof Source>
14
+
15
+ type Story = StoryObj<typeof Source>
16
+
17
+ export const Default: Story = {
18
+ args: {
19
+ source: 'website',
20
+ },
21
+ }
22
+
23
+ export const All: Story = {
24
+ render: () => ({
25
+ components: { Source },
26
+ setup: () => ({ sources: Object.keys(SOURCE_MAP) }),
27
+ template: `
28
+ <div class="flex flex-col gap-2">
29
+ <div v-for="source in sources" :key="source" class="flex items-center gap-3">
30
+ <Source :source="source" />
31
+ </div>
32
+ </div>
33
+ `,
34
+ }),
35
+ }
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import Source, { SOURCE_MAP } from './Source.vue'
4
+
5
+ describe('Source', () => {
6
+ it('renders an image for sources with an image', () => {
7
+ const wrapper = mount(Source, { props: { source: 'realworks' } })
8
+ expect(wrapper.find('img').exists()).toBe(true)
9
+ })
10
+
11
+ it('sets the correct alt text on the image', () => {
12
+ const wrapper = mount(Source, { props: { source: 'realworks' } })
13
+ expect(wrapper.find('img').attributes('alt')).toBe(SOURCE_MAP.realworks)
14
+ })
15
+
16
+ it('renders an icon for sources without an image', () => {
17
+ const wrapper = mount(Source, { props: { source: 'website' } })
18
+ expect(wrapper.find('img').exists()).toBe(false)
19
+ expect(wrapper.find('span[class*="rounded-full"]').exists()).toBe(true)
20
+ })
21
+
22
+ it('renders the label text for sources without an image', () => {
23
+ const wrapper = mount(Source, { props: { source: 'website' } })
24
+ expect(wrapper.text()).toContain(SOURCE_MAP.website)
25
+ })
26
+
27
+ it('applies the correct bg color class for icon sources', () => {
28
+ const wrapper = mount(Source, { props: { source: 'website' } })
29
+ expect(wrapper.find('span[class*="rounded-full"]').classes()).toContain(
30
+ 'bg-cornflower-blue-500',
31
+ )
32
+ })
33
+
34
+ it('applies the correct height class on the image', () => {
35
+ const wrapper = mount(Source, { props: { source: 'realworks' } })
36
+ expect(wrapper.find('img').classes()).toContain('h-4')
37
+ })
38
+
39
+ it('renders the label for all sources without errors', () => {
40
+ for (const source of Object.keys(SOURCE_MAP) as (keyof typeof SOURCE_MAP)[]) {
41
+ const wrapper = mount(Source, { props: { source } })
42
+ expect(wrapper.exists()).toBe(true)
43
+ }
44
+ })
45
+ })
@@ -0,0 +1,96 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
+ import { expect, within } from 'storybook/test'
3
+ import TabbedContend from './TabbedContent.vue'
4
+
5
+ const PANELS = `
6
+ <template #overview>
7
+ <div style="background-color: #dbeafe; padding: 32px; border-radius: 12px; min-height: 200px;">
8
+ <h2 style="font-size: 20px; font-weight: 600; margin-bottom: 8px;">Overview</h2>
9
+ <p>This is the Overview content. Here you can find a summary of everything that matters.</p>
10
+ </div>
11
+ </template>
12
+ <template #details>
13
+ <div style="background-color: #dcfce7; padding: 32px; border-radius: 12px; min-height: 200px;">
14
+ <h2 style="font-size: 20px; font-weight: 600; margin-bottom: 8px;">Details</h2>
15
+ <p>This is the Details content. All the specifics and fine print live here.</p>
16
+ </div>
17
+ </template>
18
+ <template #settings>
19
+ <div style="background-color: #fef9c3; padding: 32px; border-radius: 12px; min-height: 200px;">
20
+ <h2 style="font-size: 20px; font-weight: 600; margin-bottom: 8px;">Settings</h2>
21
+ <p>This is the Settings content. Configure your preferences here.</p>
22
+ </div>
23
+ </template>
24
+ `
25
+
26
+ export default {
27
+ title: 'Components/Molecules/Tabbed Content',
28
+ component: TabbedContend,
29
+ decorators: [
30
+ () => ({
31
+ template:
32
+ '<div style="min-height: 350px; padding: 16px; background-color: #F5F5F5;"><story /></div>',
33
+ }),
34
+ ],
35
+ } satisfies Meta<typeof TabbedContend>
36
+
37
+ type Story = StoryObj<typeof TabbedContend>
38
+
39
+ export const Default: Story = {
40
+ render: (args) => ({
41
+ components: { TabbedContend },
42
+ setup() {
43
+ return { args }
44
+ },
45
+ template: `<TabbedContend v-bind="args">${PANELS}</TabbedContend>`,
46
+ }),
47
+ args: {
48
+ tabs: ['Overview', 'Details', 'Settings'],
49
+ },
50
+ }
51
+
52
+ export const Animated: Story = {
53
+ render: (args) => ({
54
+ components: { TabbedContend },
55
+ setup() {
56
+ return { args }
57
+ },
58
+ template: `<TabbedContend v-bind="args">${PANELS}</TabbedContend>`,
59
+ }),
60
+ args: {
61
+ tabs: ['Overview', 'Details', 'Settings'],
62
+ animated: true,
63
+ },
64
+ }
65
+
66
+ /**
67
+ * `tabs` accepteert naast losse strings ook `{ label, icon }`. De slotnaam komt
68
+ * dan van het label, net als bij de stringvorm.
69
+ */
70
+ export const WithIcons: Story = {
71
+ args: {
72
+ tabs: [
73
+ { label: 'Overview', icon: 'house' },
74
+ { label: 'Details', icon: 'list' },
75
+ { label: 'Settings', icon: 'gear' },
76
+ ],
77
+ },
78
+ render: (args) => ({
79
+ components: { TabbedContend },
80
+ setup: () => ({ args }),
81
+ template: `
82
+ <TabbedContend v-bind="args" style="width: 520px">
83
+ <template #overview>Overzicht</template>
84
+ <template #details>Details</template>
85
+ <template #settings>Instellingen</template>
86
+ </TabbedContend>
87
+ `,
88
+ }),
89
+ play: async ({ canvasElement }) => {
90
+ const canvas = within(canvasElement)
91
+ // Het paneel toont het slot dat bij het label hoort; met het ruwe object als
92
+ // modelwaarde bleef het leeg.
93
+ await expect(canvas.getByRole('tabpanel')).toHaveTextContent('Overzicht')
94
+ await expect(canvasElement.querySelectorAll('i.fa-house').length).toBeGreaterThan(0)
95
+ },
96
+ }
@@ -0,0 +1,109 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, it, expect, beforeAll } from 'vitest'
3
+ import TabbedContent from './TabbedContent.vue'
4
+
5
+ beforeAll(() => {
6
+ Object.defineProperty(document, 'fonts', {
7
+ value: { ready: Promise.resolve() },
8
+ configurable: true,
9
+ })
10
+ })
11
+
12
+ const tabs = ['Overview', 'Details', 'Settings']
13
+
14
+ describe('TabbedContent', () => {
15
+ it('defaults to the first tab', () => {
16
+ const wrapper = mount(TabbedContent, {
17
+ props: { tabs },
18
+ slots: {
19
+ overview: '<div>Overview content</div>',
20
+ details: '<div>Details content</div>',
21
+ },
22
+ })
23
+ expect(wrapper.find('[role="tabpanel"]').text()).toContain('Overview content')
24
+ })
25
+
26
+ it('shows the slot for the v-model selected tab', () => {
27
+ const wrapper = mount(TabbedContent, {
28
+ props: { tabs, modelValue: 'Details' },
29
+ slots: {
30
+ overview: '<div>Overview content</div>',
31
+ details: '<div>Details content</div>',
32
+ },
33
+ })
34
+ expect(wrapper.find('[role="tabpanel"]').text()).toContain('Details content')
35
+ })
36
+
37
+ it('sets aria-labelledby based on the selected tab', () => {
38
+ const wrapper = mount(TabbedContent, {
39
+ props: { tabs, modelValue: 'Overview' },
40
+ })
41
+ expect(wrapper.find('[role="tabpanel"]').attributes('aria-labelledby')).toBe('tab-overview')
42
+ })
43
+
44
+ it('converts tab labels with spaces to hyphenated slot names', () => {
45
+ const wrapper = mount(TabbedContent, {
46
+ props: { tabs: ['My Reports', 'Other'], modelValue: 'My Reports' },
47
+ slots: {
48
+ 'my-reports': '<div>Report content</div>',
49
+ },
50
+ })
51
+ expect(wrapper.find('[role="tabpanel"]').text()).toContain('Report content')
52
+ })
53
+
54
+ it('emits update:modelValue when a tab is clicked', async () => {
55
+ const wrapper = mount(TabbedContent, {
56
+ props: { tabs, modelValue: 'Overview' },
57
+ })
58
+ const buttons = wrapper.findAll('[role="tab"]')
59
+ await buttons[1].trigger('click')
60
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['Details'])
61
+ })
62
+
63
+ it('updates displayed content when modelValue prop changes', async () => {
64
+ const wrapper = mount(TabbedContent, {
65
+ props: { tabs, modelValue: 'Overview' },
66
+ slots: {
67
+ overview: '<div>Overview content</div>',
68
+ details: '<div>Details content</div>',
69
+ },
70
+ })
71
+ await wrapper.setProps({ modelValue: 'Details' })
72
+ expect(wrapper.find('[role="tabpanel"]').text()).toContain('Details content')
73
+ })
74
+ })
75
+
76
+ describe('TabbedContent met tab-objecten', () => {
77
+ // NexxtTabs accepteert sinds de icoonondersteuning ook { label, icon }. Geen
78
+ // enkele story of test gaf die vorm door, en het component behandelde elke tab
79
+ // als string: het zette het hele object in een model dat een label verwacht.
80
+ const iconTabs = [
81
+ { label: 'Overview', icon: 'house' },
82
+ { label: 'Details', icon: 'list' },
83
+ ] as const
84
+
85
+ it('vult het model met het label en niet met het object', () => {
86
+ const wrapper = mount(TabbedContent, { props: { tabs: [...iconTabs] } })
87
+ expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual(['Overview'])
88
+ })
89
+
90
+ it('leidt de slotnaam af van het label', () => {
91
+ const wrapper = mount(TabbedContent, {
92
+ props: { tabs: [...iconTabs] },
93
+ slots: { overview: '<div>Overzicht</div>' },
94
+ })
95
+ expect(wrapper.find('[role="tabpanel"]').text()).toContain('Overzicht')
96
+ })
97
+
98
+ it('kent de volgorde van objecttabs, zodat de richting klopt', async () => {
99
+ // Op props.tabs vergelijken gaf met objecten twee keer -1, waardoor de
100
+ // richting altijd backward werd. Zichtbaar in de klassen die de Transition
101
+ // meekrijgt bij vooruit versus achteruit.
102
+ const wrapper = mount(TabbedContent, {
103
+ props: { tabs: [...iconTabs], animated: true, modelValue: 'Overview' },
104
+ })
105
+ await wrapper.setProps({ modelValue: 'Details' })
106
+
107
+ expect((wrapper.vm as unknown as { direction: string }).direction).toBe('forward')
108
+ })
109
+ })