Skip to content

Commit

Permalink
feat: display ICM managed display names for sorting keys on listings (#…
Browse files Browse the repository at this point in the history
…535)

* display ICM managed display names for sorting keys on listings
* simplify select box handling in product-listing-toolbar

Closes #534
  • Loading branch information
dhhyi authored Feb 10, 2021
1 parent 978da89 commit f432188
Show file tree
Hide file tree
Showing 19 changed files with 170 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('Product Listing Mapper', () => {
"value": "dummy",
},
"itemCount": 2,
"sortKeys": Array [],
"sortableAttributes": Array [],
}
`);
});
Expand All @@ -50,15 +50,15 @@ describe('Product Listing Mapper', () => {
"value": "dummy",
},
"itemCount": 5,
"sortKeys": Array [],
"sortableAttributes": Array [],
}
`);
});

it('should map extra arguments when supplied', () => {
expect(
productListingMapper.createPages(['A', 'B', 'C', 'D'], 'test', 'dummy', {
sortKeys: ['name-desc'],
sortableAttributes: [{ name: 'name-desc' }],
itemCount: 200,
sorting: 'name-asc',
filters: { bla: ['blubb'] },
Expand All @@ -85,8 +85,10 @@ describe('Product Listing Mapper', () => {
"value": "dummy",
},
"itemCount": 200,
"sortKeys": Array [
"name-desc",
"sortableAttributes": Array [
Object {
"name": "name-desc",
},
],
}
`);
Expand Down
12 changes: 6 additions & 6 deletions src/app/core/models/product-listing/product-listing.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { range } from 'lodash-es';
import { PRODUCT_LISTING_ITEMS_PER_PAGE } from 'ish-core/configurations/injection-keys';
import { URLFormParams } from 'ish-core/utils/url-form-params';

import { ProductListingType } from './product-listing.model';
import { ProductListingType, SortableAttributesType } from './product-listing.model';

@Injectable({ providedIn: 'root' })
export class ProductListingMapper {
Expand All @@ -16,7 +16,7 @@ export class ProductListingMapper {
productListingValue: string,
extras?: {
startPage?: number;
sortKeys?: string[];
sortableAttributes?: SortableAttributesType[];
itemCount?: number;
sorting?: string;
filters?: URLFormParams;
Expand All @@ -31,16 +31,16 @@ export class ProductListingMapper {
type: productListingType,
value: productListingValue,
},
itemCount: (extras && extras.itemCount) || skus.length,
sortKeys: (extras && extras.sortKeys) || [],
itemCount: extras?.itemCount || skus.length,
sortableAttributes: extras?.sortableAttributes || [],
...pages.reduce((acc, val, idx) => ({ ...acc, [idx + startPage]: val }), {}),
};

if (extras && extras.sorting) {
if (extras?.sorting) {
view.id.sorting = extras.sorting;
}

if (extras && extras.filters) {
if (extras?.filters) {
view.id.filters = extras.filters;
}

Expand Down
10 changes: 8 additions & 2 deletions src/app/core/models/product-listing/product-listing.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { URLFormParams } from 'ish-core/utils/url-form-params';

export interface SortableAttributesType {
name: string;
displayName?: string;
direction?: 'asc' | 'desc';
}

export interface ProductListingID {
type: string;
value: string;
Expand All @@ -11,14 +17,14 @@ export interface ProductListingID {
export interface ProductListingType {
id: ProductListingID;
itemCount?: number;
sortKeys?: string[];
sortableAttributes?: SortableAttributesType[];
[page: number]: string[];
pages?: number[];
}

export interface ProductListingView {
itemCount: number;
sortKeys: string[];
sortableAttributes: SortableAttributesType[];
lastPage: number;
products(): string[];
productsOfPage(page: number): string[];
Expand Down
2 changes: 1 addition & 1 deletion src/app/core/services/filter/filter.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ describe('Filter Service', () => {
]
`);
expect(data?.total).toMatchInlineSnapshot(`2`);
expect(data?.sortKeys).toMatchInlineSnapshot(`undefined`);
expect(data?.sortableAttributes).toMatchInlineSnapshot(`Array []`);

verify(apiService.get(anything(), anything())).once();
const [resource, params] = capture(apiService.get).last();
Expand Down
33 changes: 22 additions & 11 deletions src/app/core/services/filter/filter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CategoryHelper } from 'ish-core/models/category/category.model';
import { FilterNavigationData } from 'ish-core/models/filter-navigation/filter-navigation.interface';
import { FilterNavigationMapper } from 'ish-core/models/filter-navigation/filter-navigation.mapper';
import { FilterNavigation } from 'ish-core/models/filter-navigation/filter-navigation.model';
import { SortableAttributesType } from 'ish-core/models/product-listing/product-listing.model';
import { ProductDataStub } from 'ish-core/models/product/product.interface';
import { ProductMapper } from 'ish-core/models/product/product.mapper';
import { Product, ProductHelper } from 'ish-core/models/product/product.model';
Expand Down Expand Up @@ -72,7 +73,7 @@ export class FilterService {
searchParameter: URLFormParams,
page: number = 1,
sortKey?: string
): Observable<{ total: number; products: Partial<Product>[]; sortKeys: string[] }> {
): Observable<{ total: number; products: Partial<Product>[]; sortableAttributes: SortableAttributesType[] }> {
let params = new HttpParams()
.set('amount', this.itemsPerPage.toString())
.set('offset', ((page - 1) * this.itemsPerPage).toString())
Expand All @@ -86,16 +87,26 @@ export class FilterService {

const resource = searchParameter.category ? `categories/${searchParameter.category[0]}/products` : 'products';

return this.apiService.get(resource, { params }).pipe(
map((x: { total: number; elements: ProductDataStub[]; sortKeys: string[] }) => ({
products: x.elements.map(stub => this.productMapper.fromStubData(stub)),
total: x.total,
sortKeys: x.sortKeys,
})),
params.has('MasterSKU')
? identity
: map(({ products, sortKeys, total }) => ({ products: this.postProcessMasters(products), sortKeys, total }))
);
return this.apiService
.get<{
total: number;
elements: ProductDataStub[];
sortableAttributes: { [id: string]: SortableAttributesType };
}>(resource, { params })
.pipe(
map(x => ({
products: x.elements.map(stub => this.productMapper.fromStubData(stub)),
total: x.total,
sortableAttributes: Object.values(x.sortableAttributes || {}),
})),
params.has('MasterSKU')
? identity
: map(({ products, sortableAttributes, total }) => ({
products: this.postProcessMasters(products),
sortableAttributes,
total,
}))
);
}

/**
Expand Down
16 changes: 14 additions & 2 deletions src/app/core/services/products/products.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ describe('Products Service', () => {
},
],
type: 'ResourceCollection',
sortKeys: ['name-desc', 'name-asc'],
sortableAttributes: {
'name-desc': { name: 'name-desc' },
'name-asc': { name: 'name-asc' },
},
name: 'products',
};

Expand Down Expand Up @@ -81,7 +84,16 @@ describe('Products Service', () => {
when(apiServiceMock.get(`categories/${categoryId}/products`, anything())).thenReturn(of(productsMockData));
productsService.getCategoryProducts(categoryId, 0).subscribe(data => {
expect(data.products.map(p => p.sku)).toEqual(['ProductA', 'ProductB']);
expect(data.sortKeys).toEqual(['name-desc', 'name-asc']);
expect(data.sortableAttributes).toMatchInlineSnapshot(`
Array [
Object {
"name": "name-desc",
},
Object {
"name": "name-asc",
},
]
`);
verify(apiServiceMock.get(`categories/${categoryId}/products`, anything())).once();
done();
});
Expand Down
51 changes: 35 additions & 16 deletions src/app/core/services/products/products.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AttributeGroupTypes } from 'ish-core/models/attribute-group/attribute-g
import { CategoryHelper } from 'ish-core/models/category/category.model';
import { Link } from 'ish-core/models/link/link.model';
import { ProductLinks } from 'ish-core/models/product-links/product-links.model';
import { SortableAttributesType } from 'ish-core/models/product-listing/product-listing.model';
import { VariationProduct } from 'ish-core/models/product/product-variation.model';
import { ProductData, ProductDataStub, ProductVariationLink } from 'ish-core/models/product/product.interface';
import { ProductMapper } from 'ish-core/models/product/product.mapper';
Expand Down Expand Up @@ -63,7 +64,7 @@ export class ProductsService {
categoryUniqueId: string,
page: number,
sortKey?: string
): Observable<{ products: Product[]; sortKeys: string[]; total: number }> {
): Observable<{ products: Product[]; sortableAttributes: SortableAttributesType[]; total: number }> {
if (!categoryUniqueId) {
return throwError('getCategoryProducts() called without categoryUniqueId');
}
Expand All @@ -80,17 +81,23 @@ export class ProductsService {
}

return this.apiService
.get<{ elements: ProductDataStub[]; sortKeys: string[]; categoryUniqueId: string; total: number }>(
`categories/${CategoryHelper.getCategoryPath(categoryUniqueId)}/products`,
{ params }
)
.get<{
elements: ProductDataStub[];
sortableAttributes: { [id: string]: SortableAttributesType };
categoryUniqueId: string;
total: number;
}>(`categories/${CategoryHelper.getCategoryPath(categoryUniqueId)}/products`, { params })
.pipe(
map(response => ({
products: response.elements.map((element: ProductDataStub) => this.productMapper.fromStubData(element)),
sortKeys: response.sortKeys,
sortableAttributes: Object.values(response.sortableAttributes || {}),
total: response.total ? response.total : response.elements.length,
})),
map(({ products, sortKeys, total }) => ({ products: this.postProcessMasters(products), sortKeys, total }))
map(({ products, sortableAttributes, total }) => ({
products: this.postProcessMasters(products),
sortableAttributes,
total,
}))
);
}

Expand All @@ -105,7 +112,7 @@ export class ProductsService {
searchTerm: string,
page: number = 1,
sortKey?: string
): Observable<{ products: Product[]; sortKeys: string[]; total: number }> {
): Observable<{ products: Product[]; sortableAttributes: SortableAttributesType[]; total: number }> {
if (!searchTerm) {
return throwError('searchProducts() called without searchTerm');
}
Expand All @@ -122,22 +129,31 @@ export class ProductsService {
}

return this.apiService
.get<{ elements: ProductDataStub[]; sortKeys: string[]; total: number }>('products', { params })
.get<{
elements: ProductDataStub[];
sortKeys: string[];
sortableAttributes: { [id: string]: SortableAttributesType };
total: number;
}>('products', { params })
.pipe(
map(response => ({
products: response.elements.map(element => this.productMapper.fromStubData(element)),
sortKeys: response.sortKeys,
sortableAttributes: Object.values(response.sortableAttributes || {}),
total: response.total ? response.total : response.elements.length,
})),
map(({ products, sortKeys, total }) => ({ products: this.postProcessMasters(products), sortKeys, total }))
map(({ products, sortableAttributes, total }) => ({
products: this.postProcessMasters(products),
sortableAttributes,
total,
}))
);
}

getProductsForMaster(
masterSKU: string,
page: number = 1,
sortKey?: string
): Observable<{ products: Product[]; sortKeys: string[]; total: number }> {
): Observable<{ products: Product[]; sortableAttributes: SortableAttributesType[]; total: number }> {
if (!masterSKU) {
return throwError('getProductsForMaster() called without masterSKU');
}
Expand All @@ -154,14 +170,17 @@ export class ProductsService {
}

return this.apiService
.get<{ elements: ProductDataStub[]; sortKeys: string[]; total: number }>('products', { params })
.get<{
elements: ProductDataStub[];
sortableAttributes: { [id: string]: SortableAttributesType };
total: number;
}>('products', { params })
.pipe(
map(response => ({
products: response.elements.map(element => this.productMapper.fromStubData(element)) as Product[],
sortKeys: response.sortKeys,
sortableAttributes: Object.values(response.sortableAttributes || {}),
total: response.total ? response.total : response.elements.length,
})),
map(({ products, sortKeys, total }) => ({ products, sortKeys, total }))
}))
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/core/store/shopping/filter/filter.effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe('Filter Effects', () => {
1: ["123","234"]
id: {"type":"search","value":"test","filters":{"searchTerm":[1]}}
itemCount: 2
sortKeys: []
sortableAttributes: []
`);
done();
});
Expand Down
4 changes: 2 additions & 2 deletions src/app/core/store/shopping/filter/filter.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class FilterEffects {
mapToPayload(),
switchMap(({ id, searchParameter, page, sorting }) =>
this.filterService.getFilteredProducts(searchParameter, page, sorting).pipe(
mergeMap(({ products, total, sortKeys }) => [
mergeMap(({ products, total, sortableAttributes }) => [
...products.map((product: Product) => loadProductSuccess({ product })),
setProductListingPages(
this.productListingMapper.createPages(
Expand All @@ -98,7 +98,7 @@ export class FilterEffects {
filters: id.filters,
itemCount: total,
startPage: page,
sortKeys,
sortableAttributes,
sorting,
}
)
Expand Down
Loading

0 comments on commit f432188

Please sign in to comment.