diff --git a/core-libs/assets/src/translations/en/myAccount.json b/core-libs/assets/src/translations/en/myAccount.json index f370006eed1..cb8efdfc4d2 100644 --- a/core-libs/assets/src/translations/en/myAccount.json +++ b/core-libs/assets/src/translations/en/myAccount.json @@ -24,7 +24,8 @@ }, "bothPasswordMustMatch": "Both password must match", "passwordUpdateSuccess": "Password updated with success", - "accessDeniedError": "Access is denied" + "accessDeniedError": "Access is denied", + "heading": "Update Password" }, "updateProfileForm": { "title": "Title", @@ -40,11 +41,13 @@ }, "lastNameIsRequired": "Last name is required.", "profileUpdateSuccess": "Personal details successfully updated", - "customerId": "Customer #" + "customerId": "Customer #", + "heading": "Update Personal Details" }, "consentManagementForm": { "clearAll": "Clear all", "selectAll": "Select all", + "heading": "Privacy Consent Management", "message": { "success": { "given": "Consent successfully given.", diff --git a/core-libs/core/src/features-config/feature-toggles/config/feature-toggles.ts b/core-libs/core/src/features-config/feature-toggles/config/feature-toggles.ts index 21426f8f028..122ad390443 100644 --- a/core-libs/core/src/features-config/feature-toggles/config/feature-toggles.ts +++ b/core-libs/core/src/features-config/feature-toggles/config/feature-toggles.ts @@ -395,6 +395,17 @@ export interface FeatureTogglesInterface { */ a11yDeliveryModeFocusPreservation?: boolean; + /** + * When enabled, wraps form controls in a `
` with a visually-hidden `` + * that names the section, so screen readers announce the section heading when any + * field in the group receives focus. + * Affects: UpdateProfileComponent, MyAccountV2ProfileComponent, + * UpdatePasswordComponent, MyAccountV2PasswordComponent, + * UpdateEmailComponent, MyAccountV2EmailComponent, + * ConsentManagementComponent + */ + a11yFormFieldSectionLegend?: boolean; + /** * When enabled, `AuthHttpHeaderService` executes DI-provided * `ExpiredRefreshTokenHandler` to take over `handleExpiredRefreshToken()` behavior in case of expired refresh token scenarios. @@ -613,6 +624,7 @@ export const defaultFeatureToggles: Required = { a11yCartQuickOrderFormEnableSubmitAndAddValidation: false, a11yConsentManagementFocusPreservation: false, a11yDeliveryModeFocusPreservation: false, + a11yFormFieldSectionLegend: false, a11yVocalizeDropdownItemCount: false, a11yRestoreFocusOnNgSelect: false, a11yKeepFocusOnConsentManagementButtons: false, diff --git a/core-libs/storefront/cms-components/myaccount/consent-management/components/consent-management.component.html b/core-libs/storefront/cms-components/myaccount/consent-management/components/consent-management.component.html index 2d6eac978a9..0208cc5499a 100644 --- a/core-libs/storefront/cms-components/myaccount/consent-management/components/consent-management.component.html +++ b/core-libs/storefront/cms-components/myaccount/consent-management/components/consent-management.component.html @@ -52,20 +52,43 @@ - diff --git a/feature-libs/user/profile/components/update-email/my-account-v2-email.component.spec.ts b/feature-libs/user/profile/components/update-email/my-account-v2-email.component.spec.ts index 959f78b7dd4..e340667b4f8 100644 --- a/feature-libs/user/profile/components/update-email/my-account-v2-email.component.spec.ts +++ b/feature-libs/user/profile/components/update-email/my-account-v2-email.component.spec.ts @@ -12,6 +12,7 @@ import { import { By } from '@angular/platform-browser'; import { CxDatePipe, + FeatureDirective, GlobalMessageService, I18nTestingModule, MockDatePipe, @@ -27,6 +28,7 @@ import { } from '@spartacus/storefront'; import { MockUrlPipe } from 'core-libs/core/src/routing/configurable-routes/url-translation/testing/mock-url.pipe'; import { UrlTestingModule } from 'core-libs/core/src/routing/configurable-routes/url-translation/testing/url-testing.module'; +import { MockFeatureDirective } from 'core-libs/storefront/shared/test/mock-feature-directive'; import { BehaviorSubject, Subject, of } from 'rxjs'; import { UserProfileFacade } from '../../root/facade'; import { MyAccountV2EmailComponent } from './my-account-v2-email.component'; @@ -89,6 +91,7 @@ describe('MyAccountV2EmailComponent', () => { FormErrorsModule, PasswordVisibilityToggleModule, MyAccountV2EmailComponent, + MockFeatureDirective, ], providers: [ { @@ -104,7 +107,13 @@ describe('MyAccountV2EmailComponent', () => { }) .overrideComponent(MyAccountV2EmailComponent, { remove: { - imports: [TranslatePipe, CxDatePipe, UrlPipe, SpinnerComponent], + imports: [ + TranslatePipe, + CxDatePipe, + UrlPipe, + SpinnerComponent, + FeatureDirective, + ], }, add: { imports: [ @@ -112,6 +121,7 @@ describe('MyAccountV2EmailComponent', () => { MockDatePipe, MockUrlPipe, MockCxSpinnerComponent, + MockFeatureDirective, ], changeDetection: ChangeDetectionStrategy.Default, }, @@ -215,4 +225,14 @@ describe('MyAccountV2EmailComponent', () => { expect(submitBtn).toBeNull(); }); }); + + describe('Accessibility', () => { + it('should render a fieldset with a visually-hidden legend inside the form when editing', () => { + component.onEdit(); + fixture.detectChanges(); + const legend = el.query(By.css('fieldset legend')); + expect(legend).toBeTruthy(); + expect(legend.nativeElement.classList).toContain('cx-visually-hidden'); + }); + }); }); diff --git a/feature-libs/user/profile/components/update-email/my-account-v2-email.component.ts b/feature-libs/user/profile/components/update-email/my-account-v2-email.component.ts index b040cb41705..540fe1ed588 100644 --- a/feature-libs/user/profile/components/update-email/my-account-v2-email.component.ts +++ b/feature-libs/user/profile/components/update-email/my-account-v2-email.component.ts @@ -16,7 +16,12 @@ import { ReactiveFormsModule, UntypedFormGroup, } from '@angular/forms'; -import { GlobalMessageType, TranslatePipe, User } from '@spartacus/core'; +import { + FeatureDirective, + GlobalMessageType, + TranslatePipe, + User, +} from '@spartacus/core'; import { FormErrorsComponent, MessageComponent, @@ -43,6 +48,7 @@ import { UpdateEmailComponentService } from './update-email-component.service'; PasswordVisibilityToggleDirective, AsyncPipe, TranslatePipe, + FeatureDirective, ], }) export class MyAccountV2EmailComponent implements OnInit { diff --git a/feature-libs/user/profile/components/update-email/update-email.component.html b/feature-libs/user/profile/components/update-email/update-email.component.html index 3a2893f7664..e049a714a45 100644 --- a/feature-libs/user/profile/components/update-email/update-email.component.html +++ b/feature-libs/user/profile/components/update-email/update-email.component.html @@ -1,103 +1,216 @@
- - - - - - - - {{ 'common.cancel' | cxTranslate }} - - - + +
+ + {{ 'updateEmailForm.heading' | cxTranslate }} + + + + + + + + + {{ 'common.cancel' | cxTranslate }} + + + +
+
+ + + + + + + + + {{ 'common.cancel' | cxTranslate }} + + + +
diff --git a/feature-libs/user/profile/components/update-email/update-email.component.spec.ts b/feature-libs/user/profile/components/update-email/update-email.component.spec.ts index 03329fcffeb..81432d9775e 100644 --- a/feature-libs/user/profile/components/update-email/update-email.component.spec.ts +++ b/feature-libs/user/profile/components/update-email/update-email.component.spec.ts @@ -163,4 +163,12 @@ describe('UpdateEmailComponent', () => { expect(service.save).toHaveBeenCalled(); }); }); + + describe('Accessibility', () => { + it('should render a fieldset with a visually-hidden legend inside the form', () => { + const legend = el.query(By.css('fieldset legend')); + expect(legend).toBeTruthy(); + expect(legend.nativeElement.classList).toContain('cx-visually-hidden'); + }); + }); }); diff --git a/feature-libs/user/profile/components/update-email/update-email.component.ts b/feature-libs/user/profile/components/update-email/update-email.component.ts index cee780437b9..6c5f97a3cc3 100644 --- a/feature-libs/user/profile/components/update-email/update-email.component.ts +++ b/feature-libs/user/profile/components/update-email/update-email.component.ts @@ -12,7 +12,7 @@ import { UntypedFormGroup, } from '@angular/forms'; import { RouterLink } from '@angular/router'; -import { TranslatePipe, UrlPipe } from '@spartacus/core'; +import { FeatureDirective, TranslatePipe, UrlPipe } from '@spartacus/core'; import { BtnLikeLinkDirective, FormErrorsComponent, @@ -43,6 +43,7 @@ import { UpdateEmailComponentService } from './update-email-component.service'; AsyncPipe, UrlPipe, TranslatePipe, + FeatureDirective, ], }) export class UpdateEmailComponent { diff --git a/feature-libs/user/profile/components/update-password/my-account-v2-password.component.html b/feature-libs/user/profile/components/update-password/my-account-v2-password.component.html index f9e460118c2..5615c8c8110 100644 --- a/feature-libs/user/profile/components/update-password/my-account-v2-password.component.html +++ b/feature-libs/user/profile/components/update-password/my-account-v2-password.component.html @@ -1,130 +1,265 @@
- - - - - - - - - - - -
- - + +
+
+ + + + + - {{ 'common.save' | cxTranslate }} - - + + + + + + + + +
+ + +
+
diff --git a/feature-libs/user/profile/components/update-password/my-account-v2-password.component.spec.ts b/feature-libs/user/profile/components/update-password/my-account-v2-password.component.spec.ts index 1d3036fb610..94e452aa66f 100644 --- a/feature-libs/user/profile/components/update-password/my-account-v2-password.component.spec.ts +++ b/feature-libs/user/profile/components/update-password/my-account-v2-password.component.spec.ts @@ -12,6 +12,7 @@ import { import { By } from '@angular/platform-browser'; import { CxDatePipe, + FeatureDirective, GlobalMessageService, I18nTestingModule, MockDatePipe, @@ -26,6 +27,7 @@ import { } from '@spartacus/storefront'; import { MockUrlPipe } from 'core-libs/core/src/routing/configurable-routes/url-translation/testing/mock-url.pipe'; import { UrlTestingModule } from 'core-libs/core/src/routing/configurable-routes/url-translation/testing/url-testing.module'; +import { MockFeatureDirective } from 'core-libs/storefront/shared/test/mock-feature-directive'; import { BehaviorSubject } from 'rxjs'; import { MyAccountV2PasswordComponent } from './my-account-v2-password.component'; import { UpdatePasswordComponentService } from './update-password-component.service'; @@ -76,6 +78,7 @@ describe('MyAccountV2PasswordComponent', () => { FormErrorsModule, PasswordVisibilityToggleModule, MyAccountV2PasswordComponent, + MockFeatureDirective, ], providers: [ { @@ -87,7 +90,13 @@ describe('MyAccountV2PasswordComponent', () => { }) .overrideComponent(MyAccountV2PasswordComponent, { remove: { - imports: [TranslatePipe, CxDatePipe, UrlPipe, SpinnerComponent], + imports: [ + TranslatePipe, + CxDatePipe, + UrlPipe, + SpinnerComponent, + FeatureDirective, + ], }, add: { imports: [ @@ -95,6 +104,7 @@ describe('MyAccountV2PasswordComponent', () => { MockDatePipe, MockUrlPipe, MockCxSpinnerComponent, + MockFeatureDirective, ], changeDetection: ChangeDetectionStrategy.Default, }, @@ -175,4 +185,12 @@ describe('MyAccountV2PasswordComponent', () => { expect(cxMsg).toBeNull(); }); }); + + describe('Accessibility', () => { + it('should render a fieldset with a visually-hidden legend inside the form', () => { + const legend = el.query(By.css('fieldset legend')); + expect(legend).toBeTruthy(); + expect(legend.nativeElement.classList).toContain('cx-visually-hidden'); + }); + }); }); diff --git a/feature-libs/user/profile/components/update-password/my-account-v2-password.component.ts b/feature-libs/user/profile/components/update-password/my-account-v2-password.component.ts index 7660ab38b3d..b83d4941b39 100644 --- a/feature-libs/user/profile/components/update-password/my-account-v2-password.component.ts +++ b/feature-libs/user/profile/components/update-password/my-account-v2-password.component.ts @@ -11,7 +11,11 @@ import { ReactiveFormsModule, UntypedFormGroup, } from '@angular/forms'; -import { GlobalMessageType, TranslatePipe } from '@spartacus/core'; +import { + FeatureDirective, + GlobalMessageType, + TranslatePipe, +} from '@spartacus/core'; import { FormErrorsComponent, MessageComponent, @@ -36,6 +40,7 @@ import { UpdatePasswordComponentService } from './update-password-component.serv FormErrorsComponent, AsyncPipe, TranslatePipe, + FeatureDirective, ], }) export class MyAccountV2PasswordComponent { diff --git a/feature-libs/user/profile/components/update-password/update-password.component.html b/feature-libs/user/profile/components/update-password/update-password.component.html index a9bb93a994e..01388250a57 100644 --- a/feature-libs/user/profile/components/update-password/update-password.component.html +++ b/feature-libs/user/profile/components/update-password/update-password.component.html @@ -1,110 +1,225 @@
-