mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
backport 1800, 1860
This commit is contained in:
@@ -88,6 +88,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
|
||||
<wa-checkbox help-text="What should the user know about the checkbox?">Label</wa-checkbox>
|
||||
```
|
||||
|
||||
````jsx:react
|
||||
import WaCheckbox from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
const App = () => <WaCheckbox help-text="What should the user know about the switch?">Label</WaCheckbox>;
|
||||
|
||||
### 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.
|
||||
@@ -122,7 +134,7 @@ Use the `setCustomValidity()` method to set a custom validation message. This wi
|
||||
});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
````
|
||||
|
||||
```jsx:react
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
@@ -74,6 +74,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
|
||||
<wa-switch help-text="What should the user know about the switch?">Label</wa-switch>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import WaSwitch from '@shoelace-style/shoelace/dist/react/checkbox';
|
||||
|
||||
const App = () => <WaSwitch help-text="What should the user know about the switch?">Label</WaSwitch>;
|
||||
```
|
||||
|
||||
### Custom Styles
|
||||
|
||||
Use the available custom properties to change how the switch is styled.
|
||||
|
||||
@@ -24,6 +24,8 @@ New versions of Web Awesome are released as-needed and generally occur when a cr
|
||||
## Next
|
||||
|
||||
- Added the Arabic translation [#1852]
|
||||
- Added help text to `<sl-checkbox>` [#1860]
|
||||
- Added help text to `<sl-switch>` [#1800]
|
||||
- Fixed a bug in `<sl-option>` that caused HTML tags to be included in `getTextLabel()`
|
||||
|
||||
## 2.13.1
|
||||
|
||||
@@ -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 { WebAwesomeFormControl } from '../../internal/webawesome-element.js
|
||||
* @dependency wa-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 wa-blur - Emitted when the checkbox loses focus.
|
||||
* @event wa-change - Emitted when the checked state changes.
|
||||
@@ -35,6 +37,7 @@ import type { WebAwesomeFormControl } from '../../internal/webawesome-element.js
|
||||
* @csspart checked-icon - The checked icon, a `<wa-icon>` element.
|
||||
* @csspart indeterminate-icon - The indeterminate icon, a `<wa-icon>` element.
|
||||
* @csspart label - The container that wraps the checkbox's label.
|
||||
* @csspart form-control-help-text - The help text's wrapper.
|
||||
*
|
||||
* @cssproperty --background - The checkbox's background styles.
|
||||
* @cssproperty --background-checked - The checkbox's background styles when checked.
|
||||
@@ -56,6 +59,7 @@ export default class WaCheckbox extends WebAwesomeElement implements WebAwesomeF
|
||||
defaultValue: (control: WaCheckbox) => control.defaultChecked,
|
||||
setValue: (control: WaCheckbox, checked: boolean) => (control.checked = checked)
|
||||
});
|
||||
private readonly hasSlotController = new HasSlotController(this, 'help-text');
|
||||
|
||||
@query('input[type="checkbox"]') input: HTMLInputElement;
|
||||
|
||||
@@ -97,6 +101,9 @@ export default class WaCheckbox extends WebAwesomeElement implements WebAwesomeF
|
||||
/** 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;
|
||||
@@ -189,75 +196,93 @@ export default class WaCheckbox extends WebAwesomeElement implements WebAwesomeF
|
||||
}
|
||||
|
||||
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`
|
||||
<wa-icon
|
||||
part="checked-icon"
|
||||
class="checkbox__checked-icon"
|
||||
library="system"
|
||||
name="check"
|
||||
variant="solid"
|
||||
></wa-icon>
|
||||
`
|
||||
: ''}
|
||||
${!this.checked && this.indeterminate
|
||||
? html`
|
||||
<wa-icon
|
||||
part="indeterminate-icon"
|
||||
class="checkbox__indeterminate-icon"
|
||||
library="system"
|
||||
name="minus"
|
||||
variant="solid"
|
||||
></wa-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'}
|
||||
aria-describedby="help-text"
|
||||
@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`
|
||||
<wa-icon part="checked-icon" class="checkbox__checked-icon" library="system" name="check"></wa-icon>
|
||||
`
|
||||
: ''}
|
||||
${!this.checked && this.indeterminate
|
||||
? html`
|
||||
<wa-icon
|
||||
part="indeterminate-icon"
|
||||
class="checkbox__indeterminate-icon"
|
||||
library="system"
|
||||
name="indeterminate"
|
||||
></wa-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>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
--background: var(--wa-form-controls-background);
|
||||
|
||||
@@ -23,6 +23,7 @@ describe('<wa-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 () => {
|
||||
|
||||
@@ -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';
|
||||
@@ -18,6 +19,7 @@ import type { WebAwesomeFormControl } from '../../internal/webawesome-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 wa-blur - Emitted when the control loses focus.
|
||||
* @event wa-change - Emitted when the control's checked state changes.
|
||||
@@ -29,6 +31,7 @@ import type { WebAwesomeFormControl } from '../../internal/webawesome-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 --background - The switch's background styles.
|
||||
* @cssproperty --background-checked - The switch's background styles when checked.
|
||||
@@ -52,6 +55,7 @@ export default class WaSwitch extends WebAwesomeElement implements WebAwesomeFor
|
||||
defaultValue: (control: WaSwitch) => control.defaultChecked,
|
||||
setValue: (control: WaSwitch, 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 WaSwitch extends WebAwesomeElement implements WebAwesomeFor
|
||||
/** 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;
|
||||
@@ -189,46 +196,69 @@ export default class WaSwitch extends WebAwesomeElement implements WebAwesomeFor
|
||||
}
|
||||
|
||||
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}
|
||||
/>
|
||||
<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>
|
||||
<span part="control" class="switch__control">
|
||||
<span part="thumb" class="switch__thumb"></span>
|
||||
</span>
|
||||
|
||||
<div part="label" class="switch__label">
|
||||
<slot></slot>
|
||||
<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>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
--background: var(--wa-form-controls-resting-color);
|
||||
|
||||
@@ -21,6 +21,7 @@ describe('<wa-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 () => {
|
||||
|
||||
Reference in New Issue
Block a user