From bf10fab8cc87977193f81ce1c6e2c03c6846bb5f Mon Sep 17 00:00:00 2001 From: Stanislav Sukhanov Date: Fri, 10 Jan 2025 13:36:28 +0100 Subject: [PATCH 1/9] fix: (CXSPA-9041) Popover directive HCT contrast (#19835) --- .../feature-toggles/config/feature-toggles.ts | 6 ++++++ .../src/app/spartacus/spartacus-features.module.ts | 1 + .../shared/components/popover/popover.directive.ts | 10 +++++++++- .../scss/components/misc/popover/_popover.scss | 10 ++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) 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 f21d7e1f097..29c0075021f 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 @@ -211,6 +211,11 @@ export interface FeatureTogglesInterface { */ a11yPopoverFocus?: boolean; + /** + * Fix popover appearance when a High Contrast Theme is applied. + */ + a11yPopoverHighContrast?: boolean; + /** * Adds Datepicker and Combobox label and corrects heading order for 'CheckoutScheduleReplenishmentOrderComponent'. */ @@ -984,6 +989,7 @@ export const defaultFeatureToggles: Required = { a11yPreventHorizontalScroll: false, a11yReorderDialog: true, a11yPopoverFocus: true, + a11yPopoverHighContrast: false, a11yScheduleReplenishment: true, a11yScrollToTop: true, a11ySavedCartsZoom: true, diff --git a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts index af5c234d5f4..f9a02531c6c 100644 --- a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts +++ b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts @@ -317,6 +317,7 @@ if (environment.cpq) { a11yPreventHorizontalScroll: true, a11yReorderDialog: true, a11yPopoverFocus: true, + a11yPopoverHighContrast: true, a11yScheduleReplenishment: true, a11yScrollToTop: true, a11ySavedCartsZoom: true, diff --git a/projects/storefrontlib/shared/components/popover/popover.directive.ts b/projects/storefrontlib/shared/components/popover/popover.directive.ts index 256660049d5..c5d32d5402c 100644 --- a/projects/storefrontlib/shared/components/popover/popover.directive.ts +++ b/projects/storefrontlib/shared/components/popover/popover.directive.ts @@ -225,8 +225,16 @@ export class PopoverDirective implements OnInit { this.cxPopoverOptions?.autoPositioning; if (this.cxPopoverOptions?.appendToBody) { + const body = this.winRef.document.body; + const element = this.featureConfigService?.isEnabled( + 'a11yPopoverHighContrast' + ) + ? // we need to select first child element if exists, + // otherwise HCT theming in popover will not be picked up. + (body.firstElementChild ?? body) + : body; this.renderer.appendChild( - this.winRef.document.body, + element, this.popoverContainer.location.nativeElement ); } diff --git a/projects/storefrontstyles/scss/components/misc/popover/_popover.scss b/projects/storefrontstyles/scss/components/misc/popover/_popover.scss index 7098be1208e..4708af828ac 100644 --- a/projects/storefrontstyles/scss/components/misc/popover/_popover.scss +++ b/projects/storefrontstyles/scss/components/misc/popover/_popover.scss @@ -209,4 +209,14 @@ font-size: 1rem; } } + + @include cx-highContrastTheme { + background-color: var(--cx-color-background); + box-shadow: none; + .popover-body > .cx-close-row { + .close { + color: var(--cx-color-text); + } + } + } } From a7a101465a77ca2fa4601e86b8d9b5b5a13aecfa Mon Sep 17 00:00:00 2001 From: PioBar <72926984+Pio-Bar@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:31:41 +0100 Subject: [PATCH 2/9] fix: (CXSPA-8999) - Descriptive repeating buttons (#19786) --- .../add-to-cart/add-to-cart.component.html | 1 + .../set-preferred-store.component.html | 93 +++++++++++++------ .../set-preferred-store.component.spec.ts | 8 +- .../set-preferred-store.component.ts | 7 ++ .../set-preferred-store.module.ts | 4 +- .../presentational/store/store.component.html | 8 ++ .../feature-toggles/config/feature-toggles.ts | 7 ++ .../spartacus/spartacus-features.module.ts | 1 + .../product-list-item.component.html | 2 +- 9 files changed, 100 insertions(+), 31 deletions(-) diff --git a/feature-libs/cart/base/components/add-to-cart/add-to-cart.component.html b/feature-libs/cart/base/components/add-to-cart/add-to-cart.component.html index 6be6a177de3..9ddda4e5208 100644 --- a/feature-libs/cart/base/components/add-to-cart/add-to-cart.component.html +++ b/feature-libs/cart/base/components/add-to-cart/add-to-cart.component.html @@ -43,6 +43,7 @@ " type="submit" [disabled]="disabled || quantity <= 0 || quantity > maxQuantity" + [attr.aria-describedby]="productCode + '_header'" > + + +
+
+ +
+ +
+
+
+
- -
- - + " + class="setpreferredstore-container" + (click)="setAsPreferred()" + > +
+ +
+ + +
diff --git a/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.component.spec.ts b/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.component.spec.ts index 1313c8424c7..2fe0be79102 100644 --- a/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.component.spec.ts +++ b/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.component.spec.ts @@ -7,6 +7,7 @@ import { PreferredStoreFacade, } from '@spartacus/pickup-in-store/root'; import { IconTestingModule, OutletContextData } from '@spartacus/storefront'; +import { MockFeatureDirectivesModule } from 'projects/storefrontlib/shared/test/mock-feature-directives.module'; import { of } from 'rxjs'; import { MockPreferredStoreService } from '../../../core/services/preferred-store.service.spec'; import { SetPreferredStoreComponent } from './set-preferred-store.component'; @@ -24,7 +25,12 @@ describe('SetPreferredStoreComponent without outlet.context$', () => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [SetPreferredStoreComponent], - imports: [I18nTestingModule, IconTestingModule, CommonModule], + imports: [ + I18nTestingModule, + IconTestingModule, + CommonModule, + MockFeatureDirectivesModule, + ], providers: [ { provide: PreferredStoreFacade, useClass: MockPreferredStoreService }, ], diff --git a/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.component.ts b/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.component.ts index 1f20d9be9c0..77a03fdf887 100644 --- a/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.component.ts +++ b/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.component.ts @@ -44,8 +44,15 @@ export class SetPreferredStoreComponent implements OnInit, OnDestroy { ngOnDestroy() { this.subscription.unsubscribe(); } + setAsPreferred(): boolean { this.preferredStoreFacade.setPreferredStore(this.pointOfServiceName); return false; } + + getSetStoreButtonLabel(storeName: string): string { + return this.pointOfServiceName.name === storeName + ? 'setPreferredStore.myStore' + : 'setPreferredStore.makeThisMyStore'; + } } diff --git a/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.module.ts b/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.module.ts index c82d29e635a..7b31699bc10 100644 --- a/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.module.ts +++ b/feature-libs/pickup-in-store/components/container/set-preferred-store/set-preferred-store.module.ts @@ -6,7 +6,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { I18nModule } from '@spartacus/core'; +import { FeaturesConfigModule, I18nModule } from '@spartacus/core'; import { StoreFinderOutlets } from '@spartacus/storefinder/root'; import { IconModule, @@ -16,7 +16,7 @@ import { import { SetPreferredStoreComponent } from './set-preferred-store.component'; @NgModule({ - imports: [CommonModule, IconModule, I18nModule], + imports: [CommonModule, IconModule, I18nModule, FeaturesConfigModule], exports: [SetPreferredStoreComponent], declarations: [SetPreferredStoreComponent], providers: [ diff --git a/feature-libs/pickup-in-store/components/presentational/store/store.component.html b/feature-libs/pickup-in-store/components/presentational/store/store.component.html index 83285034766..4090dc6d34a 100644 --- a/feature-libs/pickup-in-store/components/presentational/store/store.component.html +++ b/feature-libs/pickup-in-store/components/presentational/store/store.component.html @@ -8,6 +8,11 @@ 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 29c0075021f..9457e66901b 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 @@ -820,6 +820,12 @@ export interface FeatureTogglesInterface { */ a11yHideConsentButtonWhenBannerVisible?: boolean; + /** + * Adds a unique `aria-label` to repeating buttons that contain the same text. + * Affects: SetPreferredStoreComponent + */ + a11yRepeatingButtonsUniqueLabels?: boolean; + /** * Ensures that borders across all UI elements are visible and meet accessibility standards in high-contrast dark and light themes. * This change is applied globally to enhance usability for users relying on high-contrast modes. @@ -1091,6 +1097,7 @@ export const defaultFeatureToggles: Required = { a11yFocusOnCardAfterSelecting: false, a11ySearchableDropdownFirstElementFocus: false, a11yHideConsentButtonWhenBannerVisible: false, + a11yRepeatingButtonsUniqueLabels: false, a11yHighContrastBorders: false, occCartNameAndDescriptionInHttpRequestBody: false, cmsBottomHeaderSlotUsingFlexStyles: false, diff --git a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts index f9a02531c6c..08ca9f9e27c 100644 --- a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts +++ b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts @@ -420,6 +420,7 @@ if (environment.cpq) { a11yFocusOnCardAfterSelecting: true, a11ySearchableDropdownFirstElementFocus: true, a11yHideConsentButtonWhenBannerVisible: true, + a11yRepeatingButtonsUniqueLabels: true, a11yHighContrastBorders: true, cmsBottomHeaderSlotUsingFlexStyles: true, useSiteThemeService: true, diff --git a/projects/storefrontlib/cms-components/product/product-list/product-list-item/product-list-item.component.html b/projects/storefrontlib/cms-components/product/product-list/product-list-item/product-list-item.component.html index c54dbe95eb2..e80bb446705 100644 --- a/projects/storefrontlib/cms-components/product/product-list/product-list-item/product-list-item.component.html +++ b/projects/storefrontlib/cms-components/product/product-list/product-list-item/product-list-item.component.html @@ -20,7 +20,7 @@
-

+

Date: Mon, 13 Jan 2025 14:50:01 +0100 Subject: [PATCH 3/9] fix: (CXSPA-9003) fix issue with checkout country/region label read twice (#19834) Closes: https://jira.tools.sap/browse/CXSPA-9003 --- .../address-book/address-form/address-form.component.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/feature-libs/user/profile/components/address-book/address-form/address-form.component.html b/feature-libs/user/profile/components/address-book/address-form/address-form.component.html index 39b63cc2f42..3a439c9055d 100644 --- a/feature-libs/user/profile/components/address-book/address-form/address-form.component.html +++ b/feature-libs/user/profile/components/address-book/address-form/address-form.component.html @@ -23,9 +23,7 @@ bindValue="isocode" placeholder="{{ 'addressForm.selectOne' | cxTranslate }}" (change)="countrySelected($event)" - [cxNgSelectA11y]="{ - ariaLabel: 'addressForm.country' | cxTranslate, - }" + [cxNgSelectA11y]="{}" > Date: Mon, 13 Jan 2025 09:58:54 -0500 Subject: [PATCH 4/9] fix: add opf-load-once capability on js and css resources (#19825) CXSPA-8883 --- .../opf-resource-loader.service.spec.ts | 2 + .../services/opf-resource-loader.service.ts | 71 +++++++++++-------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/integration-libs/opf/base/root/services/opf-resource-loader.service.spec.ts b/integration-libs/opf/base/root/services/opf-resource-loader.service.spec.ts index 76b77c4b9d4..b4d0c8bcda0 100644 --- a/integration-libs/opf/base/root/services/opf-resource-loader.service.spec.ts +++ b/integration-libs/opf/base/root/services/opf-resource-loader.service.spec.ts @@ -50,11 +50,13 @@ describe('OpfResourceLoaderService', () => { const mockScriptResource = { url: 'script-url', type: OpfDynamicScriptResourceType.SCRIPT, + attributes: [{ key: 'opf-load-once', value: 'true' }], }; const mockStyleResource = { url: 'style-url', type: OpfDynamicScriptResourceType.STYLES, + attributes: [{ key: 'opf-load-once', value: 'true' }], }; spyOn(opfResourceLoaderService, 'loadScript').and.callThrough(); diff --git a/integration-libs/opf/base/root/services/opf-resource-loader.service.ts b/integration-libs/opf/base/root/services/opf-resource-loader.service.ts index c9020a9d3e3..5bb3fb4e9d7 100644 --- a/integration-libs/opf/base/root/services/opf-resource-loader.service.ts +++ b/integration-libs/opf/base/root/services/opf-resource-loader.service.ts @@ -22,11 +22,12 @@ export class OpfResourceLoaderService { protected document = inject(DOCUMENT); protected platformId = inject(PLATFORM_ID); - protected readonly OPF_RESOURCE_ATTRIBUTE_KEY = 'data-opf-resource'; protected readonly CORS_DEFAULT_VALUE = 'anonymous'; + protected readonly OPF_RESOURCE_LOAD_ONCE_ATTRIBUTE_KEY = 'opf-load-once'; + protected readonly OPF_RESOURCE_ATTRIBUTE_KEY = 'data-opf-resource'; protected embedStyles(embedOptions: { - attributes?: OpfKeyValueMap[]; + attributes?: { [key: string]: string }; src: string; sri?: string; callback?: EventListener; @@ -38,22 +39,18 @@ export class OpfResourceLoaderService { link.href = src; link.rel = 'stylesheet'; link.type = 'text/css'; - link.setAttribute(this.OPF_RESOURCE_ATTRIBUTE_KEY, 'true'); if (sri) { link.integrity = sri; - const corsKeyvalue = attributes?.find( - (attr) => attr.key === 'crossorigin' && !!attr.value?.length - ); - link.crossOrigin = corsKeyvalue?.value ?? this.CORS_DEFAULT_VALUE; + link.crossOrigin = attributes?.['crossorigin'] ?? this.CORS_DEFAULT_VALUE; + delete attributes?.['crossorigin']; } - if (attributes?.length) { - attributes.forEach((attribute) => { - const { key, value } = attribute; + + attributes && + Object.keys(attributes)?.forEach((key) => { if (!(key in link)) { - link.setAttribute(key, value); + link.setAttribute(key, attributes[key as keyof object]); } }); - } if (callback) { link.addEventListener('load', callback); @@ -74,39 +71,53 @@ export class OpfResourceLoaderService { return this.scriptLoader.hasScript(src); } + /** + * Create attributes intended to script and link elements. + * + * Return attributes list including keyValueList and OPF specific attribute with below logic: + * + * 1. Resource loads only once: 'opf-load-once' key detected, no additional attribute added. + * 2. Resource deleted at page/payment change: 'data-opf-resource' attribute is added. + */ + + protected createAttributesList(keyValueList?: OpfKeyValueMap[] | undefined): { + [key: string]: string; + } { + const attributes: { [key: string]: string } = {}; + keyValueList?.forEach((keyValue: OpfKeyValueMap) => { + attributes[keyValue.key] = keyValue.value; + }); + if (attributes?.[this.OPF_RESOURCE_LOAD_ONCE_ATTRIBUTE_KEY] === 'true') { + attributes[this.OPF_RESOURCE_ATTRIBUTE_KEY] = 'true'; + } + delete attributes?.[this.OPF_RESOURCE_LOAD_ONCE_ATTRIBUTE_KEY]; + return attributes; + } + /** * Loads a script specified in the resource object. * * The returned Promise is resolved when the script is loaded or already present. * The returned Promise is rejected when a loading error occurs. */ + protected loadScript(resource: OpfDynamicScriptResource): Promise { return new Promise((resolve, reject) => { - const attributes: any = { + const attributes: { [key: string]: string } = { type: 'text/javascript', - [this.OPF_RESOURCE_ATTRIBUTE_KEY]: true, + ...this.createAttributesList(resource.attributes), }; if (resource?.sri) { attributes['integrity'] = resource.sri; - const corsKeyvalue: OpfKeyValueMap | undefined = - resource?.attributes?.find( - (attr) => attr.key === 'crossorigin' && !!attr.value?.length - ); attributes['crossOrigin'] = - corsKeyvalue?.value ?? this.CORS_DEFAULT_VALUE; - } - - if (resource.attributes) { - resource.attributes.forEach((attribute) => { - attributes[attribute.key] = attribute.value; - }); + attributes?.['crossorigin'] ?? this.CORS_DEFAULT_VALUE; + delete attributes?.['crossorigin']; } - - if (resource.url && !this.hasScript(resource.url)) { + if (resource?.url && !this.hasScript(resource.url)) { this.scriptLoader.embedScript({ + attributes, src: resource.url, - attributes: attributes, callback: () => resolve(), errorCallback: () => reject(), disableKeyRestriction: true, @@ -123,11 +134,12 @@ export class OpfResourceLoaderService { * The returned Promise is resolved when the stylesheet is loaded or already present. * The returned Promise is rejected when a loading error occurs. */ + protected loadStyles(resource: OpfDynamicScriptResource): Promise { return new Promise((resolve, reject) => { if (resource.url && !this.hasStyles(resource.url)) { this.embedStyles({ - attributes: resource?.attributes, + attributes: this.createAttributesList(resource?.attributes), src: resource.url, sri: resource?.sri, callback: () => resolve(), @@ -167,6 +179,7 @@ export class OpfResourceLoaderService { * The returned Promise is resolved when all resources are loaded. * The returned Promise is also resolved (not rejected!) immediately when any loading error occurs. */ + loadResources( scripts: OpfDynamicScriptResource[] = [], styles: OpfDynamicScriptResource[] = [] From 125c6a91bc45c8543a7bc47b31cb0e9430a92e94 Mon Sep 17 00:00:00 2001 From: petarmarkov9449 <166535589+petarmarkov9449@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:25:54 +0200 Subject: [PATCH 5/9] fix: (CXSPA-9007) - Fix 'dialog' is conveyed twice in ConsentManagementDialog (#19843) --- .../anonymous-consent-dialog.component.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/projects/storefrontlib/shared/components/anonymous-consents-dialog/anonymous-consent-dialog.component.ts b/projects/storefrontlib/shared/components/anonymous-consents-dialog/anonymous-consent-dialog.component.ts index 54a5680fec1..f4c9aa07422 100644 --- a/projects/storefrontlib/shared/components/anonymous-consents-dialog/anonymous-consent-dialog.component.ts +++ b/projects/storefrontlib/shared/components/anonymous-consents-dialog/anonymous-consent-dialog.component.ts @@ -7,7 +7,6 @@ import { Component, ElementRef, - HostBinding, HostListener, inject, OnDestroy, @@ -35,9 +34,6 @@ import { LaunchDialogService } from '../../../layout/launch-dialog/services/laun templateUrl: './anonymous-consent-dialog.component.html', }) export class AnonymousConsentDialogComponent implements OnInit, OnDestroy { - @HostBinding('attr.role') role = 'dialog'; - @HostBinding('attr.aria-modal') modal = true; - private subscriptions = new Subscription(); private featureConfigService = inject(FeatureConfigService); From e7b7e653ec7e07b35787b6402c39ae4183606848 Mon Sep 17 00:00:00 2001 From: Florent Letendre Date: Tue, 14 Jan 2025 09:33:51 -0500 Subject: [PATCH 6/9] fix: add resource load-once capability (#19859) CXSPA-8883 Option to load css and js files only once rather than deleted and re-adding at each payment/page changes. This option is toggled on OPF server server side by adding 'opf-load-once'='true' attribute to resource file. --- .../opf/base/root/services/opf-resource-loader.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integration-libs/opf/base/root/services/opf-resource-loader.service.ts b/integration-libs/opf/base/root/services/opf-resource-loader.service.ts index 5bb3fb4e9d7..ca2d35f8f4d 100644 --- a/integration-libs/opf/base/root/services/opf-resource-loader.service.ts +++ b/integration-libs/opf/base/root/services/opf-resource-loader.service.ts @@ -87,7 +87,10 @@ export class OpfResourceLoaderService { keyValueList?.forEach((keyValue: OpfKeyValueMap) => { attributes[keyValue.key] = keyValue.value; }); - if (attributes?.[this.OPF_RESOURCE_LOAD_ONCE_ATTRIBUTE_KEY] === 'true') { + if ( + !attributes?.[this.OPF_RESOURCE_LOAD_ONCE_ATTRIBUTE_KEY] || + attributes[this.OPF_RESOURCE_LOAD_ONCE_ATTRIBUTE_KEY] !== 'true' + ) { attributes[this.OPF_RESOURCE_ATTRIBUTE_KEY] = 'true'; } delete attributes?.[this.OPF_RESOURCE_LOAD_ONCE_ATTRIBUTE_KEY]; From 22476ef1e4c5d1b99e006b944123dba4da96e9e8 Mon Sep 17 00:00:00 2001 From: petarmarkov9449 <166535589+petarmarkov9449@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:28:33 +0100 Subject: [PATCH 7/9] fix: (CXSPA-9009) - Fix convey input field errors on repeat navigation (#19852) --- .../asm-create-customer-form.component.html | 9 +++++ .../csagent-login-form.component.html | 6 +++ .../customer-selection.component.html | 3 ++ .../cart-coupon/cart-coupon.component.html | 3 ++ ...port-to-new-saved-cart-form.component.html | 7 +++- .../saved-cart-form-dialog.component.html | 6 +++ ...eckout-billing-address-form.component.html | 15 ++++++++ .../checkout-login.component.html | 6 +++ .../checkout-payment-form.component.html | 24 ++++++++++++ ...omer-ticketing-close-dialog.component.html | 7 +++- ...mer-ticketing-reopen-dialog.component.html | 7 +++- ...mer-ticketing-create-dialog.component.html | 14 ++++++- .../order-guest-register-form.component.html | 6 +++ ...unt-summary-document-filter.component.html | 9 +++++ .../budget/form/budget-form.component.html | 9 +++++ .../form/cost-center-form.component.html | 6 +++ .../form/permission-form.component.html | 6 +++ .../unit/form/unit-form.component.html | 6 +++ .../form/unit-address-form.component.html | 15 ++++++++ .../form/user-group-form.component.html | 6 +++ .../user-change-password-form.component.html | 6 +++ .../user/form/user-form.component.html | 9 +++++ .../order-approval-detail-form.component.html | 3 ++ .../user-registration-form.component.html | 38 ++++++++++++++++++- .../login-form/login-form.component.html | 6 +++ .../otp-login-form.component.html | 14 ++++++- .../address-form/address-form.component.html | 15 ++++++++ .../forgot-password.component.html | 3 ++ .../register/register.component.html | 15 ++++++++ .../reset-password.component.html | 6 +++ .../my-account-v2-email.component.html | 9 +++++ .../update-email/update-email.component.html | 9 +++++ .../my-account-v2-password.component.html | 6 +++ .../update-password.component.html | 9 +++++ .../my-account-v2-profile.component.html | 9 +++++ .../update-profile.component.html | 9 +++++ .../claim-dialog/claim-dialog.component.html | 3 ++ .../product-reviews.component.html | 9 +++++ .../date-picker/date-picker.component.html | 3 ++ 39 files changed, 342 insertions(+), 9 deletions(-) diff --git a/feature-libs/asm/components/asm-create-customer-form/asm-create-customer-form.component.html b/feature-libs/asm/components/asm-create-customer-form/asm-create-customer-form.component.html index 80a3a9f3b21..0e7c14a6698 100644 --- a/feature-libs/asm/components/asm-create-customer-form/asm-create-customer-form.component.html +++ b/feature-libs/asm/components/asm-create-customer-form/asm-create-customer-form.component.html @@ -49,10 +49,13 @@

diff --git a/feature-libs/cart/saved-cart/components/saved-cart-form-dialog/saved-cart-form-dialog.component.html b/feature-libs/cart/saved-cart/components/saved-cart-form-dialog/saved-cart-form-dialog.component.html index 1936e7f93f4..d2e90b698d9 100644 --- a/feature-libs/cart/saved-cart/components/saved-cart-form-dialog/saved-cart-form-dialog.component.html +++ b/feature-libs/cart/saved-cart/components/saved-cart-form-dialog/saved-cart-form-dialog.component.html @@ -236,6 +236,7 @@ required="true" [maxLength]="nameMaxLength" class="form-control" + [attr.aria-describedby]="'nameError'" formControlName="name" required type="text" @@ -244,6 +245,8 @@ @@ -276,6 +280,8 @@ @@ -107,8 +110,11 @@ class="form-control" placeholder="{{ 'addressForm.lastName.placeholder' | cxTranslate }}" formControlName="lastName" + [attr.aria-describedby]="'lastNameError'" /> @@ -125,8 +131,11 @@ class="form-control" placeholder="{{ 'addressForm.streetAddress' | cxTranslate }}" formControlName="line1" + [attr.aria-describedby]="'line1Error'" /> @@ -157,8 +166,11 @@ class="form-control" placeholder="{{ 'addressForm.city.placeholder' | cxTranslate }}" formControlName="town" + [attr.aria-describedby]="'townError'" /> @@ -177,8 +189,11 @@ 'addressForm.zipCode.placeholder' | cxTranslate }}" formControlName="postalCode" + [attr.aria-describedby]="'postalCodeError'" /> diff --git a/feature-libs/checkout/base/components/checkout-login/checkout-login.component.html b/feature-libs/checkout/base/components/checkout-login/checkout-login.component.html index 1cad8005f2a..83cb8889fdc 100644 --- a/feature-libs/checkout/base/components/checkout-login/checkout-login.component.html +++ b/feature-libs/checkout/base/components/checkout-login/checkout-login.component.html @@ -17,11 +17,14 @@ placeholder="{{ 'checkoutLogin.emailAddress.placeholder' | cxTranslate }}" + [attr.aria-describedby]="'emailError'" /> - +

{{ diff --git a/feature-libs/customer-ticketing/components/details/customer-ticketing-reopen/customer-ticketing-reopen-dialog/customer-ticketing-reopen-dialog.component.html b/feature-libs/customer-ticketing/components/details/customer-ticketing-reopen/customer-ticketing-reopen-dialog/customer-ticketing-reopen-dialog.component.html index 9207b6db8e7..01c41cc2df4 100644 --- a/feature-libs/customer-ticketing/components/details/customer-ticketing-reopen/customer-ticketing-reopen-dialog/customer-ticketing-reopen-dialog.component.html +++ b/feature-libs/customer-ticketing/components/details/customer-ticketing-reopen/customer-ticketing-reopen-dialog/customer-ticketing-reopen-dialog.component.html @@ -34,9 +34,14 @@ class="form-control" formControlName="message" rows="5" + [attr.aria-describedby]="'messageError'" > - +

{{ diff --git a/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create-dialog/customer-ticketing-create-dialog.component.html b/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create-dialog/customer-ticketing-create-dialog.component.html index 56179763eef..2bae1dd4e51 100644 --- a/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create-dialog/customer-ticketing-create-dialog.component.html +++ b/feature-libs/customer-ticketing/components/list/customer-ticketing-create/customer-ticketing-create-dialog/customer-ticketing-create-dialog.component.html @@ -38,9 +38,14 @@ class="form-control" formControlName="subject" rows="1" + [attr.aria-describedby]="'subjectError'" > - +

{{ 'customerTicketing.charactersLeft' @@ -110,9 +115,14 @@ class="form-control" formControlName="message" rows="5" + [attr.aria-describedby]="'messageError'" > - +

{{ diff --git a/feature-libs/order/components/order-confirmation/order-guest-register-form/order-guest-register-form.component.html b/feature-libs/order/components/order-confirmation/order-guest-register-form/order-guest-register-form.component.html index 5fce3dc6482..098b33d3e2c 100644 --- a/feature-libs/order/components/order-confirmation/order-guest-register-form/order-guest-register-form.component.html +++ b/feature-libs/order/components/order-confirmation/order-guest-register-form/order-guest-register-form.component.html @@ -26,12 +26,15 @@

{{ 'checkoutOrderConfirmation.createAccount' | cxTranslate }}

placeholder="{{ 'register.password.placeholder' | cxTranslate }}" formControlName="password" [attr.aria-label]="'register.password.placeholder' | cxTranslate" + [attr.aria-describedby]="'passwordError'" cxPasswordVisibilitySwitch /> {{ 'checkoutOrderConfirmation.createAccount' | cxTranslate }} [attr.aria-label]=" 'register.confirmPassword.placeholder' | cxTranslate " + [attr.aria-describedby]="'passwordconfError'" cxPasswordVisibilitySwitch />
diff --git a/feature-libs/user/profile/components/address-book/address-form/address-form.component.html b/feature-libs/user/profile/components/address-book/address-form/address-form.component.html index 3a439c9055d..24a28a33e96 100644 --- a/feature-libs/user/profile/components/address-book/address-form/address-form.component.html +++ b/feature-libs/user/profile/components/address-book/address-form/address-form.component.html @@ -78,9 +78,12 @@ type="text" placeholder="{{ 'addressForm.firstName.placeholder' | cxTranslate }}" formControlName="firstName" + [attr.aria-describedby]="'firstNameError'" /> {{ 'register.confirmThatRead' | cxTranslate }} @@ -282,6 +295,8 @@ @@ -63,6 +67,8 @@ 'myCoupons.claimCouponCode.placeholder' | cxTranslate }}" class="form-control" + [attr.aria-describedby]="'couponCodeError'" /> {{ 'productReview.overallRating' | cxTranslate }} type="text" class="form-control" formControlName="title" + [attr.aria-describedby]="'titleError'" /> {{ 'productReview.overallRating' | cxTranslate }} class="form-control" rows="3" formControlName="comment" + [attr.aria-describedby]="'commentError'" > {{ 'productReview.overallRating' | cxTranslate }} type="number" formControlName="rating" class="rating-input" + [attr.aria-describedby]="'ratingError'" /> {{ 'productReview.overallRating' | cxTranslate }} Date: Thu, 16 Jan 2025 10:55:08 +0100 Subject: [PATCH 8/9] fix: (CXSPA-9005) fix ng-select options issue - is conveyed twice (#19844) Closes https://jira.tools.sap/browse/CXSPA-9005 --- .../feature-toggles/config/feature-toggles.ts | 2 ++ .../app/spartacus/spartacus-features.module.ts | 7 ++++++- .../ng-select-a11y/ng-select-a11y.directive.ts | 18 +++++++++++++++--- 3 files changed, 23 insertions(+), 4 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..5de9b0a1893 100644 --- a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts +++ b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts @@ -380,7 +380,12 @@ 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 + * TODO: CXSPA-9005: Remove this flag and related code in next major release + */ + a11yNgSelectOptionsCount: false, a11yNgSelectCloseDropdownOnEscape: true, a11yRepeatedCancelOrderError: true, a11yAddedToCartActiveDialog: true, 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..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 @@ -19,13 +19,13 @@ import { Renderer2, SecurityContext, } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DomSanitizer } from '@angular/platform-browser'; +import { NgSelectComponent } from '@ng-select/ng-select'; 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 { map } from 'rxjs/operators'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { BREAKPOINT, BreakpointService } from '../../../layout'; const ARIA_LABEL = 'aria-label'; @@ -40,6 +40,10 @@ 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); @@ -47,6 +51,10 @@ export class NgSelectA11yDirective implements AfterViewInit { 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; @@ -138,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 From b15d92409f7ad496604586848cdd6b520fcf5ae2 Mon Sep 17 00:00:00 2001 From: Uros Lates Date: Thu, 16 Jan 2025 11:35:15 +0100 Subject: [PATCH 9/9] fix: (CXSPA-9012) fix breadcrumbs current page ariaLabel (#19862) Closes: https://jira.tools.sap/browse/CXSPA-9012 --- projects/assets/src/translations/en/common.json | 1 + .../breadcrumb/breadcrumb.component.html | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/projects/assets/src/translations/en/common.json b/projects/assets/src/translations/en/common.json index a96f9e87010..2af166452f4 100644 --- a/projects/assets/src/translations/en/common.json +++ b/projects/assets/src/translations/en/common.json @@ -21,6 +21,7 @@ "search": "Search", "add": "Add", "breadcrumbs": "breadcrumbs", + "breadcrumbsCurrentPage": "Current Page", "selectFile": "Select file", "clear": "Clear", "loaded": "Loaded", diff --git a/projects/storefrontlib/cms-components/navigation/breadcrumb/breadcrumb.component.html b/projects/storefrontlib/cms-components/navigation/breadcrumb/breadcrumb.component.html index 39ad7ed124a..507bc0dea02 100644 --- a/projects/storefrontlib/cms-components/navigation/breadcrumb/breadcrumb.component.html +++ b/projects/storefrontlib/cms-components/navigation/breadcrumb/breadcrumb.component.html @@ -1,7 +1,17 @@