Skip to content

Commit 73a8826

Browse files
authored
feat(input): add color prop support (#26237)
1 parent 203c55e commit 73a8826

File tree

50 files changed

+346
-2
lines changed

Some content is hidden

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

50 files changed

+346
-2
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,17 @@
3636
background-size: $input-md-input-clear-icon-size;
3737
}
3838

39-
// Input Max Length Counter
39+
// Input Bottom
4040
// ----------------------------------------------------------------
41+
/**
42+
* If the input has a validity state, the
43+
* border and label should reflect that as a color.
44+
*/
45+
:host(.ion-touched.ion-valid) .input-bottom,
46+
:host(.ion-touched.ion-invalid) .input-bottom {
47+
--border-color: var(--highlight-color);
48+
}
49+
4150
.input-bottom .counter {
4251
letter-spacing: .0333333333em;
4352
}
@@ -60,6 +69,11 @@
6069
color: var(--highlight-color);
6170
}
6271

72+
:host(.ion-touched.ion-valid) label,
73+
:host(.ion-touched.ion-invalid) label {
74+
color: var(--highlight-color);
75+
}
76+
6377
// Input Highlight
6478
// ----------------------------------------------------------------
6579

core/src/components/input/input.scss

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,24 @@
352352
padding-inline-start: 16px;
353353
}
354354

