mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
refactor styles and simplify custom states (#1016)
This commit is contained in:
@@ -21,7 +21,7 @@ import styles from './{{ tagWithoutPrefix tag }}.css';
|
||||
*/
|
||||
@customElement("{{ tag }}")
|
||||
export default class {{ properCase tag }} extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
/** An example attribute. */
|
||||
@property() attr = 'example';
|
||||
|
||||
@@ -28,7 +28,7 @@ import styles from './animated-image.css';
|
||||
*/
|
||||
@customElement('wa-animated-image')
|
||||
export default class WaAnimatedImage extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
@query('.animated') animatedImage: HTMLImageElement;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { animations } from './animations.js';
|
||||
*/
|
||||
@customElement('wa-animation')
|
||||
export default class WaAnimation extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private animation?: Animation;
|
||||
private hasStarted = false;
|
||||
|
||||
@@ -29,7 +29,7 @@ import styles from './avatar.css';
|
||||
*/
|
||||
@customElement('wa-avatar')
|
||||
export default class WaAvatar extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
@state() private hasError = false;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import styles from './badge.css';
|
||||
*/
|
||||
@customElement('wa-badge')
|
||||
export default class WaBadge extends WebAwesomeElement {
|
||||
static shadowStyle = [variantStyles, appearanceStyles, styles];
|
||||
static css = [variantStyles, appearanceStyles, styles];
|
||||
|
||||
/** The badge's theme variant. Defaults to `brand` if not within another element with a variant. */
|
||||
@property({ reflect: true }) variant: 'brand' | 'neutral' | 'success' | 'warning' | 'danger' = 'brand';
|
||||
|
||||
@@ -24,7 +24,7 @@ import styles from './breadcrumb-item.css';
|
||||
*/
|
||||
@customElement('wa-breadcrumb-item')
|
||||
export default class WaBreadcrumbItem extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import styles from './breadcrumb.css';
|
||||
*/
|
||||
@customElement('wa-breadcrumb')
|
||||
export default class WaBreadcrumb extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private separatorDir = this.localize.dir();
|
||||
|
||||
@@ -20,7 +20,7 @@ import styles from './button-group.css';
|
||||
*/
|
||||
@customElement('wa-button-group')
|
||||
export default class WaButtonGroup extends WebAwesomeElement {
|
||||
static shadowStyle = [sizeStyles, variantStyles, styles];
|
||||
static css = [sizeStyles, variantStyles, styles];
|
||||
|
||||
@query('slot') defaultSlot: HTMLSlotElement;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ import styles from './button.css';
|
||||
*/
|
||||
@customElement('wa-button')
|
||||
export default class WaButton extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = [styles, variantStyles, sizeStyles, appearanceStyles];
|
||||
static css = [styles, variantStyles, sizeStyles, appearanceStyles];
|
||||
|
||||
static get validators() {
|
||||
return [...super.validators, MirrorValidator()];
|
||||
|
||||
@@ -24,7 +24,7 @@ import styles from './callout.css';
|
||||
*/
|
||||
@customElement('wa-callout')
|
||||
export default class WaCallout extends WebAwesomeElement {
|
||||
static shadowStyle = [variantStyles, appearanceStyles, sizeStyles, styles];
|
||||
static css = [variantStyles, appearanceStyles, sizeStyles, styles];
|
||||
|
||||
/** The callout's theme variant. Defaults to `brand` if not within another element with a variant. */
|
||||
@property({ reflect: true }) variant: 'brand' | 'neutral' | 'success' | 'warning' | 'danger' | 'brand' = 'brand';
|
||||
|
||||
@@ -30,7 +30,7 @@ import styles from './card.css';
|
||||
*/
|
||||
@customElement('wa-card')
|
||||
export default class WaCard extends WebAwesomeElement {
|
||||
static shadowStyle = [sizeStyles, appearanceStyles, styles];
|
||||
static css = [sizeStyles, appearanceStyles, styles];
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header', 'media');
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import styles from './carousel-item.css';
|
||||
*/
|
||||
@customElement('wa-carousel-item')
|
||||
export default class WaCarouselItem extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
@@ -52,7 +52,7 @@ import styles from './carousel.css';
|
||||
*/
|
||||
@customElement('wa-carousel')
|
||||
export default class WaCarousel extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
/** When set, allows the user to navigate the carousel in the same direction indefinitely. */
|
||||
@property({ type: Boolean, reflect: true }) loop = false;
|
||||
|
||||
@@ -199,17 +199,17 @@ describe('<wa-checkbox>', () => {
|
||||
|
||||
expect(checkbox.checkValidity()).to.be.false;
|
||||
expect(checkbox.checkValidity()).to.be.false;
|
||||
expect(checkbox.hasCustomState('invalid')).to.be.true;
|
||||
expect(checkbox.hasCustomState('valid')).to.be.false;
|
||||
expect(checkbox.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(checkbox.hasCustomState('user-valid')).to.be.false;
|
||||
expect(checkbox.customStates.has('invalid')).to.be.true;
|
||||
expect(checkbox.customStates.has('valid')).to.be.false;
|
||||
expect(checkbox.customStates.has('user-invalid')).to.be.true;
|
||||
expect(checkbox.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
await clickOnElement(checkbox);
|
||||
await checkbox.updateComplete;
|
||||
await aTimeout(0);
|
||||
|
||||
expect(checkbox.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(checkbox.hasCustomState('user-valid')).to.be.false;
|
||||
expect(checkbox.customStates.has('user-invalid')).to.be.true;
|
||||
expect(checkbox.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should be invalid when required and unchecked', async () => {
|
||||
@@ -244,12 +244,12 @@ describe('<wa-checkbox>', () => {
|
||||
`);
|
||||
const checkbox = el.querySelector<WaCheckbox>('wa-checkbox')!;
|
||||
|
||||
expect(checkbox.hasCustomState('required')).to.be.true;
|
||||
expect(checkbox.hasCustomState('optional')).to.be.false;
|
||||
expect(checkbox.hasCustomState('invalid')).to.be.true;
|
||||
expect(checkbox.hasCustomState('valid')).to.be.false;
|
||||
expect(checkbox.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(checkbox.hasCustomState('user-valid')).to.be.false;
|
||||
expect(checkbox.customStates.has('required')).to.be.true;
|
||||
expect(checkbox.customStates.has('optional')).to.be.false;
|
||||
expect(checkbox.customStates.has('invalid')).to.be.true;
|
||||
expect(checkbox.customStates.has('valid')).to.be.false;
|
||||
expect(checkbox.customStates.has('user-invalid')).to.be.false;
|
||||
expect(checkbox.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ import styles from './checkbox.css';
|
||||
*/
|
||||
@customElement('wa-checkbox')
|
||||
export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = [formControlStyles, sizeStyles, styles];
|
||||
static css = [formControlStyles, sizeStyles, styles];
|
||||
|
||||
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };
|
||||
|
||||
@@ -156,14 +156,14 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
|
||||
this.input.indeterminate = this.indeterminate; // force a sync update
|
||||
}
|
||||
|
||||
this.toggleCustomState('checked', this.checked);
|
||||
this.toggleCustomState('indeterminate', this.indeterminate);
|
||||
this.customStates.set('checked', this.checked);
|
||||
this.customStates.set('indeterminate', this.indeterminate);
|
||||
this.updateValidity();
|
||||
}
|
||||
|
||||
@watch('disabled')
|
||||
handleDisabledChange() {
|
||||
this.toggleCustomState('disabled', this.disabled);
|
||||
this.customStates.set('disabled', this.disabled);
|
||||
}
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
||||
|
||||
@@ -501,12 +501,12 @@ describe('<wa-color-picker>', () => {
|
||||
const grid = el.shadowRoot!.querySelector('[part~="grid"]')!;
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.false;
|
||||
expect(el.hasCustomState('valid')).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.false;
|
||||
expect(el.customStates.has('valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
await clickOnElement(trigger);
|
||||
await aTimeout(500);
|
||||
@@ -514,8 +514,8 @@ describe('<wa-color-picker>', () => {
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.true;
|
||||
});
|
||||
|
||||
it('should receive the correct validation attributes ("states") when invalid', async () => {
|
||||
@@ -523,12 +523,12 @@ describe('<wa-color-picker>', () => {
|
||||
const trigger = el.shadowRoot!.querySelector('[part~="trigger"]')!;
|
||||
const grid = el.shadowRoot!.querySelector('[part~="grid"]')!;
|
||||
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.true;
|
||||
expect(el.hasCustomState('valid')).to.be.false;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.true;
|
||||
expect(el.customStates.has('valid')).to.be.false;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
await clickOnElement(trigger);
|
||||
await aTimeout(500);
|
||||
@@ -536,8 +536,8 @@ describe('<wa-color-picker>', () => {
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ declare const EyeDropper: EyeDropperConstructor;
|
||||
*/
|
||||
@customElement('wa-color-picker')
|
||||
export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = [visuallyHidden, sizeStyles, formControlStyles, styles];
|
||||
static css = [visuallyHidden, sizeStyles, formControlStyles, styles];
|
||||
|
||||
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import styles from './comparison.css';
|
||||
*/
|
||||
@customElement('wa-comparison')
|
||||
export default class WaComparison extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@@ -55,12 +55,12 @@ export default class WaComparison extends WebAwesomeElement {
|
||||
|
||||
drag(this, {
|
||||
onMove: x => {
|
||||
this.toggleCustomState('dragging', true);
|
||||
this.customStates.set('dragging', true);
|
||||
this.position = parseFloat(clamp((x / width) * 100, 0, 100).toFixed(2));
|
||||
if (isRtl) this.position = 100 - this.position;
|
||||
},
|
||||
onStop: () => {
|
||||
this.toggleCustomState('dragging', false);
|
||||
this.customStates.set('dragging', false);
|
||||
},
|
||||
initialEvent: event,
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@ import styles from './copy-button.css';
|
||||
*/
|
||||
@customElement('wa-copy-button')
|
||||
export default class WaCopyButton extends WebAwesomeElement {
|
||||
static shadowStyle = [visuallyHidden, styles];
|
||||
static css = [visuallyHidden, styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import styles from './details.css';
|
||||
*/
|
||||
@customElement('wa-details')
|
||||
export default class WaDetails extends WebAwesomeElement {
|
||||
static shadowStyle = [appearanceStyles, styles];
|
||||
static css = [appearanceStyles, styles];
|
||||
|
||||
private detailsObserver: MutationObserver;
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@@ -54,7 +54,7 @@ import styles from './dialog.css';
|
||||
*/
|
||||
@customElement('wa-dialog')
|
||||
export default class WaDialog extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header-actions', 'label');
|
||||
|
||||
@@ -15,7 +15,7 @@ import styles from './divider.css';
|
||||
*/
|
||||
@customElement('wa-divider')
|
||||
export default class WaDivider extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
/** Sets the divider's orientation. */
|
||||
@property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'horizontal';
|
||||
|
||||
@@ -59,7 +59,7 @@ import styles from './drawer.css';
|
||||
*/
|
||||
@customElement('wa-drawer')
|
||||
export default class WaDrawer extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header-actions', 'label');
|
||||
|
||||
@@ -44,7 +44,7 @@ import styles from './dropdown.css';
|
||||
*/
|
||||
@customElement('wa-dropdown')
|
||||
export default class WaDropdown extends WebAwesomeElement {
|
||||
static shadowStyle = [sizeStyles, styles];
|
||||
static css = [sizeStyles, styles];
|
||||
|
||||
@query('.dropdown') popup: WaPopup;
|
||||
@query('#trigger') trigger: HTMLSlotElement;
|
||||
|
||||
@@ -26,7 +26,7 @@ import styles from './icon-button.css';
|
||||
*/
|
||||
@customElement('wa-icon-button')
|
||||
export default class WaIconButton extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
@query('.icon-button') button: HTMLButtonElement | HTMLLinkElement;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ interface IconSource {
|
||||
*/
|
||||
@customElement('wa-icon')
|
||||
export default class WaIcon extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
@state() private svg: SVGElement | HTMLTemplateResult | null = null;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { requestInclude } from './request.js';
|
||||
*/
|
||||
@customElement('wa-include')
|
||||
export default class WaInclude extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
/**
|
||||
* The location of the HTML file to include. Be sure you trust the content you are including as it will be executed as
|
||||
|
||||
@@ -108,12 +108,12 @@ describe('<wa-input>', () => {
|
||||
const el = await fixture<WaInput>(html` <wa-input required value="a"></wa-input> `);
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.false;
|
||||
expect(el.hasCustomState('valid')).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.false;
|
||||
expect(el.customStates.has('valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
el.focus();
|
||||
await el.updateComplete;
|
||||
@@ -123,19 +123,19 @@ describe('<wa-input>', () => {
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.true;
|
||||
});
|
||||
|
||||
it('should receive the correct validation attributes ("states") when invalid', async () => {
|
||||
const el = await fixture<WaInput>(html` <wa-input required></wa-input> `);
|
||||
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.true;
|
||||
expect(el.hasCustomState('valid')).to.be.false;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.true;
|
||||
expect(el.customStates.has('valid')).to.be.false;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
el.focus();
|
||||
await el.updateComplete;
|
||||
@@ -145,20 +145,20 @@ describe('<wa-input>', () => {
|
||||
el.blur();
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('user-invalid')).to.be.true;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
|
||||
const el = await fixture<HTMLFormElement>(html` <form novalidate><wa-input required></wa-input></form> `);
|
||||
const input = el.querySelector<WaInput>('wa-input')!;
|
||||
|
||||
expect(input.hasCustomState('required')).to.be.true;
|
||||
expect(input.hasCustomState('optional')).to.be.false;
|
||||
expect(input.hasCustomState('invalid')).to.be.true;
|
||||
expect(input.hasCustomState('valid')).to.be.false;
|
||||
expect(input.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(input.hasCustomState('user-valid')).to.be.false;
|
||||
expect(input.customStates.has('required')).to.be.true;
|
||||
expect(input.customStates.has('optional')).to.be.false;
|
||||
expect(input.customStates.has('invalid')).to.be.true;
|
||||
expect(input.customStates.has('valid')).to.be.false;
|
||||
expect(input.customStates.has('user-invalid')).to.be.false;
|
||||
expect(input.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -215,10 +215,10 @@ describe('<wa-input>', () => {
|
||||
await input.updateComplete;
|
||||
|
||||
expect(input.checkValidity()).to.be.false;
|
||||
expect(input.hasCustomState('invalid')).to.be.true;
|
||||
expect(input.hasCustomState('valid')).to.be.false;
|
||||
expect(input.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(input.hasCustomState('user-valid')).to.be.false;
|
||||
expect(input.customStates.has('invalid')).to.be.true;
|
||||
expect(input.customStates.has('valid')).to.be.false;
|
||||
expect(input.customStates.has('user-invalid')).to.be.false;
|
||||
expect(input.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
input.focus();
|
||||
await sendKeys({ type: 'test' });
|
||||
@@ -226,8 +226,8 @@ describe('<wa-input>', () => {
|
||||
input.blur();
|
||||
await input.updateComplete;
|
||||
|
||||
expect(input.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(input.hasCustomState('user-valid')).to.be.false;
|
||||
expect(input.customStates.has('user-invalid')).to.be.true;
|
||||
expect(input.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should be present in form data when using the form attribute and located outside of a <form>', async () => {
|
||||
|
||||
@@ -57,7 +57,7 @@ import styles from './input.css';
|
||||
*/
|
||||
@customElement('wa-input')
|
||||
export default class WaInput extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = [sizeStyles, appearanceStyles, formControlStyles, styles];
|
||||
static css = [sizeStyles, appearanceStyles, formControlStyles, styles];
|
||||
|
||||
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };
|
||||
|
||||
@@ -300,7 +300,7 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has('value')) {
|
||||
this.toggleCustomState('blank', !this.value);
|
||||
this.customStates.set('blank', !this.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ import { SubmenuController } from './submenu-controller.js';
|
||||
*/
|
||||
@customElement('wa-menu-item')
|
||||
export default class WaMenuItem extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@@ -133,7 +133,7 @@ export default class WaMenuItem extends WebAwesomeElement {
|
||||
this.dispatchEvent(new Event('slotchange', { bubbles: true, composed: false, cancelable: false }));
|
||||
}
|
||||
|
||||
this.toggleCustomState('has-submenu', this.isSubmenu());
|
||||
this.customStates.set('has-submenu', this.isSubmenu());
|
||||
}
|
||||
|
||||
private handleHostClick = (event: MouseEvent) => {
|
||||
@@ -201,7 +201,7 @@ export default class WaMenuItem extends WebAwesomeElement {
|
||||
render() {
|
||||
const isRtl = this.hasUpdated ? this.localize.dir() === 'rtl' : this.dir === 'rtl';
|
||||
const isSubmenuExpanded = this.submenuController.isExpanded();
|
||||
this.toggleCustomState('submenu-expanded', isSubmenuExpanded);
|
||||
this.customStates.set('submenu-expanded', isSubmenuExpanded);
|
||||
|
||||
this.internals.ariaHasPopup = this.isSubmenu() + '';
|
||||
this.internals.ariaExpanded = isSubmenuExpanded + '';
|
||||
|
||||
@@ -13,7 +13,7 @@ import styles from './menu-label.css';
|
||||
*/
|
||||
@customElement('wa-menu-label')
|
||||
export default class WaMenuLabel extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
render() {
|
||||
return html`<slot></slot>`;
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface MenuSelectEventDetail {
|
||||
*/
|
||||
@customElement('wa-menu')
|
||||
export default class WaMenu extends WebAwesomeElement {
|
||||
static shadowStyle = [sizeStyles, styles];
|
||||
static css = [sizeStyles, styles];
|
||||
|
||||
/** The component's size. */
|
||||
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
|
||||
|
||||
@@ -17,7 +17,7 @@ import styles from './mutation-observer.css';
|
||||
*/
|
||||
@customElement('wa-mutation-observer')
|
||||
export default class WaMutationObserver extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private mutationObserver: MutationObserver;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import styles from './option.css';
|
||||
*/
|
||||
@customElement('wa-option')
|
||||
export default class WaOption extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
// @ts-expect-error - Controller is currently unused
|
||||
private readonly localize = new LocalizeController(this);
|
||||
@@ -130,9 +130,9 @@ export default class WaOption extends WebAwesomeElement {
|
||||
// We need this because Safari doesn't honor :hover styles while dragging
|
||||
// Test case: https://codepen.io/leaverou/pen/VYZOOjy
|
||||
if (event.type === 'mouseenter') {
|
||||
this.toggleCustomState('hover', true);
|
||||
this.customStates.set('hover', true);
|
||||
} else if (event.type === 'mouseleave') {
|
||||
this.toggleCustomState('hover', false);
|
||||
this.customStates.set('hover', false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,7 +145,7 @@ export default class WaOption extends WebAwesomeElement {
|
||||
|
||||
if (changedProperties.has('selected')) {
|
||||
this.setAttribute('aria-selected', this.selected ? 'true' : 'false');
|
||||
this.toggleCustomState('selected', this.selected);
|
||||
this.customStates.set('selected', this.selected);
|
||||
}
|
||||
|
||||
if (changedProperties.has('value')) {
|
||||
@@ -165,7 +165,7 @@ export default class WaOption extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
if (changedProperties.has('current')) {
|
||||
this.toggleCustomState('current', this.current);
|
||||
this.customStates.set('current', this.current);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ function toLength(px: number | string): string {
|
||||
*/
|
||||
@customElement('wa-page')
|
||||
export default class WaPage extends WebAwesomeElement {
|
||||
static shadowStyle = [visuallyHidden, styles];
|
||||
static css = [visuallyHidden, styles];
|
||||
|
||||
private headerResizeObserver = this.slotResizeObserver('header');
|
||||
private subheaderResizeObserver = this.slotResizeObserver('subheader');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { PropertyValues } from 'lit';
|
||||
import { html } from 'lit';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
@@ -40,10 +41,12 @@ const openPopovers = new Set<WaPopover>();
|
||||
* @cssproperty [--max-width=25rem] - The maximum width of the popover's body content.
|
||||
* @cssproperty [--show-duration=100ms] - The speed of the show animation.
|
||||
* @cssproperty [--hide-duration=100ms] - The speed of the hide animation.
|
||||
*
|
||||
* @cssstate open - Applied when the popover is open.
|
||||
*/
|
||||
@customElement('wa-popover')
|
||||
export default class WaPopover extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
static dependencies = { 'wa-popup': WaPopup };
|
||||
|
||||
@query('dialog') dialog: HTMLDialogElement;
|
||||
@@ -110,6 +113,12 @@ export default class WaPopover extends WebAwesomeElement {
|
||||
}
|
||||
}
|
||||
|
||||
updated(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has('open')) {
|
||||
this.customStates.set('open', this.open);
|
||||
}
|
||||
}
|
||||
|
||||
private handleAnchorClick = () => {
|
||||
// Clicks on the anchor should toggle the popover
|
||||
this.open = !this.open;
|
||||
|
||||
@@ -68,7 +68,7 @@ const SUPPORTS_POPOVER = globalThis?.HTMLElement?.prototype.hasOwnProperty('popo
|
||||
*/
|
||||
@customElement('wa-popup')
|
||||
export default class WaPopup extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private anchorEl: Element | VirtualElement | null;
|
||||
private cleanup: ReturnType<typeof autoUpdate> | undefined;
|
||||
|
||||
@@ -24,7 +24,7 @@ import styles from './progress-bar.css';
|
||||
*/
|
||||
@customElement('wa-progress-bar')
|
||||
export default class WaProgressBar extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
/** The current progress as a percentage, 0 to 100. */
|
||||
|
||||
@@ -25,7 +25,7 @@ import styles from './progress-ring.css';
|
||||
*/
|
||||
@customElement('wa-progress-ring')
|
||||
export default class WaProgressRing extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ let QrCreator: _QrCreator.default;
|
||||
*/
|
||||
@customElement('wa-qr-code')
|
||||
export default class WaQrCode extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
@query('canvas') canvas: HTMLElement;
|
||||
|
||||
|
||||
@@ -99,12 +99,12 @@ describe('<wa-radio-group>', () => {
|
||||
const secondRadio = radioGroup.querySelectorAll('wa-radio')[1];
|
||||
|
||||
expect(radioGroup.checkValidity()).to.be.true;
|
||||
expect(radioGroup.hasCustomState('required')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('optional')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('valid')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('required')).to.be.true;
|
||||
expect(radioGroup.customStates.has('optional')).to.be.false;
|
||||
expect(radioGroup.customStates.has('invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('valid')).to.be.true;
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
// TODO: Go back to clickOnElement when we can determine why CI is not cleaning up elements.
|
||||
// await clickOnElement(secondRadio);
|
||||
@@ -113,8 +113,8 @@ describe('<wa-radio-group>', () => {
|
||||
await radioGroup.updateComplete
|
||||
|
||||
expect(radioGroup.checkValidity()).to.be.true;
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.true;
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.true;
|
||||
});
|
||||
|
||||
it('should receive the correct validation attributes ("states") when invalid', async () => {
|
||||
@@ -126,12 +126,12 @@ describe('<wa-radio-group>', () => {
|
||||
`);
|
||||
const secondRadio = radioGroup.querySelectorAll('wa-radio')[1];
|
||||
|
||||
expect(radioGroup.hasCustomState('required')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('optional')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('invalid')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('valid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('required')).to.be.true;
|
||||
expect(radioGroup.customStates.has('optional')).to.be.false;
|
||||
expect(radioGroup.customStates.has('invalid')).to.be.true;
|
||||
expect(radioGroup.customStates.has('valid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
// TODO: Go back to clickOnElement when we can determine why CI is not cleaning up elements.
|
||||
// await clickOnElement(secondRadio);
|
||||
@@ -140,8 +140,8 @@ describe('<wa-radio-group>', () => {
|
||||
radioGroup.value = '';
|
||||
await radioGroup.updateComplete;
|
||||
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.true;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
|
||||
@@ -155,12 +155,12 @@ describe('<wa-radio-group>', () => {
|
||||
`);
|
||||
const radioGroup = el.querySelector<WaRadioGroup>('wa-radio-group')!;
|
||||
|
||||
expect(radioGroup.hasCustomState('required')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('optional')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('invalid')).to.be.true;
|
||||
expect(radioGroup.hasCustomState('valid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(radioGroup.hasCustomState('user-valid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('required')).to.be.true;
|
||||
expect(radioGroup.customStates.has('optional')).to.be.false;
|
||||
expect(radioGroup.customStates.has('invalid')).to.be.true;
|
||||
expect(radioGroup.customStates.has('valid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-invalid')).to.be.false;
|
||||
expect(radioGroup.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should show a constraint validation error when setCustomValidity() is called', async () => {
|
||||
|
||||
@@ -37,7 +37,7 @@ import styles from './radio-group.css';
|
||||
*/
|
||||
@customElement('wa-radio-group')
|
||||
export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = [sizeStyles, formControlStyles, styles];
|
||||
static css = [sizeStyles, formControlStyles, styles];
|
||||
|
||||
static get validators() {
|
||||
const validators = isServer
|
||||
|
||||
@@ -40,7 +40,7 @@ import styles from './radio.css';
|
||||
*/
|
||||
@customElement('wa-radio')
|
||||
export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = [formControlStyles, sizeStyles, styles];
|
||||
static css = [formControlStyles, sizeStyles, styles];
|
||||
|
||||
@state() checked = false;
|
||||
|
||||
@@ -86,13 +86,13 @@ export default class WaRadio extends WebAwesomeFormAssociatedElement {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has('checked')) {
|
||||
this.toggleCustomState('checked', this.checked);
|
||||
this.customStates.set('checked', this.checked);
|
||||
this.setAttribute('aria-checked', this.checked ? 'true' : 'false');
|
||||
this.tabIndex = this.checked ? 0 : -1;
|
||||
}
|
||||
|
||||
if (changedProperties.has('disabled')) {
|
||||
this.toggleCustomState('disabled', this.disabled);
|
||||
this.customStates.set('disabled', this.disabled);
|
||||
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import styles from './rating.css';
|
||||
*/
|
||||
@customElement('wa-rating')
|
||||
export default class WaRating extends WebAwesomeElement {
|
||||
static shadowStyle = [sizeStyles, styles];
|
||||
static css = [sizeStyles, styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import styles from './resize-observer.css';
|
||||
*/
|
||||
@customElement('wa-resize-observer')
|
||||
export default class WaResizeObserver extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private resizeObserver: ResizeObserver;
|
||||
private observedElements: HTMLElement[] = [];
|
||||
|
||||
@@ -20,7 +20,7 @@ import styles from './scroller.css';
|
||||
*/
|
||||
@customElement('wa-scroller')
|
||||
export default class WaScroller extends WebAwesomeElement {
|
||||
static shadowStyle = [styles];
|
||||
static css = [styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private resizeObserver = new ResizeObserver(() => this.updateScroll());
|
||||
|
||||
@@ -331,12 +331,12 @@ describe('<wa-select>', () => {
|
||||
const secondOption = el.querySelectorAll('wa-option')[1];
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.false;
|
||||
expect(el.hasCustomState('valid')).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.false;
|
||||
expect(el.customStates.has('valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
await el.show();
|
||||
await clickOnElement(secondOption);
|
||||
@@ -345,8 +345,8 @@ describe('<wa-select>', () => {
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.true;
|
||||
});
|
||||
|
||||
it('should receive the correct validation attributes ("states") when invalid', async () => {
|
||||
@@ -359,12 +359,12 @@ describe('<wa-select>', () => {
|
||||
`);
|
||||
const secondOption = el.querySelectorAll('wa-option')[1];
|
||||
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.true;
|
||||
expect(el.hasCustomState('valid')).to.be.false;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.true;
|
||||
expect(el.customStates.has('valid')).to.be.false;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
await el.show();
|
||||
await clickOnElement(secondOption);
|
||||
@@ -373,8 +373,8 @@ describe('<wa-select>', () => {
|
||||
el.blur();
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('user-invalid')).to.be.true;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
|
||||
@@ -389,12 +389,12 @@ describe('<wa-select>', () => {
|
||||
`);
|
||||
const select = el.querySelector<WaSelect>('wa-select')!;
|
||||
|
||||
expect(select.hasCustomState('required')).to.be.true;
|
||||
expect(select.hasCustomState('optional')).to.be.false;
|
||||
expect(select.hasCustomState('invalid')).to.be.true;
|
||||
expect(select.hasCustomState('valid')).to.be.false;
|
||||
expect(select.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(select.hasCustomState('user-valid')).to.be.false;
|
||||
expect(select.customStates.has('required')).to.be.true;
|
||||
expect(select.customStates.has('optional')).to.be.false;
|
||||
expect(select.customStates.has('invalid')).to.be.true;
|
||||
expect(select.customStates.has('valid')).to.be.false;
|
||||
expect(select.customStates.has('user-invalid')).to.be.false;
|
||||
expect(select.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ import styles from './select.css';
|
||||
*/
|
||||
@customElement('wa-select')
|
||||
export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = [appearanceStyles, formControlStyles, sizeStyles, styles];
|
||||
static css = [appearanceStyles, formControlStyles, sizeStyles, styles];
|
||||
|
||||
static get validators() {
|
||||
const validators = isServer
|
||||
@@ -740,7 +740,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has('value')) {
|
||||
this.toggleCustomState('blank', !this.value);
|
||||
this.customStates.set('blank', !this.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import styles from './skeleton.css';
|
||||
*/
|
||||
@customElement('wa-skeleton')
|
||||
export default class WaSkeleton extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
/** Determines which effect the skeleton will use. */
|
||||
@property({ reflect: true }) effect: 'pulse' | 'sheen' | 'none' = 'none';
|
||||
|
||||
@@ -133,18 +133,18 @@ describe('<wa-slider>', () => {
|
||||
await slider.updateComplete;
|
||||
|
||||
expect(slider.checkValidity()).to.be.false;
|
||||
expect(slider.hasCustomState('invalid')).to.be.true;
|
||||
expect(slider.hasCustomState('valid')).to.be.false;
|
||||
expect(slider.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(slider.hasCustomState('user-valid')).to.be.false;
|
||||
expect(slider.customStates.has('invalid')).to.be.true;
|
||||
expect(slider.customStates.has('valid')).to.be.false;
|
||||
expect(slider.customStates.has('user-invalid')).to.be.false;
|
||||
expect(slider.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
await clickOnElement(slider);
|
||||
await slider.updateComplete;
|
||||
slider.blur();
|
||||
await slider.updateComplete;
|
||||
|
||||
expect(slider.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(slider.hasCustomState('user-valid')).to.be.false;
|
||||
expect(slider.customStates.has('user-invalid')).to.be.true;
|
||||
expect(slider.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
|
||||
@@ -154,10 +154,10 @@ describe('<wa-slider>', () => {
|
||||
slider.setCustomValidity('Invalid value');
|
||||
await slider.updateComplete;
|
||||
|
||||
expect(slider.hasCustomState('invalid')).to.be.true;
|
||||
expect(slider.hasCustomState('valid')).to.be.false;
|
||||
expect(slider.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(slider.hasCustomState('user-valid')).to.be.false;
|
||||
expect(slider.customStates.has('invalid')).to.be.true;
|
||||
expect(slider.customStates.has('valid')).to.be.false;
|
||||
expect(slider.customStates.has('user-invalid')).to.be.false;
|
||||
expect(slider.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should be present in form data when using the form attribute and located outside of a <form>', async () => {
|
||||
|
||||
@@ -45,7 +45,7 @@ import styles from './slider.css';
|
||||
*/
|
||||
@customElement('wa-slider')
|
||||
export default class WaSlider extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = [formControlStyles, styles];
|
||||
static css = [formControlStyles, styles];
|
||||
|
||||
static get validators() {
|
||||
return [...super.validators, MirrorValidator()];
|
||||
|
||||
@@ -19,7 +19,7 @@ import styles from './spinner.css';
|
||||
*/
|
||||
@customElement('wa-spinner')
|
||||
export default class WaSpinner extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import styles from './split-panel.css';
|
||||
*/
|
||||
@customElement('wa-split-panel')
|
||||
export default class WaSplitPanel extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private cachedPositionInPixels: number;
|
||||
private isCollapsed = false;
|
||||
|
||||
@@ -230,12 +230,12 @@ describe('<wa-switch>', () => {
|
||||
const el = await fixture<HTMLFormElement>(html` <form novalidate><wa-switch required></wa-switch></form> `);
|
||||
const waSwitch = el.querySelector<WaSwitch>('wa-switch')!;
|
||||
|
||||
expect(waSwitch.hasCustomState('required')).to.be.true;
|
||||
expect(waSwitch.hasCustomState('optional')).to.be.false;
|
||||
expect(waSwitch.hasCustomState('invalid')).to.be.true;
|
||||
expect(waSwitch.hasCustomState('valid')).to.be.false;
|
||||
expect(waSwitch.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(waSwitch.hasCustomState('user-valid')).to.be.false;
|
||||
expect(waSwitch.customStates.has('required')).to.be.true;
|
||||
expect(waSwitch.customStates.has('optional')).to.be.false;
|
||||
expect(waSwitch.customStates.has('invalid')).to.be.true;
|
||||
expect(waSwitch.customStates.has('valid')).to.be.false;
|
||||
expect(waSwitch.customStates.has('user-invalid')).to.be.false;
|
||||
expect(waSwitch.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ import styles from './switch.css';
|
||||
@customElement('wa-switch')
|
||||
export default class WaSwitch extends WebAwesomeFormAssociatedElement {
|
||||
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };
|
||||
static shadowStyle = [formControlStyles, sizeStyles, styles];
|
||||
static css = [formControlStyles, sizeStyles, styles];
|
||||
|
||||
static get validators() {
|
||||
return [...super.validators, MirrorValidator()];
|
||||
@@ -170,7 +170,7 @@ export default class WaSwitch extends WebAwesomeFormAssociatedElement {
|
||||
this.input.checked = this.checked; // force a sync update
|
||||
}
|
||||
|
||||
this.toggleCustomState('checked', this.checked);
|
||||
this.customStates.set('checked', this.checked);
|
||||
this.updateValidity();
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ import styles from './tab-group.css';
|
||||
*/
|
||||
@customElement('wa-tab-group')
|
||||
export default class WaTabGroup extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private activeTab?: WaTab;
|
||||
private mutationObserver: MutationObserver;
|
||||
|
||||
@@ -21,7 +21,7 @@ let id = 0;
|
||||
*/
|
||||
@customElement('wa-tab-panel')
|
||||
export default class WaTabPanel extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private readonly attrId = ++id;
|
||||
private readonly componentId = `wa-tab-panel-${this.attrId}`;
|
||||
|
||||
@@ -23,7 +23,7 @@ let id = 0;
|
||||
*/
|
||||
@customElement('wa-tab')
|
||||
export default class WaTab extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
private readonly attrId = ++id;
|
||||
private readonly componentId = `wa-tab-${this.attrId}`;
|
||||
|
||||
@@ -28,7 +28,7 @@ import styles from './tag.css';
|
||||
*/
|
||||
@customElement('wa-tag')
|
||||
export default class WaTag extends WebAwesomeElement {
|
||||
static shadowStyle = [sizeStyles, variantStyles, appearanceStyles, styles];
|
||||
static css = [sizeStyles, variantStyles, appearanceStyles, styles];
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
||||
@@ -144,12 +144,12 @@ describe('<wa-textarea>', () => {
|
||||
const el = await fixture<WaTextarea>(html` <wa-textarea required value="a"></wa-textarea> `);
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.false;
|
||||
expect(el.hasCustomState('valid')).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.false;
|
||||
expect(el.customStates.has('valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
el.focus();
|
||||
await sendKeys({ press: 'b' });
|
||||
@@ -158,19 +158,19 @@ describe('<wa-textarea>', () => {
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.checkValidity()).to.be.true;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.true;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.true;
|
||||
});
|
||||
|
||||
it('should receive the correct validation attributes ("states") when invalid', async () => {
|
||||
const el = await fixture<WaTextarea>(html` <wa-textarea required></wa-textarea> `);
|
||||
|
||||
expect(el.hasCustomState('required')).to.be.true;
|
||||
expect(el.hasCustomState('optional')).to.be.false;
|
||||
expect(el.hasCustomState('invalid')).to.be.true;
|
||||
expect(el.hasCustomState('valid')).to.be.false;
|
||||
expect(el.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('required')).to.be.true;
|
||||
expect(el.customStates.has('optional')).to.be.false;
|
||||
expect(el.customStates.has('invalid')).to.be.true;
|
||||
expect(el.customStates.has('valid')).to.be.false;
|
||||
expect(el.customStates.has('user-invalid')).to.be.false;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
el.focus();
|
||||
await sendKeys({ press: 'a' });
|
||||
@@ -179,8 +179,8 @@ describe('<wa-textarea>', () => {
|
||||
el.blur();
|
||||
await el.updateComplete;
|
||||
|
||||
expect(el.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(el.hasCustomState('user-valid')).to.be.false;
|
||||
expect(el.customStates.has('user-invalid')).to.be.true;
|
||||
expect(el.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should receive validation attributes ("states") even when novalidate is used on the parent form', async () => {
|
||||
@@ -189,12 +189,12 @@ describe('<wa-textarea>', () => {
|
||||
`);
|
||||
const textarea = el.querySelector<WaTextarea>('wa-textarea')!;
|
||||
|
||||
expect(textarea.hasCustomState('required')).to.be.true;
|
||||
expect(textarea.hasCustomState('optional')).to.be.false;
|
||||
expect(textarea.hasCustomState('invalid')).to.be.true;
|
||||
expect(textarea.hasCustomState('valid')).to.be.false;
|
||||
expect(textarea.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(textarea.hasCustomState('user-valid')).to.be.false;
|
||||
expect(textarea.customStates.has('required')).to.be.true;
|
||||
expect(textarea.customStates.has('optional')).to.be.false;
|
||||
expect(textarea.customStates.has('invalid')).to.be.true;
|
||||
expect(textarea.customStates.has('valid')).to.be.false;
|
||||
expect(textarea.customStates.has('user-invalid')).to.be.false;
|
||||
expect(textarea.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -237,10 +237,10 @@ describe('<wa-textarea>', () => {
|
||||
await textarea.updateComplete;
|
||||
|
||||
expect(textarea.checkValidity()).to.be.false;
|
||||
expect(textarea.hasCustomState('invalid')).to.be.true;
|
||||
expect(textarea.hasCustomState('valid')).to.be.false;
|
||||
expect(textarea.hasCustomState('user-invalid')).to.be.false;
|
||||
expect(textarea.hasCustomState('user-valid')).to.be.false;
|
||||
expect(textarea.customStates.has('invalid')).to.be.true;
|
||||
expect(textarea.customStates.has('valid')).to.be.false;
|
||||
expect(textarea.customStates.has('user-invalid')).to.be.false;
|
||||
expect(textarea.customStates.has('user-valid')).to.be.false;
|
||||
|
||||
textarea.focus();
|
||||
await sendKeys({ type: 'test' });
|
||||
@@ -248,8 +248,8 @@ describe('<wa-textarea>', () => {
|
||||
textarea.blur();
|
||||
await textarea.updateComplete;
|
||||
|
||||
expect(textarea.hasCustomState('user-invalid')).to.be.true;
|
||||
expect(textarea.hasCustomState('user-valid')).to.be.false;
|
||||
expect(textarea.customStates.has('user-invalid')).to.be.true;
|
||||
expect(textarea.customStates.has('user-valid')).to.be.false;
|
||||
});
|
||||
|
||||
it('should be present in form data when using the form attribute and located outside of a <form>', async () => {
|
||||
|
||||
@@ -43,7 +43,7 @@ import styles from './textarea.css';
|
||||
*/
|
||||
@customElement('wa-textarea')
|
||||
export default class WaTextarea extends WebAwesomeFormAssociatedElement {
|
||||
static shadowStyle = [formControlStyles, appearanceStyles, sizeStyles, styles];
|
||||
static css = [formControlStyles, appearanceStyles, sizeStyles, styles];
|
||||
|
||||
static get validators() {
|
||||
return [...super.validators, MirrorValidator()];
|
||||
@@ -270,7 +270,7 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has('value')) {
|
||||
this.toggleCustomState('blank', !this.value);
|
||||
this.customStates.set('blank', !this.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ import styles from './tooltip.css';
|
||||
*/
|
||||
@customElement('wa-tooltip')
|
||||
export default class WaTooltip extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
static dependencies = { 'wa-popup': WaPopup };
|
||||
|
||||
private hoverTimeout: number;
|
||||
|
||||
@@ -128,7 +128,7 @@ describe('<wa-tree-item>', () => {
|
||||
await leafItem.updateComplete;
|
||||
|
||||
// Assert
|
||||
expect(leafItem.hasCustomState('selected')).to.be.true;
|
||||
expect(leafItem.customStates.has('selected')).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -150,7 +150,7 @@ describe('<wa-tree-item>', () => {
|
||||
await leafItem.updateComplete;
|
||||
|
||||
// Assert
|
||||
expect(leafItem.hasCustomState('expanded')).to.be.true;
|
||||
expect(leafItem.customStates.has('expanded')).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ import styles from './tree-item.css';
|
||||
*/
|
||||
@customElement('wa-tree-item')
|
||||
export default class WaTreeItem extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
static isTreeItem(node: Node) {
|
||||
return node instanceof Element && node.getAttribute('role') === 'treeitem';
|
||||
@@ -188,23 +188,23 @@ export default class WaTreeItem extends WebAwesomeElement {
|
||||
|
||||
@watch('disabled')
|
||||
handleDisabledChange() {
|
||||
this.toggleCustomState('disabled', this.disabled);
|
||||
this.customStates.set('disabled', this.disabled);
|
||||
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
|
||||
}
|
||||
|
||||
@watch('expanded')
|
||||
handleExpandedState() {
|
||||
this.toggleCustomState('expanded', this.expanded);
|
||||
this.customStates.set('expanded', this.expanded);
|
||||
}
|
||||
|
||||
@watch('indeterminate')
|
||||
handleIndeterminateStateChange() {
|
||||
this.toggleCustomState('indeterminate', this.indeterminate);
|
||||
this.customStates.set('indeterminate', this.indeterminate);
|
||||
}
|
||||
|
||||
@watch('selected')
|
||||
handleSelectedChange() {
|
||||
this.toggleCustomState('selected', this.selected);
|
||||
this.customStates.set('selected', this.selected);
|
||||
this.setAttribute('aria-selected', this.selected ? 'true' : 'false');
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ function syncCheckboxes(changedTreeItem: WaTreeItem, initialSync = false) {
|
||||
*/
|
||||
@customElement('wa-tree')
|
||||
export default class WaTree extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
|
||||
@query('slot[name=expand-icon]') expandedIconSlot: HTMLSlotElement;
|
||||
|
||||
@@ -70,7 +70,7 @@ export const viewportPropertyConverter = {
|
||||
*/
|
||||
@customElement('wa-viewport-demo')
|
||||
export default class WaViewportDemo extends WebAwesomeElement {
|
||||
static shadowStyle = styles;
|
||||
static css = styles;
|
||||
|
||||
@query('[part~=frame]')
|
||||
private viewportElement: HTMLElement;
|
||||
|
||||
@@ -184,7 +184,7 @@ function runAllValidityTests(
|
||||
control.customError = 'MyError';
|
||||
await control.updateComplete;
|
||||
expect(control.validity.valid).to.equal(false);
|
||||
expect(control.hasCustomState('invalid')).to.equal(true);
|
||||
expect(control.customStates.has('invalid')).to.equal(true);
|
||||
expect(control.validationMessage).to.equal('MyError');
|
||||
});
|
||||
|
||||
@@ -193,7 +193,7 @@ function runAllValidityTests(
|
||||
// expect(control.validity.valid).to.equal(true)
|
||||
control.setAttribute('custom-error', 'MyError');
|
||||
await control.updateComplete;
|
||||
expect(control.hasCustomState('invalid')).to.equal(true);
|
||||
expect(control.customStates.has('invalid')).to.equal(true);
|
||||
expect(control.validationMessage).to.equal('MyError');
|
||||
});
|
||||
|
||||
@@ -207,7 +207,7 @@ function runAllValidityTests(
|
||||
expect(control.disabled).to.equal(true);
|
||||
// expect(control.hasAttribute("disabled")).to.equal(false)
|
||||
expect(control.matches(':disabled')).to.equal(true);
|
||||
expect(control.hasCustomState('disabled')).to.equal(true);
|
||||
expect(control.customStates.has('disabled')).to.equal(true);
|
||||
|
||||
fieldset.disabled = false;
|
||||
|
||||
@@ -215,7 +215,7 @@ function runAllValidityTests(
|
||||
expect(control.disabled).to.equal(false);
|
||||
expect(control.hasAttribute('disabled')).to.equal(false);
|
||||
expect(control.matches(':disabled')).to.equal(false);
|
||||
expect(control.hasCustomState('disabled')).to.equal(false);
|
||||
expect(control.customStates.has('disabled')).to.equal(false);
|
||||
});
|
||||
|
||||
// it("This is the one edge case with ':disabled'. If you disable a fieldset, and then disable the element directly, it will not reflect the disabled attribute.", async () => {
|
||||
@@ -246,7 +246,7 @@ function runAllValidityTests(
|
||||
expect(control.disabled).to.equal(true);
|
||||
expect(control.hasAttribute('disabled')).to.equal(true);
|
||||
expect(control.matches(':disabled')).to.equal(true);
|
||||
expect(control.hasCustomState('disabled')).to.equal(true);
|
||||
expect(control.customStates.has('disabled')).to.equal(true);
|
||||
|
||||
control.disabled = false;
|
||||
await control.updateComplete;
|
||||
@@ -254,7 +254,7 @@ function runAllValidityTests(
|
||||
expect(control.disabled).to.equal(false);
|
||||
expect(control.hasAttribute('disabled')).to.equal(false);
|
||||
expect(control.matches(':disabled')).to.equal(false);
|
||||
expect(control.hasCustomState('disabled')).to.equal(false);
|
||||
expect(control.customStates.has('disabled')).to.equal(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CSSResult, CSSResultGroup, PropertyValues } from 'lit';
|
||||
import { LitElement, isServer, unsafeCSS } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import componentStyles from '../styles/component/host.css';
|
||||
import hostStyles from '../styles/component/host.css';
|
||||
|
||||
// Augment Lit's module
|
||||
declare module 'lit' {
|
||||
@@ -15,6 +15,31 @@ declare module 'lit' {
|
||||
}
|
||||
|
||||
export default class WebAwesomeElement extends LitElement {
|
||||
/**
|
||||
* One or more CSS files to include in the component's shadow root. Host styles are automatically prepended. We use
|
||||
* this instead of Lit's styles property because we're importing CSS files as strings and need to convert them using
|
||||
* unsafeCSS.
|
||||
*/
|
||||
static css?: CSSResultGroup | CSSResult | string | (CSSResult | string)[];
|
||||
|
||||
/**
|
||||
* Override the default styles property to fetch and convert string CSS files. Components can override this behavior
|
||||
* by setting their own `static styles = []` property.
|
||||
*/
|
||||
static get styles(): CSSResultGroup {
|
||||
const styles = Array.isArray(this.css) ? this.css : this.css ? [this.css] : [];
|
||||
return [hostStyles, ...styles].map(style => (typeof style === 'string' ? unsafeCSS(style) : style));
|
||||
}
|
||||
|
||||
#hasRecordedInitialProperties = false;
|
||||
initialReflectedProperties: Map<string, unknown> = new Map();
|
||||
internals: ElementInternals;
|
||||
|
||||
// Make localization attributes reactive
|
||||
@property() dir: string;
|
||||
@property() lang: string;
|
||||
@property({ type: Boolean, reflect: true, attribute: 'did-ssr' }) didSSR = isServer || Boolean(this.shadowRoot);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -26,52 +51,16 @@ export default class WebAwesomeElement extends LitElement {
|
||||
console.error('Element internals are not supported in your browser. Consider using a polyfill');
|
||||
}
|
||||
|
||||
this.toggleCustomState('wa-defined');
|
||||
this.customStates.set('wa-defined', true);
|
||||
|
||||
let Self = this.constructor as typeof WebAwesomeElement;
|
||||
for (let [property, spec] of Self.elementProperties) {
|
||||
if (spec.default === 'inherit' && spec.initial !== undefined && typeof property === 'string') {
|
||||
this.toggleCustomState(`initial-${property}-${spec.initial}`);
|
||||
this.customStates.set(`initial-${property}-${spec.initial}`, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make localization attributes reactive
|
||||
@property() dir: string;
|
||||
@property() lang: string;
|
||||
|
||||
/**
|
||||
* One or more styles for the element’s own shadow DOM.
|
||||
* Shared component styles will automatically be added.
|
||||
* If that is not desirable, the subclass can define its own styles property.
|
||||
*/
|
||||
static shadowStyle?: CSSResultGroup | CSSResult | string | (CSSResult | string)[];
|
||||
|
||||
/** The base styles property will only get called if the subclass does not define a styles property of its own */
|
||||
static get styles(): CSSResultGroup {
|
||||
const shadowStyle = this.shadowStyle
|
||||
? Array.isArray(this.shadowStyle)
|
||||
? this.shadowStyle
|
||||
: [this.shadowStyle]
|
||||
: [];
|
||||
|
||||
// Convert any string styles to Lit’s CSSResult
|
||||
const shadowStyles = [componentStyles, ...shadowStyle].map(style =>
|
||||
typeof style === 'string' ? unsafeCSS(style) : style,
|
||||
);
|
||||
|
||||
return shadowStyles;
|
||||
}
|
||||
|
||||
@property({ type: Boolean, reflect: true, attribute: 'did-ssr' }) didSSR = isServer || Boolean(this.shadowRoot);
|
||||
|
||||
#hasRecordedInitialProperties = false;
|
||||
|
||||
// Store the constructor value of all `static properties = {}`
|
||||
initialReflectedProperties: Map<string, unknown> = new Map();
|
||||
|
||||
internals: ElementInternals;
|
||||
|
||||
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
|
||||
if (!this.#hasRecordedInitialProperties) {
|
||||
(this.constructor as typeof WebAwesomeElement).elementProperties.forEach(
|
||||
@@ -131,43 +120,26 @@ export default class WebAwesomeElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if states are supported by the element */
|
||||
private hasStatesSupport(): boolean {
|
||||
return Boolean(this.internals?.states);
|
||||
}
|
||||
|
||||
/** Adds a custom state to the element. */
|
||||
addCustomState(state: string) {
|
||||
if (this.hasStatesSupport()) {
|
||||
this.internals.states.add(state);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes a custom state from the element. */
|
||||
deleteCustomState(state: string) {
|
||||
if (this.hasStatesSupport()) {
|
||||
this.internals.states.delete(state);
|
||||
}
|
||||
}
|
||||
|
||||
/** Toggles a custom state on the element. */
|
||||
toggleCustomState(state: string, force?: boolean) {
|
||||
if (typeof force === 'boolean') {
|
||||
if (force) {
|
||||
this.addCustomState(state);
|
||||
/**
|
||||
* Methods for setting and checking custom states.
|
||||
*/
|
||||
public customStates = {
|
||||
/** Adds or removes the specified custom state. */
|
||||
set: (customState: string, active: boolean) => {
|
||||
if (!Boolean(this.internals?.states)) return;
|
||||
if (active) {
|
||||
this.internals.states.add(customState);
|
||||
} else {
|
||||
this.deleteCustomState(state);
|
||||
this.internals.states.delete(customState);
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
this.toggleCustomState(state, !this.hasCustomState(state));
|
||||
}
|
||||
|
||||
/** Determines if the element has the specified custom state. */
|
||||
hasCustomState(state: string): boolean {
|
||||
return this.hasStatesSupport() ? this.internals.states.has(state) : false;
|
||||
}
|
||||
/** Determines whether or not the element currently has the specified state. */
|
||||
has: (customState: string) => {
|
||||
if (!Boolean(this.internals?.states)) return false;
|
||||
return this.internals.states.has(customState);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a native event, this function cancels it and dispatches it again from the host element using the desired
|
||||
|
||||
@@ -172,7 +172,7 @@ export class WebAwesomeFormAssociatedElement
|
||||
}
|
||||
|
||||
if (changedProperties.has('disabled')) {
|
||||
this.toggleCustomState('disabled', this.disabled);
|
||||
this.customStates.set('disabled', this.disabled);
|
||||
|
||||
if (this.hasAttribute('disabled') || (!isServer && !this.matches(':disabled'))) {
|
||||
this.toggleAttribute('disabled', this.disabled);
|
||||
@@ -255,12 +255,12 @@ export class WebAwesomeFormAssociatedElement
|
||||
const isValid = this.internals.validity.valid;
|
||||
const hasInteracted = this.hasInteracted;
|
||||
|
||||
this.toggleCustomState('required', required);
|
||||
this.toggleCustomState('optional', !required);
|
||||
this.toggleCustomState('invalid', !isValid);
|
||||
this.toggleCustomState('valid', isValid);
|
||||
this.toggleCustomState('user-invalid', !isValid && hasInteracted);
|
||||
this.toggleCustomState('user-valid', isValid && hasInteracted);
|
||||
this.customStates.set('required', required);
|
||||
this.customStates.set('optional', !required);
|
||||
this.customStates.set('invalid', !isValid);
|
||||
this.customStates.set('valid', isValid);
|
||||
this.customStates.set('user-invalid', !isValid && hasInteracted);
|
||||
this.customStates.set('user-valid', isValid && hasInteracted);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user