Skip to content

Commit 34aac90

Browse files
committed
docs: fix and document use of keyboard focus classes on text fields
Our Storybook was adding the keyboard focus class on click, which isn't the intended design. Click focus and focused by use of a keyboard have different styles. This update stops the class from being applied on click, and documents how the class should be added by the implementation.
1 parent 60685ce commit 34aac90

File tree

4 files changed

+97
-25
lines changed

4 files changed

+97
-25
lines changed

components/textfield/stories/template.js

+29-5
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,13 @@ export const Template = ({
120120
style=${styleMap(customStyles)}
121121
@click=${onclick}
122122
@focusin=${function() {
123-
updateArgs({
123+
updateArgs?.({
124124
isFocused: true,
125-
isKeyboardFocused: true
126125
});
127126
}}
128127
@focusout=${function() {
129-
updateArgs({
128+
updateArgs?.({
130129
isFocused: false,
131-
isKeyboardFocused: false
132130
});
133131
}}
134132
id=${ifDefined(id)}
@@ -231,7 +229,7 @@ export const TextFieldOptions = (args, context) => Container({
231229
"gap": "8px",
232230
},
233231
heading: "Default",
234-
content: Template({...args, context})
232+
content: Template(args, context)
235233
})}
236234
${Container({
237235
withBorder: false,
@@ -275,3 +273,29 @@ export const TextFieldOptions = (args, context) => Container({
275273
})}
276274
`
277275
});
276+
277+
export const KeyboardFocusTemplate = (args, context) => Container({
278+
direction: "column",
279+
withBorder: false,
280+
wrapperStyles: {
281+
rowGap: "12px",
282+
},
283+
content: html`
284+
${Container({
285+
withBorder: false,
286+
containerStyles: {
287+
"gap": "8px",
288+
},
289+
heading: "Default",
290+
content: Template({...args, isKeyboardFocused: true}, context)
291+
})}
292+
${Container({
293+
withBorder: false,
294+
containerStyles: {
295+
"gap": "8px",
296+
},
297+
heading: "Quiet",
298+
content: Template({...args, isKeyboardFocused: true, isQuiet: true}, context)
299+
})}
300+
`
301+
});

components/textfield/stories/textarea.stories.js

+20-10
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ import { Sizes } from "@spectrum-css/preview/decorators";
22
import { disableDefaultModes } from "@spectrum-css/preview/modes";
33
import metadata from "../metadata/metadata.json";
44
import packageJson from "../package.json";
5-
import { HelpTextOptionsTextArea, Template, TextAreaOptions } from "./textarea.template.js";
5+
import { HelpTextOptionsTextArea, KeyboardFocusTemplate, Template, TextAreaOptions } from "./textarea.template.js";
66
import { TextAreaGroup } from "./textarea.test.js";
77
import { default as Textfield } from "./textfield.stories.js";
88

99
/**
1010
* A text area is multi-line text field using the `<textarea>` element that lets a user input a longer amount of text than a standard text field. It can include all of the standard validation options supported by the text field component.
11-
*/
12-
11+
*/
1312
export default {
1413
title: "Text area",
1514
component: "TextArea",
@@ -52,7 +51,7 @@ CharacterCount.parameters = {
5251

5352
/**
5453
* A text area in a disabled state shows that an input field exists, but is not available in that circumstance. This can be used to maintain layout continuity and communicate that a field may become available later.
55-
*/
54+
*/
5655
export const Disabled = Template.bind({});
5756
Disabled.tags = ["!dev"];
5857
Disabled.args = {
@@ -66,7 +65,7 @@ Disabled.parameters = {
6665
* A text area can have [help text](/docs/components-help-text--docs) below the field to give extra context or instruction about what a user should input in the field. The help text area has two options: a description and an error message. The description communicates a hint or helpful information, such as specific requirements for correctly filling out the field. The error message communicates an error for when the field requirements aren’t met, prompting a user to adjust what they had originally input.
6766
*
6867
* Instead of placeholder text, use the help text description to convey requirements or to show any formatting examples that would help user comprehension. Putting instructions for how to complete an input, requirements, or any other essential information into placeholder text is not accessible.
69-
*/
68+
*/
7069
export const HelpText = HelpTextOptionsTextArea.bind({});
7170
HelpText.tags = ["!dev"];
7271
HelpText.parameters = {
@@ -84,7 +83,7 @@ Quiet.parameters = {
8483

8584
/**
8685
* Text area has a read-only option for when content in the disabled state still needs to be shown. This allows for content to be copied, but not interacted with or changed.
87-
*/
86+
*/
8887
export const Readonly = Template.bind({});
8988
Readonly.tags = ["!dev"];
9089
Readonly.args = {
@@ -98,7 +97,7 @@ Readonly.storyName = "Read-only";
9897

9998
/**
10099
* Side labels are most useful when vertical space is limited.
101-
*/
100+
*/
102101
export const SideLabel = Template.bind({});
103102
SideLabel.tags = ["!dev"];
104103
SideLabel.args = {
@@ -112,10 +111,9 @@ SideLabel.parameters = {
112111
chromatic: { disableSnapshot: true }
113112
};
114113

115-
116114
/**
117115
* Text area can display a validation icon when the text entry is expected to conform to a specific format (e.g., email address, credit card number, password creation requirements, etc.). The icon appears as soon as a user types a valid entry in the field.
118-
*/
116+
*/
119117
export const Validation = Template.bind({});
120118
Validation.tags = ["!dev"];
121119
Validation.args = {
@@ -126,7 +124,6 @@ Validation.parameters = {
126124
};
127125
Validation.storyName = "Validation icon";
128126

129-
130127
export const Sizing = (args, context) => Sizes({
131128
Template: Template,
132129
withHeading: false,
@@ -142,6 +139,19 @@ Sizing.parameters = {
142139
chromatic: { disableSnapshot: true }
143140
};
144141

142+
/**
143+
* When the text area was focused using the keyboard (e.g. with the tab key), the implementation must add the `is-keyboardFocused` class, which
144+
* displays the focus indicator. This indicator should not appear on focus from a click or tap.
145+
*/
146+
export const KeyboardFocus = KeyboardFocusTemplate.bind({});
147+
KeyboardFocus.tags = ["!dev"];
148+
KeyboardFocus.args = {
149+
isKeyboardFocused: true,
150+
};
151+
KeyboardFocus.parameters = {
152+
chromatic: { disableSnapshot: true }
153+
};
154+
145155
// ********* VRT ONLY ********* //
146156
// @todo should this show text field and text area in the same snapshot?
147157
export const WithForcedColors = TextAreaGroup.bind({});

components/textfield/stories/textarea.template.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export const TextAreaOptions = (args, context) => Container({
5050
"gap": "8px",
5151
},
5252
heading: "Default",
53-
content: Template({...args, context})
53+
content: Template(args, context)
5454
})}
5555
${Container({
5656
withBorder: false,
@@ -94,3 +94,29 @@ export const TextAreaOptions = (args, context) => Container({
9494
})}
9595
`
9696
});
97+
98+
export const KeyboardFocusTemplate = (args, context) => Container({
99+
direction: "column",
100+
withBorder: false,
101+
wrapperStyles: {
102+
rowGap: "12px",
103+
},
104+
content: html`
105+
${Container({
106+
withBorder: false,
107+
containerStyles: {
108+
"gap": "8px",
109+
},
110+
heading: "Default",
111+
content: Template({...args, isKeyboardFocused: true}, context)
112+
})}
113+
${Container({
114+
withBorder: false,
115+
containerStyles: {
116+
"gap": "8px",
117+
},
118+
heading: "Quiet",
119+
content: Template({...args, isKeyboardFocused: true, isQuiet: true}, context)
120+
})}
121+
`
122+
});

components/textfield/stories/textfield.stories.js

+21-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { disableDefaultModes } from "@spectrum-css/preview/modes";
33
import { isDisabled, isFocused, isInvalid, isKeyboardFocused, isLoading, isQuiet, isReadOnly, isRequired, isValid, size } from "@spectrum-css/preview/types";
44
import metadata from "../metadata/metadata.json";
55
import packageJson from "../package.json";
6-
import { HelpTextOptions, Template, TextFieldOptions } from "./template.js";
6+
import { HelpTextOptions, KeyboardFocusTemplate, Template, TextFieldOptions } from "./template.js";
77
import { TextFieldGroup } from "./textfield.test.js";
88

99
/**
@@ -143,7 +143,7 @@ export default {
143143

144144
/**
145145
* Text fields should always have a label. In rare cases where context is sufficient and an accessibility expert has reviewed the design, the label could be undefined. These text fields without a visible label should still include an aria-label in HTML (depending on the context, “aria-label” or “aria-labelledby”).
146-
*/
146+
*/
147147

148148
export const Default = TextFieldGroup.bind({});
149149
Default.tags = ["!autodocs"];
@@ -160,7 +160,7 @@ Standard.parameters = {
160160

161161
/**
162162
* Text fields can display a character count indicator when the length of the text entry needs to be kept under a predefined value. Character count indicators can be used in conjunction with other indicators (validation icon, “optional” or “required” indicators) when necessary.
163-
*/
163+
*/
164164
export const CharacterCount = Template.bind({});
165165
CharacterCount.tags = ["!dev"];
166166
CharacterCount.args = {
@@ -174,7 +174,7 @@ CharacterCount.parameters = {
174174

175175
/**
176176
* A text field in a disabled state shows that an input field exists, but is not available in that circumstance. This can be used to maintain layout continuity and communicate that a field may become available later.
177-
*/
177+
*/
178178
export const Disabled = Template.bind({});
179179
Disabled.tags = ["!dev"];
180180
Disabled.args = {
@@ -188,7 +188,7 @@ Disabled.parameters = {
188188
* A text field can have [help text](/docs/components-help-text--docs) below the field to give extra context or instruction about what a user should input in the field. The help text area has two options: a description and an error message. The description communicates a hint or helpful information, such as specific requirements for correctly filling out the field. The error message communicates an error for when the field requirements aren’t met, prompting a user to adjust what they had originally input.
189189
*
190190
* Instead of placeholder text, use the help text description to convey requirements or to show any formatting examples that would help user comprehension. Putting instructions for how to complete an input, requirements, or any other essential information into placeholder text is not accessible.
191-
*/
191+
*/
192192
export const HelpText = HelpTextOptions.bind({});
193193
HelpText.tags = ["!dev"];
194194
HelpText.parameters = {
@@ -197,7 +197,7 @@ HelpText.parameters = {
197197

198198
/**
199199
* Quiet text fields can have no visible background. This style works best when a clear layout (vertical stack, table, grid) makes it easy to parse. Too many quiet components in a small space can be hard to read.
200-
*/
200+
*/
201201
export const Quiet = TextFieldOptions.bind({});
202202
Quiet.tags = ["!dev"];
203203
Quiet.args = {
@@ -210,7 +210,7 @@ Quiet.parameters = {
210210

211211
/**
212212
* Text fields have a read-only option for when content in the disabled state still needs to be shown. This allows for content to be copied, but not interacted with or changed.
213-
*/
213+
*/
214214
export const Readonly = Template.bind({});
215215
Readonly.tags = ["!dev"];
216216
Readonly.args = {
@@ -224,7 +224,7 @@ Readonly.storyName = "Read-only";
224224

225225
/**
226226
* Side labels are most useful when vertical space is limited.
227-
*/
227+
*/
228228
export const SideLabel = Template.bind({});
229229
SideLabel.tags = ["!dev"];
230230
SideLabel.args = {
@@ -254,7 +254,7 @@ Sizing.parameters = {
254254

255255
/**
256256
* Text fields can display a validation icon when the text entry is expected to conform to a specific format (e.g., email address, credit card number, password creation requirements, etc.). The icon appears as soon as a user types a valid entry in the field.
257-
*/
257+
*/
258258
export const Validation = Template.bind({});
259259
Validation.tags = ["!dev"];
260260
Validation.args = {
@@ -265,6 +265,18 @@ Validation.parameters = {
265265
};
266266
Validation.storyName = "Validation icon";
267267

268+
/**
269+
* When the text field was focused using the keyboard (e.g. with the tab key), the implementation must add the `is-keyboardFocused` class, which
270+
* displays the focus indicator. This indicator should not appear on focus from a click or tap.
271+
*/
272+
export const KeyboardFocus = KeyboardFocusTemplate.bind({});
273+
KeyboardFocus.tags = ["!dev"];
274+
KeyboardFocus.args = {
275+
isKeyboardFocused: true,
276+
};
277+
KeyboardFocus.parameters = {
278+
chromatic: { disableSnapshot: true }
279+
};
268280

269281
// ********* VRT ONLY ********* //
270282
// @todo should this show text field and text area in the same snapshot?

0 commit comments

Comments
 (0)