@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.
- package/dist/components/editor/molecules/EditorColor/EditorColor.vue.d.ts +7 -5
- package/dist/components/editor/molecules/EditorColor/_ColorField.vue.d.ts +4 -1
- package/dist/components/organisms/ColorPicker/ColorPicker.vue.d.ts +3 -1
- package/dist/index.js +95 -172
- package/dist/nuxt.js +0 -1
- package/package.json +38 -36
- package/src/attribute-contract.test.ts +151 -0
- package/src/components/atoms/AnimatedNumber/AnimatedNumber.stories.ts +36 -0
- package/src/components/atoms/AnimatedNumber/AnimatedNumber.test.ts +21 -0
- package/src/components/atoms/Avatar/Avatar.stories.ts +122 -0
- package/src/components/atoms/Avatar/Avatar.test.ts +134 -0
- package/src/components/atoms/Button/Button.stories.ts +290 -0
- package/src/components/atoms/Button/Button.test.ts +406 -0
- package/src/components/atoms/Card/Card.stories.ts +45 -0
- package/src/components/atoms/Card/Card.test.ts +46 -0
- package/src/components/atoms/Checkbox/Checkbox.stories.ts +35 -0
- package/src/components/atoms/Checkbox/Checkbox.test.ts +55 -0
- package/src/components/atoms/Chip/Chip.stories.ts +274 -0
- package/src/components/atoms/Chip/Chip.test.ts +230 -0
- package/src/components/atoms/EnergyLabel/EnergyLabel.stories.ts +189 -0
- package/src/components/atoms/EnergyLabel/EnergyLabel.test.ts +100 -0
- package/src/components/atoms/FloatingPanel/FloatingPanel.stories.ts +677 -0
- package/src/components/atoms/FloatingPanel/FloatingPanel.test.ts +864 -0
- package/src/components/atoms/Icon/Icon.stories.ts +63 -0
- package/src/components/atoms/Icon/Icon.test.ts +89 -0
- package/src/components/atoms/Loader/Loader.stories.ts +36 -0
- package/src/components/atoms/Loader/Loader.test.ts +40 -0
- package/src/components/atoms/PickerItem/PickerItem.stories.ts +172 -0
- package/src/components/atoms/PickerItem/PickerItem.test.ts +142 -0
- package/src/components/atoms/PickerPreview/PickerPreview.stories.ts +183 -0
- package/src/components/atoms/PickerPreview/PickerPreview.test.ts +137 -0
- package/src/components/atoms/PresenceIndicator/PresenceIndicator.stories.ts +21 -0
- package/src/components/atoms/PropertyListing/PropertyListing.stories.ts +38 -0
- package/src/components/atoms/PropertyListing/PropertyListing.test.ts +46 -0
- package/src/components/atoms/SelectableOption/SelectableOption.stories.ts +131 -0
- package/src/components/atoms/SelectableOption/SelectableOption.test.ts +96 -0
- package/src/components/atoms/Skeleton/Skeleton.stories.ts +27 -0
- package/src/components/atoms/Skeleton/Skeleton.test.ts +28 -0
- package/src/components/atoms/Slider/Slider.stories.ts +151 -0
- package/src/components/atoms/Slider/Slider.test.ts +191 -0
- package/src/components/atoms/Squircle/Squircle.stories.ts +114 -0
- package/src/components/atoms/Squircle/Squircle.test.ts +70 -0
- package/src/components/atoms/Switch/Switch.stories.ts +91 -0
- package/src/components/atoms/Switch/Switch.test.ts +183 -0
- package/src/components/atoms/Tooltip/Tooltip.stories.ts +158 -0
- package/src/components/atoms/Tooltip/Tooltip.test.ts +134 -0
- package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.stories.ts +124 -0
- package/src/components/editor/atoms/EditorDimensionField/EditorDimensionField.test.ts +464 -0
- package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.stories.ts +266 -0
- package/src/components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.test.ts +421 -0
- package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.stories.ts +365 -0
- package/src/components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.test.ts +484 -0
- package/src/components/editor/composables/useRadioGrid.test.ts +261 -0
- package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.stories.ts +73 -0
- package/src/components/editor/molecules/EditorAlignItems/EditorAlignItems.test.ts +189 -0
- package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.stories.ts +309 -0
- package/src/components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.test.ts +359 -0
- package/src/components/editor/molecules/EditorAnchor/EditorAnchor.stories.ts +138 -0
- package/src/components/editor/molecules/EditorAnchor/EditorAnchor.test.ts +344 -0
- package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.stories.ts +87 -0
- package/src/components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.test.ts +262 -0
- package/src/components/editor/molecules/EditorBlur/EditorBlur.stories.ts +115 -0
- package/src/components/editor/molecules/EditorBlur/EditorBlur.test.ts +243 -0
- package/src/components/editor/molecules/EditorBorder/EditorBorder.stories.ts +187 -0
- package/src/components/editor/molecules/EditorBorder/EditorBorder.test.ts +456 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.stories.ts +633 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.test.ts +776 -0
- package/src/components/editor/molecules/EditorColor/EditorColor.vue +7 -5
- package/src/components/editor/molecules/EditorColor/EditorColorGradient.test.ts +832 -0
- package/src/components/editor/molecules/EditorColor/_ColorField.vue +20 -15
- package/src/components/editor/molecules/EditorColor/gradientClass.test.ts +303 -0
- package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.stories.ts +72 -0
- package/src/components/editor/molecules/EditorDashPattern/EditorDashPattern.test.ts +124 -0
- package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.stories.ts +46 -0
- package/src/components/editor/molecules/EditorFlexDirection/EditorFlexDirection.test.ts +97 -0
- package/src/components/editor/molecules/EditorFontSize/EditorFontSize.stories.ts +248 -0
- package/src/components/editor/molecules/EditorFontSize/EditorFontSize.test.ts +288 -0
- package/src/components/editor/molecules/EditorGap/EditorGap.stories.ts +129 -0
- package/src/components/editor/molecules/EditorGap/EditorGap.test.ts +253 -0
- package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.stories.ts +68 -0
- package/src/components/editor/molecules/EditorHeadingType/EditorHeadingType.test.ts +150 -0
- package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.stories.ts +71 -0
- package/src/components/editor/molecules/EditorJustifyContent/EditorJustifyContent.test.ts +161 -0
- package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.stories.ts +204 -0
- package/src/components/editor/molecules/EditorLetterSpacing/EditorLetterSpacing.test.ts +238 -0
- package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.stories.ts +257 -0
- package/src/components/editor/molecules/EditorLineHeight/EditorLineHeight.test.ts +286 -0
- package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.stories.ts +135 -0
- package/src/components/editor/molecules/EditorLineStyle/EditorLineStyle.test.ts +230 -0
- package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.stories.ts +154 -0
- package/src/components/editor/molecules/EditorMaxWidth/EditorMaxWidth.test.ts +112 -0
- package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.stories.ts +206 -0
- package/src/components/editor/molecules/EditorOverrideStatus/EditorOverrideStatus.test.ts +332 -0
- package/src/components/editor/molecules/EditorPadding/EditorPadding.stories.ts +374 -0
- package/src/components/editor/molecules/EditorPadding/EditorPadding.test.ts +712 -0
- package/src/components/editor/molecules/EditorRadius/EditorRadius.stories.ts +64 -0
- package/src/components/editor/molecules/EditorRadius/EditorRadius.test.ts +137 -0
- package/src/components/editor/molecules/EditorShadow/EditorShadow.stories.ts +103 -0
- package/src/components/editor/molecules/EditorShadow/EditorShadow.test.ts +168 -0
- package/src/components/editor/molecules/EditorSize/EditorSize.stories.ts +84 -0
- package/src/components/editor/molecules/EditorSize/EditorSize.test.ts +253 -0
- package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.stories.ts +49 -0
- package/src/components/editor/molecules/EditorSizeLimit/EditorSizeLimit.test.ts +43 -0
- package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.stories.ts +69 -0
- package/src/components/editor/molecules/EditorTextAlign/EditorTextAlign.test.ts +155 -0
- package/src/components/molecules/Announcement/Announcement.stories.ts +224 -0
- package/src/components/molecules/Announcement/Announcement.test.ts +161 -0
- package/src/components/molecules/ContactInfoCard/ContactInfoCard.stories.ts +99 -0
- package/src/components/molecules/ContactInfoCard/ContactInfoCard.test.ts +70 -0
- package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.stories.ts +148 -0
- package/src/components/molecules/CustomerJourneyTile/CustomerJourneyTile.test.ts +271 -0
- package/src/components/molecules/DoughnutChart/DoughnutChart.stories.ts +45 -0
- package/src/components/molecules/DoughnutChart/DoughnutChart.test.ts +205 -0
- package/src/components/molecules/InfoBlock/InfoBlock.stories.ts +84 -0
- package/src/components/molecules/InfoBlock/InfoBlock.test.ts +101 -0
- package/src/components/molecules/InputField/InputField.stories.ts +117 -0
- package/src/components/molecules/InputField/InputField.test.ts +138 -0
- package/src/components/molecules/InputSelect/InputSelect.stories.ts +116 -0
- package/src/components/molecules/InputSelect/InputSelect.test.ts +127 -0
- package/src/components/molecules/ModalFooter/ModalFooter.stories.ts +204 -0
- package/src/components/molecules/ModalFooter/ModalFooter.test.ts +276 -0
- package/src/components/molecules/PageCard/PageCard.stories.ts +100 -0
- package/src/components/molecules/PageCard/PageCard.test.ts +91 -0
- package/src/components/molecules/PageContainer/PageContainer.stories.ts +90 -0
- package/src/components/molecules/PageContainer/PageContainer.test.ts +50 -0
- package/src/components/molecules/PageMockup/PageMockup.stories.ts +38 -0
- package/src/components/molecules/PageMockup/PageMockup.test.ts +20 -0
- package/src/components/molecules/PageTypeTile/PageTypeTile.stories.ts +63 -0
- package/src/components/molecules/PageTypeTile/PageTypeTile.test.ts +41 -0
- package/src/components/molecules/Pagination/Pagination.stories.ts +72 -0
- package/src/components/molecules/Pagination/Pagination.test.ts +130 -0
- package/src/components/molecules/PickerGroup/PickerGroup.stories.ts +90 -0
- package/src/components/molecules/PickerGroup/PickerGroup.test.ts +145 -0
- package/src/components/molecules/PickerList/PickerList.stories.ts +436 -0
- package/src/components/molecules/PickerList/PickerList.test.ts +870 -0
- package/src/components/molecules/PlaceholderInput/PlaceholderInput.stories.ts +48 -0
- package/src/components/molecules/PlaceholderInput/PlaceholderInput.test.ts +107 -0
- package/src/components/molecules/PropertyCard/PropertyCard.stories.ts +46 -0
- package/src/components/molecules/PropertyCard/PropertyCard.test.ts +59 -0
- package/src/components/molecules/Select/Select.stories.ts +264 -0
- package/src/components/molecules/Select/Select.test.ts +315 -0
- package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.stories.ts +84 -0
- package/src/components/molecules/SidebarMenuItem/SidebarMenuItem.test.ts +103 -0
- package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.stories.ts +11 -0
- package/src/components/molecules/SkeletonPropertyCard/SkeletonPropertyCard.test.ts +38 -0
- package/src/components/molecules/SocialIcons/SocialIcons.stories.ts +28 -0
- package/src/components/molecules/SocialIcons/SocialIcons.test.ts +58 -0
- package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.stories.ts +11 -0
- package/src/components/molecules/SocialMediaCustomTemplate/SocialMediaCustomTemplate.test.ts +116 -0
- package/src/components/molecules/SocialMediaType/SocialMediaType.stories.ts +37 -0
- package/src/components/molecules/SocialMediaType/SocialMediaType.test.ts +126 -0
- package/src/components/molecules/Source/Source.stories.ts +35 -0
- package/src/components/molecules/Source/Source.test.ts +45 -0
- package/src/components/molecules/TabbedContent/TabbedContent.stories.ts +96 -0
- package/src/components/molecules/TabbedContent/TabbedContent.test.ts +109 -0
- package/src/components/molecules/Tabs/Tabs.stories.ts +230 -0
- package/src/components/molecules/Tabs/Tabs.test.ts +280 -0
- package/src/components/molecules/TemplateCard/TemplateCard.stories.ts +327 -0
- package/src/components/molecules/TemplateCard/TemplateCard.test.ts +339 -0
- package/src/components/molecules/Textarea/Textarea.stories.ts +216 -0
- package/src/components/molecules/Textarea/Textarea.test.ts +191 -0
- package/src/components/molecules/TimelineEvent/TimelineEvent.stories.ts +319 -0
- package/src/components/molecules/TimelineEvent/TimelineEvent.test.ts +224 -0
- package/src/components/molecules/TimelineLanes/TimelineLanes.stories.ts +29 -0
- package/src/components/molecules/TimelineLanes/TimelineLanes.test.ts +30 -0
- package/src/components/molecules/Toast/Toast.stories.ts +110 -0
- package/src/components/molecules/Toast/Toast.test.ts +103 -0
- package/src/components/molecules/ToggleGroup/ToggleGroup.stories.ts +297 -0
- package/src/components/molecules/ToggleGroup/ToggleGroup.test.ts +578 -0
- package/src/components/molecules/Tree/Tree.stories.ts +884 -0
- package/src/components/molecules/Tree/Tree.test.ts +1247 -0
- package/src/components/molecules/Tree/tree-drop.test.ts +223 -0
- package/src/components/organisms/Calendar/Calendar.stories.ts +108 -0
- package/src/components/organisms/Calendar/Calendar.test.ts +279 -0
- package/src/components/organisms/Calendar/_CalendarDayView.test.ts +104 -0
- package/src/components/organisms/Calendar/_CalendarHeader.test.ts +86 -0
- package/src/components/organisms/Calendar/_CalendarMonthView.test.ts +52 -0
- package/src/components/organisms/Calendar/_CalendarTimePicker.test.ts +89 -0
- package/src/components/organisms/Calendar/_CalendarYearView.test.ts +56 -0
- package/src/components/organisms/ColorPicker/ColorPicker.stories.ts +229 -0
- package/src/components/organisms/ColorPicker/ColorPicker.test.ts +696 -0
- package/src/components/organisms/ColorPicker/ColorPicker.vue +12 -3
- package/src/components/organisms/ColorPicker/gradient.test.ts +67 -0
- package/src/components/organisms/ColorPicker/gradientGeometry.test.ts +206 -0
- package/src/components/organisms/Combobox/Combobox.stories.ts +234 -0
- package/src/components/organisms/Combobox/Combobox.test.ts +512 -0
- package/src/components/organisms/Combobox/Combobox.vue +1 -1
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.stories.ts +158 -0
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.test.ts +524 -0
- package/src/components/organisms/ComboboxPanel/ComboboxPanel.vue +1 -1
- package/src/components/organisms/CountrySelect/CountrySelect.stories.ts +139 -0
- package/src/components/organisms/CountrySelect/CountrySelect.test.ts +149 -0
- package/src/components/organisms/DatePicker/DatePicker.stories.ts +163 -0
- package/src/components/organisms/DatePicker/DatePicker.test.ts +214 -0
- package/src/components/organisms/DropdownButton/DropdownButton.stories.ts +123 -0
- package/src/components/organisms/DropdownButton/DropdownButton.test.ts +379 -0
- package/src/components/organisms/FontPicker/FontPicker.stories.ts +476 -0
- package/src/components/organisms/FontPicker/FontPicker.test.ts +873 -0
- package/src/components/organisms/Header/Header.stories.ts +75 -0
- package/src/components/organisms/Header/Header.test.ts +233 -0
- package/src/components/organisms/IconPicker/IconPicker.stories.ts +734 -0
- package/src/components/organisms/IconPicker/IconPicker.test.ts +669 -0
- package/src/components/organisms/IconPicker/_IconPickerGrid.test.ts +184 -0
- package/src/components/organisms/IconPicker/_IconPickerRail.test.ts +102 -0
- package/src/components/organisms/IconPicker/useIconPickerNavigation.test.ts +215 -0
- package/src/components/organisms/Modal/Modal.stories.ts +350 -0
- package/src/components/organisms/Modal/Modal.test.ts +275 -0
- package/src/components/organisms/PickerPanel/PickerPanel.stories.ts +452 -0
- package/src/components/organisms/PickerPanel/PickerPanel.test.ts +358 -0
- package/src/components/organisms/ProgressBar/ProgressBar.stories.ts +100 -0
- package/src/components/organisms/ProgressBar/ProgressBar.test.ts +417 -0
- package/src/components/organisms/ReferenceCard/ReferenceCard.stories.ts +191 -0
- package/src/components/organisms/ReferenceCard/ReferenceCard.test.ts +165 -0
- package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.stories.ts +65 -0
- package/src/components/organisms/SocialMediaTemplate/SocialMediaTemplate.test.ts +466 -0
- package/src/components/organisms/StepperHeader/StepperHeader.stories.ts +91 -0
- package/src/components/organisms/StepperHeader/StepperHeader.test.ts +279 -0
- package/src/components/organisms/Table/Table.stories.ts +332 -0
- package/src/components/organisms/Table/Table.test.ts +320 -0
- package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.stories.ts +198 -0
- package/src/components/organisms/TimelinePhaseblock/TimelinePhaseblock.test.ts +283 -0
- package/src/components/organisms/TimelineTrack/TimelineTrack.stories.ts +120 -0
- package/src/components/organisms/TimelineTrack/TimelineTrack.test.ts +91 -0
- package/src/components/organisms/Toaster/Toaster.stories.ts +51 -0
- package/src/components/organisms/Toaster/Toaster.test.ts +75 -0
- package/src/components/organisms/VideoControls/VideoControls.stories.ts +792 -0
- package/src/components/organisms/VideoControls/VideoControls.test.ts +1056 -0
- package/src/components/organisms/VideoPlayer/VideoPlayer.stories.ts +327 -0
- package/src/components/organisms/VideoPlayer/VideoPlayer.test.ts +439 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import Table from './Table.vue'
|
|
4
|
+
|
|
5
|
+
const columns = [
|
|
6
|
+
{ key: 'name', label: 'Name', sortable: true },
|
|
7
|
+
{ key: 'email', label: 'Email' },
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
const rows = [
|
|
11
|
+
{ name: 'Alice', email: 'alice@example.com' },
|
|
12
|
+
{ name: 'Bob', email: 'bob@example.com' },
|
|
13
|
+
{ name: 'Carol', email: 'carol@example.com' },
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
describe('Table', () => {
|
|
17
|
+
describe('rendering', () => {
|
|
18
|
+
it('renders column headers', () => {
|
|
19
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
20
|
+
expect(wrapper.text()).toContain('Name')
|
|
21
|
+
expect(wrapper.text()).toContain('Email')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('renders row data', () => {
|
|
25
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
26
|
+
expect(wrapper.text()).toContain('Alice')
|
|
27
|
+
expect(wrapper.text()).toContain('bob@example.com')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('renders empty slot when rows is empty', () => {
|
|
31
|
+
const wrapper = mount(Table, { props: { columns, rows: [] } })
|
|
32
|
+
expect(wrapper.text()).toContain('Geen data beschikbaar')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('renders custom empty slot content', () => {
|
|
36
|
+
const wrapper = mount(Table, {
|
|
37
|
+
props: { columns, rows: [] },
|
|
38
|
+
slots: { empty: 'Nothing here' },
|
|
39
|
+
})
|
|
40
|
+
expect(wrapper.text()).toContain('Nothing here')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('renders cell slot content', () => {
|
|
44
|
+
const wrapper = mount(Table, {
|
|
45
|
+
props: { columns, rows },
|
|
46
|
+
slots: { 'cell-name': '<strong>custom</strong>' },
|
|
47
|
+
})
|
|
48
|
+
expect(wrapper.html()).toContain('<strong>custom</strong>')
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe('sorting', () => {
|
|
53
|
+
it('renders sort icon on sortable columns', () => {
|
|
54
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
55
|
+
const headers = wrapper.findAll('[role="columnheader"]')
|
|
56
|
+
const nameHeader = headers[0]
|
|
57
|
+
expect(nameHeader.find('i').exists()).toBe(true)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('does not render sort icon on non-sortable columns', () => {
|
|
61
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
62
|
+
const headers = wrapper.findAll('[role="columnheader"]')
|
|
63
|
+
const emailHeader = headers[1]
|
|
64
|
+
expect(emailHeader.find('i').exists()).toBe(false)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('emits update:sortKey when clicking a sortable header', async () => {
|
|
68
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
69
|
+
const nameHeader = wrapper.findAll('[role="columnheader"]')[0]
|
|
70
|
+
await nameHeader.trigger('click')
|
|
71
|
+
expect(wrapper.emitted('update:sortKey')).toBeTruthy()
|
|
72
|
+
expect(wrapper.emitted('update:sortKey')![0]).toEqual(['name'])
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('emits desc direction when clicking an already sorted column', async () => {
|
|
76
|
+
const wrapper = mount(Table, {
|
|
77
|
+
props: { columns, rows, sortKey: 'name', sortDirection: 'asc' },
|
|
78
|
+
})
|
|
79
|
+
const nameHeader = wrapper.findAll('[role="columnheader"]')[0]
|
|
80
|
+
await nameHeader.trigger('click')
|
|
81
|
+
expect(wrapper.emitted('update:sortDirection')![0]).toEqual(['desc'])
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('does not emit sort when clicking a non-sortable header', async () => {
|
|
85
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
86
|
+
const emailHeader = wrapper.findAll('[role="columnheader"]')[1]
|
|
87
|
+
await emailHeader.trigger('click')
|
|
88
|
+
expect(wrapper.emitted('update:sortKey')).toBeFalsy()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('sets aria-sort to ascending on the active sort column', () => {
|
|
92
|
+
const wrapper = mount(Table, {
|
|
93
|
+
props: { columns, rows, sortKey: 'name', sortDirection: 'asc' },
|
|
94
|
+
})
|
|
95
|
+
const nameHeader = wrapper.findAll('[role="columnheader"]')[0]
|
|
96
|
+
expect(nameHeader.attributes('aria-sort')).toBe('ascending')
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('sets aria-sort to descending on the active sort column', () => {
|
|
100
|
+
const wrapper = mount(Table, {
|
|
101
|
+
props: { columns, rows, sortKey: 'name', sortDirection: 'desc' },
|
|
102
|
+
})
|
|
103
|
+
const nameHeader = wrapper.findAll('[role="columnheader"]')[0]
|
|
104
|
+
expect(nameHeader.attributes('aria-sort')).toBe('descending')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('sets aria-sort to none on an inactive sortable column', () => {
|
|
108
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
109
|
+
const nameHeader = wrapper.findAll('[role="columnheader"]')[0]
|
|
110
|
+
expect(nameHeader.attributes('aria-sort')).toBe('none')
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('selection', () => {
|
|
115
|
+
it('does not render checkboxes when selectable is false', () => {
|
|
116
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
117
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('renders a checkbox per row and one in the header when selectable', () => {
|
|
121
|
+
const wrapper = mount(Table, {
|
|
122
|
+
props: { columns, rows, selectable: true },
|
|
123
|
+
})
|
|
124
|
+
const checkboxes = wrapper.findAll('input[type="checkbox"]')
|
|
125
|
+
expect(checkboxes).toHaveLength(rows.length + 1)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('emits update:modelValue with all rows when select-all is clicked', async () => {
|
|
129
|
+
const wrapper = mount(Table, {
|
|
130
|
+
props: { columns, rows, selectable: true },
|
|
131
|
+
})
|
|
132
|
+
const headerCheckbox = wrapper.find('input[type="checkbox"]')
|
|
133
|
+
await headerCheckbox.setValue(true)
|
|
134
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([rows])
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('emits update:modelValue with empty array when deselecting all', async () => {
|
|
138
|
+
const wrapper = mount(Table, {
|
|
139
|
+
props: { columns, rows, selectable: true, modelValue: [...rows] },
|
|
140
|
+
})
|
|
141
|
+
const headerCheckbox = wrapper.find('input[type="checkbox"]')
|
|
142
|
+
await headerCheckbox.setValue(false)
|
|
143
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([[]])
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('emits update:modelValue with the row when a row checkbox is changed', async () => {
|
|
147
|
+
const wrapper = mount(Table, {
|
|
148
|
+
props: { columns, rows, selectable: true },
|
|
149
|
+
})
|
|
150
|
+
const rowCheckboxes = wrapper.findAll('input[type="checkbox"]').slice(1)
|
|
151
|
+
await rowCheckboxes[0].setValue(true)
|
|
152
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([[rows[0]]])
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('removes row from selection when an already-selected row checkbox is changed', async () => {
|
|
156
|
+
const wrapper = mount(Table, {
|
|
157
|
+
props: { columns, rows, selectable: true, modelValue: [rows[0], rows[1]] },
|
|
158
|
+
})
|
|
159
|
+
const rowCheckboxes = wrapper.findAll('input[type="checkbox"]').slice(1)
|
|
160
|
+
await rowCheckboxes[0].setValue(false)
|
|
161
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([[rows[1]]])
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
describe('single selection', () => {
|
|
165
|
+
it('does not render select-all checkbox in single mode', () => {
|
|
166
|
+
const wrapper = mount(Table, {
|
|
167
|
+
props: { columns, rows, selectable: true, selectionMode: 'single' },
|
|
168
|
+
})
|
|
169
|
+
const checkboxes = wrapper.findAll('input[type="checkbox"]')
|
|
170
|
+
expect(checkboxes).toHaveLength(rows.length)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('replaces selection when a different row is selected in single mode', async () => {
|
|
174
|
+
const wrapper = mount(Table, {
|
|
175
|
+
props: {
|
|
176
|
+
columns,
|
|
177
|
+
rows,
|
|
178
|
+
selectable: true,
|
|
179
|
+
selectionMode: 'single',
|
|
180
|
+
modelValue: [rows[0]],
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
const rowCheckboxes = wrapper.findAll('input[type="checkbox"]')
|
|
184
|
+
await rowCheckboxes[1].setValue(true)
|
|
185
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([[rows[1]]])
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('unselects row when clicking the checkbox of a selected row in single mode', async () => {
|
|
189
|
+
const wrapper = mount(Table, {
|
|
190
|
+
props: {
|
|
191
|
+
columns,
|
|
192
|
+
rows,
|
|
193
|
+
selectable: true,
|
|
194
|
+
selectionMode: 'single',
|
|
195
|
+
modelValue: [rows[0]],
|
|
196
|
+
},
|
|
197
|
+
})
|
|
198
|
+
const rowCheckboxes = wrapper.findAll('input[type="checkbox"]')
|
|
199
|
+
await rowCheckboxes[0].setValue(false)
|
|
200
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([[]])
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
describe('row click', () => {
|
|
206
|
+
it('emits row-click with the row data when clicking a row', async () => {
|
|
207
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
208
|
+
const dataRows = wrapper.findAll('[role="row"]').slice(1)
|
|
209
|
+
await dataRows[0].trigger('click')
|
|
210
|
+
expect(wrapper.emitted('row-click')).toBeTruthy()
|
|
211
|
+
expect(wrapper.emitted('row-click')![0]).toEqual([rows[0]])
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('does not toggle selection when clicking a row directly', async () => {
|
|
215
|
+
const wrapper = mount(Table, {
|
|
216
|
+
props: { columns, rows, selectable: true, modelValue: [] },
|
|
217
|
+
})
|
|
218
|
+
const dataRows = wrapper.findAll('[role="row"]').slice(1)
|
|
219
|
+
await dataRows[0].trigger('click')
|
|
220
|
+
expect(wrapper.emitted('update:modelValue')).toBeFalsy()
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('does not emit row-click or selection when clicking a button inside a row', async () => {
|
|
224
|
+
const wrapper = mount(Table, {
|
|
225
|
+
props: { columns, rows, selectable: true, modelValue: [] },
|
|
226
|
+
slots: { 'cell-name': '<button>action</button>' },
|
|
227
|
+
})
|
|
228
|
+
await wrapper.find('button').trigger('click')
|
|
229
|
+
expect(wrapper.emitted('row-click')).toBeFalsy()
|
|
230
|
+
expect(wrapper.emitted('update:modelValue')).toBeFalsy()
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('does not emit row-click or selection when clicking a link inside a row', async () => {
|
|
234
|
+
const wrapper = mount(Table, {
|
|
235
|
+
props: { columns, rows, selectable: true, modelValue: [] },
|
|
236
|
+
slots: { 'cell-name': '<a href="#">link</a>' },
|
|
237
|
+
})
|
|
238
|
+
await wrapper.find('a').trigger('click')
|
|
239
|
+
expect(wrapper.emitted('row-click')).toBeFalsy()
|
|
240
|
+
expect(wrapper.emitted('update:modelValue')).toBeFalsy()
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('emits row-click but not update:modelValue when selectable is false', async () => {
|
|
244
|
+
const wrapper = mount(Table, { props: { columns, rows } })
|
|
245
|
+
const dataRows = wrapper.findAll('[role="row"]').slice(1)
|
|
246
|
+
await dataRows[0].trigger('click')
|
|
247
|
+
expect(wrapper.emitted('row-click')).toBeTruthy()
|
|
248
|
+
expect(wrapper.emitted('update:modelValue')).toBeFalsy()
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
describe('selectOnRowClick', () => {
|
|
252
|
+
it('toggles selection when clicking a row if selectOnRowClick is true', async () => {
|
|
253
|
+
const wrapper = mount(Table, {
|
|
254
|
+
props: {
|
|
255
|
+
columns,
|
|
256
|
+
rows,
|
|
257
|
+
selectable: true,
|
|
258
|
+
selectOnRowClick: true,
|
|
259
|
+
modelValue: [],
|
|
260
|
+
},
|
|
261
|
+
})
|
|
262
|
+
const dataRows = wrapper.findAll('[role="row"]').slice(1)
|
|
263
|
+
await dataRows[0].trigger('click')
|
|
264
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([[rows[0]]])
|
|
265
|
+
expect(wrapper.emitted('row-click')).toBeFalsy()
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('adds cursor-pointer class when selectOnRowClick is enabled', () => {
|
|
269
|
+
const wrapper = mount(Table, {
|
|
270
|
+
props: {
|
|
271
|
+
columns,
|
|
272
|
+
rows,
|
|
273
|
+
selectable: true,
|
|
274
|
+
selectOnRowClick: true,
|
|
275
|
+
},
|
|
276
|
+
})
|
|
277
|
+
const dataRows = wrapper.findAll('[role="row"]').slice(1)
|
|
278
|
+
expect(dataRows[0].classes()).toContain('cursor-pointer')
|
|
279
|
+
})
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
describe('loading', () => {
|
|
284
|
+
it('does not render the loading overlay when loading is false', () => {
|
|
285
|
+
const wrapper = mount(Table, { props: { columns, rows, loading: false } })
|
|
286
|
+
expect(wrapper.find('[data-testid="loading-overlay"]').exists()).toBe(false)
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
it('renders the loading overlay when loading is true', () => {
|
|
290
|
+
const wrapper = mount(Table, { props: { columns, rows, loading: true } })
|
|
291
|
+
expect(wrapper.find('[data-testid="loading-overlay"]').exists()).toBe(true)
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it('renders custom loading slot content', () => {
|
|
295
|
+
const wrapper = mount(Table, {
|
|
296
|
+
props: { columns, rows, loading: true },
|
|
297
|
+
slots: { loading: '<span>custom loader</span>' },
|
|
298
|
+
})
|
|
299
|
+
expect(wrapper.text()).toContain('custom loader')
|
|
300
|
+
})
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
describe('keyboard navigation', () => {
|
|
304
|
+
it('moves focus to next row on ArrowDown', async () => {
|
|
305
|
+
const wrapper = mount(Table, { props: { columns, rows }, attachTo: document.body })
|
|
306
|
+
const dataRows = wrapper.findAll('[role="row"]').slice(1)
|
|
307
|
+
await dataRows[0].trigger('keydown', { key: 'ArrowDown' })
|
|
308
|
+
expect(document.activeElement).toBe(dataRows[1].element)
|
|
309
|
+
wrapper.unmount()
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('moves focus to previous row on ArrowUp', async () => {
|
|
313
|
+
const wrapper = mount(Table, { props: { columns, rows }, attachTo: document.body })
|
|
314
|
+
const dataRows = wrapper.findAll('[role="row"]').slice(1)
|
|
315
|
+
await dataRows[1].trigger('keydown', { key: 'ArrowUp' })
|
|
316
|
+
expect(document.activeElement).toBe(dataRows[0].element)
|
|
317
|
+
wrapper.unmount()
|
|
318
|
+
})
|
|
319
|
+
})
|
|
320
|
+
})
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import TimelinePhaseblock from './TimelinePhaseblock.vue'
|
|
3
|
+
import type { TimelinePhase } from '../../molecules/TimelineEvent/TimelineTypes'
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Components/Organisms/Timeline Phaseblock',
|
|
7
|
+
component: TimelinePhaseblock,
|
|
8
|
+
} satisfies Meta<typeof TimelinePhaseblock>
|
|
9
|
+
|
|
10
|
+
type Story = StoryObj<typeof TimelinePhaseblock>
|
|
11
|
+
|
|
12
|
+
const makePhase = (
|
|
13
|
+
value: number,
|
|
14
|
+
entered_at: string | null = '2024-06-01T09:00:00.000Z',
|
|
15
|
+
exited_at: string | null = '2024-07-01T09:00:00.000Z',
|
|
16
|
+
events: TimelinePhase['events'] = [],
|
|
17
|
+
): TimelinePhase => ({
|
|
18
|
+
phase: { value },
|
|
19
|
+
entered_at,
|
|
20
|
+
exited_at,
|
|
21
|
+
events,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const sampleEvent = {
|
|
25
|
+
id: 1,
|
|
26
|
+
type: 'contact_created',
|
|
27
|
+
happened_at: '2024-06-10T10:30:00.000Z',
|
|
28
|
+
medium_type: null,
|
|
29
|
+
url: null,
|
|
30
|
+
page_title: null,
|
|
31
|
+
request_reason: null,
|
|
32
|
+
agenda_item_type: null,
|
|
33
|
+
source: 'system',
|
|
34
|
+
direction: 'outbound',
|
|
35
|
+
description: null,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Single phase at the end of the timeline (no separator shown)
|
|
39
|
+
export const SinglePhase: Story = {
|
|
40
|
+
args: {
|
|
41
|
+
phase: makePhase(1),
|
|
42
|
+
index: 0,
|
|
43
|
+
phases: [makePhase(1)],
|
|
44
|
+
isSplitView: true,
|
|
45
|
+
isLast: true,
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// First phase (index 0) in split view — shows the Outbound/Inbound header
|
|
50
|
+
export const FirstPhaseWithHeader: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
phase: makePhase(4),
|
|
53
|
+
index: 0,
|
|
54
|
+
phases: [makePhase(4), makePhase(2)],
|
|
55
|
+
isSplitView: true,
|
|
56
|
+
isLast: false,
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Phase separator visible — phase progressed upward (arrow up)
|
|
61
|
+
export const PhaseProgressedUp: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
phase: makePhase(4),
|
|
64
|
+
index: 0,
|
|
65
|
+
phases: [makePhase(4), makePhase(2)],
|
|
66
|
+
isSplitView: true,
|
|
67
|
+
isLast: false,
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Phase separator visible — phase regressed downward (arrow down)
|
|
72
|
+
export const PhaseRegressedDown: Story = {
|
|
73
|
+
args: {
|
|
74
|
+
phase: makePhase(2),
|
|
75
|
+
index: 0,
|
|
76
|
+
phases: [makePhase(2), makePhase(4)],
|
|
77
|
+
isSplitView: true,
|
|
78
|
+
isLast: false,
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Last item in a multi-phase list — no separator shown
|
|
83
|
+
export const LastInList: Story = {
|
|
84
|
+
args: {
|
|
85
|
+
phase: makePhase(1),
|
|
86
|
+
index: 1,
|
|
87
|
+
phases: [makePhase(4), makePhase(1)],
|
|
88
|
+
isSplitView: true,
|
|
89
|
+
isLast: true,
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Non-split view — with phase separator
|
|
94
|
+
export const NonSplitView: Story = {
|
|
95
|
+
args: {
|
|
96
|
+
phase: makePhase(3),
|
|
97
|
+
index: 0,
|
|
98
|
+
phases: [makePhase(3), makePhase(1)],
|
|
99
|
+
isSplitView: false,
|
|
100
|
+
isLast: false,
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Non-split view — single/last phase (no separator)
|
|
105
|
+
export const NonSplitViewLast: Story = {
|
|
106
|
+
args: {
|
|
107
|
+
phase: makePhase(3),
|
|
108
|
+
index: 0,
|
|
109
|
+
phases: [makePhase(3)],
|
|
110
|
+
isSplitView: false,
|
|
111
|
+
isLast: true,
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Non-split view — with events
|
|
116
|
+
export const NonSplitViewWithEvents: Story = {
|
|
117
|
+
args: {
|
|
118
|
+
phase: makePhase(4, '2024-06-01T09:00:00.000Z', '2024-07-01T09:00:00.000Z', [
|
|
119
|
+
sampleEvent,
|
|
120
|
+
{ ...sampleEvent, id: 2, type: 'page_viewed', happened_at: '2024-06-12T14:00:00.000Z' },
|
|
121
|
+
]),
|
|
122
|
+
index: 0,
|
|
123
|
+
phases: [makePhase(4), makePhase(2)],
|
|
124
|
+
isSplitView: false,
|
|
125
|
+
isLast: false,
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Phase with events
|
|
130
|
+
export const WithEvents: Story = {
|
|
131
|
+
args: {
|
|
132
|
+
phase: makePhase(4, '2024-06-01T09:00:00.000Z', '2024-07-01T09:00:00.000Z', [
|
|
133
|
+
sampleEvent,
|
|
134
|
+
{ ...sampleEvent, id: 2, type: 'page_viewed', happened_at: '2024-06-12T14:00:00.000Z' },
|
|
135
|
+
{
|
|
136
|
+
...sampleEvent,
|
|
137
|
+
id: 3,
|
|
138
|
+
type: 'whatsapp_message_received',
|
|
139
|
+
direction: 'inbound',
|
|
140
|
+
happened_at: '2024-06-15T11:00:00.000Z',
|
|
141
|
+
},
|
|
142
|
+
]),
|
|
143
|
+
index: 0,
|
|
144
|
+
phases: [makePhase(4), makePhase(2)],
|
|
145
|
+
isSplitView: true,
|
|
146
|
+
isLast: false,
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// All phases shown together (newest first)
|
|
151
|
+
export const FullTimeline: Story = {
|
|
152
|
+
args: {
|
|
153
|
+
phase: makePhase(5, '2024-08-01T09:00:00.000Z', null, [
|
|
154
|
+
{ ...sampleEvent, id: 4, type: 'proposal_accepted', happened_at: '2024-08-05T10:00:00.000Z' },
|
|
155
|
+
]),
|
|
156
|
+
index: 0,
|
|
157
|
+
phases: [
|
|
158
|
+
makePhase(5, '2024-08-01T09:00:00.000Z', null),
|
|
159
|
+
makePhase(4, '2024-06-01T09:00:00.000Z', '2024-08-01T09:00:00.000Z'),
|
|
160
|
+
makePhase(1, '2024-01-01T09:00:00.000Z', '2024-06-01T09:00:00.000Z'),
|
|
161
|
+
],
|
|
162
|
+
isSplitView: true,
|
|
163
|
+
isLast: false,
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Phase with no entered_at or exited_at dates
|
|
168
|
+
export const NoDates: Story = {
|
|
169
|
+
args: {
|
|
170
|
+
phase: makePhase(2, null, null),
|
|
171
|
+
index: 0,
|
|
172
|
+
phases: [makePhase(2, null, null), makePhase(1, null, null)],
|
|
173
|
+
isSplitView: true,
|
|
174
|
+
isLast: false,
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Inactive phase (value 0)
|
|
179
|
+
export const InactivePhase: Story = {
|
|
180
|
+
args: {
|
|
181
|
+
phase: makePhase(0),
|
|
182
|
+
index: 0,
|
|
183
|
+
phases: [makePhase(0)],
|
|
184
|
+
isSplitView: true,
|
|
185
|
+
isLast: true,
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Wonen phase (value 6 — highest)
|
|
190
|
+
export const WonenPhase: Story = {
|
|
191
|
+
args: {
|
|
192
|
+
phase: makePhase(6),
|
|
193
|
+
index: 0,
|
|
194
|
+
phases: [makePhase(6), makePhase(5)],
|
|
195
|
+
isSplitView: true,
|
|
196
|
+
isLast: false,
|
|
197
|
+
},
|
|
198
|
+
}
|