From 0365143e335ea9fa6ee449536c09afb400d86f8a Mon Sep 17 00:00:00 2001 From: Ketevani Kvirikashvili Date: Fri, 18 Feb 2022 19:45:21 +0200 Subject: [PATCH] [AAE-6376] Adding Sort by Category service (#7502) * [AAE-6376] Adding Sort by Category service * indentation fix * fix * fix * indentation fix * fix CR * fix lint * missing semicolon * fix name * fix CR and added unit tests * small fix --- lib/core/services/public-api.ts | 1 + .../services/sort-by-category.service.spec.ts | 111 ++++++++++++++++++ lib/core/services/sort-by-category.service.ts | 103 ++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 lib/core/services/sort-by-category.service.spec.ts create mode 100644 lib/core/services/sort-by-category.service.ts diff --git a/lib/core/services/public-api.ts b/lib/core/services/public-api.ts index 2bae07293e..2b4d8a377b 100644 --- a/lib/core/services/public-api.ts +++ b/lib/core/services/public-api.ts @@ -68,3 +68,4 @@ export * from './language.service'; export * from './identity-user.service.interface'; export * from './identity-group.interface'; export * from './language-item.interface'; +export * from './sort-by-category.service'; diff --git a/lib/core/services/sort-by-category.service.spec.ts b/lib/core/services/sort-by-category.service.spec.ts new file mode 100644 index 0000000000..14b8a37474 --- /dev/null +++ b/lib/core/services/sort-by-category.service.spec.ts @@ -0,0 +1,111 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SortableByCategoryItem, SortByCategoryMapperService } from './sort-by-category.service'; + +interface TestSortableByCategoryItem extends SortableByCategoryItem { + id: string; +} + +describe('SortByCategoryMapperService', () => { + let mapper: SortByCategoryMapperService; + + const DEFAULT_CATEGORIES = [ + '', + 'DefaultCategory1', + 'DefaultCategory2', + 'DefaultCategory3' + ]; + + beforeEach(() => { + mapper = new SortByCategoryMapperService(); + }); + + it('should map items by categories', () => { + const items: TestSortableByCategoryItem[] = [ + { id: 'id1', name: 'firstCategory_222', category: 'category1' }, + { id: 'id2', name: 'secondCategory_AA', category: 'category2' }, + { id: 'id3', name: 'firstCategory_111', category: 'category1' }, + { id: 'id4', name: 'secondCategory_BB', category: 'category2' }, + { id: 'id5', name: 'Default_a', category: DEFAULT_CATEGORIES[1] }, + { id: 'id6', name: 'Default_b', category: DEFAULT_CATEGORIES[0] } + ]; + + const expectedItemsByCategory = [ + { category: 'category1', items: [items[2], items[0]] }, + { category: 'category2', items: [items[1], items[3]] }, + { category: '', items: [items[4], items[5]] } + ]; + + const result = mapper.mapItems(items, DEFAULT_CATEGORIES); + + expect(result).toEqual(expectedItemsByCategory); + }); + + it('should set all items under default category', () => { + const defaulValues: TestSortableByCategoryItem[] = [{ + name: 'name-a', + id: 'id', + category: DEFAULT_CATEGORIES[1] + }, { + name: 'name-b', + id: 'id2', + category: DEFAULT_CATEGORIES[2] + }, { + name: 'name-c', + id: 'id3', + category: DEFAULT_CATEGORIES[0] + }]; + + const result = mapper.mapItems(defaulValues, DEFAULT_CATEGORIES); + + expect(result.length).toBe(1); + expect(result[0].category).toBe(''); + expect(result[0].items.length).toBe(defaulValues.length); + }); + + it('should work if no items are present', () => { + const result = mapper.mapItems([], DEFAULT_CATEGORIES); + + expect(result.length).toBe(0); + }); + + it('should work if the default categories are empty', () => { + const result = mapper.mapItems([{id: 'id', name: 'name', category: ''}], []); + + expect(result.length).toBe(1); + expect(result[0].category).toBe(''); + }); + + it('should set items in ascending order in appropriate category', () => { + const contents = [ + { id: 'id1', name: 'item-b', category: 'cat1' }, + { id: 'id2', name: 'item2', category: 'cat2' }, + { id: 'id3', name: 'item-a', category: 'cat1' } + ]; + + const result = mapper.mapItems(contents, DEFAULT_CATEGORIES); + + expect(result.length).toBe(2); + expect(result[0].category).toBe('cat1'); + expect(result[0].items[0]).toEqual({ id: 'id3', name: 'item-a', category: 'cat1' }); + expect(result[0].items[1]).toEqual({ id: 'id1', name: 'item-b', category: 'cat1' }); + + expect(result[1].category).toBe('cat2'); + expect(result[1].items[0]).toEqual({ id: 'id2', name: 'item2', category: 'cat2' }); + }); +}); diff --git a/lib/core/services/sort-by-category.service.ts b/lib/core/services/sort-by-category.service.ts new file mode 100644 index 0000000000..3d84ca8458 --- /dev/null +++ b/lib/core/services/sort-by-category.service.ts @@ -0,0 +1,103 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; + +export interface SortableByCategoryItem { + name: string; + category?: string; +} + +export interface ItemsByCategory { + category: string; + items: T[]; +} + +@Injectable({ + providedIn: 'root' +}) +export class SortByCategoryMapperService { + + private defaultCategories: string[] = []; + + mapItems(items: T[], defaultCategories: string[]): ItemsByCategory[] { + this.defaultCategories = defaultCategories; + + const sortedItems = this.sortItems(items); + const itemsByCategory = this.mapItemsByCategory(sortedItems); + const itemsSortedByCategory = this.sortCategories(itemsByCategory); + + return itemsSortedByCategory; + } + + private mapItemsByCategory(items: T[]): ItemsByCategory[] { + const itemsByCategoryObject: { [category: string]: T[] } = {}; + + items.forEach((item) => { + const category = this.mapItemDefaultCategory(item); + if (!itemsByCategoryObject[category]) { + itemsByCategoryObject[category] = []; + } + + itemsByCategoryObject[category].push(item); + }); + + const itemsByCategory: ItemsByCategory[] = Object.keys(itemsByCategoryObject).map((key) => { + const category = key; + return { category, items: itemsByCategoryObject[category] }; + }); + + return itemsByCategory; + } + + private sortItems(items: T[]): T[] { + return items.sort((itemA, itemB) => itemA.name.localeCompare(itemB.name)); + } + + private sortCategories(itemsByCategory: ItemsByCategory[]): ItemsByCategory[] { + return itemsByCategory.sort((itemA, itemB) => { + if (itemB.category === '' && itemA.category === '') { + return 0; + } + + if (itemA.category === '') { + return 1; + } + + if (itemB.category === '') { + return -1; + } + + return itemA.category.localeCompare(itemB.category); + } + ); + } + + private mapItemDefaultCategory(listItem: SortableByCategoryItem): string { + const itemCategory = listItem.category; + + if (!this.isDefaultCategory(itemCategory)) { + return (itemCategory ?? ''); + } + + return ''; + } + + private isDefaultCategory(category?: string): boolean { + return category ? this.defaultCategories.includes(category) : false; + } +}