Skip to content

Commit 9ebada9

Browse files
committed
App validation tests
1 parent f1bb778 commit 9ebada9

File tree

8 files changed

+878
-0
lines changed

8 files changed

+878
-0
lines changed

packages/angular/test/base/src/app/standalone/app-standalone/app.routes.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ export const routes: Routes = [
4040
]
4141
},
4242
{ path: 'tabs-basic', loadComponent: () => import('../tabs-basic/tabs-basic.component').then(c => c.TabsBasicComponent) },
43+
{
44+
path: 'validation',
45+
children: [
46+
{ path: 'input-validation', loadComponent: () => import('../validation/input-validation/input-validation.component').then(c => c.InputValidationComponent) },
47+
{ path: 'textarea-validation', loadComponent: () => import('../validation/textarea-validation/textarea-validation.component').then(c => c.TextareaValidationComponent) },
48+
{ path: '**', redirectTo: 'input-validation' }
49+
]
50+
},
4351
{
4452
path: 'value-accessors',
4553
children: [

packages/angular/test/base/src/app/standalone/home-page/home-page.component.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,22 @@
107107
</ion-item>
108108
</ion-list>
109109

110+
<ion-list>
111+
<ion-list-header>
112+
<ion-label>Validation Tests</ion-label>
113+
</ion-list-header>
114+
<ion-item routerLink="/standalone/validation/input-validation">
115+
<ion-label>
116+
Input Validation Test
117+
</ion-label>
118+
</ion-item>
119+
<ion-item routerLink="/standalone/validation/textarea-validation">
120+
<ion-label>
121+
Textarea Validation Test
122+
</ion-label>
123+
</ion-item>
124+
</ion-list>
125+
110126
<ion-list>
111127
<ion-list-header>
112128
<ion-label>Value Accessors</ion-label>
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<ion-header>
2+
<ion-toolbar>
3+
<ion-title>Input - Validation Test</ion-title>
4+
</ion-toolbar>
5+
</ion-header>
6+
7+
<ion-content class="ion-padding">
8+
<div class="validation-info">
9+
<h2>Screen Reader Testing Instructions:</h2>
10+
<ol>
11+
<li>Enable your screen reader (VoiceOver, NVDA, JAWS, etc.)</li>
12+
<li>Tab through the form fields</li>
13+
<li>When you tab away from an empty required field, the error should be announced immediately</li>
14+
<li>The error text should be announced BEFORE the next field is announced</li>
15+
<li>Test in Chrome, Safari, and Firefox to verify consistent behavior</li>
16+
</ol>
17+
</div>
18+
19+
<form [formGroup]="form">
20+
<div class="grid">
21+
<div>
22+
<h2>Required Email Field</h2>
23+
<ion-input
24+
#emailInput
25+
id="email-input"
26+
type="email"
27+
label="Email"
28+
labelPlacement="floating"
29+
fill="outline"
30+
placeholder="Enter your email"
31+
[helperText]="fieldMetadata.email.helperText"
32+
[errorText]="fieldMetadata.email.errorText"
33+
formControlName="email"
34+
required
35+
[class.ion-touched]="isTouched('email')"
36+
[class.ion-invalid]="isInvalid('email')"
37+
[class.ion-valid]="isValid('email')"
38+
(ionBlur)="onIonBlur('email', emailInput)"
39+
(ionInput)="onIonInput('email', emailInput)"
40+
(focusout)="onFocusOut('email', emailInput)"
41+
></ion-input>
42+
</div>
43+
44+
<div>
45+
<h2>Required Name Field</h2>
46+
<ion-input
47+
#nameInput
48+
id="name-input"
49+
type="text"
50+
label="Full Name"
51+
labelPlacement="floating"
52+
fill="outline"
53+
placeholder="Enter your full name"
54+
[helperText]="fieldMetadata.name.helperText"
55+
[errorText]="fieldMetadata.name.errorText"
56+
formControlName="name"
57+
required
58+
[class.ion-touched]="isTouched('name')"
59+
[class.ion-invalid]="isInvalid('name')"
60+
[class.ion-valid]="isValid('name')"
61+
(ionBlur)="onIonBlur('name', nameInput)"
62+
(ionInput)="onIonInput('name', nameInput)"
63+
(focusout)="onFocusOut('name', nameInput)"
64+
></ion-input>
65+
</div>
66+
67+
<div>
68+
<h2>Phone Number (Pattern Validation)</h2>
69+
<ion-input
70+
#phoneInput
71+
id="phone-input"
72+
type="tel"
73+
label="Phone"
74+
labelPlacement="floating"
75+
fill="outline"
76+
placeholder="(555) 555-5555"
77+
pattern="^\(\d{3}\) \d{3}-\d{4}$"
78+
[helperText]="fieldMetadata.phone.helperText"
79+
[errorText]="fieldMetadata.phone.errorText"
80+
formControlName="phone"
81+
required
82+
[class.ion-touched]="isTouched('phone')"
83+
[class.ion-invalid]="isInvalid('phone')"
84+
[class.ion-valid]="isValid('phone')"
85+
(ionBlur)="onIonBlur('phone', phoneInput)"
86+
(ionInput)="onIonInput('phone', phoneInput)"
87+
(focusout)="onFocusOut('phone', phoneInput)"
88+
></ion-input>
89+
</div>
90+
91+
<div>
92+
<h2>Password (Min Length)</h2>
93+
<ion-input
94+
#passwordInput
95+
id="password-input"
96+
type="password"
97+
label="Password"
98+
labelPlacement="floating"
99+
fill="outline"
100+
placeholder="Enter password"
101+
minlength="8"
102+
[helperText]="fieldMetadata.password.helperText"
103+
[errorText]="fieldMetadata.password.errorText"
104+
formControlName="password"
105+
required
106+
[class.ion-touched]="isTouched('password')"
107+
[class.ion-invalid]="isInvalid('password')"
108+
[class.ion-valid]="isValid('password')"
109+
(ionBlur)="onIonBlur('password', passwordInput)"
110+
(ionInput)="onIonInput('password', passwordInput)"
111+
(focusout)="onFocusOut('password', passwordInput)"
112+
></ion-input>
113+
</div>
114+
115+
<div>
116+
<h2>Age (Number Range)</h2>
117+
<ion-input
118+
#ageInput
119+
id="age-input"
120+
type="number"
121+
label="Age"
122+
labelPlacement="floating"
123+
fill="outline"
124+
placeholder="Enter your age"
125+
min="18"
126+
max="120"
127+
[helperText]="fieldMetadata.age.helperText"
128+
[errorText]="fieldMetadata.age.errorText"
129+
formControlName="age"
130+
required
131+
[class.ion-touched]="isTouched('age')"
132+
[class.ion-invalid]="isInvalid('age')"
133+
[class.ion-valid]="isValid('age')"
134+
(ionBlur)="onIonBlur('age', ageInput)"
135+
(ionInput)="onIonInput('age', ageInput)"
136+
(focusout)="onFocusOut('age', ageInput)"
137+
></ion-input>
138+
</div>
139+
140+
<div>
141+
<h2>Optional Field (No Validation)</h2>
142+
<ion-input
143+
#optionalInput
144+
id="optional-input"
145+
type="text"
146+
label="Optional Info"
147+
labelPlacement="floating"
148+
fill="outline"
149+
placeholder="This field is optional"
150+
[helperText]="fieldMetadata.optional.helperText"
151+
formControlName="optional"
152+
[class.ion-touched]="isTouched('optional')"
153+
[class.ion-invalid]="isInvalid('optional')"
154+
[class.ion-valid]="isValid('optional')"
155+
(ionBlur)="onIonBlur('optional', optionalInput)"
156+
(ionInput)="onIonInput('optional', optionalInput)"
157+
(focusout)="onFocusOut('optional', optionalInput)"
158+
></ion-input>
159+
</div>
160+
</div>
161+
</form>
162+
163+
<div class="ion-padding">
164+
<ion-button id="submit-btn" expand="block" [disabled]="!isFormValid()" (click)="onSubmit()">Submit Form</ion-button>
165+
<ion-button id="reset-btn" expand="block" fill="outline" (click)="onReset()">Reset Form</ion-button>
166+
</div>
167+
168+
<!-- Hidden region for debugging screen reader announcements -->
169+
<div aria-live="polite" aria-atomic="true" class="aria-live-region" id="debug-region" #debugRegion></div>
170+
</ion-content>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
.grid {
2+
display: grid;
3+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
4+
grid-row-gap: 20px;
5+
grid-column-gap: 20px;
6+
}
7+
8+
h2 {
9+
font-size: 12px;
10+
font-weight: normal;
11+
color: var(--ion-color-step-600);
12+
margin-top: 10px;
13+
margin-bottom: 5px;
14+
}
15+
16+
.aria-live-region {
17+
position: absolute;
18+
left: -10000px;
19+
width: 1px;
20+
height: 1px;
21+
overflow: hidden;
22+
}
23+
24+
.validation-info {
25+
margin: 20px;
26+
padding: 10px;
27+
background: var(--ion-color-light);
28+
border-radius: 4px;
29+
}
30+
31+
.validation-info h2 {
32+
font-size: 14px;
33+
font-weight: 600;
34+
margin-bottom: 10px;
35+
}
36+
37+
.validation-info ol {
38+
margin: 0;
39+
padding-left: 20px;
40+
}
41+
42+
.validation-info li {
43+
margin-bottom: 5px;
44+
}

0 commit comments

Comments
 (0)