@qld-gov-au/qgds-bootstrap5 2.0.11 → 2.0.12
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/.storybook/preview.js +5 -2
- package/dist/assets/components/bs5/dateinput/dateinput.hbs +27 -27
- package/dist/assets/components/bs5/formcheck/formcheck.hbs +10 -2
- package/dist/assets/components/bs5/head/head.hbs +1 -1
- package/dist/assets/components/bs5/searchInput/searchInput.hbs +31 -29
- package/dist/assets/components/bs5/select/select.hbs +19 -19
- package/dist/assets/components/bs5/textarea/textarea.hbs +17 -17
- package/dist/assets/components/bs5/textbox/textbox.hbs +17 -18
- package/dist/assets/css/qld.bootstrap.css +2 -2
- package/dist/assets/css/qld.bootstrap.css.map +3 -3
- package/dist/assets/css/qld.bootstrap.legacy.css +2 -2
- package/dist/assets/css/qld.bootstrap.legacy.css.map +3 -3
- package/dist/assets/js/handlebars.helpers.bundle.js +1 -1
- package/dist/assets/js/handlebars.init.min.js +134 -125
- package/dist/assets/js/handlebars.init.min.js.map +2 -2
- package/dist/assets/js/handlebars.partials.js +134 -125
- package/dist/assets/js/handlebars.partials.js.map +2 -2
- package/dist/assets/js/qld.bootstrap.min.js +9 -10
- package/dist/assets/js/qld.bootstrap.min.js.map +3 -3
- package/dist/assets/node/handlebars.init.min.js +51 -7
- package/dist/assets/node/handlebars.init.min.js.map +2 -2
- package/dist/components/bs5/dateinput/dateinput.hbs +27 -27
- package/dist/components/bs5/formcheck/formcheck.hbs +10 -2
- package/dist/components/bs5/head/head.hbs +1 -1
- package/dist/components/bs5/searchInput/searchInput.hbs +31 -29
- package/dist/components/bs5/select/select.hbs +19 -19
- package/dist/components/bs5/textarea/textarea.hbs +17 -17
- package/dist/components/bs5/textbox/textbox.hbs +17 -18
- package/dist/package.json +1 -1
- package/dist/sample-data/dateinput/dateinput.data.json +14 -12
- package/dist/sample-data/formcheck/stories/checkbox/checkbox.data.json +4 -5
- package/dist/sample-data/formcheck/stories/radio/radio.data.json +4 -4
- package/dist/sample-data/searchInput/searchInput.data.json +19 -10
- package/dist/sample-data/select/select.data.json +12 -10
- package/dist/sample-data/textarea/textarea.data.json +14 -11
- package/dist/sample-data/textbox/textbox.data.json +13 -10
- package/package.json +1 -1
- package/src/components/bs5/dateinput/Dateinput.js +26 -11
- package/src/components/bs5/dateinput/dateinput.data.json +14 -12
- package/src/components/bs5/dateinput/dateinput.hbs +27 -27
- package/src/components/bs5/formcheck/Formcheck.js +57 -6
- package/src/components/bs5/formcheck/_form-variables.scss +131 -0
- package/src/components/bs5/formcheck/formcheck.hbs +10 -2
- package/src/components/bs5/formcheck/formcheck.scss +229 -66
- package/src/components/bs5/formcheck/stories/bootstrap-validation/bootstrap-validation.stories.js +304 -0
- package/src/components/bs5/formcheck/stories/checkbox/checkbox.data.json +4 -5
- package/src/components/bs5/formcheck/stories/checkbox/checkbox.stories.js +19 -111
- package/src/components/bs5/formcheck/stories/radio/radio.data.json +4 -4
- package/src/components/bs5/formcheck/stories/radio/radio.stories.js +30 -122
- package/src/components/bs5/inpageAlert/inpageAlert.scss +1 -1
- package/src/components/bs5/pageLayout/{ThemeShowcase.stories.js → PaletteShowcase.stories.js} +36 -35
- package/src/components/bs5/searchInput/__snapshots__/searchInput.test.js.snap +24 -28
- package/src/components/bs5/searchInput/search.functions.js +93 -76
- package/src/components/bs5/searchInput/searchInput.data.json +19 -10
- package/src/components/bs5/searchInput/searchInput.hbs +31 -29
- package/src/components/bs5/searchInput/searchInput.scss +140 -196
- package/src/components/bs5/searchInput/searchInput.stories.js +35 -13
- package/src/components/bs5/searchInput/searchInput.test.js +5 -1
- package/src/components/bs5/select/Select.js +13 -5
- package/src/components/bs5/select/Select.stories.js +27 -83
- package/src/components/bs5/select/select.data.json +12 -10
- package/src/components/bs5/select/select.hbs +19 -19
- package/src/components/bs5/textarea/Textarea.js +13 -5
- package/src/components/bs5/textarea/Textarea.stories.js +29 -55
- package/src/components/bs5/textarea/textarea.data.json +14 -11
- package/src/components/bs5/textarea/textarea.hbs +17 -17
- package/src/components/bs5/textbox/Textbox.js +16 -5
- package/src/components/bs5/textbox/Textbox.stories.js +26 -51
- package/src/components/bs5/textbox/textInput.scss +12 -232
- package/src/components/bs5/textbox/textbox.data.json +13 -10
- package/src/components/bs5/textbox/textbox.hbs +17 -18
- package/src/css/functions/_index.scss +2 -0
- package/src/css/functions/remify.scss +32 -0
- package/src/css/functions/snap-line-height.scss +7 -0
- package/src/css/main.scss +1 -1
- package/src/css/mixins/focusable.scss +3 -0
- package/src/css/{qld-theme.scss → qld-palettes.scss} +30 -23
- package/src/components/bs5/formcheck/_formcheck.stories.bak.js +0 -432
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
@use "../../../css/mixins" as m;
|
|
2
|
+
@use "../../../css/functions" as f;
|
|
3
|
+
@import "form-variables";
|
|
1
4
|
// NTGOV Form Controls, radios, checkboxes and input groups
|
|
2
5
|
// https://getbootstrap.com/docs/5.2/forms/form-control/#variables
|
|
3
6
|
|
|
@@ -16,11 +19,8 @@ $input-padding-y-lg: $input-padding-y;
|
|
|
16
19
|
$input-padding-x-lg: $input-padding-x;
|
|
17
20
|
$input-font-size-lg: 1rem;
|
|
18
21
|
|
|
19
|
-
$input-border-width:
|
|
22
|
+
$input-border-width: 2px;
|
|
20
23
|
$input-border-radius: $border-radius;
|
|
21
|
-
$input-transition:
|
|
22
|
-
border-color 0.15s ease-in-out,
|
|
23
|
-
box-shadow 0.15s ease-in-out;
|
|
24
24
|
|
|
25
25
|
// Checks and radios (LARGE)
|
|
26
26
|
$form-check-input-width: 2rem;
|
|
@@ -29,8 +29,9 @@ $form-check-input-width: 2rem;
|
|
|
29
29
|
$form-check-input-width-small: 1.375rem;
|
|
30
30
|
|
|
31
31
|
$form-check-min-height: $font-size-base * $line-height-base;
|
|
32
|
-
$form-check-padding-start: $form-check-input-width +
|
|
33
|
-
$form-check-margin-
|
|
32
|
+
$form-check-padding-start: $form-check-input-width + f.remify(8px);
|
|
33
|
+
$form-check-margin-top: f.remify(8px);
|
|
34
|
+
$form-check-margin-bottom: f.remify(16px);
|
|
34
35
|
$form-check-label-color: $body-color;
|
|
35
36
|
$form-check-label-cursor: null;
|
|
36
37
|
$form-check-transition: null;
|
|
@@ -52,8 +53,7 @@ $form-check-valid-color: var(--#{$prefix}notify-success);
|
|
|
52
53
|
$form-check-invalid-color: var(--#{$prefix}notify-warning);
|
|
53
54
|
|
|
54
55
|
// OUTLINES
|
|
55
|
-
$form-check-input-outline: 3px solid var(--#{$prefix}
|
|
56
|
-
$form-check-input-outline-dark: 3px solid var(--#{$prefix}dark-focus);
|
|
56
|
+
$form-check-input-outline: 3px solid var(--#{$prefix}focus-color);
|
|
57
57
|
|
|
58
58
|
// SMALL BORDER WIDTH
|
|
59
59
|
$form-check-input-border-width-small: 2px;
|
|
@@ -80,9 +80,7 @@ $form-check-bg-image-dark: url("data:image/svg+xml,%3Csvg width='18' height='14'
|
|
|
80
80
|
// ESCAPE SVGs FOR ICONS
|
|
81
81
|
$form-radio-button: #{escape-svg($form-radio-bg-image)};
|
|
82
82
|
$form-radio-button-small: #{escape-svg($form-radio-bg-image-small)};
|
|
83
|
-
$form-radio-button-dark: #{escape-svg($form-radio-bg-image-dark)};
|
|
84
83
|
$form-checkbox-button: #{escape-svg($form-check-bg-image)};
|
|
85
|
-
$form-checkbox-button-dark: #{escape-svg($form-check-bg-image-dark)};
|
|
86
84
|
|
|
87
85
|
$form-check-input-indeterminate-color: $component-active-color;
|
|
88
86
|
$form-check-input-indeterminate-bg-color: $component-active-bg;
|
|
@@ -108,30 +106,133 @@ $form-check-inline-margin-end: 1rem;
|
|
|
108
106
|
margin-bottom: 0.5rem;
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
%form-control {
|
|
110
|
+
$_padding: calc(#{$input-padding-y} - var(--#{$prefix}border-top-width, 0px))
|
|
111
|
+
calc(#{$input-padding-x} - var(--#{$prefix}border-right-width, 0px))
|
|
112
|
+
calc(#{$input-padding-y} - var(--#{$prefix}border-bottom-width, 0px))
|
|
113
|
+
calc(#{$input-padding-x} - var(--#{$prefix}border-right-width, 0px));
|
|
114
|
+
@extend %form-control-variables;
|
|
115
|
+
|
|
116
|
+
color: var(--#{$prefix}color);
|
|
117
|
+
background-color: var(--#{$prefix}bg);
|
|
118
|
+
border: {
|
|
119
|
+
style: solid;
|
|
120
|
+
width: var(--#{$prefix}border-width);
|
|
121
|
+
color: var(--#{$prefix}border-color);
|
|
122
|
+
}
|
|
123
|
+
padding: $_padding;
|
|
124
|
+
border-radius: var(--#{$prefix}border-radius);
|
|
125
|
+
font-size: $input-font-size;
|
|
126
|
+
min-height: f.remify(52px);
|
|
115
127
|
|
|
116
|
-
|
|
117
|
-
--#{$prefix}input-border-color: #{$form-check-input-checked-border-color};
|
|
118
|
-
--#{$prefix}input-color: var(--#{$prefix}dark-grey-muted);
|
|
119
|
-
--#{$prefix}input-border-color-hover: var(--#{$prefix}button-dark-blue);
|
|
128
|
+
transition: none; // no transitions until a holistic decision at design system level
|
|
120
129
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
font-size: $input-font-size;
|
|
130
|
+
&::placeholder {
|
|
131
|
+
color: var(--#{$prefix}placeholder-color);
|
|
132
|
+
}
|
|
125
133
|
|
|
126
|
-
&:
|
|
127
|
-
|
|
128
|
-
border-color: var(--#{$prefix}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
&:hover {
|
|
135
|
+
background-color: var(--#{$prefix}bg-hover);
|
|
136
|
+
border-color: var(--#{$prefix}border-color-hover);
|
|
137
|
+
|
|
138
|
+
&::placeholder {
|
|
139
|
+
color: var(--#{$prefix}placeholder-color-hover);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@include m.focusable() {
|
|
144
|
+
color: var(--#{$prefix}color-focus);
|
|
145
|
+
background-color: var(--#{$prefix}bg-focus);
|
|
146
|
+
border-color: var(--#{$prefix}border-color-focus);
|
|
147
|
+
box-shadow: none !important;
|
|
148
|
+
|
|
149
|
+
&::placeholder {
|
|
150
|
+
color: var(--#{$prefix}placeholder-color-focus);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
&:disabled,
|
|
155
|
+
&.disabled,
|
|
156
|
+
&.is-disabled {
|
|
157
|
+
&,
|
|
158
|
+
&:hover {
|
|
159
|
+
cursor: not-allowed;
|
|
160
|
+
opacity: 0.5;
|
|
161
|
+
background-color: var(--#{$prefix}bg);
|
|
162
|
+
border-color: var(--#{$prefix}border-color);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Bootstrap base styling overrides
|
|
166
|
+
&.is-valid,
|
|
167
|
+
&.is-invalid,
|
|
168
|
+
.was-validated &:valid,
|
|
169
|
+
.was-validated &:invalid {
|
|
170
|
+
border-color: var(
|
|
171
|
+
--#{$prefix}border-color
|
|
172
|
+
); // already scoped to .valid/.invalid.
|
|
173
|
+
// USe the same padding value as default state, and remove the background image icon.
|
|
174
|
+
padding: $_padding;
|
|
175
|
+
background-image: none;
|
|
176
|
+
|
|
177
|
+
&:focus {
|
|
178
|
+
border-color: var(
|
|
179
|
+
--#{$prefix}border-color-focus
|
|
180
|
+
); // This value already scoped to valid/invalid.
|
|
181
|
+
}
|
|
132
182
|
}
|
|
133
183
|
}
|
|
134
184
|
|
|
185
|
+
.form-control {
|
|
186
|
+
@extend %form-control;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// .qld-select legacy class
|
|
190
|
+
.form-select,
|
|
191
|
+
.qld-select {
|
|
192
|
+
@extend %form-control;
|
|
193
|
+
|
|
194
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='%23008635' d='M4.251 181.1C7.392 177.7 11.69 175.1 16 175.1c3.891 0 7.781 1.406 10.86 4.25l197.1 181.1l197.1-181.1c6.5-6 16.64-5.625 22.61 .9062c6 6.5 5.594 16.59-.8906 22.59l-208 192c-6.156 5.688-15.56 5.688-21.72 0l-208-192C-1.343 197.7-1.749 187.6 4.251 181.1z'/%3E%3C/svg%3E");
|
|
195
|
+
background-repeat: no-repeat;
|
|
196
|
+
appearance: none;
|
|
197
|
+
background-position: right 1rem center;
|
|
198
|
+
background-size: f.remify(20px);
|
|
199
|
+
background-origin: border-box;
|
|
200
|
+
width: 100%;
|
|
201
|
+
|
|
202
|
+
&.disabled,
|
|
203
|
+
&:disabled,
|
|
204
|
+
&.is-disabled {
|
|
205
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='%23636363' d='M4.251 181.1C7.392 177.7 11.69 175.1 16 175.1c3.891 0 7.781 1.406 10.86 4.25l197.1 181.1l197.1-181.1c6.5-6 16.64-5.625 22.61 .9062c6 6.5 5.594 16.59-.8906 22.59l-208 192c-6.156 5.688-15.56 5.688-21.72 0l-208-192C-1.343 197.7-1.749 187.6 4.251 181.1z'/%3E%3C/svg%3E");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
&.qld-input-error,
|
|
209
|
+
&.is-invalid {
|
|
210
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='%23E22339' d='M4.251 181.1C7.392 177.7 11.69 175.1 16 175.1c3.891 0 7.781 1.406 10.86 4.25l197.1 181.1l197.1-181.1c6.5-6 16.64-5.625 22.61 .9062c6 6.5 5.594 16.59-.8906 22.59l-208 192c-6.156 5.688-15.56 5.688-21.72 0l-208-192C-1.343 197.7-1.749 187.6 4.251 181.1z'/%3E%3C/svg%3E");
|
|
211
|
+
|
|
212
|
+
&:not([multiple]):not([size]),
|
|
213
|
+
&:not([multiple])[size="1"] {
|
|
214
|
+
background-size: 1rem auto;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
&.qld-input-success,
|
|
219
|
+
&.is-valid {
|
|
220
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='%23339d37' d='M4.251 181.1C7.392 177.7 11.69 175.1 16 175.1c3.891 0 7.781 1.406 10.86 4.25l197.1 181.1l197.1-181.1c6.5-6 16.64-5.625 22.61 .9062c6 6.5 5.594 16.59-.8906 22.59l-208 192c-6.156 5.688-15.56 5.688-21.72 0l-208-192C-1.343 197.7-1.749 187.6 4.251 181.1z'/%3E%3C/svg%3E");
|
|
221
|
+
|
|
222
|
+
&:not([multiple]):not([size]),
|
|
223
|
+
&:not([multiple])[size="1"] {
|
|
224
|
+
background-size: 1rem auto;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.form-check {
|
|
230
|
+
display: flex;
|
|
231
|
+
align-items: center;
|
|
232
|
+
margin-block: f.remify(8px 16px);
|
|
233
|
+
padding-left: 0; // override bootstrap default - icon is now attached to <label>.
|
|
234
|
+
}
|
|
235
|
+
|
|
135
236
|
.form-check-input {
|
|
136
237
|
// hides the original input element, and + label recreates it visually
|
|
137
238
|
background-size: calc(2rem - 8px);
|
|
@@ -152,7 +253,6 @@ $form-check-inline-margin-end: 1rem;
|
|
|
152
253
|
vertical-align: bottom;
|
|
153
254
|
line-height: 2rem;
|
|
154
255
|
padding-left: 2.5rem;
|
|
155
|
-
margin-left: -1.5em;
|
|
156
256
|
|
|
157
257
|
// before is the outer box with border,
|
|
158
258
|
// after is the check or radio icon
|
|
@@ -236,26 +336,6 @@ $form-check-inline-margin-end: 1rem;
|
|
|
236
336
|
}
|
|
237
337
|
}
|
|
238
338
|
|
|
239
|
-
// DEFAULT VALID & INVALID
|
|
240
|
-
.valid &,
|
|
241
|
-
.invalid & {
|
|
242
|
-
&:focus + label::before {
|
|
243
|
-
outline: $form-check-input-outline;
|
|
244
|
-
outline-offset: 2px;
|
|
245
|
-
box-shadow: none;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
.valid & {
|
|
249
|
-
+ label::before {
|
|
250
|
-
outline: $form-check-valid-color solid 2px;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
.invalid & {
|
|
254
|
-
+ label::before {
|
|
255
|
-
outline: $form-check-invalid-color solid 2px;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
339
|
.small & {
|
|
260
340
|
&:checked + label::before {
|
|
261
341
|
background-size: 22px 22px;
|
|
@@ -287,32 +367,115 @@ $form-check-inline-margin-end: 1rem;
|
|
|
287
367
|
|
|
288
368
|
.dark &,
|
|
289
369
|
.dark-alt & {
|
|
290
|
-
&:hover + label {
|
|
291
|
-
--_background-color: $form-check-hover;
|
|
292
|
-
}
|
|
293
|
-
&:hover + label::after {
|
|
294
|
-
background-color: $form-check-hover;
|
|
295
|
-
}
|
|
296
370
|
+ label::before,
|
|
297
371
|
+ label::after {
|
|
298
372
|
border-color: $form-check-input-border-dark;
|
|
299
373
|
}
|
|
300
|
-
&:
|
|
301
|
-
|
|
302
|
-
|
|
374
|
+
&:hover + label {
|
|
375
|
+
--_background-color: $form-check-hover;
|
|
376
|
+
|
|
377
|
+
&::after {
|
|
378
|
+
background-color: $form-check-hover;
|
|
303
379
|
}
|
|
304
380
|
}
|
|
305
|
-
|
|
306
|
-
|
|
381
|
+
&:checked + label::after {
|
|
382
|
+
background-color: $form-check-input-checked-color-dark;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
&:focus + label::before {
|
|
386
|
+
outline: $form-check-input-outline;
|
|
307
387
|
}
|
|
308
388
|
}
|
|
309
389
|
}
|
|
310
390
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
391
|
+
%_form-check-input-valid,
|
|
392
|
+
%_form-check-input-invalid {
|
|
393
|
+
+ .form-check-label {
|
|
394
|
+
color: var(--body-color);
|
|
395
|
+
}
|
|
396
|
+
&:focus + .form-check-label::before {
|
|
397
|
+
outline: $form-check-input-outline;
|
|
398
|
+
outline-offset: 2px;
|
|
399
|
+
box-shadow: none;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
%_form-check-input-valid {
|
|
404
|
+
+ .form-check-label::before {
|
|
405
|
+
outline: $form-check-valid-color solid 2px;
|
|
317
406
|
}
|
|
318
407
|
}
|
|
408
|
+
|
|
409
|
+
%_form-check-input-invalid {
|
|
410
|
+
+ .form-check-label::before {
|
|
411
|
+
outline: $form-check-invalid-color solid 2px;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.valid .form-check-input,
|
|
416
|
+
.is-valid .form-check-input,
|
|
417
|
+
.was-validated .form-check-input:valid {
|
|
418
|
+
@extend %_form-check-input-valid;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.invalid .form-check-input,
|
|
422
|
+
.is-invalid .form-check-input,
|
|
423
|
+
.was-validated .form-check-input:invalid {
|
|
424
|
+
@extend %_form-check-input-invalid;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
%_valid-feedback,
|
|
428
|
+
%_invalid-feedback {
|
|
429
|
+
font-size: f.remify(14px);
|
|
430
|
+
line-height: f.snap-line-height(1.5);
|
|
431
|
+
display: none;
|
|
432
|
+
gap: f.remify(4px);
|
|
433
|
+
flex-wrap: nowrap;
|
|
434
|
+
margin-block: f.remify(8px);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
%_valid-feedback {
|
|
438
|
+
@extend %form-valid-feedback-variables;
|
|
439
|
+
color: var(--color-text);
|
|
440
|
+
|
|
441
|
+
@include m.make-icon(
|
|
442
|
+
$name: "status-success",
|
|
443
|
+
$size: "sm",
|
|
444
|
+
$pseudo: "before"
|
|
445
|
+
) {
|
|
446
|
+
flex-shrink: 0;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
%_invalid-feedback {
|
|
451
|
+
@extend %form-invalid-feedback-variables;
|
|
452
|
+
color: var(--color-text);
|
|
453
|
+
@include m.make-icon($name: "status-error", $size: "sm", $pseudo: "before") {
|
|
454
|
+
flex-shrink: 0;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// .qld-input-success legacy class which is applied to both inputs, and feedback text >:(
|
|
459
|
+
.valid-feedback,
|
|
460
|
+
.qld-input-success:not(select, textarea, input[type="text"], .date-container) {
|
|
461
|
+
@extend %_valid-feedback;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.invalid-feedback,
|
|
465
|
+
.qld-input-error:not(select, textarea, input[type="text"], .date-container) {
|
|
466
|
+
@extend %_invalid-feedback;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Bootstrap default styles already support placing feedback after the input element.
|
|
470
|
+
// However for QGDS Feedback is placed before the validated input element, so CSS sibling doesn't work here.
|
|
471
|
+
// We can has() to check for the existence of next siblings instead.
|
|
472
|
+
.valid-feedback:has(~ .is-valid, ~ * .is-valid ),
|
|
473
|
+
.invalid-feedback:has(~ .is-invalid, ~ * .is-invalid),
|
|
474
|
+
.was-validated .valid-feedback:has(~ :where(input, select, textarea):valid, ~ * :where(input, select, textarea):valid), // need to scope to input:valid within has(), :valid seems to match non inputs. the form-check input is wrapped in a div, so * > :input-valid
|
|
475
|
+
.was-validated .invalid-feedback:has(~ :invalid, ~ * :invalid),
|
|
476
|
+
.valid .valid-feedback,
|
|
477
|
+
.invalid .invalid-feedback,
|
|
478
|
+
.qld-input-success:not(select, textarea, input[type="text"]),
|
|
479
|
+
.qld-input-error:not(select, textarea, input[type="text"]) {
|
|
480
|
+
display: flex;
|
|
481
|
+
}
|
package/src/components/bs5/formcheck/stories/bootstrap-validation/bootstrap-validation.stories.js
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// Bootstrap5Validation.stories.js
|
|
2
|
+
import { Formcheck, argTypes } from "../../Formcheck.js";
|
|
3
|
+
import { Textarea } from "../../../textarea/Textarea.js";
|
|
4
|
+
import { Textbox } from "../../../textbox/Textbox.js";
|
|
5
|
+
import { Select } from "../../../select/Select.js";
|
|
6
|
+
import { Dateinput } from "../../../dateinput/Dateinput.js";
|
|
7
|
+
import checkboxData from "../checkbox/checkbox.data.json";
|
|
8
|
+
import textAreaData from "../../../textarea/textarea.data.json";
|
|
9
|
+
import textInputData from "../../../textbox/textbox.data.json";
|
|
10
|
+
import dateInputData from "../../../dateinput/dateinput.data.json";
|
|
11
|
+
import selectData from "../../../select/select.data.json";
|
|
12
|
+
|
|
13
|
+
checkboxData.optionalLabel = null;
|
|
14
|
+
textAreaData.optionalLabel = null;
|
|
15
|
+
textInputData.optionalLabel = null;
|
|
16
|
+
selectData.optionalLabel = null;
|
|
17
|
+
dateInputData.optionalLabel = null;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* This page tests and compares various validation styling methods.
|
|
21
|
+
*
|
|
22
|
+
* Server side validation:
|
|
23
|
+
* - via Bootstrap's `.is-valid`/`.is-invalid` classes.
|
|
24
|
+
* - also via legacy `.valid` and `.invalid` classes on the form element.
|
|
25
|
+
*
|
|
26
|
+
* Client side validation:
|
|
27
|
+
* - via `.was-validated` class on the `<form>` element targeting html5 `:valid` and `:invalid` input element pseudoclasses.
|
|
28
|
+
*
|
|
29
|
+
* #### Multiple input forms and validation feedback
|
|
30
|
+
* Because validation feedback (error or success messages) styles rely on sibling input selectors,
|
|
31
|
+
* it is important to wrap individual form components in a containing div when also providing validation feedback.
|
|
32
|
+
*/
|
|
33
|
+
export default {
|
|
34
|
+
tags: ["autodocs"],
|
|
35
|
+
title: "3. Components/Forms/Form Validation",
|
|
36
|
+
args: {
|
|
37
|
+
...checkboxData, // There is no shared form element abstract args, only use controls common to all inputs (isValid, successMessage, errorMessage)
|
|
38
|
+
_storyId: "default", // make it easier to create unique ids for each input.
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
render: (args) => `
|
|
42
|
+
<div class="mb-32">${new Formcheck({ ...args, listitems: [{ ...checkboxData.listitems[0], id: `${args._storyId}Checkbox1` }] }).html}</div>
|
|
43
|
+
<div class="mb-32">${
|
|
44
|
+
new Formcheck({
|
|
45
|
+
...checkboxData,
|
|
46
|
+
isValid: args.isValid,
|
|
47
|
+
type: "radio",
|
|
48
|
+
listitems: [
|
|
49
|
+
{ ...checkboxData.listitems[0], id: `${args._storyId}Radio1` },
|
|
50
|
+
{ ...checkboxData.listitems[1], id: `${args._storyId}Radio2` },
|
|
51
|
+
],
|
|
52
|
+
}).html
|
|
53
|
+
}</div>
|
|
54
|
+
<div class="mb-32">${new Textarea({ ...textAreaData, isValid: args.isValid, id: `${args._storyId}Textarea`, isRequired: true, successMessageText: args.successMessageText, errorMessageText: args.errorMessageText }).html}</div>
|
|
55
|
+
<div class="mb-32">${new Textbox({ ...textInputData, isValid: args.isValid, id: `${args._storyId}TextInput`, isRequired: true, successMessageText: args.successMessageText, errorMessageText: args.errorMessageText }).html}</div>
|
|
56
|
+
<div class="mb-32">${new Select({ ...selectData, isValid: args.isValid, id: `${args._storyId}SelectInvalid`, isRequired: true, successMessageText: args.successMessageText, errorMessageText: args.errorMessageText }).html}</div>
|
|
57
|
+
<div class="mb-32">${new Dateinput({ ...dateInputData, isValid: args.isValid, id: `${args._storyId}DateInvalid`, isRequired: true, successMessageText: args.successMessageText, errorMessageText: args.errorMessageText }).html}</div>
|
|
58
|
+
`,
|
|
59
|
+
|
|
60
|
+
argTypes,
|
|
61
|
+
parameters: {
|
|
62
|
+
controls: {
|
|
63
|
+
include: ["isValid", "successMessageText", "errorMessageText"],
|
|
64
|
+
},
|
|
65
|
+
backgrounds: { disable: false },
|
|
66
|
+
},
|
|
67
|
+
globals: { backgrounds: { value: "default" } },
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Server side validation with `isValid: false`
|
|
72
|
+
*/
|
|
73
|
+
export const BootstrapIsInvalid = {
|
|
74
|
+
name: "Server side - invalid",
|
|
75
|
+
args: {
|
|
76
|
+
isValid: false,
|
|
77
|
+
_storyId: "BootstrapIsInvalid",
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Server side validation with `isValid: true`
|
|
83
|
+
*/
|
|
84
|
+
export const BootstrapIsValid = {
|
|
85
|
+
name: "Server side - valid",
|
|
86
|
+
args: {
|
|
87
|
+
isValid: true,
|
|
88
|
+
_storyId: "BootstrapIsValid",
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Client side validation offers the best user experience, however it not always suit your needs.
|
|
94
|
+
* For client side validation using HTML5 features, add the `novalidate` boolean attribute to your `<form>`.
|
|
95
|
+
* This disables the browser default feedback tooltips, but still provides access to the form validation APIs in JavaScript.
|
|
96
|
+
* Try to submit the form below; our JavaScript will intercept the submit button and relay feedback to you.
|
|
97
|
+
* Validation styles using built-in HTML5 validation require class `was-validated` to be added to the `<form>` and rely on the `:invalid` and `:valid` pseudoclasses.
|
|
98
|
+
* #### Multiple input forms and validation feedback
|
|
99
|
+
* Because validation feedback (error or success messages) styles rely on sibling input selectors,
|
|
100
|
+
* it is important to wrap individual form components in a containing div when also providing validation feedback.
|
|
101
|
+
*/
|
|
102
|
+
export const HTML5NeedsValidation = {
|
|
103
|
+
name: "Client side validation",
|
|
104
|
+
|
|
105
|
+
args: {
|
|
106
|
+
...checkboxData,
|
|
107
|
+
_storyId: "HTML5NeedsValidation",
|
|
108
|
+
listitems: [
|
|
109
|
+
{
|
|
110
|
+
...checkboxData.listitems[0],
|
|
111
|
+
id: "needs-validation-1",
|
|
112
|
+
label: "Required checkbox (unchecked will be invalid)",
|
|
113
|
+
isChecked: false,
|
|
114
|
+
isRequired: true,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
...checkboxData.listitems[1],
|
|
118
|
+
id: "needs-validation-2",
|
|
119
|
+
label: "Required checkbox (checked will be valid)",
|
|
120
|
+
isChecked: true,
|
|
121
|
+
isRequired: true,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
render: (args) => {
|
|
126
|
+
return `
|
|
127
|
+
<form class="needs-validation" novalidate>
|
|
128
|
+
<div class="row">
|
|
129
|
+
<div class="col">
|
|
130
|
+
<div class="mb-32">${new Formcheck({ ...args, listitems: [{ ...checkboxData.listitems[0], id: `${args._storyId}CheckboxInvalid` }] }).html}</div>
|
|
131
|
+
<div class="mb-32">${new Textarea({ ...textAreaData, id: `${args._storyId}TextareaInvalid`, value: "", isRequired: true }).html}</div>
|
|
132
|
+
<div class="mb-32">${new Textbox({ ...textInputData, id: `${args._storyId}TextInputInvalid`, value: "", isRequired: true }).html}</div>
|
|
133
|
+
<div class="mb-32">${new Select({ ...selectData, id: `${args._storyId}SelectInvalid`, isRequired: true }).html}</div>
|
|
134
|
+
<div class="mb-32">${new Dateinput({ ...dateInputData, id: `${args._storyId}DateInvalid`, isRequired: true }).html}</div>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="col">
|
|
137
|
+
<div class="mb-32">${new Formcheck({ ...args, listitems: [{ ...checkboxData.listitems[1], id: `${args._storyId}CheckboxValid` }] }).html}</div>
|
|
138
|
+
<div class="mb-32">${new Textarea({ ...textAreaData, id: `${args._storyId}TextareaValid`, value: "I have value!", isRequired: true }).html}</div>
|
|
139
|
+
<div class="mb-32">${new Textbox({ ...textInputData, id: `${args._storyId}TextInputValid`, value: "I have value also!", isRequired: true }).html}</div>
|
|
140
|
+
<div class="mb-32">${new Select({ ...selectData, id: `${args._storyId}SelectValid`, isRequired: true }).html}</div>
|
|
141
|
+
<div class="mb-32">${new Dateinput({ ...dateInputData, id: `${args._storyId}DateValid`, isRequired: true }).html}</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<button class="btn btn-primary mt-3" type="submit" onclick="validateForm(event)">Validate</button>
|
|
146
|
+
</form>
|
|
147
|
+
<script>
|
|
148
|
+
function validateForm(event) {
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
event.stopPropagation();
|
|
151
|
+
const form = event.target.closest('form');
|
|
152
|
+
|
|
153
|
+
form.elements["${args._storyId}CheckboxInvalid"].checked = false;
|
|
154
|
+
form.elements["${args._storyId}CheckboxValid"].checked = true;
|
|
155
|
+
|
|
156
|
+
form.elements["${args._storyId}TextareaInvalid"].value = "";
|
|
157
|
+
form.elements["${args._storyId}TextareaValid"].value = "I have value!";
|
|
158
|
+
|
|
159
|
+
form.elements["${args._storyId}TextInputInvalid"].value = "";
|
|
160
|
+
form.elements["${args._storyId}TextInputValid"].value = "I have value also!";
|
|
161
|
+
|
|
162
|
+
form.elements["${args._storyId}SelectInvalid"].value = null;
|
|
163
|
+
form.elements["${args._storyId}SelectValid"].value = "2";
|
|
164
|
+
|
|
165
|
+
form.elements["${args._storyId}DateInvalid-dayinput"].value = "";
|
|
166
|
+
|
|
167
|
+
form.elements["${args._storyId}DateValid-dayinput"].value = "2";
|
|
168
|
+
form.elements["${args._storyId}DateValid-monthinput"].value = "2";
|
|
169
|
+
form.elements["${args._storyId}DateValid-yearinput"].value = "2222";
|
|
170
|
+
|
|
171
|
+
form.classList.add('was-validated');
|
|
172
|
+
}
|
|
173
|
+
</script>`;
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const BootstrapInvalidDark = {
|
|
178
|
+
name: "Dark Palette - Invalid",
|
|
179
|
+
args: {
|
|
180
|
+
isValid: false,
|
|
181
|
+
_storyId: "BootstrapInvalidDark",
|
|
182
|
+
},
|
|
183
|
+
parameters: {
|
|
184
|
+
docs: {
|
|
185
|
+
source: {
|
|
186
|
+
excludeDecorators: false,
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
globals: { backgrounds: { value: "dark" } },
|
|
191
|
+
decorators: [
|
|
192
|
+
(Story) => {
|
|
193
|
+
return `
|
|
194
|
+
<div class="dark">${Story()}</div>`;
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export const BootstrapValidDark = {
|
|
200
|
+
name: "Dark Palette - Valid",
|
|
201
|
+
args: {
|
|
202
|
+
isValid: true,
|
|
203
|
+
_storyId: "BootstrapValidDark",
|
|
204
|
+
},
|
|
205
|
+
parameters: {
|
|
206
|
+
docs: {
|
|
207
|
+
source: {
|
|
208
|
+
excludeDecorators: false,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
globals: { backgrounds: { value: "dark" } },
|
|
213
|
+
decorators: [
|
|
214
|
+
(Story) => {
|
|
215
|
+
return `
|
|
216
|
+
<div class="dark">${Story()}</div>`;
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* `.valid` and `.invalid` classes on the form or parent element.
|
|
223
|
+
*/
|
|
224
|
+
export const ValidationComparison = {
|
|
225
|
+
name: "Legacy Validation Comparison",
|
|
226
|
+
args: {
|
|
227
|
+
...checkboxData,
|
|
228
|
+
questionLabel: "Comparison of validation methods",
|
|
229
|
+
listitems: [
|
|
230
|
+
{
|
|
231
|
+
...checkboxData.listitems[0],
|
|
232
|
+
id: "compare-1",
|
|
233
|
+
label: "Legacy .valid",
|
|
234
|
+
isChecked: true,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
...checkboxData.listitems[1],
|
|
238
|
+
id: "compare-2",
|
|
239
|
+
label: "Legacy .invalid",
|
|
240
|
+
isChecked: false,
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
...checkboxData.listitems[2],
|
|
244
|
+
id: "compare-3",
|
|
245
|
+
label: "Bootstrap .is-valid",
|
|
246
|
+
isChecked: true,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
...checkboxData.listitems[3],
|
|
250
|
+
id: "compare-4",
|
|
251
|
+
label: "Bootstrap .is-invalid",
|
|
252
|
+
isChecked: false,
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
render: (args) => {
|
|
257
|
+
return `
|
|
258
|
+
<div class="row">
|
|
259
|
+
<div class="col">
|
|
260
|
+
<div class="valid">
|
|
261
|
+
${
|
|
262
|
+
new Formcheck({
|
|
263
|
+
...args,
|
|
264
|
+
questionLabel: "Legacy classes - .valid",
|
|
265
|
+
listitems: [args.listitems[0]],
|
|
266
|
+
}).html
|
|
267
|
+
}
|
|
268
|
+
</div>
|
|
269
|
+
<div class="invalid">
|
|
270
|
+
${
|
|
271
|
+
new Formcheck({
|
|
272
|
+
...args,
|
|
273
|
+
questionLabel: "Legacy classes - .invalid",
|
|
274
|
+
listitems: [args.listitems[1]],
|
|
275
|
+
}).html
|
|
276
|
+
}
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
<div class="col">
|
|
280
|
+
<div>
|
|
281
|
+
${
|
|
282
|
+
new Formcheck({
|
|
283
|
+
...args,
|
|
284
|
+
questionLabel: "Bootstrap classes - .is-valid",
|
|
285
|
+
isValid: true,
|
|
286
|
+
listitems: [args.listitems[2]],
|
|
287
|
+
}).html
|
|
288
|
+
}
|
|
289
|
+
</div>
|
|
290
|
+
<div>
|
|
291
|
+
${
|
|
292
|
+
new Formcheck({
|
|
293
|
+
...args,
|
|
294
|
+
questionLabel: "Bootstrap classes .is-invalid",
|
|
295
|
+
isValid: false,
|
|
296
|
+
listitems: [args.listitems[3]],
|
|
297
|
+
}).html
|
|
298
|
+
}
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
`;
|
|
303
|
+
},
|
|
304
|
+
};
|