diff --git a/src/material/timepicker/timepicker-toggle.html b/src/material/timepicker/timepicker-toggle.html index ef15001b8e5a..879329c90dff 100644 --- a/src/material/timepicker/timepicker-toggle.html +++ b/src/material/timepicker/timepicker-toggle.html @@ -2,7 +2,8 @@ mat-icon-button type="button" aria-haspopup="listbox" - [attr.aria-label]="ariaLabel()" + [attr.aria-label]="getAriaLabel()" + [attr.aria-labelledby]="ariaLabelledby()" [attr.aria-expanded]="timepicker().isOpen()" [attr.tabindex]="_isDisabled() ? -1 : tabIndex()" [disabled]="_isDisabled()" diff --git a/src/material/timepicker/timepicker-toggle.ts b/src/material/timepicker/timepicker-toggle.ts index 9bf902390a16..1bf7a67fcdad 100644 --- a/src/material/timepicker/timepicker-toggle.ts +++ b/src/material/timepicker/timepicker-toggle.ts @@ -62,6 +62,14 @@ export class MatTimepickerToggle<D> { alias: 'aria-label', }); + /** Screen-reader labelled by id for the button. */ + readonly ariaLabelledby = input<string | undefined>(undefined, { + alias: 'aria-labelledby', + }); + + /** Default aria-label for the toggle if none is provided. */ + private readonly _defaultAriaLabel = 'Open timepicker options'; + /** Whether the toggle button is disabled. */ readonly disabled: InputSignalWithTransform<boolean, unknown> = input(false, { transform: booleanAttribute, @@ -84,4 +92,12 @@ export class MatTimepickerToggle<D> { event.stopPropagation(); } } + + /** + * Checks for ariaLabelledby and if empty uses custom + * aria-label or defaultAriaLabel if neither is provided. + */ + getAriaLabel(): string | null { + return this.ariaLabelledby() ? null : this.ariaLabel() || this._defaultAriaLabel; + } } diff --git a/tools/public_api_guard/material/timepicker.md b/tools/public_api_guard/material/timepicker.md index 528d217bd53f..d99141df8ddc 100644 --- a/tools/public_api_guard/material/timepicker.md +++ b/tools/public_api_guard/material/timepicker.md @@ -124,15 +124,17 @@ export interface MatTimepickerSelected<D> { // @public export class MatTimepickerToggle<D> { readonly ariaLabel: InputSignal<string | undefined>; + readonly ariaLabelledby: InputSignal<string | undefined>; readonly disabled: InputSignalWithTransform<boolean, unknown>; readonly disableRipple: InputSignalWithTransform<boolean, unknown>; + getAriaLabel(): string | null; // (undocumented) protected _isDisabled: Signal<boolean>; protected _open(event: Event): void; readonly tabIndex: InputSignal<number | null>; readonly timepicker: InputSignal<MatTimepicker<D>>; // (undocumented) - static ɵcmp: i0.ɵɵComponentDeclaration<MatTimepickerToggle<any>, "mat-timepicker-toggle", ["matTimepickerToggle"], { "timepicker": { "alias": "for"; "required": true; "isSignal": true; }; "ariaLabel": { "alias": "aria-label"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "tabIndex": { "alias": "tabIndex"; "required": false; "isSignal": true; }; "disableRipple": { "alias": "disableRipple"; "required": false; "isSignal": true; }; }, {}, never, ["[matTimepickerToggleIcon]"], true, never>; + static ɵcmp: i0.ɵɵComponentDeclaration<MatTimepickerToggle<any>, "mat-timepicker-toggle", ["matTimepickerToggle"], { "timepicker": { "alias": "for"; "required": true; "isSignal": true; }; "ariaLabel": { "alias": "aria-label"; "required": false; "isSignal": true; }; "ariaLabelledby": { "alias": "aria-labelledby"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "tabIndex": { "alias": "tabIndex"; "required": false; "isSignal": true; }; "disableRipple": { "alias": "disableRipple"; "required": false; "isSignal": true; }; }, {}, never, ["[matTimepickerToggleIcon]"], true, never>; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration<MatTimepickerToggle<any>, never>; }