diff --git a/package-lock.json b/package-lock.json index 8a7e9273e89..29b8d6676b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24367,6 +24367,7 @@ "@vitest/coverage-v8": "^3.2.4", "@wc-toolkit/storybook-helpers": "^9.0.1", "del": "^8.0.1", + "dom-accessibility-api": "^0.7.1", "globby": "^14.1.0", "happy-dom": "^18.0.1", "lit": "^3.3.1", @@ -24437,6 +24438,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/craftcms-cp/node_modules/dom-accessibility-api": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.7.1.tgz", + "integrity": "sha512-vdnCeZD+3wZ+8h8xXL/ZtBlvvoobOFyPzSiIfO6sGOZDqjFx4aLMAjZhl4rawj5xYz3UwP6Tgvyh0iH4IOCVnQ==", + "dev": true, + "license": "MIT" + }, "packages/craftcms-cp/node_modules/glob": { "version": "11.0.3", "dev": true, diff --git a/packages/craftcms-cp/package.json b/packages/craftcms-cp/package.json index 529190bda51..6884c0fb04c 100644 --- a/packages/craftcms-cp/package.json +++ b/packages/craftcms-cp/package.json @@ -71,6 +71,7 @@ "@vitest/coverage-v8": "^3.2.4", "@wc-toolkit/storybook-helpers": "^9.0.1", "del": "^8.0.1", + "dom-accessibility-api": "^0.7.1", "globby": "^14.1.0", "happy-dom": "^18.0.1", "lit": "^3.3.1", diff --git a/packages/craftcms-cp/src/components/button/button.ts b/packages/craftcms-cp/src/components/button/button.ts index 64712d69107..e27724ef979 100644 --- a/packages/craftcms-cp/src/components/button/button.ts +++ b/packages/craftcms-cp/src/components/button/button.ts @@ -1,8 +1,10 @@ import {LionButtonSubmit} from '@lion/ui/button.js'; import {html, nothing} from 'lit'; -import {property} from 'lit/decorators.js'; +import {property, state} from 'lit/decorators.js'; import styles from './button.styles.js'; import '../spinner/spinner.js'; +import '../icon/icon.js'; +import {computeAccessibleName} from "dom-accessibility-api"; /** * @summary Interactive element that triggers an action or event. @@ -25,6 +27,26 @@ export default class CraftButton extends LionButtonSubmit { return [...super.styles, styles]; } + override async firstUpdated() { + super.firstUpdated(); + + await this.updateComplete; + + const childComponents = this.querySelectorAll('craft-icon, craft-spinner'); + await Promise.all( + Array.from(childComponents).map((child: any) => child.updateComplete) + ); + + if (!this.accessibleName) { + this.accessibleName = computeAccessibleName(this); + } + + this._hasAccessibilityError = !this.accessibleName || this.accessibleName.trim() === ''; + } + + /** The computed accessible name */ + @property() accessibleName: string; + /** Visual appearance of the button */ @property({reflect: true}) appearance: 'accent' | 'plain' = 'accent'; @@ -39,6 +61,9 @@ export default class CraftButton extends LionButtonSubmit { /** Show a spinner instead of the label */ @property({reflect: true, type: Boolean}) loading: boolean = false; + @state() + private _hasAccessibilityError: boolean = false; + override render() { return html`
+ ${this._hasAccessibilityError + ? html`