@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,335 @@
1
+ import { html, nothing } from 'lit';
2
+ import './ds-textarea.js';
3
+ import '../token-provider/token-provider.js';
4
+
5
+ export default {
6
+ title: 'Components/Textarea',
7
+ component: 'ds-textarea',
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ label: { control: 'text' },
11
+ info: { control: 'text' },
12
+ labelPosition: {
13
+ control: 'select',
14
+ options: ['top', 'inline-start']
15
+ },
16
+ labelWidth: { control: 'text' },
17
+ placeholder: { control: 'text' },
18
+ value: { control: 'text' },
19
+ helper: { control: 'text' },
20
+ validationStatus: {
21
+ control: 'select',
22
+ options: ['', 'error', 'success']
23
+ },
24
+ validationMessage: { control: 'text' },
25
+ disabled: { control: 'boolean' },
26
+ readonly: { control: 'boolean' },
27
+ required: { control: 'boolean' },
28
+ rows: { control: 'number' },
29
+ minlength: { control: 'number' },
30
+ maxlength: { control: 'number' },
31
+ showCounter: { control: 'boolean' },
32
+ resize: {
33
+ control: 'select',
34
+ options: ['none', 'vertical']
35
+ },
36
+ autoResize: { control: 'boolean' },
37
+ clearable: { control: 'boolean' }
38
+ },
39
+ args: {
40
+ label: 'Description',
41
+ info: '',
42
+ labelPosition: 'top',
43
+ labelWidth: '',
44
+ placeholder: 'Enter your text here...',
45
+ value: '',
46
+ helper: '',
47
+ validationStatus: '',
48
+ validationMessage: '',
49
+ disabled: false,
50
+ readonly: false,
51
+ required: false,
52
+ rows: 3,
53
+ minlength: undefined,
54
+ maxlength: undefined,
55
+ showCounter: false,
56
+ resize: 'vertical',
57
+ autoResize: false,
58
+ clearable: false
59
+ }
60
+ };
61
+
62
+ export const Default = {
63
+ render: (args) => html`
64
+ <ds-textarea
65
+ label="${args.label}"
66
+ info="${args.info || nothing}"
67
+ label-position="${args.labelPosition || nothing}"
68
+ label-width="${args.labelWidth || nothing}"
69
+ placeholder="${args.placeholder}"
70
+ value="${args.value}"
71
+ helper="${args.helper || nothing}"
72
+ validation-status="${args.validationStatus || nothing}"
73
+ validation-message="${args.validationMessage || nothing}"
74
+ ?disabled="${args.disabled}"
75
+ ?readonly="${args.readonly}"
76
+ ?required="${args.required}"
77
+ rows="${args.rows}"
78
+ minlength="${args.minlength || nothing}"
79
+ maxlength="${args.maxlength || nothing}"
80
+ ?show-counter="${args.showCounter}"
81
+ resize="${args.resize}"
82
+ ?auto-resize="${args.autoResize}"
83
+ ?clearable="${args.clearable}"
84
+ ></ds-textarea>
85
+ `
86
+ };
87
+
88
+ export const WithCounter = {
89
+ args: {
90
+ label: 'Bio',
91
+ placeholder: 'Tell us about yourself...',
92
+ maxlength: 200,
93
+ showCounter: true,
94
+ helper: 'Maximum 200 characters'
95
+ },
96
+ render: (args) => html`
97
+ <ds-textarea
98
+ label="${args.label}"
99
+ placeholder="${args.placeholder}"
100
+ maxlength="${args.maxlength}"
101
+ ?show-counter="${args.showCounter}"
102
+ helper="${args.helper}"
103
+ ></ds-textarea>
104
+ `
105
+ };
106
+
107
+ export const WithError = {
108
+ args: {
109
+ label: 'Comments',
110
+ value: 'Too short',
111
+ validationStatus: 'error',
112
+ validationMessage: 'Comment must be at least 10 characters',
113
+ minlength: 10,
114
+ maxlength: 500,
115
+ showCounter: true
116
+ },
117
+ render: (args) => html`
118
+ <ds-textarea
119
+ label="${args.label}"
120
+ value="${args.value}"
121
+ validation-status="${args.validationStatus}"
122
+ validation-message="${args.validationMessage}"
123
+ minlength="${args.minlength}"
124
+ maxlength="${args.maxlength}"
125
+ ?show-counter="${args.showCounter}"
126
+ ></ds-textarea>
127
+ `
128
+ };
129
+
130
+ export const AutoResize = {
131
+ args: {
132
+ label: 'Auto-growing textarea',
133
+ placeholder: 'Type something and watch it grow...',
134
+ helper: 'This textarea automatically grows with your content',
135
+ autoResize: true,
136
+ rows: 2
137
+ },
138
+ render: (args) => html`
139
+ <ds-textarea
140
+ label="${args.label}"
141
+ placeholder="${args.placeholder}"
142
+ helper="${args.helper}"
143
+ ?auto-resize="${args.autoResize}"
144
+ rows="${args.rows}"
145
+ ></ds-textarea>
146
+ `
147
+ };
148
+
149
+ export const ResizeNone = {
150
+ args: {
151
+ label: 'Fixed height textarea',
152
+ placeholder: 'This textarea cannot be resized manually',
153
+ resize: 'none',
154
+ rows: 5
155
+ },
156
+ render: (args) => html`
157
+ <ds-textarea
158
+ label="${args.label}"
159
+ placeholder="${args.placeholder}"
160
+ resize="${args.resize}"
161
+ rows="${args.rows}"
162
+ ></ds-textarea>
163
+ `
164
+ };
165
+
166
+ export const Disabled = {
167
+ args: {
168
+ label: 'Disabled textarea',
169
+ value: 'This content cannot be edited',
170
+ disabled: true,
171
+ rows: 4
172
+ },
173
+ render: (args) => html`
174
+ <ds-textarea
175
+ label="${args.label}"
176
+ value="${args.value}"
177
+ ?disabled="${args.disabled}"
178
+ rows="${args.rows}"
179
+ ></ds-textarea>
180
+ `
181
+ };
182
+
183
+ export const Readonly = {
184
+ args: {
185
+ label: 'Read-only textarea',
186
+ value: 'This content is read-only and can be selected but not modified.',
187
+ readonly: true,
188
+ rows: 3
189
+ },
190
+ render: (args) => html`
191
+ <ds-textarea
192
+ label="${args.label}"
193
+ value="${args.value}"
194
+ ?readonly="${args.readonly}"
195
+ rows="${args.rows}"
196
+ ></ds-textarea>
197
+ `
198
+ };
199
+
200
+ export const WithInfo = {
201
+ args: {
202
+ label: 'Feedback',
203
+ info: 'Please provide detailed feedback about your experience',
204
+ placeholder: 'Share your thoughts...',
205
+ maxlength: 1000,
206
+ showCounter: true,
207
+ rows: 5
208
+ },
209
+ render: (args) => html`
210
+ <ds-textarea
211
+ label="${args.label}"
212
+ info="${args.info}"
213
+ placeholder="${args.placeholder}"
214
+ maxlength="${args.maxlength}"
215
+ ?show-counter="${args.showCounter}"
216
+ rows="${args.rows}"
217
+ ></ds-textarea>
218
+ `
219
+ };
220
+
221
+ export const Clearable = {
222
+ args: {
223
+ label: 'Clearable textarea',
224
+ value: 'This text can be cleared',
225
+ clearable: true,
226
+ helper: 'Focus the field to see the clear button (or press Escape)',
227
+ rows: 3
228
+ },
229
+ render: (args) => html`
230
+ <ds-textarea
231
+ label="${args.label}"
232
+ value="${args.value}"
233
+ ?clearable="${args.clearable}"
234
+ helper="${args.helper}"
235
+ rows="${args.rows}"
236
+ ></ds-textarea>
237
+ `
238
+ };
239
+
240
+ export const AllStates = {
241
+ render: () => html`
242
+ <div style="display: flex; flex-direction: column; gap: 24px;">
243
+ <ds-textarea
244
+ label="Default"
245
+ placeholder="Enter text..."
246
+ rows="3"
247
+ ></ds-textarea>
248
+
249
+ <ds-textarea
250
+ label="With counter"
251
+ maxlength="100"
252
+ show-counter
253
+ value="Sample text"
254
+ rows="3"
255
+ ></ds-textarea>
256
+
257
+ <ds-textarea
258
+ label="Auto-resize"
259
+ placeholder="Grows automatically..."
260
+ auto-resize
261
+ rows="2"
262
+ ></ds-textarea>
263
+
264
+ <ds-textarea
265
+ label="With error"
266
+ value="Too short"
267
+ validation-status="error"
268
+ validation-message="Minimum 20 characters required"
269
+ minlength="20"
270
+ rows="3"
271
+ ></ds-textarea>
272
+
273
+ <ds-textarea
274
+ label="Disabled"
275
+ value="Disabled content"
276
+ disabled
277
+ rows="3"
278
+ ></ds-textarea>
279
+
280
+ <ds-textarea
281
+ label="Read-only"
282
+ value="Read-only content that can be selected"
283
+ readonly
284
+ rows="3"
285
+ ></ds-textarea>
286
+
287
+ <ds-textarea
288
+ label="Fixed height (no resize)"
289
+ resize="none"
290
+ placeholder="Cannot be resized manually"
291
+ rows="4"
292
+ ></ds-textarea>
293
+
294
+ <ds-textarea
295
+ label="With info button"
296
+ info="This is additional information about this field"
297
+ placeholder="Enter description..."
298
+ rows="3"
299
+ ></ds-textarea>
300
+
301
+ <ds-textarea
302
+ label="Clearable"
303
+ value="Focus me to see clear button"
304
+ clearable
305
+ helper="Press Escape or click the X button to clear"
306
+ rows="3"
307
+ ></ds-textarea>
308
+ </div>
309
+ `
310
+ };
311
+
312
+ export const InlineLabel = {
313
+ args: {
314
+ label: 'Description',
315
+ labelPosition: 'inline-start',
316
+ labelWidth: '120px',
317
+ placeholder: 'Enter your text...',
318
+ helper: 'Label is positioned inline with the field',
319
+ maxlength: 200,
320
+ showCounter: true,
321
+ rows: 4
322
+ },
323
+ render: (args) => html`
324
+ <ds-textarea
325
+ label="${args.label}"
326
+ label-position="${args.labelPosition}"
327
+ label-width="${args.labelWidth}"
328
+ placeholder="${args.placeholder}"
329
+ helper="${args.helper}"
330
+ maxlength="${args.maxlength}"
331
+ ?show-counter="${args.showCounter}"
332
+ rows="${args.rows}"
333
+ ></ds-textarea>
334
+ `
335
+ };
@@ -0,0 +1,218 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import './ds-textarea.js';
3
+
4
+ describe('ds-textarea', () => {
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-textarea></ds-textarea>';
18
+ const element = container.querySelector('ds-textarea');
19
+ await new Promise(resolve => setTimeout(resolve, 0));
20
+
21
+ expect(element.label).toBe('');
22
+ expect(element.value).toBe('');
23
+ expect(element.rows).toBe(3);
24
+ expect(element.resize).toBe('vertical');
25
+ expect(element.autoResize).toBe(false);
26
+ });
27
+
28
+ it('renders label when provided', async () => {
29
+ container.innerHTML = '<ds-textarea label="Description"></ds-textarea>';
30
+ const element = container.querySelector('ds-textarea');
31
+ await new Promise(resolve => setTimeout(resolve, 0));
32
+
33
+ const label = element.shadowRoot.querySelector('label');
34
+ expect(label.textContent).toBe('Description');
35
+ });
36
+
37
+ it('renders textarea with correct rows', async () => {
38
+ container.innerHTML = '<ds-textarea rows="5"></ds-textarea>';
39
+ const element = container.querySelector('ds-textarea');
40
+ await new Promise(resolve => setTimeout(resolve, 0));
41
+
42
+ const textarea = element.shadowRoot.querySelector('textarea');
43
+ expect(textarea.getAttribute('rows')).toBe('5');
44
+ });
45
+
46
+ it('binds value to textarea', async () => {
47
+ container.innerHTML = '<ds-textarea value="Test content"></ds-textarea>';
48
+ const element = container.querySelector('ds-textarea');
49
+ await new Promise(resolve => setTimeout(resolve, 0));
50
+
51
+ const textarea = element.shadowRoot.querySelector('textarea');
52
+ expect(textarea.value).toBe('Test content');
53
+ });
54
+
55
+ it('updates value on input', async () => {
56
+ container.innerHTML = '<ds-textarea></ds-textarea>';
57
+ const element = container.querySelector('ds-textarea');
58
+ await new Promise(resolve => setTimeout(resolve, 0));
59
+
60
+ const textarea = element.shadowRoot.querySelector('textarea');
61
+ textarea.value = 'New content';
62
+ textarea.dispatchEvent(new Event('input'));
63
+ await new Promise(resolve => setTimeout(resolve, 0));
64
+
65
+ expect(element.value).toBe('New content');
66
+ });
67
+
68
+ it('dispatches input event when typing', async () => {
69
+ container.innerHTML = '<ds-textarea></ds-textarea>';
70
+ const element = container.querySelector('ds-textarea');
71
+ await new Promise(resolve => setTimeout(resolve, 0));
72
+
73
+ const inputSpy = vi.fn();
74
+ element.addEventListener('input', inputSpy);
75
+
76
+ const textarea = element.shadowRoot.querySelector('textarea');
77
+ textarea.value = 'Test';
78
+ textarea.dispatchEvent(new Event('input'));
79
+
80
+ expect(inputSpy).toHaveBeenCalledTimes(1);
81
+ expect(inputSpy.mock.calls[0][0].detail.value).toBe('Test');
82
+ });
83
+
84
+ it('dispatches change event on blur', async () => {
85
+ container.innerHTML = '<ds-textarea value="Test"></ds-textarea>';
86
+ const element = container.querySelector('ds-textarea');
87
+ await new Promise(resolve => setTimeout(resolve, 0));
88
+
89
+ const changeSpy = vi.fn();
90
+ element.addEventListener('change', changeSpy);
91
+
92
+ const textarea = element.shadowRoot.querySelector('textarea');
93
+ textarea.dispatchEvent(new Event('change'));
94
+
95
+ expect(changeSpy).toHaveBeenCalledTimes(1);
96
+ });
97
+
98
+ it('shows counter when maxlength and show-counter are set', async () => {
99
+ container.innerHTML = '<ds-textarea maxlength="100" show-counter value="Hello"></ds-textarea>';
100
+ const element = container.querySelector('ds-textarea');
101
+ await new Promise(resolve => setTimeout(resolve, 0));
102
+
103
+ const counter = element.shadowRoot.querySelector('.counter');
104
+ expect(counter).toBeTruthy();
105
+ expect(counter.textContent).toBe('5/100');
106
+ });
107
+
108
+ it('updates counter on input', async () => {
109
+ container.innerHTML = '<ds-textarea maxlength="50" show-counter></ds-textarea>';
110
+ const element = container.querySelector('ds-textarea');
111
+ await new Promise(resolve => setTimeout(resolve, 0));
112
+
113
+ const textarea = element.shadowRoot.querySelector('textarea');
114
+ textarea.value = 'Test content';
115
+ textarea.dispatchEvent(new Event('input'));
116
+ await new Promise(resolve => setTimeout(resolve, 0));
117
+
118
+ const counter = element.shadowRoot.querySelector('.counter');
119
+ expect(counter.textContent).toBe('12/50');
120
+ });
121
+
122
+ it('marks counter as over-limit when exceeding maxlength', async () => {
123
+ container.innerHTML = '<ds-textarea maxlength="5" show-counter value="This is too long"></ds-textarea>';
124
+ const element = container.querySelector('ds-textarea');
125
+ await new Promise(resolve => setTimeout(resolve, 0));
126
+
127
+ const counter = element.shadowRoot.querySelector('.counter');
128
+ expect(counter.classList.contains('over-limit')).toBe(true);
129
+ });
130
+
131
+ it('applies resize attribute correctly', async () => {
132
+ container.innerHTML = '<ds-textarea resize="none"></ds-textarea>';
133
+ const element = container.querySelector('ds-textarea');
134
+ await new Promise(resolve => setTimeout(resolve, 0));
135
+
136
+ expect(element.getAttribute('resize')).toBe('none');
137
+ });
138
+
139
+ it('disables textarea when disabled', async () => {
140
+ container.innerHTML = '<ds-textarea disabled></ds-textarea>';
141
+ const element = container.querySelector('ds-textarea');
142
+ await new Promise(resolve => setTimeout(resolve, 0));
143
+
144
+ const textarea = element.shadowRoot.querySelector('textarea');
145
+ expect(textarea.disabled).toBe(true);
146
+ });
147
+
148
+ it('makes textarea readonly when readonly', async () => {
149
+ container.innerHTML = '<ds-textarea readonly></ds-textarea>';
150
+ const element = container.querySelector('ds-textarea');
151
+ await new Promise(resolve => setTimeout(resolve, 0));
152
+
153
+ const textarea = element.shadowRoot.querySelector('textarea');
154
+ expect(textarea.readOnly).toBe(true);
155
+ });
156
+
157
+ it('sets required attribute', async () => {
158
+ container.innerHTML = '<ds-textarea required></ds-textarea>';
159
+ const element = container.querySelector('ds-textarea');
160
+ await new Promise(resolve => setTimeout(resolve, 0));
161
+
162
+ const textarea = element.shadowRoot.querySelector('textarea');
163
+ expect(textarea.required).toBe(true);
164
+ });
165
+
166
+ it('applies minlength and maxlength', async () => {
167
+ container.innerHTML = '<ds-textarea minlength="10" maxlength="100"></ds-textarea>';
168
+ const element = container.querySelector('ds-textarea');
169
+ await new Promise(resolve => setTimeout(resolve, 0));
170
+
171
+ const textarea = element.shadowRoot.querySelector('textarea');
172
+ expect(textarea.getAttribute('minlength')).toBe('10');
173
+ expect(textarea.getAttribute('maxlength')).toBe('100');
174
+ });
175
+
176
+ it('renders helper text', async () => {
177
+ container.innerHTML = '<ds-textarea helper="Enter your description"></ds-textarea>';
178
+ const element = container.querySelector('ds-textarea');
179
+ await new Promise(resolve => setTimeout(resolve, 0));
180
+
181
+ const message = element.shadowRoot.querySelector('.field-message__text');
182
+ expect(message.textContent).toBe('Enter your description');
183
+ });
184
+
185
+ it('renders error message', async () => {
186
+ container.innerHTML = `
187
+ <ds-textarea
188
+ validation-status="error"
189
+ validation-message="This field is required"
190
+ ></ds-textarea>
191
+ `;
192
+ const element = container.querySelector('ds-textarea');
193
+ await new Promise(resolve => setTimeout(resolve, 0));
194
+
195
+ const errorMessage = element.shadowRoot.querySelector('.field-message--error');
196
+ expect(errorMessage).toBeTruthy();
197
+ const icon = element.shadowRoot.querySelector('.field-message__icon');
198
+ expect(icon).toBeTruthy();
199
+ });
200
+
201
+ it('sets aria-invalid when validation status is error', async () => {
202
+ container.innerHTML = '<ds-textarea validation-status="error"></ds-textarea>';
203
+ const element = container.querySelector('ds-textarea');
204
+ await new Promise(resolve => setTimeout(resolve, 0));
205
+
206
+ const textarea = element.shadowRoot.querySelector('textarea');
207
+ expect(textarea.getAttribute('aria-invalid')).toBe('true');
208
+ });
209
+
210
+ it('does not show counter without maxlength', async () => {
211
+ container.innerHTML = '<ds-textarea show-counter></ds-textarea>';
212
+ const element = container.querySelector('ds-textarea');
213
+ await new Promise(resolve => setTimeout(resolve, 0));
214
+
215
+ const counter = element.shadowRoot.querySelector('.counter');
216
+ expect(counter).toBeFalsy();
217
+ });
218
+ });
@@ -0,0 +1 @@
1
+ export { DsTextarea } from './ds-textarea.js';