From 8c0d5fc8a10d7fda4eb4f6bae26869b7bc0f1c38 Mon Sep 17 00:00:00 2001 From: AleksanderSklorz <115619721+AleksanderSklorz@users.noreply.github.com> Date: Wed, 10 Dec 2025 09:08:57 +0100 Subject: [PATCH] [ACS-10282] screen reader personal files perform actions empty state not announced by screen reader (#11444) * [ACS-10282] Display and read errors for actions values * [ACS-10282] Added documentation --- docs/README.md | 2 +- .../card-view-property-validator.directive.md | 33 +++++++ docs/versionIndex.md | 9 ++ .../card-view-boolitem.component.html | 8 +- .../card-view-boolitem.component.spec.ts | 41 +++++++- .../card-view-boolitem.component.ts | 23 ++++- .../card-view-selectitem.component.html | 6 +- .../card-view-selectitem.component.scss | 2 +- .../card-view-selectitem.component.spec.ts | 41 +++++++- .../card-view-selectitem.component.ts | 19 +++- .../card-view-textitem.component.html | 11 ++- .../card-view-textitem.component.spec.ts | 60 ++++++++++++ .../card-view-property-validator.directive.ts | 49 ++++++++++ ...-view-property-validator.spec.directive.ts | 93 +++++++++++++++++++ 14 files changed, 379 insertions(+), 18 deletions(-) create mode 100644 docs/core/directives/card-view-property-validator.directive.md create mode 100644 lib/core/src/lib/card-view/directives/card-view-property-validator.directive.ts create mode 100644 lib/core/src/lib/card-view/directives/card-view-property-validator.spec.directive.ts diff --git a/docs/README.md b/docs/README.md index 23941f6a11..6cb106f313 100644 --- a/docs/README.md +++ b/docs/README.md @@ -140,7 +140,7 @@ A collection of Angular components for generic use. | [Logout directive](core/directives/logout.directive.md) | Logs the user out when the decorated element is clicked. | [Source](../lib/core/src/lib/directives/logout.directive.ts) | | [Node Download directive](core/directives/node-download.directive.md) | Allows folders and/or files to be downloaded, with multiple nodes packed as a '.ZIP' archive. | [Source](../lib/content-services/src/lib/directives/node-download.directive.ts) | | [Upload Directive](core/directives/upload.directive.md) | Uploads content in response to file drag and drop. | [Source](../lib/core/src/lib/directives/upload.directive.ts) | - +| [CardViewPropertyValidator Directive](core/directives/card-view-property-validator.directive.md) | Checks validators defined on property.| [Source](../lib/core/src/lib/card-view/directives/card-view-property-validator.directive.ts) | ### Dialogs | Name | Description | Source link | diff --git a/docs/core/directives/card-view-property-validator.directive.md b/docs/core/directives/card-view-property-validator.directive.md new file mode 100644 index 0000000000..dfd7e4426e --- /dev/null +++ b/docs/core/directives/card-view-property-validator.directive.md @@ -0,0 +1,33 @@ +--- +Title: CardViewPropertyValidator directive +Added: v8.3.0 +Status: Active +Last reviewed: 2025-12-10 +--- + +# [CardViewPropertyValidator directive](../../../lib/core/src/lib/card-view/directives/card-view-property-validator.directive.ts "Defined in card-view-property-validator.directive.ts") + +Checks validators defined on property. + +## Basic Usage + +```html + +``` + +## Class members + +### Properties + +| Name | Type | Default value | Description | +|----------|---------------------------------------------------------------------------------------------------|---------------|-----------------------------------------------------| +| property | [`CardViewBaseItemModel`](../../../lib/core/src/lib/card-view/models/card-view-baseitem.model.ts) | | Property for which validations should be triggered. | + +### Events + +| Name | Type | Description | +|-----------|------------------------------------------------------------------------|----------------------------------------------------------------------------------| +| validated | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`` | Emitted after validation. Emits list of errors or empty array if input is valid. | diff --git a/docs/versionIndex.md b/docs/versionIndex.md index a99ae64a5e..041ccda419 100644 --- a/docs/versionIndex.md +++ b/docs/versionIndex.md @@ -12,6 +12,7 @@ backend services have been tested with each released version of ADF. ## Versions +- [v8.3.0](#v830) - [v7.0.0-alpha.3](#v700-alpha3) - [v6.8.0](#v680) - [v6.7.0](#v670) @@ -48,6 +49,14 @@ backend services have been tested with each released version of ADF. - [v2.1.0](#v210) - [v2.0.0](#v200) +## v8.3.0 + + + +- [CardViewPropertyValidator Directive](core/directives/card-view-property-validator.directive.md) + + + ## v7.0.0-alpha.3 diff --git a/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.html b/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.html index 016caa651b..cfd18235e0 100644 --- a/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.html +++ b/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.html @@ -2,12 +2,18 @@
+ (ngModelChange)="changed($event)" + adf-card-view-property-validator + [property]="property" + (validated)="onValidation($event)" + #checkbox="ngModel" >
{{ property.label | translate }}
+
diff --git a/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.spec.ts b/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.spec.ts index 01c1deee77..f32c31c7d5 100644 --- a/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.spec.ts +++ b/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.spec.ts @@ -16,13 +16,17 @@ */ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MatCheckboxChange } from '@angular/material/checkbox'; import { CardViewUpdateService } from '../../services/card-view-update.service'; import { CardViewBoolItemComponent } from './card-view-boolitem.component'; import { CardViewBoolItemModel } from '../../models/card-view-boolitem.model'; import { UnitTestingUtils } from '../../../testing/unit-testing-utils'; import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; +import { CardViewPropertyValidatorDirective } from '../../directives/card-view-property-validator.directive'; +import { FormControl, NgModel } from '@angular/forms'; +import { MatError } from '@angular/material/form-field'; +import { Injector } from '@angular/core'; +import { MatCheckbox } from '@angular/material/checkbox'; describe('CardViewBoolItemComponent', () => { let fixture: ComponentFixture; @@ -170,7 +174,7 @@ describe('CardViewBoolItemComponent', () => { spyOn(cardViewUpdateService, 'update'); const property = { ...component.property }; - component.changed({ checked: true } as MatCheckboxChange); + component.changed(true); expect(cardViewUpdateService.update).toHaveBeenCalledWith(property, true); }); @@ -178,7 +182,7 @@ describe('CardViewBoolItemComponent', () => { it('should update the property value after a changed', async () => { component.property.value = true; - component.changed({ checked: false } as MatCheckboxChange); + component.changed(false); fixture.detectChanges(); await fixture.whenStable(); @@ -202,4 +206,35 @@ describe('CardViewBoolItemComponent', () => { testingUtils.clickByDataAutomationId('card-boolean-label-boolKey'); }); }); + + describe('Validation', () => { + let cardViewPropertyValidator: CardViewPropertyValidatorDirective; + let control: FormControl; + + const getCheckboxElementInjector = (): Injector => testingUtils.getByDirective(MatCheckbox).injector; + + beforeEach(() => { + component.editable = true; + fixture.detectChanges(); + const checkboxElementInjector = getCheckboxElementInjector(); + cardViewPropertyValidator = checkboxElementInjector.get(CardViewPropertyValidatorDirective); + control = checkboxElementInjector.get(NgModel).control; + }); + + it('should have assigned correct property', () => { + expect(cardViewPropertyValidator.property).toBe(component.property); + }); + + it('should display correct error', () => { + cardViewPropertyValidator.validated.emit(['Error 1', 'Error 2']); + control.setErrors({ + error1: 'Error 1', + error2: 'Error 2' + }); + control.markAsTouched(); + + fixture.detectChanges(); + expect(testingUtils.getByDirective(MatError).nativeElement.textContent).toBe('Error 1Error 2'); + }); + }); }); diff --git a/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.ts b/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.ts index c919f0755c..bc2a33ad0f 100644 --- a/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.ts +++ b/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.ts @@ -16,15 +16,18 @@ */ import { Component, Input } from '@angular/core'; -import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox'; +import { MatCheckboxModule } from '@angular/material/checkbox'; import { CardViewBoolItemModel } from '../../models/card-view-boolitem.model'; import { BaseCardView } from '../base-card-view'; import { CommonModule } from '@angular/common'; import { TranslatePipe } from '@ngx-translate/core'; +import { CardViewPropertyValidatorDirective } from '../../directives/card-view-property-validator.directive'; +import { FormsModule } from '@angular/forms'; +import { MatError } from '@angular/material/form-field'; @Component({ selector: 'adf-card-view-boolitem', - imports: [CommonModule, MatCheckboxModule, TranslatePipe], + imports: [CommonModule, MatCheckboxModule, TranslatePipe, CardViewPropertyValidatorDirective, FormsModule, MatError], templateUrl: './card-view-boolitem.component.html', styles: [ ` @@ -38,8 +41,18 @@ export class CardViewBoolItemComponent extends BaseCardView'); } } diff --git a/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.html b/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.html index 1a416d2731..06aebd2329 100644 --- a/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.html +++ b/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.html @@ -27,12 +27,15 @@ >{{ property.label | translate }} {{ 'CORE.CARDVIEW.NONE' | translate }} @@ -40,6 +43,7 @@ {{ option.label | translate }} + diff --git a/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.scss b/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.scss index f137cf948f..356f11e417 100644 --- a/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.scss +++ b/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.scss @@ -45,7 +45,7 @@ } #{ms.$mat-form-field-subscript-wrapper} { - display: none; + display: block; } .adf-property-read-only { diff --git a/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.spec.ts b/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.spec.ts index 41170667df..ea0ef4c803 100644 --- a/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.spec.ts +++ b/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.spec.ts @@ -26,6 +26,9 @@ import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { UnitTestingUtils } from '../../../testing/unit-testing-utils'; import { CardViewUpdateService } from '../../services/card-view-update.service'; import { DebugElement } from '@angular/core'; +import { CardViewPropertyValidatorDirective } from '../../directives/card-view-property-validator.directive'; +import { MatError } from '@angular/material/form-field'; +import { FormControl, NgModel } from '@angular/forms'; describe('CardViewSelectItemComponent', () => { let loader: HarnessLoader; @@ -59,6 +62,8 @@ describe('CardViewSelectItemComponent', () => { editable: true }; + const getSelectElement = (): DebugElement => testingUtils.getByDataAutomationClass('select-box'); + beforeEach(() => { TestBed.configureTestingModule({ imports: [CardViewSelectItemComponent] @@ -96,10 +101,9 @@ describe('CardViewSelectItemComponent', () => { component.ngOnChanges({}); fixture.detectChanges(); - const selectBox = testingUtils.getByDataAutomationClass('select-box'); expect(getReadOnlyElement()).not.toBeNull(); - expect(selectBox).toBeNull(); + expect(getSelectElement()).toBeNull(); }); it('should read only value have title', () => { @@ -336,4 +340,37 @@ describe('CardViewSelectItemComponent', () => { expect(await options[1].getText()).toContain('Option 2'); }); }); + + describe('Validation', () => { + let cardViewPropertyValidator: CardViewPropertyValidatorDirective; + let control: FormControl; + + beforeEach(() => { + component.property = new CardViewSelectItemModel({ + ...mockDefaultProps, + editable: true + }); + component.editable = true; + fixture.detectChanges(); + const selectElementInjector = getSelectElement().injector; + cardViewPropertyValidator = selectElementInjector.get(CardViewPropertyValidatorDirective); + control = selectElementInjector.get(NgModel).control; + }); + + it('should have assigned correct property', () => { + expect(cardViewPropertyValidator.property).toBe(component.property); + }); + + it('should display correct error', () => { + cardViewPropertyValidator.validated.emit(['Error 1', 'Error 2']); + control.setErrors({ + error1: 'Error 1', + error2: 'Error 2' + }); + control.markAsTouched(); + + fixture.detectChanges(); + expect(testingUtils.getByDirective(MatError).nativeElement.textContent).toBe('Error 1Error 2'); + }); + }); }); diff --git a/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.ts b/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.ts index 26d2e7e7d4..99193fdd18 100644 --- a/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.ts +++ b/lib/core/src/lib/card-view/components/card-view-selectitem/card-view-selectitem.component.ts @@ -29,8 +29,9 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { SelectFilterInputComponent } from './select-filter-input/select-filter-input.component'; import { MatAutocompleteModule, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatInputModule } from '@angular/material/input'; -import { ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { CardViewPropertyValidatorDirective } from '../../directives/card-view-property-validator.directive'; @Component({ selector: 'adf-card-view-selectitem', @@ -42,7 +43,9 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; SelectFilterInputComponent, MatAutocompleteModule, MatInputModule, - ReactiveFormsModule + ReactiveFormsModule, + CardViewPropertyValidatorDirective, + FormsModule ], templateUrl: './card-view-selectitem.component.html', styleUrls: ['./card-view-selectitem.component.scss'], @@ -50,7 +53,6 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; host: { class: 'adf-card-view-selectitem' } }) export class CardViewSelectItemComponent extends BaseCardView> implements OnInit, OnChanges { - private appConfig = inject(AppConfigService); static HIDE_FILTER_LIMIT = 5; @Input() options$: Observable[]>; @@ -70,6 +72,13 @@ export class CardViewSelectItemComponent extends BaseCardView'); + } + get showProperty(): boolean { return this.displayEmpty || !this.property.isEmpty(); } diff --git a/lib/core/src/lib/card-view/components/card-view-textitem/card-view-textitem.component.html b/lib/core/src/lib/card-view/components/card-view-textitem/card-view-textitem.component.html index 2a398d6986..636081531c 100644 --- a/lib/core/src/lib/card-view/components/card-view-textitem/card-view-textitem.component.html +++ b/lib/core/src/lib/card-view/components/card-view-textitem/card-view-textitem.component.html @@ -38,6 +38,8 @@ [title]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate" [attr.data-automation-id]="'card-textitem-value-' + property.key" (keydown)="undoText($event)" + (blur)="update()" + [aria-describedby]="'adf-card-textitem-error-' + property.key" />