@nexxtmove/ui 0.1.31 → 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.
Files changed (54) hide show
  1. package/dist/index.d.ts +104 -17
  2. package/dist/index.js +132 -128
  3. package/package.json +1 -1
  4. package/src/components/Calendar/_CalendarDayView.vue +1 -1
  5. package/src/components/Chip/Chip.vue +9 -4
  6. package/src/components/Table/Table.vue +2 -2
  7. package/src/components/AnimatedNumber/AnimatedNumber.stories.ts +0 -15
  8. package/src/components/AnimatedNumber/AnimatedNumber.test.ts +0 -56
  9. package/src/components/Avatar/Avatar.stories.ts +0 -28
  10. package/src/components/Avatar/Avatar.test.ts +0 -55
  11. package/src/components/Button/Button.stories.ts +0 -212
  12. package/src/components/Button/Button.test.ts +0 -318
  13. package/src/components/Calendar/Calendar.stories.ts +0 -91
  14. package/src/components/Calendar/Calendar.test.ts +0 -279
  15. package/src/components/Calendar/_CalendarDayView.test.ts +0 -104
  16. package/src/components/Calendar/_CalendarHeader.test.ts +0 -86
  17. package/src/components/Calendar/_CalendarMonthView.test.ts +0 -52
  18. package/src/components/Calendar/_CalendarYearView.test.ts +0 -56
  19. package/src/components/Checkbox/Checkbox.stories.ts +0 -35
  20. package/src/components/Checkbox/Checkbox.test.ts +0 -55
  21. package/src/components/Chip/Chip.stories.ts +0 -42
  22. package/src/components/Chip/Chip.test.ts +0 -51
  23. package/src/components/CustomerJourneyTile/CustomerJourneyTile.stories.ts +0 -86
  24. package/src/components/CustomerJourneyTile/CustomerJourneyTile.test.ts +0 -178
  25. package/src/components/DatePicker/DatePicker.stories.ts +0 -149
  26. package/src/components/DatePicker/DatePicker.test.ts +0 -191
  27. package/src/components/DropdownButton/DropdownButton.stories.ts +0 -68
  28. package/src/components/DropdownButton/DropdownButton.test.ts +0 -124
  29. package/src/components/Header/Header.stories.ts +0 -54
  30. package/src/components/Header/Header.test.ts +0 -183
  31. package/src/components/Icon/Icon.stories.ts +0 -50
  32. package/src/components/Icon/Icon.test.ts +0 -73
  33. package/src/components/InfoBlock/InfoBlock.stories.ts +0 -90
  34. package/src/components/InfoBlock/InfoBlock.test.ts +0 -101
  35. package/src/components/ProgressBar/ProgressBar.stories.ts +0 -30
  36. package/src/components/ProgressBar/ProgressBar.test.ts +0 -314
  37. package/src/components/SocialIcons/SocialIcons.stories.ts +0 -34
  38. package/src/components/SocialIcons/SocialIcons.test.ts +0 -58
  39. package/src/components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.stories.ts +0 -11
  40. package/src/components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.test.ts +0 -131
  41. package/src/components/SocialMediaTemplate/SocialMediaTemplate.stories.ts +0 -71
  42. package/src/components/SocialMediaTemplate/SocialMediaTemplate.test.ts +0 -466
  43. package/src/components/SocialMediaType/SocialMediaType.stories.ts +0 -43
  44. package/src/components/SocialMediaType/SocialMediaType.test.ts +0 -126
  45. package/src/components/StepperHeader/StepperHeader.stories.ts +0 -47
  46. package/src/components/StepperHeader/StepperHeader.test.ts +0 -244
  47. package/src/components/Table/Table.stories.ts +0 -283
  48. package/src/components/Table/Table.test.ts +0 -220
  49. package/src/components/TimelineEvent/TimelineEvent.stories.ts +0 -291
  50. package/src/components/TimelineEvent/TimelineEvent.test.ts +0 -166
  51. package/src/components/TimelinePhaseblock/TimelinePhaseblock.stories.ts +0 -198
  52. package/src/components/TimelinePhaseblock/TimelinePhaseblock.test.ts +0 -283
  53. package/src/components/Tooltip/Tooltip.stories.ts +0 -146
  54. package/src/components/Tooltip/Tooltip.test.ts +0 -122
