Skip to content

Commit

Permalink
feat: add "Product List (REST)" CMS component (#1397)
Browse files Browse the repository at this point in the history
  • Loading branch information
shauke authored Mar 31, 2023
1 parent 48004e7 commit dc5c9dc
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/app/shared/cms/cms.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CMSImageComponent } from './components/cms-image/cms-image.component';
import { CMSProductListCategoryComponent } from './components/cms-product-list-category/cms-product-list-category.component';
import { CMSProductListFilterComponent } from './components/cms-product-list-filter/cms-product-list-filter.component';
import { CMSProductListManualComponent } from './components/cms-product-list-manual/cms-product-list-manual.component';
import { CMSProductListRestComponent } from './components/cms-product-list-rest/cms-product-list-rest.component';
import { CMSStandardPageComponent } from './components/cms-standard-page/cms-standard-page.component';
import { CMSStaticPageComponent } from './components/cms-static-page/cms-static-page.component';
import { CMSTextComponent } from './components/cms-text/cms-text.component';
Expand Down Expand Up @@ -89,6 +90,14 @@ import { CMS_COMPONENT } from './configurations/injection-keys';
},
multi: true,
},
{
provide: CMS_COMPONENT,
useValue: {
definitionQualifiedName: 'app_sf_base_cm:component.common.productListRest.pagelet2-Component',
class: CMSProductListRestComponent,
},
multi: true,
},
{
provide: CMS_COMPONENT,
useValue: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<ng-container *ngIf="productSKUs$ | async as productSKUs">
<div *ngIf="productSKUs?.length" class="product-list-container" [ngClass]="pagelet.stringParam('CSSClass')">
<h2 *ngIf="pagelet.stringParam('Title')">{{ pagelet.stringParam('Title') }}</h2>
<ish-products-list
[productSKUs]="productSKUs"
[listStyle]="pagelet.stringParam('ListStyle')"
[slideItems]="pagelet.numberParam('SlideItems')"
[listItemStyle]="$any(pagelet.stringParam('ListItemStyle'))"
[listItemCSSClass]="pagelet.stringParam('ListItemCSSClass')"
>
</ish-products-list>
</div>
</ng-container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// eslint-disable-next-line ish-custom-rules/ban-imports-file-pattern
import { HttpClient } from '@angular/common/http';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { instance, mock, when } from 'ts-mockito';

import { createContentPageletView } from 'ish-core/models/content-view/content-view.model';

import { CMSProductListRestComponent } from './cms-product-list-rest.component';

const restEndpoint = 'https://icm/products?attrs=sku';

const simpleRestEndpoint = 'https://icm/simpleProductSkuArray';

const pageletData = {
definitionQualifiedName: 'fq',
id: 'id',
displayName: 'name',
domain: 'domain',
configurationParameters: {
ProductsRestEndpoint: restEndpoint,
ProductsRestResponseMapper: 'data.elements.map(item => item.attributes[0].value)',
},
};

const restJson = {
elements: [
{ attributes: [{ name: 'sku', type: 'String', value: '111' }] },
{ attributes: [{ name: 'sku', type: 'String', value: '222' }] },
{ attributes: [{ name: 'sku', type: 'String', value: '333' }] },
{ attributes: [{ name: 'sku', type: 'String', value: '444' }] },
],
type: 'ResourceCollection',
name: 'products',
};

const skuArray = ['aaa', 'bbb', 'ccc'];

describe('Cms Product List Rest Component', () => {
let component: CMSProductListRestComponent;
let fixture: ComponentFixture<CMSProductListRestComponent>;
let element: HTMLElement;
let httpClient: HttpClient;

beforeEach(async () => {
httpClient = mock(HttpClient);

await TestBed.configureTestingModule({
declarations: [CMSProductListRestComponent],
providers: [{ provide: HttpClient, useFactory: () => instance(httpClient) }],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(CMSProductListRestComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
component.pagelet = createContentPageletView(pageletData);
});

it('should be created', () => {
expect(component).toBeTruthy();
expect(element).toBeTruthy();
expect(() => fixture.detectChanges()).not.toThrow();
});

describe('getProductSKUs$', () => {
beforeEach(() => {
when(httpClient.get(restEndpoint)).thenReturn(of(restJson));
when(httpClient.get(simpleRestEndpoint)).thenReturn(of(skuArray));
});

it('should map REST JSON data to a product SKUs array', done => {
component.ngOnChanges();
component.productSKUs$.subscribe(productSKUs => {
expect(productSKUs).toMatchInlineSnapshot(`
[
"111",
"222",
"333",
"444",
]
`);
done();
});
});

it('should map REST JSON data to a product SKUs array and limit it to only 2 elements', done => {
component.pagelet = createContentPageletView({
...pageletData,
configurationParameters: {
...pageletData.configurationParameters,
MaxNumberOfProducts: 2,
},
});
component.ngOnChanges();
component.productSKUs$.subscribe(productSKUs => {
expect(productSKUs).toMatchInlineSnapshot(`
[
"111",
"222",
]
`);
done();
});
});

it('should just return simple SKUs array', done => {
component.pagelet = createContentPageletView({
...pageletData,
configurationParameters: {
ProductsRestEndpoint: 'https://icm/simpleProductSkuArray',
},
});
component.ngOnChanges();
component.productSKUs$.subscribe(productSKUs => {
expect(productSKUs).toMatchInlineSnapshot(`
[
"aaa",
"bbb",
"ccc",
]
`);
done();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// eslint-disable-next-line ish-custom-rules/ban-imports-file-pattern
import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { ContentPageletView } from 'ish-core/models/content-view/content-view.model';
import { CMSComponent } from 'ish-shared/cms/models/cms-component/cms-component.model';

@Component({
selector: 'ish-cms-product-list-rest',
templateUrl: './cms-product-list-rest.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CMSProductListRestComponent implements CMSComponent, OnChanges {
@Input() pagelet: ContentPageletView;

productSKUs$: Observable<string[]>;

constructor(private httpClient: HttpClient) {}

ngOnChanges() {
if (this.pagelet.hasParam('ProductsRestEndpoint')) {
this.productSKUs$ = this.getProductSKUs$();
}
}

getProductSKUs$(): Observable<string[]> {
return this.httpClient.get<unknown>(this.pagelet.stringParam('ProductsRestEndpoint')).pipe(
map(data => {
let skus: string[] = [];

// if the REST response is not already an Array of SKUs
// a given mapper function can be applied to the REST 'data' to map the data to an Array of SKUs
skus = this.pagelet.hasParam('ProductsRestResponseMapper')
? Function('data', `"use strict"; return ${this.pagelet.stringParam('ProductsRestResponseMapper')}`)(data)
: data;

// limit the number of rendered products
if (this.pagelet.hasParam('MaxNumberOfProducts')) {
skus = skus.splice(0, this.pagelet.numberParam('MaxNumberOfProducts'));
}

return skus;
})
);
}
}
2 changes: 2 additions & 0 deletions src/app/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { CMSImageComponent } from './cms/components/cms-image/cms-image.componen
import { CMSProductListCategoryComponent } from './cms/components/cms-product-list-category/cms-product-list-category.component';
import { CMSProductListFilterComponent } from './cms/components/cms-product-list-filter/cms-product-list-filter.component';
import { CMSProductListManualComponent } from './cms/components/cms-product-list-manual/cms-product-list-manual.component';
import { CMSProductListRestComponent } from './cms/components/cms-product-list-rest/cms-product-list-rest.component';
import { CMSStandardPageComponent } from './cms/components/cms-standard-page/cms-standard-page.component';
import { CMSStaticPageComponent } from './cms/components/cms-static-page/cms-static-page.component';
import { CMSTextComponent } from './cms/components/cms-text/cms-text.component';
Expand Down Expand Up @@ -194,6 +195,7 @@ const declaredComponents = [
CMSProductListCategoryComponent,
CMSProductListFilterComponent,
CMSProductListManualComponent,
CMSProductListRestComponent,
CMSStandardPageComponent,
CMSStaticPageComponent,
CMSTextComponent,
Expand Down

0 comments on commit dc5c9dc

Please sign in to comment.