@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,45 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import NexxtCard from './Card.vue'
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/Atoms/Card',
|
|
6
|
+
component: NexxtCard,
|
|
7
|
+
} satisfies Meta<typeof NexxtCard>
|
|
8
|
+
|
|
9
|
+
type Story = StoryObj<typeof NexxtCard>
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
args: {},
|
|
13
|
+
render: (args) => ({
|
|
14
|
+
components: { NexxtCard },
|
|
15
|
+
setup: () => ({ args }),
|
|
16
|
+
template: `<NexxtCard v-bind="args"><div>Default slot content</div></NexxtCard>`,
|
|
17
|
+
}),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const WithImage: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
image: 'https://placehold.co/333x160',
|
|
23
|
+
},
|
|
24
|
+
render: (args) => ({
|
|
25
|
+
components: { NexxtCard },
|
|
26
|
+
setup: () => ({ args }),
|
|
27
|
+
template: `<NexxtCard v-bind="args"><div>Default slot content</div></NexxtCard>`,
|
|
28
|
+
}),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const WithCustomImageSlot: Story = {
|
|
32
|
+
render: () => ({
|
|
33
|
+
components: { NexxtCard },
|
|
34
|
+
template: `
|
|
35
|
+
<NexxtCard>
|
|
36
|
+
<template #image>
|
|
37
|
+
<div class="flex h-40 w-full items-center justify-center rounded-tl-lg rounded-tr-lg bg-gray-100">
|
|
38
|
+
<span class="text-gray-400 text-sm">Custom image slot</span>
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
41
|
+
<div>Default slot content</div>
|
|
42
|
+
</NexxtCard>
|
|
43
|
+
`,
|
|
44
|
+
}),
|
|
45
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import NexxtCard from './Card.vue'
|
|
4
|
+
|
|
5
|
+
const slotContent = '<div>Slot Content</div>'
|
|
6
|
+
|
|
7
|
+
describe('NexxtCard', () => {
|
|
8
|
+
it('renders slot content', () => {
|
|
9
|
+
const wrapper = mount(NexxtCard, {
|
|
10
|
+
slots: { default: slotContent },
|
|
11
|
+
})
|
|
12
|
+
expect(wrapper.html()).toContain('Slot Content')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('renders image when provided', () => {
|
|
16
|
+
const wrapper = mount(NexxtCard, {
|
|
17
|
+
props: { image: 'https://placehold.co/333x160' },
|
|
18
|
+
slots: { default: slotContent },
|
|
19
|
+
})
|
|
20
|
+
const img = wrapper.find('img')
|
|
21
|
+
expect(img.exists()).toBe(true)
|
|
22
|
+
expect(img.attributes('src')).toBe('https://placehold.co/333x160')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('does not render image when not provided', () => {
|
|
26
|
+
const wrapper = mount(NexxtCard, {
|
|
27
|
+
slots: { default: slotContent },
|
|
28
|
+
})
|
|
29
|
+
expect(wrapper.find('img').exists()).toBe(false)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('applies correct classes', () => {
|
|
33
|
+
const wrapper = mount(NexxtCard, {
|
|
34
|
+
slots: { default: slotContent },
|
|
35
|
+
})
|
|
36
|
+
// `card` is what supplies the background: it applies `bg-card-bg` from the
|
|
37
|
+
// token set. This used to assert a `bg-color-` class that Tailwind never
|
|
38
|
+
// generated any CSS for, so the assertion passed on a class that did
|
|
39
|
+
// nothing.
|
|
40
|
+
expect(wrapper.classes()).toContain('card')
|
|
41
|
+
expect(wrapper.classes()).toContain('rounded-t-lg')
|
|
42
|
+
const content = wrapper.find('.card-content')
|
|
43
|
+
expect(content.classes()).toContain('border')
|
|
44
|
+
expect(content.classes()).toContain('rounded-b-lg')
|
|
45
|
+
})
|
|
46
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import Checkbox from './Checkbox.vue'
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/Atoms/Checkbox',
|
|
6
|
+
component: Checkbox,
|
|
7
|
+
} satisfies Meta<typeof Checkbox>
|
|
8
|
+
|
|
9
|
+
type Story = StoryObj<typeof Checkbox>
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
args: {
|
|
13
|
+
modelValue: false,
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Checked: Story = {
|
|
18
|
+
args: {
|
|
19
|
+
modelValue: true,
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Disabled: Story = {
|
|
24
|
+
args: {
|
|
25
|
+
modelValue: false,
|
|
26
|
+
disabled: true,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const DisabledChecked: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
modelValue: true,
|
|
33
|
+
disabled: true,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import Checkbox from './Checkbox.vue'
|
|
4
|
+
|
|
5
|
+
describe('Checkbox', () => {
|
|
6
|
+
it('renders a checkbox input', () => {
|
|
7
|
+
const wrapper = mount(Checkbox)
|
|
8
|
+
|
|
9
|
+
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('is unchecked by default', () => {
|
|
13
|
+
const wrapper = mount(Checkbox)
|
|
14
|
+
|
|
15
|
+
expect((wrapper.find('input').element as HTMLInputElement).checked).toBe(false)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('is checked when modelValue is true', () => {
|
|
19
|
+
const wrapper = mount(Checkbox, { props: { modelValue: true } })
|
|
20
|
+
|
|
21
|
+
expect((wrapper.find('input').element as HTMLInputElement).checked).toBe(true)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('emits update:modelValue when toggled', async () => {
|
|
25
|
+
const wrapper = mount(Checkbox, { props: { modelValue: false } })
|
|
26
|
+
|
|
27
|
+
await wrapper.find('input').setValue(true)
|
|
28
|
+
|
|
29
|
+
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('disables the input when disabled prop is true', () => {
|
|
33
|
+
const wrapper = mount(Checkbox, { props: { disabled: true } })
|
|
34
|
+
|
|
35
|
+
expect((wrapper.find('input').element as HTMLInputElement).disabled).toBe(true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('applies opacity class when disabled', () => {
|
|
39
|
+
const wrapper = mount(Checkbox, { props: { disabled: true } })
|
|
40
|
+
|
|
41
|
+
expect(wrapper.find('label').classes()).toContain('opacity-50')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('forwards attributes to the input', () => {
|
|
45
|
+
const wrapper = mount(Checkbox, { attrs: { 'aria-label': 'Accept terms' } })
|
|
46
|
+
|
|
47
|
+
expect(wrapper.find('input').attributes('aria-label')).toBe('Accept terms')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('renders the check icon', () => {
|
|
51
|
+
const wrapper = mount(Checkbox)
|
|
52
|
+
|
|
53
|
+
expect(wrapper.find('i.fa-check').exists()).toBe(true)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import Chip from './Chip.vue'
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/Atoms/Chip',
|
|
6
|
+
component: Chip,
|
|
7
|
+
} satisfies Meta<typeof Chip>
|
|
8
|
+
|
|
9
|
+
type Story = StoryObj<typeof Chip>
|
|
10
|
+
|
|
11
|
+
// The full variant set, in the order the design system lists them. Driven from
|
|
12
|
+
// data so the overview story cannot silently fall out of sync with the single
|
|
13
|
+
// stories below it.
|
|
14
|
+
const variants = ['default', 'warning', 'success', 'danger', 'neutral', 'accent', 'yellow']
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Every variant, solid and ghost, in one view — this is the story to review when
|
|
18
|
+
* judging the set as a whole.
|
|
19
|
+
*
|
|
20
|
+
* Note the one deliberate exception: solid `yellow` is the only variant that does
|
|
21
|
+
* not carry white text. White on `yellow-400` measures 1.4:1, which fails every
|
|
22
|
+
* WCAG threshold, so it uses dark `yellow-950` text instead. The ghost `yellow`
|
|
23
|
+
* likewise steps its text down to `yellow-700` where the other ghosts sit at 500.
|
|
24
|
+
*/
|
|
25
|
+
export const AllVariants: Story = {
|
|
26
|
+
render: () => ({
|
|
27
|
+
components: { Chip },
|
|
28
|
+
setup: () => ({ variants }),
|
|
29
|
+
template: `
|
|
30
|
+
<div style="display: inline-grid; grid-template-columns: auto auto auto; gap: 12px 24px; align-items: center; font-family: system-ui, sans-serif;">
|
|
31
|
+
<span style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #374151;">Variant</span>
|
|
32
|
+
<span style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #374151;">Solid</span>
|
|
33
|
+
<span style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #374151;">Ghost</span>
|
|
34
|
+
|
|
35
|
+
<template v-for="variant in variants" :key="variant">
|
|
36
|
+
<span style="font-size: 13px; color: #111827;">
|
|
37
|
+
{{ variant }}
|
|
38
|
+
<span v-if="variant === 'yellow'" style="color: #6b7280;">(dark text)</span>
|
|
39
|
+
</span>
|
|
40
|
+
<span><Chip :variant="variant">{{ variant }}</Chip></span>
|
|
41
|
+
<span><Chip :variant="variant" ghost>{{ variant }}</Chip></span>
|
|
42
|
+
</template>
|
|
43
|
+
</div>
|
|
44
|
+
`,
|
|
45
|
+
}),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The same set placed on a mid-grey canvas. Solid `yellow` is light enough that it
|
|
50
|
+
* can read as an unstyled gap on white, so this is where to check that it still
|
|
51
|
+
* looks like a filled chip rather than a hole in the layout.
|
|
52
|
+
*/
|
|
53
|
+
export const AllVariantsOnGrey: Story = {
|
|
54
|
+
render: () => ({
|
|
55
|
+
components: { Chip },
|
|
56
|
+
setup: () => ({ variants }),
|
|
57
|
+
template: `
|
|
58
|
+
<div style="background: #6b7280; padding: 24px; border-radius: 12px; display: flex; flex-direction: column; gap: 16px;">
|
|
59
|
+
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
|
60
|
+
<Chip v-for="variant in variants" :key="variant" :variant="variant">{{ variant }}</Chip>
|
|
61
|
+
</div>
|
|
62
|
+
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
|
63
|
+
<Chip v-for="variant in variants" :key="variant" :variant="variant" ghost>{{ variant }}</Chip>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
`,
|
|
67
|
+
}),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Solid `yellow` next to the other solid variants, so the dark-text exception is
|
|
72
|
+
* seen in context rather than discovered later. Every other solid chip is
|
|
73
|
+
* white-on-colour; this one is `yellow-950` on `yellow-400`.
|
|
74
|
+
*/
|
|
75
|
+
export const YellowContrast: Story = {
|
|
76
|
+
render: () => ({
|
|
77
|
+
components: { Chip },
|
|
78
|
+
template: `
|
|
79
|
+
<div style="display: flex; flex-direction: column; gap: 12px; font-family: system-ui, sans-serif;">
|
|
80
|
+
<div style="display: flex; align-items: center; gap: 12px;">
|
|
81
|
+
<Chip variant="yellow">Yellow solid</Chip>
|
|
82
|
+
<span style="font-size: 12px; color: #374151;">yellow-950 on yellow-400 — the only solid chip with dark text</span>
|
|
83
|
+
</div>
|
|
84
|
+
<div style="display: flex; align-items: center; gap: 12px;">
|
|
85
|
+
<Chip variant="warning">Warning solid</Chip>
|
|
86
|
+
<span style="font-size: 12px; color: #374151;">white on orange-500 — how every other solid chip is built</span>
|
|
87
|
+
</div>
|
|
88
|
+
<div style="display: flex; align-items: center; gap: 12px;">
|
|
89
|
+
<Chip variant="yellow" ghost>Yellow ghost</Chip>
|
|
90
|
+
<span style="font-size: 12px; color: #374151;">yellow-700 on yellow-50 — steps down where other ghosts use 500</span>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
`,
|
|
94
|
+
}),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The `icon` prop renders a leading icon before the chip label. */
|
|
98
|
+
export const WithIcon: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
icon: 'circle-check',
|
|
101
|
+
variant: 'success',
|
|
102
|
+
default: 'Verified',
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The leading icon across the whole variant set, so it can be checked that the
|
|
108
|
+
* icon inherits each variant's text colour and that nothing overflows the chip's
|
|
109
|
+
* fixed 20px height.
|
|
110
|
+
*/
|
|
111
|
+
export const IconAllVariants: Story = {
|
|
112
|
+
render: () => ({
|
|
113
|
+
components: { Chip },
|
|
114
|
+
setup: () => ({
|
|
115
|
+
rows: [
|
|
116
|
+
{ variant: 'default', icon: 'circle-info', label: 'Information' },
|
|
117
|
+
{ variant: 'warning', icon: 'triangle-exclamation', label: 'Warning' },
|
|
118
|
+
{ variant: 'success', icon: 'circle-check', label: 'Approved' },
|
|
119
|
+
{ variant: 'danger', icon: 'circle-xmark', label: 'Rejected' },
|
|
120
|
+
{ variant: 'neutral', icon: 'clock', label: 'Pending' },
|
|
121
|
+
{ variant: 'accent', icon: 'star', label: 'Featured' },
|
|
122
|
+
{ variant: 'yellow', icon: 'bolt', label: 'Draft' },
|
|
123
|
+
],
|
|
124
|
+
}),
|
|
125
|
+
template: `
|
|
126
|
+
<div style="display: flex; flex-direction: column; gap: 16px;">
|
|
127
|
+
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
|
128
|
+
<Chip v-for="row in rows" :key="row.variant" :variant="row.variant" :icon="row.icon">
|
|
129
|
+
{{ row.label }}
|
|
130
|
+
</Chip>
|
|
131
|
+
</div>
|
|
132
|
+
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
|
133
|
+
<Chip v-for="row in rows" :key="row.variant" :variant="row.variant" :icon="row.icon" ghost>
|
|
134
|
+
{{ row.label }}
|
|
135
|
+
</Chip>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
`,
|
|
139
|
+
}),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The same chip with and without `icon`, so the horizontal padding change the icon
|
|
144
|
+
* brings with it is visible side by side.
|
|
145
|
+
*/
|
|
146
|
+
export const IconVersusNoIcon: Story = {
|
|
147
|
+
render: () => ({
|
|
148
|
+
components: { Chip },
|
|
149
|
+
template: `
|
|
150
|
+
<div style="display: flex; align-items: center; gap: 16px;">
|
|
151
|
+
<Chip variant="accent">No icon</Chip>
|
|
152
|
+
<Chip variant="accent" icon="star">With icon</Chip>
|
|
153
|
+
</div>
|
|
154
|
+
`,
|
|
155
|
+
}),
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** A leading icon combined with the trailing `close` slot. */
|
|
159
|
+
export const IconWithCloseSlot: Story = {
|
|
160
|
+
render: () => ({
|
|
161
|
+
components: { Chip },
|
|
162
|
+
template: `
|
|
163
|
+
<Chip variant="neutral" icon="filter">
|
|
164
|
+
Amsterdam
|
|
165
|
+
<template #close>
|
|
166
|
+
<span aria-hidden="true" style="padding-left: 6px;">×</span>
|
|
167
|
+
</template>
|
|
168
|
+
</Chip>
|
|
169
|
+
`,
|
|
170
|
+
}),
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const Default: Story = {
|
|
174
|
+
args: {
|
|
175
|
+
default: 'Default',
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const Warning: Story = {
|
|
180
|
+
args: {
|
|
181
|
+
variant: 'warning',
|
|
182
|
+
default: 'Warning',
|
|
183
|
+
},
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const Success: Story = {
|
|
187
|
+
args: {
|
|
188
|
+
variant: 'success',
|
|
189
|
+
default: 'Succes',
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const Danger: Story = {
|
|
194
|
+
args: {
|
|
195
|
+
variant: 'danger',
|
|
196
|
+
default: 'Danger',
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export const Neutral: Story = {
|
|
201
|
+
args: {
|
|
202
|
+
variant: 'neutral',
|
|
203
|
+
default: 'Neutral',
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export const Accent: Story = {
|
|
208
|
+
args: {
|
|
209
|
+
variant: 'accent',
|
|
210
|
+
default: 'Accent',
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export const Yellow: Story = {
|
|
215
|
+
args: {
|
|
216
|
+
variant: 'yellow',
|
|
217
|
+
default: 'Yellow',
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const GhostDefault: Story = {
|
|
222
|
+
args: {
|
|
223
|
+
ghost: true,
|
|
224
|
+
default: 'Ghost Default',
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export const GhostWarning: Story = {
|
|
229
|
+
args: {
|
|
230
|
+
variant: 'warning',
|
|
231
|
+
ghost: true,
|
|
232
|
+
default: 'Ghost Warning',
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export const GhostSuccess: Story = {
|
|
237
|
+
args: {
|
|
238
|
+
variant: 'success',
|
|
239
|
+
ghost: true,
|
|
240
|
+
default: 'Ghost Success',
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export const GhostDanger: Story = {
|
|
245
|
+
args: {
|
|
246
|
+
variant: 'danger',
|
|
247
|
+
ghost: true,
|
|
248
|
+
default: 'Ghost Danger',
|
|
249
|
+
},
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export const GhostNeutral: Story = {
|
|
253
|
+
args: {
|
|
254
|
+
variant: 'neutral',
|
|
255
|
+
ghost: true,
|
|
256
|
+
default: 'Ghost Neutral',
|
|
257
|
+
},
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export const GhostAccent: Story = {
|
|
261
|
+
args: {
|
|
262
|
+
variant: 'accent',
|
|
263
|
+
ghost: true,
|
|
264
|
+
default: 'Ghost Accent',
|
|
265
|
+
},
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export const GhostYellow: Story = {
|
|
269
|
+
args: {
|
|
270
|
+
variant: 'yellow',
|
|
271
|
+
ghost: true,
|
|
272
|
+
default: 'Ghost Yellow',
|
|
273
|
+
},
|
|
274
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import Chip from './Chip.vue'
|
|
4
|
+
|
|
5
|
+
describe('Chip', () => {
|
|
6
|
+
it('renders correctly with default slot content', () => {
|
|
7
|
+
const wrapper = mount(Chip, {
|
|
8
|
+
slots: {
|
|
9
|
+
default: 'Test Chip',
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
expect(wrapper.text()).toBe('Test Chip')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('renders default text when no slot is provided', () => {
|
|
16
|
+
const wrapper = mount(Chip)
|
|
17
|
+
expect(wrapper.text()).toBe('Chip')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('applies the correct background color for default variant', () => {
|
|
21
|
+
const wrapper = mount(Chip)
|
|
22
|
+
expect(wrapper.classes()).toContain('bg-cornflower-blue-600')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('applies the correct background color for warning variant', () => {
|
|
26
|
+
const wrapper = mount(Chip, {
|
|
27
|
+
props: {
|
|
28
|
+
variant: 'warning',
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
expect(wrapper.classes()).toContain('bg-orange-500')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('applies the correct background color for success variant', () => {
|
|
35
|
+
const wrapper = mount(Chip, {
|
|
36
|
+
props: {
|
|
37
|
+
variant: 'success',
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
expect(wrapper.classes()).toContain('bg-green-500')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('applies the correct background color for danger variant', () => {
|
|
44
|
+
const wrapper = mount(Chip, {
|
|
45
|
+
props: {
|
|
46
|
+
variant: 'danger',
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
expect(wrapper.classes()).toContain('bg-brick-500')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('applies the correct background color for neutral variant', () => {
|
|
53
|
+
const wrapper = mount(Chip, {
|
|
54
|
+
props: {
|
|
55
|
+
variant: 'neutral',
|
|
56
|
+
},
|
|
57
|
+
})
|
|
58
|
+
expect(wrapper.classes()).toContain('bg-gray-700')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('applies the correct background color for accent variant', () => {
|
|
62
|
+
const wrapper = mount(Chip, {
|
|
63
|
+
props: {
|
|
64
|
+
variant: 'accent',
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
expect(wrapper.classes()).toContain('bg-purple-600')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it.each([
|
|
71
|
+
{
|
|
72
|
+
variant: undefined,
|
|
73
|
+
bg: 'bg-cornflower-blue-50',
|
|
74
|
+
border: 'border-cornflower-blue-500',
|
|
75
|
+
text: 'text-cornflower-blue-500',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
variant: 'warning' as const,
|
|
79
|
+
bg: 'bg-orange-50',
|
|
80
|
+
border: 'border-orange-500',
|
|
81
|
+
text: 'text-orange-500',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
variant: 'success' as const,
|
|
85
|
+
bg: 'bg-green-50',
|
|
86
|
+
border: 'border-green-500',
|
|
87
|
+
text: 'text-green-500',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
variant: 'danger' as const,
|
|
91
|
+
bg: 'bg-brick-100/20',
|
|
92
|
+
border: 'border-brick-500',
|
|
93
|
+
text: 'text-brick-500',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
variant: 'neutral' as const,
|
|
97
|
+
bg: 'bg-gray-100',
|
|
98
|
+
border: 'border-gray-600',
|
|
99
|
+
text: 'text-gray-700',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
variant: 'accent' as const,
|
|
103
|
+
bg: 'bg-purple-50',
|
|
104
|
+
border: 'border-purple-500',
|
|
105
|
+
text: 'text-purple-500',
|
|
106
|
+
},
|
|
107
|
+
])(
|
|
108
|
+
'ghost=true with variant "$variant" applies light background, border and colored text',
|
|
109
|
+
({ variant, bg, border, text }) => {
|
|
110
|
+
const wrapper = mount(Chip, { props: { ghost: true, ...(variant ? { variant } : {}) } })
|
|
111
|
+
expect(wrapper.classes()).toContain(bg)
|
|
112
|
+
expect(wrapper.classes()).toContain('border')
|
|
113
|
+
expect(wrapper.classes()).toContain(border)
|
|
114
|
+
expect(wrapper.find('span > span').classes()).toContain(text)
|
|
115
|
+
},
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
it.each([
|
|
119
|
+
{ variant: undefined, text: 'text-white' },
|
|
120
|
+
{ variant: 'warning' as const, text: 'text-white' },
|
|
121
|
+
{ variant: 'success' as const, text: 'text-white' },
|
|
122
|
+
{ variant: 'danger' as const, text: 'text-white' },
|
|
123
|
+
{ variant: 'neutral' as const, text: 'text-white' },
|
|
124
|
+
{ variant: 'accent' as const, text: 'text-white' },
|
|
125
|
+
])('ghost=false with variant "$variant" uses white text and no border', ({ variant, text }) => {
|
|
126
|
+
const wrapper = mount(Chip, { props: { ...(variant ? { variant } : {}) } })
|
|
127
|
+
expect(wrapper.classes()).not.toContain('border')
|
|
128
|
+
expect(wrapper.find('span > span').classes()).toContain(text)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
// Class names are assembled at runtime from fragments. Writing a complete utility
|
|
133
|
+
// class as a literal in a file under src/components/ makes Tailwind's @source scan
|
|
134
|
+
// generate that CSS, which turns a broken component green in Storybook only.
|
|
135
|
+
// See CONTRIBUTING.md.
|
|
136
|
+
const tw = (...parts: string[]) => parts.join('-')
|
|
137
|
+
|
|
138
|
+
// Matches a colour utility (text-white, text-gray-700, ...) but not text-center.
|
|
139
|
+
const colourClass = /^text-(white|black|[a-z]+(-[a-z]+)*-\d{2,3})(\/\d+)?$/
|
|
140
|
+
|
|
141
|
+
const label = (wrapper: ReturnType<typeof mount>) => wrapper.find('span > span')
|
|
142
|
+
|
|
143
|
+
describe('Chip yellow variant', () => {
|
|
144
|
+
it('solid yellow renders the yellow surface with white text', () => {
|
|
145
|
+
const wrapper = mount(Chip, { props: { variant: 'yellow' } })
|
|
146
|
+
|
|
147
|
+
expect(wrapper.classes()).toContain(tw('bg', 'yellow', '500'))
|
|
148
|
+
expect(label(wrapper).classes()).toContain(tw('text', 'white'))
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('solid yellow renders no border, like every other solid variant', () => {
|
|
152
|
+
const wrapper = mount(Chip, { props: { variant: 'yellow' } })
|
|
153
|
+
|
|
154
|
+
expect(wrapper.classes()).not.toContain('border')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('ghost yellow renders a yellow border and yellow label on a lighter yellow surface', () => {
|
|
158
|
+
const ghost = mount(Chip, { props: { variant: 'yellow', ghost: true } })
|
|
159
|
+
const solid = mount(Chip, { props: { variant: 'yellow' } })
|
|
160
|
+
|
|
161
|
+
const bg = ghost.classes().find((c) => c.startsWith(tw('bg', 'yellow') + '-'))
|
|
162
|
+
const border = ghost.classes().find((c) => c.startsWith(tw('border', 'yellow') + '-'))
|
|
163
|
+
const text = label(ghost)
|
|
164
|
+
.classes()
|
|
165
|
+
.find((c) => c.startsWith(tw('text', 'yellow') + '-'))
|
|
166
|
+
|
|
167
|
+
expect(bg).toBeDefined()
|
|
168
|
+
expect(border).toBeDefined()
|
|
169
|
+
expect(text).toBeDefined()
|
|
170
|
+
expect(ghost.classes()).toContain('border')
|
|
171
|
+
// the ghost surface must not be the solid fill
|
|
172
|
+
expect(bg).not.toBe(solid.classes().find((c) => c.startsWith(tw('bg', 'yellow') + '-')))
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe('Chip explicit label colour', () => {
|
|
177
|
+
it.each(['default', 'warning', 'success', 'danger', 'neutral', 'accent', 'yellow'] as const)(
|
|
178
|
+
'solid variant "%s" sets an explicit label colour',
|
|
179
|
+
(variant) => {
|
|
180
|
+
const wrapper = mount(Chip, { props: { variant } })
|
|
181
|
+
|
|
182
|
+
expect(
|
|
183
|
+
label(wrapper)
|
|
184
|
+
.classes()
|
|
185
|
+
.filter((c) => colourClass.test(c)),
|
|
186
|
+
).toHaveLength(1)
|
|
187
|
+
},
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
it.each(['default', 'warning', 'success', 'danger', 'neutral', 'accent', 'yellow'] as const)(
|
|
191
|
+
'ghost variant "%s" sets an explicit label colour',
|
|
192
|
+
(variant) => {
|
|
193
|
+
const wrapper = mount(Chip, { props: { variant, ghost: true } })
|
|
194
|
+
|
|
195
|
+
expect(
|
|
196
|
+
label(wrapper)
|
|
197
|
+
.classes()
|
|
198
|
+
.filter((c) => colourClass.test(c)),
|
|
199
|
+
).toHaveLength(1)
|
|
200
|
+
},
|
|
201
|
+
)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
describe('Chip icon prop', () => {
|
|
205
|
+
it('renders no icon when the prop is omitted', () => {
|
|
206
|
+
const wrapper = mount(Chip, { slots: { default: 'Label' } })
|
|
207
|
+
|
|
208
|
+
expect(wrapper.find('i').exists()).toBe(false)
|
|
209
|
+
expect(wrapper.text()).toBe('Label')
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('renders the named icon ahead of the label text', () => {
|
|
213
|
+
const wrapper = mount(Chip, { props: { icon: 'megaphone' }, slots: { default: 'Label' } })
|
|
214
|
+
|
|
215
|
+
expect(wrapper.find('i.fa-megaphone').exists()).toBe(true)
|
|
216
|
+
const html = wrapper.html()
|
|
217
|
+
expect(html.indexOf('fa-megaphone')).toBeLessThan(html.indexOf('Label'))
|
|
218
|
+
expect(wrapper.text()).toContain('Label')
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('renders the icon alongside a close slot without dropping either', () => {
|
|
222
|
+
const wrapper = mount(Chip, {
|
|
223
|
+
props: { icon: 'circle-check' },
|
|
224
|
+
slots: { default: 'Label', close: '<button class="x">x</button>' },
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
expect(wrapper.find('i.fa-circle-check').exists()).toBe(true)
|
|
228
|
+
expect(wrapper.find('button.x').exists()).toBe(true)
|
|
229
|
+
})
|
|
230
|
+
})
|