Skip to content

Commit ef33270

Browse files
authored
fix(input, textarea): show error state after touch (#26940)
resolves #26939, resolves #21643
1 parent 4810e6f commit ef33270

File tree

104 files changed

+258
-129
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+258
-129
lines changed

core/src/components/input/input.md.outline.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
* If the input has a validity state, the
2121
* border should reflect that as a color.
2222
*/
23-
:host(.input-fill-outline.ion-touched.ion-valid),
23+
:host(.has-focus.input-fill-outline.ion-valid),
2424
:host(.input-fill-outline.ion-touched.ion-invalid) {
2525
--border-color: var(--highlight-color);
2626
}

core/src/components/input/input.md.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
* If the input has a validity state, the
4848
* border and label should reflect that as a color.
4949
*/
50-
:host(.ion-touched.ion-valid),
50+
:host(.has-focus.ion-valid),
5151
:host(.ion-touched.ion-invalid) {
5252
--border-color: var(--highlight-color);
5353
}
@@ -76,9 +76,9 @@
7676
color: var(--highlight-color);
7777
}
7878

79-
:host(.input-label-placement-floating.ion-touched.ion-valid) .label-text-wrapper,
79+
:host(.has-focus.input-label-placement-floating.ion-valid) .label-text-wrapper,
8080
:host(.input-label-placement-floating.ion-touched.ion-invalid) .label-text-wrapper,
81-
:host(.input-label-placement-stacked.ion-touched.ion-valid) .label-text-wrapper,
81+
:host(.has-focus.input-label-placement-stacked.ion-valid) .label-text-wrapper,
8282
:host(.input-label-placement-stacked.ion-touched.ion-invalid) .label-text-wrapper {
8383
color: var(--highlight-color);
8484
}

core/src/components/input/input.md.solid.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
* If the input has a validity state, the
2626
* border should reflect that as a color.
2727
*/
28-
:host(.input-fill-solid.ion-touched.ion-valid),
28+
:host(.has-focus.input-fill-solid.ion-valid),
2929
:host(.input-fill-solid.ion-touched.ion-invalid) {
3030
--border-color: var(--highlight-color);
3131
}

core/src/components/input/input.scss

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,17 @@
319319
--highlight-color: var(--highlight-color-invalid);
320320
}
321321

322-
:host(.ion-touched.ion-valid) {
322+
/**
323+
* The component highlight is only shown
324+
* on focus, so we can safely set the valid
325+
* color state when valid. If we
326+
* set it when .has-focus is present then
327+
* the highlight color would change
328+
* from the valid color to the component's
329+
* color during the transition after the
330+
* component loses focus.
331+
*/
332+
:host(.ion-valid) {
323333
--highlight-color: var(--highlight-color-valid);
324334
}
325335

@@ -346,8 +356,14 @@
346356
/**
347357
* If the input has a validity state, the
348358
* border and label should reflect that as a color.
359+
* The invalid state should show if the input is
360+
* invalid and has already been touched.
361+
* The valid state should show if the input
362+
* is valid, has already been touched, and
363+
* is currently focused. Do not show the valid
364+
* highlight when the input is blurred.
349365
*/
350-
:host(.ion-touched.ion-valid),
366+
:host(.has-focus.ion-valid),
351367
:host(.ion-touched.ion-invalid) {
352368
--border-color: var(--highlight-color);
353369
}
@@ -372,11 +388,11 @@
372388
color: #{$background-color-step-550};
373389
}
374390

