Skip to content

Commit

Permalink
feat: introduce shared ProductsList component with list and carousel …
Browse files Browse the repository at this point in the history
…view (#673)
  • Loading branch information
Eisie96 authored and shauke committed Jun 10, 2021
1 parent 6aa7fb4 commit 8df9599
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ProductView } from 'ish-core/models/product-view/product-view.model';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProductItemComponent implements OnInit {
@Input() displayType: 'tile' | 'row' = 'tile';
@Input() displayType: 'tile' | 'row' | string = 'tile';

product$: Observable<ProductView>;
loading$: Observable<boolean>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<ng-container *ngIf="listStyle === 'carousel'; else plainList">
<div class="product-list">
<swiper [config]="swiperConfig">
<ng-template *ngFor="let sku of productSKUs" swiperSlide>
<div [ngClass]="listItemCSSClass">
<ish-product-item ishProductContext [sku]="sku" [displayType]="listItemStyle"></ish-product-item>
</div>
</ng-template>
</swiper>
</div>
</ng-container>

<ng-template #plainList>
<div class="product-list row">
<div *ngFor="let sku of productSKUs" class="product-list-item" [ngClass]="listItemCSSClass">
<ish-product-item ishProductContext [sku]="sku" [displayType]="listItemStyle"></ish-product-item>
</div>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MockComponent, MockDirective } from 'ng-mocks';
import { SwiperComponent } from 'swiper/angular';

import { ProductContextDirective } from 'ish-core/directives/product-context.directive';
import { ProductItemComponent } from 'ish-shared/components/product/product-item/product-item.component';

import { ProductsListComponent } from './products-list.component';

describe('Products List Component', () => {
let component: ProductsListComponent;
let fixture: ComponentFixture<ProductsListComponent>;
let element: HTMLElement;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
MockComponent(ProductItemComponent),
MockComponent(SwiperComponent),
MockDirective(ProductContextDirective),
ProductsListComponent,
],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(ProductsListComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
});

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

describe('carousel', () => {
beforeEach(() => {
component.productSKUs = ['1', '2'];
});

it('should display a carousel when listStyle is set to carousel', () => {
component.listStyle = 'carousel';

fixture.detectChanges();

expect(element).toMatchInlineSnapshot(`<div class="product-list"><swiper></swiper></div>`);
});
});

it('should set displayType of product item to listItemStyle value', () => {
component.productSKUs = ['1', '2'];
component.listItemStyle = 'tile';

fixture.detectChanges();

const productItem = fixture.debugElement.query(By.css('ish-product-item'))
.componentInstance as ProductItemComponent;

expect(productItem.displayType).toEqual('tile');
});

it('should display product items for all product skus', () => {
component.productSKUs = ['1', '2', '3'];
component.listItemStyle = 'row';

fixture.detectChanges();

expect(element.querySelectorAll('ish-product-item')).toHaveLength(3);
expect(element.querySelectorAll('ish-product-item')).toMatchInlineSnapshot(`
NodeList [
<ish-product-item
ishproductcontext=""
ng-reflect-display-type="row"
ng-reflect-sku="1"
></ish-product-item>,
<ish-product-item
ishproductcontext=""
ng-reflect-display-type="row"
ng-reflect-sku="2"
></ish-product-item>,
<ish-product-item
ishproductcontext=""
ng-reflect-display-type="row"
ng-reflect-sku="3"
></ish-product-item>,
]
`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { ChangeDetectionStrategy, Component, Inject, Input, OnChanges } from '@angular/core';
import { SwiperOptions } from 'swiper';
import SwiperCore, { Navigation, Pagination } from 'swiper/core';

import {
LARGE_BREAKPOINT_WIDTH,
MEDIUM_BREAKPOINT_WIDTH,
SMALL_BREAKPOINT_WIDTH,
} from 'ish-core/configurations/injection-keys';

SwiperCore.use([Pagination, Navigation]);

@Component({
selector: 'ish-products-list',
templateUrl: './products-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProductsListComponent implements OnChanges {
@Input() productSKUs: string[];
@Input() listStyle: string;
@Input() slideItems: number;
@Input() listItemStyle: string;
@Input() listItemCSSClass: string;

/**
* configuration of swiper carousel
* https://swiperjs.com/swiper-api
*/
swiperConfig: SwiperOptions;

constructor(
@Inject(SMALL_BREAKPOINT_WIDTH) private smallBreakpointWidth: number,
@Inject(MEDIUM_BREAKPOINT_WIDTH) private mediumBreakpointWidth: number,
@Inject(LARGE_BREAKPOINT_WIDTH) private largeBreakpointWidth: number
) {
this.swiperConfig = {
direction: 'horizontal',
navigation: true,
pagination: {
clickable: true,
},
observer: true,
observeParents: true,
};
}

ngOnChanges(): void {
this.configureSlides(this.slideItems);
}

/**
* Configure Swipers slidesPerView/slidesPerGroup settings
* with breakpoint responsive design considerations based on the given slide items.
* @param slideItems The amount of slide items that should be rendered if enough screen space is available.
*/
configureSlides(slideItems: number) {
switch (slideItems) {
case 1: {
this.swiperConfig.breakpoints = {
0: {
slidesPerView: 1,
slidesPerGroup: 1,
},
};
break;
}
case 2: {
this.swiperConfig.breakpoints = {
0: {
slidesPerView: 1,
slidesPerGroup: 1,
},
[this.smallBreakpointWidth]: {
slidesPerView: 2,
slidesPerGroup: 2,
},
};
break;
}
case 3: {
this.swiperConfig.breakpoints = {
0: {
slidesPerView: 1,
slidesPerGroup: 1,
},
[this.smallBreakpointWidth]: {
slidesPerView: 2,
slidesPerGroup: 2,
},
[this.mediumBreakpointWidth]: {
slidesPerView: 3,
slidesPerGroup: 3,
},
};
break;
}
default: {
this.swiperConfig.breakpoints = {
0: {
slidesPerView: 1,
slidesPerGroup: 1,
},
[this.smallBreakpointWidth]: {
slidesPerView: 2,
slidesPerGroup: 2,
},
[this.mediumBreakpointWidth]: {
slidesPerView: 3,
slidesPerGroup: 3,
},
[this.largeBreakpointWidth]: {
slidesPerView: 4,
slidesPerGroup: 4,
},
};
}
}
}
}
2 changes: 2 additions & 0 deletions src/app/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ import { ProductShipmentComponent } from './components/product/product-shipment/
import { ProductTileComponent } from './components/product/product-tile/product-tile.component';
import { ProductVariationDisplayComponent } from './components/product/product-variation-display/product-variation-display.component';
import { ProductVariationSelectComponent } from './components/product/product-variation-select/product-variation-select.component';
import { ProductsListComponent } from './components/product/products-list/products-list.component';
import { PromotionDetailsComponent } from './components/promotion/promotion-details/promotion-details.component';
import { PromotionRemoveComponent } from './components/promotion/promotion-remove/promotion-remove.component';
import { RecentlyViewedComponent } from './components/recently/recently-viewed/recently-viewed.component';
Expand Down Expand Up @@ -253,6 +254,7 @@ const exportedComponents = [
ProductShipmentComponent,
ProductVariationDisplayComponent,
ProductVariationSelectComponent,
ProductsListComponent,
PromotionDetailsComponent,
PromotionRemoveComponent,
RecentlyViewedComponent,
Expand Down

0 comments on commit 8df9599

Please sign in to comment.