@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,379 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
import type { IconName } from '@awesome.me/kit-37b149f3ef/icons'
|
|
5
|
+
import DropdownButton, { type NexxtDropdownButtonProps } from './DropdownButton.vue'
|
|
6
|
+
|
|
7
|
+
vi.mock('@floating-ui/vue', async (importOriginal) => {
|
|
8
|
+
const actual = await importOriginal<typeof import('@floating-ui/vue')>()
|
|
9
|
+
return {
|
|
10
|
+
...actual,
|
|
11
|
+
useFloating: () => ({
|
|
12
|
+
floatingStyles: ref({}),
|
|
13
|
+
placement: ref('bottom-start'),
|
|
14
|
+
isPositioned: ref(true),
|
|
15
|
+
}),
|
|
16
|
+
autoUpdate: () => () => {},
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const Teleport = { template: '<div><slot /></div>' }
|
|
21
|
+
const stubs = {
|
|
22
|
+
NexxtIcon: { template: '<span :data-name="name" />', props: ['name'] },
|
|
23
|
+
Teleport,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const defaultProps: NexxtDropdownButtonProps = {
|
|
27
|
+
label: 'Actions',
|
|
28
|
+
action: '/main-action',
|
|
29
|
+
options: [
|
|
30
|
+
{ label: 'Edit', action: '/edit', icon: 'pencil' as IconName },
|
|
31
|
+
{ label: 'Delete', action: '/delete', icon: 'trash-can' as IconName },
|
|
32
|
+
],
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const mountDropdown = (props: Partial<NexxtDropdownButtonProps> = {}) =>
|
|
36
|
+
mount(DropdownButton, {
|
|
37
|
+
props: { ...defaultProps, ...props } as typeof defaultProps,
|
|
38
|
+
global: { stubs },
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const iconOnlyDefaults = {
|
|
42
|
+
iconOnly: true as const,
|
|
43
|
+
triggerIcon: 'ellipsis-vertical' as IconName,
|
|
44
|
+
options: [
|
|
45
|
+
{ label: 'Edit', action: '/edit', icon: 'pencil' as IconName },
|
|
46
|
+
{ label: 'Delete', action: '/delete', icon: 'trash-can' as IconName },
|
|
47
|
+
],
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const mountIconOnly = (props: Partial<NexxtDropdownButtonProps> = {}) =>
|
|
51
|
+
mount(DropdownButton, {
|
|
52
|
+
props: { ...iconOnlyDefaults, ...props } as typeof iconOnlyDefaults,
|
|
53
|
+
global: { stubs },
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
describe('DropdownButton', () => {
|
|
57
|
+
describe('checkbox options are controlled by the selected prop', () => {
|
|
58
|
+
const optionsWith = (selected: boolean) => [{ label: 'A', checkbox: true, selected }]
|
|
59
|
+
|
|
60
|
+
const openMenu = async () => {
|
|
61
|
+
const wrapper = mount(DropdownButton, {
|
|
62
|
+
props: { label: 'Filter', options: optionsWith(true) } as never,
|
|
63
|
+
global: { stubs: { Teleport } },
|
|
64
|
+
})
|
|
65
|
+
await wrapper.find('button').trigger('click')
|
|
66
|
+
return wrapper
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const checkedState = (wrapper: ReturnType<typeof mount>) =>
|
|
70
|
+
wrapper.findAll('[role="menuitemcheckbox"]')[0]?.attributes('aria-checked')
|
|
71
|
+
|
|
72
|
+
it('lets the parent reset an option the user toggled', async () => {
|
|
73
|
+
const wrapper = await openMenu()
|
|
74
|
+
expect(checkedState(wrapper)).toBe('true')
|
|
75
|
+
|
|
76
|
+
await wrapper.findAll('[role="menuitemcheckbox"]')[0]?.trigger('click')
|
|
77
|
+
expect(checkedState(wrapper)).toBe('false')
|
|
78
|
+
|
|
79
|
+
// The parent insists the option is selected again. Internal toggle state
|
|
80
|
+
// used to shadow the prop permanently, so this stayed 'false' and a
|
|
81
|
+
// filter menu could never be reset from outside.
|
|
82
|
+
await wrapper.setProps({ options: optionsWith(true) } as never)
|
|
83
|
+
expect(checkedState(wrapper)).toBe('true')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('still responds immediately to a click before the parent echoes it back', async () => {
|
|
87
|
+
const wrapper = await openMenu()
|
|
88
|
+
await wrapper.findAll('[role="menuitemcheckbox"]')[0]?.trigger('click')
|
|
89
|
+
expect(checkedState(wrapper)).toBe('false')
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('accessible name of the trigger', () => {
|
|
94
|
+
const ariaLabelOf = (props: Record<string, unknown>) =>
|
|
95
|
+
mount(DropdownButton, {
|
|
96
|
+
props: props as never,
|
|
97
|
+
global: { stubs: { Teleport } },
|
|
98
|
+
})
|
|
99
|
+
.find('button')
|
|
100
|
+
.attributes('aria-label')
|
|
101
|
+
|
|
102
|
+
it('never renders undefined when iconOnly has no label', () => {
|
|
103
|
+
const name = ariaLabelOf({ iconOnly: true, icon: 'ellipsis', options: [{ label: 'A' }] })
|
|
104
|
+
expect(name).toBeTruthy()
|
|
105
|
+
expect(name).not.toContain('undefined')
|
|
106
|
+
expect(name).toBe('Menu openen')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('uses the visible label when there is one', () => {
|
|
110
|
+
expect(ariaLabelOf({ iconOnly: true, label: 'Acties', options: [{ label: 'A' }] })).toBe(
|
|
111
|
+
'Acties menu openen',
|
|
112
|
+
)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('lets a consumer supply the accessible name', () => {
|
|
116
|
+
expect(
|
|
117
|
+
ariaLabelOf({ iconOnly: true, ariaLabel: 'Meer opties', options: [{ label: 'A' }] }),
|
|
118
|
+
).toBe('Meer opties')
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('rendering', () => {
|
|
123
|
+
it('renders the main label', () => {
|
|
124
|
+
const wrapper = mountDropdown()
|
|
125
|
+
expect(wrapper.text()).toContain('Actions')
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('renders the main icon when provided', () => {
|
|
129
|
+
const wrapper = mountDropdown({ icon: 'download' })
|
|
130
|
+
expect(wrapper.find('[data-name="download"]').exists()).toBe(true)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('renders the primary variant by default', () => {
|
|
134
|
+
const wrapper = mountDropdown()
|
|
135
|
+
expect(wrapper.find('.bg-button-bg-primary-default').exists()).toBe(true)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('renders the secondary variant when specified', () => {
|
|
139
|
+
const wrapper = mountDropdown({ variant: 'secondary' })
|
|
140
|
+
expect(wrapper.find('.bg-button-bg-secondary-default').exists()).toBe(true)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('does not show the dropdown menu by default', () => {
|
|
144
|
+
const wrapper = mountDropdown()
|
|
145
|
+
expect(wrapper.find('[role="menu"]').exists()).toBe(false)
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
describe('interactions', () => {
|
|
150
|
+
it('emits navigate when main button is clicked with a string action', async () => {
|
|
151
|
+
const wrapper = mountDropdown()
|
|
152
|
+
const mainButton = wrapper.findAll('button')[0]
|
|
153
|
+
await mainButton.trigger('click')
|
|
154
|
+
|
|
155
|
+
expect(wrapper.emitted('navigate')).toBeTruthy()
|
|
156
|
+
expect(wrapper.emitted('navigate')?.[0]).toEqual(['/main-action'])
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('executes function when main button is clicked with function action', async () => {
|
|
160
|
+
const action = vi.fn()
|
|
161
|
+
const wrapper = mountDropdown({ action })
|
|
162
|
+
const mainButton = wrapper.findAll('button')[0]
|
|
163
|
+
await mainButton.trigger('click')
|
|
164
|
+
|
|
165
|
+
expect(action).toHaveBeenCalled()
|
|
166
|
+
expect(wrapper.emitted('navigate')).toBeFalsy()
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('toggles the dropdown menu when chevron is clicked', async () => {
|
|
170
|
+
const wrapper = mountDropdown()
|
|
171
|
+
const toggleButton = wrapper.findAll('button')[1]
|
|
172
|
+
|
|
173
|
+
await toggleButton.trigger('click')
|
|
174
|
+
expect(wrapper.find('[role="menu"]').exists()).toBe(true)
|
|
175
|
+
expect(toggleButton.attributes('aria-expanded')).toBe('true')
|
|
176
|
+
|
|
177
|
+
await toggleButton.trigger('click')
|
|
178
|
+
expect(wrapper.find('[role="menu"]').exists()).toBe(false)
|
|
179
|
+
expect(toggleButton.attributes('aria-expanded')).toBe('false')
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('applies selected style to selected options', async () => {
|
|
183
|
+
const wrapper = mountDropdown({
|
|
184
|
+
options: [
|
|
185
|
+
{ label: 'Edit', action: '/edit', selected: true },
|
|
186
|
+
{ label: 'Delete', action: '/delete' },
|
|
187
|
+
],
|
|
188
|
+
})
|
|
189
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
190
|
+
|
|
191
|
+
const items = wrapper.findAll('[role="menuitem"]')
|
|
192
|
+
expect(items[0].classes()).toContain('bg-cornflower-blue-100')
|
|
193
|
+
expect(items[1].classes()).not.toContain('bg-cornflower-blue-100')
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('closes the dropdown and emits navigate when an option is clicked', async () => {
|
|
197
|
+
const wrapper = mountDropdown()
|
|
198
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
199
|
+
|
|
200
|
+
const option = wrapper.find('[role="menuitem"]')
|
|
201
|
+
await option.trigger('click')
|
|
202
|
+
|
|
203
|
+
expect(wrapper.emitted('navigate')).toBeTruthy()
|
|
204
|
+
expect(wrapper.emitted('navigate')?.[0]).toEqual(['/edit'])
|
|
205
|
+
expect(wrapper.find('[role="menu"]').exists()).toBe(false)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe('placement', () => {
|
|
210
|
+
it('uses bottom-end placement by default (right-aligned)', () => {
|
|
211
|
+
const wrapper = mountDropdown()
|
|
212
|
+
const panelEl = wrapper.findComponent({ name: 'NexxtFloatingPanel' })
|
|
213
|
+
expect(panelEl.props('placement')).toBe('bottom-end')
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('uses bottom-start placement when placement is set to left', () => {
|
|
217
|
+
const wrapper = mountDropdown({ placement: 'left' })
|
|
218
|
+
const panelEl = wrapper.findComponent({ name: 'NexxtFloatingPanel' })
|
|
219
|
+
expect(panelEl.props('placement')).toBe('bottom-start')
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
describe('accessibility', () => {
|
|
224
|
+
it('has correct ARIA roles', async () => {
|
|
225
|
+
const wrapper = mountDropdown()
|
|
226
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
227
|
+
|
|
228
|
+
expect(wrapper.find('[role="menu"]').exists()).toBe(true)
|
|
229
|
+
expect(wrapper.find('[role="menuitem"]').exists()).toBe(true)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('has aria-haspopup on the toggle button', () => {
|
|
233
|
+
const wrapper = mountDropdown()
|
|
234
|
+
const toggleButton = wrapper.findAll('button')[1]
|
|
235
|
+
expect(toggleButton.attributes('aria-haspopup')).toBe('menu')
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
describe('grouped options', () => {
|
|
240
|
+
const groupedProps: Partial<NexxtDropdownButtonProps> = {
|
|
241
|
+
options: [
|
|
242
|
+
{
|
|
243
|
+
title: 'File',
|
|
244
|
+
items: [
|
|
245
|
+
{ label: 'Download PDF', action: '/pdf', icon: 'file-pdf' as IconName },
|
|
246
|
+
{ label: 'Download CSV', action: '/csv', icon: 'file-csv' as IconName },
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
title: 'Manage',
|
|
251
|
+
items: [
|
|
252
|
+
{ label: 'Edit', action: '/edit', icon: 'pencil' as IconName },
|
|
253
|
+
{ label: 'Delete', action: '/delete', icon: 'trash-can' as IconName },
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
it('renders group titles', async () => {
|
|
260
|
+
const wrapper = mountDropdown(groupedProps)
|
|
261
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
262
|
+
|
|
263
|
+
const text = wrapper.text()
|
|
264
|
+
expect(text).toContain('File')
|
|
265
|
+
expect(text).toContain('Manage')
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('renders all items across groups', async () => {
|
|
269
|
+
const wrapper = mountDropdown(groupedProps)
|
|
270
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
271
|
+
|
|
272
|
+
const items = wrapper.findAll('[role="menuitem"]')
|
|
273
|
+
expect(items).toHaveLength(4)
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
it('emits navigate when a grouped option is clicked', async () => {
|
|
277
|
+
const wrapper = mountDropdown(groupedProps)
|
|
278
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
279
|
+
|
|
280
|
+
await wrapper.find('[role="menuitem"]').trigger('click')
|
|
281
|
+
|
|
282
|
+
expect(wrapper.emitted('navigate')?.[0]).toEqual(['/pdf'])
|
|
283
|
+
})
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
describe('checkbox options', () => {
|
|
287
|
+
const checkboxProps: Partial<NexxtDropdownButtonProps> = {
|
|
288
|
+
options: [
|
|
289
|
+
{ label: 'Name', action: vi.fn(), checkbox: true },
|
|
290
|
+
{ label: 'Email', action: vi.fn(), checkbox: true },
|
|
291
|
+
] as NexxtDropdownButtonProps['options'],
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
it('renders menuitemcheckbox role', async () => {
|
|
295
|
+
const wrapper = mountDropdown(checkboxProps)
|
|
296
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
297
|
+
|
|
298
|
+
expect(wrapper.findAll('[role="menuitemcheckbox"]')).toHaveLength(2)
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
it('starts unchecked', async () => {
|
|
302
|
+
const wrapper = mountDropdown(checkboxProps)
|
|
303
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
304
|
+
|
|
305
|
+
const items = wrapper.findAll('[role="menuitemcheckbox"]')
|
|
306
|
+
expect(items[0].attributes('aria-checked')).toBe('false')
|
|
307
|
+
expect(items[1].attributes('aria-checked')).toBe('false')
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it('toggles checked state on click', async () => {
|
|
311
|
+
const wrapper = mountDropdown(checkboxProps)
|
|
312
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
313
|
+
|
|
314
|
+
const item = wrapper.find('[role="menuitemcheckbox"]')
|
|
315
|
+
expect(item.attributes('aria-checked')).toBe('false')
|
|
316
|
+
|
|
317
|
+
await item.trigger('click')
|
|
318
|
+
expect(item.attributes('aria-checked')).toBe('true')
|
|
319
|
+
|
|
320
|
+
await item.trigger('click')
|
|
321
|
+
expect(item.attributes('aria-checked')).toBe('false')
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('keeps the dropdown open after clicking a checkbox item', async () => {
|
|
325
|
+
const wrapper = mountDropdown(checkboxProps)
|
|
326
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
327
|
+
|
|
328
|
+
await wrapper.find('[role="menuitemcheckbox"]').trigger('click')
|
|
329
|
+
|
|
330
|
+
expect(wrapper.find('[role="menu"]').exists()).toBe(true)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('calls the action when a checkbox item is clicked', async () => {
|
|
334
|
+
const action = vi.fn()
|
|
335
|
+
const wrapper = mountDropdown({
|
|
336
|
+
options: [{ label: 'Name', action, checkbox: true }] as NexxtDropdownButtonProps['options'],
|
|
337
|
+
})
|
|
338
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
339
|
+
await wrapper.find('[role="menuitemcheckbox"]').trigger('click')
|
|
340
|
+
|
|
341
|
+
expect(action).toHaveBeenCalled()
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it('emits checkboxChange with label and new checked value', async () => {
|
|
345
|
+
const wrapper = mountDropdown(checkboxProps)
|
|
346
|
+
await wrapper.findAll('button')[1].trigger('click')
|
|
347
|
+
|
|
348
|
+
const item = wrapper.find('[role="menuitemcheckbox"]')
|
|
349
|
+
await item.trigger('click')
|
|
350
|
+
expect(wrapper.emitted('checkboxChange')?.[0]).toEqual(['Name', true])
|
|
351
|
+
|
|
352
|
+
await item.trigger('click')
|
|
353
|
+
expect(wrapper.emitted('checkboxChange')?.[1]).toEqual(['Name', false])
|
|
354
|
+
})
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
describe('iconOnly', () => {
|
|
358
|
+
it('renders only a single button', () => {
|
|
359
|
+
const wrapper = mountIconOnly()
|
|
360
|
+
expect(wrapper.findAll('button')).toHaveLength(1)
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
it('does not render a label', () => {
|
|
364
|
+
const wrapper = mountIconOnly()
|
|
365
|
+
expect(wrapper.text()).toBe('')
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
it('does not render a separator', () => {
|
|
369
|
+
const wrapper = mountIconOnly()
|
|
370
|
+
expect(wrapper.find('.w-px').exists()).toBe(false)
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
it('toggles the dropdown when the button is clicked', async () => {
|
|
374
|
+
const wrapper = mountIconOnly()
|
|
375
|
+
await wrapper.find('button').trigger('click')
|
|
376
|
+
expect(wrapper.find('[role="menu"]').exists()).toBe(true)
|
|
377
|
+
})
|
|
378
|
+
})
|
|
379
|
+
})
|