@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.
Files changed (230) hide show
  1. package/package.json +27 -0
  2. package/src/ds-accordion/ds-accordion-item.js +288 -0
  3. package/src/ds-accordion/ds-accordion-item.stories.js +82 -0
  4. package/src/ds-accordion/ds-accordion.a11y.test.js +92 -0
  5. package/src/ds-accordion/ds-accordion.js +68 -0
  6. package/src/ds-accordion/ds-accordion.stories.js +118 -0
  7. package/src/ds-accordion/ds-accordion.test.js +146 -0
  8. package/src/ds-accordion/index.js +2 -0
  9. package/src/ds-action-bar/ds-action-bar.js +116 -0
  10. package/src/ds-action-bar/ds-action-bar.stories.js +86 -0
  11. package/src/ds-action-bar/ds-action-bar.test.js +64 -0
  12. package/src/ds-action-bar/index.js +1 -0
  13. package/src/ds-alert/ds-alert.a11y.test.js +151 -0
  14. package/src/ds-alert/ds-alert.js +223 -0
  15. package/src/ds-alert/ds-alert.mdx +142 -0
  16. package/src/ds-alert/ds-alert.stories.js +166 -0
  17. package/src/ds-alert/ds-alert.test.js +256 -0
  18. package/src/ds-alert/index.js +1 -0
  19. package/src/ds-avatar/ds-avatar.a11y.test.js +45 -0
  20. package/src/ds-avatar/ds-avatar.js +216 -0
  21. package/src/ds-avatar/ds-avatar.stories.js +120 -0
  22. package/src/ds-avatar/ds-avatar.test.js +83 -0
  23. package/src/ds-avatar/index.js +1 -0
  24. package/src/ds-avatar-extended/ds-avatar-extended.a11y.test.js +29 -0
  25. package/src/ds-avatar-extended/ds-avatar-extended.js +108 -0
  26. package/src/ds-avatar-extended/ds-avatar-extended.stories.js +93 -0
  27. package/src/ds-avatar-extended/ds-avatar-extended.test.js +66 -0
  28. package/src/ds-avatar-extended/index.js +1 -0
  29. package/src/ds-banner/ds-banner.a11y.test.js +51 -0
  30. package/src/ds-banner/ds-banner.js +233 -0
  31. package/src/ds-banner/ds-banner.stories.js +185 -0
  32. package/src/ds-banner/ds-banner.test.js +116 -0
  33. package/src/ds-banner/index.js +1 -0
  34. package/src/ds-breadcrumb-item/ds-breadcrumb-item.js +135 -0
  35. package/src/ds-breadcrumb-item/ds-breadcrumb-item.stories.js +49 -0
  36. package/src/ds-breadcrumb-item/ds-breadcrumb-item.test.js +55 -0
  37. package/src/ds-breadcrumbs/ds-breadcrumbs.js +194 -0
  38. package/src/ds-breadcrumbs/ds-breadcrumbs.stories.js +54 -0
  39. package/src/ds-breadcrumbs/ds-breadcrumbs.test.js +33 -0
  40. package/src/ds-button/ds-button.a11y.test.js +49 -0
  41. package/src/ds-button/ds-button.js +205 -0
  42. package/src/ds-button/ds-button.mdx +141 -0
  43. package/src/ds-button/ds-button.stories.js +152 -0
  44. package/src/ds-button/ds-button.test.js +62 -0
  45. package/src/ds-button/index.js +1 -0
  46. package/src/ds-button-group/ds-button-group.js +82 -0
  47. package/src/ds-button-group/ds-button-group.mdx +39 -0
  48. package/src/ds-button-group/ds-button-group.stories.js +47 -0
  49. package/src/ds-button-group/ds-button-group.test.js +47 -0
  50. package/src/ds-button-group/index.js +1 -0
  51. package/src/ds-checkbox/ds-checkbox.a11y.test.js +79 -0
  52. package/src/ds-checkbox/ds-checkbox.js +271 -0
  53. package/src/ds-checkbox/ds-checkbox.stories.js +77 -0
  54. package/src/ds-checkbox/ds-checkbox.test.js +191 -0
  55. package/src/ds-checkbox/index.js +1 -0
  56. package/src/ds-checkbox-group/ds-checkbox-group.a11y.test.js +146 -0
  57. package/src/ds-checkbox-group/ds-checkbox-group.js +235 -0
  58. package/src/ds-checkbox-group/ds-checkbox-group.stories.js +210 -0
  59. package/src/ds-checkbox-group/ds-checkbox-group.test.js +150 -0
  60. package/src/ds-checkbox-group/index.js +1 -0
  61. package/src/ds-dialog/ds-dialog.js +466 -0
  62. package/src/ds-dialog/ds-dialog.stories.js +274 -0
  63. package/src/ds-dialog/ds-dialog.test.js +441 -0
  64. package/src/ds-dialog/index.js +1 -0
  65. package/src/ds-dropdown/ds-dropdown.a11y.test.js +80 -0
  66. package/src/ds-dropdown/ds-dropdown.js +891 -0
  67. package/src/ds-dropdown/ds-dropdown.stories.js +259 -0
  68. package/src/ds-dropdown/ds-dropdown.test.js +268 -0
  69. package/src/ds-dropdown/index.js +1 -0
  70. package/src/ds-dropdown-group/ds-dropdown-group.js +55 -0
  71. package/src/ds-dropdown-panel/ds-dropdown-panel.js +34 -0
  72. package/src/ds-file-uploaded/ds-file-uploaded.a11y.test.js +40 -0
  73. package/src/ds-file-uploaded/ds-file-uploaded.js +135 -0
  74. package/src/ds-file-uploaded/ds-file-uploaded.mdx +33 -0
  75. package/src/ds-file-uploaded/ds-file-uploaded.stories.js +81 -0
  76. package/src/ds-file-uploaded/ds-file-uploaded.test.js +85 -0
  77. package/src/ds-file-uploader/ds-file-uploader.a11y.test.js +61 -0
  78. package/src/ds-file-uploader/ds-file-uploader.js +442 -0
  79. package/src/ds-file-uploader/ds-file-uploader.mdx +44 -0
  80. package/src/ds-file-uploader/ds-file-uploader.stories.js +76 -0
  81. package/src/ds-file-uploader/ds-file-uploader.test.js +142 -0
  82. package/src/ds-header/ds-header.a11y.test.js +38 -0
  83. package/src/ds-header/ds-header.js +149 -0
  84. package/src/ds-header/ds-header.stories.js +63 -0
  85. package/src/ds-header/ds-header.test.js +52 -0
  86. package/src/ds-header/index.js +1 -0
  87. package/src/ds-header-nav/ds-header-nav.a11y.test.js +69 -0
  88. package/src/ds-header-nav/ds-header-nav.js +114 -0
  89. package/src/ds-header-nav/ds-header-nav.stories.js +17 -0
  90. package/src/ds-header-nav/ds-header-nav.test.js +93 -0
  91. package/src/ds-header-nav-item/ds-header-nav-item.a11y.test.js +71 -0
  92. package/src/ds-header-nav-item/ds-header-nav-item.js +124 -0
  93. package/src/ds-header-nav-item/ds-header-nav-item.stories.js +43 -0
  94. package/src/ds-header-nav-item/ds-header-nav-item.test.js +61 -0
  95. package/src/ds-icon/ds-icon.a11y.test.js +49 -0
  96. package/src/ds-icon/ds-icon.js +75 -0
  97. package/src/ds-icon/ds-icon.mdx +36 -0
  98. package/src/ds-icon/ds-icon.stories.js +88 -0
  99. package/src/ds-icon/ds-icon.test.js +97 -0
  100. package/src/ds-icon/index.js +1 -0
  101. package/src/ds-icon-button/ds-icon-button.a11y.test.js +55 -0
  102. package/src/ds-icon-button/ds-icon-button.js +224 -0
  103. package/src/ds-icon-button/ds-icon-button.mdx +131 -0
  104. package/src/ds-icon-button/ds-icon-button.stories.js +128 -0
  105. package/src/ds-icon-button/ds-icon-button.test.js +90 -0
  106. package/src/ds-icon-button/index.js +1 -0
  107. package/src/ds-input/ds-input.a11y.test.js +145 -0
  108. package/src/ds-input/ds-input.js +645 -0
  109. package/src/ds-input/ds-input.mdx +251 -0
  110. package/src/ds-input/ds-input.stories.js +298 -0
  111. package/src/ds-input/ds-input.test.js +792 -0
  112. package/src/ds-input/index.js +1 -0
  113. package/src/ds-link/ds-link.js +111 -0
  114. package/src/ds-link/ds-link.stories.js +56 -0
  115. package/src/ds-link/ds-link.test.js +74 -0
  116. package/src/ds-list-item/ds-list-item.a11y.test.js +39 -0
  117. package/src/ds-list-item/ds-list-item.js +292 -0
  118. package/src/ds-list-item/ds-list-item.stories.js +101 -0
  119. package/src/ds-list-item/ds-list-item.test.js +63 -0
  120. package/src/ds-menu/ds-menu.js +30 -0
  121. package/src/ds-menu/ds-menu.stories.js +120 -0
  122. package/src/ds-menu/ds-menu.test.js +123 -0
  123. package/src/ds-menu-group/ds-menu-group.js +101 -0
  124. package/src/ds-menu-group/ds-menu-group.stories.js +99 -0
  125. package/src/ds-nav-item/ds-nav-item.a11y.test.js +91 -0
  126. package/src/ds-nav-item/ds-nav-item.js +307 -0
  127. package/src/ds-nav-item/ds-nav-item.stories.js +99 -0
  128. package/src/ds-nav-item/ds-nav-item.test.js +169 -0
  129. package/src/ds-nav-item/index.js +1 -0
  130. package/src/ds-nav-vertical/ds-nav-vertical.a11y.test.js +69 -0
  131. package/src/ds-nav-vertical/ds-nav-vertical.js +173 -0
  132. package/src/ds-nav-vertical/ds-nav-vertical.stories.js +124 -0
  133. package/src/ds-nav-vertical/ds-nav-vertical.test.js +176 -0
  134. package/src/ds-nav-vertical/index.js +1 -0
  135. package/src/ds-pagination/ds-pagination.a11y.test.js +50 -0
  136. package/src/ds-pagination/ds-pagination.js +232 -0
  137. package/src/ds-pagination/ds-pagination.stories.js +63 -0
  138. package/src/ds-pagination/ds-pagination.test.js +141 -0
  139. package/src/ds-pagination/index.js +1 -0
  140. package/src/ds-progress-bar/ds-progress-bar.a11y.test.js +25 -0
  141. package/src/ds-progress-bar/ds-progress-bar.js +81 -0
  142. package/src/ds-progress-bar/ds-progress-bar.stories.js +69 -0
  143. package/src/ds-progress-bar/ds-progress-bar.test.js +60 -0
  144. package/src/ds-radio/ds-radio.a11y.test.js +69 -0
  145. package/src/ds-radio/ds-radio.js +240 -0
  146. package/src/ds-radio/ds-radio.stories.js +102 -0
  147. package/src/ds-radio/ds-radio.test.js +114 -0
  148. package/src/ds-radio/index.js +1 -0
  149. package/src/ds-radio-group/ds-radio-group.a11y.test.js +164 -0
  150. package/src/ds-radio-group/ds-radio-group.js +257 -0
  151. package/src/ds-radio-group/ds-radio-group.stories.js +247 -0
  152. package/src/ds-radio-group/ds-radio-group.test.js +194 -0
  153. package/src/ds-radio-group/index.js +1 -0
  154. package/src/ds-rich-list/ds-rich-list.js +246 -0
  155. package/src/ds-rich-list/ds-rich-list.stories.js +368 -0
  156. package/src/ds-rich-list/ds-rich-list.test.js +293 -0
  157. package/src/ds-rich-list-item/ds-rich-list-item.js +579 -0
  158. package/src/ds-rich-list-item/ds-rich-list-item.stories.js +197 -0
  159. package/src/ds-rich-list-item/ds-rich-list-item.test.js +434 -0
  160. package/src/ds-slider/ds-slider.js +399 -0
  161. package/src/ds-slider/ds-slider.stories.js +107 -0
  162. package/src/ds-slider/ds-slider.test.js +308 -0
  163. package/src/ds-spinner/ds-spinner.js +173 -0
  164. package/src/ds-spinner/ds-spinner.stories.js +52 -0
  165. package/src/ds-spinner/ds-spinner.test.js +50 -0
  166. package/src/ds-status-border/ds-status-border.js +88 -0
  167. package/src/ds-status-border/ds-status-border.stories.js +242 -0
  168. package/src/ds-status-border/ds-status-border.test.js +168 -0
  169. package/src/ds-stepper/ds-stepper.a11y.test.js +198 -0
  170. package/src/ds-stepper/ds-stepper.js +207 -0
  171. package/src/ds-stepper/ds-stepper.stories.js +530 -0
  172. package/src/ds-stepper/ds-stepper.test.js +311 -0
  173. package/src/ds-stepper-item/ds-stepper-item.js +485 -0
  174. package/src/ds-stepper-item/ds-stepper-item.stories.js +288 -0
  175. package/src/ds-switch/ds-switch.js +348 -0
  176. package/src/ds-switch/ds-switch.stories.js +145 -0
  177. package/src/ds-switch/ds-switch.test.js +226 -0
  178. package/src/ds-switch/index.js +1 -0
  179. package/src/ds-tab-item/ds-tab-item.js +341 -0
  180. package/src/ds-tab-item/ds-tab-item.stories.js +69 -0
  181. package/src/ds-tabs/ds-tab-panel.js +48 -0
  182. package/src/ds-tabs/ds-tabs.a11y.test.js +56 -0
  183. package/src/ds-tabs/ds-tabs.js +180 -0
  184. package/src/ds-tabs/ds-tabs.stories.js +152 -0
  185. package/src/ds-tabs/ds-tabs.test.js +306 -0
  186. package/src/ds-tabs/index.js +3 -0
  187. package/src/ds-tag-action/ds-tag-action.a11y.test.js +32 -0
  188. package/src/ds-tag-action/ds-tag-action.js +185 -0
  189. package/src/ds-tag-action/ds-tag-action.stories.js +55 -0
  190. package/src/ds-tag-action/ds-tag-action.test.js +44 -0
  191. package/src/ds-tag-removable/ds-tag-removable.a11y.test.js +24 -0
  192. package/src/ds-tag-removable/ds-tag-removable.js +146 -0
  193. package/src/ds-tag-removable/ds-tag-removable.stories.js +52 -0
  194. package/src/ds-tag-removable/ds-tag-removable.test.js +46 -0
  195. package/src/ds-tag-status/ds-tag-status.a11y.test.js +93 -0
  196. package/src/ds-tag-status/ds-tag-status.js +164 -0
  197. package/src/ds-tag-status/ds-tag-status.stories.js +200 -0
  198. package/src/ds-tag-status/ds-tag-status.test.js +140 -0
  199. package/src/ds-tag-status/index.js +1 -0
  200. package/src/ds-textarea/ds-textarea-clearable.test.js +89 -0
  201. package/src/ds-textarea/ds-textarea.a11y.test.js +66 -0
  202. package/src/ds-textarea/ds-textarea.js +505 -0
  203. package/src/ds-textarea/ds-textarea.stories.js +335 -0
  204. package/src/ds-textarea/ds-textarea.test.js +218 -0
  205. package/src/ds-textarea/index.js +1 -0
  206. package/src/ds-thumbnail/ds-thumbnail.js +207 -0
  207. package/src/ds-thumbnail/ds-thumbnail.stories.js +217 -0
  208. package/src/ds-thumbnail/ds-thumbnail.test.js +220 -0
  209. package/src/ds-toast/ds-toast-provider.js +110 -0
  210. package/src/ds-toast/ds-toast.a11y.test.js +34 -0
  211. package/src/ds-toast/ds-toast.js +243 -0
  212. package/src/ds-toast/ds-toast.stories.js +143 -0
  213. package/src/ds-toast/ds-toast.test.js +93 -0
  214. package/src/ds-toast/index.js +2 -0
  215. package/src/ds-tooltip/ds-tooltip.a11y.test.js +110 -0
  216. package/src/ds-tooltip/ds-tooltip.js +217 -0
  217. package/src/ds-tooltip/ds-tooltip.mdx +75 -0
  218. package/src/ds-tooltip/ds-tooltip.stories.js +72 -0
  219. package/src/ds-tooltip/ds-tooltip.test.js +191 -0
  220. package/src/ds-tooltip/index.js +1 -0
  221. package/src/ds-tooltip/positioner.js +117 -0
  222. package/src/index.js +50 -0
  223. package/src/mixins/field-label.mixin.js +113 -0
  224. package/src/mixins/field-message.mixin.js +66 -0
  225. package/src/token-provider/index.js +1 -0
  226. package/src/token-provider/token-provider.a11y.test.js +44 -0
  227. package/src/token-provider/token-provider.js +85 -0
  228. package/src/token-provider/token-provider.stories.js +105 -0
  229. package/src/token-provider/token-provider.test.js +134 -0
  230. package/src/utils/number-input.utils.js +42 -0
