Skip to content

Commit

Permalink
fix: introduce method to exclude failed/offline products from product…
Browse files Browse the repository at this point in the history
… lists (used for recently viewed and wishlist improvements) (#1647)

* do not display the recently viewed headings and links if no available products are on the recently viewed list
* display the no items on list info for the wishlist widget if no available products are on the lists
* inspired by #1420

 Co-authored-by: Jan Joosse <j.joosse@rijkzwaan.nl>
  • Loading branch information
Stefan Hauke authored and shauke committed Apr 30, 2024
1 parent 0f664eb commit f2f5ebd
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 14 deletions.
9 changes: 9 additions & 0 deletions src/app/core/facades/shopping.facade.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Inject, Injectable } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { isEqual } from 'lodash-es';
import { Observable, combineLatest, identity } from 'rxjs';
import { debounce, distinctUntilChanged, filter, map, pairwise, startWith, switchMap, tap } from 'rxjs/operators';

Expand Down Expand Up @@ -120,6 +121,14 @@ export class ShoppingFacade {

failedProducts$ = this.store.pipe(select(getFailedProducts));

// remove all SKUs from the productSKUs that are also contained in the failed products
excludeFailedProducts$(productSKUs: string[] | Observable<string[]>) {
return combineLatest([toObservable(productSKUs), this.store.pipe(select(getFailedProducts))]).pipe(
distinctUntilChanged<[string[], string[]]>(isEqual),
map(([skus, failed]) => skus.filter(sku => !failed.includes(sku)))
);
}

productPrices$(sku: string | Observable<string>, fresh = false) {
return toObservable(sku).pipe(
whenTruthy(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { MockComponent } from 'ng-mocks';
import { of } from 'rxjs';
import { instance, mock, verify, when } from 'ts-mockito';
import { anything, instance, mock, verify, when } from 'ts-mockito';

import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { BreadcrumbComponent } from 'ish-shared/components/common/breadcrumb/breadcrumb.component';
import { ProductsListComponent } from 'ish-shared/components/product/products-list/products-list.component';

Expand All @@ -16,15 +17,21 @@ describe('Recently Page Component', () => {
let fixture: ComponentFixture<RecentlyPageComponent>;
let element: HTMLElement;
let recentlyFacade: RecentlyFacade;
let shoppingFacade: ShoppingFacade;

beforeEach(async () => {
recentlyFacade = mock(RecentlyFacade);
shoppingFacade = mock(ShoppingFacade);

when(recentlyFacade.recentlyViewedProducts$).thenReturn(of(['sku1', 'sku2']));

await TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
declarations: [MockComponent(BreadcrumbComponent), MockComponent(ProductsListComponent), RecentlyPageComponent],
providers: [{ provide: RecentlyFacade, useFactory: () => instance(recentlyFacade) }],
providers: [
{ provide: RecentlyFacade, useFactory: () => instance(recentlyFacade) },
{ provide: ShoppingFacade, useFactory: () => instance(shoppingFacade) },
],
}).compileComponents();
});

Expand All @@ -41,6 +48,8 @@ describe('Recently Page Component', () => {
});

it('should trigger facade when clear button is clicked', () => {
when(recentlyFacade.mostRecentlyViewedProducts$).thenReturn(of(['P1', 'P2', 'P3']));
when(shoppingFacade.excludeFailedProducts$(anything())).thenReturn(of(['P1', 'P2', 'P3']));
fixture.detectChanges();
verify(recentlyFacade.clearRecentlyViewedProducts()).never();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

import { ShoppingFacade } from 'ish-core/facades/shopping.facade';

import { RecentlyFacade } from '../../facades/recently.facade';

@Component({
Expand All @@ -11,10 +13,10 @@ import { RecentlyFacade } from '../../facades/recently.facade';
export class RecentlyPageComponent implements OnInit {
products$: Observable<string[]>;

constructor(private recentlyFacade: RecentlyFacade) {}
constructor(private recentlyFacade: RecentlyFacade, private shoppingFacade: ShoppingFacade) {}

ngOnInit() {
this.products$ = this.recentlyFacade.recentlyViewedProducts$;
this.products$ = this.shoppingFacade.excludeFailedProducts$(this.recentlyFacade.recentlyViewedProducts$);
}

clearAll() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { RouterTestingModule } from '@angular/router/testing';
import { TranslatePipe } from '@ngx-translate/core';
import { MockComponent, MockPipe } from 'ng-mocks';
import { of } from 'rxjs';
import { instance, mock, when } from 'ts-mockito';
import { anything, instance, mock, when } from 'ts-mockito';

import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { findAllDataTestingIDs } from 'ish-core/utils/dev/html-query-utils';
import { ProductsListComponent } from 'ish-shared/components/product/products-list/products-list.component';

Expand All @@ -18,15 +19,20 @@ describe('Recently Viewed Component', () => {
let component: RecentlyViewedComponent;
let element: HTMLElement;
let recentlyFacade: RecentlyFacade;
let shoppingFacade: ShoppingFacade;
let location: Location;

beforeEach(async () => {
recentlyFacade = mock(RecentlyFacade);
shoppingFacade = mock(ShoppingFacade);

await TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([{ path: 'recently', component: RecentlyViewedComponent }])],
declarations: [MockComponent(ProductsListComponent), MockPipe(TranslatePipe), RecentlyViewedComponent],
providers: [{ provide: RecentlyFacade, useFactory: () => instance(recentlyFacade) }],
providers: [
{ provide: RecentlyFacade, useFactory: () => instance(recentlyFacade) },
{ provide: ShoppingFacade, useFactory: () => instance(shoppingFacade) },
],
}).compileComponents();
});

Expand All @@ -51,12 +57,14 @@ describe('Recently Viewed Component', () => {

it('should display view all link on page', () => {
when(recentlyFacade.mostRecentlyViewedProducts$).thenReturn(of(['P1', 'P2', 'P3']));
when(shoppingFacade.excludeFailedProducts$(anything())).thenReturn(of(['P1', 'P2', 'P3']));
fixture.detectChanges();
expect(findAllDataTestingIDs(fixture)).toContain('view-all');
});

it('should navigate to recently page when view-all link is clicked', fakeAsync(() => {
when(recentlyFacade.mostRecentlyViewedProducts$).thenReturn(of(['P1']));
when(shoppingFacade.excludeFailedProducts$(anything())).thenReturn(of(['P1']));
fixture.detectChanges();

expect(location.path()).toMatchInlineSnapshot(`""`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { GenerateLazyComponent } from 'ish-core/utils/module-loader/generate-lazy-component.decorator';

import { RecentlyFacade } from '../../facades/recently.facade';
Expand All @@ -14,9 +15,11 @@ import { RecentlyFacade } from '../../facades/recently.facade';
export class RecentlyViewedComponent implements OnInit {
recentlyProducts$: Observable<string[]>;

constructor(private recentlyFacade: RecentlyFacade) {}
constructor(private recentlyFacade: RecentlyFacade, private shoppingFacade: ShoppingFacade) {}

ngOnInit() {
this.recentlyProducts$ = this.recentlyFacade.mostRecentlyViewedProducts$;
this.recentlyProducts$ = this.shoppingFacade.excludeFailedProducts$(
this.recentlyFacade.mostRecentlyViewedProducts$
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { MockComponent } from 'ng-mocks';
import { EMPTY, of } from 'rxjs';
import { instance, mock, when } from 'ts-mockito';
import { anything, instance, mock, when } from 'ts-mockito';

import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { ProductsListComponent } from 'ish-shared/components/product/products-list/products-list.component';

import { WishlistsFacade } from '../../facades/wishlists.facade';
Expand All @@ -16,15 +17,21 @@ describe('Wishlist Widget Component', () => {
let element: HTMLElement;

let wishlistFacadeMock: WishlistsFacade;
let shoppingFacade: ShoppingFacade;

beforeEach(async () => {
wishlistFacadeMock = mock(WishlistsFacade);
shoppingFacade = mock(ShoppingFacade);
when(wishlistFacadeMock.allWishlistsItemsSkus$).thenReturn(EMPTY);
when(shoppingFacade.excludeFailedProducts$(anything())).thenReturn(EMPTY);

await TestBed.configureTestingModule({
declarations: [MockComponent(ProductsListComponent), WishlistWidgetComponent],
imports: [TranslateModule.forRoot()],
providers: [{ provide: WishlistsFacade, useFactory: () => instance(wishlistFacadeMock) }],
providers: [
{ provide: ShoppingFacade, useFactory: () => instance(shoppingFacade) },
{ provide: WishlistsFacade, useFactory: () => instance(wishlistFacadeMock) },
],
}).compileComponents();
});

Expand All @@ -42,12 +49,14 @@ describe('Wishlist Widget Component', () => {

it('should display empty list text if there are no wishlist products', () => {
when(wishlistFacadeMock.allWishlistsItemsSkus$).thenReturn(of());
when(shoppingFacade.excludeFailedProducts$(anything())).thenReturn(of());
fixture.detectChanges();
expect(element.querySelector('[data-testing-id=emptyWishlistProductsList]')).toBeTruthy();
});

it('should display product list if there are wishlist products', () => {
when(wishlistFacadeMock.allWishlistsItemsSkus$).thenReturn(of(['sku1', 'sku2']));
when(shoppingFacade.excludeFailedProducts$(anything())).thenReturn(of(['sku1', 'sku2']));
fixture.detectChanges();
expect(element.querySelector('[data-testing-id=wishlistProductsList]')).toBeTruthy();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

import { ProductContextDisplayProperties } from 'ish-core/facades/product-context.facade';
import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { GenerateLazyComponent } from 'ish-core/utils/module-loader/generate-lazy-component.decorator';

import { WishlistsFacade } from '../../facades/wishlists.facade';
Expand All @@ -19,7 +20,7 @@ export class WishlistWidgetComponent implements OnInit {
allWishlistsItemsSkus$: Observable<string[]>;
tileConfiguration: Partial<ProductContextDisplayProperties>;

constructor(private wishlistsFacade: WishlistsFacade) {
constructor(private wishlistsFacade: WishlistsFacade, private shoppingFacade: ShoppingFacade) {
this.tileConfiguration = {
addToWishlist: false,
addToOrderTemplate: false,
Expand All @@ -29,6 +30,8 @@ export class WishlistWidgetComponent implements OnInit {
}

ngOnInit() {
this.allWishlistsItemsSkus$ = this.wishlistsFacade.allWishlistsItemsSkus$;
this.allWishlistsItemsSkus$ = this.shoppingFacade.excludeFailedProducts$(
this.wishlistsFacade.allWishlistsItemsSkus$
);
}
}
2 changes: 1 addition & 1 deletion src/assets/i18n/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@
"account.wishlists.new_wishlist.confirmation": "Ihre Wunschliste \"{{0}}\" wurde erstellt. ",
"account.wishlists.new_wishlist_dialog.header": "Wunschliste hinzufügen",
"account.wishlists.new_wishlist_form.create_button.text": "Anlegen",
"account.wishlists.no_items": "Sie haben keine Artikel zu Ihrer Wunschliste hinzugefügt.",
"account.wishlists.no_items": "Sie haben keine verfügbaren Artikel zu Ihrer Wunschliste hinzugefügt.",
"account.wishlists.no_wishlists": "Derzeit haben Sie keine Wunschlisten.",
"account.wishlists.table.preferred": "Standardliste",
"account.wishlists.widget.heading": "Artikel auf Ihrer Wunschliste",
Expand Down
2 changes: 1 addition & 1 deletion src/assets/i18n/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@
"account.wishlists.new_wishlist.confirmation": "Your wish list \"{{0}}\" has been created.",
"account.wishlists.new_wishlist_dialog.header": "Add wish list",
"account.wishlists.new_wishlist_form.create_button.text": "Create",
"account.wishlists.no_items": "You have not added any items to your wish list.",
"account.wishlists.no_items": "You have not added any available items to your wish list.",
"account.wishlists.no_wishlists": "Currently you don’t have any wish lists.",
"account.wishlists.table.preferred": "Preferred",
"account.wishlists.widget.heading": "Your wish list items",
Expand Down

0 comments on commit f2f5ebd

Please sign in to comment.