@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,530 @@
1
+ import { html, LitElement, css } from 'lit';
2
+ import './ds-stepper.js';
3
+ import '../ds-stepper-item/ds-stepper-item.js';
4
+ import '../ds-button/ds-button.js';
5
+ import '../ds-checkbox/ds-checkbox.js';
6
+ import '../ds-input/ds-input.js';
7
+ import '../ds-button-group/ds-button-group.js';
8
+
9
+ // Helper component for interactive demo (CONTROLLED MODE)
10
+ class StepperDemo extends LitElement {
11
+ static properties = {
12
+ currentStep: { type: Number },
13
+ maxStepReached: { type: Number },
14
+ _completedSteps: { type: Array, state: true }
15
+ };
16
+
17
+ static styles = css`
18
+ :host {
19
+ display: block;
20
+ padding: var(--ds-space-lg, 24px);
21
+ font-family: var(--ds-typo-font-family);
22
+ color: var(--ds-color-text-default);
23
+ }
24
+ .controls {
25
+ display: flex;
26
+ gap: var(--ds-space-md, 16px);
27
+ margin-top: var(--ds-space-xl, 32px);
28
+ justify-content: space-between;
29
+ align-items: center;
30
+ }
31
+ .nav-buttons {
32
+ display: flex;
33
+ gap: var(--ds-space-md, 16px);
34
+ }
35
+ .content {
36
+ margin-top: var(--ds-space-xl, 32px);
37
+ padding: var(--ds-space-lg, 24px);
38
+ background: var(--ds-color-bg-surface-secondary, #f5f5f5);
39
+ border-radius: var(--ds-radius-lg, 8px);
40
+ text-align: center;
41
+ border: 1px dashed var(--ds-color-border-default, #e0e0e0);
42
+ }
43
+ .content h3 {
44
+ margin-top: 0;
45
+ font: var(--ds-typo-heading-xs);
46
+ color: var(--ds-color-text-heading);
47
+ }
48
+ .content p {
49
+ font: var(--ds-typo-content-body-regular);
50
+ color: var(--ds-color-text-secondary);
51
+ }
52
+ .step-actions {
53
+ margin-top: var(--ds-space-md, 16px);
54
+ display: flex;
55
+ justify-content: center;
56
+ align-items: center;
57
+ gap: var(--ds-space-sm, 8px);
58
+ }
59
+ `;
60
+
61
+ constructor() {
62
+ super();
63
+ this.currentStep = 1;
64
+ this.maxStepReached = 1;
65
+ this.totalSteps = 4;
66
+ this._completedSteps = [];
67
+ }
68
+
69
+ _isCompleted(step) {
70
+ return this._completedSteps.includes(step);
71
+ }
72
+
73
+ _toggleComplete(e) {
74
+ const checked = e.detail ? e.detail.checked : e.target.checked;
75
+
76
+ if (checked) {
77
+ if (!this._completedSteps.includes(this.currentStep)) {
78
+ this._completedSteps = [...this._completedSteps, this.currentStep];
79
+ }
80
+ } else {
81
+ this._completedSteps = this._completedSteps.filter(s => s !== this.currentStep);
82
+ }
83
+ }
84
+
85
+ // Handle ds-change event from stepper
86
+ _handleStepChange(e) {
87
+ const newStepIndex = e.detail.currentStep; // 0-based
88
+ const stepNumber = newStepIndex + 1; // 1-based
89
+
90
+ // Logic: Allow going back, or going forward only if unlocked
91
+ if (stepNumber <= this.maxStepReached + 1) {
92
+ this.currentStep = stepNumber;
93
+ if (this.currentStep > this.maxStepReached) {
94
+ this.maxStepReached = this.currentStep;
95
+ }
96
+ }
97
+ }
98
+
99
+ _next() {
100
+ if (this.currentStep < this.totalSteps) {
101
+ if (!this._completedSteps.includes(this.currentStep)) {
102
+ this._completedSteps = [...this._completedSteps, this.currentStep];
103
+ }
104
+
105
+ this.currentStep++;
106
+ if (this.currentStep > this.maxStepReached) {
107
+ this.maxStepReached = this.currentStep;
108
+ }
109
+ }
110
+ }
111
+
112
+ _prev() {
113
+ if (this.currentStep > 1) {
114
+ this.currentStep--;
115
+ }
116
+ }
117
+
118
+ render() {
119
+ // Note: We use active-step (0-based) prop, converting from our 1-based state
120
+ return html`
121
+ <ds-stepper
122
+ active-step="${this.currentStep - 1}"
123
+ .autoComplete=${false}
124
+ @ds-change=${this._handleStepChange}
125
+ >
126
+ <ds-stepper-item ?completed=${this._isCompleted(1)}>Account Info</ds-stepper-item>
127
+ <ds-stepper-item ?completed=${this._isCompleted(2)}>Personal Details</ds-stepper-item>
128
+ <ds-stepper-item ?completed=${this._isCompleted(3)}>Payment</ds-stepper-item>
129
+ <ds-stepper-item ?completed=${this._isCompleted(4)}>Confirmation</ds-stepper-item>
130
+ </ds-stepper>
131
+
132
+ <div class="content">
133
+ <h3>Step ${this.currentStep}: ${this._getStepValidation(this.currentStep)}</h3>
134
+ <p>Interactive content for step ${this.currentStep} goes here.</p>
135
+
136
+ <div class="step-actions">
137
+ <ds-checkbox
138
+ .checked=${this._isCompleted(this.currentStep)}
139
+ @change=${this._toggleComplete}
140
+ >
141
+ Mark step as completed
142
+ </ds-checkbox>
143
+ </div>
144
+ </div>
145
+
146
+ <div class="controls">
147
+ <ds-button
148
+ variant="secondary"
149
+ @click=${this._prev}
150
+ ?disabled=${this.currentStep === 1}
151
+ >
152
+ Previous
153
+ </ds-button>
154
+
155
+ <div class="nav-buttons">
156
+ <ds-button
157
+ variant="primary"
158
+ @click=${this._next}
159
+ ?disabled=${this.currentStep === this.totalSteps}
160
+ >
161
+ ${this.currentStep === this.totalSteps ? 'Finish' : 'Next'}
162
+ </ds-button>
163
+ </div>
164
+ </div>
165
+ `;
166
+ }
167
+
168
+ _getStepValidation(step) {
169
+ switch (step) {
170
+ case 1: return 'Enter Account Info';
171
+ case 2: return 'Enter Personal Details';
172
+ case 3: return 'Enter Payment Info';
173
+ case 4: return 'Confirm Order';
174
+ default: return '';
175
+ }
176
+ }
177
+ }
178
+
179
+ if (!customElements.get('ds-stepper-demo')) {
180
+ customElements.define('ds-stepper-demo', StepperDemo);
181
+ }
182
+
183
+ export default {
184
+ title: 'Components/Stepper',
185
+ component: 'ds-stepper',
186
+ tags: ['autodocs'],
187
+ argTypes: {
188
+ direction: {
189
+ control: 'select',
190
+ options: ['horizontal', 'vertical'],
191
+ description: 'Layout direction of the stepper'
192
+ },
193
+ activeStep: {
194
+ control: { type: 'number', min: 0, max: 4 },
195
+ description: 'Controlled active step index (0-based)'
196
+ }
197
+ }
198
+ };
199
+
200
+ // ===== UNCONTROLLED (Self-managed) =====
201
+ export const Uncontrolled = {
202
+ args: {
203
+ direction: 'horizontal'
204
+ },
205
+ render: (args) => html`
206
+ <div style="display: flex; flex-direction: column; gap: 16px;">
207
+ <p style="font: var(--ds-typo-content-body-regular); color: var(--ds-color-text-secondary);">
208
+ <strong>Uncontrolled Mode:</strong> No <code>active-step</code> prop provided. Click any step to navigate. The component manages its own state.
209
+ </p>
210
+ <ds-stepper direction="${args.direction}">
211
+ <ds-stepper-item>Step 1</ds-stepper-item>
212
+ <ds-stepper-item>Step 2</ds-stepper-item>
213
+ <ds-stepper-item>Step 3</ds-stepper-item>
214
+ <ds-stepper-item>Step 4</ds-stepper-item>
215
+ </ds-stepper>
216
+ </div>
217
+ `,
218
+ parameters: {
219
+ docs: {
220
+ description: {
221
+ story: 'In Uncontrolled mode (no `active-step` prop), the stepper automatically updates its state when clicked. Ideal for simple navigation or tabs.'
222
+ }
223
+ }
224
+ }
225
+ };
226
+
227
+ // ===== CONTROLLED (Prop-driven) =====
228
+ export const Controlled = {
229
+ args: {
230
+ direction: 'horizontal',
231
+ activeStep: 1
232
+ },
233
+ render: (args) => html`
234
+ <div style="display: flex; flex-direction: column; gap: 16px;">
235
+ <p style="font: var(--ds-typo-content-body-regular); color: var(--ds-color-text-secondary);">
236
+ <strong>Controlled Mode:</strong> <code>active-step</code> is set to <strong>${args.activeStep}</strong>. Clicking steps emits <code>ds-change</code> but DOES NOT change the UI unless the prop is updated (try changing the control below).
237
+ </p>
238
+ <ds-stepper direction="${args.direction}" active-step="${args.activeStep}">
239
+ <ds-stepper-item>Step 1</ds-stepper-item>
240
+ <ds-stepper-item>Step 2</ds-stepper-item>
241
+ <ds-stepper-item>Step 3</ds-stepper-item>
242
+ <ds-stepper-item>Step 4</ds-stepper-item>
243
+ </ds-stepper>
244
+ </div>
245
+ `,
246
+ parameters: {
247
+ docs: {
248
+ description: {
249
+ story: 'In Controlled mode (with `active-step` prop), the component effectively becomes read-only regarding internal state changes. It relies on the parent to update the prop.'
250
+ }
251
+ }
252
+ }
253
+ };
254
+
255
+ // ===== LINEAR MODE =====
256
+ export const Linear = {
257
+ args: {
258
+ direction: 'horizontal',
259
+ activeStep: 1
260
+ },
261
+ render: (args) => html`
262
+ <div style="display: flex; flex-direction: column; gap: 48px;">
263
+ <div>
264
+ <h3 style="font: var(--ds-typo-heading-xs); color: var(--ds-color-text-heading); margin-bottom: 16px;">Linear Mode (future steps non-interactive)</h3>
265
+ <ds-stepper direction="${args.direction}" active-step="${args.activeStep}" linear>
266
+ <ds-stepper-item>Step 1</ds-stepper-item>
267
+ <ds-stepper-item>Step 2</ds-stepper-item>
268
+ <ds-stepper-item>Step 3</ds-stepper-item>
269
+ <ds-stepper-item>Step 4</ds-stepper-item>
270
+ </ds-stepper>
271
+ </div>
272
+ </div>
273
+ `
274
+ };
275
+
276
+ // ===== ERROR STATE =====
277
+ export const ErrorState = {
278
+ render: (args) => html`
279
+ <ds-stepper direction="horizontal" active-step="1">
280
+ <ds-stepper-item>Account Info</ds-stepper-item>
281
+ <ds-stepper-item error>Payment Details</ds-stepper-item>
282
+ <ds-stepper-item>Review</ds-stepper-item>
283
+ <ds-stepper-item>Confirm</ds-stepper-item>
284
+ </ds-stepper>
285
+ `
286
+ };
287
+
288
+ // ===== INTERACTIVE DEMO =====
289
+ export const InteractiveDemo = {
290
+ render: () => html`<ds-stepper-demo></ds-stepper-demo>`,
291
+ parameters: {
292
+ docs: {
293
+ description: {
294
+ story: 'A fully interactive demo using CONTROLLED mode. The parent component listens to `ds-change` and updates the `active-step` prop.'
295
+ }
296
+ }
297
+ }
298
+ };
299
+
300
+ // Vertical Wizard Demo
301
+ class VerticalWizardDemo extends LitElement {
302
+ static properties = {
303
+ currentStep: { type: Number }, // 1-based
304
+ maxStepReached: { type: Number }
305
+ };
306
+
307
+ static styles = css`
308
+ :host {
309
+ display: block;
310
+ padding: var(--ds-space-lg, 24px);
311
+ font-family: var(--ds-typo-font-family);
312
+ color: var(--ds-color-text-default);
313
+ }
314
+ h2 {
315
+ font: var(--ds-typo-heading-md);
316
+ margin-bottom: var(--ds-space-lg, 24px);
317
+ color: var(--ds-color-text-heading);
318
+ }
319
+ .step-content {
320
+ padding: var(--ds-space-md, 16px) 0;
321
+ display: flex;
322
+ flex-direction: column;
323
+ gap: var(--ds-space-lg, 24px);
324
+ max-width: 400px;
325
+ }
326
+ .actions {
327
+ display: flex;
328
+ gap: var(--ds-space-sm, 8px);
329
+ margin-top: var(--ds-space-md, 16px);
330
+ padding-top: var(--ds-space-md, 16px);
331
+ border-top: 1px solid var(--ds-color-border-subtle);
332
+ }
333
+ `;
334
+
335
+ constructor() {
336
+ super();
337
+ this.currentStep = 1;
338
+ this.maxStepReached = 1;
339
+ }
340
+
341
+ // Handle ds-change event (Uncontrolled or Controlled, here we use Controlled pattern logic)
342
+ _handleStepChange(e) {
343
+ const step = e.detail.currentStep + 1;
344
+ if (step <= this.maxStepReached) {
345
+ this.currentStep = step;
346
+ }
347
+ }
348
+
349
+ _next() {
350
+ const nextStep = this.currentStep + 1;
351
+ if (nextStep > this.maxStepReached) {
352
+ this.maxStepReached = nextStep;
353
+ }
354
+ this.currentStep = nextStep;
355
+ }
356
+
357
+ _prev() {
358
+ if (this.currentStep > 1) {
359
+ this.currentStep--;
360
+ }
361
+ }
362
+
363
+ render() {
364
+ // Using UNCONTROLLED mode for demonstration where possible, or CONTROLLED if we need to sync content
365
+ // Actually for a wizard, CONTROLLED is best to sync the content slots visibility (although CSS handles some of it)
366
+ // Here we use active-step to ensure complete sync
367
+ return html`
368
+ <div style="display: flex; flex-direction: column; gap: var(--ds-space-lg, 24px);">
369
+ <h2>Checkout Process</h2>
370
+ <ds-stepper
371
+ direction="vertical"
372
+ active-step="${this.currentStep - 1}"
373
+ @ds-change=${this._handleStepChange}
374
+ >
375
+
376
+ <!-- Step 1 -->
377
+ <ds-stepper-item>
378
+ <span style="font: var(--ds-typo-heading-xs); color: var(--ds-color-text-default);">Account Details</span>
379
+ <div slot="content" class="step-content">
380
+ <ds-input label="Username" style="width: 100%;"></ds-input>
381
+ <ds-input label="Email" type="email" style="width: 100%;"></ds-input>
382
+ <div class="actions">
383
+ <ds-button variant="primary" @click=${this._next}>Continue</ds-button>
384
+ </div>
385
+ </div>
386
+ </ds-stepper-item>
387
+
388
+ <!-- Step 2 -->
389
+ <ds-stepper-item ?disabled=${this.maxStepReached < 2}>
390
+ <span style="font: var(--ds-typo-heading-xs); color: var(--ds-color-text-default);">Shipping Address</span>
391
+ <div slot="content" class="step-content">
392
+ <ds-input label="Street Address" style="width: 100%;"></ds-input>
393
+ <div class="actions">
394
+ <ds-button variant="secondary" @click=${this._prev}>Back</ds-button>
395
+ <ds-button variant="primary" @click=${this._next}>Continue</ds-button>
396
+ </div>
397
+ </div>
398
+ </ds-stepper-item>
399
+
400
+ <!-- Step 3 -->
401
+ <ds-stepper-item ?disabled=${this.maxStepReached < 3}>
402
+ <span style="font: var(--ds-typo-heading-xs); color: var(--ds-color-text-default);">Payment</span>
403
+ <div slot="content" class="step-content">
404
+ <p>Payment details go here.</p>
405
+ <div class="actions">
406
+ <ds-button variant="secondary" @click=${this._prev}>Back</ds-button>
407
+ <ds-button variant="primary">Submit Order</ds-button>
408
+ </div>
409
+ </div>
410
+ </ds-stepper-item>
411
+
412
+ </ds-stepper>
413
+ </div>
414
+ `;
415
+ }
416
+ }
417
+ customElements.define('vertical-wizard-demo', VerticalWizardDemo);
418
+
419
+ export const VerticalWizard = {
420
+ render: () => html`<vertical-wizard-demo></vertical-wizard-demo>`,
421
+ parameters: {
422
+ docs: {
423
+ description: {
424
+ story: 'Vertical wizard example using Controlled mode.'
425
+ }
426
+ }
427
+ }
428
+ };
429
+
430
+ // Demo component for non-sequential navigation
431
+ class NonSequentialDemo extends LitElement {
432
+ static properties = {
433
+ currentStep: { type: Number },
434
+ _completedSteps: { type: Set, state: true }
435
+ };
436
+
437
+ static styles = css`
438
+ :host {
439
+ display: block;
440
+ padding: var(--ds-space-lg, 24px);
441
+ font-family: var(--ds-typo-font-family);
442
+ color: var(--ds-color-text-default);
443
+ }
444
+ .info {
445
+ margin-bottom: var(--ds-space-lg, 24px);
446
+ padding: var(--ds-space-md, 16px);
447
+ background: var(--ds-color-bg-info-subtle, #e3f2fd);
448
+ border-radius: var(--ds-radius-md, 4px);
449
+ border-left: 4px solid var(--ds-color-border-info, #1976d2);
450
+ }
451
+ h4 {
452
+ margin: 0 0 var(--ds-space-xs, 4px);
453
+ font: var(--ds-typo-content-body-bold);
454
+ color: var(--ds-color-text-default);
455
+ }
456
+ p {
457
+ margin: 0;
458
+ font: var(--ds-typo-content-caption-regular);
459
+ color: var(--ds-color-text-secondary);
460
+ }
461
+ .controls {
462
+ margin-top: var(--ds-space-lg, 24px);
463
+ display: flex;
464
+ gap: var(--ds-space-md, 16px);
465
+ }
466
+ `;
467
+
468
+ constructor() {
469
+ super();
470
+ this.currentStep = 0;
471
+ this._completedSteps = new Set();
472
+ }
473
+
474
+ _handleChange(e) {
475
+ this.currentStep = e.detail.currentStep;
476
+ }
477
+
478
+ _completeStep(stepIndex) {
479
+ this._completedSteps = new Set([...this._completedSteps, stepIndex]);
480
+ }
481
+
482
+ _uncompleteStep(stepIndex) {
483
+ const newSet = new Set(this._completedSteps);
484
+ newSet.delete(stepIndex);
485
+ this._completedSteps = newSet;
486
+ }
487
+
488
+ render() {
489
+ return html`
490
+ <div class="info">
491
+ <h4>🎯 Non-Sequential Navigation (auto-complete="false")</h4>
492
+ <p>Click any step to navigate freely. Completed state is controlled manually — try the buttons below.</p>
493
+ </div>
494
+
495
+ <ds-stepper
496
+ .activeStep=${this.currentStep}
497
+ .autoComplete=${false}
498
+ @ds-change=${this._handleChange}
499
+ >
500
+ <ds-stepper-item ?completed=${this._completedSteps.has(0)}>Account Setup</ds-stepper-item>
501
+ <ds-stepper-item ?completed=${this._completedSteps.has(1)}>Personal Details</ds-stepper-item>
502
+ <ds-stepper-item ?completed=${this._completedSteps.has(2)}>Preferences</ds-stepper-item>
503
+ <ds-stepper-item ?completed=${this._completedSteps.has(3)}>Confirmation</ds-stepper-item>
504
+ </ds-stepper>
505
+
506
+ <div class="controls">
507
+ <ds-button
508
+ variant="primary"
509
+ @click=${() => this._completeStep(this.currentStep)}
510
+ >✓ Mark Current Step Complete</ds-button>
511
+ <ds-button
512
+ variant="secondary"
513
+ @click=${() => this._uncompleteStep(this.currentStep)}
514
+ >✗ Uncomplete Current Step</ds-button>
515
+ </div>
516
+ `;
517
+ }
518
+ }
519
+ customElements.define('non-sequential-demo', NonSequentialDemo);
520
+
521
+ export const NonSequentialNavigation = {
522
+ render: () => html`<non-sequential-demo></non-sequential-demo>`,
523
+ parameters: {
524
+ docs: {
525
+ description: {
526
+ story: 'Demonstrates `auto-complete="false"` mode: clicking steps changes `current` but does NOT auto-complete previous steps. Completion is fully manual via the `completed` attribute.'
527
+ }
528
+ }
529
+ }
530
+ };