@@ -1,318 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { mount } from '@vue/test-utils'
3
- import Button from './Button.vue'
4
-
5
- describe('Button', () => {
6
- it('renders slot content', () => {
7
- const wrapper = mount(Button, {
8
- slots: {
9
- default: 'Click me',
10
- },
11
- })
12
-
13
- expect(wrapper.text()).toBe('Click me')
14
- })
15
-
16
- it('renders complex slot content', () => {
17
- const wrapper = mount(Button, {
18
- slots: {
19
- default: '<span>Complex</span> <span>Text</span>',
20
- },
21
- })
22
-
23
- expect(wrapper.html()).toContain('<span>Complex</span>')
24
- expect(wrapper.html()).toContain('<span>Text</span>')
25
- })
26
-
27
- it('applies primary variant by default', () => {
28
- const wrapper = mount(Button, {
29
- slots: {
30
- default: 'Click me',
31
- },
32
- })
33
-
34
- expect(wrapper.classes()).toContain('button-primary')
35
- })
36
-
37
- it('applies secondary variant when specified', () => {
38
- const wrapper = mount(Button, {
39
- props: {
40
- variant: 'secondary',
41
- },
42
- slots: {
43
- default: 'Click me',
44
- },
45
- })
46
-
47
- expect(wrapper.classes()).toContain('button-secondary')
48
- })
49
-
50
- it('applies success variant when specified', () => {
51
- const wrapper = mount(Button, {
52
- props: {
53
- variant: 'success',
54
- },
55
- slots: {
56
- default: 'Click me',
57
- },
58
- })
59
-
60
- expect(wrapper.classes()).toContain('button-success')
61
- })
62
-
63
- it('applies danger variant when specified', () => {
64
- const wrapper = mount(Button, {
65
- props: {
66
- variant: 'danger',
67
- },
68
- slots: {
69
- default: 'Click me',
70
- },
71
- })
72
-
73
- expect(wrapper.classes()).toContain('button-danger')
74
- })
75
-
76
- it('applies link variant when specified', () => {
77
- const wrapper = mount(Button, {
78
- props: {
79
- variant: 'link',
80
- },
81
- slots: {
82
- default: 'Click me',
83
- },
84
- })
85
-
86
- expect(wrapper.classes()).toContain('button-link')
87
- })
88
-
89
- it('applies link variant special classes', () => {
90
- const wrapper = mount(Button, {
91
- props: {
92
- variant: 'link',
93
- },
94
- slots: {
95
- default: 'Click me',
96
- },
97
- })
98
-
99
- const span = wrapper.find('span')
100
- expect(span.classes()).toContain('body-link')
101
- expect(span.classes()).toContain('underline-offset-1')
102
- expect(span.classes()).toContain('hover:underline-offset-2')
103
- })
104
-
105
- it('does not apply hover underline offset when link is disabled', () => {
106
- const wrapper = mount(Button, {
107
- props: {
108
- variant: 'link',
109
- disabled: true,
110
- },
111
- slots: {
112
- default: 'Click me',
113
- },
114
- })
115
-
116
- const span = wrapper.find('span')
117
- expect(span.classes()).not.toContain('hover:underline-offset-2')
118
- })
119
-
120
- it('renders icon when icon prop is provided', () => {
121
- const wrapper = mount(Button, {
122
- props: {
123
- icon: 'star',
124
- },
125
- slots: {
126
- default: 'Click me',
127
- },
128
- })
129
-
130
- const icon = wrapper.find('i')
131
- expect(icon.exists()).toBe(true)
132
- expect(icon.classes()).toContain('fa-light')
133
- expect(icon.classes()).toContain('fa-star')
134
- })
135
-
136
- it('does not render icon when icon prop is not provided', () => {
137
- const wrapper = mount(Button, {
138
- slots: {
139
- default: 'Click me',
140
- },
141
- })
142
-
143
- const icon = wrapper.find('i')
144
- expect(icon.exists()).toBe(false)
145
- })
146
-
147
- it('positions icon on the right when iconRight is true', () => {
148
- const wrapper = mount(Button, {
149
- props: {
150
- icon: 'arrow-right',
151
- iconRight: true,
152
- },
153
- slots: {
154
- default: 'Next',
155
- },
156
- })
157
-
158
- const icon = wrapper.find('i')
159
- expect(icon.classes()).toContain('order-last')
160
- })
161
-
162
- it('positions icon on the left by default', () => {
163
- const wrapper = mount(Button, {
164
- props: {
165
- icon: 'arrow-right',
166
- },
167
- slots: {
168
- default: 'Back',
169
- },
170
- })
171
-
172
- const icon = wrapper.find('i')
173
- expect(icon.classes()).not.toContain('order-last')
174
- })
175
-
176
- it('renders as button type="button"', () => {
177
- const wrapper = mount(Button, {
178
- slots: {
179
- default: 'Click me',
180
- },
181
- })
182
-
183
- expect(wrapper.element.tagName).toBe('BUTTON')
184
- expect(wrapper.attributes('type')).toBe('button')
185
- })
186
-
187
- it('disables button when disabled prop is true', () => {
188
- const wrapper = mount(Button, {
189
- props: {
190
- disabled: true,
191
- },
192
- slots: {
193
- default: 'Click me',
194
- },
195
- })
196
-
197
- expect(wrapper.attributes('disabled')).toBeDefined()
198
- expect(wrapper.classes()).toContain('disabled:cursor-not-allowed')
199
- })
200
-
201
- it('applies disabled styles for primary variant', () => {
202
- const wrapper = mount(Button, {
203
- props: {
204
- variant: 'primary',
205
- disabled: true,
206
- },
207
- slots: {
208
- default: 'Click me',
209
- },
210
- })
211
-
212
- expect(wrapper.attributes()).toHaveProperty('disabled')
213
- })
214
-
215
- it('hides icon and slot content when loading', () => {
216
- const wrapper = mount(Button, {
217
- props: {
218
- loading: true,
219
- icon: 'star',
220
- },
221
- slots: {
222
- default: 'Submit',
223
- },
224
- })
225
-
226
- const starIcon = wrapper.find('.fa-star')
227
- expect(starIcon.classes()).toContain('opacity-0')
228
-
229
- const label = wrapper.find('span')
230
- expect(label.classes()).toContain('opacity-0')
231
- })
232
-
233
- it('removes hover effects when loading or disabled in link variant', async () => {
234
- const wrapper = mount(Button, {
235
- props: {
236
- variant: 'link',
237
- loading: true,
238
- },
239
- slots: {
240
- default: 'Link',
241
- },
242
- })
243
-
244
- expect(wrapper.find('span').classes()).not.toContain('hover:underline-offset-2')
245
-
246
- await wrapper.setProps({ loading: false, disabled: true })
247
- expect(wrapper.find('span').classes()).not.toContain('hover:underline-offset-2')
248
-
249
- await wrapper.setProps({ disabled: false })
250
- expect(wrapper.find('span').classes()).toContain('hover:underline-offset-2')
251
- })
252
-
253
- it('renders icon-only button without padding', () => {
254
- const wrapper = mount(Button, {
255
- props: {
256
- icon: 'star',
257
- },
258
- })
259
-
260
- expect(wrapper.classes()).toContain('w-10')
261
- expect(wrapper.classes()).not.toContain('px-4')
262
- })
263
-
264
- it('displays spinner icon when loading is true', () => {
265
- const wrapper = mount(Button, {
266
- props: {
267
- loading: true,
268
- },
269
- })
270
-
271
- const icon = wrapper.find('i')
272
- expect(icon.exists()).toBe(true)
273
- expect(icon.classes()).toContain('fa-spinner-third')
274
- expect(icon.classes()).toContain('fa-spin')
275
- })
276
-
277
- it('disables button when loading is true', () => {
278
- const wrapper = mount(Button, {
279
- props: {
280
- loading: true,
281
- },
282
- })
283
-
284
- expect(wrapper.classes()).toContain('pointer-events-none')
285
- })
286
-
287
- it('set the button type prop', async () => {
288
- const wrapper = mount(Button)
289
-
290
- expect(wrapper.attributes('type')).toBe('button')
291
-
292
- await wrapper.setProps({ buttonType: 'submit' })
293
- expect(wrapper.attributes('type')).toBe('submit')
294
-
295
- await wrapper.setProps({ buttonType: 'reset' })
296
- expect(wrapper.attributes('type')).toBe('reset')
297
- })
298
-
299
- it('no animated span whitout default slot', async () => {
300
- const wrapper = mount(Button)
301
-
302
- const slotSpanWrapper = wrapper.find('span')
303
-
304
- expect(slotSpanWrapper.exists()).toBe(false)
305
- })
306
-
307
- it('has animated span with default slot', async () => {
308
- const wrapper = mount(Button, {
309
- slots: {
310
- default: 'text',
311
- },
312
- })
313
-
314
- const slotSpanWrapper = wrapper.find('span')
315
-
316
- expect(slotSpanWrapper.exists()).toBe(true)
317
- })
318
- })
@@ -1,91 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/vue3-vite'
2
- import { ref } from 'vue'
3
- import { enUS } from 'date-fns/locale'
4
- import Calendar from './Calendar.vue'
5
- export default {
6
- title: 'Components/Atoms/Calendar',
7
- component: Calendar,
8
- parameters: {
9
- design: {
10
- type: 'figma',
11
- url: 'https://www.figma.com/design/CdbFJ7qUga6mtjagcPDvYK/Design-System?node-id=713-720&t=CbHvOQfEMIr7M5mO-4',
12
- },
13
- },
14
- } satisfies Meta<typeof Calendar>
15
-
16
- type Story = StoryObj<typeof Calendar>
17
-
18
- export const Default: Story = {
19
- args: {},
20
- }
21
-
22
- export const WithSelectedDate: Story = {
23
- args: {
24
- modelValue: new Date(),
25
- },
26
- }
27
-
28
- export const WithMarkedDates: Story = {
29
- args: {
30
- markedDates: [
31
- new Date(new Date().getFullYear(), new Date().getMonth(), 5),
32
- new Date(new Date().getFullYear(), new Date().getMonth(), 12),
33
- new Date(new Date().getFullYear(), new Date().getMonth(), 20),
34
- ],
35
- },
36
- }
37
-
38
- export const WithDisabledDates: Story = {
39
- args: {
40
- disabledDates: [
41
- new Date(new Date().getFullYear(), new Date().getMonth(), 10),
42
- new Date(new Date().getFullYear(), new Date().getMonth(), 11),
43
- new Date(new Date().getFullYear(), new Date().getMonth(), 12),
44
- ],
45
- },
46
- }
47
-
48
- export const WithDateRange: Story = {
49
- args: {
50
- minDate: new Date(new Date().getFullYear(), new Date().getMonth(), 5),
51
- maxDate: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 20),
52
- },
53
- }
54
-
55
- export const WithMonthYearPicker: Story = {
56
- args: {
57
- monthYearPicker: true,
58
- },
59
- }
60
-
61
- export const EnglishLocale: Story = {
62
- args: {
63
- locale: enUS,
64
- },
65
- }
66
-
67
- export const Playground: Story = {
68
- render: (args) => ({
69
- components: { Calendar },
70
- setup() {
71
- const selected = ref<Date | null>(null)
72
- return { args, selected }
73
- },
74
- template: `
75
- <Calendar
76
- v-bind="args"
77
- v-model="selected"
78
- />
79
- <p v-if="selected" style="margin-top: 12px; font-size: 13px; color: #555; display: block;">
80
- Geselecteerd: {{ selected.toLocaleDateString('nl-NL') }}
81
- </p>
82
- `,
83
- }),
84
- args: {
85
- monthYearPicker: true,
86
- markedDates: [
87
- new Date(new Date().getFullYear(), new Date().getMonth(), 8),
88
- new Date(new Date().getFullYear(), new Date().getMonth(), 15),
89
- ],
90
- },
91
- }
@@ -1,279 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import { mount } from '@vue/test-utils'
3
- import { nextTick } from 'vue'
4
- import Calendar from './Calendar.vue'
5
-
6
- // Mock today's date for consistent testing
7
- const MOCK_TODAY = new Date(Date.UTC(2026, 2, 20)) // March 20, 2026
8
- vi.useFakeTimers()
9
- vi.setSystemTime(MOCK_TODAY)
10
-
11
- // Fixed reference date used across tests
12
- const YEAR = 2026
13
- const MONTH = 2 // March (0-indexed)
14
- const TODAY = new Date(Date.UTC(YEAR, MONTH, 20))
15
-
16
- const mountCalendar = (props = {}) => mount(Calendar, { props })
17
-
18
- describe('Calendar', () => {
19
- // ── Rendering ─────────────────────────────────────────────────────────────
20
-
21
- it('renders the calendar widget', () => {
22
- const wrapper = mountCalendar({ modelValue: TODAY })
23
- expect(wrapper.find('[class*="rounded-xl"]').exists()).toBe(true)
24
- })
25
-
26
- it('shows 7 weekday header columns', () => {
27
- const wrapper = mountCalendar({ modelValue: TODAY })
28
- const headers = wrapper.findAll('[class*="grid-cols-7"] > div').slice(0, 7)
29
- expect(headers.length).toBe(7)
30
- })
31
-
32
- // ── Day selection ──────────────────────────────────────────────────────────
33
-
34
- it('emits update:modelValue and select when a day is clicked', async () => {
35
- const wrapper = mountCalendar({ modelValue: TODAY })
36
-
37
- const dayButtons = wrapper
38
- .findAll('button[type="button"]')
39
- .filter((b) => /^\d{1,2}$/.test(b.text().trim()))
40
- await dayButtons[10]!.trigger('click')
41
- await nextTick()
42
-
43
- expect(wrapper.emitted('update:modelValue')).toBeTruthy()
44
- expect(wrapper.emitted('update:modelValue')![0]![0]).toBeInstanceOf(Date)
45
- expect(wrapper.emitted('select')).toBeTruthy()
46
- })
47
-
48
- // ── Month navigation ───────────────────────────────────────────────────────
49
-
50
- it('navigates to next month', async () => {
51
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 1)) })
52
-
53
- const labelBefore = wrapper
54
- .find('[class*="flex"][class*="items-center"][class*="gap-1"]')
55
- .text()
56
- const nextBtn = wrapper
57
- .findAll('button[aria-label]')
58
- .find((b) => b.attributes('aria-label') === 'Volgende maand')
59
- await nextBtn!.trigger('click')
60
- await nextTick()
61
-
62
- const labelAfter = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]').text()
63
- expect(labelAfter).not.toBe(labelBefore)
64
- })
65
-
66
- it('navigates to previous month', async () => {
67
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 1)) })
68
-
69
- const labelBefore = wrapper
70
- .find('[class*="flex"][class*="items-center"][class*="gap-1"]')
71
- .text()
72
- const prevBtn = wrapper
73
- .findAll('button[aria-label]')
74
- .find((b) => b.attributes('aria-label') === 'Vorige maand')
75
- await prevBtn!.trigger('click')
76
- await nextTick()
77
-
78
- const labelAfter = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]').text()
79
- expect(labelAfter).not.toBe(labelBefore)
80
- })
81
-
82
- // ── Marked dates ───────────────────────────────────────────────────────────
83
-
84
- it('renders a dot for marked dates', () => {
85
- const markedDates = [new Date(Date.UTC(YEAR, MONTH, 10))]
86
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 1)), markedDates })
87
-
88
- expect(wrapper.find('[class*="rounded-full"][class*="bg-cornflower"]').exists()).toBe(true)
89
- })
90
-
91
- // ── Disabled dates ─────────────────────────────────────────────────────────
92
-
93
- it('disables specified dates', () => {
94
- const disabledDates = [new Date(Date.UTC(YEAR, MONTH, 15))]
95
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 1)), disabledDates })
96
-
97
- const disabled = wrapper
98
- .findAll('button[disabled]')
99
- .filter((b) => /^\d{1,2}$/.test(b.text().trim()))
100
- expect(disabled.length).toBeGreaterThan(0)
101
- })
102
-
103
- it('does not emit when clicking a disabled date', async () => {
104
- const disabledDates = [new Date(Date.UTC(YEAR, MONTH, 15))]
105
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 1)), disabledDates })
106
-
107
- const disabledBtn = wrapper
108
- .findAll('button[disabled]')
109
- .find((b) => /^\d{1,2}$/.test(b.text().trim()))
110
- await disabledBtn?.trigger('click')
111
- await nextTick()
112
-
113
- expect(wrapper.emitted('update:modelValue')).toBeFalsy()
114
- })
115
-
116
- // ── minDate / maxDate ──────────────────────────────────────────────────────
117
-
118
- it('disables dates before minDate', () => {
119
- const minDate = new Date(Date.UTC(YEAR, MONTH, 10))
120
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 15)), minDate })
121
-
122
- const disabled = wrapper
123
- .findAll('button[disabled]')
124
- .filter((b) => /^\d{1,2}$/.test(b.text().trim()))
125
- expect(disabled.length).toBeGreaterThan(0)
126
- })
127
-
128
- it('disables dates after maxDate', () => {
129
- const maxDate = new Date(Date.UTC(YEAR, MONTH, 20))
130
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 15)), maxDate })
131
-
132
- const disabled = wrapper
133
- .findAll('button[disabled]')
134
- .filter((b) => /^\d{1,2}$/.test(b.text().trim()))
135
- expect(disabled.length).toBeGreaterThan(0)
136
- })
137
-
138
- it('disables prev nav when already at minDate month', () => {
139
- const minDate = new Date(Date.UTC(YEAR, MONTH, 1))
140
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 15)), minDate })
141
-
142
- const prevBtn = wrapper.find('button[aria-label="Vorige maand"]')
143
- expect(prevBtn.attributes('disabled')).toBeDefined()
144
- })
145
-
146
- it('disables next nav when already at maxDate month', () => {
147
- const maxDate = new Date(Date.UTC(YEAR, MONTH, 28))
148
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 15)), maxDate })
149
-
150
- const nextBtn = wrapper.find('button[aria-label="Volgende maand"]')
151
- expect(nextBtn.attributes('disabled')).toBeDefined()
152
- })
153
-
154
- it('does not navigate prev when at minDate month', async () => {
155
- const minDate = new Date(Date.UTC(YEAR, MONTH, 1))
156
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 15)), minDate })
157
-
158
- const prevBtn = wrapper.find('button[aria-label="Vorige maand"]')
159
- await prevBtn.trigger('click')
160
- await nextTick()
161
-
162
- const label = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]').text()
163
- expect(label).toContain('Mrt')
164
- })
165
-
166
- it('does not navigate next when at maxDate month', async () => {
167
- const maxDate = new Date(Date.UTC(YEAR, MONTH, 28))
168
- const wrapper = mountCalendar({ modelValue: new Date(Date.UTC(YEAR, MONTH, 15)), maxDate })
169
-
170
- const nextBtn = wrapper.find('button[aria-label="Volgende maand"]')
171
- await nextBtn.trigger('click')
172
- await nextTick()
173
-
174
- const label = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]').text()
175
- expect(label).toContain('Mrt')
176
- })
177
-
178
- // ── monthYearPicker ────────────────────────────────────────────────────────
179
-
180
- it('month name is not a button without monthYearPicker prop', () => {
181
- const wrapper = mountCalendar({ modelValue: TODAY })
182
-
183
- const header = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]')
184
- expect(header.find('button').exists()).toBe(false)
185
- })
186
-
187
- it('month name is a button with monthYearPicker prop', () => {
188
- const wrapper = mountCalendar({ modelValue: TODAY, monthYearPicker: true })
189
-
190
- const header = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]')
191
- expect(header.find('button').exists()).toBe(true)
192
- })
193
-
194
- it('opens month picker when month name is clicked', async () => {
195
- const wrapper = mountCalendar({ modelValue: TODAY, monthYearPicker: true })
196
-
197
- const header = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]')
198
- const monthBtn = header.findAll('button').find((b) => b.text().includes('Mrt'))
199
- await monthBtn!.trigger('click')
200
- await nextTick()
201
-
202
- // Month view uses grid-cols-3
203
- expect(wrapper.find('[class*="grid-cols-3"]').exists()).toBe(true)
204
- })
205
-
206
- it('opens year picker when year is clicked', async () => {
207
- const wrapper = mountCalendar({ modelValue: TODAY, monthYearPicker: true })
208
-
209
- const header = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]')
210
- const buttons = header.findAll('button')
211
- await buttons[1]!.trigger('click')
212
- await nextTick()
213
-
214
- expect(wrapper.find('[class*="snap-y"]').exists()).toBe(true)
215
- })
216
-
217
- it('returns to days view after selecting a month', async () => {
218
- const wrapper = mountCalendar({ modelValue: TODAY, monthYearPicker: true })
219
-
220
- const header = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]')
221
- await header.find('button').trigger('click')
222
- await nextTick()
223
-
224
- const monthButtons = wrapper.findAll('[class*="grid-cols-3"] button')
225
- await monthButtons[0]!.trigger('click')
226
- await nextTick()
227
-
228
- expect(wrapper.find('[class*="grid-cols-7"]').exists()).toBe(true)
229
- expect(wrapper.find('[class*="grid-cols-3"]').exists()).toBe(false)
230
- })
231
-
232
- it('returns to months view after selecting a year', async () => {
233
- const wrapper = mountCalendar({ modelValue: TODAY, monthYearPicker: true })
234
-
235
- const header = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]')
236
- await header.findAll('button')[1]!.trigger('click')
237
- await nextTick()
238
-
239
- const yearButtons = wrapper.findAll('[class*="snap-center"]')
240
- await yearButtons[0]!.trigger('click')
241
- await nextTick()
242
-
243
- expect(wrapper.find('[class*="grid-cols-3"]').exists()).toBe(true)
244
- expect(wrapper.find('[class*="snap-y"]').exists()).toBe(false)
245
- })
246
-
247
- it('selects correct date from month picker', async () => {
248
- const wrapper = mountCalendar({ modelValue: TODAY, monthYearPicker: true })
249
-
250
- const header = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]')
251
- await header.find('button').trigger('click')
252
- await nextTick()
253
-
254
- const monthButtons = wrapper.findAll('[class*="grid-cols-3"] button')
255
- await monthButtons[0]?.trigger('click')
256
- await nextTick()
257
-
258
- const labelAfter = wrapper.find('[class*="cornflower-blue-600"]').text()
259
- expect(labelAfter).toContain('Jan')
260
- })
261
-
262
- it('selects correct year from year drum', async () => {
263
- const wrapper = mountCalendar({ modelValue: TODAY, monthYearPicker: true })
264
-
265
- const header = wrapper.find('[class*="flex"][class*="items-center"][class*="gap-1"]')
266
- const headerButtons = header.findAll('button')
267
- await headerButtons[1]?.trigger('click')
268
- await nextTick()
269
-
270
- const yearButtons = wrapper.findAll('[class*="snap-center"]')
271
- const targetYear = (YEAR - 1).toString()
272
- const targetBtn = yearButtons.find((btn) => btn.text().includes(targetYear))
273
- await targetBtn!.trigger('click')
274
- await nextTick()
275
-
276
- const labelAfter = wrapper.find('[class*="cornflower-blue-600"]').text()
277
- expect(labelAfter).toContain(targetYear)
278
- })
279
- })