@nuvia-ui/components 4.0.1
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/package.json +27 -0
- package/src/ds-accordion/ds-accordion-item.js +288 -0
- package/src/ds-accordion/ds-accordion-item.stories.js +82 -0
- package/src/ds-accordion/ds-accordion.a11y.test.js +92 -0
- package/src/ds-accordion/ds-accordion.js +68 -0
- package/src/ds-accordion/ds-accordion.stories.js +118 -0
- package/src/ds-accordion/ds-accordion.test.js +146 -0
- package/src/ds-accordion/index.js +2 -0
- package/src/ds-action-bar/ds-action-bar.js +116 -0
- package/src/ds-action-bar/ds-action-bar.stories.js +86 -0
- package/src/ds-action-bar/ds-action-bar.test.js +64 -0
- package/src/ds-action-bar/index.js +1 -0
- package/src/ds-alert/ds-alert.a11y.test.js +151 -0
- package/src/ds-alert/ds-alert.js +223 -0
- package/src/ds-alert/ds-alert.mdx +142 -0
- package/src/ds-alert/ds-alert.stories.js +166 -0
- package/src/ds-alert/ds-alert.test.js +256 -0
- package/src/ds-alert/index.js +1 -0
- package/src/ds-avatar/ds-avatar.a11y.test.js +45 -0
- package/src/ds-avatar/ds-avatar.js +216 -0
- package/src/ds-avatar/ds-avatar.stories.js +120 -0
- package/src/ds-avatar/ds-avatar.test.js +83 -0
- package/src/ds-avatar/index.js +1 -0
- package/src/ds-avatar-extended/ds-avatar-extended.a11y.test.js +29 -0
- package/src/ds-avatar-extended/ds-avatar-extended.js +108 -0
- package/src/ds-avatar-extended/ds-avatar-extended.stories.js +93 -0
- package/src/ds-avatar-extended/ds-avatar-extended.test.js +66 -0
- package/src/ds-avatar-extended/index.js +1 -0
- package/src/ds-banner/ds-banner.a11y.test.js +51 -0
- package/src/ds-banner/ds-banner.js +233 -0
- package/src/ds-banner/ds-banner.stories.js +185 -0
- package/src/ds-banner/ds-banner.test.js +116 -0
- package/src/ds-banner/index.js +1 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.js +135 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.stories.js +49 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.test.js +55 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.js +194 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.stories.js +54 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.test.js +33 -0
- package/src/ds-button/ds-button.a11y.test.js +49 -0
- package/src/ds-button/ds-button.js +205 -0
- package/src/ds-button/ds-button.mdx +141 -0
- package/src/ds-button/ds-button.stories.js +152 -0
- package/src/ds-button/ds-button.test.js +62 -0
- package/src/ds-button/index.js +1 -0
- package/src/ds-button-group/ds-button-group.js +82 -0
- package/src/ds-button-group/ds-button-group.mdx +39 -0
- package/src/ds-button-group/ds-button-group.stories.js +47 -0
- package/src/ds-button-group/ds-button-group.test.js +47 -0
- package/src/ds-button-group/index.js +1 -0
- package/src/ds-checkbox/ds-checkbox.a11y.test.js +79 -0
- package/src/ds-checkbox/ds-checkbox.js +271 -0
- package/src/ds-checkbox/ds-checkbox.stories.js +77 -0
- package/src/ds-checkbox/ds-checkbox.test.js +191 -0
- package/src/ds-checkbox/index.js +1 -0
- package/src/ds-checkbox-group/ds-checkbox-group.a11y.test.js +146 -0
- package/src/ds-checkbox-group/ds-checkbox-group.js +235 -0
- package/src/ds-checkbox-group/ds-checkbox-group.stories.js +210 -0
- package/src/ds-checkbox-group/ds-checkbox-group.test.js +150 -0
- package/src/ds-checkbox-group/index.js +1 -0
- package/src/ds-dialog/ds-dialog.js +466 -0
- package/src/ds-dialog/ds-dialog.stories.js +274 -0
- package/src/ds-dialog/ds-dialog.test.js +441 -0
- package/src/ds-dialog/index.js +1 -0
- package/src/ds-dropdown/ds-dropdown.a11y.test.js +80 -0
- package/src/ds-dropdown/ds-dropdown.js +891 -0
- package/src/ds-dropdown/ds-dropdown.stories.js +259 -0
- package/src/ds-dropdown/ds-dropdown.test.js +268 -0
- package/src/ds-dropdown/index.js +1 -0
- package/src/ds-dropdown-group/ds-dropdown-group.js +55 -0
- package/src/ds-dropdown-panel/ds-dropdown-panel.js +34 -0
- package/src/ds-file-uploaded/ds-file-uploaded.a11y.test.js +40 -0
- package/src/ds-file-uploaded/ds-file-uploaded.js +135 -0
- package/src/ds-file-uploaded/ds-file-uploaded.mdx +33 -0
- package/src/ds-file-uploaded/ds-file-uploaded.stories.js +81 -0
- package/src/ds-file-uploaded/ds-file-uploaded.test.js +85 -0
- package/src/ds-file-uploader/ds-file-uploader.a11y.test.js +61 -0
- package/src/ds-file-uploader/ds-file-uploader.js +442 -0
- package/src/ds-file-uploader/ds-file-uploader.mdx +44 -0
- package/src/ds-file-uploader/ds-file-uploader.stories.js +76 -0
- package/src/ds-file-uploader/ds-file-uploader.test.js +142 -0
- package/src/ds-header/ds-header.a11y.test.js +38 -0
- package/src/ds-header/ds-header.js +149 -0
- package/src/ds-header/ds-header.stories.js +63 -0
- package/src/ds-header/ds-header.test.js +52 -0
- package/src/ds-header/index.js +1 -0
- package/src/ds-header-nav/ds-header-nav.a11y.test.js +69 -0
- package/src/ds-header-nav/ds-header-nav.js +114 -0
- package/src/ds-header-nav/ds-header-nav.stories.js +17 -0
- package/src/ds-header-nav/ds-header-nav.test.js +93 -0
- package/src/ds-header-nav-item/ds-header-nav-item.a11y.test.js +71 -0
- package/src/ds-header-nav-item/ds-header-nav-item.js +124 -0
- package/src/ds-header-nav-item/ds-header-nav-item.stories.js +43 -0
- package/src/ds-header-nav-item/ds-header-nav-item.test.js +61 -0
- package/src/ds-icon/ds-icon.a11y.test.js +49 -0
- package/src/ds-icon/ds-icon.js +75 -0
- package/src/ds-icon/ds-icon.mdx +36 -0
- package/src/ds-icon/ds-icon.stories.js +88 -0
- package/src/ds-icon/ds-icon.test.js +97 -0
- package/src/ds-icon/index.js +1 -0
- package/src/ds-icon-button/ds-icon-button.a11y.test.js +55 -0
- package/src/ds-icon-button/ds-icon-button.js +224 -0
- package/src/ds-icon-button/ds-icon-button.mdx +131 -0
- package/src/ds-icon-button/ds-icon-button.stories.js +128 -0
- package/src/ds-icon-button/ds-icon-button.test.js +90 -0
- package/src/ds-icon-button/index.js +1 -0
- package/src/ds-input/ds-input.a11y.test.js +145 -0
- package/src/ds-input/ds-input.js +645 -0
- package/src/ds-input/ds-input.mdx +251 -0
- package/src/ds-input/ds-input.stories.js +298 -0
- package/src/ds-input/ds-input.test.js +792 -0
- package/src/ds-input/index.js +1 -0
- package/src/ds-link/ds-link.js +111 -0
- package/src/ds-link/ds-link.stories.js +56 -0
- package/src/ds-link/ds-link.test.js +74 -0
- package/src/ds-list-item/ds-list-item.a11y.test.js +39 -0
- package/src/ds-list-item/ds-list-item.js +292 -0
- package/src/ds-list-item/ds-list-item.stories.js +101 -0
- package/src/ds-list-item/ds-list-item.test.js +63 -0
- package/src/ds-menu/ds-menu.js +30 -0
- package/src/ds-menu/ds-menu.stories.js +120 -0
- package/src/ds-menu/ds-menu.test.js +123 -0
- package/src/ds-menu-group/ds-menu-group.js +101 -0
- package/src/ds-menu-group/ds-menu-group.stories.js +99 -0
- package/src/ds-nav-item/ds-nav-item.a11y.test.js +91 -0
- package/src/ds-nav-item/ds-nav-item.js +307 -0
- package/src/ds-nav-item/ds-nav-item.stories.js +99 -0
- package/src/ds-nav-item/ds-nav-item.test.js +169 -0
- package/src/ds-nav-item/index.js +1 -0
- package/src/ds-nav-vertical/ds-nav-vertical.a11y.test.js +69 -0
- package/src/ds-nav-vertical/ds-nav-vertical.js +173 -0
- package/src/ds-nav-vertical/ds-nav-vertical.stories.js +124 -0
- package/src/ds-nav-vertical/ds-nav-vertical.test.js +176 -0
- package/src/ds-nav-vertical/index.js +1 -0
- package/src/ds-pagination/ds-pagination.a11y.test.js +50 -0
- package/src/ds-pagination/ds-pagination.js +232 -0
- package/src/ds-pagination/ds-pagination.stories.js +63 -0
- package/src/ds-pagination/ds-pagination.test.js +141 -0
- package/src/ds-pagination/index.js +1 -0
- package/src/ds-progress-bar/ds-progress-bar.a11y.test.js +25 -0
- package/src/ds-progress-bar/ds-progress-bar.js +81 -0
- package/src/ds-progress-bar/ds-progress-bar.stories.js +69 -0
- package/src/ds-progress-bar/ds-progress-bar.test.js +60 -0
- package/src/ds-radio/ds-radio.a11y.test.js +69 -0
- package/src/ds-radio/ds-radio.js +240 -0
- package/src/ds-radio/ds-radio.stories.js +102 -0
- package/src/ds-radio/ds-radio.test.js +114 -0
- package/src/ds-radio/index.js +1 -0
- package/src/ds-radio-group/ds-radio-group.a11y.test.js +164 -0
- package/src/ds-radio-group/ds-radio-group.js +257 -0
- package/src/ds-radio-group/ds-radio-group.stories.js +247 -0
- package/src/ds-radio-group/ds-radio-group.test.js +194 -0
- package/src/ds-radio-group/index.js +1 -0
- package/src/ds-rich-list/ds-rich-list.js +246 -0
- package/src/ds-rich-list/ds-rich-list.stories.js +368 -0
- package/src/ds-rich-list/ds-rich-list.test.js +293 -0
- package/src/ds-rich-list-item/ds-rich-list-item.js +579 -0
- package/src/ds-rich-list-item/ds-rich-list-item.stories.js +197 -0
- package/src/ds-rich-list-item/ds-rich-list-item.test.js +434 -0
- package/src/ds-slider/ds-slider.js +399 -0
- package/src/ds-slider/ds-slider.stories.js +107 -0
- package/src/ds-slider/ds-slider.test.js +308 -0
- package/src/ds-spinner/ds-spinner.js +173 -0
- package/src/ds-spinner/ds-spinner.stories.js +52 -0
- package/src/ds-spinner/ds-spinner.test.js +50 -0
- package/src/ds-status-border/ds-status-border.js +88 -0
- package/src/ds-status-border/ds-status-border.stories.js +242 -0
- package/src/ds-status-border/ds-status-border.test.js +168 -0
- package/src/ds-stepper/ds-stepper.a11y.test.js +198 -0
- package/src/ds-stepper/ds-stepper.js +207 -0
- package/src/ds-stepper/ds-stepper.stories.js +530 -0
- package/src/ds-stepper/ds-stepper.test.js +311 -0
- package/src/ds-stepper-item/ds-stepper-item.js +485 -0
- package/src/ds-stepper-item/ds-stepper-item.stories.js +288 -0
- package/src/ds-switch/ds-switch.js +348 -0
- package/src/ds-switch/ds-switch.stories.js +145 -0
- package/src/ds-switch/ds-switch.test.js +226 -0
- package/src/ds-switch/index.js +1 -0
- package/src/ds-tab-item/ds-tab-item.js +341 -0
- package/src/ds-tab-item/ds-tab-item.stories.js +69 -0
- package/src/ds-tabs/ds-tab-panel.js +48 -0
- package/src/ds-tabs/ds-tabs.a11y.test.js +56 -0
- package/src/ds-tabs/ds-tabs.js +180 -0
- package/src/ds-tabs/ds-tabs.stories.js +152 -0
- package/src/ds-tabs/ds-tabs.test.js +306 -0
- package/src/ds-tabs/index.js +3 -0
- package/src/ds-tag-action/ds-tag-action.a11y.test.js +32 -0
- package/src/ds-tag-action/ds-tag-action.js +185 -0
- package/src/ds-tag-action/ds-tag-action.stories.js +55 -0
- package/src/ds-tag-action/ds-tag-action.test.js +44 -0
- package/src/ds-tag-removable/ds-tag-removable.a11y.test.js +24 -0
- package/src/ds-tag-removable/ds-tag-removable.js +146 -0
- package/src/ds-tag-removable/ds-tag-removable.stories.js +52 -0
- package/src/ds-tag-removable/ds-tag-removable.test.js +46 -0
- package/src/ds-tag-status/ds-tag-status.a11y.test.js +93 -0
- package/src/ds-tag-status/ds-tag-status.js +164 -0
- package/src/ds-tag-status/ds-tag-status.stories.js +200 -0
- package/src/ds-tag-status/ds-tag-status.test.js +140 -0
- package/src/ds-tag-status/index.js +1 -0
- package/src/ds-textarea/ds-textarea-clearable.test.js +89 -0
- package/src/ds-textarea/ds-textarea.a11y.test.js +66 -0
- package/src/ds-textarea/ds-textarea.js +505 -0
- package/src/ds-textarea/ds-textarea.stories.js +335 -0
- package/src/ds-textarea/ds-textarea.test.js +218 -0
- package/src/ds-textarea/index.js +1 -0
- package/src/ds-thumbnail/ds-thumbnail.js +207 -0
- package/src/ds-thumbnail/ds-thumbnail.stories.js +217 -0
- package/src/ds-thumbnail/ds-thumbnail.test.js +220 -0
- package/src/ds-toast/ds-toast-provider.js +110 -0
- package/src/ds-toast/ds-toast.a11y.test.js +34 -0
- package/src/ds-toast/ds-toast.js +243 -0
- package/src/ds-toast/ds-toast.stories.js +143 -0
- package/src/ds-toast/ds-toast.test.js +93 -0
- package/src/ds-toast/index.js +2 -0
- package/src/ds-tooltip/ds-tooltip.a11y.test.js +110 -0
- package/src/ds-tooltip/ds-tooltip.js +217 -0
- package/src/ds-tooltip/ds-tooltip.mdx +75 -0
- package/src/ds-tooltip/ds-tooltip.stories.js +72 -0
- package/src/ds-tooltip/ds-tooltip.test.js +191 -0
- package/src/ds-tooltip/index.js +1 -0
- package/src/ds-tooltip/positioner.js +117 -0
- package/src/index.js +50 -0
- package/src/mixins/field-label.mixin.js +113 -0
- package/src/mixins/field-message.mixin.js +66 -0
- package/src/token-provider/index.js +1 -0
- package/src/token-provider/token-provider.a11y.test.js +44 -0
- package/src/token-provider/token-provider.js +85 -0
- package/src/token-provider/token-provider.stories.js +105 -0
- package/src/token-provider/token-provider.test.js +134 -0
- package/src/utils/number-input.utils.js +42 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import './ds-button.js';
|
|
2
|
+
import '../ds-icon/ds-icon.js';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Components/Button',
|
|
7
|
+
component: 'ds-button',
|
|
8
|
+
argTypes: {
|
|
9
|
+
variant: {
|
|
10
|
+
control: 'select',
|
|
11
|
+
options: ['primary', 'secondary', 'outline', 'action', 'tertiary'],
|
|
12
|
+
},
|
|
13
|
+
disabled: { control: 'boolean' },
|
|
14
|
+
label: { control: 'text' },
|
|
15
|
+
iconStart: { control: 'text' },
|
|
16
|
+
iconEnd: { control: 'text' }
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const createButton = ({ variant, disabled, label, iconStart, iconEnd }) => {
|
|
21
|
+
const btn = document.createElement('ds-button');
|
|
22
|
+
|
|
23
|
+
if (variant) btn.setAttribute('variant', variant);
|
|
24
|
+
if (disabled) btn.setAttribute('disabled', ''); // Boolean attribute
|
|
25
|
+
|
|
26
|
+
if (iconStart) {
|
|
27
|
+
const icon = document.createElement('ds-icon');
|
|
28
|
+
icon.setAttribute('slot', 'icon-start');
|
|
29
|
+
icon.setAttribute('name', iconStart);
|
|
30
|
+
icon.setAttribute('size', 'sm');
|
|
31
|
+
btn.appendChild(icon);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (label) {
|
|
35
|
+
btn.appendChild(document.createTextNode(label));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (iconEnd) {
|
|
39
|
+
const icon = document.createElement('ds-icon');
|
|
40
|
+
icon.setAttribute('slot', 'icon-end');
|
|
41
|
+
icon.setAttribute('name', iconEnd);
|
|
42
|
+
icon.setAttribute('size', 'sm');
|
|
43
|
+
btn.appendChild(icon);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return btn;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const Primary = {
|
|
50
|
+
args: {
|
|
51
|
+
variant: 'primary',
|
|
52
|
+
label: 'Save Changes',
|
|
53
|
+
},
|
|
54
|
+
render: createButton
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const Secondary = {
|
|
58
|
+
args: {
|
|
59
|
+
variant: 'secondary',
|
|
60
|
+
label: 'Cancel',
|
|
61
|
+
},
|
|
62
|
+
render: createButton
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const Outline = {
|
|
66
|
+
args: {
|
|
67
|
+
variant: 'outline',
|
|
68
|
+
label: 'Learn More',
|
|
69
|
+
},
|
|
70
|
+
render: createButton
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const Action = {
|
|
74
|
+
args: {
|
|
75
|
+
variant: 'action',
|
|
76
|
+
label: 'Edit',
|
|
77
|
+
},
|
|
78
|
+
render: createButton
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const Tertiary = {
|
|
82
|
+
args: {
|
|
83
|
+
variant: 'tertiary',
|
|
84
|
+
label: 'Skip',
|
|
85
|
+
},
|
|
86
|
+
render: createButton
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const WithIconStart = {
|
|
90
|
+
args: {
|
|
91
|
+
variant: 'primary',
|
|
92
|
+
label: 'Icon Start',
|
|
93
|
+
iconStart: 'star',
|
|
94
|
+
disabled: false
|
|
95
|
+
},
|
|
96
|
+
render: createButton
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const WithIconEnd = {
|
|
100
|
+
args: {
|
|
101
|
+
variant: 'primary',
|
|
102
|
+
label: 'Icon End',
|
|
103
|
+
iconEnd: 'arrow-forward',
|
|
104
|
+
},
|
|
105
|
+
render: createButton
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const WithBothIcons = {
|
|
109
|
+
args: {
|
|
110
|
+
variant: 'primary',
|
|
111
|
+
label: 'Both Icons',
|
|
112
|
+
iconStart: 'check',
|
|
113
|
+
iconEnd: 'close',
|
|
114
|
+
},
|
|
115
|
+
render: createButton
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const Disabled = {
|
|
119
|
+
args: {
|
|
120
|
+
variant: 'primary',
|
|
121
|
+
label: 'Disabled',
|
|
122
|
+
disabled: true,
|
|
123
|
+
},
|
|
124
|
+
render: createButton
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const AllVariants = {
|
|
128
|
+
render: () => {
|
|
129
|
+
const container = document.createElement('div');
|
|
130
|
+
container.style.cssText = 'display: flex; flex-direction: column; gap: 20px;';
|
|
131
|
+
|
|
132
|
+
// Row 1: Enabled
|
|
133
|
+
const row1 = document.createElement('div');
|
|
134
|
+
row1.style.cssText = 'display: flex; gap: 16px; flex-wrap: wrap; align-items: center;';
|
|
135
|
+
|
|
136
|
+
['primary', 'secondary', 'outline', 'action', 'tertiary'].forEach(variant => {
|
|
137
|
+
row1.appendChild(createButton({ variant, label: variant.charAt(0).toUpperCase() + variant.slice(1) }));
|
|
138
|
+
});
|
|
139
|
+
container.appendChild(row1);
|
|
140
|
+
|
|
141
|
+
// Row 2: Disabled
|
|
142
|
+
const row2 = document.createElement('div');
|
|
143
|
+
row2.style.cssText = 'display: flex; gap: 16px; flex-wrap: wrap; align-items: center;';
|
|
144
|
+
|
|
145
|
+
['primary', 'secondary', 'outline', 'action', 'tertiary'].forEach(variant => {
|
|
146
|
+
row2.appendChild(createButton({ variant, label: variant.charAt(0).toUpperCase() + variant.slice(1), disabled: true }));
|
|
147
|
+
});
|
|
148
|
+
container.appendChild(row2);
|
|
149
|
+
|
|
150
|
+
return container;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import './ds-button.js';
|
|
3
|
+
|
|
4
|
+
describe('ds-button', () => {
|
|
5
|
+
let container;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
container = document.createElement('div');
|
|
9
|
+
document.body.appendChild(container);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
container.remove();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should render default primary variant', async () => {
|
|
17
|
+
container.innerHTML = '<ds-button>Click me</ds-button>';
|
|
18
|
+
const el = container.querySelector('ds-button');
|
|
19
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
20
|
+
|
|
21
|
+
const button = el.shadowRoot.querySelector('button');
|
|
22
|
+
expect(button).toBeTruthy();
|
|
23
|
+
expect(el.variant).toBe('primary');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should reflect variant attribute', async () => {
|
|
27
|
+
container.innerHTML = '<ds-button variant="secondary">Secondary</ds-button>';
|
|
28
|
+
const el = container.querySelector('ds-button');
|
|
29
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
30
|
+
|
|
31
|
+
expect(el.variant).toBe('secondary');
|
|
32
|
+
expect(el.getAttribute('variant')).toBe('secondary');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should render disabled state', async () => {
|
|
36
|
+
container.innerHTML = '<ds-button disabled>Disabled</ds-button>';
|
|
37
|
+
const el = container.querySelector('ds-button');
|
|
38
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
39
|
+
|
|
40
|
+
const button = el.shadowRoot.querySelector('button');
|
|
41
|
+
expect(button.disabled).toBe(true);
|
|
42
|
+
expect(el.hasAttribute('disabled')).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should render slots correctly', async () => {
|
|
46
|
+
container.innerHTML = `
|
|
47
|
+
<ds-button>
|
|
48
|
+
<div slot="icon-start">Start</div>
|
|
49
|
+
Label
|
|
50
|
+
<div slot="icon-end">End</div>
|
|
51
|
+
</ds-button>
|
|
52
|
+
`;
|
|
53
|
+
const el = container.querySelector('ds-button');
|
|
54
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
55
|
+
|
|
56
|
+
const slots = el.shadowRoot.querySelectorAll('slot');
|
|
57
|
+
expect(slots.length).toBe(3);
|
|
58
|
+
expect(slots[0].name).toBe('icon-start');
|
|
59
|
+
expect(slots[1].name).toBe('');
|
|
60
|
+
expect(slots[2].name).toBe('icon-end');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DsButton } from './ds-button.js';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Button component for the Design System
|
|
5
|
+
*
|
|
6
|
+
* @element ds-button-group
|
|
7
|
+
*
|
|
8
|
+
* @prop {string} align - Layout/Alignment of buttons: 'start' | 'end' | 'stack' (default: 'start')
|
|
9
|
+
* 'start': Left aligned
|
|
10
|
+
* 'end': Right aligned (reversed visual order)
|
|
11
|
+
* 'stack': Vertical stack, full width
|
|
12
|
+
*
|
|
13
|
+
* @slot - Button content
|
|
14
|
+
*/
|
|
15
|
+
export class DsButtonGroup extends LitElement {
|
|
16
|
+
static properties = {
|
|
17
|
+
align: { type: String, reflect: true }
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
static styles = css`
|
|
21
|
+
:host {
|
|
22
|
+
display: flex;
|
|
23
|
+
width: 100%;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Base Layout */
|
|
27
|
+
.button-group {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-wrap: wrap;
|
|
30
|
+
width: 100%;
|
|
31
|
+
gap: var(--ds-space-sm, 8px);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* Alignment / Layout Variants
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/* Start (Default) */
|
|
39
|
+
:host([align="start"]) .button-group {
|
|
40
|
+
flex-direction: row;
|
|
41
|
+
justify-content: flex-start;
|
|
42
|
+
align-items: center;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* End (Visual Inversion for Dialogs) */
|
|
46
|
+
:host([align="end"]) .button-group {
|
|
47
|
+
flex-direction: row-reverse;
|
|
48
|
+
justify-content: flex-start;
|
|
49
|
+
align-items: center;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Stack (Vertical, Full Width) */
|
|
53
|
+
:host([align="stack"]) .button-group {
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
align-items: stretch;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Ensure buttons take full width when stacked */
|
|
59
|
+
:host([align="stack"]) ::slotted(ds-button) {
|
|
60
|
+
width: 100%;
|
|
61
|
+
display: flex; /* Ensure ds-button behaves as a flex container if it isn't already, or fills width */
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Specific fix for ds-button internal styling if needed */
|
|
65
|
+
/* If ds-button host is inline-block, width:100% works but we might need to ensure its internal button is also 100% */
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
constructor() {
|
|
69
|
+
super();
|
|
70
|
+
this.align = 'start';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
render() {
|
|
74
|
+
return html`
|
|
75
|
+
<div class="button-group">
|
|
76
|
+
<slot></slot>
|
|
77
|
+
</div>
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
customElements.define('ds-button-group', DsButtonGroup);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Meta, Story, Canvas, Controls } from '@storybook/blocks';
|
|
2
|
+
import { DsButtonGroup } from './ds-button-group';
|
|
3
|
+
import * as stories from './ds-button-group.stories';
|
|
4
|
+
|
|
5
|
+
<Meta title="Components/Button Group" component={DsButtonGroup} />
|
|
6
|
+
|
|
7
|
+
# Button Group
|
|
8
|
+
|
|
9
|
+
The `ds-button-group` component is used to group related buttons together. It supports horizontal and vertical stacking, custom spacing, and alignment options.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<ds-button-group>
|
|
15
|
+
<ds-button>Primary</ds-button>
|
|
16
|
+
<ds-button variant="secondary">Secondary</ds-button>
|
|
17
|
+
</ds-button-group>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### Default (Left Aligned)
|
|
23
|
+
<Canvas>
|
|
24
|
+
<Story name="Default" story={stories.Default} />
|
|
25
|
+
</Canvas>
|
|
26
|
+
|
|
27
|
+
### Right Aligned (Dialog Footer)
|
|
28
|
+
<Canvas>
|
|
29
|
+
<Story name="RightAligned" story={stories.RightAligned} />
|
|
30
|
+
</Canvas>
|
|
31
|
+
|
|
32
|
+
### Vertical Stack
|
|
33
|
+
<Canvas>
|
|
34
|
+
<Story name="VerticalStack" story={stories.Stacked} />
|
|
35
|
+
</Canvas>
|
|
36
|
+
|
|
37
|
+
## API
|
|
38
|
+
|
|
39
|
+
<Controls />
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import './ds-button-group';
|
|
3
|
+
import '../ds-button/ds-button';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Components/Button Group',
|
|
7
|
+
component: 'ds-button-group',
|
|
8
|
+
argTypes: {
|
|
9
|
+
align: {
|
|
10
|
+
control: { type: 'select' },
|
|
11
|
+
options: ['start', 'end', 'stack'],
|
|
12
|
+
description: 'Layout of the buttons. "end" reverses visual order. "stack" is vertical full-width.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const Template = ({ align }) => html`
|
|
18
|
+
<div style="max-width: 300px;">
|
|
19
|
+
<ds-button-group align=${align}>
|
|
20
|
+
<ds-button variant="primary">Primary</ds-button>
|
|
21
|
+
<ds-button variant="secondary">Secondary</ds-button>
|
|
22
|
+
<ds-button variant="action">Action</ds-button>
|
|
23
|
+
</ds-button-group>
|
|
24
|
+
</div>
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
export const Default = Template.bind({});
|
|
28
|
+
Default.args = {
|
|
29
|
+
align: 'start',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const RightAligned = Template.bind({});
|
|
33
|
+
RightAligned.args = {
|
|
34
|
+
align: 'end',
|
|
35
|
+
};
|
|
36
|
+
RightAligned.parameters = {
|
|
37
|
+
docs: {
|
|
38
|
+
description: {
|
|
39
|
+
story: 'Visual Order is reversed [Action] [Secondary] [Primary] relative to DOM.',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const Stacked = Template.bind({});
|
|
45
|
+
Stacked.args = {
|
|
46
|
+
align: 'stack',
|
|
47
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import './ds-button-group.js';
|
|
3
|
+
import '../ds-button/ds-button.js';
|
|
4
|
+
|
|
5
|
+
describe('ds-button-group', () => {
|
|
6
|
+
let container;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
container = document.createElement('div');
|
|
10
|
+
document.body.appendChild(container);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
container.remove();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('renders with default values', async () => {
|
|
18
|
+
container.innerHTML = `
|
|
19
|
+
<ds-button-group>
|
|
20
|
+
<ds-button>Button 1</ds-button>
|
|
21
|
+
<ds-button>Button 2</ds-button>
|
|
22
|
+
</ds-button-group>
|
|
23
|
+
`;
|
|
24
|
+
const el = container.querySelector('ds-button-group');
|
|
25
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
26
|
+
|
|
27
|
+
expect(el).toBeTruthy();
|
|
28
|
+
expect(el.align).toBe('start');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('applies correct alignment attribute', async () => {
|
|
32
|
+
container.innerHTML = '<ds-button-group align="end"></ds-button-group>';
|
|
33
|
+
const el = container.querySelector('ds-button-group');
|
|
34
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
35
|
+
|
|
36
|
+
expect(el.getAttribute('align')).toBe('end');
|
|
37
|
+
expect(el.align).toBe('end');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('applies stack alignment', async () => {
|
|
41
|
+
container.innerHTML = '<ds-button-group align="stack"></ds-button-group>';
|
|
42
|
+
const el = container.querySelector('ds-button-group');
|
|
43
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
44
|
+
|
|
45
|
+
expect(el.getAttribute('align')).toBe('stack');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ds-button-group';
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import axe from 'axe-core';
|
|
3
|
+
import './ds-checkbox.js';
|
|
4
|
+
|
|
5
|
+
describe('ds-checkbox a11y', () => {
|
|
6
|
+
let container;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
container = document.createElement('div');
|
|
10
|
+
document.body.appendChild(container);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
container.remove();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should pass axe accessibility checks in default state', async () => {
|
|
18
|
+
container.innerHTML = '<ds-checkbox label="Accept terms"></ds-checkbox>';
|
|
19
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
20
|
+
|
|
21
|
+
const results = await axe.run(container, {
|
|
22
|
+
rules: { 'color-contrast': { enabled: false } }
|
|
23
|
+
});
|
|
24
|
+
if (results.violations.length > 0) {
|
|
25
|
+
console.log('Violations:', JSON.stringify(results.violations, null, 2));
|
|
26
|
+
}
|
|
27
|
+
expect(results.violations).toHaveLength(0);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should pass axe accessibility checks when checked', async () => {
|
|
31
|
+
container.innerHTML = '<ds-checkbox label="I agree" checked></ds-checkbox>';
|
|
32
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
33
|
+
|
|
34
|
+
const results = await axe.run(container, {
|
|
35
|
+
rules: { 'color-contrast': { enabled: false } }
|
|
36
|
+
});
|
|
37
|
+
expect(results.violations).toHaveLength(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should pass axe accessibility checks when indeterminate', async () => {
|
|
41
|
+
container.innerHTML = '<ds-checkbox label="Select all" indeterminate></ds-checkbox>';
|
|
42
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
43
|
+
|
|
44
|
+
const results = await axe.run(container, {
|
|
45
|
+
rules: { 'color-contrast': { enabled: false } }
|
|
46
|
+
});
|
|
47
|
+
expect(results.violations).toHaveLength(0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should pass axe accessibility checks when disabled', async () => {
|
|
51
|
+
container.innerHTML = '<ds-checkbox label="Disabled option" disabled></ds-checkbox>';
|
|
52
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
53
|
+
|
|
54
|
+
const results = await axe.run(container, {
|
|
55
|
+
rules: { 'color-contrast': { enabled: false } }
|
|
56
|
+
});
|
|
57
|
+
expect(results.violations).toHaveLength(0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should pass axe accessibility checks in error state', async () => {
|
|
61
|
+
container.innerHTML = '<ds-checkbox label="Required field" validation-status="error"></ds-checkbox>';
|
|
62
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
63
|
+
|
|
64
|
+
const results = await axe.run(container, {
|
|
65
|
+
rules: { 'color-contrast': { enabled: false } }
|
|
66
|
+
});
|
|
67
|
+
expect(results.violations).toHaveLength(0);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should pass axe accessibility checks with required attribute', async () => {
|
|
71
|
+
container.innerHTML = '<ds-checkbox label="Required checkbox" required></ds-checkbox>';
|
|
72
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
73
|
+
|
|
74
|
+
const results = await axe.run(container, {
|
|
75
|
+
rules: { 'color-contrast': { enabled: false } }
|
|
76
|
+
});
|
|
77
|
+
expect(results.violations).toHaveLength(0);
|
|
78
|
+
});
|
|
79
|
+
});
|