Skip to content

Commit a8c2989

Browse files
[CMSDS-3788] Revert Google Translate fix (#3867)
* Remove key mutation on paragraph element * Adding testable example * Update snap shots for examples * Update drawer
1 parent 4938d75 commit a8c2989

7 files changed

Lines changed: 179 additions & 15 deletions

File tree

examples/react-app/src/scripts/index.js

Lines changed: 179 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,176 @@
11
// Named import from main entry file. This example has been configured to use Webpack's tree shaking
22
// to only bundle imported components. Without this optimization, all components will be imported
33
// your build process.
4-
import { Alert, Button, Drawer, UsaBanner } from '@cmsgov/design-system';
4+
import {
5+
Alert,
6+
Button,
7+
Drawer,
8+
MultiInputDateField,
9+
TextField,
10+
UsaBanner,
11+
} from '@cmsgov/design-system';
512
import { useState } from 'react';
613
import ReactDOM from 'react-dom';
714

15+
function ExampleForm() {
16+
const [formData, setFormData] = useState({
17+
username: '',
18+
password: '',
19+
birthdate: { day: '', month: '', year: '' },
20+
});
21+
22+
const [errors, setErrors] = useState({
23+
username: '',
24+
password: '',
25+
birthdate: '',
26+
});
27+
28+
const [touched, setTouched] = useState(false);
29+
30+
const validateInput = (type) => {
31+
setTouched(true);
32+
const yearNum = parseInt(formData?.birthdate.year, 10);
33+
const newErrors = { ...errors };
34+
35+
switch (type) {
36+
case 'username':
37+
formData.username.length < 5
38+
? (newErrors.username = 'Username must be at least 5 characters')
39+
: (newErrors.username = '');
40+
break;
41+
case 'password':
42+
formData.password.length < 6
43+
? (newErrors.password = 'Password must be at least 6 characters')
44+
: (newErrors.password = '');
45+
break;
46+
case 'year':
47+
!yearNum || yearNum >= new Date().getFullYear()
48+
? (newErrors.birthdate = 'Please enter a year in the past')
49+
: (newErrors.birthdate = '');
50+
break;
51+
default:
52+
break;
53+
}
54+
55+
setErrors(newErrors);
56+
};
57+
58+
const handleSubmit = (e) => {
59+
e.preventDefault();
60+
console.log('Form submitted:', formData);
61+
};
62+
63+
return (
64+
<form onSubmit={handleSubmit}>
65+
<h2>Form Example</h2>
66+
67+
<TextField
68+
name="username"
69+
label="Username"
70+
hint="Your username can only include letters in the American-English alphabet."
71+
required
72+
minlength={5}
73+
maxlength={100}
74+
pattern="^[A-Za-z]+$"
75+
defaultValue={formData.username}
76+
onChange={(e) => setFormData((prev) => ({ ...prev, username: e.target.value }))}
77+
onBlur={() => validateInput('username')}
78+
errorMessage={touched ? errors.username : ''}
79+
errorPlacement="bottom"
80+
/>
81+
82+
<TextField
83+
name="password"
84+
label="Password"
85+
type="password"
86+
required
87+
minlength={6}
88+
defaultValue={formData.password}
89+
onChange={(e) => setFormData((prev) => ({ ...prev, password: e.target.value }))}
90+
onBlur={() => validateInput('password')}
91+
errorMessage={touched ? errors.password : ''}
92+
errorPlacement="bottom"
93+
/>
94+
95+
<MultiInputDateField
96+
label="Date of Birth"
97+
hint="For example: 10/31/1965"
98+
monthLabel="Month"
99+
dayLabel="Day"
100+
yearLabel="Year"
101+
errorMessage={touched ? errors.birthdate : ''}
102+
errorPlacement="bottom"
103+
monthValue={formData.birthdate.month}
104+
dayValue={formData.birthdate.day}
105+
yearValue={formData.birthdate.year}
106+
onChange={(event, dateObject) =>
107+
setFormData((prev) => ({ ...prev, birthdate: dateObject }))
108+
}
109+
onBlur={() => validateInput('year')}
110+
dayInvalid={!!errors.birthdate}
111+
monthInvalid={!!errors.birthdate}
112+
yearInvalid={!!errors.birthdate}
113+
/>
114+
115+
<div className="ds-u-margin-top--2">
116+
<Button type="submit">Submit</Button>
117+
</div>
118+
</form>
119+
);
120+
}
121+
122+
const ExampleInstantValidationForm = () => {
123+
const [username, setUsername] = useState('');
124+
const [error, setError] = useState(false);
125+
126+
const validateUsername = () => {
127+
if (username.length < 5) {
128+
setError(true);
129+
} else {
130+
setError(false);
131+
}
132+
};
133+
134+
return (
135+
<>
136+
<h2>Instant Validation Example</h2>
137+
<p>
138+
Because of the way VoiceOver works (or does not), we need to test{' '}
139+
<a href="https://design.cms.gov/patterns/Forms/error-validation/#instant-validation">
140+
instant validation
141+
</a>{' '}
142+
in the following manner:
143+
</p>
144+
<ol>
145+
<li>Have your screen reader of choice going (likely VoiceOver)</li>
146+
<li>Tab to the input to focus it</li>
147+
<li>Enter text that does not meet the requirement</li>
148+
<li>Tab to the next element</li>
149+
<li>
150+
Observe the alert being fired audibly. VoiceOver should read out the alert{' '}
151+
<code>onBlur</code>
152+
</li>
153+
</ol>
154+
<p>
155+
Note: there is no other focusable element after the TextField because VoiceOver will hide
156+
any alert and just announce the content in the next focusable element instead of the alert.
157+
</p>
158+
159+
<TextField
160+
name="username"
161+
label="Username"
162+
requirementLabel="Required."
163+
hint="Must be at least 5 characters long."
164+
defaultValue={username}
165+
onChange={(e) => setUsername(e.target.value)}
166+
onBlur={validateUsername}
167+
errorMessage={error ? 'Username must be longer.' : ''}
168+
errorPlacement="bottom"
169+
/>
170+
</>
171+
);
172+
};
173+
8174
const Example = function () {
9175
const [open, setOpen] = useState(false);
10176
return (
@@ -21,22 +187,23 @@ const Example = function () {
21187
<h1 className="ds-text-heading--3xl">React-app example</h1>
22188
<Alert heading="Hello world">
23189
<p className="ds-c-alert__text">You did it! You&rsquo;ve ran the example.</p>
24-
{open && (
25-
<Drawer
26-
footerTitle="Footer Title"
27-
footerBody={<p className="ds-text ds-u-margin--0">Footer content</p>}
28-
heading="Drawer Heading"
29-
onCloseClick={() => setOpen(false)}
30-
hasFocusTrap={true}
31-
>
32-
Test
33-
</Drawer>
34-
)}
190+
<Drawer
191+
footerTitle="Footer Title"
192+
footerBody={<p className="ds-text ds-u-margin--0">Footer content</p>}
193+
heading="Drawer Heading"
194+
isOpen={open}
195+
onCloseClick={() => setOpen(false)}
196+
hasFocusTrap={true}
197+
>
198+
Test
199+
</Drawer>
35200
<Button onClick={() => setOpen(true)} className="ds-u-margin-top--2">
36201
Learn more
37202
</Button>
38203
</Alert>
39204
</div>
205+
<ExampleForm />
206+
<ExampleInstantValidationForm />
40207
</div>
41208
</div>
42209
);

packages/design-system/src/components/InlineError/InlineError.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@ export function InlineError({
4747

4848
return (
4949
<p
50-
// Adding a key forces React to remount this element when `children` changes.
51-
// This helps avoid reconciliation errors caused by Google Translate directly mutating the DOM.
52-
key={children ? children.toString() : 'no-error'}
5350
{...otherProps}
5451
className={classes}
5552
id={useId('inline-error--', id)}
134 KB
Loading
46.8 KB
Loading
53.5 KB
Loading
90.2 KB
Loading
90.1 KB
Loading

0 commit comments

Comments
 (0)