Skip to content

Commit

Permalink
fix: (CXSPA-8605) - Cart delivery selector fix (#19371)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pio-Bar authored Oct 10, 2024
1 parent cf92e4a commit 901ddd4
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
[displayPickupLocation]="(storeDetails$ | async)?.displayName"
[selectedOption]="pickupOption$ | async"
(pickupOptionChange)="onPickupOptionChange($event)"
(pickupLocationChange)="openDialog()"
(pickupLocationChange)="openDialog($event)"
></cx-pickup-options>
</ng-container>
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { CommonModule } from '@angular/common';
import { ElementRef } from '@angular/core';
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing';
import { ActiveCartFacade, Cart, OrderEntry } from '@spartacus/cart/base/root';
import { CmsService, I18nTestingModule, Page } from '@spartacus/core';
import {
CmsService,
FeatureConfigService,
I18nTestingModule,
Page,
} from '@spartacus/core';
import {
AugmentedPointOfService,
IntendedPickupLocationFacade,
Expand All @@ -16,8 +22,8 @@ import {
PreferredStoreFacade,
} from '@spartacus/pickup-in-store/root';
import {
LaunchDialogService,
LAUNCH_CALLER,
LaunchDialogService,
OutletContextData,
} from '@spartacus/storefront';
import { cold } from 'jasmine-marbles';
Expand Down Expand Up @@ -109,6 +115,12 @@ const mockOutletContext: { item: OrderEntry; cartType: string } = {
cartType: 'cart',
};

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

const context$ = of(mockOutletContext);

class MockIntendedPickupLocationFacade {
Expand Down Expand Up @@ -167,6 +179,10 @@ describe('CartPickupOptionsContainerComponent', () => {
provide: IntendedPickupLocationFacade,
useClass: MockIntendedPickupLocationFacade,
},
{
provide: FeatureConfigService,
useClass: MockFeatureConfigService,
},
],
});

Expand Down Expand Up @@ -198,10 +214,11 @@ describe('CartPickupOptionsContainerComponent', () => {
});

it('should trigger and open dialog', () => {
component.openDialog();
const triggerElement = new ElementRef({});
component.openDialog(triggerElement);
expect(launchDialogService.openDialog).toHaveBeenCalledWith(
LAUNCH_CALLER.PICKUP_IN_STORE,
component.element,
triggerElement,
component['vcr'],
{ productCode: 'productCode1', entryNumber: 1, quantity: 1 }
);
Expand All @@ -210,14 +227,23 @@ describe('CartPickupOptionsContainerComponent', () => {
it('should not openDialog if display name is not set and ship it is selected', () => {
spyOn(component, 'openDialog');
component['displayNameIsSet'] = false;
component.onPickupOptionChange('delivery');
const pickupOption: PickupOption = 'delivery';
const event = {
option: pickupOption,
triggerElement: new ElementRef({}),
};
component.onPickupOptionChange(event);
expect(component.openDialog).not.toHaveBeenCalled();
});

it('should check call update Entry on pickup option change when option is pickup', fakeAsync(() => {
const entryNumber = 2;
const pickupOption: PickupOption = 'pickup';
const quantity = 3;
const event = {
option: pickupOption,
triggerElement: new ElementRef({}),
};

component.entryNumber = entryNumber;
component.quantity = quantity;
Expand All @@ -226,7 +252,7 @@ describe('CartPickupOptionsContainerComponent', () => {
spyOn(pickupOptionService, 'setPickupOption');
spyOn(activeCartService, 'updateEntry');

component.onPickupOptionChange(pickupOption);
component.onPickupOptionChange(event);
expect(pickupOptionService.setPickupOption).toHaveBeenCalledWith(
entryNumber,
pickupOption
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import {
Component,
ElementRef,
inject,
OnDestroy,
OnInit,
Optional,
Expand All @@ -18,7 +19,7 @@ import {
CartType,
OrderEntry,
} from '@spartacus/cart/base/root';
import { CmsService, Page } from '@spartacus/core';
import { CmsService, FeatureConfigService, Page } from '@spartacus/core';
import {
cartWithIdAndUserId,
getProperty,
Expand All @@ -30,8 +31,8 @@ import {
RequiredDeepPath,
} from '@spartacus/pickup-in-store/root';
import {
LaunchDialogService,
LAUNCH_CALLER,
LaunchDialogService,
OutletContextData,
} from '@spartacus/storefront';
import { EMPTY, iif, Observable, of, Subscription } from 'rxjs';
Expand Down Expand Up @@ -79,6 +80,12 @@ export function orderEntryWithRequiredFields(
templateUrl: 'cart-pickup-options-container.component.html',
})
export class CartPickupOptionsContainerComponent implements OnInit, OnDestroy {
// TODO: Remove element reference once 'a11yDialogTriggerRefocus' feature flag is removed.
/**
* @deprecated since 2211.28.0
* This reference does not point to any element and will be removed at earliest convinience.
* The 'triggerElement' is passed through 'PickupOptionChange' event instead.
*/
@ViewChild('open') element: ElementRef;

pickupOption$: Observable<PickupOption | undefined>;
Expand All @@ -98,6 +105,7 @@ export class CartPickupOptionsContainerComponent implements OnInit, OnDestroy {
private displayNameIsSet = false;
page?: string;
readonly CartType = CartType;
private featureConfigService = inject(FeatureConfigService);
constructor(
protected activeCartFacade: ActiveCartFacade,
protected launchDialogService: LaunchDialogService,
Expand Down Expand Up @@ -253,51 +261,108 @@ export class CartPickupOptionsContainerComponent implements OnInit, OnDestroy {
tap((_) => (this.displayNameIsSet = true))
);
}

onPickupOptionChange(pickupOption: PickupOption): void {
this.pickupOptionFacade.setPickupOption(this.entryNumber, pickupOption);
if (pickupOption === 'delivery') {
this.activeCartFacade.updateEntry(
this.entryNumber,
this.quantity,
undefined,
true
);
return;
}
[pickupOption]
.filter((option) => option === 'pickup')
.forEach(() => {
this.subscription.add(
this.storeDetails$
.pipe(
filter(({ name }) => !!name),
tap(({ name }) =>
this.activeCartFacade.updateEntry(
this.entryNumber,
this.quantity,
name,
true
// TODO: Remove 'PickupOption' argument type once 'a11yDialogTriggerRefocus' feature flag is removed.
/**
* @deprecated since 2211.28.0 - Use event param instead of option.
* @param event - Object containing the selected option and the element that triggered the change.
*/
onPickupOptionChange(pickupOption: PickupOption): void;
// eslint-disable-next-line @typescript-eslint/unified-signatures
onPickupOptionChange(event: {
option: PickupOption;
triggerElement: ElementRef;
}): void;
onPickupOptionChange(
event: { option: PickupOption; triggerElement: ElementRef } | PickupOption
): void {
/* istanbul ignore else */
if (
this.featureConfigService.isEnabled('a11yDialogTriggerRefocus') &&
typeof event === 'object'
) {
this.pickupOptionFacade.setPickupOption(this.entryNumber, event.option);
if (event.option === 'delivery') {
this.activeCartFacade.updateEntry(
this.entryNumber,
this.quantity,
undefined,
true
);
return;
}
[event.option]
.filter((option) => option === 'pickup')
.forEach(() => {
this.subscription.add(
this.storeDetails$
.pipe(
filter(({ name }) => !!name),
tap(({ name }) =>
this.activeCartFacade.updateEntry(
this.entryNumber,
this.quantity,
name,
true
)
)
)
)
.subscribe()
.subscribe()
);
});

if (!this.displayNameIsSet) {
this.openDialog(event.triggerElement);
}
} else if (typeof event === 'string') {
this.pickupOptionFacade.setPickupOption(this.entryNumber, event);
if (event === 'delivery') {
this.activeCartFacade.updateEntry(
this.entryNumber,
this.quantity,
undefined,
true
);
});
return;
}
[event]
.filter((option) => option === 'pickup')
.forEach(() => {
this.subscription.add(
this.storeDetails$
.pipe(
filter(({ name }) => !!name),
tap(({ name }) =>
this.activeCartFacade.updateEntry(
this.entryNumber,
this.quantity,
name,
true
)
)
)
.subscribe()
);
});

if (!this.displayNameIsSet) {
this.openDialog();
if (!this.displayNameIsSet) {
this.openDialog();
}
}
}

ngOnDestroy(): void {
this.subscription.unsubscribe();
}

openDialog(): void {
// TODO: Make argument required once 'a11yDialogTriggerRefocus' feature flag is removed.
/**
* @deprecated since 2211.28.0 - The use of TriggerElement param will become mandatory.
* @param triggerElement - The reference of element that triggered the dialog. Used to refocus on it after the dialog is closed.
*/
openDialog(triggerElement?: ElementRef): void {
const dialog = this.launchDialogService.openDialog(
LAUNCH_CALLER.PICKUP_IN_STORE,
this.element,
triggerElement ? triggerElement : this.element,
this.vcr,
{
productCode: this.productCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ export interface FeatureTogglesInterface {

/**
* When enabled, the focus will be returned to the trigger element after the dialog is closed.
* Affected components: 'AddtoCartComponent', 'PickupOptionsComponent'
* Affected components: 'AddtoCartComponent', 'PickupOptionsComponent', CartPickupOptionsContainerComponent, PDPPickupOptionsContainerComponent
*/
a11yDialogTriggerRefocus?: boolean;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import * as cart from '../../helpers/cart';
import * as checkout from '../../helpers/checkout-flow';

describe('Focus managment for a11y', () => {
Expand Down Expand Up @@ -34,8 +35,17 @@ describe('Focus managment for a11y', () => {
});

context('Pick up in store modal', () => {
it('Should re-focus the element triggering the modal after it closes', () => {
it('Should re-focus the element triggering the modal on PDP after it closes', () => {
cy.visit(`/product/266685`);
cy.contains('Select Store').click();
cy.get('[aria-label="Close"]').click();
cy.contains('Select Store').should('have.focus');
});

it('Should re-focus the element triggering the modal in Cart after it closes', () => {
cy.visit(`/product/266685`);
cart.addProductAsAnonymous();
cy.visit('/cart');
cy.contains('Select Store').click();
cy.get('[aria-label="Close"]').click();
cy.contains('Select Store').should('have.focus');
Expand Down

0 comments on commit 901ddd4

Please sign in to comment.