add help text to sl-checkbox to match sl-switch

pull/1860/head
Cory LaViska 2024-02-08 13:34:06 -05:00
rodzic 9451c3b8de
commit 5edfcfa386
5 zmienionych plików z 100 dodań i 50 usunięć

Wyświetl plik

@ -89,6 +89,20 @@ 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-checkbox help-text="What should the user know about the checkbox?">Label</sl-checkbox>
```
```jsx:react
import SlCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
const App = () => <SlCheckbox help-text="What should the user know about the switch?">Label</SlCheckbox>;
```
### Custom Validity
Use the `setCustomValidity()` method to set a custom validation message. This will prevent the form from submitting and make the browser display the error message you provide. To clear the error, call this function with an empty string.

Wyświetl plik

@ -18,6 +18,8 @@ New versions of Shoelace are released as-needed and generally occur when a criti
## 2.13.1
- Added help text to `<sl-checkbox>`
- Added help text to `<sl-switch>` [#1800]
- Fixed a bug where the safe triangle was always visible when selecting nested `<sl-menu>` elements [#1835]
## 2.13.0

Wyświetl plik

@ -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';
@ -21,6 +22,7 @@ import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
* @dependency sl-icon
*
* @slot - The checkbox's label.
* @slot help-text - Text that describes how to use the checkbox. Alternatively, you can use the `help-text` attribute.
*
* @event sl-blur - Emitted when the checkbox loses focus.
* @event sl-change - Emitted when the checked state changes.
@ -35,6 +37,7 @@ import type { ShoelaceFormControl } from '../../internal/shoelace-element.js';
* @csspart checked-icon - The checked icon, an `<sl-icon>` element.
* @csspart indeterminate-icon - The indeterminate icon, an `<sl-icon>` element.
* @csspart label - The container that wraps the checkbox's label.
* @csspart form-control-help-text - The help text's wrapper.
*/
export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormControl {
static styles: CSSResultGroup = styles;
@ -45,6 +48,7 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
defaultValue: (control: SlCheckbox) => control.defaultChecked,
setValue: (control: SlCheckbox, checked: boolean) => (control.checked = checked)
});
private readonly hasSlotController = new HasSlotController(this, 'help-text');
@query('input[type="checkbox"]') input: HTMLInputElement;
@ -86,6 +90,9 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
/** Makes the checkbox a required field. */
@property({ type: Boolean, reflect: true }) required = false;
/** The checkbox'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;
@ -178,68 +185,92 @@ export default class SlCheckbox extends ShoelaceElement implements ShoelaceFormC
}
render() {
const hasHelpTextSlot = this.hasSlotController.test('help-text');
const hasHelpText = this.helpText ? true : !!hasHelpTextSlot;
//
// NOTE: we use a <div> around the label slot because of this Chrome bug.
//
// https://bugs.chromium.org/p/chromium/issues/detail?id=1413733
//
return html`
<label
part="base"
<div
class=${classMap({
checkbox: true,
'checkbox--checked': this.checked,
'checkbox--disabled': this.disabled,
'checkbox--focused': this.hasFocus,
'checkbox--indeterminate': this.indeterminate,
'checkbox--small': this.size === 'small',
'checkbox--medium': this.size === 'medium',
'checkbox--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="checkbox__input"
type="checkbox"
title=${this.title /* An empty title prevents browser validation tooltips from appearing on hover */}
name=${this.name}
value=${ifDefined(this.value)}
.indeterminate=${live(this.indeterminate)}
.checked=${live(this.checked)}
.disabled=${this.disabled}
.required=${this.required}
aria-checked=${this.checked ? 'true' : 'false'}
@click=${this.handleClick}
@input=${this.handleInput}
@invalid=${this.handleInvalid}
@blur=${this.handleBlur}
@focus=${this.handleFocus}
/>
<span
part="control${this.checked ? ' control--checked' : ''}${this.indeterminate ? ' control--indeterminate' : ''}"
class="checkbox__control"
<label
part="base"
class=${classMap({
checkbox: true,
'checkbox--checked': this.checked,
'checkbox--disabled': this.disabled,
'checkbox--focused': this.hasFocus,
'checkbox--indeterminate': this.indeterminate,
'checkbox--small': this.size === 'small',
'checkbox--medium': this.size === 'medium',
'checkbox--large': this.size === 'large'
})}
>
${this.checked
? html`
<sl-icon part="checked-icon" class="checkbox__checked-icon" library="system" name="check"></sl-icon>
`
: ''}
${!this.checked && this.indeterminate
? html`
<sl-icon
part="indeterminate-icon"
class="checkbox__indeterminate-icon"
library="system"
name="indeterminate"
></sl-icon>
`
: ''}
</span>
<input
class="checkbox__input"
type="checkbox"
title=${this.title /* An empty title prevents browser validation tooltips from appearing on hover */}
name=${this.name}
value=${ifDefined(this.value)}
.indeterminate=${live(this.indeterminate)}
.checked=${live(this.checked)}
.disabled=${this.disabled}
.required=${this.required}
aria-checked=${this.checked ? 'true' : 'false'}
@click=${this.handleClick}
@input=${this.handleInput}
@invalid=${this.handleInvalid}
@blur=${this.handleBlur}
@focus=${this.handleFocus}
/>
<div part="label" class="checkbox__label">
<slot></slot>
<span
part="control${this.checked ? ' control--checked' : ''}${this.indeterminate
? ' control--indeterminate'
: ''}"
class="checkbox__control"
>
${this.checked
? html`
<sl-icon part="checked-icon" class="checkbox__checked-icon" library="system" name="check"></sl-icon>
`
: ''}
${!this.checked && this.indeterminate
? html`
<sl-icon
part="indeterminate-icon"
class="checkbox__indeterminate-icon"
library="system"
name="indeterminate"
></sl-icon>
`
: ''}
</span>
<div part="label" class="checkbox__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>
`;
}
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -23,6 +23,7 @@ describe('<sl-checkbox>', () => {
expect(el.checked).to.be.false;
expect(el.indeterminate).to.be.false;
expect(el.defaultChecked).to.be.false;
expect(el.helpText).to.equal('');
});
it('should have title if title attribute is set', async () => {