diff --git a/libs/designsystem/empty-state/src/empty-state.component.ts b/libs/designsystem/empty-state/src/empty-state.component.ts index 754cec73d6..56440591b3 100644 --- a/libs/designsystem/empty-state/src/empty-state.component.ts +++ b/libs/designsystem/empty-state/src/empty-state.component.ts @@ -5,6 +5,7 @@ import { ContentChildren, ElementRef, Input, + OnInit, QueryList, } from '@angular/core'; import { ButtonComponent } from '@kirbydesign/designsystem/button'; @@ -15,14 +16,14 @@ import { ButtonComponent } from '@kirbydesign/designsystem/button'; styleUrls: ['./empty-state.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class EmptyStateComponent implements AfterContentInit { - _title: string; +export class EmptyStateComponent implements AfterContentInit, OnInit { + private _title: string; @Input() iconName: string; @Input() set title(value: string) { this._title = value; - this.setAccessibleModalLabel(); + this.setAriaLabelOnModal(); } get title(): string { @@ -33,9 +34,21 @@ export class EmptyStateComponent implements AfterContentInit { @ContentChildren(ButtonComponent) private slottedButtons: QueryList; + private ionModalElement: HTMLIonModalElement; + private modalElementDialog: HTMLElement; constructor(private elementRef: ElementRef) {} + ngOnInit(): void { + /* If initialized inside a modal that is not labelled, we want to set + * the aria-label attribute on ion-modal to the title of empty state. + * Further updates are handled by title setter. + */ + this.ionModalElement = this.elementRef.nativeElement.closest('ion-modal'); + this.modalElementDialog = this.ionModalElement?.shadowRoot.querySelector('[role="dialog"]'); + this.setAriaLabelOnModal(); + } + ngAfterContentInit() { this.enforceAttentionLevelRules(); @@ -46,16 +59,11 @@ export class EmptyStateComponent implements AfterContentInit { }); } - setAccessibleModalLabel() { - /* If we are inside a modal that is not labelled, we want to set - the aria-label attribute on ion-modal to point to the title of empty state */ - const ionModalDialog = this.elementRef.nativeElement - .closest('ion-modal') - .shadowRoot.querySelector('[role="dialog"]'); - - ionModalDialog.ariaLabel ?? (ionModalDialog.ariaLabel = this.title); + private setAriaLabelOnModal() { + if (this.modalElementDialog && this._title) { + this.modalElementDialog.ariaLabel = this._title; + } } - /** Enforces that all slotted buttons will have their attention * level set to 3, except the first button if it has * level 1. diff --git a/libs/designsystem/modal/src/modal-wrapper/compact/modal-compact-wrapper.component.ts b/libs/designsystem/modal/src/modal-wrapper/compact/modal-compact-wrapper.component.ts index 831b7b26c7..33b1433d36 100644 --- a/libs/designsystem/modal/src/modal-wrapper/compact/modal-compact-wrapper.component.ts +++ b/libs/designsystem/modal/src/modal-wrapper/compact/modal-compact-wrapper.component.ts @@ -27,12 +27,17 @@ import { CanDismissHelper } from '../../modal/services/can-dismiss.helper'; host: { '[class.ion-page]': 'false' }, //Ensure ion-page class doesn't get applied by Ionic Modal Controller }) export class ModalCompactWrapperComponent implements Modal, OnInit { + private _ariaLabel: string; + @Input() config: ModalConfig; @Input() content: TemplateRef; - @Input() set titleId(value: string) { - // local titleId variable to be used in ngAfterViewInit if setter is called before OnInit - this._titleId = value; - this.ionModalElement?.setAttribute('aria-labelledby', value); + @Input() set ariaLabel(value: string) { + this._ariaLabel = value; + this.setAriaLabelOnModal(); + } + + get ariaLabel(): string { + return this._ariaLabel; } scrollY: number = Math.abs(this.windowRef.nativeWindow.scrollY); @@ -40,11 +45,11 @@ export class ModalCompactWrapperComponent implements Modal, OnInit { componentPropsInjector: Injector; private ionModalElement: HTMLIonModalElement; + private modalElementDialog: HTMLElement; private readonly ionModalDidPresent = new Subject(); private readonly ionModalWillDismiss = new Subject(); readonly didPresent = firstValueFrom(this.ionModalDidPresent); readonly willClose = firstValueFrom(this.ionModalWillDismiss); - private _titleId; constructor( private injector: Injector, @@ -55,8 +60,14 @@ export class ModalCompactWrapperComponent implements Modal, OnInit { ngOnInit(): void { this.ionModalElement = this.elementRef.nativeElement.closest('ion-modal'); + this.modalElementDialog = this.ionModalElement.shadowRoot.querySelector('[role="dialog"]'); + + /* If initialized with ariaLabel, we want to set the aria-label attribute immediately. + * Further updates are handled by title setter. + */ + this.setAriaLabelOnModal(); + console.log('ModalCompactWrapperComponent ngOnInit ariaLabel:', this._ariaLabel); - this.addAriaLabelledByToIonModal(); this.listenForIonModalDidPresent(); this.listenForIonModalWillDismiss(); this.componentPropsInjector = Injector.create({ @@ -64,11 +75,6 @@ export class ModalCompactWrapperComponent implements Modal, OnInit { parent: this.injector, }); } - addAriaLabelledByToIonModal() { - if (this.ionModalElement && this._titleId) { - this.ionModalElement?.setAttribute('aria-labelledby', this._titleId); - } - } private listenForIonModalDidPresent() { if (this.ionModalElement) { @@ -88,6 +94,12 @@ export class ModalCompactWrapperComponent implements Modal, OnInit { } } + private setAriaLabelOnModal() { + if (this.modalElementDialog && this.ariaLabel) { + this.modalElementDialog.ariaLabel = this.ariaLabel; + } + } + async close(data?: any): Promise { const ionModalElement = this.elementRef.nativeElement.closest('ion-modal'); if (ionModalElement) { diff --git a/libs/designsystem/modal/src/modal/modal-component/modal.component.html b/libs/designsystem/modal/src/modal/modal-component/modal.component.html index 2cfa99cc17..82a63d1b59 100644 --- a/libs/designsystem/modal/src/modal/modal-component/modal.component.html +++ b/libs/designsystem/modal/src/modal/modal-component/modal.component.html @@ -20,7 +20,6 @@ *ngIf="flavor === 'compact'; else modalWrapper" [config]="_config" [content]="template" - [titleId]="titleId" >