375-
:host(.ion-invalid) .input-bottom .error-text {
391+
:host(.ion-touched.ion-invalid) .input-bottom .error-text {
376392
display: block;
377393
}
378394

379-
:host(.ion-invalid) .input-bottom .helper-text {
395+
:host(.ion-touched.ion-invalid) .input-bottom .helper-text {
380396
display: none;
381397
}
382398

@@ -398,17 +414,6 @@
398414
padding-inline-start: 16px;
399415
}
400416

401-
// Input Highlight
402-
// ----------------------------------------------------------------
403-
404-
:host(.ion-touched.ion-invalid) {
405-
--highlight-color: var(--highlight-color-invalid);
406-
}
407-
408-
:host(.ion-touched.ion-valid) {
409-
--highlight-color: var(--highlight-color-valid);
410-
}
411-
412417
// Input Native
413418
// ----------------------------------------------------------------
414419

core/src/components/input/test/bottom-content/index.html

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,17 @@ <h2>Helper Hint</h2>
6262

6363
<div class="grid-item">
6464
<h2>Error Hint</h2>
65-
<ion-input class="ion-invalid" label="Email" error-text="Please enter a valid email"></ion-input>
65+
<ion-input
66+
class="ion-touched ion-invalid"
67+
label="Email"
68+
error-text="Please enter a valid email"
69+
></ion-input>
6670
</div>
6771

6872
<div class="grid-item">
6973
<h2>Custom Error Color</h2>
7074
<ion-input
71-
class="ion-invalid custom-error-color"
75+
class="ion-touched ion-invalid custom-error-color"
7276
label="Email"
7377
error-text="Please enter a valid email"
7478
></ion-input>
@@ -92,7 +96,7 @@ <h2>Counter with Helper</h2>
9296
<div class="grid-item">
9397
<h2>Counter with Error</h2>
9498
<ion-input
95-
class="ion-invalid"
99+
class="ion-touched ion-invalid"
96100
label="Email"
97101
counter="true"
98102
maxlength="100"

core/src/components/input/test/bottom-content/input.e2e.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ test.describe('input: hint text', () => {
5252
});
5353
test('error text should be visible when input is invalid', async ({ page }) => {
5454
await page.setContent(
55-
`<ion-input class="ion-invalid" helper-text="my helper" error-text="my error" label="my input"></ion-input>`
55+
`<ion-input class="ion-invalid ion-touched" helper-text="my helper" error-text="my error" label="my input"></ion-input>`
5656
);
5757

5858
const helperText = page.locator('ion-input .helper-text');
@@ -68,7 +68,7 @@ test.describe('input: hint text', () => {
6868
--highlight-color-invalid: purple;
6969
}
7070
</style>
71-
<ion-input class="ion-invalid custom-input" label="my label" error-text="my error"></ion-input>
71+
<ion-input class="ion-invalid ion-touched custom-input" label="my label" error-text="my error"></ion-input>
7272
`);
7373

7474
const errorText = page.locator('ion-input .error-text');
@@ -88,7 +88,9 @@ test.describe('input: hint text', () => {
8888
);
8989
});
9090
test('should not have visual regressions when rendering error text', async ({ page }) => {
91-
await page.setContent(`<ion-input class="ion-invalid" error-text="my helper" label="my input"></ion-input>`);
91+
await page.setContent(
92+
`<ion-input class="ion-invalid ion-touched" error-text="my helper" label="my input"></ion-input>`
93+
);
9294

9395
const bottomEl = page.locator('ion-input .input-bottom');
9496
expect(await bottomEl.screenshot()).toMatchSnapshot(

core/src/components/input/test/highlight/index.html

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ <h2>Focus</h2>
6262
</div>
6363

6464
<div class="grid-item">
65-
<h2>Valid</h2>
65+
<h2>Valid, Focus</h2>
6666
<ion-input
6767
label-placement="start"
68-
class="ion-touched ion-valid"
68+
class="ion-valid has-focus"
6969
label="Email"
7070
error-text="Please enter a valid email"
7171
helper-text="Enter an email"
@@ -105,10 +105,10 @@ <h2>Focus</h2>
105105
</div>
106106

107107
<div class="grid-item">
108-
<h2>Valid</h2>
108+
<h2>Valid, Focus</h2>
109109
<ion-input
110110
label-placement="floating"
111-
class="ion-touched ion-valid"
111+
class="ion-valid has-focus"
112112
label="Email"
113113
error-text="Please enter a valid email"
114114
helper-text="Enter an email"
@@ -148,10 +148,10 @@ <h2>Focus</h2>
148148
</div>
149149

150150
<div class="grid-item">
151-
<h2>Valid</h2>
151+
<h2>Valid, Focus</h2>
152152
<ion-input
153153
label-placement="stacked"
154-
class="ion-touched ion-valid"
154+
class="ion-valid has-focus"
155155
label="Email"
156156
error-text="Please enter a valid email"
157157
helper-text="Enter an email"
@@ -192,11 +192,11 @@ <h2>Focus</h2>
192192
</div>
193193

194194
<div class="grid-item">
195-
<h2>Valid</h2>
195+
<h2>Valid, Focus</h2>
196196
<ion-input
197197
fill="solid"
198198
label-placement="start"
199-
class="ion-touched ion-valid"
199+
class="ion-valid has-focus"
200200
label="Email"
201201
error-text="Please enter a valid email"
202202
helper-text="Enter an email"
@@ -238,11 +238,11 @@ <h2>Focus</h2>
238238
</div>
239239

240240
<div class="grid-item">
241-
<h2>Valid</h2>
241+
<h2>Valid, Focus</h2>
242242
<ion-input
243243
fill="solid"
244244
label-placement="floating"
245-
class="ion-touched ion-valid"
245+
class="ion-valid has-focus"
246246
label="Email"
247247
error-text="Please enter a valid email"
248248
helper-text="Enter an email"
@@ -284,11 +284,11 @@ <h2>Focus</h2>
284284
</div>
285285

286286
<div class="grid-item">
287-
<h2>Valid</h2>
287+
<h2>Valid, Focus</h2>
288288
<ion-input
289289
fill="solid"
290290
label-placement="stacked"
291-
class="ion-touched ion-valid"
291+
class="ion-valid has-focus"
292292
label="Email"
293293
error-text="Please enter a valid email"
294294
helper-text="Enter an email"
@@ -330,11 +330,11 @@ <h2>Focus</h2>
330330
</div>
331331

332332
<div class="grid-item">
333-
<h2>Valid</h2>
333+
<h2>Valid, Focus</h2>
334334
<ion-input
335335
fill="outline"
336336
label-placement="start"
337-
class="ion-touched ion-valid"
337+
class="ion-valid has-focus"
338338
label="Email"
339339
error-text="Please enter a valid email"
340340
helper-text="Enter an email"
@@ -376,11 +376,11 @@ <h2>Focus</h2>
376376
</div>
377377

378378
<div class="grid-item">
379-
<h2>Valid</h2>
379+
<h2>Valid, Focus</h2>
380380
<ion-input
381381
fill="outline"
382382
label-placement="floating"
383-
class="ion-touched ion-valid"
383+
class="ion-valid has-focus"
384384
label="Email"
385385
error-text="Please enter a valid email"
386386
helper-text="Enter an email"
@@ -422,11 +422,11 @@ <h2>Focus</h2>
422422
</div>
423423

424424
<div class="grid-item">
425-
<h2>Valid</h2>
425+
<h2>Valid, Focus</h2>
426426
<ion-input
427427
fill="outline"
428428
label-placement="stacked"
429-
class="ion-touched ion-valid"
429+
class="ion-valid has-focus"
430430
label="Email"
431431
error-text="Please enter a valid email"
432432
helper-text="Enter an email"

core/src/components/input/test/highlight/input.e2e.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ test.describe('input: highlights', () => {
1010
await page.setContent(`
1111
<ion-input
1212
13-
class="ion-touched ion-valid"
13+
class="ion-valid has-focus"
1414
label="Email"
1515
error-text="Please enter a valid email"
1616
helper-text="Enter an email"
@@ -61,7 +61,7 @@ test.describe('input: highlights', () => {
6161
<ion-input
6262
fill="solid"
6363
64-
class="ion-touched ion-valid"
64+
class="ion-valid has-focus"
6565
label="Email"
6666
error-text="Please enter a valid email"
6767
helper-text="Enter an email"
@@ -114,7 +114,7 @@ test.describe('input: highlights', () => {
114114
<ion-input
115115
fill="outline"
116116
117-
class="ion-touched ion-valid"
117+
class="ion-valid has-focus"
118118
label="Email"
119119
error-text="Please enter a valid email"
120120
helper-text="Enter an email"

core/src/components/select/select.md.outline.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
* If the select has a validity state, the
2121
* border should reflect that as a color.
2222
*/
23-
:host(.select-fill-outline.ion-touched.ion-valid),
23+
:host(.has-focus.select-fill-outline.ion-valid),
2424
:host(.select-fill-outline.ion-touched.ion-invalid) {
2525
--border-color: var(--highlight-color);
2626
}

core/src/components/select/select.md.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@
4545
color: var(--highlight-color);
4646
}
4747

48-
:host(.select-label-placement-floating.ion-touched.ion-valid) .label-text-wrapper,
48+
:host(.has-focus.select-label-placement-floating.ion-valid) .label-text-wrapper,
4949
:host(.select-label-placement-floating.ion-touched.ion-invalid) .label-text-wrapper,
50-
:host(.select-label-placement-stacked.ion-touched.ion-valid) .label-text-wrapper,
50+
:host(.has-focus.select-label-placement-stacked.ion-valid) .label-text-wrapper,
5151
:host(.select-label-placement-stacked.ion-touched.ion-invalid) .label-text-wrapper {
5252
color: var(--highlight-color);
5353
}
@@ -103,7 +103,7 @@
103103
* color if there is a validation state.
104104
*/
105105
:host(.select-expanded) .select-wrapper .select-icon,
106-
:host(.ion-touched.ion-valid) .select-wrapper .select-icon,
106+
:host(.has-focus.ion-valid) .select-wrapper .select-icon,
107107
:host(.ion-touched.ion-invalid) .select-wrapper .select-icon,
108108
:host(.ion-focused) .select-wrapper .select-icon {
109109
color: var(--highlight-color);

core/src/components/select/select.md.solid.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
* If the select has a validity state, the
2626
* border should reflect that as a color.
2727
*/
28-
:host(.select-fill-solid.ion-touched.ion-valid),
28+
:host(.has-focus.select-fill-solid.ion-valid),
2929
:host(.select-fill-solid.ion-touched.ion-invalid) {
3030
--border-color: var(--highlight-color);
3131
}

core/src/components/select/select.scss

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,17 @@ button {
226226
--highlight-color: var(--highlight-color-invalid);
227227
}
228228

229-
:host(.ion-touched.ion-valid) {
229+
/**
230+
* The component highlight is only shown
231+
* on focus, so we can safely set the valid
232+
* color state when touched/valid. If we
233+
* set it when .has-focus is present then
234+
* the highlight color would change
235+
* from the valid color to the component's
236+
* color during the transition after the
237+
* component loses focus.
238+
*/
239+
:host(.ion-valid) {
230240
--highlight-color: var(--highlight-color-valid);
231241
}
232242

0 commit comments

Comments
 (0)