355+
// Input Highlight
356+
// ----------------------------------------------------------------
357+
358+
:host(.ion-touched.ion-invalid) {
359+
--highlight-color: var(--highlight-color-invalid);
360+
}
361+
362+
:host(.ion-touched.ion-valid) {
363+
--highlight-color: var(--highlight-color-valid);
364+
}
365+
366+
// Input Native
367+
// ----------------------------------------------------------------
368+
369+
:host(.has-focus) input {
370+
caret-color: var(--highlight-color);
371+
}
372+
355373
// Input Label
356374
// ----------------------------------------------------------------
357375

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<!DOCTYPE html>
2+
<html lang="en" dir="ltr">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Input - Color</title>
6+
<meta
7+
name="viewport"
8+
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
9+
/>
10+
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
11+
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
12+
<script src="../../../../../scripts/testing/scripts.js"></script>
13+
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
14+
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
15+
<style>
16+
.grid {
17+
display: grid;
18+
grid-template-columns: repeat(5, minmax(250px, 1fr));
19+
grid-row-gap: 20px;
20+
grid-column-gap: 20px;
21+
}
22+
h2 {
23+
font-size: 12px;
24+
font-weight: normal;
25+
26+
color: #6f7378;
27+
28+
margin-top: 10px;
29+
}
30+
@media screen and (max-width: 800px) {
31+
.grid {
32+
grid-template-columns: 1fr;
33+
padding: 0;
34+
}
35+
}
36+
</style>
37+
</head>
38+
39+
<body>
40+
<ion-app>
41+
<ion-header>
42+
<ion-toolbar>
43+
<ion-title>Input - Color</ion-title>
44+
</ion-toolbar>
45+
</ion-header>
46+
47+
<ion-content id="content" class="ion-padding">
48+
<h1>No Fill</h1>
49+
<div class="grid">
50+
<div class="grid-item">
51+
<h2>Start</h2>
52+
<ion-input value="[email protected]" label="Email" color="danger"></ion-input>
53+
</div>
54+
55+
<div class="grid-item">
56+
<h2>End</h2>
57+
<ion-input value="[email protected]" label="Email" label-placement="end" color="danger"></ion-input>
58+
</div>
59+
60+
<div class="grid-item">
61+
<h2>Fixed</h2>
62+
<ion-input value="[email protected]" label="Email" label-placement="fixed" color="danger"></ion-input>
63+
</div>
64+
65+
<div class="grid-item">
66+
<h2>Floating</h2>
67+
<ion-input label="Email" label-placement="floating" color="danger"></ion-input>
68+
</div>
69+
70+
<div class="grid-item">
71+
<h2>Stacked</h2>
72+
<ion-input value="[email protected]" label="Email" label-placement="stacked" color="danger"></ion-input>
73+
</div>
74+
</div>
75+
76+
<h1>Solid</h1>
77+
<div class="grid">
78+
<div class="grid-item">
79+
<h2>Start</h2>
80+
<ion-input fill="solid" value="[email protected]" label="Email" color="danger"></ion-input>
81+
</div>
82+
83+
<div class="grid-item">
84+
<h2>End</h2>
85+
<ion-input fill="solid" value="[email protected]" label="Email" label-placement="end" color="danger"></ion-input>
86+
</div>
87+
88+
<div class="grid-item">
89+
<h2>Fixed</h2>
90+
<ion-input
91+
fill="solid"
92+
93+
label="Email"
94+
label-placement="fixed"
95+
color="danger"
96+
></ion-input>
97+
</div>
98+
99+
<div class="grid-item">
100+
<h2>Floating</h2>
101+
<ion-input fill="solid" label="Email" label-placement="floating" color="danger"></ion-input>
102+
</div>
103+
104+
<div class="grid-item">
105+
<h2>Stacked</h2>
106+
<ion-input
107+
fill="solid"
108+
109+
label="Email"
110+
label-placement="stacked"
111+
color="danger"
112+
></ion-input>
113+
</div>
114+
</div>
115+
116+
<h1>Outline</h1>
117+
<div class="grid">
118+
<div class="grid-item">
119+
<h2>Start</h2>
120+
<ion-input fill="outline" value="[email protected]" label="Email" color="danger"></ion-input>
121+
</div>
122+
123+
<div class="grid-item">
124+
<h2>End</h2>
125+
<ion-input
126+
fill="outline"
127+
128+
label="Email"
129+
label-placement="end"
130+
color="danger"
131+
></ion-input>
132+
</div>
133+
134+
<div class="grid-item">
135+
<h2>Fixed</h2>
136+
<ion-input
137+
fill="outline"
138+
139+
label="Email"
140+
label-placement="fixed"
141+
color="danger"
142+
></ion-input>
143+
</div>
144+
145+
<div class="grid-item">
146+
<h2>Floating</h2>
147+
<ion-input fill="outline" label="Email" label-placement="floating" color="danger"></ion-input>
148+
</div>
149+
150+
<div class="grid-item">
151+
<h2>Stacked</h2>
152+
<ion-input
153+
fill="outline"
154+
155+
label="Email"
156+
label-placement="stacked"
157+
color="danger"
158+
></ion-input>
159+
</div>
160+
</div>
161+
</ion-content>
162+
</ion-app>
163+
</body>
164+
</html>
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { expect } from '@playwright/test';
2+
import { test } from '@utils/test/playwright';
3+
4+
test.describe('input: color', () => {
5+
test.beforeEach(({ skip }) => {
6+
skip.rtl();
7+
skip.mode(
8+
'ios',
9+
'iOS does not have any color theming besides the caret which cannot be captured in a stable manner in screenshots.'
10+
);
11+
});
12+
13+
/**
14+
* Manually setting the .has-focus class
15+
* lets us quickly test the rendering of a
16+
* focused input without need to wait for
17+
* focus events.
18+
*/
19+
test.describe('input: fill none', () => {
20+
test('should set label and highlight color on focus with start label placement', async ({ page }) => {
21+
await page.setContent(`
22+
<ion-input class="has-focus" color="danger" label="Email" label-placement="start" value="[email protected]"></ion-input>
23+
`);
24+
25+
const input = page.locator('ion-input');
26+
expect(await input.screenshot()).toMatchSnapshot(`input-no-fill-color-start-${page.getSnapshotSettings()}.png`);
27+
});
28+
test('should set label and highlight color on focus with end label placement', async ({ page }) => {
29+
await page.setContent(`
30+
<ion-input class="has-focus" color="danger" label="Email" label-placement="end" value="[email protected]"></ion-input>
31+
`);
32+
33+
const input = page.locator('ion-input');
34+
expect(await input.screenshot()).toMatchSnapshot(`input-no-fill-color-end-${page.getSnapshotSettings()}.png`);
35+
});
36+
test('should set label and highlight color on focus with fixed label placement', async ({ page }) => {
37+
await page.setContent(`
38+
<ion-input class="has-focus" color="danger" label="Email" label-placement="fixed" value="[email protected]"></ion-input>
39+
`);
40+
41+
const input = page.locator('ion-input');
42+
expect(await input.screenshot()).toMatchSnapshot(`input-no-fill-color-fixed-${page.getSnapshotSettings()}.png`);
43+
});
44+
test('should set label and highlight color on focus with floating label placement', async ({ page }) => {
45+
await page.setContent(`
46+
<ion-input class="has-focus" color="danger" label="Email" label-placement="floating" value="[email protected]"></ion-input>
47+
`);
48+
49+
const input = page.locator('ion-input');
50+
expect(await input.screenshot()).toMatchSnapshot(
51+
`input-no-fill-color-floating-${page.getSnapshotSettings()}.png`
52+
);
53+
});
54+
test('should set label and highlight color on focus with stacked label placement', async ({ page }) => {
55+
await page.setContent(`
56+
<ion-input class="has-focus" color="danger" label="Email" label-placement="stacked" value="[email protected]"></ion-input>
57+
`);
58+
59+
const input = page.locator('ion-input');
60+
expect(await input.screenshot()).toMatchSnapshot(`input-no-fill-color-stacked-${page.getSnapshotSettings()}.png`);
61+
});
62+
});
63+
test.describe('input: fill solid', () => {
64+
test('should set label and highlight color on focus with start label placement', async ({ page }) => {
65+
await page.setContent(`
66+
<ion-input fill="solid" class="has-focus" color="danger" label="Email" label-placement="start" value="[email protected]"></ion-input>
67+
`);
68+
69+
const input = page.locator('ion-input');
70+
expect(await input.screenshot()).toMatchSnapshot(`input-solid-color-start-${page.getSnapshotSettings()}.png`);
71+
});
72+
test('should set label and highlight color on focus with end label placement', async ({ page }) => {
73+
await page.setContent(`
74+
<ion-input fill="solid" class="has-focus" color="danger" label="Email" label-placement="end" value="[email protected]"></ion-input>
75+
`);
76+
77+
const input = page.locator('ion-input');
78+
expect(await input.screenshot()).toMatchSnapshot(`input-solid-color-end-${page.getSnapshotSettings()}.png`);
79+
});
80+
test('should set label and highlight color on focus with fixed label placement', async ({ page }) => {
81+
await page.setContent(`
82+
<ion-input fill="solid" class="has-focus" color="danger" label="Email" label-placement="fixed" value="[email protected]"></ion-input>
83+
`);
84+
85+
const input = page.locator('ion-input');
86+
expect(await input.screenshot()).toMatchSnapshot(`input-solid-color-fixed-${page.getSnapshotSettings()}.png`);
87+
});
88+
test('should set label and highlight color on focus with floating label placement', async ({ page }) => {
89+
await page.setContent(`
90+
<ion-input fill="solid" class="has-focus" color="danger" label="Email" label-placement="floating" value="[email protected]"></ion-input>
91+
`);
92+
93+
const input = page.locator('ion-input');
94+
expect(await input.screenshot()).toMatchSnapshot(`input-solid-color-floating-${page.getSnapshotSettings()}.png`);
95+
});
96+
test('should set label and highlight color on focus with stacked label placement', async ({ page }) => {
97+
await page.setContent(`
98+
<ion-input fill="solid" class="has-focus" color="danger" label="Email" label-placement="stacked" value="[email protected]"></ion-input>
99+
`);
100+
101+
const input = page.locator('ion-input');
102+
expect(await input.screenshot()).toMatchSnapshot(`input-solid-color-stacked-${page.getSnapshotSettings()}.png`);
103+
});
104+
});
105+
test.describe('input: fill outline', () => {
106+
test('should set label and highlight color on focus with start label placement', async ({ page }) => {
107+
await page.setContent(`
108+
<ion-input fill="outline" class="has-focus" color="danger" label="Email" label-placement="start" value="[email protected]"></ion-input>
109+
`);
110+
111+
const input = page.locator('ion-input');
112+
expect(await input.screenshot()).toMatchSnapshot(`input-outline-color-start-${page.getSnapshotSettings()}.png`);
113+
});
114+
test('should set label and highlight color on focus with end label placement', async ({ page }) => {
115+
await page.setContent(`
116+
<ion-input fill="outline" class="has-focus" color="danger" label="Email" label-placement="end" value="[email protected]"></ion-input>
117+
`);
118+
119+
const input = page.locator('ion-input');
120+
expect(await input.screenshot()).toMatchSnapshot(`input-outline-color-end-${page.getSnapshotSettings()}.png`);
121+
});
122+
test('should set label and highlight color on focus with fixed label placement', async ({ page }) => {
123+
await page.setContent(`
124+
<ion-input fill="outline" class="has-focus" color="danger" label="Email" label-placement="fixed" value="[email protected]"></ion-input>
125+
`);
126+
127+
const input = page.locator('ion-input');
128+
expect(await input.screenshot()).toMatchSnapshot(`input-outline-color-fixed-${page.getSnapshotSettings()}.png`);
129+
});
130+
test('should set label and highlight color on focus with floating label placement', async ({ page }) => {
131+
await page.setContent(`
132+
<ion-input fill="outline" class="has-focus" color="danger" label="Email" label-placement="floating" value="[email protected]"></ion-input>
133+
`);
134+
135+
const input = page.locator('ion-input');
136+
expect(await input.screenshot()).toMatchSnapshot(
137+
`input-outline-color-floating-${page.getSnapshotSettings()}.png`
138+
);
139+
});
140+
test('should set label and highlight color on focus with stacked label placement', async ({ page }) => {
141+
await page.setContent(`
142+
<ion-input fill="outline" class="has-focus" color="danger" label="Email" label-placement="stacked" value="[email protected]"></ion-input>
143+
`);
144+
145+
const input = page.locator('ion-input');
146+
expect(await input.screenshot()).toMatchSnapshot(`input-outline-color-stacked-${page.getSnapshotSettings()}.png`);
147+
});
148+
});
149+
});
7.52 KB
2.88 KB
7.3 KB
7.51 KB
2.9 KB
7.31 KB

0 commit comments

Comments
 (0)