Skip to content

Commit

Permalink
feat: use new REST API for filters and products on master variation p…
Browse files Browse the repository at this point in the history
…age (#414)

* feat: use new REST API for filters and products on master variation page
* fix: use correct display name for badge
* refactor: restructure filter service
* refactor: display product variation count depending on selected filters for product tile
* chore: hide verbose re-occuring actions in redux dev tools
* refactor: propagate filters from product listing to master variation page
* fix: default change detection for price component for updates in FF
  • Loading branch information
dhhyi authored Nov 23, 2020
1 parent 2b81911 commit 5b9847c
Show file tree
Hide file tree
Showing 30 changed files with 565 additions and 619 deletions.
9 changes: 8 additions & 1 deletion src/app/core/facades/shopping.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
getProduct,
getProductBundleParts,
getProductLinks,
getProductVariationCount,
getProductVariationOptions,
getProducts,
getSelectedProduct,
Expand Down Expand Up @@ -98,6 +99,12 @@ export class ShoppingFacade {
);
}

productVariationCount$(sku: string) {
return toObservable(sku).pipe(
switchMap(plainSKU => this.store.pipe(select(getProductVariationCount, { sku: plainSKU })))
);
}

productBundleParts$(sku: string) {
return this.store.pipe(select(getProductBundleParts, { sku }));
}
Expand Down Expand Up @@ -166,7 +173,7 @@ export class ShoppingFacade {
return this.store.pipe(
select(getAvailableFilter),
whenTruthy(),
map(x => (withCategoryFilter ? x : { ...x, filter: x.filter.filter(f => f.id !== 'CategoryUUIDLevelMulti') }))
map(x => (withCategoryFilter ? x : { ...x, filter: x.filter?.filter(f => f.id !== 'CategoryUUIDLevelMulti') }))
);
}

Expand Down
202 changes: 142 additions & 60 deletions src/app/core/models/product-variation/product-variation.helper.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FilterNavigation } from 'ish-core/models/filter-navigation/filter-navigation.model';
import { VariationProductView } from 'ish-core/models/product-view/product-view.model';

