@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,242 @@
1
+ import { html } from 'lit';
2
+ import './ds-status-border.js';
3
+
4
+ export default {
5
+ title: 'Components/StatusBorder',
6
+ component: 'ds-status-border',
7
+ tags: ['autodocs'],
8
+ argTypes: {
9
+ status: {
10
+ control: { type: 'select' },
11
+ options: [
12
+ 'critical', 'danger', 'fail',
13
+ 'warning-major', 'warning-minor',
14
+ 'success', 'info', 'inprogress',
15
+ 'pending', 'neutral', 'new'
16
+ ],
17
+ description: 'Status type'
18
+ },
19
+ orientation: {
20
+ control: { type: 'select' },
21
+ options: ['vertical', 'horizontal'],
22
+ description: 'Border orientation'
23
+ },
24
+ selected: {
25
+ control: 'boolean',
26
+ description: 'Increases thickness from 2px to 4px'
27
+ },
28
+ disabled: {
29
+ control: 'boolean',
30
+ description: 'Visually disables the border (opacity: 0.5)'
31
+ }
32
+ }
33
+ };
34
+
35
+ // ─── Playground ───
36
+ export const Playground = {
37
+ args: {
38
+ status: 'critical',
39
+ orientation: 'vertical',
40
+ selected: false,
41
+ disabled: false
42
+ },
43
+ render: (args) => html`
44
+ <div style="position: relative; width: 200px; height: 100px; border: 1px solid var(--ds-color-border-default); border-radius: var(--ds-radius-container);">
45
+ <ds-status-border
46
+ status="${args.status}"
47
+ orientation="${args.orientation}"
48
+ ?selected="${args.selected}"
49
+ ?disabled="${args.disabled}"
50
+ style="position: absolute; left: 0; top: 0; ${args.orientation === 'vertical' ? 'bottom: 0;' : 'right: 0;'}"
51
+ ></ds-status-border>
52
+ <div style="padding: var(--ds-space-md); font: var(--ds-typo-content-body-regular);">
53
+ Status: <strong>${args.status}</strong>
54
+ </div>
55
+ </div>
56
+ `
57
+ };
58
+
59
+ // ─── All Statuses ───
60
+ export const AllStatuses = {
61
+ render: () => {
62
+ const statuses = [
63
+ 'critical', 'danger', 'fail',
64
+ 'warning-major', 'warning-minor',
65
+ 'success', 'info', 'inprogress',
66
+ 'pending', 'neutral', 'new'
67
+ ];
68
+
69
+ return html`
70
+ <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px;">
71
+ ${statuses.map(status => html`
72
+ <div style="position: relative; height: 80px; border: 1px solid var(--ds-color-border-default); border-radius: var(--ds-radius-container);">
73
+ <ds-status-border
74
+ status="${status}"
75
+ style="position: absolute; left: 0; top: 0; bottom: 0;"
76
+ ></ds-status-border>
77
+ <div style="padding: var(--ds-space-md); font: var(--ds-typo-content-caption-bold);">
78
+ ${status}
79
+ </div>
80
+ </div>
81
+ `)}
82
+ </div>
83
+ `;
84
+ }
85
+ };
86
+
87
+ // ─── Orientations ───
88
+ export const Orientations = {
89
+ render: () => html`
90
+ <div style="display: flex; gap: 24px; flex-direction: column;">
91
+ <div>
92
+ <p style="font: var(--ds-typo-content-caption-bold); color: var(--ds-color-text-secondary); margin: 0 0 8px;">Vertical</p>
93
+ <div style="position: relative; width: 200px; height: 100px; border: 1px solid var(--ds-color-border-default); border-radius: var(--ds-radius-container);">
94
+ <ds-status-border
95
+ status="critical"
96
+ orientation="vertical"
97
+ style="position: absolute; left: 0; top: 0; bottom: 0;"
98
+ ></ds-status-border>
99
+ <div style="padding: var(--ds-space-md); font: var(--ds-typo-content-body-regular);">
100
+ Vertical border
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ <div>
106
+ <p style="font: var(--ds-typo-content-caption-bold); color: var(--ds-color-text-secondary); margin: 0 0 8px;">Horizontal</p>
107
+ <div style="position: relative; width: 200px; height: 100px; border: 1px solid var(--ds-color-border-default); border-radius: var(--ds-radius-container);">
108
+ <ds-status-border
109
+ status="success"
110
+ orientation="horizontal"
111
+ style="position: absolute; left: 0; top: 0; right: 0;"
112
+ ></ds-status-border>
113
+ <div style="padding: var(--ds-space-md); padding-top: calc(var(--ds-space-md) + 8px); font: var(--ds-typo-content-body-regular);">
114
+ Horizontal border
115
+ </div>
116
+ </div>
117
+ </div>
118
+ </div>
119
+ `
120
+ };
121
+
122
+ // ─── Selected State ───
123
+ export const SelectedState = {
124
+ render: () => html`
125
+ <div style="display: flex; gap: 24px;">
126
+ <div>
127
+ <p style="font: var(--ds-typo-content-caption-bold); color: var(--ds-color-text-secondary); margin: 0 0 8px;">Normal (2px)</p>
128
+ <div style="position: relative; width: 200px; height: 100px; border: 1px solid var(--ds-color-border-default); border-radius: var(--ds-radius-container);">
129
+ <ds-status-border
130
+ status="warning-major"
131
+ style="position: absolute; left: 0; top: 0; bottom: 0;"
132
+ ></ds-status-border>
133
+ <div style="padding: var(--ds-space-md); font: var(--ds-typo-content-body-regular);">
134
+ Default thickness
135
+ </div>
136
+ </div>
137
+ </div>
138
+
139
+ <div>
140
+ <p style="font: var(--ds-typo-content-caption-bold); color: var(--ds-color-text-secondary); margin: 0 0 8px;">Selected (4px)</p>
141
+ <div style="position: relative; width: 200px; height: 100px; border: 1px solid var(--ds-color-border-default); border-radius: var(--ds-radius-container);">
142
+ <ds-status-border
143
+ status="warning-major"
144
+ selected
145
+ style="position: absolute; left: 0; top: 0; bottom: 0;"
146
+ ></ds-status-border>
147
+ <div style="padding: var(--ds-space-md); font: var(--ds-typo-content-body-regular);">
148
+ Selected thickness
149
+ </div>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ `
154
+ };
155
+
156
+ // ─── Composed in Card ───
157
+ export const ComposedInCard = {
158
+ render: () => html`
159
+ <style>
160
+ .demo-card {
161
+ position: relative;
162
+ width: 300px;
163
+ min-height: 120px;
164
+ padding: var(--ds-space-md);
165
+ border: 1px solid var(--ds-color-border-default);
166
+ border-radius: var(--ds-radius-container);
167
+ background: var(--ds-color-bg-default);
168
+ }
169
+ .demo-card h3 {
170
+ margin: 0 0 var(--ds-space-sm);
171
+ font: var(--ds-typo-content-body-bold);
172
+ color: var(--ds-color-text-default);
173
+ }
174
+ .demo-card p {
175
+ margin: 0;
176
+ font: var(--ds-typo-content-body-regular);
177
+ color: var(--ds-color-text-secondary);
178
+ }
179
+ </style>
180
+
181
+ <div style="display: flex; gap: 16px; flex-direction: column;">
182
+ <div class="demo-card">
183
+ <ds-status-border status="critical" style="position: absolute; left: 0; top: 0; bottom: 0;"></ds-status-border>
184
+ <h3>Critical Alert</h3>
185
+ <p>System failure detected in production environment.</p>
186
+ </div>
187
+
188
+ <div class="demo-card">
189
+ <ds-status-border status="warning-major" style="position: absolute; left: 0; top: 0; bottom: 0;"></ds-status-border>
190
+ <h3>Warning</h3>
191
+ <p>High memory usage in container cluster.</p>
192
+ </div>
193
+
194
+ <div class="demo-card">
195
+ <ds-status-border status="success" style="position: absolute; left: 0; top: 0; bottom: 0;"></ds-status-border>
196
+ <h3>Deployment Successful</h3>
197
+ <p>All services running smoothly.</p>
198
+ </div>
199
+
200
+ <div class="demo-card">
201
+ <ds-status-border status="inprogress" style="position: absolute; left: 0; top: 0; bottom: 0;"></ds-status-border>
202
+ <h3>Build in Progress</h3>
203
+ <p>Compiling assets and running tests.</p>
204
+ </div>
205
+ </div>
206
+ `
207
+ };
208
+
209
+ // ─── Disabled State ───
210
+ export const DisabledState = {
211
+ render: () => html`
212
+ <div style="display: flex; gap: 24px;">
213
+ <div>
214
+ <p style="font: var(--ds-typo-content-caption-bold); color: var(--ds-color-text-secondary); margin: 0 0 8px;">Normal</p>
215
+ <div style="position: relative; width: 200px; height: 100px; border: 1px solid var(--ds-color-border-default); border-radius: var(--ds-radius-container);">
216
+ <ds-status-border
217
+ status="critical"
218
+ style="position: absolute; left: 0; top: 0; bottom: 0;"
219
+ ></ds-status-border>
220
+ <div style="padding: var(--ds-space-md); font: var(--ds-typo-content-body-regular);">
221
+ Active border
222
+ </div>
223
+ </div>
224
+ </div>
225
+
226
+ <div>
227
+ <p style="font: var(--ds-typo-content-caption-bold); color: var(--ds-color-text-secondary); margin: 0 0 8px;">Disabled</p>
228
+ <div style="position: relative; width: 200px; height: 100px; border: 1px solid var(--ds-color-border-default); border-radius: var(--ds-radius-container);">
229
+ <ds-status-border
230
+ status="critical"
231
+ disabled
232
+ style="position: absolute; left: 0; top: 0; bottom: 0;"
233
+ ></ds-status-border>
234
+ <div style="padding: var(--ds-space-md); font: var(--ds-typo-content-body-regular); opacity: 0.5;">
235
+ Disabled border
236
+ </div>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ `
241
+ };
242
+
@@ -0,0 +1,168 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import './ds-status-border.js';
3
+
4
+ describe('ds-status-border', () => {
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
+ async function createStatusBorder(attrs = {}) {
17
+ const attrStr = Object.entries(attrs)
18
+ .map(([k, v]) => {
19
+ if (typeof v === 'boolean') return v ? k : '';
20
+ return `${k}="${v}"`;
21
+ })
22
+ .filter(Boolean)
23
+ .join(' ');
24
+ container.innerHTML = `<ds-status-border ${attrStr}></ds-status-border>`;
25
+ const el = container.querySelector('ds-status-border');
26
+ await el.updateComplete;
27
+ return el;
28
+ }
29
+
30
+ // ─── Rendering ───
31
+
32
+ it('renders with default props', async () => {
33
+ const el = await createStatusBorder();
34
+ expect(el).toBeTruthy();
35
+ expect(el.status).toBe('neutral');
36
+ expect(el.orientation).toBe('vertical');
37
+ expect(el.selected).toBe(false);
38
+ });
39
+
40
+ it('renders border element', async () => {
41
+ const el = await createStatusBorder();
42
+ const border = el.shadowRoot.querySelector('.border');
43
+ expect(border).toBeTruthy();
44
+ });
45
+
46
+ // ─── Status Mapping ───
47
+
48
+ it('sets status attribute', async () => {
49
+ const el = await createStatusBorder({ status: 'critical' });
50
+ expect(el.getAttribute('status')).toBe('critical');
51
+ });
52
+
53
+ it('supports all status values', async () => {
54
+ const statuses = [
55
+ 'critical', 'danger', 'fail',
56
+ 'warning-major', 'warning-minor',
57
+ 'success', 'info', 'inprogress',
58
+ 'pending', 'neutral', 'new'
59
+ ];
60
+
61
+ for (const status of statuses) {
62
+ const el = await createStatusBorder({ status });
63
+ expect(el.getAttribute('status')).toBe(status);
64
+ }
65
+ });
66
+
67
+ // ─── Orientation ───
68
+
69
+ it('defaults to vertical orientation', async () => {
70
+ const el = await createStatusBorder();
71
+ expect(el.orientation).toBe('vertical');
72
+ });
73
+
74
+ it('supports horizontal orientation', async () => {
75
+ const el = await createStatusBorder({ orientation: 'horizontal' });
76
+ expect(el.getAttribute('orientation')).toBe('horizontal');
77
+ });
78
+
79
+ it('applies correct width for vertical orientation', async () => {
80
+ const el = await createStatusBorder({ orientation: 'vertical' });
81
+ const style = getComputedStyle(el);
82
+ expect(style.width).toBe('2px');
83
+ });
84
+
85
+ it('applies correct height for horizontal orientation', async () => {
86
+ const el = await createStatusBorder({ orientation: 'horizontal' });
87
+ const style = getComputedStyle(el);
88
+ expect(style.height).toBe('2px');
89
+ });
90
+
91
+ // ─── Selected State ───
92
+
93
+ it('applies selected attribute', async () => {
94
+ const el = await createStatusBorder({ selected: true });
95
+ expect(el.hasAttribute('selected')).toBe(true);
96
+ });
97
+
98
+ it('applies 4px width when selected (vertical)', async () => {
99
+ const el = await createStatusBorder({ orientation: 'vertical', selected: true });
100
+ const style = getComputedStyle(el);
101
+ expect(style.width).toBe('4px');
102
+ });
103
+
104
+ it('applies 4px height when selected (horizontal)', async () => {
105
+ const el = await createStatusBorder({ orientation: 'horizontal', selected: true });
106
+ const style = getComputedStyle(el);
107
+ expect(style.height).toBe('4px');
108
+ });
109
+
110
+ // ─── Property Changes ───
111
+
112
+ it('updates status dynamically', async () => {
113
+ const el = await createStatusBorder({ status: 'neutral' });
114
+ expect(el.status).toBe('neutral');
115
+
116
+ el.status = 'critical';
117
+ await el.updateComplete;
118
+ expect(el.getAttribute('status')).toBe('critical');
119
+ });
120
+
121
+ it('updates orientation dynamically', async () => {
122
+ const el = await createStatusBorder({ orientation: 'vertical' });
123
+ expect(el.orientation).toBe('vertical');
124
+
125
+ el.orientation = 'horizontal';
126
+ await el.updateComplete;
127
+ expect(el.getAttribute('orientation')).toBe('horizontal');
128
+ });
129
+
130
+ it('updates selected state dynamically', async () => {
131
+ const el = await createStatusBorder({ selected: false });
132
+ expect(el.selected).toBe(false);
133
+
134
+ el.selected = true;
135
+ await el.updateComplete;
136
+ expect(el.hasAttribute('selected')).toBe(true);
137
+ });
138
+
139
+ // ─── Disabled State ───
140
+
141
+ it('applies disabled attribute', async () => {
142
+ const el = await createStatusBorder({ disabled: true });
143
+ expect(el.hasAttribute('disabled')).toBe(true);
144
+ });
145
+
146
+ it('applies opacity when disabled', async () => {
147
+ const el = await createStatusBorder({ disabled: true });
148
+ const style = getComputedStyle(el);
149
+ expect(style.opacity).toBe('0.5');
150
+ });
151
+
152
+ it('updates disabled state dynamically', async () => {
153
+ const el = await createStatusBorder({ disabled: false });
154
+ expect(el.disabled).toBe(false);
155
+
156
+ el.disabled = true;
157
+ await el.updateComplete;
158
+ expect(el.hasAttribute('disabled')).toBe(true);
159
+ });
160
+
161
+ // ─── CSS Parts ───
162
+
163
+ it('exposes border part', async () => {
164
+ const el = await createStatusBorder();
165
+ const part = el.shadowRoot.querySelector('[part="border"]');
166
+ expect(part).toBeTruthy();
167
+ });
168
+ });
@@ -0,0 +1,198 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import './ds-stepper.js';
3
+ import '../ds-stepper-item/ds-stepper-item.js';
4
+
5
+ const wait = (ms = 50) => new Promise(resolve => setTimeout(resolve, ms));
6
+
7
+ describe('ds-stepper a11y', () => {
8
+ let container;
9
+
10
+ beforeEach(() => {
11
+ container = document.createElement('div');
12
+ document.body.appendChild(container);
13
+ });
14
+
15
+ afterEach(() => {
16
+ container.remove();
17
+ });
18
+
19
+ it('has role="group" on stepper container', async () => {
20
+ container.innerHTML = `
21
+ <ds-stepper>
22
+ <ds-stepper-item>Step 1</ds-stepper-item>
23
+ </ds-stepper>
24
+ `;
25
+ const el = container.querySelector('ds-stepper');
26
+ await wait();
27
+
28
+ expect(el.getAttribute('role')).toBe('group');
29
+ });
30
+
31
+ it('has default aria-label "Progress"', async () => {
32
+ container.innerHTML = `
33
+ <ds-stepper>
34
+ <ds-stepper-item>Step 1</ds-stepper-item>
35
+ </ds-stepper>
36
+ `;
37
+ const el = container.querySelector('ds-stepper');
38
+ await wait();
39
+
40
+ expect(el.getAttribute('aria-label')).toBe('Progress');
41
+ });
42
+
43
+ it('supports custom aria-label via label prop', async () => {
44
+ container.innerHTML = `
45
+ <ds-stepper label="Checkout Progress">
46
+ <ds-stepper-item>Step 1</ds-stepper-item>
47
+ </ds-stepper>
48
+ `;
49
+ const el = container.querySelector('ds-stepper');
50
+ await wait();
51
+
52
+ expect(el.getAttribute('aria-label')).toBe('Checkout Progress');
53
+ });
54
+
55
+ it('sets aria-current="step" on the current step', async () => {
56
+ container.innerHTML = `
57
+ <ds-stepper active-step="1">
58
+ <ds-stepper-item>Step 1</ds-stepper-item>
59
+ <ds-stepper-item>Step 2</ds-stepper-item>
60
+ <ds-stepper-item>Step 3</ds-stepper-item>
61
+ </ds-stepper>
62
+ `;
63
+ await wait();
64
+
65
+ const items = container.querySelectorAll('ds-stepper-item');
66
+ expect(items[0].getAttribute('aria-current')).toBeNull();
67
+ expect(items[1].getAttribute('aria-current')).toBe('step');
68
+ expect(items[2].getAttribute('aria-current')).toBeNull();
69
+ });
70
+
71
+ it('indicator has role="button" and tabindex when interactive', async () => {
72
+ container.innerHTML = `
73
+ <ds-stepper>
74
+ <ds-stepper-item>Step 1</ds-stepper-item>
75
+ </ds-stepper>
76
+ `;
77
+ await wait();
78
+
79
+ const item = container.querySelector('ds-stepper-item');
80
+ const indicator = item.shadowRoot.querySelector('.indicator');
81
+
82
+ expect(indicator.getAttribute('role')).toBe('button');
83
+ expect(indicator.getAttribute('tabindex')).toBe('0');
84
+ });
85
+
86
+ it('indicator has role="presentation" and tabindex=-1 when disabled', async () => {
87
+ container.innerHTML = `
88
+ <ds-stepper>
89
+ <ds-stepper-item disabled>Step 1</ds-stepper-item>
90
+ </ds-stepper>
91
+ `;
92
+ await wait();
93
+
94
+ const item = container.querySelector('ds-stepper-item');
95
+ const indicator = item.shadowRoot.querySelector('.indicator');
96
+
97
+ expect(indicator.getAttribute('role')).toBe('presentation');
98
+ expect(indicator.getAttribute('tabindex')).toBe('-1');
99
+ });
100
+
101
+ it('indicator has aria-label with step count', async () => {
102
+ container.innerHTML = `
103
+ <ds-stepper>
104
+ <ds-stepper-item>Step 1</ds-stepper-item>
105
+ <ds-stepper-item>Step 2</ds-stepper-item>
106
+ <ds-stepper-item>Step 3</ds-stepper-item>
107
+ </ds-stepper>
108
+ `;
109
+ await wait();
110
+
111
+ const items = container.querySelectorAll('ds-stepper-item');
112
+ const indicator = items[1].shadowRoot.querySelector('.indicator');
113
+
114
+ expect(indicator.getAttribute('aria-label')).toBe('Step 2 of 3');
115
+ });
116
+
117
+ it('indicator has aria-disabled for disabled steps', async () => {
118
+ container.innerHTML = `
119
+ <ds-stepper>
120
+ <ds-stepper-item disabled>Step 1</ds-stepper-item>
121
+ </ds-stepper>
122
+ `;
123
+ await wait();
124
+
125
+ const item = container.querySelector('ds-stepper-item');
126
+ const indicator = item.shadowRoot.querySelector('.indicator');
127
+
128
+ expect(indicator.getAttribute('aria-disabled')).toBe('true');
129
+ });
130
+
131
+ it('supports keyboard Enter on interactive step', async () => {
132
+ container.innerHTML = `
133
+ <ds-stepper>
134
+ <ds-stepper-item>Step 1</ds-stepper-item>
135
+ <ds-stepper-item>Step 2</ds-stepper-item>
136
+ </ds-stepper>
137
+ `;
138
+ await wait();
139
+
140
+ const el = container.querySelector('ds-stepper');
141
+ const items = container.querySelectorAll('ds-stepper-item');
142
+
143
+ let eventDetail = null;
144
+ el.addEventListener('ds-change', (e) => {
145
+ eventDetail = e.detail;
146
+ });
147
+
148
+ const indicator = items[1].shadowRoot.querySelector('.indicator');
149
+ indicator.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, composed: true }));
150
+ await wait();
151
+
152
+ expect(eventDetail).not.toBeNull();
153
+ expect(eventDetail.currentStep).toBe(1);
154
+ });
155
+
156
+ it('supports keyboard Space on interactive step', async () => {
157
+ container.innerHTML = `
158
+ <ds-stepper>
159
+ <ds-stepper-item>Step 1</ds-stepper-item>
160
+ <ds-stepper-item>Step 2</ds-stepper-item>
161
+ </ds-stepper>
162
+ `;
163
+ await wait();
164
+
165
+ const el = container.querySelector('ds-stepper');
166
+ const items = container.querySelectorAll('ds-stepper-item');
167
+
168
+ let eventDetail = null;
169
+ el.addEventListener('ds-change', (e) => {
170
+ eventDetail = e.detail;
171
+ });
172
+
173
+ const indicator = items[1].shadowRoot.querySelector('.indicator');
174
+ indicator.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', bubbles: true, composed: true }));
175
+ await wait();
176
+
177
+ expect(eventDetail).not.toBeNull();
178
+ expect(eventDetail.currentStep).toBe(1);
179
+ });
180
+
181
+ it('linear-locked steps are not interactive', async () => {
182
+ container.innerHTML = `
183
+ <ds-stepper linear active-step="0">
184
+ <ds-stepper-item>Step 1</ds-stepper-item>
185
+ <ds-stepper-item>Step 2</ds-stepper-item>
186
+ <ds-stepper-item>Step 3</ds-stepper-item>
187
+ </ds-stepper>
188
+ `;
189
+ await wait();
190
+
191
+ const items = container.querySelectorAll('ds-stepper-item');
192
+ // Step 3 should be locked (not completed, not current)
193
+ const indicator3 = items[2].shadowRoot.querySelector('.indicator');
194
+
195
+ expect(indicator3.getAttribute('role')).toBe('presentation');
196
+ expect(indicator3.getAttribute('tabindex')).toBe('-1');
197
+ });
198
+ });