@nuvia-ui/components 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +27 -0
- package/src/ds-accordion/ds-accordion-item.js +288 -0
- package/src/ds-accordion/ds-accordion-item.stories.js +82 -0
- package/src/ds-accordion/ds-accordion.a11y.test.js +92 -0
- package/src/ds-accordion/ds-accordion.js +68 -0
- package/src/ds-accordion/ds-accordion.stories.js +118 -0
- package/src/ds-accordion/ds-accordion.test.js +146 -0
- package/src/ds-accordion/index.js +2 -0
- package/src/ds-action-bar/ds-action-bar.js +116 -0
- package/src/ds-action-bar/ds-action-bar.stories.js +86 -0
- package/src/ds-action-bar/ds-action-bar.test.js +64 -0
- package/src/ds-action-bar/index.js +1 -0
- package/src/ds-alert/ds-alert.a11y.test.js +151 -0
- package/src/ds-alert/ds-alert.js +223 -0
- package/src/ds-alert/ds-alert.mdx +142 -0
- package/src/ds-alert/ds-alert.stories.js +166 -0
- package/src/ds-alert/ds-alert.test.js +256 -0
- package/src/ds-alert/index.js +1 -0
- package/src/ds-avatar/ds-avatar.a11y.test.js +45 -0
- package/src/ds-avatar/ds-avatar.js +216 -0
- package/src/ds-avatar/ds-avatar.stories.js +120 -0
- package/src/ds-avatar/ds-avatar.test.js +83 -0
- package/src/ds-avatar/index.js +1 -0
- package/src/ds-avatar-extended/ds-avatar-extended.a11y.test.js +29 -0
- package/src/ds-avatar-extended/ds-avatar-extended.js +108 -0
- package/src/ds-avatar-extended/ds-avatar-extended.stories.js +93 -0
- package/src/ds-avatar-extended/ds-avatar-extended.test.js +66 -0
- package/src/ds-avatar-extended/index.js +1 -0
- package/src/ds-banner/ds-banner.a11y.test.js +51 -0
- package/src/ds-banner/ds-banner.js +233 -0
- package/src/ds-banner/ds-banner.stories.js +185 -0
- package/src/ds-banner/ds-banner.test.js +116 -0
- package/src/ds-banner/index.js +1 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.js +135 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.stories.js +49 -0
- package/src/ds-breadcrumb-item/ds-breadcrumb-item.test.js +55 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.js +194 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.stories.js +54 -0
- package/src/ds-breadcrumbs/ds-breadcrumbs.test.js +33 -0
- package/src/ds-button/ds-button.a11y.test.js +49 -0
- package/src/ds-button/ds-button.js +205 -0
- package/src/ds-button/ds-button.mdx +141 -0
- package/src/ds-button/ds-button.stories.js +152 -0
- package/src/ds-button/ds-button.test.js +62 -0
- package/src/ds-button/index.js +1 -0
- package/src/ds-button-group/ds-button-group.js +82 -0
- package/src/ds-button-group/ds-button-group.mdx +39 -0
- package/src/ds-button-group/ds-button-group.stories.js +47 -0
- package/src/ds-button-group/ds-button-group.test.js +47 -0
- package/src/ds-button-group/index.js +1 -0
- package/src/ds-checkbox/ds-checkbox.a11y.test.js +79 -0
- package/src/ds-checkbox/ds-checkbox.js +271 -0
- package/src/ds-checkbox/ds-checkbox.stories.js +77 -0
- package/src/ds-checkbox/ds-checkbox.test.js +191 -0
- package/src/ds-checkbox/index.js +1 -0
- package/src/ds-checkbox-group/ds-checkbox-group.a11y.test.js +146 -0
- package/src/ds-checkbox-group/ds-checkbox-group.js +235 -0
- package/src/ds-checkbox-group/ds-checkbox-group.stories.js +210 -0
- package/src/ds-checkbox-group/ds-checkbox-group.test.js +150 -0
- package/src/ds-checkbox-group/index.js +1 -0
- package/src/ds-dialog/ds-dialog.js +466 -0
- package/src/ds-dialog/ds-dialog.stories.js +274 -0
- package/src/ds-dialog/ds-dialog.test.js +441 -0
- package/src/ds-dialog/index.js +1 -0
- package/src/ds-dropdown/ds-dropdown.a11y.test.js +80 -0
- package/src/ds-dropdown/ds-dropdown.js +891 -0
- package/src/ds-dropdown/ds-dropdown.stories.js +259 -0
- package/src/ds-dropdown/ds-dropdown.test.js +268 -0
- package/src/ds-dropdown/index.js +1 -0
- package/src/ds-dropdown-group/ds-dropdown-group.js +55 -0
- package/src/ds-dropdown-panel/ds-dropdown-panel.js +34 -0
- package/src/ds-file-uploaded/ds-file-uploaded.a11y.test.js +40 -0
- package/src/ds-file-uploaded/ds-file-uploaded.js +135 -0
- package/src/ds-file-uploaded/ds-file-uploaded.mdx +33 -0
- package/src/ds-file-uploaded/ds-file-uploaded.stories.js +81 -0
- package/src/ds-file-uploaded/ds-file-uploaded.test.js +85 -0
- package/src/ds-file-uploader/ds-file-uploader.a11y.test.js +61 -0
- package/src/ds-file-uploader/ds-file-uploader.js +442 -0
- package/src/ds-file-uploader/ds-file-uploader.mdx +44 -0
- package/src/ds-file-uploader/ds-file-uploader.stories.js +76 -0
- package/src/ds-file-uploader/ds-file-uploader.test.js +142 -0
- package/src/ds-header/ds-header.a11y.test.js +38 -0
- package/src/ds-header/ds-header.js +149 -0
- package/src/ds-header/ds-header.stories.js +63 -0
- package/src/ds-header/ds-header.test.js +52 -0
- package/src/ds-header/index.js +1 -0
- package/src/ds-header-nav/ds-header-nav.a11y.test.js +69 -0
- package/src/ds-header-nav/ds-header-nav.js +114 -0
- package/src/ds-header-nav/ds-header-nav.stories.js +17 -0
- package/src/ds-header-nav/ds-header-nav.test.js +93 -0
- package/src/ds-header-nav-item/ds-header-nav-item.a11y.test.js +71 -0
- package/src/ds-header-nav-item/ds-header-nav-item.js +124 -0
- package/src/ds-header-nav-item/ds-header-nav-item.stories.js +43 -0
- package/src/ds-header-nav-item/ds-header-nav-item.test.js +61 -0
- package/src/ds-icon/ds-icon.a11y.test.js +49 -0
- package/src/ds-icon/ds-icon.js +75 -0
- package/src/ds-icon/ds-icon.mdx +36 -0
- package/src/ds-icon/ds-icon.stories.js +88 -0
- package/src/ds-icon/ds-icon.test.js +97 -0
- package/src/ds-icon/index.js +1 -0
- package/src/ds-icon-button/ds-icon-button.a11y.test.js +55 -0
- package/src/ds-icon-button/ds-icon-button.js +224 -0
- package/src/ds-icon-button/ds-icon-button.mdx +131 -0
- package/src/ds-icon-button/ds-icon-button.stories.js +128 -0
- package/src/ds-icon-button/ds-icon-button.test.js +90 -0
- package/src/ds-icon-button/index.js +1 -0
- package/src/ds-input/ds-input.a11y.test.js +145 -0
- package/src/ds-input/ds-input.js +645 -0
- package/src/ds-input/ds-input.mdx +251 -0
- package/src/ds-input/ds-input.stories.js +298 -0
- package/src/ds-input/ds-input.test.js +792 -0
- package/src/ds-input/index.js +1 -0
- package/src/ds-link/ds-link.js +111 -0
- package/src/ds-link/ds-link.stories.js +56 -0
- package/src/ds-link/ds-link.test.js +74 -0
- package/src/ds-list-item/ds-list-item.a11y.test.js +39 -0
- package/src/ds-list-item/ds-list-item.js +292 -0
- package/src/ds-list-item/ds-list-item.stories.js +101 -0
- package/src/ds-list-item/ds-list-item.test.js +63 -0
- package/src/ds-menu/ds-menu.js +30 -0
- package/src/ds-menu/ds-menu.stories.js +120 -0
- package/src/ds-menu/ds-menu.test.js +123 -0
- package/src/ds-menu-group/ds-menu-group.js +101 -0
- package/src/ds-menu-group/ds-menu-group.stories.js +99 -0
- package/src/ds-nav-item/ds-nav-item.a11y.test.js +91 -0
- package/src/ds-nav-item/ds-nav-item.js +307 -0
- package/src/ds-nav-item/ds-nav-item.stories.js +99 -0
- package/src/ds-nav-item/ds-nav-item.test.js +169 -0
- package/src/ds-nav-item/index.js +1 -0
- package/src/ds-nav-vertical/ds-nav-vertical.a11y.test.js +69 -0
- package/src/ds-nav-vertical/ds-nav-vertical.js +173 -0
- package/src/ds-nav-vertical/ds-nav-vertical.stories.js +124 -0
- package/src/ds-nav-vertical/ds-nav-vertical.test.js +176 -0
- package/src/ds-nav-vertical/index.js +1 -0
- package/src/ds-pagination/ds-pagination.a11y.test.js +50 -0
- package/src/ds-pagination/ds-pagination.js +232 -0
- package/src/ds-pagination/ds-pagination.stories.js +63 -0
- package/src/ds-pagination/ds-pagination.test.js +141 -0
- package/src/ds-pagination/index.js +1 -0
- package/src/ds-progress-bar/ds-progress-bar.a11y.test.js +25 -0
- package/src/ds-progress-bar/ds-progress-bar.js +81 -0
- package/src/ds-progress-bar/ds-progress-bar.stories.js +69 -0
- package/src/ds-progress-bar/ds-progress-bar.test.js +60 -0
- package/src/ds-radio/ds-radio.a11y.test.js +69 -0
- package/src/ds-radio/ds-radio.js +240 -0
- package/src/ds-radio/ds-radio.stories.js +102 -0
- package/src/ds-radio/ds-radio.test.js +114 -0
- package/src/ds-radio/index.js +1 -0
- package/src/ds-radio-group/ds-radio-group.a11y.test.js +164 -0
- package/src/ds-radio-group/ds-radio-group.js +257 -0
- package/src/ds-radio-group/ds-radio-group.stories.js +247 -0
- package/src/ds-radio-group/ds-radio-group.test.js +194 -0
- package/src/ds-radio-group/index.js +1 -0
- package/src/ds-rich-list/ds-rich-list.js +246 -0
- package/src/ds-rich-list/ds-rich-list.stories.js +368 -0
- package/src/ds-rich-list/ds-rich-list.test.js +293 -0
- package/src/ds-rich-list-item/ds-rich-list-item.js +579 -0
- package/src/ds-rich-list-item/ds-rich-list-item.stories.js +197 -0
- package/src/ds-rich-list-item/ds-rich-list-item.test.js +434 -0
- package/src/ds-slider/ds-slider.js +399 -0
- package/src/ds-slider/ds-slider.stories.js +107 -0
- package/src/ds-slider/ds-slider.test.js +308 -0
- package/src/ds-spinner/ds-spinner.js +173 -0
- package/src/ds-spinner/ds-spinner.stories.js +52 -0
- package/src/ds-spinner/ds-spinner.test.js +50 -0
- package/src/ds-status-border/ds-status-border.js +88 -0
- package/src/ds-status-border/ds-status-border.stories.js +242 -0
- package/src/ds-status-border/ds-status-border.test.js +168 -0
- package/src/ds-stepper/ds-stepper.a11y.test.js +198 -0
- package/src/ds-stepper/ds-stepper.js +207 -0
- package/src/ds-stepper/ds-stepper.stories.js +530 -0
- package/src/ds-stepper/ds-stepper.test.js +311 -0
- package/src/ds-stepper-item/ds-stepper-item.js +485 -0
- package/src/ds-stepper-item/ds-stepper-item.stories.js +288 -0
- package/src/ds-switch/ds-switch.js +348 -0
- package/src/ds-switch/ds-switch.stories.js +145 -0
- package/src/ds-switch/ds-switch.test.js +226 -0
- package/src/ds-switch/index.js +1 -0
- package/src/ds-tab-item/ds-tab-item.js +341 -0
- package/src/ds-tab-item/ds-tab-item.stories.js +69 -0
- package/src/ds-tabs/ds-tab-panel.js +48 -0
- package/src/ds-tabs/ds-tabs.a11y.test.js +56 -0
- package/src/ds-tabs/ds-tabs.js +180 -0
- package/src/ds-tabs/ds-tabs.stories.js +152 -0
- package/src/ds-tabs/ds-tabs.test.js +306 -0
- package/src/ds-tabs/index.js +3 -0
- package/src/ds-tag-action/ds-tag-action.a11y.test.js +32 -0
- package/src/ds-tag-action/ds-tag-action.js +185 -0
- package/src/ds-tag-action/ds-tag-action.stories.js +55 -0
- package/src/ds-tag-action/ds-tag-action.test.js +44 -0
- package/src/ds-tag-removable/ds-tag-removable.a11y.test.js +24 -0
- package/src/ds-tag-removable/ds-tag-removable.js +146 -0
- package/src/ds-tag-removable/ds-tag-removable.stories.js +52 -0
- package/src/ds-tag-removable/ds-tag-removable.test.js +46 -0
- package/src/ds-tag-status/ds-tag-status.a11y.test.js +93 -0
- package/src/ds-tag-status/ds-tag-status.js +164 -0
- package/src/ds-tag-status/ds-tag-status.stories.js +200 -0
- package/src/ds-tag-status/ds-tag-status.test.js +140 -0
- package/src/ds-tag-status/index.js +1 -0
- package/src/ds-textarea/ds-textarea-clearable.test.js +89 -0
- package/src/ds-textarea/ds-textarea.a11y.test.js +66 -0
- package/src/ds-textarea/ds-textarea.js +505 -0
- package/src/ds-textarea/ds-textarea.stories.js +335 -0
- package/src/ds-textarea/ds-textarea.test.js +218 -0
- package/src/ds-textarea/index.js +1 -0
- package/src/ds-thumbnail/ds-thumbnail.js +207 -0
- package/src/ds-thumbnail/ds-thumbnail.stories.js +217 -0
- package/src/ds-thumbnail/ds-thumbnail.test.js +220 -0
- package/src/ds-toast/ds-toast-provider.js +110 -0
- package/src/ds-toast/ds-toast.a11y.test.js +34 -0
- package/src/ds-toast/ds-toast.js +243 -0
- package/src/ds-toast/ds-toast.stories.js +143 -0
- package/src/ds-toast/ds-toast.test.js +93 -0
- package/src/ds-toast/index.js +2 -0
- package/src/ds-tooltip/ds-tooltip.a11y.test.js +110 -0
- package/src/ds-tooltip/ds-tooltip.js +217 -0
- package/src/ds-tooltip/ds-tooltip.mdx +75 -0
- package/src/ds-tooltip/ds-tooltip.stories.js +72 -0
- package/src/ds-tooltip/ds-tooltip.test.js +191 -0
- package/src/ds-tooltip/index.js +1 -0
- package/src/ds-tooltip/positioner.js +117 -0
- package/src/index.js +50 -0
- package/src/mixins/field-label.mixin.js +113 -0
- package/src/mixins/field-message.mixin.js +66 -0
- package/src/token-provider/index.js +1 -0
- package/src/token-provider/token-provider.a11y.test.js +44 -0
- package/src/token-provider/token-provider.js +85 -0
- package/src/token-provider/token-provider.stories.js +105 -0
- package/src/token-provider/token-provider.test.js +134 -0
- package/src/utils/number-input.utils.js +42 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { Meta, Canvas, Story, Controls } from '@storybook/blocks';
|
|
2
|
+
import * as InputStories from './ds-input.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={InputStories} />
|
|
5
|
+
|
|
6
|
+
# Input
|
|
7
|
+
|
|
8
|
+
A comprehensive text input component for forms with label, help text, validation states, and customizable slots.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
The `ds-input` component provides a fully-featured form input with support for:
|
|
13
|
+
- Labels with optional info buttons
|
|
14
|
+
- Help text and validation messages (error/success)
|
|
15
|
+
- Multiple input types (text, email, password, number)
|
|
16
|
+
- Prefix/suffix content via slots
|
|
17
|
+
- Custom actions (icons, buttons) inside the field
|
|
18
|
+
- Password visibility toggle
|
|
19
|
+
- Number steppers
|
|
20
|
+
- Clearable input (× button on focus)
|
|
21
|
+
- All interaction states (enabled, hover, focus, disabled, read-only, error)
|
|
22
|
+
|
|
23
|
+
<Canvas of={InputStories.Default} />
|
|
24
|
+
|
|
25
|
+
## Props
|
|
26
|
+
|
|
27
|
+
<Controls />
|
|
28
|
+
|
|
29
|
+
## States
|
|
30
|
+
|
|
31
|
+
### Default
|
|
32
|
+
Basic input with label and helper text.
|
|
33
|
+
|
|
34
|
+
<Canvas of={InputStories.Default} />
|
|
35
|
+
|
|
36
|
+
### Disabled
|
|
37
|
+
Input cannot be interacted with.
|
|
38
|
+
|
|
39
|
+
<Canvas of={InputStories.Disabled} />
|
|
40
|
+
|
|
41
|
+
### Read Only
|
|
42
|
+
Value cannot be changed but is readable.
|
|
43
|
+
|
|
44
|
+
<Canvas of={InputStories.ReadOnly} />
|
|
45
|
+
|
|
46
|
+
### Error State
|
|
47
|
+
Shows error message with icon and error styling.
|
|
48
|
+
|
|
49
|
+
<Canvas of={InputStories.Error} />
|
|
50
|
+
|
|
51
|
+
### Success State
|
|
52
|
+
Shows success message with success styling.
|
|
53
|
+
|
|
54
|
+
<Canvas of={InputStories.Success} />
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
### With Info Button
|
|
59
|
+
Label can include an info button for additional context. The button is positioned outside the label element for accessibility.
|
|
60
|
+
|
|
61
|
+
<Canvas of={InputStories.WithInfoButton} />
|
|
62
|
+
|
|
63
|
+
### Clearable Input
|
|
64
|
+
When `clearable` is enabled, a clear button (×) appears when the input is focused and has a value. Users can also press ESC to clear.
|
|
65
|
+
|
|
66
|
+
<Canvas of={InputStories.Clearable} />
|
|
67
|
+
|
|
68
|
+
### Password with Toggle
|
|
69
|
+
Password inputs can show a visibility toggle button.
|
|
70
|
+
|
|
71
|
+
<Canvas of={InputStories.PasswordInput} />
|
|
72
|
+
|
|
73
|
+
### Number with Steppers
|
|
74
|
+
Number inputs automatically display increment/decrement buttons.
|
|
75
|
+
|
|
76
|
+
<Canvas of={InputStories.NumberInput} />
|
|
77
|
+
|
|
78
|
+
### With Prefix
|
|
79
|
+
Add text or content before the input field.
|
|
80
|
+
|
|
81
|
+
<Canvas of={InputStories.WithPrefix} />
|
|
82
|
+
|
|
83
|
+
### With Suffix
|
|
84
|
+
Add text or content after the input field.
|
|
85
|
+
|
|
86
|
+
<Canvas of={InputStories.WithSuffix} />
|
|
87
|
+
|
|
88
|
+
### With Action Slot
|
|
89
|
+
Add an icon or button inside the field (right side).
|
|
90
|
+
|
|
91
|
+
<Canvas of={InputStories.WithAction} />
|
|
92
|
+
|
|
93
|
+
### Prefix and Suffix Combined
|
|
94
|
+
Use both prefix and suffix together.
|
|
95
|
+
|
|
96
|
+
<Canvas of={InputStories.WithPrefixAndSuffix} />
|
|
97
|
+
|
|
98
|
+
## Input Types
|
|
99
|
+
|
|
100
|
+
### Text
|
|
101
|
+
<Canvas of={InputStories.TextInput} />
|
|
102
|
+
|
|
103
|
+
### Email
|
|
104
|
+
<Canvas of={InputStories.EmailInput} />
|
|
105
|
+
|
|
106
|
+
### Password
|
|
107
|
+
<Canvas of={InputStories.PasswordInput} />
|
|
108
|
+
|
|
109
|
+
### Number
|
|
110
|
+
<Canvas of={InputStories.NumberInput} />
|
|
111
|
+
|
|
112
|
+
### Decimal Numbers
|
|
113
|
+
<Canvas of={InputStories.DecimalInput} />
|
|
114
|
+
|
|
115
|
+
## Usage
|
|
116
|
+
|
|
117
|
+
### Basic Usage
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<ds-input
|
|
121
|
+
label="Email"
|
|
122
|
+
placeholder="your@email.com"
|
|
123
|
+
helper="We'll never share your email"
|
|
124
|
+
></ds-input>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### With Validation
|
|
128
|
+
|
|
129
|
+
```html
|
|
130
|
+
<ds-input
|
|
131
|
+
label="Email"
|
|
132
|
+
value="invalid@"
|
|
133
|
+
validation-status="error"
|
|
134
|
+
validation-message="Please enter a valid email address"
|
|
135
|
+
></ds-input>
|
|
136
|
+
|
|
137
|
+
<ds-input
|
|
138
|
+
label="Username"
|
|
139
|
+
value="john_doe"
|
|
140
|
+
validation-status="success"
|
|
141
|
+
validation-message="Username is available"
|
|
142
|
+
></ds-input>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### With Clearable
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<ds-input
|
|
149
|
+
label="Search"
|
|
150
|
+
placeholder="Type to search..."
|
|
151
|
+
clearable
|
|
152
|
+
></ds-input>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### With Slots
|
|
156
|
+
|
|
157
|
+
```html
|
|
158
|
+
<ds-input label="Website" placeholder="example.com">
|
|
159
|
+
<span slot="prefix">https://</span>
|
|
160
|
+
</ds-input>
|
|
161
|
+
|
|
162
|
+
<ds-input label="Price" type="number" value="99.99">
|
|
163
|
+
<span slot="prefix">$</span>
|
|
164
|
+
<span slot="suffix">USD</span>
|
|
165
|
+
</ds-input>
|
|
166
|
+
|
|
167
|
+
<ds-input label="Search" placeholder="Type to search..." clearable>
|
|
168
|
+
<ds-icon-button
|
|
169
|
+
slot="action"
|
|
170
|
+
icon="search"
|
|
171
|
+
variant="action"
|
|
172
|
+
size="s"
|
|
173
|
+
></ds-icon-button>
|
|
174
|
+
</ds-input>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Password with Toggle
|
|
178
|
+
|
|
179
|
+
```html
|
|
180
|
+
<ds-input
|
|
181
|
+
label="Password"
|
|
182
|
+
type="password"
|
|
183
|
+
show-password-toggle
|
|
184
|
+
helper="Min. 8 characters"
|
|
185
|
+
></ds-input>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Number Input
|
|
189
|
+
|
|
190
|
+
```html
|
|
191
|
+
<ds-input
|
|
192
|
+
label="Quantity"
|
|
193
|
+
type="number"
|
|
194
|
+
value="1"
|
|
195
|
+
step="1"
|
|
196
|
+
></ds-input>
|
|
197
|
+
|
|
198
|
+
<ds-input
|
|
199
|
+
label="Price"
|
|
200
|
+
type="number"
|
|
201
|
+
value="10.00"
|
|
202
|
+
step="0.5"
|
|
203
|
+
></ds-input>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Keyboard Shortcuts
|
|
207
|
+
|
|
208
|
+
| Key | Action |
|
|
209
|
+
|-----|--------|
|
|
210
|
+
| `Tab` | Navigate between input and action buttons |
|
|
211
|
+
| `ESC` | Clear input value (when `clearable` is enabled) |
|
|
212
|
+
| `Ctrl/Cmd + A` | Select all text (native behavior) |
|
|
213
|
+
| `Enter/Space` | Activate focused button (password toggle, clear, etc.) |
|
|
214
|
+
|
|
215
|
+
## Events
|
|
216
|
+
|
|
217
|
+
| Event | Detail | Description |
|
|
218
|
+
|-------|--------|-------------|
|
|
219
|
+
| `input` | `{ value }` | Fired when input value changes |
|
|
220
|
+
| `change` | `{ value }` | Fired when input loses focus |
|
|
221
|
+
| `info-click` | `{ info }` | Fired when info button is clicked |
|
|
222
|
+
|
|
223
|
+
## Accessibility
|
|
224
|
+
|
|
225
|
+
### ARIA Attributes
|
|
226
|
+
- `aria-invalid="true"` when validation-status is "error"
|
|
227
|
+
- `aria-required="true"` when required
|
|
228
|
+
- `aria-describedby` links to helper text or validation message
|
|
229
|
+
- Icon buttons have descriptive `aria-label` attributes
|
|
230
|
+
|
|
231
|
+
### Keyboard Navigation
|
|
232
|
+
- All interactive elements (input, info button, action buttons) are keyboard accessible
|
|
233
|
+
- Tab order follows visual layout: info button → input → action buttons
|
|
234
|
+
- Clear button only appears on focus to reduce tab stops
|
|
235
|
+
|
|
236
|
+
### Screen Reader Support
|
|
237
|
+
- Validation messages are announced when they change
|
|
238
|
+
- Icon button labels clearly describe their purpose ("Clear input", "Show password", etc.)
|
|
239
|
+
- Error icon provides visual indication for sighted users while ARIA attributes convey state to screen readers
|
|
240
|
+
|
|
241
|
+
### Focus Management
|
|
242
|
+
- Clear focus indicators on all interactive elements
|
|
243
|
+
- Selecting all text on focus helps users quickly replace content
|
|
244
|
+
- Clear button automatically refocuses input after clearing
|
|
245
|
+
|
|
246
|
+
### Best Practices
|
|
247
|
+
- Always provide a descriptive label
|
|
248
|
+
- Use helper text to provide additional context
|
|
249
|
+
- For password fields, use `show-password-toggle` for better UX
|
|
250
|
+
- Use validation-status and validation-message for form feedback
|
|
251
|
+
- The clearable feature works best for search and filter inputs
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import '../ds-icon/ds-icon.js';
|
|
2
|
+
import '../ds-icon-button/ds-icon-button.js';
|
|
3
|
+
import './ds-input.js';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Components/Input',
|
|
7
|
+
component: 'ds-input',
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
label: { control: 'text' },
|
|
11
|
+
info: { control: 'text' },
|
|
12
|
+
placeholder: { control: 'text' },
|
|
13
|
+
value: { control: 'text' },
|
|
14
|
+
type: {
|
|
15
|
+
control: 'select',
|
|
16
|
+
options: ['text', 'email', 'password', 'number']
|
|
17
|
+
},
|
|
18
|
+
helper: { control: 'text' },
|
|
19
|
+
validationStatus: {
|
|
20
|
+
control: 'select',
|
|
21
|
+
options: [undefined, 'error', 'success']
|
|
22
|
+
},
|
|
23
|
+
validationMessage: { control: 'text' },
|
|
24
|
+
disabled: { control: 'boolean' },
|
|
25
|
+
readonly: { control: 'boolean' },
|
|
26
|
+
required: { control: 'boolean' },
|
|
27
|
+
showPasswordToggle: { control: 'boolean' },
|
|
28
|
+
clearable: { control: 'boolean' },
|
|
29
|
+
step: { control: 'number' },
|
|
30
|
+
min: { control: 'number' },
|
|
31
|
+
max: { control: 'number' },
|
|
32
|
+
clamp: { control: 'boolean' },
|
|
33
|
+
width: { control: 'text' },
|
|
34
|
+
textAlign: {
|
|
35
|
+
control: 'select',
|
|
36
|
+
options: ['left', 'center', 'right']
|
|
37
|
+
},
|
|
38
|
+
autocomplete: { control: 'text' },
|
|
39
|
+
labelPosition: {
|
|
40
|
+
control: 'select',
|
|
41
|
+
options: ['top', 'inline-start']
|
|
42
|
+
},
|
|
43
|
+
labelWidth: { control: 'text' }
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Helper to convert camelCase to kebab-case
|
|
48
|
+
const toKebabCase = (str) => str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
49
|
+
|
|
50
|
+
const Template = (args) => {
|
|
51
|
+
const input = document.createElement('ds-input');
|
|
52
|
+
|
|
53
|
+
Object.keys(args).forEach(key => {
|
|
54
|
+
if (args[key] !== undefined && args[key] !== null && args[key] !== '') {
|
|
55
|
+
const attrName = toKebabCase(key);
|
|
56
|
+
if (attrName === 'min' || attrName === 'max' || attrName === 'step') {
|
|
57
|
+
// Numeric props need to be passed as properties/attributes carefully
|
|
58
|
+
input.setAttribute(attrName, args[key]);
|
|
59
|
+
} else if (typeof args[key] === 'boolean') {
|
|
60
|
+
if (args[key]) input.setAttribute(attrName, '');
|
|
61
|
+
} else {
|
|
62
|
+
input.setAttribute(attrName, args[key]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return input;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const TemplateWithSlots = (args, slots = {}) => {
|
|
71
|
+
const wrapper = document.createElement('div');
|
|
72
|
+
|
|
73
|
+
let slotsHTML = '';
|
|
74
|
+
if (slots.prefix) slotsHTML += `<span slot="prefix">${slots.prefix}</span>`;
|
|
75
|
+
if (slots.suffix) slotsHTML += `<span slot="suffix">${slots.suffix}</span>`;
|
|
76
|
+
if (slots.action) slotsHTML += slots.action;
|
|
77
|
+
|
|
78
|
+
const attrs = Object.keys(args)
|
|
79
|
+
.filter(key => args[key] !== undefined && args[key] !== null && args[key] !== '')
|
|
80
|
+
.map(key => {
|
|
81
|
+
const attrName = toKebabCase(key);
|
|
82
|
+
if (typeof args[key] === 'boolean') {
|
|
83
|
+
return args[key] ? attrName : '';
|
|
84
|
+
}
|
|
85
|
+
return `${attrName}="${args[key]}"`;
|
|
86
|
+
})
|
|
87
|
+
.join(' ');
|
|
88
|
+
|
|
89
|
+
wrapper.innerHTML = `<ds-input ${attrs}>${slotsHTML}</ds-input>`;
|
|
90
|
+
return wrapper.firstElementChild;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// BASIC EXAMPLES
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
export const Default = Template.bind({});
|
|
98
|
+
Default.args = {
|
|
99
|
+
label: 'Email',
|
|
100
|
+
placeholder: 'Enter your email',
|
|
101
|
+
helper: 'We\'ll never share your email',
|
|
102
|
+
autocomplete: 'email'
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const InlineLabel = Template.bind({});
|
|
106
|
+
InlineLabel.args = {
|
|
107
|
+
label: 'Username',
|
|
108
|
+
labelPosition: 'inline-start',
|
|
109
|
+
labelWidth: '120px',
|
|
110
|
+
placeholder: 'Enter username',
|
|
111
|
+
helper: 'Label is aligned to the left'
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const WithInfoButton = Template.bind({});
|
|
115
|
+
WithInfoButton.args = {
|
|
116
|
+
label: 'API Key',
|
|
117
|
+
info: 'You can find your API key in account settings',
|
|
118
|
+
placeholder: 'Enter API key'
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
export const LongLabelWithInfo = Template.bind({});
|
|
123
|
+
LongLabelWithInfo.args = {
|
|
124
|
+
label: 'This is a very long label that will eventually wrap to the next line in a narrow container',
|
|
125
|
+
info: 'Information about this very long label',
|
|
126
|
+
placeholder: 'Enter text here'
|
|
127
|
+
};
|
|
128
|
+
LongLabelWithInfo.decorators = [
|
|
129
|
+
(Story) => {
|
|
130
|
+
const wrapper = document.createElement('div');
|
|
131
|
+
wrapper.style.maxWidth = '200px';
|
|
132
|
+
wrapper.appendChild(Story());
|
|
133
|
+
return wrapper;
|
|
134
|
+
}
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// STATES
|
|
139
|
+
// ============================================================================
|
|
140
|
+
|
|
141
|
+
export const Disabled = Template.bind({});
|
|
142
|
+
Disabled.args = {
|
|
143
|
+
label: 'Email',
|
|
144
|
+
value: 'user@example.com',
|
|
145
|
+
disabled: true
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const ReadOnly = Template.bind({});
|
|
149
|
+
ReadOnly.args = {
|
|
150
|
+
label: 'Username',
|
|
151
|
+
value: 'john_doe',
|
|
152
|
+
readonly: true,
|
|
153
|
+
helper: 'Username cannot be changed'
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const Error = Template.bind({});
|
|
157
|
+
Error.args = {
|
|
158
|
+
label: 'Email',
|
|
159
|
+
value: 'invalid@',
|
|
160
|
+
validationStatus: 'error',
|
|
161
|
+
validationMessage: 'Please enter a valid email address'
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const Success = Template.bind({});
|
|
165
|
+
Success.args = {
|
|
166
|
+
label: 'Email',
|
|
167
|
+
value: 'user@example.com',
|
|
168
|
+
validationStatus: 'success',
|
|
169
|
+
validationMessage: 'Email is available'
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// ============================================================================
|
|
173
|
+
// INPUT TYPES
|
|
174
|
+
// ============================================================================
|
|
175
|
+
|
|
176
|
+
export const TextInput = Template.bind({});
|
|
177
|
+
TextInput.args = {
|
|
178
|
+
label: 'Full Name',
|
|
179
|
+
type: 'text',
|
|
180
|
+
placeholder: 'John Doe',
|
|
181
|
+
required: true,
|
|
182
|
+
autocomplete: 'name'
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
export const EmailInput = Template.bind({});
|
|
186
|
+
EmailInput.args = {
|
|
187
|
+
label: 'Email Address',
|
|
188
|
+
type: 'email',
|
|
189
|
+
placeholder: 'you@example.com',
|
|
190
|
+
helper: 'Enter a valid email',
|
|
191
|
+
autocomplete: 'email'
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const PasswordInput = Template.bind({});
|
|
195
|
+
PasswordInput.args = {
|
|
196
|
+
label: 'Password',
|
|
197
|
+
type: 'password',
|
|
198
|
+
placeholder: 'Enter password',
|
|
199
|
+
showPasswordToggle: true,
|
|
200
|
+
helper: 'Min. 8 characters',
|
|
201
|
+
autocomplete: 'current-password'
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export const NumberInput = Template.bind({});
|
|
205
|
+
NumberInput.args = {
|
|
206
|
+
label: 'Age',
|
|
207
|
+
type: 'number',
|
|
208
|
+
value: '18',
|
|
209
|
+
min: 0,
|
|
210
|
+
max: 120,
|
|
211
|
+
step: 1,
|
|
212
|
+
clamp: true,
|
|
213
|
+
helper: 'Enter value between 0-120 (auto-clamps on blur)'
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export const DecimalInput = Template.bind({});
|
|
217
|
+
DecimalInput.args = {
|
|
218
|
+
label: 'Price',
|
|
219
|
+
type: 'number',
|
|
220
|
+
value: '10.00',
|
|
221
|
+
step: 0.5,
|
|
222
|
+
min: 0,
|
|
223
|
+
helper: 'Increments by 0.50'
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
export const Clearable = Template.bind({});
|
|
227
|
+
Clearable.args = {
|
|
228
|
+
label: 'Search',
|
|
229
|
+
placeholder: 'Type to search...',
|
|
230
|
+
clearable: true,
|
|
231
|
+
value: 'example query',
|
|
232
|
+
helper: 'Click × to clear or press ESC'
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// ============================================================================
|
|
236
|
+
// WITH SLOTS
|
|
237
|
+
// ============================================================================
|
|
238
|
+
|
|
239
|
+
export const WithPrefix = (args) => {
|
|
240
|
+
return TemplateWithSlots(
|
|
241
|
+
{
|
|
242
|
+
...args,
|
|
243
|
+
label: args.label || 'Website',
|
|
244
|
+
placeholder: args.placeholder || 'example.com'
|
|
245
|
+
},
|
|
246
|
+
{ prefix: 'https://' }
|
|
247
|
+
);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
export const WithSuffix = (args) => {
|
|
251
|
+
return TemplateWithSlots(
|
|
252
|
+
{
|
|
253
|
+
...args,
|
|
254
|
+
label: args.label || 'Domain',
|
|
255
|
+
placeholder: args.placeholder || 'mysite'
|
|
256
|
+
},
|
|
257
|
+
{ suffix: '.com' }
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export const WithPrefixAndSuffix = (args) => {
|
|
262
|
+
return TemplateWithSlots(
|
|
263
|
+
{
|
|
264
|
+
...args,
|
|
265
|
+
label: args.label || 'Price',
|
|
266
|
+
type: args.type || 'number',
|
|
267
|
+
value: args.value || '99.99',
|
|
268
|
+
step: args.step || 0.01
|
|
269
|
+
},
|
|
270
|
+
{ prefix: '$', suffix: 'USD' }
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export const WithAction = (args) => {
|
|
275
|
+
const actionHTML = '<ds-icon-button slot="action" icon="search" variant="action" size="s" aria-label="Search"></ds-icon-button>';
|
|
276
|
+
return TemplateWithSlots(
|
|
277
|
+
{
|
|
278
|
+
...args,
|
|
279
|
+
label: args.label || 'Search',
|
|
280
|
+
placeholder: args.placeholder || 'Search...',
|
|
281
|
+
clearable: args.clearable !== undefined ? args.clearable : true,
|
|
282
|
+
value: args.value || 'test query'
|
|
283
|
+
},
|
|
284
|
+
{ action: actionHTML }
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export const CustomWidth = Template.bind({});
|
|
289
|
+
CustomWidth.args = {
|
|
290
|
+
label: 'Compact Input',
|
|
291
|
+
width: '120px',
|
|
292
|
+
textAlign: 'center',
|
|
293
|
+
placeholder: '000',
|
|
294
|
+
type: 'number',
|
|
295
|
+
helper: 'Centered text, fixed width'
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
|