import { ProductVariationHelper } from './product-variation.helper';
Expand All @@ -18,6 +19,7 @@ const variationProduct = {
{ name: 'Attr 2', type: 'VariationAttribute', value: 'B', variationAttributeId: 'a2' },
{ name: 'Attr 2', type: 'VariationAttribute', value: 'C', variationAttributeId: 'a2' },
],
variations: () => variationProduct.variations(),
}),
variations: () => [
{
Expand Down Expand Up @@ -60,78 +62,158 @@ const variationProduct = {
} as VariationProductView;

describe('Product Variation Helper', () => {
it('should build variation option groups for variation product', () => {
const expectedGroups = [
{
id: 'a1',
label: 'Attr 1',
options: [
{
label: 'A',
value: 'A',
type: 'a1',
alternativeCombination: false,
active: true,
},
describe('buildVariationOptionGroups', () => {
it('should build variation option groups for variation product', () => {
const expectedGroups = [
{
id: 'a1',
label: 'Attr 1',
options: [
{
label: 'A',
value: 'A',
type: 'a1',
alternativeCombination: false,
active: true,
},
{
label: 'B',
value: 'B',
type: 'a1',
alternativeCombination: false,
active: false,
},
],
},
{
id: 'a2',
label: 'Attr 2',
options: [
{
label: 'A',
value: 'A',
type: 'a2',
alternativeCombination: false,
active: true,
},
{
label: 'B',
value: 'B',
type: 'a2',
alternativeCombination: false,
active: false,
},
{
label: 'C',
value: 'C',
type: 'a2',
alternativeCombination: true,
active: false,
},
],
},
];

const result = ProductVariationHelper.buildVariationOptionGroups(variationProduct);
expect(result).toEqual(expectedGroups);
});
});

describe('findPossibleVariationForSelection', () => {
it('should find possible variation on variation option group selection', () => {
// perfect match
expect(
ProductVariationHelper.findPossibleVariationForSelection({ a1: 'A', a2: 'B' }, variationProduct).sku
).toEqual('333');

// possible varations
expect(
ProductVariationHelper.findPossibleVariationForSelection({ a1: 'A', a2: 'C' }, variationProduct).sku
).toEqual('333');

expect(
ProductVariationHelper.findPossibleVariationForSelection({ a1: 'A', a2: 'C' }, variationProduct, 'a1').sku
).toEqual('333');

expect(
ProductVariationHelper.findPossibleVariationForSelection({ a1: 'A', a2: 'C' }, variationProduct, 'a2').sku
).toEqual('666');
});
});

describe('productVariationCount', () => {
it('should return zero when no inputs are given', () => {
expect(ProductVariationHelper.productVariationCount(undefined, undefined)).toEqual(0);
});

it('should use variation length when no filters are given', () => {
expect(ProductVariationHelper.productVariationCount(variationProduct.productMaster(), undefined)).toEqual(5);
});

it('should ignore irrelevant selections when counting', () => {
const filters = {
filter: [
{
label: 'B',
value: 'B',
type: 'a1',
alternativeCombination: false,
active: false,
id: 'xyz',
facets: [{ selected: true, name: 'xyz=foobar' }],
},
],
},
{
id: 'a2',
label: 'Attr 2',
options: [
{
label: 'A',
value: 'A',
type: 'a2',
alternativeCombination: false,
active: true,
},
} as FilterNavigation;

expect(ProductVariationHelper.productVariationCount(variationProduct.productMaster(), filters)).toEqual(5);
});

it('should filter for products matching single selected attributes', () => {
const filters = {
filter: [
{
label: 'B',
value: 'B',
type: 'a2',
alternativeCombination: false,
active: false,
id: 'a1',
facets: [
{ selected: true, name: 'a1=A' },
{ selected: false, name: 'a1=B' },
],
},
{
label: 'C',
value: 'C',
type: 'a2',
alternativeCombination: true,
active: false,
id: 'xyz',
facets: [{ selected: true, name: 'xyz=foobar' }],
},
],
},
];
} as FilterNavigation;

const result = ProductVariationHelper.buildVariationOptionGroups(variationProduct);
expect(result).toEqual(expectedGroups);
});
expect(ProductVariationHelper.productVariationCount(variationProduct.productMaster(), filters)).toEqual(2);
});

it('should find possible variation on variation option group selection', () => {
// perfect match
expect(
ProductVariationHelper.findPossibleVariationForSelection({ a1: 'A', a2: 'B' }, variationProduct).sku
).toEqual('333');
it('should filter for products matching multiple selected attributes', () => {
const filters = {
filter: [
{
id: 'a2',
facets: [
{ selected: true, name: 'a2=A' },
{ selected: true, name: 'a2=C' },
],
},
],
} as FilterNavigation;

// possible varations
expect(
ProductVariationHelper.findPossibleVariationForSelection({ a1: 'A', a2: 'C' }, variationProduct).sku
).toEqual('333');
expect(ProductVariationHelper.productVariationCount(variationProduct.productMaster(), filters)).toEqual(3);
});

expect(
ProductVariationHelper.findPossibleVariationForSelection({ a1: 'A', a2: 'C' }, variationProduct, 'a1').sku
).toEqual('333');
it('should filter for products matching multiple selected attributes over multiple facets', () => {
const filters = {
filter: [
{
id: 'a1',
facets: [{ selected: true, name: 'a1=B' }],
},
{
id: 'a2',
facets: [{ selected: true, name: 'a2=B' }],
},
],
} as FilterNavigation;

expect(
ProductVariationHelper.findPossibleVariationForSelection({ a1: 'A', a2: 'C' }, variationProduct, 'a2').sku
).toEqual('666');
expect(ProductVariationHelper.productVariationCount(variationProduct.productMaster(), filters)).toEqual(1);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { groupBy } from 'lodash-es';
import { flatten, groupBy } from 'lodash-es';

import { FilterNavigation } from 'ish-core/models/filter-navigation/filter-navigation.model';
import { VariationProductMasterView, VariationProductView } from 'ish-core/models/product-view/product-view.model';
import { objectToArray } from 'ish-core/utils/functions';

Expand Down Expand Up @@ -153,4 +154,29 @@ export class ProductVariationHelper {
static hasDefaultVariation(product: VariationProductMasterView): boolean {
return product && !!product.defaultVariationSKU;
}

static productVariationCount(product: VariationProductMasterView, filters: FilterNavigation): number {
if (!product) {
return 0;
} else if (!filters?.filter) {
return product.variations()?.length;
}

const selectedFacets = flatten(
filters.filter.map(filter => filter.facets.filter(facet => facet.selected).map(facet => facet.name))
).map(selected => selected.split('='));

return product
.variations()
.map(p => p.variableVariationAttributes)
.filter(attrs =>
attrs.every(
attr =>
// attribute is not selected
!selectedFacets.find(([key]) => key === attr.variationAttributeId) ||
// selection is variation
selectedFacets.find(([key, val]) => key === attr.variationAttributeId && val === attr.value)
)
).length;
}
}

This file was deleted.

28 changes: 0 additions & 28 deletions src/app/core/models/search-parameter/search-parameter.mapper.ts

This file was deleted.

This file was deleted.

Loading

0 comments on commit 5b9847c

Please sign in to comment.