diff --git a/feature-libs/checkout/base/components/checkout-delivery-address/checkout-delivery-address.component.spec.ts b/feature-libs/checkout/base/components/checkout-delivery-address/checkout-delivery-address.component.spec.ts index 4ffc059ad42..abc6fb649a4 100644 --- a/feature-libs/checkout/base/components/checkout-delivery-address/checkout-delivery-address.component.spec.ts +++ b/feature-libs/checkout/base/components/checkout-delivery-address/checkout-delivery-address.component.spec.ts @@ -199,6 +199,7 @@ describe('CheckoutDeliveryAddressComponent', () => { spyOn(component, 'addAddress').and.callThrough(); spyOn(component, 'selectAddress').and.callThrough(); spyOn(component, 'setAddress').and.callThrough(); + spyOn(component, 'getCardRole').and.callThrough(); }); it('should be created', () => { @@ -345,6 +346,41 @@ describe('CheckoutDeliveryAddressComponent', () => { ); expect(card.actions?.length).toBe(1); }); + + describe('role', () => { + beforeEach(() => { + spyOn(featureConfig, 'isEnabled').and.returnValue(true); + }); + it('should be set to "region" for selected address', () => { + expect( + component.getCardContent( + mockAddress1, + mockAddress1, + 'default', + 'shipTo', + 'selected', + 'P', + 'M' + ).role + ).toEqual('region'); + expect(component['getCardRole']).toHaveBeenCalledWith(true); + }); + + it('should be set to "button" for all non selected addresses', () => { + expect( + component.getCardContent( + mockAddress1, + mockAddress2, + 'default', + 'shipTo', + 'selected', + 'P', + 'M' + ).role + ).toEqual('button'); + expect(component['getCardRole']).toHaveBeenCalledWith(false); + }); + }); }); describe('UI continue button', () => { diff --git a/feature-libs/checkout/base/components/checkout-delivery-address/checkout-delivery-address.component.ts b/feature-libs/checkout/base/components/checkout-delivery-address/checkout-delivery-address.component.ts index ceb05c41b1a..04609ab5c90 100644 --- a/feature-libs/checkout/base/components/checkout-delivery-address/checkout-delivery-address.component.ts +++ b/feature-libs/checkout/base/components/checkout-delivery-address/checkout-delivery-address.component.ts @@ -118,8 +118,10 @@ export class CheckoutDeliveryAddressComponent implements OnInit { const numbers = getAddressNumbers(address, textPhone, textMobile); const isSelected: boolean = selected && selected.id === address.id; + const role = this.getCardRole(isSelected); + return { - role: 'region', + role, title: address.defaultAddress ? textDefaultDeliveryAddress : '', textBold: address.firstName + ' ' + address.lastName, text: [ @@ -225,7 +227,11 @@ export class CheckoutDeliveryAddressComponent implements OnInit { 'checkoutAddress.defaultDeliveryAddress' ), this.translationService.translate('checkoutAddress.shipToThisAddress'), - this.translationService.translate('addressCard.selected'), + this.featureConfigService?.isEnabled( + 'a11ySelectLabelWithContextForSelectedAddrOrPayment' + ) + ? this.translationService.translate('addressCard.selectedAddress') + : this.translationService.translate('addressCard.selected'), this.translationService.translate('addressCard.phoneNumber'), this.translationService.translate('addressCard.mobileNumber'), ]); @@ -329,4 +335,12 @@ export class CheckoutDeliveryAddressComponent implements OnInit { protected shouldUseAddressSavedInCart(): boolean { return !!this.checkoutConfigService?.shouldUseAddressSavedInCart(); } + + protected getCardRole(isCardSelected: boolean): 'button' | 'region' { + const isButtonRole = + this.featureConfigService?.isEnabled( + 'a11ySelectLabelWithContextForSelectedAddrOrPayment' + ) && !isCardSelected; + return isButtonRole ? 'button' : 'region'; + } } diff --git a/feature-libs/checkout/base/components/checkout-payment-method/checkout-payment-method.component.spec.ts b/feature-libs/checkout/base/components/checkout-payment-method/checkout-payment-method.component.spec.ts index 82ed2c11f85..ae49b1db0fb 100644 --- a/feature-libs/checkout/base/components/checkout-payment-method/checkout-payment-method.component.spec.ts +++ b/feature-libs/checkout/base/components/checkout-payment-method/checkout-payment-method.component.spec.ts @@ -617,5 +617,55 @@ describe('CheckoutPaymentMethodComponent', () => { ); expect(globalMessageService.add).not.toHaveBeenCalled(); }); + + describe('createCard().role', () => { + let paymentMethod1: PaymentDetails; + beforeEach(() => { + spyOn(featureConfig, 'isEnabled').and.returnValue(true); + paymentMethod1 = { + id: 'selected payment method', + accountHolderName: 'Name', + cardNumber: '123456789', + cardType: { + code: 'Visa', + name: 'Visa', + }, + expiryMonth: '01', + expiryYear: '2022', + cvn: '123', + defaultPayment: true, + }; + }); + + it('should be set to "region" for selected payment card', () => { + expect( + component['createCard']( + paymentMethod1, + { + textDefaultPaymentMethod: '✓ DEFAULT', + textExpires: 'Expires', + textUseThisPayment: 'Use this payment', + textSelected: 'Selected', + }, + paymentMethod1 + ).role + ).toEqual('region'); + }); + + it('should be set to "button" for non selected payment cards', () => { + expect( + component['createCard']( + paymentMethod1, + { + textDefaultPaymentMethod: '✓ DEFAULT', + textExpires: 'Expires', + textUseThisPayment: 'Use this payment', + textSelected: 'Selected', + }, + { ...paymentMethod1, id: 'newId' } + ).role + ).toEqual('button'); + }); + }); }); }); diff --git a/feature-libs/checkout/base/components/checkout-payment-method/checkout-payment-method.component.ts b/feature-libs/checkout/base/components/checkout-payment-method/checkout-payment-method.component.ts index 866decf0e04..cc9b0c9a391 100644 --- a/feature-libs/checkout/base/components/checkout-payment-method/checkout-payment-method.component.ts +++ b/feature-libs/checkout/base/components/checkout-payment-method/checkout-payment-method.component.ts @@ -151,7 +151,11 @@ export class CheckoutPaymentMethodComponent implements OnInit, OnDestroy { this.selectedMethod$, this.translationService.translate('paymentForm.useThisPayment'), this.translationService.translate('paymentCard.defaultPaymentMethod'), - this.translationService.translate('paymentCard.selected'), + this.featureConfigService?.isEnabled( + 'a11ySelectLabelWithContextForSelectedAddrOrPayment' + ) + ? this.translationService.translate('paymentCard.selectedPayment') + : this.translationService.translate('paymentCard.selected'), ]).pipe( tap(([paymentMethods, selectedMethod]) => this.selectDefaultPaymentMethod(paymentMethods, selectedMethod) @@ -299,9 +303,14 @@ export class CheckoutPaymentMethodComponent implements OnInit, OnDestroy { 'a11yHideSelectBtnForSelectedAddrOrPayment' ); const isSelected = selected?.id === paymentDetails.id; + const isButtonRole = + this.featureConfigService?.isEnabled( + 'a11ySelectLabelWithContextForSelectedAddrOrPayment' + ) && !isSelected; + const role = isButtonRole ? 'button' : 'region'; return { - role: 'region', + role, title: paymentDetails.defaultPayment ? cardLabels.textDefaultPaymentMethod : '', diff --git a/feature-libs/user/profile/assets/translations/en/address.json b/feature-libs/user/profile/assets/translations/en/address.json index 7b74ac04b06..bbfad2191ca 100644 --- a/feature-libs/user/profile/assets/translations/en/address.json +++ b/feature-libs/user/profile/assets/translations/en/address.json @@ -57,6 +57,7 @@ "addressCard": { "default": "DEFAULT", "selected": "Selected", + "selectedAddress": "Selected Address", "setAsDefault": "Set as default", "shipTo": "Ship To", "billTo": "Bill To", diff --git a/projects/assets/src/translations/en/payment.json b/projects/assets/src/translations/en/payment.json index a38ca12d4ab..b20f8300ed9 100644 --- a/projects/assets/src/translations/en/payment.json +++ b/projects/assets/src/translations/en/payment.json @@ -40,6 +40,7 @@ "defaultPaymentLabel": "Default payment method", "additionalPaymentLabel": "Additional payment method {{ number }}", "selected": "Selected", + "selectedPayment": "Selected Payment", "deletePaymentSuccess": "Payment method deleted successfully" }, "paymentTypes": { 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 5c0757de1ff..d8cf6df9599 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 @@ -324,6 +324,13 @@ export interface FeatureTogglesInterface { */ a11yHideSelectBtnForSelectedAddrOrPayment?: boolean; + /** + * If enabled, the "Checkout Shipping address/Payment" views + * will have a more a11y friendly selected label, including the context + * indicating weather the user is on a selected Address or Payment regsion. + */ + a11ySelectLabelWithContextForSelectedAddrOrPayment?: boolean; + /** * Determines whether the controls in the `CarouselComponent` are focusable and accessible from the keyboard. */ @@ -690,6 +697,7 @@ export const defaultFeatureToggles: Required = { a11yUnitsListKeyboardControls: true, a11yCartItemsLinksStyles: true, a11yHideSelectBtnForSelectedAddrOrPayment: false, + a11ySelectLabelWithContextForSelectedAddrOrPayment: false, a11yFocusableCarouselControls: true, a11yUseTrapTabInsteadOfTrapInDialogs: false, cmsGuardsServiceUseGuardsComposer: false, diff --git a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts index d05893f4f9d..30e56511751 100644 --- a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts +++ b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts @@ -333,6 +333,7 @@ if (environment.cpq) { a11yUnitsListKeyboardControls: true, a11yCartItemsLinksStyles: true, a11yHideSelectBtnForSelectedAddrOrPayment: true, + a11ySelectLabelWithContextForSelectedAddrOrPayment: true, a11yFocusableCarouselControls: true, a11yUseTrapTabInsteadOfTrapInDialogs: true, cmsGuardsServiceUseGuardsComposer: true,