From fbf0a2b84289153b416383f5383931e4753b5ed7 Mon Sep 17 00:00:00 2001 From: markuczy Date: Fri, 10 Jan 2025 11:30:56 +0100 Subject: [PATCH 1/6] feat: filter strategy abstract class --- .../data-sort-base/data-sort-base.ts | 6 +- .../data-table/data-table.component.html | 4 +- .../data-table/data-table.component.ts | 8 +- .../filter-view/filter-view.component.html | 8 +- .../interactive-data-view.component.spec.ts | 4 +- .../interactive-data-view/storybook-config.ts | 2 +- .../src/lib/model/filter.model.ts | 20 ++++- .../src/lib/utils/filter-strategy.ts | 74 +++++++++++++++++++ 8 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 libs/angular-accelerator/src/lib/utils/filter-strategy.ts diff --git a/libs/angular-accelerator/src/lib/components/data-sort-base/data-sort-base.ts b/libs/angular-accelerator/src/lib/components/data-sort-base/data-sort-base.ts index 42856df1..07ef787b 100644 --- a/libs/angular-accelerator/src/lib/components/data-sort-base/data-sort-base.ts +++ b/libs/angular-accelerator/src/lib/components/data-sort-base/data-sort-base.ts @@ -80,11 +80,13 @@ export class DataSortBase { )?.toString() switch (filter.filterType) { case undefined: - case FilterType.EQUAL: + case FilterType.EQUALS: return value === String(filter.value) - case FilterType.TRUTHY: { + case FilterType.IS_NOT_EMPTY: { return filter.value ? !!value : !value } + default: + return true } }) ) diff --git a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.html b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.html index e9fa0e7d..2ac56695 100644 --- a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.html +++ b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.html @@ -164,7 +164,7 @@ - filter.columnId === currentFilterColumn?.id && currentFilterColumn.filterType === FilterType.TRUTHY + filter.columnId === currentFilterColumn?.id && currentFilterColumn.filterType === FilterType.IS_NOT_EMPTY ) .map((filter) => filter.value) }) @@ -465,7 +465,7 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon .filter( (filter) => filter.columnId === currentFilterColumn?.id && - (!currentFilterColumn.filterType || currentFilterColumn.filterType === FilterType.EQUAL) + (!currentFilterColumn.filterType || currentFilterColumn.filterType === FilterType.EQUALS) ) .map((filter) => filter.value) }) @@ -473,7 +473,7 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon this.currentEqualFilterOptions$ = combineLatest([this._rows$, this.currentFilterColumn$, this._filters$]).pipe( filter( ([_, currentFilterColumn, __]) => - !currentFilterColumn?.filterType || currentFilterColumn.filterType === FilterType.EQUAL + !currentFilterColumn?.filterType || currentFilterColumn.filterType === FilterType.EQUALS ), mergeMap(([rows, currentFilterColumn, filters]) => { if (!currentFilterColumn?.id) { @@ -484,7 +484,7 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon .filter( (filter) => filter.columnId === currentFilterColumn?.id && - (!currentFilterColumn.filterType || currentFilterColumn.filterType === FilterType.EQUAL) + (!currentFilterColumn.filterType || currentFilterColumn.filterType === FilterType.EQUALS) ) .map((filter) => filter.value) diff --git a/libs/angular-accelerator/src/lib/components/filter-view/filter-view.component.html b/libs/angular-accelerator/src/lib/components/filter-view/filter-view.component.html index 17d80fad..e1e3df80 100644 --- a/libs/angular-accelerator/src/lib/components/filter-view/filter-view.component.html +++ b/libs/angular-accelerator/src/lib/components/filter-view/filter-view.component.html @@ -45,7 +45,7 @@ style="white-space: nowrap" class="p-chip-text flex flex-nowrap" >{{column?.nameKey ?? '' | translate }}: - + { diff --git a/libs/angular-accelerator/src/lib/components/interactive-data-view/storybook-config.ts b/libs/angular-accelerator/src/lib/components/interactive-data-view/storybook-config.ts index 47b33c6f..41d6284f 100644 --- a/libs/angular-accelerator/src/lib/components/interactive-data-view/storybook-config.ts +++ b/libs/angular-accelerator/src/lib/components/interactive-data-view/storybook-config.ts @@ -139,7 +139,7 @@ export const defaultInteractiveDataViewArgs = { nameKey: 'Available', sortable: false, filterable: true, - filterType: FilterType.TRUTHY, + filterType: FilterType.IS_NOT_EMPTY, predefinedGroupKeys: ['test2'], }, { diff --git a/libs/angular-accelerator/src/lib/model/filter.model.ts b/libs/angular-accelerator/src/lib/model/filter.model.ts index 8a6c34dd..8ab0aefd 100644 --- a/libs/angular-accelerator/src/lib/model/filter.model.ts +++ b/libs/angular-accelerator/src/lib/model/filter.model.ts @@ -4,7 +4,23 @@ export interface ColumnFilterDataSelectOptions { export type Filter = { columnId: string; value: unknown; filterType?: FilterType } +export interface FilterObject { + type: FilterType + value1: unknown + value2: unknown +} + export enum FilterType { - EQUAL = 'EQUAL', - TRUTHY = 'TRUTHY', + ENDS_WITH = 'endsWith', + STARTS_WITH = 'startsWith', + CONTAINS = 'contains', + NOT_CONTAINS = 'notContains', + EQUALS = 'equals', + NOT_EQUALS = 'notEquals', + LESS_THAN = 'lessThan', + GREATER_THAN = 'greaterThan', + LESS_THAN_OR_EQUAL = 'lessThanOrEqual', + GREATER_THAN_OR_EQUAL = 'greaterThanOrEqual', + IS_EMPTY = 'isEmpty', + IS_NOT_EMPTY = 'isNotEmpty', } diff --git a/libs/angular-accelerator/src/lib/utils/filter-strategy.ts b/libs/angular-accelerator/src/lib/utils/filter-strategy.ts new file mode 100644 index 00000000..c1a40d90 --- /dev/null +++ b/libs/angular-accelerator/src/lib/utils/filter-strategy.ts @@ -0,0 +1,74 @@ +import { FilterObject } from '../model/filter.model' + +/* eslint-disable @typescript-eslint/no-unused-vars */ +export abstract class FilterStrategy { + endsWith(value: unknown, target: unknown): boolean { + console.error('endsWith method not implemented') + return true + } + + startsWith(value: unknown, target: unknown): boolean { + console.error('startsWith method not implemented') + return true + } + + contains(value: unknown, target: unknown): boolean { + console.error('contains method not implemented') + return true + } + + notContains(value: unknown, target: unknown): boolean { + console.error('notContains method not implemented') + return true + } + + equals(value: unknown, target: unknown): boolean { + console.error('equals method not implemented') + return true + } + + notEquals(value: unknown, target: unknown): boolean { + console.error('notEquals method not implemented') + return true + } + + lessThan(value: unknown, target: unknown): boolean { + console.error('lessThan method not implemented') + return true + } + + greaterThan(value: unknown, target: unknown): boolean { + console.error('greaterThan method not implemented') + return true + } + + lessThanOrEqual(value: unknown, target: unknown): boolean { + console.error('lessThanOrEqual method not implemented') + return true + } + + greaterThanOrEqual(value: unknown, target: unknown): boolean { + console.error('greaterThanOrEqual method not implemented') + return true + } + + isEmpty(value: unknown): boolean { + console.error('isEmpty method not implemented') + return true + } + + isNotEmpty(value: unknown): boolean { + console.error('isNotEmpty method not implemented') + return true + } + + compare(a: unknown, b: unknown): number { + console.error('compare method not implemented') + return 0 + } + + filter(hayStack: unknown[], filterObject: FilterObject): unknown[] { + const { type, ...rest } = filterObject + return hayStack.filter((item) => this[type](item, rest)) + } +} From a848d9bb9ed27905ab0072a76312c5c63d3dd0e1 Mon Sep 17 00:00:00 2001 From: markuczy Date: Fri, 17 Jan 2025 12:03:09 +0100 Subject: [PATCH 2/6] feat: filter strategy adjusted to column type strategy --- libs/angular-accelerator/src/index.ts | 1 + .../src/lib/model/filter.model.ts | 6 ------ ...{filter-strategy.ts => column-type-strategy.ts} | 14 +++++++++----- 3 files changed, 10 insertions(+), 11 deletions(-) rename libs/angular-accelerator/src/lib/utils/{filter-strategy.ts => column-type-strategy.ts} (81%) diff --git a/libs/angular-accelerator/src/index.ts b/libs/angular-accelerator/src/index.ts index 44cdf9df..8c705737 100644 --- a/libs/angular-accelerator/src/index.ts +++ b/libs/angular-accelerator/src/index.ts @@ -53,6 +53,7 @@ export * from './lib/functions/at-least-one-field-filled-validator' export * from './lib/utils/async-translate-loader.utils' export * from './lib/utils/caching-translate-loader.utils' export * from './lib/utils/colorutils' +export * from './lib/utils/column-type-strategy' export * from './lib/utils/create-translate-loader.utils' export * from './lib/utils/dateutils' export * from './lib/utils/objectutils' diff --git a/libs/angular-accelerator/src/lib/model/filter.model.ts b/libs/angular-accelerator/src/lib/model/filter.model.ts index 8ab0aefd..8a106b36 100644 --- a/libs/angular-accelerator/src/lib/model/filter.model.ts +++ b/libs/angular-accelerator/src/lib/model/filter.model.ts @@ -4,12 +4,6 @@ export interface ColumnFilterDataSelectOptions { export type Filter = { columnId: string; value: unknown; filterType?: FilterType } -export interface FilterObject { - type: FilterType - value1: unknown - value2: unknown -} - export enum FilterType { ENDS_WITH = 'endsWith', STARTS_WITH = 'startsWith', diff --git a/libs/angular-accelerator/src/lib/utils/filter-strategy.ts b/libs/angular-accelerator/src/lib/utils/column-type-strategy.ts similarity index 81% rename from libs/angular-accelerator/src/lib/utils/filter-strategy.ts rename to libs/angular-accelerator/src/lib/utils/column-type-strategy.ts index c1a40d90..bcb8efd4 100644 --- a/libs/angular-accelerator/src/lib/utils/filter-strategy.ts +++ b/libs/angular-accelerator/src/lib/utils/column-type-strategy.ts @@ -1,7 +1,7 @@ -import { FilterObject } from '../model/filter.model' +import { Filter, FilterType } from '../model/filter.model' /* eslint-disable @typescript-eslint/no-unused-vars */ -export abstract class FilterStrategy { +export abstract class ColumnTypeStrategy { endsWith(value: unknown, target: unknown): boolean { console.error('endsWith method not implemented') return true @@ -67,8 +67,12 @@ export abstract class FilterStrategy { return 0 } - filter(hayStack: unknown[], filterObject: FilterObject): unknown[] { - const { type, ...rest } = filterObject - return hayStack.filter((item) => this[type](item, rest)) + filter(hayStack: unknown[], filter: Filter): unknown[] { + const { filterType, value } = filter + if (!filterType) { + console.warn('Filter does not have a type set. All items will resolve as true') + return hayStack + } + return hayStack.filter((item) => this[filterType](item, value)) } } From 435c6c0ef695e09a658dbb0cc61a43dc29db7c21 Mon Sep 17 00:00:00 2001 From: markuczy Date: Fri, 17 Jan 2025 12:05:15 +0100 Subject: [PATCH 3/6] fix: lint issues --- libs/angular-accelerator/src/lib/utils/column-type-strategy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/angular-accelerator/src/lib/utils/column-type-strategy.ts b/libs/angular-accelerator/src/lib/utils/column-type-strategy.ts index bcb8efd4..6c803a3d 100644 --- a/libs/angular-accelerator/src/lib/utils/column-type-strategy.ts +++ b/libs/angular-accelerator/src/lib/utils/column-type-strategy.ts @@ -1,4 +1,4 @@ -import { Filter, FilterType } from '../model/filter.model' +import { Filter } from '../model/filter.model' /* eslint-disable @typescript-eslint/no-unused-vars */ export abstract class ColumnTypeStrategy { From ae8b02e807e2bb97c4893f353577e9f994f016f7 Mon Sep 17 00:00:00 2001 From: markuczy Date: Fri, 17 Jan 2025 19:58:45 +0100 Subject: [PATCH 4/6] feat: column included in strategy methods --- libs/angular-accelerator/src/index.ts | 2 +- .../src/lib/model/filter.model.ts | 4 +- .../src/lib/utils/column-type-strategy.ts | 78 ----- .../lib/utils/data-operation-strategy.spec.ts | 278 ++++++++++++++++++ .../src/lib/utils/data-operation-strategy.ts | 93 ++++++ 5 files changed, 375 insertions(+), 80 deletions(-) delete mode 100644 libs/angular-accelerator/src/lib/utils/column-type-strategy.ts create mode 100644 libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts create mode 100644 libs/angular-accelerator/src/lib/utils/data-operation-strategy.ts diff --git a/libs/angular-accelerator/src/index.ts b/libs/angular-accelerator/src/index.ts index 8c705737..08b22e87 100644 --- a/libs/angular-accelerator/src/index.ts +++ b/libs/angular-accelerator/src/index.ts @@ -53,7 +53,7 @@ export * from './lib/functions/at-least-one-field-filled-validator' export * from './lib/utils/async-translate-loader.utils' export * from './lib/utils/caching-translate-loader.utils' export * from './lib/utils/colorutils' -export * from './lib/utils/column-type-strategy' +export * from './lib/utils/data-operation-strategy' export * from './lib/utils/create-translate-loader.utils' export * from './lib/utils/dateutils' export * from './lib/utils/objectutils' diff --git a/libs/angular-accelerator/src/lib/model/filter.model.ts b/libs/angular-accelerator/src/lib/model/filter.model.ts index 8a106b36..79adb661 100644 --- a/libs/angular-accelerator/src/lib/model/filter.model.ts +++ b/libs/angular-accelerator/src/lib/model/filter.model.ts @@ -2,7 +2,9 @@ export interface ColumnFilterDataSelectOptions { reverse: boolean } -export type Filter = { columnId: string; value: unknown; filterType?: FilterType } +export type FilterObject = { columnId: string; filterType?: FilterType } + +export type Filter = FilterObject & { value: unknown } export enum FilterType { ENDS_WITH = 'endsWith', diff --git a/libs/angular-accelerator/src/lib/utils/column-type-strategy.ts b/libs/angular-accelerator/src/lib/utils/column-type-strategy.ts deleted file mode 100644 index 6c803a3d..00000000 --- a/libs/angular-accelerator/src/lib/utils/column-type-strategy.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Filter } from '../model/filter.model' - -/* eslint-disable @typescript-eslint/no-unused-vars */ -export abstract class ColumnTypeStrategy { - endsWith(value: unknown, target: unknown): boolean { - console.error('endsWith method not implemented') - return true - } - - startsWith(value: unknown, target: unknown): boolean { - console.error('startsWith method not implemented') - return true - } - - contains(value: unknown, target: unknown): boolean { - console.error('contains method not implemented') - return true - } - - notContains(value: unknown, target: unknown): boolean { - console.error('notContains method not implemented') - return true - } - - equals(value: unknown, target: unknown): boolean { - console.error('equals method not implemented') - return true - } - - notEquals(value: unknown, target: unknown): boolean { - console.error('notEquals method not implemented') - return true - } - - lessThan(value: unknown, target: unknown): boolean { - console.error('lessThan method not implemented') - return true - } - - greaterThan(value: unknown, target: unknown): boolean { - console.error('greaterThan method not implemented') - return true - } - - lessThanOrEqual(value: unknown, target: unknown): boolean { - console.error('lessThanOrEqual method not implemented') - return true - } - - greaterThanOrEqual(value: unknown, target: unknown): boolean { - console.error('greaterThanOrEqual method not implemented') - return true - } - - isEmpty(value: unknown): boolean { - console.error('isEmpty method not implemented') - return true - } - - isNotEmpty(value: unknown): boolean { - console.error('isNotEmpty method not implemented') - return true - } - - compare(a: unknown, b: unknown): number { - console.error('compare method not implemented') - return 0 - } - - filter(hayStack: unknown[], filter: Filter): unknown[] { - const { filterType, value } = filter - if (!filterType) { - console.warn('Filter does not have a type set. All items will resolve as true') - return hayStack - } - return hayStack.filter((item) => this[filterType](item, value)) - } -} diff --git a/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts new file mode 100644 index 00000000..21102896 --- /dev/null +++ b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts @@ -0,0 +1,278 @@ +import { TestBed } from '@angular/core/testing' +import { DataOperationStrategy } from './data-operation-strategy' +import { DataTableColumn } from '../model/data-table-column.model' +import { FilterObject, FilterType } from '../model/filter.model' +import { ColumnType } from '../model/column-type.model' + +class NumberOperationStrategy extends DataOperationStrategy { + override equals(column: DataTableColumn, value: unknown, target: unknown): boolean { + return value === target + } + override lessThan(column: DataTableColumn, value: unknown, target: unknown): boolean { + return Number(value) < Number(target) + } + override compare(a: unknown, b: unknown, column: DataTableColumn): number { + return Number(a) - Number(b) + } +} + +class DateOperationStrategy extends DataOperationStrategy { + override equals(column: DataTableColumn, value: unknown, target: unknown): boolean { + let precision: 'day' | 'year' = 'year' + if (column.id === 'dayCol') precision = 'day' + + if (value === undefined) return false + const valueDate = value as Date + const targetDate = target as Date + if (precision === 'day') { + return ( + valueDate.getFullYear() === targetDate.getFullYear() && + valueDate.getMonth() === targetDate.getMonth() && + valueDate.getDate() === targetDate.getDate() + ) + } + return valueDate.getFullYear() === targetDate.getFullYear() + } + + override isNotEmpty(column: DataTableColumn, value: unknown): boolean { + return value !== undefined + } + + override compare(a: unknown, b: unknown, column: DataTableColumn): number { + let precision: 'day' | 'year' = 'year' + if (column.id === 'dayCol') precision = 'day' + + const aDate = a as Date + const bDate = b as Date + + const aYear = aDate.getFullYear() + const aMonth = aDate.getMonth() + const aDay = aDate.getDate() + + const bYear = bDate.getFullYear() + const bMonth = bDate.getMonth() + const bDay = bDate.getDate() + + if (aYear !== bYear || precision === 'year') { + return aYear - bYear + } + if (aMonth !== bMonth) { + return aMonth - bMonth + } + return aDay - bDay + } + + override filterOptions(hayStack: unknown[], filterObject: FilterObject, columns: DataTableColumn[]) { + if (filterObject.filterType === FilterType.IS_NOT_EMPTY) { + return ['yes', 'no'] + } + const column = columns.find((c) => c.id === filterObject.columnId) + if (!column) { + console.warn('Filter does not have a column id set. All items will be considered a valid option') + return hayStack + } + + let precision: 'day' | 'year' = 'year' + if (column.id === 'dayCol') precision = 'day' + + return hayStack + .filter((v) => v !== undefined) + .filter((item, index, self) => index === self.findIndex((t) => this.compare(t, item, column) === 0)) + } +} + +describe('DataOperationStrategy', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [], + }).compileComponents() + }) + + describe('NumberOperationStrategy', () => { + const items = [1, 2, 3, 2, 4] + let strategy = new NumberOperationStrategy() + const columns: DataTableColumn[] = [ + { + id: 'col', + nameKey: '', + columnType: ColumnType.NUMBER, + }, + ] + + it('should result in equal numbers for filter', () => { + const result = strategy.filter( + items, + { + value: 2, + filterType: FilterType.EQUALS, + columnId: 'col', + }, + columns + ) + expect(result).toEqual([2, 2]) + }) + + it('should result in lower numbers for filter', () => { + const result = strategy.filter( + items, + { + value: 3, + filterType: FilterType.LESS_THAN, + columnId: 'col', + }, + columns + ) + expect(result).toEqual([1, 2, 2]) + }) + + it('should result in unique numbers for filterOptions', () => { + const result = strategy.filterOptions( + items, + { + filterType: FilterType.LESS_THAN, + columnId: 'col', + }, + columns + ) + expect(result).toEqual([1, 2, 3, 4]) + }) + + it('should return all items for filter if filter type not set', () => { + const result = strategy.filter( + items, + { + value: 3, + columnId: 'col', + }, + columns + ) + expect(result).toEqual(items) + }) + it('should return all items for filter if column not found', () => { + const result = strategy.filter( + items, + { + value: 3, + filterType: FilterType.EQUALS, + columnId: 'col', + }, + [] + ) + expect(result).toEqual(items) + }) + it('should return all items for filterOptions if column not found', () => { + const result = strategy.filterOptions( + items, + { + filterType: FilterType.LESS_THAN, + columnId: 'col', + }, + columns + ) + expect(result).toEqual([1, 2, 3, 4]) + }) + }) + + describe('DateOperationStrategy', () => { + const items = [ + new Date(2020, 1, 13), + new Date(2020, 1, 13), + new Date(2021, 1, 13), + new Date(2022, 7, 20), + new Date(2022, 1, 13), + new Date(2024, 7, 20), + undefined, + ] + let strategy = new DateOperationStrategy() + const columns: DataTableColumn[] = [ + { + id: 'yearCol', + nameKey: '', + columnType: ColumnType.DATE, + }, + { + id: 'dayCol', + nameKey: '', + columnType: ColumnType.DATE, + }, + ] + const yearCol = 'yearCol' + const dayCol = 'dayCol' + + it('should result in equal dates with year precision', () => { + const result = strategy.filter( + items, + { + columnId: yearCol, + filterType: FilterType.EQUALS, + value: new Date(2022, 7, 20), + }, + columns + ) + + expect(result).toEqual([new Date(2022, 7, 20), new Date(2022, 1, 13)]) + }) + + it('should result in equal dates with day precision', () => { + const result = strategy.filter( + items, + { + columnId: dayCol, + filterType: FilterType.EQUALS, + value: new Date(2020, 1, 13), + }, + columns + ) + + expect(result).toEqual([new Date(2020, 1, 13), new Date(2020, 1, 13)]) + }) + it('should result in non empty dates', () => { + const result = strategy.filter( + items, + { + columnId: yearCol, + filterType: FilterType.IS_NOT_EMPTY, + value: 'yes', + }, + columns + ) + + expect(result.length).toEqual(items.length - 1) + }) + it('should result in unique dates with year precision', () => { + const result = strategy.filterOptions( + items, + { + columnId: yearCol, + filterType: FilterType.EQUALS, + }, + columns + ) + + expect(result).toEqual([ + new Date(2020, 1, 13), + new Date(2021, 1, 13), + new Date(2022, 7, 20), + new Date(2024, 7, 20), + ]) + }) + it('should result in unique dates with day precision', () => { + const result = strategy.filterOptions( + items, + { + columnId: dayCol, + filterType: FilterType.EQUALS, + }, + columns + ) + + expect(result).toEqual([ + new Date(2020, 1, 13), + new Date(2021, 1, 13), + new Date(2022, 7, 20), + new Date(2022, 1, 13), + new Date(2024, 7, 20), + ]) + }) + }) +}) diff --git a/libs/angular-accelerator/src/lib/utils/data-operation-strategy.ts b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.ts new file mode 100644 index 00000000..d4116b6f --- /dev/null +++ b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.ts @@ -0,0 +1,93 @@ +import { DataTableColumn } from '../model/data-table-column.model' +import { Filter, FilterObject } from '../model/filter.model' + +/* eslint-disable @typescript-eslint/no-unused-vars */ +export abstract class DataOperationStrategy { + endsWith(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('endsWith method not implemented') + return true + } + + startsWith(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('startsWith method not implemented') + return true + } + + contains(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('contains method not implemented') + return true + } + + notContains(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('notContains method not implemented') + return true + } + + equals(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('equals method not implemented') + return true + } + + notEquals(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('notEquals method not implemented') + return true + } + + lessThan(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('lessThan method not implemented') + return true + } + + greaterThan(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('greaterThan method not implemented') + return true + } + + lessThanOrEqual(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('lessThanOrEqual method not implemented') + return true + } + + greaterThanOrEqual(column: DataTableColumn, value: unknown, target: unknown): boolean { + console.error('greaterThanOrEqual method not implemented') + return true + } + + isEmpty(column: DataTableColumn, value: unknown): boolean { + console.error('isEmpty method not implemented') + return true + } + + isNotEmpty(column: DataTableColumn, value: unknown): boolean { + console.error('isNotEmpty method not implemented') + return true + } + + compare(a: unknown, b: unknown, column: DataTableColumn): number { + console.error('compare method not implemented') + return 0 + } + + filterOptions(hayStack: unknown[], filterObject: FilterObject, columns: DataTableColumn[]): unknown[] { + const column = columns.find((c) => c.id === filterObject.columnId) + if (!column) { + console.warn('Filter does not have a column id set. All items will be considered a valid option') + return hayStack + } + return hayStack.filter((item, index, self) => index === self.findIndex((t) => this.compare(t, item, column) === 0)) + } + + filter(hayStack: unknown[], filter: Filter, columns: DataTableColumn[]): unknown[] { + const { filterType, value } = filter + if (!filterType) { + console.warn('Filter does not have a type set. All items will resolve as true') + return hayStack + } + const column = columns.find((c) => c.id === filter.columnId) + if (!column) { + console.warn('Filter does not have a column id set. All items will be considered a valid option') + return hayStack + } + return hayStack.filter((item) => this[filterType](column, item, value)) + } +} From a1d8b4f2669dbb1e06ed892d70c69231974c9470 Mon Sep 17 00:00:00 2001 From: markuczy Date: Fri, 17 Jan 2025 22:09:38 +0100 Subject: [PATCH 5/6] feat: tests updated --- .../lib/utils/data-operation-strategy.spec.ts | 129 +++++++++++++----- .../src/lib/utils/data-operation-strategy.ts | 14 +- 2 files changed, 103 insertions(+), 40 deletions(-) diff --git a/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts index 21102896..0acaad30 100644 --- a/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts +++ b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts @@ -6,7 +6,7 @@ import { ColumnType } from '../model/column-type.model' class NumberOperationStrategy extends DataOperationStrategy { override equals(column: DataTableColumn, value: unknown, target: unknown): boolean { - return value === target + return Number(value) === Number(target) } override lessThan(column: DataTableColumn, value: unknown, target: unknown): boolean { return Number(value) < Number(target) @@ -18,40 +18,36 @@ class NumberOperationStrategy extends DataOperationStrategy { class DateOperationStrategy extends DataOperationStrategy { override equals(column: DataTableColumn, value: unknown, target: unknown): boolean { + if (!value || !(value instanceof Date) || !(target instanceof Date)) return false + // different implementation based on the column let precision: 'day' | 'year' = 'year' if (column.id === 'dayCol') precision = 'day' - if (value === undefined) return false - const valueDate = value as Date - const targetDate = target as Date if (precision === 'day') { return ( - valueDate.getFullYear() === targetDate.getFullYear() && - valueDate.getMonth() === targetDate.getMonth() && - valueDate.getDate() === targetDate.getDate() + value.getFullYear() === target.getFullYear() && + value.getMonth() === target.getMonth() && + value.getDate() === target.getDate() ) } - return valueDate.getFullYear() === targetDate.getFullYear() + return value.getFullYear() === target.getFullYear() } override isNotEmpty(column: DataTableColumn, value: unknown): boolean { - return value !== undefined + return !!value } - override compare(a: unknown, b: unknown, column: DataTableColumn): number { + override compare(a: Date, b: Date, column: DataTableColumn): number { let precision: 'day' | 'year' = 'year' if (column.id === 'dayCol') precision = 'day' - const aDate = a as Date - const bDate = b as Date - - const aYear = aDate.getFullYear() - const aMonth = aDate.getMonth() - const aDay = aDate.getDate() + const aYear = a.getFullYear() + const aMonth = a.getMonth() + const aDay = a.getDate() - const bYear = bDate.getFullYear() - const bMonth = bDate.getMonth() - const bDay = bDate.getDate() + const bYear = b.getFullYear() + const bMonth = b.getMonth() + const bDay = b.getDate() if (aYear !== bYear || precision === 'year') { return aYear - bYear @@ -66,18 +62,22 @@ class DateOperationStrategy extends DataOperationStrategy { if (filterObject.filterType === FilterType.IS_NOT_EMPTY) { return ['yes', 'no'] } + + const hayStackValues = hayStack + .map((item) => this.mapHaystackItemToValue(item, filterObject)) + .filter((item) => !!item) const column = columns.find((c) => c.id === filterObject.columnId) if (!column) { console.warn('Filter does not have a column id set. All items will be considered a valid option') - return hayStack + return hayStackValues } let precision: 'day' | 'year' = 'year' if (column.id === 'dayCol') precision = 'day' - return hayStack - .filter((v) => v !== undefined) - .filter((item, index, self) => index === self.findIndex((t) => this.compare(t, item, column) === 0)) + return hayStackValues.filter( + (item, index, self) => index === self.findIndex((t) => this.compare(t, item, column) === 0) + ) } } @@ -89,7 +89,23 @@ describe('DataOperationStrategy', () => { }) describe('NumberOperationStrategy', () => { - const items = [1, 2, 3, 2, 4] + const items = [ + { + col: 1, + }, + { + col: 2, + }, + { + col: 3, + }, + { + col: 2, + }, + { + col: 4, + }, + ] let strategy = new NumberOperationStrategy() const columns: DataTableColumn[] = [ { @@ -109,7 +125,7 @@ describe('DataOperationStrategy', () => { }, columns ) - expect(result).toEqual([2, 2]) + expect(result).toEqual([{ col: 2 }, { col: 2 }]) }) it('should result in lower numbers for filter', () => { @@ -122,7 +138,7 @@ describe('DataOperationStrategy', () => { }, columns ) - expect(result).toEqual([1, 2, 2]) + expect(result).toEqual([{ col: 1 }, { col: 2 }, { col: 2 }]) }) it('should result in unique numbers for filterOptions', () => { @@ -167,21 +183,42 @@ describe('DataOperationStrategy', () => { filterType: FilterType.LESS_THAN, columnId: 'col', }, - columns + [] ) - expect(result).toEqual([1, 2, 3, 4]) + expect(result).toEqual(items.map((i) => i.col)) }) }) describe('DateOperationStrategy', () => { const items = [ - new Date(2020, 1, 13), - new Date(2020, 1, 13), - new Date(2021, 1, 13), - new Date(2022, 7, 20), - new Date(2022, 1, 13), - new Date(2024, 7, 20), - undefined, + { + yearCol: new Date(2020, 1, 13), + dayCol: new Date(2020, 1, 13), + }, + { + yearCol: new Date(2020, 1, 13), + dayCol: new Date(2020, 1, 13), + }, + { + yearCol: new Date(2021, 1, 13), + dayCol: new Date(2021, 1, 13), + }, + { + yearCol: new Date(2022, 7, 20), + dayCol: new Date(2022, 7, 20), + }, + { + yearCol: new Date(2022, 1, 13), + dayCol: new Date(2022, 1, 13), + }, + { + yearCol: new Date(2024, 7, 20), + dayCol: new Date(2024, 7, 20), + }, + { + yearCol: undefined, + dayCol: undefined, + }, ] let strategy = new DateOperationStrategy() const columns: DataTableColumn[] = [ @@ -210,7 +247,10 @@ describe('DataOperationStrategy', () => { columns ) - expect(result).toEqual([new Date(2022, 7, 20), new Date(2022, 1, 13)]) + expect(result).toEqual([ + { yearCol: new Date(2022, 7, 20), dayCol: new Date(2022, 7, 20) }, + { yearCol: new Date(2022, 1, 13), dayCol: new Date(2022, 1, 13) }, + ]) }) it('should result in equal dates with day precision', () => { @@ -224,7 +264,10 @@ describe('DataOperationStrategy', () => { columns ) - expect(result).toEqual([new Date(2020, 1, 13), new Date(2020, 1, 13)]) + expect(result).toEqual([ + { yearCol: new Date(2020, 1, 13), dayCol: new Date(2020, 1, 13) }, + { yearCol: new Date(2020, 1, 13), dayCol: new Date(2020, 1, 13) }, + ]) }) it('should result in non empty dates', () => { const result = strategy.filter( @@ -274,5 +317,17 @@ describe('DataOperationStrategy', () => { new Date(2024, 7, 20), ]) }) + it('should result in yes and no options with not empty filter', () => { + const result = strategy.filterOptions( + items, + { + columnId: dayCol, + filterType: FilterType.IS_NOT_EMPTY, + }, + columns + ) + + expect(result).toEqual(['yes', 'no']) + }) }) }) diff --git a/libs/angular-accelerator/src/lib/utils/data-operation-strategy.ts b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.ts index d4116b6f..9863f7d5 100644 --- a/libs/angular-accelerator/src/lib/utils/data-operation-strategy.ts +++ b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.ts @@ -1,5 +1,6 @@ import { DataTableColumn } from '../model/data-table-column.model' import { Filter, FilterObject } from '../model/filter.model' +import { ObjectUtils } from './objectutils' /* eslint-disable @typescript-eslint/no-unused-vars */ export abstract class DataOperationStrategy { @@ -69,12 +70,15 @@ export abstract class DataOperationStrategy { } filterOptions(hayStack: unknown[], filterObject: FilterObject, columns: DataTableColumn[]): unknown[] { + const hayStackOptions = hayStack.map((item) => this.mapHaystackItemToValue(item, filterObject)) const column = columns.find((c) => c.id === filterObject.columnId) if (!column) { console.warn('Filter does not have a column id set. All items will be considered a valid option') - return hayStack + return hayStackOptions } - return hayStack.filter((item, index, self) => index === self.findIndex((t) => this.compare(t, item, column) === 0)) + return hayStackOptions.filter( + (item, index, self) => index === self.findIndex((t) => this.compare(t, item, column) === 0) + ) } filter(hayStack: unknown[], filter: Filter, columns: DataTableColumn[]): unknown[] { @@ -88,6 +92,10 @@ export abstract class DataOperationStrategy { console.warn('Filter does not have a column id set. All items will be considered a valid option') return hayStack } - return hayStack.filter((item) => this[filterType](column, item, value)) + return hayStack.filter((item) => this[filterType](column, this.mapHaystackItemToValue(item, filter), value)) + } + + mapHaystackItemToValue(item: unknown, filter: Filter | FilterObject) { + return ObjectUtils.resolveFieldData(item, filter.columnId) } } From 47fecedfc1ecb8c8b47da952422dce91c8373c34 Mon Sep 17 00:00:00 2001 From: markuczy Date: Fri, 17 Jan 2025 22:13:03 +0100 Subject: [PATCH 6/6] fix: fixed linting issues --- .../src/lib/utils/data-operation-strategy.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts index 0acaad30..f73c5571 100644 --- a/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts +++ b/libs/angular-accelerator/src/lib/utils/data-operation-strategy.spec.ts @@ -4,6 +4,7 @@ import { DataTableColumn } from '../model/data-table-column.model' import { FilterObject, FilterType } from '../model/filter.model' import { ColumnType } from '../model/column-type.model' +/* eslint-disable @typescript-eslint/no-unused-vars */ class NumberOperationStrategy extends DataOperationStrategy { override equals(column: DataTableColumn, value: unknown, target: unknown): boolean { return Number(value) === Number(target) @@ -106,7 +107,7 @@ describe('DataOperationStrategy', () => { col: 4, }, ] - let strategy = new NumberOperationStrategy() + const strategy = new NumberOperationStrategy() const columns: DataTableColumn[] = [ { id: 'col', @@ -220,7 +221,7 @@ describe('DataOperationStrategy', () => { dayCol: undefined, }, ] - let strategy = new DateOperationStrategy() + const strategy = new DateOperationStrategy() const columns: DataTableColumn[] = [ { id: 'yearCol',