Skip to content

Commit 391d2f6

Browse files
committed
test(textarea): add tests for form validation and customizing with parts
1 parent cd58d35 commit 391d2f6

File tree

3 files changed

+418
-0
lines changed

3 files changed

+418
-0
lines changed
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import { expect } from '@playwright/test';
2+
import { configs, test } from '@utils/test/playwright';
3+
4+
/**
5+
* This behavior does not vary across modes/directions
6+
*/
7+
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
8+
test.describe(title('textarea: custom'), () => {
9+
test('should allow styling the container part', async ({ page }) => {
10+
await page.setContent(
11+
`
12+
<style>
13+
ion-textarea::part(container) {
14+
background-color: blue;
15+
}
16+
</style>
17+
18+
<ion-textarea label="textarea"></ion-textarea>
19+
`,
20+
config
21+
);
22+
23+
const textarea = await page.locator('ion-textarea');
24+
const container = await textarea.evaluate((el: HTMLIonTextareaElement) => {
25+
const containerEl = el.shadowRoot?.querySelector('[part="container"]') as HTMLElement | null;
26+
if (!containerEl) {
27+
return '';
28+
}
29+
return getComputedStyle(containerEl).backgroundColor;
30+
});
31+
32+
expect(container).toBe('rgb(0, 0, 255)');
33+
});
34+
35+
test('should allow styling the label part', async ({ page }) => {
36+
await page.setContent(
37+
`
38+
<style>
39+
ion-textarea::part(label) {
40+
color: green;
41+
}
42+
</style>
43+
44+
<ion-textarea label="Test Label"></ion-textarea>
45+
`,
46+
config
47+
);
48+
49+
const textarea = await page.locator('ion-textarea');
50+
const labelColor = await textarea.evaluate((el: HTMLIonTextareaElement) => {
51+
const labelEl = el.shadowRoot?.querySelector('[part="label"]') as HTMLElement | null;
52+
if (!labelEl) {
53+
return '';
54+
}
55+
return getComputedStyle(labelEl).color;
56+
});
57+
58+
expect(labelColor).toBe('rgb(0, 128, 0)');
59+
});
60+
61+
test('should allow styling the native textarea', async ({ page }) => {
62+
await page.setContent(
63+
`
64+
<style>
65+
ion-textarea::part(native) {
66+
color: red;
67+
}
68+
</style>
69+
70+
<ion-textarea label="textarea"></ion-textarea>
71+
`,
72+
config
73+
);
74+
75+
const textarea = await page.locator('ion-textarea');
76+
const color = await textarea.evaluate(
77+
(el: HTMLIonTextareaElement) =>
78+
getComputedStyle(el.shadowRoot?.querySelector('textarea') as HTMLTextAreaElement).color
79+
);
80+
81+
expect(color).toBe('rgb(255, 0, 0)');
82+
});
83+
84+
test('should allow styling the supporting-text part', async ({ page }) => {
85+
await page.setContent(
86+
`
87+
<style>
88+
ion-textarea::part(supporting-text) {
89+
color: blue;
90+
}
91+
</style>
92+
93+
<ion-textarea label="textarea" helper-text="Helper text"></ion-textarea>
94+
`,
95+
config
96+
);
97+
98+
const textarea = await page.locator('ion-textarea');
99+
await textarea.waitFor();
100+
101+
const supportingTextColor = await textarea.evaluate((el: HTMLIonTextareaElement) => {
102+
// Query for the visible helper-text element which has the supporting-text part
103+
// Use attribute selector that matches space-separated part values
104+
const helperTextEl = el.shadowRoot?.querySelector('[part~="helper-text"]') as HTMLElement | null;
105+
if (!helperTextEl) {
106+
return '';
107+
}
108+
return getComputedStyle(helperTextEl).color;
109+
});
110+
111+
expect(supportingTextColor).toBe('rgb(0, 0, 255)');
112+
});
113+
114+
test('should allow styling the helper-text part', async ({ page }) => {
115+
await page.setContent(
116+
`
117+
<style>
118+
ion-textarea::part(helper-text) {
119+
color: red;
120+
}
121+
</style>
122+
123+
<ion-textarea label="textarea" helper-text="Helper text"></ion-textarea>
124+
`,
125+
config
126+
);
127+
128+
const textarea = await page.locator('ion-textarea');
129+
await textarea.waitFor();
130+
131+
const helperTextColor = await textarea.evaluate((el: HTMLIonTextareaElement) => {
132+
const helperTextEl = el.shadowRoot?.querySelector('[part~="helper-text"]') as HTMLElement | null;
133+
if (!helperTextEl) {
134+
return '';
135+
}
136+
return getComputedStyle(helperTextEl).color;
137+
});
138+
139+
expect(helperTextColor).toBe('rgb(255, 0, 0)');
140+
});
141+
142+
test('should allow styling the error-text part', async ({ page }) => {
143+
await page.setContent(
144+
`
145+
<style>
146+
ion-textarea::part(error-text) {
147+
color: red;
148+
}
149+
</style>
150+
151+
<ion-textarea label="textarea" class="ion-invalid ion-touched" error-text="Error text"></ion-textarea>
152+
`,
153+
config
154+
);
155+
156+
const textarea = await page.locator('ion-textarea');
157+
await textarea.waitFor();
158+
159+
const errorTextColor = await textarea.evaluate((el: HTMLIonTextareaElement) => {
160+
const errorTextEl = el.shadowRoot?.querySelector('[part~="error-text"]') as HTMLElement | null;
161+
if (!errorTextEl) {
162+
return '';
163+
}
164+
return getComputedStyle(errorTextEl).color;
165+
});
166+
167+
expect(errorTextColor).toBe('rgb(255, 0, 0)');
168+
});
169+
170+
test('should allow styling the counter part', async ({ page }) => {
171+
await page.setContent(
172+
`
173+
<style>
174+
ion-textarea::part(counter) {
175+
color: green;
176+
}
177+
</style>
178+
179+
<ion-textarea label="textarea" counter="true" maxlength="100"></ion-textarea>
180+
`,
181+
config
182+
);
183+
184+
const textarea = await page.locator('ion-textarea');
185+
const counterColor = await textarea.evaluate((el: HTMLIonTextareaElement) => {
186+
const counterEl = el.shadowRoot?.querySelector('[part="counter"]') as HTMLElement | null;
187+
if (!counterEl) {
188+
return '';
189+
}
190+
return getComputedStyle(counterEl).color;
191+
});
192+
193+
expect(counterColor).toBe('rgb(0, 128, 0)');
194+
});
195+
196+
test('should allow styling the bottom part', async ({ page }) => {
197+
await page.setContent(
198+
`
199+
<style>
200+
ion-textarea::part(bottom) {
201+
background-color: blue;
202+
}
203+
</style>
204+
205+
<ion-textarea label="textarea" helper-text="Helper text"></ion-textarea>
206+
`,
207+
config
208+
);
209+
210+
const textarea = await page.locator('ion-textarea');
211+
const bottomBgColor = await textarea.evaluate((el: HTMLIonTextareaElement) => {
212+
const bottomEl = el.shadowRoot?.querySelector('[part="bottom"]') as HTMLElement | null;
213+
if (!bottomEl) {
214+
return '';
215+
}
216+
return getComputedStyle(bottomEl).backgroundColor;
217+
});
218+
219+
expect(bottomBgColor).toBe('rgb(0, 0, 255)');
220+
});
221+
});
222+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<!DOCTYPE html>
2+
<html lang="en" dir="ltr">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Textarea - Form</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+
</head>
16+
17+
<body>
18+
<ion-app>
19+
<ion-header>
20+
<ion-toolbar>
21+
<ion-title>Textarea - Form</ion-title>
22+
</ion-toolbar>
23+
</ion-header>
24+
25+
<ion-content id="content" class="ion-padding">
26+
<form onsubmit="return onSubmit(event)">
27+
<ion-textarea
28+
fill="outline"
29+
label="Label"
30+
label-placement="stacked"
31+
placeholder="Placeholder"
32+
helper-text="Helper message"
33+
counter="true"
34+
maxlength="999"
35+
required
36+
>
37+
<ion-icon slot="end" name="square-outline"></ion-icon>
38+
</ion-textarea>
39+
40+
<p>
41+
Clicking the button below should show a popup saying textarea is required in Chrome, Safari, and Firefox
42+
when the textarea is empty.
43+
</p>
44+
<button>Submit</button>
45+
</form>
46+
</ion-content>
47+
</ion-app>
48+
49+
<script>
50+
function onSubmit(event) {
51+
event.preventDefault();
52+
console.log('Form submitted');
53+
return true;
54+
}
55+
</script>
56+
</body>
57+
</html>

0 commit comments

Comments
 (0)