Skip to content

Commit

Permalink
add switch help text (#1800)
Browse files Browse the repository at this point in the history
  • Loading branch information
clintcs committed Feb 8, 2024
1 parent a5e9b94 commit 9451c3b
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 35 deletions.
12 changes: 12 additions & 0 deletions docs/pages/components/switch.md
Expand Up @@ -75,6 +75,18 @@ const App = () => (
);
```

### Help Text

Add descriptive help text to a switch with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.

```html:preview
<sl-switch help-text="What should the user know about the switch?">Label</sl-switch>
```

```jsx:react
const App = () => <SlSwitch help-text="What should the user know about the switch?">Label</SlSwitch>;
```

### Custom Styles

Use the available custom properties to change how the switch is styled.
Expand Down
100 changes: 65 additions & 35 deletions src/components/switch/switch.component.ts
@@ -1,6 +1,7 @@
import { classMap } from 'lit/directives/class-map.js';
import { defaultValue } from '../../internal/default-value.js';
import { FormControlController } from '../../internal/form.js';
import { HasSlotController } from '../../internal/slot.js';
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
Expand All @@ -18,6 +19,7 @@ import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
* @since 2.0
*
* @slot - The switch's label.
* @slot help-text - Text that describes how to use the switch. Alternatively, you can use the `help-text` attribute.
*
* @event sl-blur - Emitted when the control loses focus.
* @event sl-change - Emitted when the control's checked state changes.
Expand All @@ -29,6 +31,7 @@ import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
* @csspart control - The control that houses the switch's thumb.
* @csspart thumb - The switch's thumb.
* @csspart label - The switch's label.
* @csspart form-control-help-text - The help text's wrapper.
*
* @cssproperty --width - The width of the switch.
* @cssproperty --height - The height of the switch.
Expand All @@ -42,6 +45,7 @@ export default class SlSwitch extends ShoelaceElement implements ShoelaceFormCon
defaultValue: (control: SlSwitch) => control.defaultChecked,
setValue: (control: SlSwitch, checked: boolean) => (control.checked = checked)
});
private readonly hasSlotController = new HasSlotController(this, 'help-text');

@query('input[type="checkbox"]') input: HTMLInputElement;

Expand Down Expand Up @@ -76,6 +80,9 @@ export default class SlSwitch extends ShoelaceElement implements ShoelaceFormCon
/** Makes the switch a required field. */
@property({ type: Boolean, reflect: true }) required = false;

/** The switch's help text. If you need to display HTML, use the `help-text` slot instead. */
@property({ attribute: 'help-text' }) helpText = '';

/** Gets the validity state object */
get validity() {
return this.input.validity;
Expand Down Expand Up @@ -179,46 +186,69 @@ export default class SlSwitch extends ShoelaceElement implements ShoelaceFormCon
}

render() {
const hasHelpTextSlot = this.hasSlotController.test('help-text');
const hasHelpText = this.helpText ? true : !!hasHelpTextSlot;

return html`
<label
part="base"
<div
class=${classMap({
switch: true,
'switch--checked': this.checked,
'switch--disabled': this.disabled,
'switch--focused': this.hasFocus,
'switch--small': this.size === 'small',
'switch--medium': this.size === 'medium',
'switch--large': this.size === 'large'
'form-control': true,
'form-control--small': this.size === 'small',
'form-control--medium': this.size === 'medium',
'form-control--large': this.size === 'large',
'form-control--has-help-text': hasHelpText
})}
>
<input
class="switch__input"
type="checkbox"
title=${this.title /* An empty title prevents browser validation tooltips from appearing on hover */}
name=${this.name}
value=${ifDefined(this.value)}
.checked=${live(this.checked)}
.disabled=${this.disabled}
.required=${this.required}
role="switch"
aria-checked=${this.checked ? 'true' : 'false'}
@click=${this.handleClick}
@input=${this.handleInput}
@invalid=${this.handleInvalid}
@blur=${this.handleBlur}
@focus=${this.handleFocus}
@keydown=${this.handleKeyDown}
/>
<span part="control" class="switch__control">
<span part="thumb" class="switch__thumb"></span>
</span>
<div part="label" class="switch__label">
<slot></slot>
<label
part="base"
class=${classMap({
switch: true,
'switch--checked': this.checked,
'switch--disabled': this.disabled,
'switch--focused': this.hasFocus,
'switch--small': this.size === 'small',
'switch--medium': this.size === 'medium',
'switch--large': this.size === 'large'
})}
>
<input
class="switch__input"
type="checkbox"
title=${this.title /* An empty title prevents browser validation tooltips from appearing on hover */}
name=${this.name}
value=${ifDefined(this.value)}
.checked=${live(this.checked)}
.disabled=${this.disabled}
.required=${this.required}
role="switch"
aria-checked=${this.checked ? 'true' : 'false'}
aria-describedby="help-text"
@click=${this.handleClick}
@input=${this.handleInput}
@invalid=${this.handleInvalid}
@blur=${this.handleBlur}
@focus=${this.handleFocus}
@keydown=${this.handleKeyDown}
/>
<span part="control" class="switch__control">
<span part="thumb" class="switch__thumb"></span>
</span>
<div part="label" class="switch__label">
<slot></slot>
</div>
</label>
<div
aria-hidden=${hasHelpText ? 'false' : 'true'}
class="form-control__help-text"
id="help-text"
part="form-control-help-text"
>
<slot name="help-text">${this.helpText}</slot>
</div>
</label>
</div>
`;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/components/switch/switch.styles.ts
@@ -1,8 +1,10 @@
import { css } from 'lit';
import componentStyles from '../../styles/component.styles.js';
import formControlStyles from '../../styles/form-control.styles.js';

export default css`
${componentStyles}
${formControlStyles}
:host {
display: inline-block;
Expand Down
1 change: 1 addition & 0 deletions src/components/switch/switch.test.ts
Expand Up @@ -21,6 +21,7 @@ describe('<sl-switch>', () => {
expect(el.required).to.be.false;
expect(el.checked).to.be.false;
expect(el.defaultChecked).to.be.false;
expect(el.helpText).to.equal('');
});

it('should have title if title attribute is set', async () => {
Expand Down

0 comments on commit 9451c3b

Please sign in to comment.