Help Text Mixin

Overview API

Description

Section titled Description

It is not currently possible to provide accessible ARIA references between elements in different shadow roots. When creating your own components, use the ManageHelpText mixin to associate slotted <sp-help-text> elements with the elements they describe. This functionality is also surfaced as a base class HelpTextManagedElement if you prefer to extend from there, instead.

Spectrum Web Components leverages the ManageHelpText mixin to power elements like sp-field-group, sp-number-field, sp-radio-group, sp-search and sp-textfield.

Usage

Section titled Usage

See it on NPM! How big is this package in your project?

yarn add @spectrum-web-components/help-text

Import ManageHelpText via:

import { ManageHelpText } from '@spectrum-web-components/help-text/mange-help-text.js';

When looking to leverage the HelpTextManagedElement base class as a type and/or for extension purposes, do so via:

import { HelpTextManagedElement } from '@spectrum-web-components/help-text/HelpTextManagedElement.js';

The HelpTextManager class is also available for import as:

import { HelpTextManager } from '@spectrum-web-components/help-text/HelpTextManager.js';

ManageHelpText mixin

Section titled ManageHelpText mixin

ManageHelpText mixes two properties into your class:

  • helpTextId: the id attribute of the associated <sp-help-text>
  • renderHelpText(negative?: boolean): a method that returns a TemplateResult with the help-text and negative-help-text slots

Internal

Section titled Internal

To describe an element within your custom element's shadow root, use mode: 'internal' (the default) and set aria-describedby on the target element:

import { SpectrumElement, html } from '@spectrum-web-components/base';
import { ManageHelpText } from '@spectrum-web-components/help-text/src/manage-hep-text.js';

export class MyElement extends ManageHelpText(SpectrumElement) {
    invalid = false;

    render() {
        return html`
            <input aria-describedby=${this.helpTextId} />
            ${this.renderHelpText(this.invalid)}
        `;
    }
}

External

Section titled External

To describe the custom element itself, use mode: 'external'. This will automatically manage the application of the aria-describedby atribute on MyElement:

import { SpectrumElement, html } from '@spectrum-web-components/base';
import { ManageHelpText } from '@spectrum-web-components/help-text/src/manage-help-text.js';

export class MyElement extends ManageHelpText(SpectrumElement, {
    mode: 'external',
}) {
    invalid = false;

    render() {
        return html`
            ${this.renderHelpText(this.invalid)}
        `;
    }
}

This functionality is powered by the HelpTextManager class which is also exported from this package and can be leveraged directly. It accepts the root element on which it will manage help text and an options object that accepts the mode by which that help text will be managed at construction time. Leveraged at render time, it surfaces an id property and a render(invalid?: boolean) method for use in your template.

Usage with self-managed validity

Section titled Usage with self-managed validity

Spectrum Web Components that have an invalid attribute, like <sp-fieldgroup> and <sp-textfield>, automatically render either the help-text or negative-help-text slot based on validity. Provide both, and the appropriate <sp-help-text> element will be surfaced:

Field group
<sp-field-label for="fruit">What are your favorite fruits?</sp-field-label>
<sp-field-group horizontal id="fruit">
    <sp-checkbox value="apple">Apple</sp-checkbox>
    <sp-checkbox
        value="not-a-fruit"
        onchange="javascript:this.parentElement.invalid = this.checked"
    >
        Lettuce
    </sp-checkbox>
    <sp-checkbox value="strawberry" checked>Strawberry</sp-checkbox>
    <sp-help-text slot="help-text">One of these is not a fruit.</sp-help-text>
    <sp-help-text slot="negative-help-text" icon>
        Choose actual fruit(s).
    </sp-help-text>
</sp-field-group>
Radio group
<sp-field-label for="icecream">
    What is your favorite ice cream flavor?
</sp-field-label>
<sp-radio-group
    is="icecream"
    onchange="
        this.invalid = this.value === 'fourth';
    "
>
    <sp-radio value="first">Vanilla</sp-radio>
    <sp-radio value="second">Chocolate</sp-radio>
    <sp-radio value="third">Strawberry</sp-radio>
    <sp-radio value="fourth">I don't like ice cream</sp-radio>
    <sp-help-text slot="help-text">Everyone likes ice cream.</sp-help-text>
    <sp-help-text slot="negative-help-text" icon>
        You can't not like ice cream.
    </sp-help-text>
