diff --git a/src/app/core/facades/shopping.facade.ts b/src/app/core/facades/shopping.facade.ts
index 7bb5b68c805..ad0ee37e053 100644
--- a/src/app/core/facades/shopping.facade.ts
+++ b/src/app/core/facades/shopping.facade.ts
@@ -6,7 +6,13 @@ import { debounce, debounceTime, filter, map, switchMap, tap } from 'rxjs/operat
import { ProductListingID } from 'ish-core/models/product-listing/product-listing.model';
import { ProductCompletenessLevel, ProductHelper } from 'ish-core/models/product/product.model';
import { addProductToBasket } from 'ish-core/store/customer/basket';
-import { getCategoryLoading, getSelectedCategory, getTopLevelCategories } from 'ish-core/store/shopping/categories';
+import {
+ getCategory,
+ getCategoryLoading,
+ getNavigationCategories,
+ getSelectedCategory,
+ getTopLevelCategories,
+} from 'ish-core/store/shopping/categories';
import {
addToCompare,
getCompareProducts,
@@ -54,6 +60,14 @@ export class ShoppingFacade {
selectedCategory$ = this.store.pipe(select(getSelectedCategory));
selectedCategoryLoading$ = this.store.pipe(select(getCategoryLoading), debounceTime(500));
+ category$(uniqueId: string) {
+ return this.store.pipe(select(getCategory(uniqueId)));
+ }
+
+ navigationCategories$(uniqueId: string) {
+ return this.store.pipe(select(getNavigationCategories(uniqueId)));
+ }
+
// PRODUCT
selectedProduct$ = this.store.pipe(select(getSelectedProduct));
diff --git a/src/app/core/models/category-view/category-view.model.spec.ts b/src/app/core/models/category-view/category-view.model.spec.ts
index c5257421d25..7ac7e4402b3 100644
--- a/src/app/core/models/category-view/category-view.model.spec.ts
+++ b/src/app/core/models/category-view/category-view.model.spec.ts
@@ -39,8 +39,8 @@ describe('Category View Model', () => {
]);
const view = createCategoryView(tree, '123');
- expect(view.hasChildren()).toBeFalse();
- expect(view.children()).toBeEmpty();
+ expect(view.hasChildren).toBeFalse();
+ expect(view.children).toBeEmpty();
});
const cat1 = {
@@ -60,34 +60,16 @@ describe('Category View Model', () => {
const tree = categoryTree([cat1, cat11]);
const view = createCategoryView(tree, '123');
- expect(view.hasChildren()).toBeTrue();
- expect(view.children()).toHaveLength(1);
+ expect(view.hasChildren).toBeTrue();
+ expect(view.children).toHaveLength(1);
- expect(view.children()[0].uniqueId).toEqual('123.456');
+ expect(view.children[0]).toEqual('123.456');
});
- it('should provide methods to check if a node in a deep complex tree has children', () => {
- const tree = categoryTree([cat1, cat11, cat111]);
-
- const view = createCategoryView(tree, '123');
- expect(view.hasChildren()).toBeTrue();
- expect(view.children()).toHaveLength(1);
-
- const subCategory = view.children()[0];
- expect(subCategory.uniqueId).toEqual('123.456');
- expect(subCategory.hasChildren()).toBeTrue();
- expect(subCategory.children()).toHaveLength(1);
-
- const subSubCategory = subCategory.children()[0];
- expect(subSubCategory.uniqueId).toEqual('123.456.789');
- expect(subSubCategory.hasChildren()).toBeFalse();
- expect(subSubCategory.children()).toBeEmpty();
- });
-
- it('should provide acces to the category path of a category', () => {
+ it('should provide access to the category path of a category', () => {
const tree = categoryTree([cat1, cat11, cat111]);
const view = createCategoryView(tree, '123.456.789');
- expect(view.pathCategories().map(v => v.uniqueId)).toEqual(['123', '123.456', '123.456.789']);
+ expect(view.categoryPath).toEqual(['123', '123.456', '123.456.789']);
});
});
diff --git a/src/app/core/models/category-view/category-view.model.ts b/src/app/core/models/category-view/category-view.model.ts
index 77700d423f0..81807271fcd 100644
--- a/src/app/core/models/category-view/category-view.model.ts
+++ b/src/app/core/models/category-view/category-view.model.ts
@@ -5,9 +5,8 @@ import { Category } from 'ish-core/models/category/category.model';
* View on a {@link Category} with additional methods for navigating to sub categories or category path
*/
export interface CategoryView extends Category {
- children(): CategoryView[];
- hasChildren(): boolean;
- pathCategories(): CategoryView[];
+ children: string[];
+ hasChildren: boolean;
}
export function createCategoryView(tree: CategoryTree, uniqueId: string): CategoryView {
@@ -20,8 +19,7 @@ export function createCategoryView(tree: CategoryTree, uniqueId: string): Catego
return {
...tree.nodes[uniqueId],
- hasChildren: () => !!tree.edges[uniqueId] && !!tree.edges[uniqueId].length,
- children: () => (tree.edges[uniqueId] || []).map(id => createCategoryView(tree, id)),
- pathCategories: () => tree.nodes[uniqueId].categoryPath.map(id => createCategoryView(tree, id)),
+ children: tree.edges[uniqueId] || [],
+ hasChildren: !!tree.edges[uniqueId] && !!tree.edges[uniqueId].length,
};
}
diff --git a/src/app/core/routing/category/category.route.ts b/src/app/core/routing/category/category.route.ts
index 8d0ee81b8ea..45f0d78cf18 100644
--- a/src/app/core/routing/category/category.route.ts
+++ b/src/app/core/routing/category/category.route.ts
@@ -2,15 +2,15 @@ import { UrlMatchResult, UrlSegment } from '@angular/router';
import { MonoTypeOperatorFunction } from 'rxjs';
import { filter } from 'rxjs/operators';
-import { CategoryView } from 'ish-core/models/category-view/category-view.model';
+import { Category } from 'ish-core/models/category/category.model';
import { CoreState } from 'ish-core/store/core/core-store';
import { selectRouteParam } from 'ish-core/store/core/router';
-export function generateLocalizedCategorySlug(category: CategoryView) {
+export function generateLocalizedCategorySlug(category: Category) {
if (!category || !category.categoryPath.length) {
return '';
}
- const lastCat = category.pathCategories()[category.categoryPath.length - 1].name;
+ const lastCat = category.name;
return lastCat ? lastCat.replace(/ /g, '-') : '';
}
@@ -37,7 +37,7 @@ export function matchCategoryRoute(segments: UrlSegment[]): UrlMatchResult {
return;
}
-export function generateCategoryUrl(category: CategoryView): string {
+export function generateCategoryUrl(category: Category): string {
if (!category) {
return '/';
}
diff --git a/src/app/core/store/shopping/categories/categories.selectors.spec.ts b/src/app/core/store/shopping/categories/categories.selectors.spec.ts
index 39d20f465a9..95b2c8f77cd 100644
--- a/src/app/core/store/shopping/categories/categories.selectors.spec.ts
+++ b/src/app/core/store/shopping/categories/categories.selectors.spec.ts
@@ -19,8 +19,10 @@ import {
loadTopLevelCategoriesSuccess,
} from './categories.actions';
import {
+ getCategory,
getCategoryEntities,
getCategoryLoading,
+ getNavigationCategories,
getSelectedCategory,
getTopLevelCategories,
isTopLevelCategoriesLoaded,
@@ -35,8 +37,7 @@ describe('Categories Selectors', () => {
beforeEach(() => {
prod = { sku: 'sku' } as Product;
- cat = { uniqueId: 'Aa', categoryPath: ['Aa'] } as Category;
- cat.hasOnlineProducts = true;
+ cat = { uniqueId: 'Aa', categoryPath: ['Aa'], hasOnlineProducts: true } as Category;
@Component({ template: 'dummy' })
class DummyComponent {}
@@ -63,6 +64,7 @@ describe('Categories Selectors', () => {
it('should not select any selected category when used', () => {
expect(getSelectedCategory(store$.state)).toBeUndefined();
+ expect(getCategory(cat.uniqueId)(store$.state)).toBeUndefined();
});
it('should not select any top level categories when used', () => {
@@ -117,6 +119,7 @@ describe('Categories Selectors', () => {
it('should not select the irrelevant category when used', () => {
expect(getSelectedCategory(store$.state)).toBeUndefined();
+ expect(getCategory(cat.uniqueId)(store$.state).uniqueId).toEqual(cat.uniqueId);
});
});
@@ -133,18 +136,22 @@ describe('Categories Selectors', () => {
it('should select the selected category when used', () => {
expect(getSelectedCategory(store$.state).uniqueId).toEqual(cat.uniqueId);
+ expect(getCategory(cat.uniqueId)(store$.state).uniqueId).toEqual(cat.uniqueId);
});
});
});
describe('loading top level categories', () => {
- let catA: Category;
- let catB: Category;
-
beforeEach(() => {
- catA = { uniqueId: 'A', categoryPath: ['A'] } as Category;
- catB = { uniqueId: 'B', categoryPath: ['B'] } as Category;
- store$.dispatch(loadTopLevelCategoriesSuccess({ categories: categoryTree([catA, catB]) }));
+ const catA = { name: 'name_A', uniqueId: 'A', categoryPath: ['A'] } as Category;
+ const catA1 = { name: 'name_A.1', uniqueId: 'A.1', categoryPath: ['A', 'A.1'] } as Category;
+ const catA1a = { name: 'name_A.1.a', uniqueId: 'A.1.a', categoryPath: ['A', 'A.1', 'A.1.a'] } as Category;
+ const catA1b = { name: 'name_A.1.b', uniqueId: 'A.1.b', categoryPath: ['A', 'A.1', 'A.1.b'] } as Category;
+ const catA2 = { name: 'name_A.2', uniqueId: 'A.2', categoryPath: ['A', 'A.2'] } as Category;
+ const catB = { name: 'name_B', uniqueId: 'B', categoryPath: ['B'] } as Category;
+ store$.dispatch(
+ loadTopLevelCategoriesSuccess({ categories: categoryTree([catA, catA1, catA1a, catA1b, catA2, catB]) })
+ );
});
it('should select root categories when used', () => {
@@ -154,5 +161,68 @@ describe('Categories Selectors', () => {
it('should remember if top level categories are loaded', () => {
expect(isTopLevelCategoriesLoaded(store$.state)).toBeTrue();
});
+
+ describe('selecting navigation categories', () => {
+ it('should select top level categories when no argument was supplied', () => {
+ expect(getNavigationCategories(undefined)(store$.state)).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "hasChildren": true,
+ "name": "name_A",
+ "uniqueId": "A",
+ "url": "/name_A-catA",
+ },
+ Object {
+ "hasChildren": false,
+ "name": "name_B",
+ "uniqueId": "B",
+ "url": "/name_B-catB",
+ },
+ ]
+ `);
+ });
+
+ it('should select sub categories when sub category is selected', () => {
+ expect(getNavigationCategories('A')(store$.state)).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "hasChildren": true,
+ "name": "name_A.1",
+ "uniqueId": "A.1",
+ "url": "/name_A.1-catA.1",
+ },
+ Object {
+ "hasChildren": false,
+ "name": "name_A.2",
+ "uniqueId": "A.2",
+ "url": "/name_A.2-catA.2",
+ },
+ ]
+ `);
+ });
+
+ it('should select deeper sub categories when deeper sub category is selected', () => {
+ expect(getNavigationCategories('A.1')(store$.state)).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "hasChildren": false,
+ "name": "name_A.1.a",
+ "uniqueId": "A.1.a",
+ "url": "/name_A.1.a-catA.1.a",
+ },
+ Object {
+ "hasChildren": false,
+ "name": "name_A.1.b",
+ "uniqueId": "A.1.b",
+ "url": "/name_A.1.b-catA.1.b",
+ },
+ ]
+ `);
+ });
+
+ it('should be empty when selecting leaves', () => {
+ expect(getNavigationCategories('A.1.a')(store$.state)).toBeEmpty();
+ });
+ });
});
});
diff --git a/src/app/core/store/shopping/categories/categories.selectors.ts b/src/app/core/store/shopping/categories/categories.selectors.ts
index 001e5d6944f..dff83b39749 100644
--- a/src/app/core/store/shopping/categories/categories.selectors.ts
+++ b/src/app/core/store/shopping/categories/categories.selectors.ts
@@ -1,6 +1,9 @@
-import { createSelector } from '@ngrx/store';
+import { createSelector, createSelectorFactory, defaultMemoize } from '@ngrx/store';
+import { isEqual } from 'lodash-es';
+import { CategoryTree, CategoryTreeHelper } from 'ish-core/models/category-tree/category-tree.model';
import { createCategoryView } from 'ish-core/models/category-view/category-view.model';
+import { generateCategoryUrl } from 'ish-core/routing/category/category.route';
import { selectRouteParam } from 'ish-core/store/core/router';
import { ShoppingState, getShoppingState } from 'ish-core/store/shopping/shopping-store';
@@ -13,10 +16,21 @@ export const getCategoryTree = createSelector(getCategoriesState, state => state
*/
export const getCategoryEntities = createSelector(getCategoryTree, tree => tree.nodes);
+const getCategorySubTree = (uniqueId: string) =>
+ createSelectorFactory(projector =>
+ defaultMemoize(projector, CategoryTreeHelper.equals, CategoryTreeHelper.equals)
+ )(getCategoryTree, (tree: CategoryTree) => CategoryTreeHelper.subTree(tree, uniqueId));
+
+export const getCategory = (uniqueId: string) =>
+ createSelectorFactory(projector => defaultMemoize(projector, CategoryTreeHelper.equals, isEqual))(
+ getCategorySubTree(uniqueId),
+ (tree: CategoryTree) => createCategoryView(tree, uniqueId)
+ );
+
/**
* Retrieves the currently resolved selected category.
*/
-export const getSelectedCategory = createSelector(
+export const getSelectedCategory = createSelectorFactory(projector => defaultMemoize(projector, undefined, isEqual))(
getCategoryTree,
selectRouteParam('categoryUniqueId'),
createCategoryView
@@ -29,3 +43,31 @@ export const getTopLevelCategories = createSelector(getCategoryTree, tree =>
);
export const isTopLevelCategoriesLoaded = createSelector(getCategoriesState, state => state.topLevelLoaded);
+
+export interface NavigationCategory {
+ uniqueId: string;
+ name: string;
+ url: string;
+ hasChildren: boolean;
+}
+
+function mapNavigationCategoryFromId(uniqueId: string): NavigationCategory {
+ return {
+ uniqueId,
+ name: this.nodes[uniqueId].name,
+ url: generateCategoryUrl(this.nodes[uniqueId]),
+ hasChildren: !!this.edges[uniqueId]?.length,
+ };
+}
+
+export const getNavigationCategories = (uniqueId: string) =>
+ createSelectorFactory(projector => defaultMemoize(projector, CategoryTreeHelper.equals, isEqual))(
+ getCategoryTree,
+ (tree: CategoryTree): NavigationCategory[] => {
+ if (!uniqueId) {
+ return tree.rootIds.map(mapNavigationCategoryFromId.bind(tree));
+ }
+ const subTree = CategoryTreeHelper.subTree(tree, uniqueId);
+ return subTree.edges[uniqueId] ? subTree.edges[uniqueId].map(mapNavigationCategoryFromId.bind(subTree)) : [];
+ }
+ );
diff --git a/src/app/core/store/shopping/shopping-store.spec.ts b/src/app/core/store/shopping/shopping-store.spec.ts
index 7838f92d855..08047ba99ae 100644
--- a/src/app/core/store/shopping/shopping-store.spec.ts
+++ b/src/app/core/store/shopping/shopping-store.spec.ts
@@ -480,10 +480,6 @@ describe('Shopping Store', () => {
categories: tree(A,A.123)
[Shopping] Load Category Success:
categories: tree(A.123,A.123.456)
- [Shopping] Load Category:
- categoryId: "A.123"
- [Shopping] Load Category Success:
- categories: tree(A.123,A.123.456)
@ngrx/router-store/navigated:
routerState: {"url":"/category/A.123.456","params":{"categoryUniqueId":"A...
event: {"id":1,"url":"/category/A.123.456"}
@@ -720,10 +716,6 @@ describe('Shopping Store', () => {
categories: tree(A,A.123)
[Shopping] Load Category Success:
categories: tree(A.123,A.123.456)
- [Shopping] Load Category:
- categoryId: "A.123"
- [Shopping] Load Category Success:
- categories: tree(A.123,A.123.456)
@ngrx/router-store/navigated:
routerState: {"url":"/category/A.123.456/product/P1","params":{"categoryU...
event: {"id":1,"url":"/category/A.123.456/product/P1"}
diff --git a/src/app/pages/category/category-categories/category-categories.component.html b/src/app/pages/category/category-categories/category-categories.component.html
index 8a1a20c4f52..37d0fe60fc6 100644
--- a/src/app/pages/category/category-categories/category-categories.component.html
+++ b/src/app/pages/category/category-categories/category-categories.component.html
@@ -2,7 +2,7 @@
diff --git a/src/app/pages/category/category-list/category-list.component.html b/src/app/pages/category/category-list/category-list.component.html
index e3dd78bda4a..4f76e8d382e 100644
--- a/src/app/pages/category/category-list/category-list.component.html
+++ b/src/app/pages/category/category-list/category-list.component.html
@@ -1,5 +1,5 @@
diff --git a/src/app/pages/category/category-list/category-list.component.ts b/src/app/pages/category/category-list/category-list.component.ts
index 7139f2bb0ad..647e2e3a947 100644
--- a/src/app/pages/category/category-list/category-list.component.ts
+++ b/src/app/pages/category/category-list/category-list.component.ts
@@ -1,17 +1,10 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
-import { CategoryView } from 'ish-core/models/category-view/category-view.model';
-import { Category } from 'ish-core/models/category/category.model';
-
@Component({
selector: 'ish-category-list',
templateUrl: './category-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CategoryListComponent {
- @Input() categories: Category[];
-
- trackByFn(_, item: CategoryView) {
- return item.uniqueId;
- }
+ @Input() categories: string[];
}
diff --git a/src/app/pages/category/category-navigation/category-navigation.component.html b/src/app/pages/category/category-navigation/category-navigation.component.html
index 83673a98bf2..f1ec179e843 100644
--- a/src/app/pages/category/category-navigation/category-navigation.component.html
+++ b/src/app/pages/category/category-navigation/category-navigation.component.html
@@ -1,19 +1,15 @@
-
+
diff --git a/src/app/pages/category/category-navigation/category-navigation.component.spec.ts b/src/app/pages/category/category-navigation/category-navigation.component.spec.ts
index 13fff5ab2d2..a3fbae47377 100644
--- a/src/app/pages/category/category-navigation/category-navigation.component.spec.ts
+++ b/src/app/pages/category/category-navigation/category-navigation.component.spec.ts
@@ -1,11 +1,12 @@
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { MockPipe } from 'ng-mocks';
+import { of } from 'rxjs';
+import { instance, mock, when } from 'ts-mockito';
-import { createCategoryView } from 'ish-core/models/category-view/category-view.model';
-import { Category } from 'ish-core/models/category/category.model';
+import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { CategoryRoutePipe } from 'ish-core/routing/category/category-route.pipe';
-import { categoryTree } from 'ish-core/utils/dev/test-data-utils';
+import { NavigationCategory } from 'ish-core/store/shopping/categories';
import { CategoryNavigationComponent } from './category-navigation.component';
@@ -15,21 +16,32 @@ describe('Category Navigation Component', () => {
let element: HTMLElement;
beforeEach(async(() => {
+ const shoppingFacade = mock(ShoppingFacade);
+ when(shoppingFacade.selectedCategory$).thenReturn(of({ uniqueId: 'A.1' }));
+ when(shoppingFacade.navigationCategories$(undefined)).thenReturn(
+ of([
+ { uniqueId: 'A', name: 'nA', url: '/c/A' },
+ { uniqueId: 'B', name: 'nB', url: '/c/B' },
+ ] as NavigationCategory[])
+ );
+ when(shoppingFacade.navigationCategories$('A')).thenReturn(
+ of([
+ { uniqueId: 'A.1', name: 'nA.1', url: '/c/A/A.1' },
+ { uniqueId: 'A.2', name: 'nA.2', url: '/c/A/A.2' },
+ ] as NavigationCategory[])
+ );
+ when(shoppingFacade.navigationCategories$('B')).thenReturn(of([] as NavigationCategory[]));
+
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [CategoryNavigationComponent, MockPipe(CategoryRoutePipe)],
+ providers: [{ provide: ShoppingFacade, useFactory: () => instance(shoppingFacade) }],
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(CategoryNavigationComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
-
- const tree = categoryTree([
- { uniqueId: 'A', categoryPath: ['A'] },
- { uniqueId: 'A.1', categoryPath: ['A', 'A.1'] },
- ] as Category[]);
- component.category = createCategoryView(tree, 'A');
});
}));
@@ -38,4 +50,18 @@ describe('Category Navigation Component', () => {
expect(element).toBeTruthy();
expect(() => fixture.detectChanges()).not.toThrow();
});
+
+ it('should create all links for tree', () => {
+ fixture.detectChanges();
+ expect(element.querySelectorAll('a')).toMatchInlineSnapshot(`
+ NodeList [
+
nA ,
+
+ nA.1
+ ,
+
nA.2 ,
+
nB ,
+ ]
+ `);
+ });
});
diff --git a/src/app/pages/category/category-navigation/category-navigation.component.ts b/src/app/pages/category/category-navigation/category-navigation.component.ts
index 2f57f9de151..02e6a69a394 100644
--- a/src/app/pages/category/category-navigation/category-navigation.component.ts
+++ b/src/app/pages/category/category-navigation/category-navigation.component.ts
@@ -1,16 +1,28 @@
-import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
-import { CategoryView } from 'ish-core/models/category-view/category-view.model';
+import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { CategoryHelper } from 'ish-core/models/category/category.model';
+import { NavigationCategory } from 'ish-core/store/shopping/categories';
@Component({
selector: 'ish-category-navigation',
templateUrl: './category-navigation.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class CategoryNavigationComponent {
- @Input() category: CategoryView;
- @Input() categoryNavigationLevel: number;
+export class CategoryNavigationComponent implements OnInit {
+ @Input() uniqueId: string;
+
+ navigationCategories$: Observable
;
+ currentCategoryId$: Observable;
categoryEquals = CategoryHelper.equals;
+
+ constructor(private shoppingFacade: ShoppingFacade) {}
+
+ ngOnInit() {
+ this.navigationCategories$ = this.shoppingFacade.navigationCategories$(this.uniqueId);
+ this.currentCategoryId$ = this.shoppingFacade.selectedCategory$.pipe(map(c => c?.uniqueId));
+ }
}
diff --git a/src/app/pages/category/category-page.component.html b/src/app/pages/category/category-page.component.html
index b6061da61e7..1d94df947e3 100644
--- a/src/app/pages/category/category-page.component.html
+++ b/src/app/pages/category/category-page.component.html
@@ -1,6 +1,6 @@
-
+
@@ -12,7 +12,7 @@
-
+
{{ category.name }}
diff --git a/src/app/pages/category/category-tile/category-tile.component.html b/src/app/pages/category/category-tile/category-tile.component.html
index d5caa19b7cc..657ef39b064 100644
--- a/src/app/pages/category/category-tile/category-tile.component.html
+++ b/src/app/pages/category/category-tile/category-tile.component.html
@@ -1,4 +1,8 @@
-
+
{{ category.name }}
diff --git a/src/app/pages/category/category-tile/category-tile.component.spec.ts b/src/app/pages/category/category-tile/category-tile.component.spec.ts
index 170e778149f..9166b6a7faa 100644
--- a/src/app/pages/category/category-tile/category-tile.component.spec.ts
+++ b/src/app/pages/category/category-tile/category-tile.component.spec.ts
@@ -1,7 +1,10 @@
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { MockComponent, MockPipe } from 'ng-mocks';
+import { of } from 'rxjs';
+import { anything, instance, mock, when } from 'ts-mockito';
+import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { createCategoryView } from 'ish-core/models/category-view/category-view.model';
import { Category } from 'ish-core/models/category/category.model';
import { CategoryRoutePipe } from 'ish-core/routing/category/category-route.pipe';
@@ -16,10 +19,33 @@ describe('Category Tile Component', () => {
let fixture: ComponentFixture;
let element: HTMLElement;
+ const category = {
+ uniqueId: 'A',
+ categoryPath: ['A'],
+ images: [
+ {
+ name: 'front S',
+ type: 'Image',
+ imageActualHeight: 110,
+ imageActualWidth: 110,
+ viewID: 'front',
+ effectiveUrl: '/assets/product_img/a.jpg',
+ typeID: 'S',
+ primaryImage: false,
+ },
+ ],
+ } as Category;
+
beforeEach(async(() => {
+ const shoppingFacade = mock(ShoppingFacade);
+ when(shoppingFacade.category$(anything())).thenReturn(
+ of(createCategoryView(categoryTree([category]), category.uniqueId))
+ );
+
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [CategoryTileComponent, MockComponent(CategoryImageComponent), MockPipe(CategoryRoutePipe)],
+ providers: [{ provide: ShoppingFacade, useFactory: () => instance(shoppingFacade) }],
}).compileComponents();
}));
@@ -27,24 +53,6 @@ describe('Category Tile Component', () => {
fixture = TestBed.createComponent(CategoryTileComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
- const category = {
- uniqueId: 'A',
- categoryPath: ['A'],
- images: [
- {
- name: 'front S',
- type: 'Image',
- imageActualHeight: 110,
- imageActualWidth: 110,
- viewID: 'front',
- effectiveUrl: '/assets/product_img/a.jpg',
- typeID: 'S',
- primaryImage: false,
- },
- ],
- } as Category;
- component.category = createCategoryView(categoryTree([category]), category.uniqueId);
- fixture.detectChanges();
});
it('should be created', () => {
diff --git a/src/app/pages/category/category-tile/category-tile.component.ts b/src/app/pages/category/category-tile/category-tile.component.ts
index e686f5d793f..36e292b7c7b 100644
--- a/src/app/pages/category/category-tile/category-tile.component.ts
+++ b/src/app/pages/category/category-tile/category-tile.component.ts
@@ -1,5 +1,7 @@
-import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
+import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { CategoryView } from 'ish-core/models/category-view/category-view.model';
/**
@@ -7,16 +9,24 @@ import { CategoryView } from 'ish-core/models/category-view/category-view.model'
* category using {@link CategoryImageComponent}.
*
* @example
- *
+ *
*/
@Component({
selector: 'ish-category-tile',
templateUrl: './category-tile.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class CategoryTileComponent {
+export class CategoryTileComponent implements OnInit {
/**
* The Category to render a tile for
*/
- @Input() category: CategoryView;
+ @Input() categoryUniqueId: string;
+
+ category$: Observable;
+
+ constructor(private shoppingFacade: ShoppingFacade) {}
+
+ ngOnInit() {
+ this.category$ = this.shoppingFacade.category$(this.categoryUniqueId);
+ }
}
diff --git a/src/app/shared/components/common/breadcrumb/breadcrumb.component.spec.ts b/src/app/shared/components/common/breadcrumb/breadcrumb.component.spec.ts
index f686888550c..cb5114d929b 100644
--- a/src/app/shared/components/common/breadcrumb/breadcrumb.component.spec.ts
+++ b/src/app/shared/components/common/breadcrumb/breadcrumb.component.spec.ts
@@ -79,13 +79,13 @@ describe('Breadcrumb Component', () => {
it('should render breadcrumbtrail from home and category when available', () => {
component.category = view;
fixture.detectChanges();
- expect(element.textContent).toMatchInlineSnapshot(`"Home/cat123/cat456"`);
+ expect(element.textContent).toMatchInlineSnapshot(`"Home/123/123.456"`);
});
it('should render breadcrumbtrail from category when available', () => {
component.showHome = false;
component.category = view;
fixture.detectChanges();
- expect(element.textContent).toMatchInlineSnapshot(`"cat123/cat456"`);
+ expect(element.textContent).toMatchInlineSnapshot(`"123/123.456"`);
});
});
@@ -111,7 +111,7 @@ describe('Breadcrumb Component', () => {
const view = createProductView(product, tree);
component.product = view;
fixture.detectChanges();
- expect(element.textContent).toMatchInlineSnapshot(`"Home/n1/n2/FakeProduct"`);
+ expect(element.textContent).toMatchInlineSnapshot(`"Home/1/1.2/FakeProduct"`);
});
});
diff --git a/src/app/shared/components/common/breadcrumb/breadcrumb.component.ts b/src/app/shared/components/common/breadcrumb/breadcrumb.component.ts
index aacfd77f952..d1ed65cae17 100644
--- a/src/app/shared/components/common/breadcrumb/breadcrumb.component.ts
+++ b/src/app/shared/components/common/breadcrumb/breadcrumb.component.ts
@@ -3,7 +3,6 @@ import { ChangeDetectionStrategy, Component, DoCheck, Input } from '@angular/cor
import { BreadcrumbItem } from 'ish-core/models/breadcrumb-item/breadcrumb-item.interface';
import { CategoryView } from 'ish-core/models/category-view/category-view.model';
import { ProductView } from 'ish-core/models/product-view/product-view.model';
-import { generateCategoryUrl } from 'ish-core/routing/category/category.route';
@Component({
selector: 'ish-breadcrumb',
@@ -26,9 +25,9 @@ export class BreadcrumbComponent implements DoCheck {
const usedCategory = category ? category : product ? product.defaultCategory() : undefined;
if (usedCategory) {
trail.push(
- ...usedCategory.pathCategories().map(cat => ({
- text: cat.name,
- link: generateCategoryUrl(cat),
+ ...usedCategory.categoryPath.map(cat => ({
+ text: cat,
+ link: '/category/' + cat,
}))
);
}
diff --git a/src/app/shell/header/header-navigation/header-navigation.component.html b/src/app/shell/header/header-navigation/header-navigation.component.html
index b3d56a82f03..0f588a0b37e 100644
--- a/src/app/shell/header/header-navigation/header-navigation.component.html
+++ b/src/app/shell/header/header-navigation/header-navigation.component.html
@@ -15,16 +15,16 @@
{{ category.name }}
-
+
diff --git a/src/app/shell/header/header-navigation/header-navigation.component.spec.ts b/src/app/shell/header/header-navigation/header-navigation.component.spec.ts
index 6b42a08d3a2..582083a27b0 100644
--- a/src/app/shell/header/header-navigation/header-navigation.component.spec.ts
+++ b/src/app/shell/header/header-navigation/header-navigation.component.spec.ts
@@ -71,6 +71,7 @@ describe('Header Navigation Component', () => {
CAT_A
@@ -84,6 +85,7 @@ describe('Header Navigation Component', () => {
CAT_B
@@ -97,6 +99,7 @@ describe('Header Navigation Component', () => {
CAT_C
diff --git a/src/app/shell/header/sub-category-navigation/__snapshots__/sub-category-navigation.component.spec.ts.snap b/src/app/shell/header/sub-category-navigation/__snapshots__/sub-category-navigation.component.spec.ts.snap
deleted file mode 100644
index 0c22c696e27..00000000000
--- a/src/app/shell/header/sub-category-navigation/__snapshots__/sub-category-navigation.component.spec.ts.snap
+++ /dev/null
@@ -1,21 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Sub Category Navigation Component should be created 1`] = `
-
-`;
diff --git a/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.html b/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.html
index 177146edc9a..64a08df7914 100644
--- a/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.html
+++ b/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.html
@@ -1,28 +1,28 @@
-
- {{
+ {{
subcategory.name
}}
-
+
diff --git a/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.spec.ts b/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.spec.ts
index 613cfe55ca2..7ae44da6f94 100644
--- a/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.spec.ts
+++ b/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.spec.ts
@@ -2,12 +2,13 @@ import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { MockComponent } from 'ng-mocks';
+import { of } from 'rxjs';
+import { instance, mock, when } from 'ts-mockito';
import { MAIN_NAVIGATION_MAX_SUB_CATEGORIES_DEPTH } from 'ish-core/configurations/injection-keys';
-import { createCategoryView } from 'ish-core/models/category-view/category-view.model';
-import { Category } from 'ish-core/models/category/category.model';
+import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { CategoryRoutePipe } from 'ish-core/routing/category/category-route.pipe';
-import { categoryTree } from 'ish-core/utils/dev/test-data-utils';
+import { NavigationCategory } from 'ish-core/store/shopping/categories';
import { SubCategoryNavigationComponent } from './sub-category-navigation.component';
@@ -17,10 +18,30 @@ describe('Sub Category Navigation Component', () => {
let element: HTMLElement;
beforeEach(async(() => {
+ const shoppingFacade = mock(ShoppingFacade);
+
+ when(shoppingFacade.navigationCategories$('A')).thenReturn(
+ of([
+ { uniqueId: 'A.1', name: 'CAT_A1', url: '/CAT_A1-catA.1', hasChildren: true },
+ { uniqueId: 'A.2', name: 'CAT_A2', url: '/CAT_A2-catA.2' },
+ ] as NavigationCategory[])
+ );
+ when(shoppingFacade.navigationCategories$('A.1')).thenReturn(
+ of([{ uniqueId: 'A.1.a', name: 'CAT_A1a', url: '/CAT_A1a-catA.1.a', hasChildren: true }] as NavigationCategory[])
+ );
+ when(shoppingFacade.navigationCategories$('A.1.a')).thenReturn(
+ of([
+ { uniqueId: 'A.1.a.alpha', name: 'CAT_A1aAlpha', url: '/CAT_A1aAlpha-catA.1.a.alpha' },
+ ] as NavigationCategory[])
+ );
+
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [CategoryRoutePipe, MockComponent(FaIconComponent), SubCategoryNavigationComponent],
- providers: [{ provide: MAIN_NAVIGATION_MAX_SUB_CATEGORIES_DEPTH, useValue: 2 }],
+ providers: [
+ { provide: MAIN_NAVIGATION_MAX_SUB_CATEGORIES_DEPTH, useValue: 2 },
+ { provide: ShoppingFacade, useFactory: () => instance(shoppingFacade) },
+ ],
}).compileComponents();
}));
@@ -29,15 +50,7 @@ describe('Sub Category Navigation Component', () => {
component = fixture.componentInstance;
element = fixture.nativeElement;
- const tree = categoryTree([
- { uniqueId: 'A', name: 'CAT_A', categoryPath: ['A'] },
- { uniqueId: 'A.1', name: 'CAT_A1', categoryPath: ['A', 'A.1'] },
- { uniqueId: 'A.2', name: 'CAT_A2', categoryPath: ['A', 'A.2'] },
- { uniqueId: 'A.1.a', name: 'CAT_A1a', categoryPath: ['A', 'A.1', 'A.1.a'] },
- { uniqueId: 'A.1.a.alpha', name: 'CAT_A1aAlpha', categoryPath: ['A', 'A.1', 'A.1.a', 'A.1.a.alpha'] },
- ] as Category[]);
-
- component.category = createCategoryView(tree, 'A');
+ component.categoryUniqueId = 'A';
component.subCategoriesDepth = 1;
});
@@ -45,6 +58,29 @@ describe('Sub Category Navigation Component', () => {
expect(component).toBeTruthy();
expect(element).toBeTruthy();
expect(() => fixture.detectChanges()).not.toThrow();
- expect(element).toMatchSnapshot();
+ expect(element).toMatchInlineSnapshot(`
+
+ `);
});
});
diff --git a/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.ts b/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.ts
index 5611f66effa..73f230b0be8 100644
--- a/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.ts
+++ b/src/app/shell/header/sub-category-navigation/sub-category-navigation.component.ts
@@ -1,47 +1,48 @@
-import { ChangeDetectionStrategy, Component, Inject, Input } from '@angular/core';
+import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
import { MAIN_NAVIGATION_MAX_SUB_CATEGORIES_DEPTH } from 'ish-core/configurations/injection-keys';
-import { CategoryView } from 'ish-core/models/category-view/category-view.model';
-import { Category } from 'ish-core/models/category/category.model';
+import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
+import { NavigationCategory } from 'ish-core/store/shopping/categories';
/**
* The Sub Category Navigation Component displays second level category navigation.
- *
- * @example
- *
- *
*/
@Component({
selector: 'ish-sub-category-navigation',
templateUrl: './sub-category-navigation.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class SubCategoryNavigationComponent {
+export class SubCategoryNavigationComponent implements OnInit {
@Input() view = 'auto';
- @Input() category: CategoryView;
+ @Input() categoryUniqueId: string;
@Input() subCategoriesDepth: number;
- openedCategories = [];
+ openedCategories: string[] = [];
- constructor(@Inject(MAIN_NAVIGATION_MAX_SUB_CATEGORIES_DEPTH) public mainNavigationMaxSubCategoriesDepth: number) {}
+ navigationCategories$: Observable;
+
+ constructor(
+ private shoppingFacade: ShoppingFacade,
+ @Inject(MAIN_NAVIGATION_MAX_SUB_CATEGORIES_DEPTH) public mainNavigationMaxSubCategoriesDepth: number
+ ) {}
+
+ ngOnInit() {
+ this.navigationCategories$ = this.shoppingFacade.navigationCategories$(this.categoryUniqueId);
+ }
/**
* Indicate if specific category is expanded.
- * @param category The category item.
*/
- isOpened(category: Category): boolean {
- return this.openedCategories.includes(category.uniqueId);
+ isOpened(uniqueId: string): boolean {
+ return this.openedCategories.includes(uniqueId);
}
/**
* Toggle category open state.
- * @param category The category item.
*/
- toggleOpen(category: Category) {
- const index = this.openedCategories.findIndex(id => id === category.uniqueId);
- index > -1 ? this.openedCategories.splice(index, 1) : this.openedCategories.push(category.uniqueId);
+ toggleOpen(uniqueId: string) {
+ const index = this.openedCategories.findIndex(id => id === uniqueId);
+ index > -1 ? this.openedCategories.splice(index, 1) : this.openedCategories.push(uniqueId);
}
}