Skip to content

Commit

Permalink
feat(styles): component form hint (#3961)
Browse files Browse the repository at this point in the history
Co-authored-by: Philipp Gfeller <[email protected]>
  • Loading branch information
leagrdv and gfellerph authored Nov 20, 2024
1 parent c5ebf3f commit 81859a1
Show file tree
Hide file tree
Showing 17 changed files with 78 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .changeset/wild-bugs-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@swisspost/design-system-styles': major
---

Changed the class name of assistive text below form fields from `.form-text` to `.form-hint` and improved accessibility by connecting the hint to the form through `aria-describedby`.
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,14 @@ const FormTemplate = {
type="text"
placeholder="Placeholder"
name="example-text-field"
aria-describedby="example-form-hint"
required
/>
<label class="form-label" for="example-dialog-text-field">Label</label>
<div class="form-text">
<p class="form-hint" id="example-form-hint">
Hintus textus elare volare cantare hendrerit in vulputate velit esse molestie
consequat, vel illum dolore eu feugiat nulla facilisis.
</div>
</p>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ function render(args: Args, context: StoryContext) {
const contextual: (TemplateResult | null)[] = [
args.validation === 'is-valid' ? html` <p class="valid-feedback">Ggranda sukceso!</p> ` : null,
args.validation === 'is-invalid' ? html` <p class="invalid-feedback">Eraro okazis!</p> ` : null,
args.hint !== '' ? html` <div class="form-text">${args.hint}</div> ` : null,
args.hint !== '' ? html` <p class="form-hint" id="form-hint-${id}">${args.hint}</p> ` : null,
];

const control: TemplateResult = html`
Expand All @@ -172,6 +172,7 @@ function render(args: Args, context: StoryContext) {
?disabled="${args.disabled}"
aria-label="${useAriaLabel ? args.label : nothing}"
?aria-invalid="${VALIDATION_STATE_MAP[args.validation]}"
aria-describedby="${args.hint !== '' ? 'form-hint-' + id : nothing}"
value="${args.value ? args.value : nothing}"
/>
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,9 @@ const Template: Story = {
args.validation === 'is-invalid'
? html` <p class="invalid-feedback">Eraro okazis!</p> `
: null,
args.hint !== '' ? html` <div class="form-text">${args.hint}</div> ` : null,
args.hint !== ''
? html` <p class="form-hint" id="form-hint-${context.id}">${args.hint}</p> `
: null,
];
const control = html`
<select
Expand All @@ -253,6 +255,7 @@ const Template: Story = {
?disabled="${args.disabled}"
aria-label="${useAriaLabel ? args.label : nothing}"
aria-invalid="${ifDefined(VALIDATION_STATE_MAP[args.validation])}"
aria-describedby="${args.hint !== '' ? 'form-hint-' + context.id : ''}"
@change="${(e: Event) => {
updateArgs({ value: (e.target as HTMLSelectElement).value });
}}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const Slider: Story = {
bg => html`
<div
class="${bg} d-flex flex-wrap align-items-start flex-column gap-16 p-16"
data-color-scheme=${bg === 'bg-white' ? 'light' : 'dark'}
>
${[
...bombArgs({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,46 +195,41 @@ function render(args: Args, context: StoryContext) {
?disabled="${args.disabled}"
aria-label="${useAriaLabel ? args.label : nothing}"
?aria-invalid="${VALIDATION_STATE_MAP[args.validation]}"
aria-describedby="${args.showValue === 'text' ? 'form-hint-' + id : ''}"
@input="${(e: MouseEvent) => updateArgs({ value: (e.target as HTMLInputElement).value })}"
/>
`;

let valueElement: TemplateResult | TemplateResult[] | null = null;
let valueElement: TemplateResult | null = null;

if (args.showValue === 'text') {
valueElement = html` <p class="form-text">${args.value}</p> `;
} else if (args.showValue === 'input') {
if (args.showValue === 'input') {
const inputId = context.id
? `${context.id}_input`
: `${context.viewMode}_${context.name.replace(/\s/g, '-')}_ExampleRangeInput`;

valueElement = [
html` <label class="form-label visually-hidden" for="${inputId}">Range controller</label> `,
html`
<input
id="${inputId}"
class="form-control mw-giant"
type="text"
inputmode="decimal"
value="${args.value}"
.value="${args.value}"
?disabled="${args.disabled}"
@input="${(e: Event) => updateArgs({ value: (e.target as HTMLInputElement).value })}"
/>
`,
];
}

if (args.showValue === 'input') {
return html`
<div class="row align-items-end">
<div class="col">${[label, control, ...contextual].filter(el => el !== null)}</div>
<div class="col-auto">${valueElement}</div>
<div class="col-auto">
<label class="form-label visually-hidden" for="${inputId}">Range controller</label>
<input
id="${inputId}"
class="form-control mw-giant"
type="text"
inputmode="decimal"
value="${args.value}"
.value="${args.value}"
?disabled="${args.disabled}"
@input="${(e: Event) => updateArgs({ value: (e.target as HTMLInputElement).value })}"
/>
</div>
</div>
`;
} else {
return html`${[label, control, valueElement, ...contextual].filter(el => el !== null)}`;
} else if (args.showValue === 'text') {
valueElement = html`<p class="form-hint" id="form-hint-${id}">${args.value}</p> `;
}

return html`${[label, control, valueElement, ...contextual].filter(el => el !== null)}`;
}

export const Default: Story = {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,9 @@ function renderTextarea(args: Args, context: StoryContext) {
args.validation === 'is-invalid'
? html`<div class="invalid-feedback">Eraro okazis!</div>`
: null,
args.hint !== '' ? html`<div class="form-text">${args.hint}</div>` : null,
args.hint !== ''
? html`<p class="form-hint" id="form-hint-${context.id}">${args.hint}</p>`
: null,
];
const control = html`
<textarea
Expand All @@ -206,6 +208,7 @@ function renderTextarea(args: Args, context: StoryContext) {
?disabled=${args.disabled}
aria-label=${useAriaLabel ? args.label : nothing}
aria-invalid=${VALIDATION_STATE_MAP[args.validation] ?? nothing}
aria-describedby="${args.hint ? 'form-hint-' + context.id : ''}"
style=${args.resize ?? nothing}
>
${args.textInside ?? nothing}</textarea
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export class Search extends React.Component {
id="IconSearchFilter_Freetext"
type="text"
className="form-control"
aria-describedby="search-icon-form-hint"
placeholder=""
value={this.state.freetext}
onChange={this.searchFreetext.bind(this)}
Expand All @@ -60,9 +61,9 @@ export class Search extends React.Component {
<span className="visually-hidden">Reset Search</span>
</button>
) : null}
<div className="form-text">
<p className="form-hint" id="search-icon-form-hint">
{`Showing ${this.state.icons.length} of ${report.icons.length} icons.`}
</div>
</p>
</div>
</div>

Expand Down
18 changes: 8 additions & 10 deletions packages/documentation/src/stories/patterns/forms/forms.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import * as FormPatternsStories from './forms.stories';

All things need to be contained inside a container. The container limits the size of the content and centers it on the page.

<Source
code={`<div class="container">...</div>`}
language="html"
/>
<Source code={`<div class="container">...</div>`} language="html" />

## Basic input

Expand All @@ -24,6 +21,7 @@ next to the field.
<Canvas of={FormPatternsStories.BasicInput} sourceState="shown" />

## Vertical spacing

To add space between input, you can use [margin utilities](/?path=/docs/facaacfd-18f1-49b4-80f1-a96680730fa0--docs).

<Canvas of={FormPatternsStories.VerticalSpacing} sourceState="shown" />
Expand Down Expand Up @@ -74,13 +72,13 @@ Validation messages are placed directly after the `<input>`
(or after the `<label>` in the case of floating label). Showing and hiding of these messages is typically controlled by
adding and removing validation classes on the input:

* The message needs the class `.invalid-feedback/.valid-feedback`
* The form needs the class `.was-validated`. With this class you can manage when to display errors. Usually, errors should not be
shown on untouched fields.
- The message needs the class `.invalid-feedback/.valid-feedback`
- The form needs the class `.was-validated`. With this class you can manage when to display errors. Usually, errors should not be
shown on untouched fields.

Or
Or

The input field needs the class `.is-invalid/.is-valid`
The input field needs the class `.is-invalid/.is-valid`

<Canvas of={FormPatternsStories.Validation} sourceState="shown" />

Expand All @@ -90,7 +88,7 @@ Hints can help users better understand the type of information they need to prov
validation messages, must be placed after the `<input>` (or `<label>`
in the case of floating label) and after validation messages if there are some.

Hints use the class `.form-text`.
Hints use the class `.form-hint`.

To enable screen-readers to detect and read your hints, link the `<input>`
with the aria-describedby attribute to the hint via id.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,9 @@ export const Hints: Story = {
placeholder=" "
/>
<label class="form-label" for="firstname">Firstname</label>
<div id="firstname-hint" class="form-text">
<i class="pi pi-info" aria-hidden="true"></i>
<span>Also provide any middle names in this field</span>
</div>
<p id="firstname-hint" class="form-hint">
Also provide any middle names in this field
</p>
</div>
`,
};
Expand Down
1 change: 1 addition & 0 deletions packages/styles/src/components/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
@use 'datatable';
@use 'dialog';
@use 'form-check';
@use 'form-hint';
@use 'forms';
@use 'grid';
@use 'icon-button';
Expand Down
16 changes: 16 additions & 0 deletions packages/styles/src/components/form-hint.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@use './../functions/tokens';
@use './../tokens/components';

tokens.$default-map: components.$post-text-input;

.form-hint {
color: tokens.get('input-color-enabled-fg');
padding-inline: tokens.get('input-padding-inline-assist');
padding-block: tokens.get('input-padding-block-assist');
font-size: tokens.get('input-assist-font-size');
margin-block-start: 0;
}

.form-control:disabled .form-hint {
color: tokens.get('input-color-disabled-fg');
}
2 changes: 1 addition & 1 deletion packages/styles/src/components/form-range.scss
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ $webkit-thumb-width: 32px;
}
}

+ .form-text {
+ .form-hint {
margin-top: spacing.$size-regular;
transform: translateY(#{-0.5 * forms.$form-range-height});
}
Expand Down
6 changes: 0 additions & 6 deletions packages/styles/src/components/form-select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,6 @@
}
}

@include utilities.disabled-style {
~ .form-text {
color: var(--post-gray-60);
}
}

&:not([multiple]) {
&:disabled {
background-image: b.escape-svg(form-select.$form-select-indicator-disabled),
Expand Down
14 changes: 7 additions & 7 deletions packages/styles/src/components/forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,6 @@ tokens.$default-map: components.$post-text-input;
}
}

@include utilities.disabled-style {
~ .form-text {
color: tokens.get('input-color-disabled-fg');
}
}

&[readonly]:not(:disabled) {
border-color: tokens.get('input-color-enabled-border');
background-color: tokens.get('input-color-enabled-bg');
Expand Down Expand Up @@ -164,15 +158,21 @@ tokens.$default-map: components.$post-text-input;
}

&::placeholder {
color: tokens.get('input-color-helptext-fg');
color: tokens.get('input-color-enabled-fg');
opacity: 1;
}

&:disabled {
color: tokens.get('input-color-disabled-fg');
background-color: tokens.get('input-color-disabled-bg');
border-color: tokens.get('input-color-disabled-border');
border-style: tokens.get('input-border-style-disabled');
opacity: 1;

&::placeholder {
color: tokens.get('input-color-disabled-fg');
opacity: 1;
}
}

&:hover:not(:disabled):not([readonly])::file-selector-button {
Expand Down
1 change: 0 additions & 1 deletion packages/styles/src/themes/bootstrap/_forms.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@forward './forms/form-text';
@forward './forms/form-select';
@forward './forms/form-range';
@forward './forms/input-group';
2 changes: 0 additions & 2 deletions packages/styles/src/themes/bootstrap/forms/_form-text.scss

This file was deleted.

0 comments on commit 81859a1

Please sign in to comment.