From a96528bbf0bab5e02e80f09b85ddf7a8460dd7c0 Mon Sep 17 00:00:00 2001 From: Uros Lates Date: Thu, 9 Jan 2025 10:13:55 +0100 Subject: [PATCH 1/4] fix: (CXSPA-9005) fix ng-select options issue - is conveyed twice --- .../ng-select-a11y.directive.spec.ts | 4 +-- .../ng-select-a11y.directive.ts | 32 +++++++------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.spec.ts b/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.spec.ts index 27ceae10eea..1edfcc0fa0c 100644 --- a/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.spec.ts +++ b/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.spec.ts @@ -89,9 +89,7 @@ describe('NgSelectA11yDirective', () => { const options = select.querySelectorAll('.ng-option'); expect(options.length).toBe(3); options.forEach((option: HTMLElement, index: number) => { - expect(option.getAttribute('aria-label')).toEqual( - `${index + 1}, ${index + 1} of ${options.length}` - ); + expect(option.getAttribute('aria-label')).toEqual(`${index + 1}`); }); done(); }); diff --git a/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.ts b/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.ts index d85c509a12b..95e99f71492 100644 --- a/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.ts +++ b/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.ts @@ -19,13 +19,13 @@ import { Renderer2, SecurityContext, } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DomSanitizer } from '@angular/platform-browser'; -import { FeatureConfigService, TranslationService } from '@spartacus/core'; -import { filter, merge, take } from 'rxjs'; -import { BREAKPOINT, BreakpointService } from '../../../layout'; import { NgSelectComponent } from '@ng-select/ng-select'; +import { FeatureConfigService } from '@spartacus/core'; +import { filter, merge, take } from 'rxjs'; import { map } from 'rxjs/operators'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { BREAKPOINT, BreakpointService } from '../../../layout'; const ARIA_LABEL = 'aria-label'; @@ -40,7 +40,6 @@ export class NgSelectA11yDirective implements AfterViewInit { */ @Input() cxNgSelectA11y: { ariaLabel?: string; ariaControls?: string }; - protected translationService = inject(TranslationService); protected domSanitizer = inject(DomSanitizer); protected selectComponent = inject(NgSelectComponent); protected destroyRef = inject(DestroyRef); @@ -145,21 +144,14 @@ export class NgSelectA11yDirective implements AfterViewInit { const options: HTMLOptionElement[] = this.elementRef?.nativeElement.querySelectorAll('.ng-option'); if (options?.length) { - this.translationService - .translate('common.of') - .pipe(take(1)) - .subscribe((translation) => { - options.forEach( - (option: HTMLOptionElement, index: string | number) => { - const sanitizedOptionText = this.domSanitizer.sanitize( - SecurityContext.HTML, - option.innerText - ); - const ariaLabel = `${sanitizedOptionText}, ${+index + 1} ${translation} ${options.length}`; - this.renderer.setAttribute(option, ARIA_LABEL, ariaLabel); - } - ); - }); + options.forEach((option: HTMLOptionElement) => { + const sanitizedOptionText = this.domSanitizer.sanitize( + SecurityContext.HTML, + option.innerText + ); + const ariaLabel = `${sanitizedOptionText}`; + this.renderer.setAttribute(option, ARIA_LABEL, ariaLabel); + }); } observerInstance.disconnect(); } From e51b9833b311866c7cb00b701cd052d88f115287 Mon Sep 17 00:00:00 2001 From: Uros Lates Date: Mon, 13 Jan 2025 17:04:48 +0100 Subject: [PATCH 2/4] fix: (CXSPA-9005) fix ng-select options issue - is conveyed twice --- .../feature-toggles/config/feature-toggles.ts | 2 + .../spartacus/spartacus-features.module.ts | 6 ++- .../ng-select-a11y.directive.spec.ts | 4 +- .../ng-select-a11y.directive.ts | 38 ++++++++++++++----- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/projects/core/src/features-config/feature-toggles/config/feature-toggles.ts b/projects/core/src/features-config/feature-toggles/config/feature-toggles.ts index 9457e66901b..1f7b721e8b1 100644 --- a/projects/core/src/features-config/feature-toggles/config/feature-toggles.ts +++ b/projects/core/src/features-config/feature-toggles/config/feature-toggles.ts @@ -593,6 +593,8 @@ export interface FeatureTogglesInterface { /** * 'NgSelectA11yDirective' will now provide a count of items for each availble option. * Including this count in aria-label will help screen readers to provide more context to the user. + * Update (since 2211.33): This feature toggle and the logic behind it should be removed + * in next major relase since ng-select now correctly handles aria-label values of select options. */ a11yNgSelectOptionsCount?: boolean; diff --git a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts index 08ca9f9e27c..876ded0524c 100644 --- a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts +++ b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts @@ -380,7 +380,11 @@ if (environment.cpq) { a11yLinkBtnsToTertiaryBtns: true, a11yRepeatedPageTitleFix: true, a11yDeliveryModeRadiogroup: true, - a11yNgSelectOptionsCount: true, + /** + * Defaults to false cause ng-select options ariaLabels are working as expected + * since Spartacus 2211.33 + */ + a11yNgSelectOptionsCount: false, a11yNgSelectCloseDropdownOnEscape: true, a11yRepeatedCancelOrderError: true, a11yAddedToCartActiveDialog: true, diff --git a/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.spec.ts b/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.spec.ts index 1edfcc0fa0c..27ceae10eea 100644 --- a/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.spec.ts +++ b/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.spec.ts @@ -89,7 +89,9 @@ describe('NgSelectA11yDirective', () => { const options = select.querySelectorAll('.ng-option'); expect(options.length).toBe(3); options.forEach((option: HTMLElement, index: number) => { - expect(option.getAttribute('aria-label')).toEqual(`${index + 1}`); + expect(option.getAttribute('aria-label')).toEqual( + `${index + 1}, ${index + 1} of ${options.length}` + ); }); done(); }); diff --git a/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.ts b/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.ts index 95e99f71492..679e92810fc 100644 --- a/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.ts +++ b/projects/storefrontlib/shared/components/ng-select-a11y/ng-select-a11y.directive.ts @@ -22,7 +22,7 @@ import { import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DomSanitizer } from '@angular/platform-browser'; import { NgSelectComponent } from '@ng-select/ng-select'; -import { FeatureConfigService } from '@spartacus/core'; +import { FeatureConfigService, TranslationService } from '@spartacus/core'; import { filter, merge, take } from 'rxjs'; import { map } from 'rxjs/operators'; import { BREAKPOINT, BreakpointService } from '../../../layout'; @@ -40,12 +40,21 @@ export class NgSelectA11yDirective implements AfterViewInit { */ @Input() cxNgSelectA11y: { ariaLabel?: string; ariaControls?: string }; + //TODO: CXSPA-9005: Remove this property in next major release + /** + * @deprecated since 2211.33 + */ + protected translationService = inject(TranslationService); protected domSanitizer = inject(DomSanitizer); protected selectComponent = inject(NgSelectComponent); protected destroyRef = inject(DestroyRef); private featureConfigService = inject(FeatureConfigService); @HostListener('open') + //TODO: CXSPA-9005: Remove this method in next major release + /** + * @deprecated since 2211.33 + */ onOpen() { if (!this.featureConfigService?.isEnabled('a11yNgSelectOptionsCount')) { return; @@ -137,6 +146,10 @@ export class NgSelectA11yDirective implements AfterViewInit { } } + //TODO: CXSPA-9005: Remove this method in next major release + /** + * @deprecated since 2211.33 + */ appendAriaLabelToOptions( _changes: MutationRecord[], observerInstance: MutationObserver @@ -144,14 +157,21 @@ export class NgSelectA11yDirective implements AfterViewInit { const options: HTMLOptionElement[] = this.elementRef?.nativeElement.querySelectorAll('.ng-option'); if (options?.length) { - options.forEach((option: HTMLOptionElement) => { - const sanitizedOptionText = this.domSanitizer.sanitize( - SecurityContext.HTML, - option.innerText - ); - const ariaLabel = `${sanitizedOptionText}`; - this.renderer.setAttribute(option, ARIA_LABEL, ariaLabel); - }); + this.translationService + .translate('common.of') + .pipe(take(1)) + .subscribe((translation) => { + options.forEach( + (option: HTMLOptionElement, index: string | number) => { + const sanitizedOptionText = this.domSanitizer.sanitize( + SecurityContext.HTML, + option.innerText + ); + const ariaLabel = `${sanitizedOptionText}, ${+index + 1} ${translation} ${options.length}`; + this.renderer.setAttribute(option, ARIA_LABEL, ariaLabel); + } + ); + }); } observerInstance.disconnect(); } From 597b83d72fcd6a61579390abe129f29f3f5f3508 Mon Sep 17 00:00:00 2001 From: Uros Lates Date: Tue, 14 Jan 2025 10:46:51 +0100 Subject: [PATCH 3/4] fix: (CXSPA-9005) fix linting issue --- .../src/app/spartacus/spartacus-features.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts index 876ded0524c..39f6ebc316e 100644 --- a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts +++ b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts @@ -383,7 +383,7 @@ if (environment.cpq) { /** * Defaults to false cause ng-select options ariaLabels are working as expected * since Spartacus 2211.33 - */ + */ a11yNgSelectOptionsCount: false, a11yNgSelectCloseDropdownOnEscape: true, a11yRepeatedCancelOrderError: true, From 0f3aaf327e7e71f0d705b254688c3ac6079df0de Mon Sep 17 00:00:00 2001 From: Uros Lates Date: Wed, 15 Jan 2025 10:41:27 +0100 Subject: [PATCH 4/4] fix: address PR comments (comment update) --- .../storefrontapp/src/app/spartacus/spartacus-features.module.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts index 39f6ebc316e..5de9b0a1893 100644 --- a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts +++ b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts @@ -383,6 +383,7 @@ if (environment.cpq) { /** * Defaults to false cause ng-select options ariaLabels are working as expected * since Spartacus 2211.33 + * TODO: CXSPA-9005: Remove this flag and related code in next major release */ a11yNgSelectOptionsCount: false, a11yNgSelectCloseDropdownOnEscape: true,