@@ -0,0 +1,200 @@
1
+ import { html } from 'lit';
2
+ import './ds-tag-status.js';
3
+
4
+ export default {
5
+ title: 'Components/Tag Status',
6
+ component: 'ds-tag-status',
7
+ tags: ['autodocs'],
8
+ argTypes: {
9
+ status: {
10
+ control: 'select',
11
+ options: ['error', 'warning', 'success', 'info', 'neutral'],
12
+ description: 'Status type that determines background color and icon',
13
+ table: {
14
+ defaultValue: { summary: 'neutral' }
15
+ }
16
+ },
17
+ size: {
18
+ control: 'select',
19
+ options: ['s', 'xs'],
20
+ description: 'Size variant. Size S shows icon, XS does not.',
21
+ table: {
22
+ defaultValue: { summary: 's' }
23
+ }
24
+ },
25
+ label: {
26
+ control: 'text',
27
+ description: 'Label text to display'
28
+ },
29
+ hideIcon: {
30
+ control: 'boolean',
31
+ description: 'Hide the icon in size S',
32
+ table: {
33
+ defaultValue: { summary: 'false' }
34
+ }
35
+ },
36
+ icon: {
37
+ control: 'text',
38
+ description: 'Custom icon name (overrides status-based icon)'
39
+ }
40
+ }
41
+ };
42
+
43
+ /**
44
+ * Default tag status with neutral status and size S
45
+ */
46
+ export const Default = {
47
+ args: {
48
+ label: 'Draft',
49
+ status: 'neutral',
50
+ size: 's'
51
+ }
52
+ };
53
+
54
+ /**
55
+ * Error status for critical/failed states
56
+ */
57
+ export const Error = {
58
+ args: {
59
+ label: 'Failed',
60
+ status: 'error',
61
+ size: 's'
62
+ }
63
+ };
64
+
65
+ /**
66
+ * Warning status for caution/attention states
67
+ */
68
+ export const Warning = {
69
+ args: {
70
+ label: 'Pending Review',
71
+ status: 'warning',
72
+ size: 's'
73
+ }
74
+ };
75
+
76
+ /**
77
+ * Success status for completed/positive states
78
+ */
79
+ export const Success = {
80
+ args: {
81
+ label: 'Active',
82
+ status: 'success',
83
+ size: 's'
84
+ }
85
+ };
86
+
87
+ /**
88
+ * Info status for informational states
89
+ */
90
+ export const Info = {
91
+ args: {
92
+ label: 'Processing',
93
+ status: 'info',
94
+ size: 's'
95
+ }
96
+ };
97
+
98
+ /**
99
+ * Neutral status for default/inactive states
100
+ */
101
+ export const Neutral = {
102
+ args: {
103
+ label: 'Draft',
104
+ status: 'neutral',
105
+ size: 's'
106
+ }
107
+ };
108
+
109
+ /**
110
+ * Extra small size without icon
111
+ */
112
+ export const SizeXS = {
113
+ args: {
114
+ label: 'New',
115
+ status: 'success',
116
+ size: 'xs'
117
+ }
118
+ };
119
+
120
+ /**
121
+ * Size S with icon hidden
122
+ */
123
+ export const WithoutIcon = {
124
+ args: {
125
+ label: 'Complete',
126
+ status: 'success',
127
+ size: 's',
128
+ hideIcon: true
129
+ }
130
+ };
131
+
132
+ /**
133
+ * Neutral status with custom icon
134
+ */
135
+ export const CustomIcon = {
136
+ args: {
137
+ label: 'Scheduled',
138
+ status: 'neutral',
139
+ size: 's',
140
+ icon: 'schedule'
141
+ }
142
+ };
143
+
144
+ /**
145
+ * All status variants displayed together
146
+ */
147
+ export const AllStatuses = {
148
+ render: () => html`
149
+ <div style="display: flex; gap: 8px; flex-wrap: wrap; align-items: center;">
150
+ <ds-tag-status status="error" label="Error"></ds-tag-status>
151
+ <ds-tag-status status="warning" label="Warning"></ds-tag-status>
152
+ <ds-tag-status status="success" label="Success"></ds-tag-status>
153
+ <ds-tag-status status="info" label="Info"></ds-tag-status>
154
+ <ds-tag-status status="neutral" label="Neutral"></ds-tag-status>
155
+ </div>
156
+ `
157
+ };
158
+
159
+ /**
160
+ * All size variants displayed together
161
+ */
162
+ export const AllSizes = {
163
+ render: () => html`
164
+ <div style="display: flex; gap: 8px; align-items: center;">
165
+ <ds-tag-status size="s" status="success" label="Size S"></ds-tag-status>
166
+ <ds-tag-status size="xs" status="success" label="Size XS"></ds-tag-status>
167
+ </div>
168
+ `
169
+ };
170
+
171
+ /**
172
+ * Status tags in a real-world context
173
+ */
174
+ export const InContext = {
175
+ render: () => html`
176
+ <div style="display: flex; flex-direction: column; gap: 12px; font-family: var(--ds-font-family-content);">
177
+ <div style="display: flex; align-items: center; gap: 12px;">
178
+ <span style="color: var(--ds-color-text-default);">Order #12345</span>
179
+ <ds-tag-status status="success" label="Delivered"></ds-tag-status>
180
+ </div>
181
+ <div style="display: flex; align-items: center; gap: 12px;">
182
+ <span style="color: var(--ds-color-text-default);">Order #12346</span>
183
+ <ds-tag-status status="warning" label="In Transit"></ds-tag-status>
184
+ </div>
185
+ <div style="display: flex; align-items: center; gap: 12px;">
186
+ <span style="color: var(--ds-color-text-default);">Order #12347</span>
187
+ <ds-tag-status status="error" label="Cancelled"></ds-tag-status>
188
+ </div>
189
+ <div style="display: flex; align-items: center; gap: 12px;">
190
+ <span style="color: var(--ds-color-text-default);">Order #12348</span>
191
+ <ds-tag-status status="info" label="Processing"></ds-tag-status>
192
+ </div>
193
+ <div style="display: flex; align-items: center; gap: 12px;">
194
+ <span style="color: var(--ds-color-text-default);">Order #12349</span>
195
+ <ds-tag-status status="neutral" label="Draft"></ds-tag-status>
196
+ </div>
197
+ </div>
198
+ `
199
+ };
200
+
@@ -0,0 +1,140 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import './ds-tag-status.js';
3
+
4
+ describe('ds-tag-status', () => {
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('renders with default values', async () => {
17
+ container.innerHTML = '<ds-tag-status label="Status"></ds-tag-status>';
18
+ const element = container.querySelector('ds-tag-status');
19
+ await new Promise(resolve => setTimeout(resolve, 50));
20
+
21
+ expect(element.status).toBe('neutral');
22
+ expect(element.size).toBe('s');
23
+ expect(element.label).toBe('Status');
24
+ expect(element.hideIcon).toBe(false);
25
+ });
26
+
27
+ it('renders label correctly', async () => {
28
+ container.innerHTML = '<ds-tag-status label="Active"></ds-tag-status>';
29
+ const element = container.querySelector('ds-tag-status');
30
+ await new Promise(resolve => setTimeout(resolve, 50));
31
+
32
+ const label = element.shadowRoot.querySelector('.label');
33
+ expect(label.textContent).toBe('Active');
34
+ });
35
+
36
+ it('renders icon in size S by default', async () => {
37
+ container.innerHTML = '<ds-tag-status status="error" label="Error"></ds-tag-status>';
38
+ const element = container.querySelector('ds-tag-status');
39
+ await new Promise(resolve => setTimeout(resolve, 50));
40
+
41
+ const icon = element.shadowRoot.querySelector('ds-icon');
42
+ expect(icon).toBeTruthy();
43
+ expect(icon.getAttribute('name')).toBe('cancel');
44
+ });
45
+
46
+ it('does not render icon in size XS', async () => {
47
+ container.innerHTML = '<ds-tag-status size="xs" status="error" label="Error"></ds-tag-status>';
48
+ const element = container.querySelector('ds-tag-status');
49
+ await new Promise(resolve => setTimeout(resolve, 50));
50
+
51
+ const icon = element.shadowRoot.querySelector('ds-icon');
52
+ expect(icon).toBeFalsy();
53
+ });
54
+
55
+ it('hides icon when hide-icon is set', async () => {
56
+ container.innerHTML = '<ds-tag-status hide-icon status="success" label="Done"></ds-tag-status>';
57
+ const element = container.querySelector('ds-tag-status');
58
+ await new Promise(resolve => setTimeout(resolve, 50));
59
+
60
+ const icon = element.shadowRoot.querySelector('ds-icon');
61
+ expect(icon).toBeFalsy();
62
+ });
63
+
64
+ it('renders correct icon for each status', async () => {
65
+ const statusIcons = {
66
+ error: 'cancel',
67
+ warning: 'warning',
68
+ success: 'check-circle',
69
+ info: 'info',
70
+ neutral: 'pending'
71
+ };
72
+
73
+ for (const [status, iconName] of Object.entries(statusIcons)) {
74
+ container.innerHTML = `<ds-tag-status status="${status}" label="Test"></ds-tag-status>`;
75
+ const element = container.querySelector('ds-tag-status');
76
+ await new Promise(resolve => setTimeout(resolve, 50));
77
+
78
+ const icon = element.shadowRoot.querySelector('ds-icon');
79
+ expect(icon.getAttribute('name')).toBe(iconName);
80
+ }
81
+ });
82
+
83
+ it('reflects status attribute', async () => {
84
+ container.innerHTML = '<ds-tag-status status="warning" label="Warning"></ds-tag-status>';
85
+ const element = container.querySelector('ds-tag-status');
86
+ await new Promise(resolve => setTimeout(resolve, 50));
87
+
88
+ expect(element.getAttribute('status')).toBe('warning');
89
+ });
90
+
91
+ it('reflects size attribute', async () => {
92
+ container.innerHTML = '<ds-tag-status size="xs" label="Small"></ds-tag-status>';
93
+ const element = container.querySelector('ds-tag-status');
94
+ await new Promise(resolve => setTimeout(resolve, 50));
95
+
96
+ expect(element.getAttribute('size')).toBe('xs');
97
+ });
98
+
99
+ it('has container part for styling', async () => {
100
+ container.innerHTML = '<ds-tag-status label="Test"></ds-tag-status>';
101
+ const element = container.querySelector('ds-tag-status');
102
+ await new Promise(resolve => setTimeout(resolve, 50));
103
+
104
+ const containerEl = element.shadowRoot.querySelector('[part="container"]');
105
+ expect(containerEl).toBeTruthy();
106
+ });
107
+
108
+ it('applies correct background for each status', async () => {
109
+ const statuses = ['error', 'warning', 'success', 'info', 'neutral'];
110
+
111
+ for (const status of statuses) {
112
+ container.innerHTML = `<ds-tag-status status="${status}" label="Test"></ds-tag-status>`;
113
+ const element = container.querySelector('ds-tag-status');
114
+ await new Promise(resolve => setTimeout(resolve, 50));
115
+
116
+ // Just verify the element renders without errors
117
+ const containerEl = element.shadowRoot.querySelector('.container');
118
+ expect(containerEl).toBeTruthy();
119
+ }
120
+ });
121
+
122
+ it('uses custom icon when icon prop is set', async () => {
123
+ container.innerHTML = '<ds-tag-status status="neutral" label="Scheduled" icon="schedule"></ds-tag-status>';
124
+ const element = container.querySelector('ds-tag-status');
125
+ await new Promise(resolve => setTimeout(resolve, 50));
126
+
127
+ const icon = element.shadowRoot.querySelector('ds-icon');
128
+ expect(icon).toBeTruthy();
129
+ expect(icon.getAttribute('name')).toBe('schedule');
130
+ });
131
+
132
+ it('custom icon overrides status icon', async () => {
133
+ container.innerHTML = '<ds-tag-status status="error" label="Custom" icon="star"></ds-tag-status>';
134
+ const element = container.querySelector('ds-tag-status');
135
+ await new Promise(resolve => setTimeout(resolve, 50));
136
+
137
+ const icon = element.shadowRoot.querySelector('ds-icon');
138
+ expect(icon.getAttribute('name')).toBe('star'); // Not 'cancel' (error default)
139
+ });
140
+ });
@@ -0,0 +1 @@
1
+ export { DsTagStatus } from './ds-tag-status.js';
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import './ds-textarea.js';
3
+
4
+ describe('ds-textarea clearable', () => {
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('shows clear button when clearable, focused, and has value', async () => {
17
+ container.innerHTML = '<ds-textarea clearable value="Test"></ds-textarea>';
18
+ const element = container.querySelector('ds-textarea');
19
+ await new Promise(resolve => setTimeout(resolve, 0));
20
+
21
+ // Initially not shown (not focused)
22
+ let clearButton = element.shadowRoot.querySelector('ds-icon-button[icon="close"]');
23
+ expect(clearButton).toBeFalsy();
24
+
25
+ // Focus the textarea
26
+ const textarea = element.shadowRoot.querySelector('textarea');
27
+ textarea.focus();
28
+ textarea.dispatchEvent(new Event('focus'));
29
+ await new Promise(resolve => setTimeout(resolve, 0));
30
+
31
+ // Now should be shown
32
+ clearButton = element.shadowRoot.querySelector('ds-icon-button[icon="close"]');
33
+ expect(clearButton).toBeTruthy();
34
+ });
35
+
36
+ it('clears value when clear button is clicked', async () => {
37
+ container.innerHTML = '<ds-textarea clearable value="Test content"></ds-textarea>';
38
+ const element = container.querySelector('ds-textarea');
39
+ await new Promise(resolve => setTimeout(resolve, 0));
40
+
41
+ const textarea = element.shadowRoot.querySelector('textarea');
42
+ textarea.focus();
43
+ textarea.dispatchEvent(new Event('focus'));
44
+ await new Promise(resolve => setTimeout(resolve, 0));
45
+
46
+ const clearButton = element.shadowRoot.querySelector('ds-icon-button[icon="close"]');
47
+ clearButton.click();
48
+ await new Promise(resolve => setTimeout(resolve, 0));
49
+
50
+ expect(element.value).toBe('');
51
+ expect(textarea.value).toBe('');
52
+ });
53
+
54
+ it('clears value on Escape key when clearable', async () => {
55
+ container.innerHTML = '<ds-textarea clearable value="Test"></ds-textarea>';
56
+ const element = container.querySelector('ds-textarea');
57
+ await new Promise(resolve => setTimeout(resolve, 0));
58
+
59
+ const textarea = element.shadowRoot.querySelector('textarea');
60
+ const keyEvent = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });
61
+ textarea.dispatchEvent(keyEvent);
62
+ await new Promise(resolve => setTimeout(resolve, 0));
63
+
64
+ expect(element.value).toBe('');
65
+ });
66
+
67
+ it('does not show clear button when not focused', async () => {
68
+ container.innerHTML = '<ds-textarea clearable value="Test"></ds-textarea>';
69
+ const element = container.querySelector('ds-textarea');
70
+ await new Promise(resolve => setTimeout(resolve, 0));
71
+
72
+ const clearButton = element.shadowRoot.querySelector('ds-icon-button[icon="close"]');
73
+ expect(clearButton).toBeFalsy();
74
+ });
75
+
76
+ it('does not show clear button when value is empty', async () => {
77
+ container.innerHTML = '<ds-textarea clearable></ds-textarea>';
78
+ const element = container.querySelector('ds-textarea');
79
+ await new Promise(resolve => setTimeout(resolve, 0));
80
+
81
+ const textarea = element.shadowRoot.querySelector('textarea');
82
+ textarea.focus();
83
+ textarea.dispatchEvent(new Event('focus'));
84
+ await new Promise(resolve => setTimeout(resolve, 0));
85
+
86
+ const clearButton = element.shadowRoot.querySelector('ds-icon-button[icon="close"]');
87
+ expect(clearButton).toBeFalsy();
88
+ });
89
+ });
@@ -0,0 +1,66 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import axe from 'axe-core';
3
+ import './ds-textarea.js';
4
+
5
+ describe('ds-textarea accessibility', () => {
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
+ async function runAxe(html) {
18
+ container.innerHTML = html;
19
+ const element = container.querySelector('ds-textarea');
20
+ await new Promise(resolve => setTimeout(resolve, 100));
21
+
22
+ const results = await axe.run(container);
23
+ return results;
24
+ }
25
+
26
+ it('should pass axe accessibility checks for default state', async () => {
27
+ const results = await runAxe('<ds-textarea label="Description"></ds-textarea>');
28
+ expect(results.violations).toHaveLength(0);
29
+ });
30
+
31
+ it('should pass axe accessibility checks with value', async () => {
32
+ const results = await runAxe('<ds-textarea label="Comments" value="Test content"></ds-textarea>');
33
+ expect(results.violations).toHaveLength(0);
34
+ });
35
+
36
+ it('should pass axe accessibility checks for disabled state', async () => {
37
+ const results = await runAxe('<ds-textarea label="Feedback" disabled></ds-textarea>');
38
+ expect(results.violations).toHaveLength(0);
39
+ });
40
+
41
+ it('should pass axe accessibility checks for readonly state', async () => {
42
+ const results = await runAxe('<ds-textarea label="Content" readonly value="Read-only text"></ds-textarea>');
43
+ expect(results.violations).toHaveLength(0);
44
+ });
45
+
46
+ it('should pass axe accessibility checks for required field', async () => {
47
+ const results = await runAxe('<ds-textarea label="Required field" required></ds-textarea>');
48
+ expect(results.violations).toHaveLength(0);
49
+ });
50
+
51
+ it('should pass axe accessibility checks for error state', async () => {
52
+ const results = await runAxe(`
53
+ <ds-textarea
54
+ label="Description"
55
+ validation-status="error"
56
+ validation-message="This field is required"
57
+ ></ds-textarea>
58
+ `);
59
+ expect(results.violations).toHaveLength(0);
60
+ });
61
+
62
+ it('should pass axe accessibility checks with counter', async () => {
63
+ const results = await runAxe('<ds-textarea label="Bio" maxlength="200" show-counter></ds-textarea>');
64
+ expect(results.violations).toHaveLength(0);
65
+ });
66
+ });