</sp-radio-group>
Textarea
<sp-field-label for="textarea">Stay "Positive"</sp-field-label>
<sp-textfield
    multiline
    id="textarea"
    pattern="[P][o][s][i][t][i][v][e]"
    value="Positive"
>
    <sp-help-text slot="help-text">
        Tell us how you are feeling today.
    </sp-help-text>
    <sp-help-text slot="negative-help-text">Please be "Positive".</sp-help-text>
</sp-textfield>
Textfield
<sp-field-label for="textfield">Stay "Positive"</sp-field-label>
<sp-textfield
    id="textfield"
    pattern="[P][o][s][i][t][i][v][e]"
    value="Positive"
>
    <sp-help-text slot="help-text">
        Tell us how you are feeling today.
    </sp-help-text>
    <sp-help-text slot="negative-help-text">Please be "Positive".</sp-help-text>
</sp-textfield>

Usage with validity managed from above

Section titled Usage with validity managed from above

When the parent element does not manage its own validity, or you would prefer to leverage the parent application in deciding what content and when to deliver within your <sp-help-text> element, place your content in the help-text slot to ensure that it is available for receiving stateful content/properties across the lifecycle of the parent element in question.

Field group
<sp-field-label for="fruit">What are your favorite fruits?</sp-field-label>
<sp-field-group horizontal id="fruit">
    <sp-checkbox value="apple">Apple</sp-checkbox>
    <sp-checkbox
        value="not-a-fruit"
        onchange="
            const helpText = this.parentElement.querySelector(`[slot='help-text']`);
            helpText.icon = this.checked;
            helpText.textContent = this.checked ? 'Choose actual fruit(s).' : 'One of these is not a fruit.';
            helpText.variant = this.checked ? 'negative' : 'neutral';
        "
    >
        Lettuce
    </sp-checkbox>
    <sp-checkbox value="strawberry" checked>Strawberry</sp-checkbox>
    <sp-help-text slot="help-text">One of these is not a fruit.</sp-help-text>
</sp-field-group>
Radio group
<sp-field-label for="icecream">
    What is your favorite ice cream flavor?
</sp-field-label>
<sp-radio-group
    is="icecream"
    onchange="
        const helpText = this.querySelector(`[slot='help-text']`);
        const isInvalid = this.selected === 'fourth';
        helpText.icon = isInvalid;
        helpText.textContent = isInvalid ? 'You can\'t not like ice cream.' : 'Everyone likes ice cream.';
        helpText.variant = isInvalid ? 'negative' : 'neutral';
    "
>
    <sp-radio value="first">Vanilla</sp-radio>
    <sp-radio value="second">Chocolate</sp-radio>
    <sp-radio value="third">Strawberry</sp-radio>
    <sp-radio value="fourth">I don't like ice cream</sp-radio>
    <sp-help-text slot="help-text">Everyone likes ice cream.</sp-help-text>
</sp-radio-group>
Textarea
<sp-field-label for="textarea">Stay "Positive"</sp-field-label>
<sp-textfield
    multiline
    id="textarea"
    pattern="[P][o][s][i][t][i][v][e]"
    value="Positive"
    oninput='
        const helpText = this.querySelector(`[slot="help-text"]`);
        helpText.textContent = this.invalid ? `Please be "Positive".` : `Tell us how you are feeling today.`;
        helpText.variant = this.invalid ? `negative` : `neutral`;
    '
>
    <sp-help-text slot="help-text">
        Tell us how you're feeling today.
    </sp-help-text>
</sp-textfield>
Textfield
<sp-field-label for="textfield">Stay "Positive"</sp-field-label>
<sp-textfield
    id="textfield"
    pattern="[P][o][s][i][t][i][v][e]"
    value="Positive"
    oninput='
        const helpText = this.querySelector(`[slot="help-text"]`);
        helpText.textContent = this.invalid ? `Please be "Positive".` : `Tell us how you are feeling today.`;
        helpText.variant = this.invalid ? `negative` : `neutral`;
    '
>
    <sp-help-text slot="help-text">
        Tell us how you are feeling today.
    </sp-help-text>
</sp-textfield>

API

Slots

Section titled Slots
Name Description help-text default or non-negative help text to associate to your form element negative-help-text negative help text to associate to your form element when `invalid`