@nexxtmove/ui 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +113 -17
- package/dist/index.js +146 -111
- package/dist/nuxt.js +26 -25
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.vue +49 -0
- package/src/components/Calendar/_CalendarDayView.vue +1 -1
- package/src/components/Chip/Chip.vue +9 -4
- package/src/components/Table/Table.vue +2 -2
- package/src/components.json +1 -0
- package/src/index.ts +1 -0
- package/src/components/AnimatedNumber/AnimatedNumber.stories.ts +0 -15
- package/src/components/AnimatedNumber/AnimatedNumber.test.ts +0 -56
- package/src/components/Button/Button.stories.ts +0 -212
- package/src/components/Button/Button.test.ts +0 -318
- package/src/components/Calendar/Calendar.stories.ts +0 -91
- package/src/components/Calendar/Calendar.test.ts +0 -279
- package/src/components/Calendar/_CalendarDayView.test.ts +0 -104
- package/src/components/Calendar/_CalendarHeader.test.ts +0 -86
- package/src/components/Calendar/_CalendarMonthView.test.ts +0 -52
- package/src/components/Calendar/_CalendarYearView.test.ts +0 -56
- package/src/components/Checkbox/Checkbox.stories.ts +0 -35
- package/src/components/Checkbox/Checkbox.test.ts +0 -55
- package/src/components/Chip/Chip.stories.ts +0 -42
- package/src/components/Chip/Chip.test.ts +0 -51
- package/src/components/CustomerJourneyTile/CustomerJourneyTile.stories.ts +0 -86
- package/src/components/CustomerJourneyTile/CustomerJourneyTile.test.ts +0 -178
- package/src/components/DatePicker/DatePicker.stories.ts +0 -149
- package/src/components/DatePicker/DatePicker.test.ts +0 -191
- package/src/components/DropdownButton/DropdownButton.stories.ts +0 -68
- package/src/components/DropdownButton/DropdownButton.test.ts +0 -124
- package/src/components/Header/Header.stories.ts +0 -54
- package/src/components/Header/Header.test.ts +0 -183
- package/src/components/Icon/Icon.stories.ts +0 -50
- package/src/components/Icon/Icon.test.ts +0 -73
- package/src/components/InfoBlock/InfoBlock.stories.ts +0 -90
- package/src/components/InfoBlock/InfoBlock.test.ts +0 -101
- package/src/components/ProgressBar/ProgressBar.stories.ts +0 -30
- package/src/components/ProgressBar/ProgressBar.test.ts +0 -314
- package/src/components/SocialIcons/SocialIcons.stories.ts +0 -34
- package/src/components/SocialIcons/SocialIcons.test.ts +0 -58
- package/src/components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.stories.ts +0 -11
- package/src/components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.test.ts +0 -131
- package/src/components/SocialMediaTemplate/SocialMediaTemplate.stories.ts +0 -71
- package/src/components/SocialMediaTemplate/SocialMediaTemplate.test.ts +0 -466
- package/src/components/SocialMediaType/SocialMediaType.stories.ts +0 -43
- package/src/components/SocialMediaType/SocialMediaType.test.ts +0 -126
- package/src/components/StepperHeader/StepperHeader.stories.ts +0 -47
- package/src/components/StepperHeader/StepperHeader.test.ts +0 -244
- package/src/components/Table/Table.stories.ts +0 -283
- package/src/components/Table/Table.test.ts +0 -220
- package/src/components/TimelineEvent/TimelineEvent.stories.ts +0 -291
- package/src/components/TimelineEvent/TimelineEvent.test.ts +0 -166
- package/src/components/TimelinePhaseblock/TimelinePhaseblock.stories.ts +0 -198
- package/src/components/TimelinePhaseblock/TimelinePhaseblock.test.ts +0 -283
- package/src/components/Tooltip/Tooltip.stories.ts +0 -146
- package/src/components/Tooltip/Tooltip.test.ts +0 -122
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
|
-
import { mount } from '@vue/test-utils'
|
|
3
|
-
import ProgressBar from './ProgressBar.vue'
|
|
4
|
-
|
|
5
|
-
describe('ProgressBar', () => {
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
vi.clearAllMocks()
|
|
8
|
-
vi.stubGlobal('console', { warn: vi.fn() })
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
it('renders correct percentage for first step', () => {
|
|
12
|
-
const wrapper = mount(ProgressBar, {
|
|
13
|
-
props: {
|
|
14
|
-
steps: 5,
|
|
15
|
-
currentStep: 0,
|
|
16
|
-
},
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
const progressbar = wrapper.find('[role="progressbar"]')
|
|
20
|
-
expect(progressbar.attributes('aria-valuenow')).toBe('0')
|
|
21
|
-
|
|
22
|
-
const innerBar = progressbar.find('div')
|
|
23
|
-
expect(innerBar.attributes('style')).toContain('width: 0%')
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('renders correct percentage for middle step', () => {
|
|
27
|
-
const wrapper = mount(ProgressBar, {
|
|
28
|
-
props: {
|
|
29
|
-
steps: 5,
|
|
30
|
-
currentStep: 2,
|
|
31
|
-
},
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
const progressbar = wrapper.find('[role="progressbar"]')
|
|
35
|
-
expect(progressbar.attributes('aria-valuenow')).toBe('50')
|
|
36
|
-
expect(progressbar.find('div').attributes('style')).toContain('width: 50%')
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('renders correct percentage for last step', () => {
|
|
40
|
-
const wrapper = mount(ProgressBar, {
|
|
41
|
-
props: {
|
|
42
|
-
steps: 5,
|
|
43
|
-
currentStep: 4,
|
|
44
|
-
},
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
const progressbar = wrapper.find('[role="progressbar"]')
|
|
48
|
-
expect(progressbar.attributes('aria-valuenow')).toBe('100')
|
|
49
|
-
expect(progressbar.find('div').attributes('style')).toContain('width: 100%')
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('clamps currentStep within range', () => {
|
|
53
|
-
const wrapperLow = mount(ProgressBar, {
|
|
54
|
-
props: { steps: 5, currentStep: -1 },
|
|
55
|
-
})
|
|
56
|
-
expect(wrapperLow.find('[role="progressbar"]').attributes('aria-valuenow')).toBe('0')
|
|
57
|
-
|
|
58
|
-
const wrapperHigh = mount(ProgressBar, {
|
|
59
|
-
props: { steps: 5, currentStep: 10 },
|
|
60
|
-
})
|
|
61
|
-
expect(wrapperHigh.find('[role="progressbar"]').attributes('aria-valuenow')).toBe('100')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('handles edge case of 0 or 1 steps', () => {
|
|
65
|
-
const wrapperZero = mount(ProgressBar, {
|
|
66
|
-
props: { steps: 0, currentStep: 0 },
|
|
67
|
-
})
|
|
68
|
-
expect(wrapperZero.find('[role="progressbar"]').attributes('aria-valuenow')).toBe('0')
|
|
69
|
-
|
|
70
|
-
const wrapperOne = mount(ProgressBar, {
|
|
71
|
-
props: { steps: 1, currentStep: 0 },
|
|
72
|
-
})
|
|
73
|
-
expect(wrapperOne.find('[role="progressbar"]').attributes('aria-valuenow')).toBe('100')
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('handles empty stepTitles array', () => {
|
|
77
|
-
const wrapper = mount(ProgressBar, {
|
|
78
|
-
props: { steps: 5, currentStep: 0, stepTitles: [] },
|
|
79
|
-
})
|
|
80
|
-
expect(wrapper.find('ol').exists()).toBe(false)
|
|
81
|
-
expect(wrapper.find('.tabular-nums').exists()).toBe(false)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('displays correct step text without titles', () => {
|
|
85
|
-
const wrapper = mount(ProgressBar, {
|
|
86
|
-
props: { steps: 5, currentStep: 2 },
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
expect(wrapper.find('[role="progressbar"]').attributes('aria-valuetext')).toBe('Step 3 of 5')
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('displays correct step text with titles', () => {
|
|
93
|
-
const stepTitles = ['Start', 'Middle', 'End']
|
|
94
|
-
const wrapper = mount(ProgressBar, {
|
|
95
|
-
props: {
|
|
96
|
-
steps: 3,
|
|
97
|
-
currentStep: 1,
|
|
98
|
-
stepTitles,
|
|
99
|
-
},
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
expect(wrapper.find('[role="progressbar"]').attributes('aria-valuetext')).toBe(
|
|
103
|
-
'Step 2 of 3: Middle',
|
|
104
|
-
)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
it('renders step titles and marks current step', () => {
|
|
108
|
-
const stepTitles = ['Step 1', 'Step 2', 'Step 3']
|
|
109
|
-
const wrapper = mount(ProgressBar, {
|
|
110
|
-
props: {
|
|
111
|
-
steps: 3,
|
|
112
|
-
currentStep: 1,
|
|
113
|
-
stepTitles,
|
|
114
|
-
},
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
const items = wrapper.findAll('li')
|
|
118
|
-
expect(items).toHaveLength(3)
|
|
119
|
-
expect(items[1]!.attributes('aria-current')).toBe('step')
|
|
120
|
-
expect(items[1]!.classes()).toContain('text-sapphire-500')
|
|
121
|
-
expect(items[0]!.attributes('aria-current')).toBeUndefined()
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('uses default progress label', () => {
|
|
125
|
-
const wrapper = mount(ProgressBar, {
|
|
126
|
-
props: {
|
|
127
|
-
steps: 5,
|
|
128
|
-
currentStep: 0,
|
|
129
|
-
},
|
|
130
|
-
})
|
|
131
|
-
expect(wrapper.find('[role="progressbar"]').attributes('aria-label')).toBe('Voortgang')
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
it('uses custom progress label', () => {
|
|
135
|
-
const wrapper = mount(ProgressBar, {
|
|
136
|
-
props: {
|
|
137
|
-
steps: 5,
|
|
138
|
-
currentStep: 0,
|
|
139
|
-
progressLabel: 'My Progress',
|
|
140
|
-
},
|
|
141
|
-
})
|
|
142
|
-
expect(wrapper.find('[role="progressbar"]').attributes('aria-label')).toBe('My Progress')
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
it('warns when stepTitles length mismatch', () => {
|
|
146
|
-
const spy = vi.spyOn(console, 'warn')
|
|
147
|
-
mount(ProgressBar, {
|
|
148
|
-
props: {
|
|
149
|
-
steps: 5,
|
|
150
|
-
currentStep: 0,
|
|
151
|
-
stepTitles: ['Only one'],
|
|
152
|
-
},
|
|
153
|
-
})
|
|
154
|
-
expect(spy).toHaveBeenCalledWith(expect.stringContaining('[NexxtProgressBar]'))
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('handles missing title in titles array gracefully', () => {
|
|
158
|
-
const wrapper = mount(ProgressBar, {
|
|
159
|
-
props: {
|
|
160
|
-
steps: 3,
|
|
161
|
-
currentStep: 2, // Last step
|
|
162
|
-
stepTitles: ['Only First'], // Only index 0 exists
|
|
163
|
-
},
|
|
164
|
-
})
|
|
165
|
-
// clampedStep is 2, stepTitles[2] is undefined
|
|
166
|
-
expect(wrapper.find('[role="progressbar"]').attributes('aria-valuetext')).toBe('Step 3 of 3')
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
it('has responsive container query classes', () => {
|
|
170
|
-
const wrapper = mount(ProgressBar, {
|
|
171
|
-
props: {
|
|
172
|
-
steps: 5,
|
|
173
|
-
currentStep: 0,
|
|
174
|
-
stepTitles: ['Step 1', 'Step 2', 'Step 3', 'Step 4', 'Step 5'],
|
|
175
|
-
},
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
// Verify the root is a container
|
|
179
|
-
expect(wrapper.classes()).toContain('@container')
|
|
180
|
-
|
|
181
|
-
// Verify the digit counter hides when the container is large
|
|
182
|
-
const counter = wrapper.find('.tabular-nums')
|
|
183
|
-
expect(counter.classes()).toContain('@md:hidden')
|
|
184
|
-
|
|
185
|
-
// Verify list items hide titles when the container is small
|
|
186
|
-
const firstTitle = wrapper.find('li')
|
|
187
|
-
expect(firstTitle.classes()).toContain('@max-md:hidden')
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
it('updates animation direction correctly based on step change', async () => {
|
|
191
|
-
const wrapper = mount(ProgressBar, {
|
|
192
|
-
props: {
|
|
193
|
-
steps: 5,
|
|
194
|
-
currentStep: 2,
|
|
195
|
-
stepTitles: ['1', '2', '3', '4', '5'],
|
|
196
|
-
},
|
|
197
|
-
global: {
|
|
198
|
-
stubs: {
|
|
199
|
-
// Stubbing Transition lets us inspect the dynamic classes passed to it
|
|
200
|
-
Transition: {
|
|
201
|
-
template: '<slot />',
|
|
202
|
-
props: ['enterFromClass', 'leaveToClass'],
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
},
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
// Increment: Step 3 -> 4
|
|
209
|
-
await wrapper.setProps({ currentStep: 3 })
|
|
210
|
-
let transition = wrapper.findComponent({ name: 'Transition' })
|
|
211
|
-
expect(transition.props('enterFromClass')).toBe('translate-y-full')
|
|
212
|
-
expect(transition.props('leaveToClass')).toBe('-translate-y-full')
|
|
213
|
-
|
|
214
|
-
// Decrement: Step 4 -> 2
|
|
215
|
-
await wrapper.setProps({ currentStep: 1 })
|
|
216
|
-
transition = wrapper.findComponent({ name: 'Transition' })
|
|
217
|
-
expect(transition.props('enterFromClass')).toBe('-translate-y-full')
|
|
218
|
-
expect(transition.props('leaveToClass')).toBe('translate-y-full')
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
it('emits update:currentStep when a previous step is clicked', async () => {
|
|
222
|
-
const stepTitles = ['Step 1', 'Step 2', 'Step 3']
|
|
223
|
-
const wrapper = mount(ProgressBar, {
|
|
224
|
-
props: {
|
|
225
|
-
steps: 3,
|
|
226
|
-
currentStep: 2,
|
|
227
|
-
stepTitles,
|
|
228
|
-
},
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
const items = wrapper.findAll('li')
|
|
232
|
-
|
|
233
|
-
// Click first step (index 0, previous)
|
|
234
|
-
await items[0]!.trigger('click')
|
|
235
|
-
expect(wrapper.emitted('update:currentStep')).toBeTruthy()
|
|
236
|
-
expect(wrapper.emitted('update:currentStep')![0]).toEqual([0])
|
|
237
|
-
|
|
238
|
-
// Click second step (index 1, previous)
|
|
239
|
-
await items[1]!.trigger('click')
|
|
240
|
-
expect(wrapper.emitted('update:currentStep')![1]).toEqual([1])
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
it('does not emit update:currentStep when the current or a future step is clicked', async () => {
|
|
244
|
-
const stepTitles = ['Step 1', 'Step 2', 'Step 3']
|
|
245
|
-
const wrapper = mount(ProgressBar, {
|
|
246
|
-
props: {
|
|
247
|
-
steps: 3,
|
|
248
|
-
currentStep: 1,
|
|
249
|
-
stepTitles,
|
|
250
|
-
},
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
const items = wrapper.findAll('li')
|
|
254
|
-
|
|
255
|
-
// Click current step (index 1)
|
|
256
|
-
await items[1]!.trigger('click')
|
|
257
|
-
expect(wrapper.emitted('update:currentStep')).toBeFalsy()
|
|
258
|
-
|
|
259
|
-
// Click future step (index 2)
|
|
260
|
-
await items[2]!.trigger('click')
|
|
261
|
-
expect(wrapper.emitted('update:currentStep')).toBeFalsy()
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
it('applies interactive classes only to previous steps', () => {
|
|
265
|
-
const stepTitles = ['Step 1', 'Step 2', 'Step 3']
|
|
266
|
-
const wrapper = mount(ProgressBar, {
|
|
267
|
-
props: {
|
|
268
|
-
steps: 3,
|
|
269
|
-
currentStep: 1,
|
|
270
|
-
stepTitles,
|
|
271
|
-
},
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
const items = wrapper.findAll('li')
|
|
275
|
-
|
|
276
|
-
// Previous step
|
|
277
|
-
expect(items[0]!.classes()).toContain('cursor-pointer')
|
|
278
|
-
expect(items[0]!.classes()).toContain('hover:text-sapphire-400')
|
|
279
|
-
expect(items[0]!.attributes('role')).toBe('button')
|
|
280
|
-
expect(items[0]!.attributes('tabindex')).toBe('0')
|
|
281
|
-
expect(items[0]!.attributes('aria-label')).toBe('Ga naar Step 1')
|
|
282
|
-
|
|
283
|
-
// Current step
|
|
284
|
-
expect(items[1]!.classes()).not.toContain('cursor-pointer')
|
|
285
|
-
expect(items[1]!.attributes('role')).toBeUndefined()
|
|
286
|
-
expect(items[1]!.attributes('tabindex')).toBeUndefined()
|
|
287
|
-
|
|
288
|
-
// Future step
|
|
289
|
-
expect(items[2]!.classes()).not.toContain('cursor-pointer')
|
|
290
|
-
expect(items[2]!.attributes('role')).toBeUndefined()
|
|
291
|
-
expect(items[2]!.attributes('tabindex')).toBeUndefined()
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
it('emits update:currentStep when Enter or Space is pressed on a previous step', async () => {
|
|
295
|
-
const stepTitles = ['Step 1', 'Step 2', 'Step 3']
|
|
296
|
-
const wrapper = mount(ProgressBar, {
|
|
297
|
-
props: {
|
|
298
|
-
steps: 3,
|
|
299
|
-
currentStep: 2,
|
|
300
|
-
stepTitles,
|
|
301
|
-
},
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
const items = wrapper.findAll('li')
|
|
305
|
-
|
|
306
|
-
// Press Enter on first step
|
|
307
|
-
await items[0]!.trigger('keydown.enter')
|
|
308
|
-
expect(wrapper.emitted('update:currentStep')![0]).toEqual([0])
|
|
309
|
-
|
|
310
|
-
// Press Space on second step
|
|
311
|
-
await items[1]!.trigger('keydown.space')
|
|
312
|
-
expect(wrapper.emitted('update:currentStep')![1]).toEqual([1])
|
|
313
|
-
})
|
|
314
|
-
})
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
-
import SocialIcons from './SocialIcons.vue'
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
title: 'Components/Molecules/Social Icons',
|
|
6
|
-
component: SocialIcons,
|
|
7
|
-
parameters: {
|
|
8
|
-
design: {
|
|
9
|
-
type: 'figma',
|
|
10
|
-
url: 'https://www.figma.com/design/CdbFJ7qUga6mtjagcPDvYK/Design-System?node-id=524-17&t=CbHvOQfEMIr7M5mO-11',
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
} satisfies Meta<typeof SocialIcons>
|
|
14
|
-
|
|
15
|
-
type Story = StoryObj<typeof SocialIcons>
|
|
16
|
-
|
|
17
|
-
export const Default: Story = {
|
|
18
|
-
args: {
|
|
19
|
-
facebook: true,
|
|
20
|
-
instagram: true,
|
|
21
|
-
linkedin: true,
|
|
22
|
-
x: true,
|
|
23
|
-
google: true,
|
|
24
|
-
tiktok: true,
|
|
25
|
-
},
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const Selective: Story = {
|
|
29
|
-
args: {
|
|
30
|
-
facebook: true,
|
|
31
|
-
linkedin: true,
|
|
32
|
-
instagram: true,
|
|
33
|
-
},
|
|
34
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { mount } from '@vue/test-utils'
|
|
3
|
-
import SocialIcons from './SocialIcons.vue'
|
|
4
|
-
|
|
5
|
-
describe('SocialIcons', () => {
|
|
6
|
-
it('renders nothing when no props are set', () => {
|
|
7
|
-
const wrapper = mount(SocialIcons)
|
|
8
|
-
expect(wrapper.findAll('.rounded-full')).toHaveLength(0)
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
it('renders only the requested icons', () => {
|
|
12
|
-
const wrapper = mount(SocialIcons, {
|
|
13
|
-
props: {
|
|
14
|
-
facebook: true,
|
|
15
|
-
instagram: true,
|
|
16
|
-
},
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
const icons = wrapper.findAll('.rounded-full')
|
|
20
|
-
expect(icons).toHaveLength(2)
|
|
21
|
-
|
|
22
|
-
// Check for specific background colors to verify which icons are rendered
|
|
23
|
-
expect(icons[0]!.classes()).toContain('bg-brands-facebook')
|
|
24
|
-
expect(icons[1]!.classes()).toContain('bg-brands-instagram')
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('has correct accessibility attributes', () => {
|
|
28
|
-
const wrapper = mount(SocialIcons, {
|
|
29
|
-
props: {
|
|
30
|
-
facebook: true,
|
|
31
|
-
},
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
const list = wrapper.find('ul')
|
|
35
|
-
expect(list.exists()).toBe(true)
|
|
36
|
-
expect(list.attributes('aria-label')).toBe('Sociale media kanalen')
|
|
37
|
-
|
|
38
|
-
const item = wrapper.find('li')
|
|
39
|
-
expect(item.exists()).toBe(true)
|
|
40
|
-
expect(item.attributes('aria-label')).toBe('Facebook')
|
|
41
|
-
expect(item.findComponent({ name: 'NexxtIcon' }).attributes('aria-hidden')).toBe('true')
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('renders all icons when all props are true', () => {
|
|
45
|
-
const wrapper = mount(SocialIcons, {
|
|
46
|
-
props: {
|
|
47
|
-
facebook: true,
|
|
48
|
-
instagram: true,
|
|
49
|
-
linkedin: true,
|
|
50
|
-
x: true,
|
|
51
|
-
google: true,
|
|
52
|
-
tiktok: true,
|
|
53
|
-
},
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
expect(wrapper.findAll('.rounded-full')).toHaveLength(6)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
-
import SocialMediaCustomTemplate from './SocialMediaCustomTemplate.vue'
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
title: 'Components/Molecules/Social Media Custom Template',
|
|
6
|
-
component: SocialMediaCustomTemplate,
|
|
7
|
-
} satisfies Meta<typeof SocialMediaCustomTemplate>
|
|
8
|
-
|
|
9
|
-
type Story = StoryObj<typeof SocialMediaCustomTemplate>
|
|
10
|
-
|
|
11
|
-
export const Default: Story = {}
|
|
@@ -1,131 +0,0 @@
|
|
|
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('does not call handleEnded if videoRef is null', async () => {
|
|
92
|
-
const wrapper = mount(SocialMediaCustomTemplate)
|
|
93
|
-
// @ts-expect-error - manually setting ref to null for test
|
|
94
|
-
wrapper.vm.videoRef = null
|
|
95
|
-
|
|
96
|
-
// Trigger handleEnded directly (internally)
|
|
97
|
-
// @ts-expect-error - calling internal method
|
|
98
|
-
wrapper.vm.handleEnded()
|
|
99
|
-
|
|
100
|
-
// @ts-expect-error - checking internal state
|
|
101
|
-
expect(wrapper.vm.hasPlayedOnce).toBe(false)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('internal methods do nothing if videoRef is null', async () => {
|
|
105
|
-
const wrapper = mount(SocialMediaCustomTemplate)
|
|
106
|
-
// @ts-expect-error - manually setting ref to null for test
|
|
107
|
-
wrapper.vm.videoRef = null
|
|
108
|
-
// @ts-expect-error - manually setting hasPlayedOnce for branch test
|
|
109
|
-
wrapper.vm.hasPlayedOnce = true
|
|
110
|
-
|
|
111
|
-
// Trigger handleMouseEnter directly (internally)
|
|
112
|
-
// @ts-expect-error - calling internal method
|
|
113
|
-
wrapper.vm.handleMouseEnter()
|
|
114
|
-
// Trigger handleMouseLeave directly (internally)
|
|
115
|
-
// @ts-expect-error - calling internal method
|
|
116
|
-
wrapper.vm.handleMouseLeave()
|
|
117
|
-
|
|
118
|
-
// No errors should occur
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('plays onMounted if videoRef is valid', async () => {
|
|
122
|
-
const wrapper = mount(SocialMediaCustomTemplate)
|
|
123
|
-
const videoElement = wrapper.find('video').element as HTMLVideoElement
|
|
124
|
-
const playSpy = vi.spyOn(videoElement, 'play').mockImplementation(() => Promise.resolve())
|
|
125
|
-
|
|
126
|
-
// Simulate onMounted behavior by calling it again (it already ran once on mount)
|
|
127
|
-
// @ts-expect-error - manually calling onMounted setup code
|
|
128
|
-
wrapper.vm.handleMouseEnter() // hasPlayedOnce is false, should not play
|
|
129
|
-
expect(playSpy).not.toHaveBeenCalled()
|
|
130
|
-
})
|
|
131
|
-
})
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
-
import SocialMediaTemplate from './SocialMediaTemplate.vue'
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
title: 'Components/Organisms/Social Media Template',
|
|
6
|
-
component: SocialMediaTemplate,
|
|
7
|
-
argTypes: {
|
|
8
|
-
variant: {
|
|
9
|
-
control: 'select',
|
|
10
|
-
options: ['post', 'video', 'slideshow'],
|
|
11
|
-
},
|
|
12
|
-
selected: {
|
|
13
|
-
control: 'boolean',
|
|
14
|
-
},
|
|
15
|
-
muted: {
|
|
16
|
-
control: 'boolean',
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
parameters: {
|
|
20
|
-
design: {
|
|
21
|
-
type: 'figma',
|
|
22
|
-
url: 'https://www.figma.com/design/CdbFJ7qUga6mtjagcPDvYK/Design-System?node-id=578-340&t=CbHvOQfEMIr7M5mO-11',
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
} satisfies Meta<typeof SocialMediaTemplate>
|
|
26
|
-
|
|
27
|
-
type Story = StoryObj<typeof SocialMediaTemplate>
|
|
28
|
-
|
|
29
|
-
export const Post: Story = {
|
|
30
|
-
args: {
|
|
31
|
-
variant: 'post',
|
|
32
|
-
media: 'https://loremflickr.com/400/600/house',
|
|
33
|
-
mediaType: 'image',
|
|
34
|
-
name: 'Amsterdam',
|
|
35
|
-
type: 'Woning',
|
|
36
|
-
selected: false,
|
|
37
|
-
},
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const Video: Story = {
|
|
41
|
-
args: {
|
|
42
|
-
variant: 'video',
|
|
43
|
-
media: '/barcelona.mp4',
|
|
44
|
-
mediaType: 'video',
|
|
45
|
-
muted: true,
|
|
46
|
-
name: 'Barcelona',
|
|
47
|
-
type: 'Woning',
|
|
48
|
-
selected: false,
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const Slideshow: Story = {
|
|
53
|
-
args: {
|
|
54
|
-
variant: 'slideshow',
|
|
55
|
-
media: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
|
|
56
|
-
mediaType: 'image',
|
|
57
|
-
name: 'Stockholm',
|
|
58
|
-
type: 'Bedrijfsunit',
|
|
59
|
-
},
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const Selected: Story = {
|
|
63
|
-
args: {
|
|
64
|
-
variant: 'post',
|
|
65
|
-
media: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
|
|
66
|
-
mediaType: 'image',
|
|
67
|
-
name: 'Amsterdam',
|
|
68
|
-
type: 'Woning',
|
|
69
|
-
selected: true,
|
|
70
|
-
},
|
|
71
|
-
}
|