Skip to content

Commit

Permalink
fix: (CXSPA-7957) - Add to wishlist restore focus on activation (#19225)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pio-Bar authored Sep 25, 2024
1 parent 0a5ce27 commit 36843d7
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"
>
<button
#removeFromWishlistButton
class="btn btn-tertiary button-remove"
(click)="remove(entry)"
[disabled]="loading$ | async"
Expand All @@ -24,6 +25,7 @@
</div>
<ng-template #addItem>
<button
#addToWishlistButton
class="btn btn-tertiary button-add"
(click)="add(product)"
[disabled]="loading$ | async"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { Cart, OrderEntry } from '@spartacus/cart/base/root';
import { WishListFacade } from '@spartacus/cart/wish-list/root';
import { AuthService, I18nTestingModule, Product } from '@spartacus/core';
import {
AuthService,
FeatureConfigService,
I18nTestingModule,
Product,
} from '@spartacus/core';
import { CurrentProductService } from '@spartacus/storefront';
import { MockFeatureDirective } from 'projects/storefrontlib/shared/test/mock-feature-directive';
import { BehaviorSubject, Observable, of } from 'rxjs';
Expand Down Expand Up @@ -93,6 +98,12 @@ class MockUrlPipe implements PipeTransform {
transform(): any {}
}

class MockFeatureConfigService {
isEnabled() {
return true;
}
}

@Directive({
selector: '[cxAtMessage]',
})
Expand Down Expand Up @@ -123,6 +134,7 @@ describe('AddToWishListComponent', () => {
provide: CurrentProductService,
useClass: MockCurrentProductService,
},
{ provide: FeatureConfigService, useClass: MockFeatureConfigService },
],
})
.overrideComponent(AddToWishListComponent, {
Expand Down Expand Up @@ -239,4 +251,36 @@ describe('AddToWishListComponent', () => {
});
});
});

describe('restoreFocus', () => {
it('should refocus on removeFromWishlistButton', () => {
component.removeFromWishlistButton = {
nativeElement: {
focus: jasmine.createSpy('focus'),
},
};
component.loading$ = of(false);

component['restoreFocus']();

expect(
component.removeFromWishlistButton.nativeElement.focus
).toHaveBeenCalled();
});

it('should refocus on addToWishlistButton', () => {
component.addToWishlistButton = {
nativeElement: {
focus: jasmine.createSpy('focus'),
},
} as any;

component.loading$ = of(false);
component['restoreFocus']();

expect(
component.addToWishlistButton.nativeElement.focus
).toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,25 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { ChangeDetectionStrategy, Component } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
inject,
ViewChild,
} from '@angular/core';
import { OrderEntry } from '@spartacus/cart/base/root';
import { WishListFacade } from '@spartacus/cart/wish-list/root';
import {
AuthService,
Product,
FeatureConfigService,
isNotNullable,
Product,
useFeatureStyles,
} from '@spartacus/core';
import { CurrentProductService, ICON_TYPE } from '@spartacus/storefront';
import { Observable } from 'rxjs';
import { filter, map, tap } from 'rxjs/operators';
import { filter, map, take, tap } from 'rxjs/operators';

@Component({
selector: 'cx-add-to-wishlist',
Expand All @@ -31,6 +38,11 @@ export class AddToWishListComponent {
wishListEntries$: Observable<OrderEntry[]>;
loading$: Observable<boolean>;

@ViewChild('addToWishlistButton') addToWishlistButton: ElementRef;
@ViewChild('removeFromWishlistButton') removeFromWishlistButton: ElementRef;

private featureConfigService = inject(FeatureConfigService);

userLoggedIn$: Observable<boolean> = this.authService.isUserLoggedIn().pipe(
tap((isLogin) => {
if (isLogin) {
Expand All @@ -54,11 +66,17 @@ export class AddToWishListComponent {
add(product: Product): void {
if (product.code) {
this.wishListFacade.addEntry(product.code);
if (this.featureConfigService.isEnabled('a11yAddToWishlistFocus')) {
this.restoreFocus();
}
}
}

remove(entry: OrderEntry): void {
this.wishListFacade.removeEntry(entry);
if (this.featureConfigService.isEnabled('a11yAddToWishlistFocus')) {
this.restoreFocus();
}
}

getProductInWishList(
Expand All @@ -81,4 +99,20 @@ export class AddToWishListComponent {
map((wishList) => wishList.entries ?? [])
);
}

/**
* When disabling the button, the focus gets lost unexpecedly.
* This method makes sure that it is restored after.
*/
protected restoreFocus(): void {
this.loading$
.pipe(
filter((isLoading) => !isLoading),
take(1)
)
.subscribe(() => {
this.removeFromWishlistButton?.nativeElement.focus() ||
this.addToWishlistButton?.nativeElement.focus();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,11 @@ export interface FeatureTogglesInterface {
*/
a11yDialogsHeading?: boolean;

/**
* The 'AddToWishListComponent' will restore focus to the button after adding or removing an item from the wishlist.
*/
a11yAddToWishlistFocus?: boolean;

/**
* `SearchBoxComponent` should no longer lose focus after closing the popup the esc key.
*/
Expand Down Expand Up @@ -635,6 +640,7 @@ export const defaultFeatureToggles: Required<FeatureTogglesInterface> = {
a11yQuickOrderAriaControls: false,
a11yRemoveStatusLoadedRole: false,
a11yDialogsHeading: false,
a11yAddToWishlistFocus: false,
a11ySearchBoxFocusOnEscape: false,
occCartNameAndDescriptionInHttpRequestBody: false,
cmsBottomHeaderSlotUsingFlexStyles: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ if (environment.cpq) {
a11yQuickOrderAriaControls: true,
a11yRemoveStatusLoadedRole: true,
a11yDialogsHeading: true,
a11yAddToWishlistFocus: true,
a11ySearchBoxFocusOnEscape: true,
cmsBottomHeaderSlotUsingFlexStyles: true,
useSiteThemeService: false,
Expand Down

0 comments on commit 36843d7

Please sign in to comment.