From 606abdb4fd663be35d8c5fd5798454aade35c41f Mon Sep 17 00:00:00 2001 From: Wiktor Danielewski <63188869+wiktord2000@users.noreply.github.com> Date: Fri, 17 Nov 2023 12:49:00 +0100 Subject: [PATCH] [AAE-17103] Handle icon type (#9012) * [AAE-17103] Add typeof pipe * [AAE-17103] Create IconCellComponent * [AAE-17103] Update tests for IconCellComponent * [AAE-17103] Small fix for boolean cell tests * [AAE-17103] Update formatting * [AAE-17103] Update icon cell * [AAE-17103] Update tests * [AAE-17103] Improve boolean cell * [AAE-17103] Improve icon cell * [AAE-17103] Update tests * [AAE-17103] Udate doc about icon type * [AAE-17103] Switch to inject feature of Angular * [AAE-17103] Update IconCellComponent * [AAE-17103] Remove typeof pipe * [AAE-17103] Add ChangeDetectorRef * [AAE-17103] Refactor AmoundCell * [AAE-17103] Small improvement * [AAE-17103] Update tests structure * [AAE-17103] Small update --- docs/core/components/data-column.component.md | 1 + .../amount-cell/amount-cell.component.ts | 12 +-- .../boolean-cell/boolean-cell.component.ts | 2 +- .../datatable-cell.component.ts | 1 - .../datatable/datatable.component.html | 8 +- .../datatable/datatable.component.spec.ts | 11 ++ .../filesize-cell/filesize-cell.component.ts | 2 +- .../icon-cell/icon-cell.component.spec.ts | 102 ++++++++++++++++++ .../icon-cell/icon-cell.component.ts | 58 ++++++++++ .../src/lib/datatable/datatable.module.ts | 4 +- 10 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.spec.ts create mode 100644 lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.ts diff --git a/docs/core/components/data-column.component.md b/docs/core/components/data-column.component.md index 8fc0748c4a..1e9e3d36c5 100644 --- a/docs/core/components/data-column.component.md +++ b/docs/core/components/data-column.component.md @@ -77,6 +77,7 @@ The `type` input allows us to specify the type of hosted values for a given colu - `amount` - This column is responsible for displaying currencies. It expects numerals represented by a string or a number. This type comes with [`currencyConfig`](#default-currency-config), - `number` - This column is responsible for displaying numbers (integers and decimals). It expects numerals represented by a string or a number. This type comes with [`decimalConfig`](#default-decimal-config) - `location` - This column displays a clickable location link pointing to the parent path of the node. **Note:** This type is strongly related to the document list component ([document-list.component.md](../../content-services/components/document-list.component.md)). +- `icon` - Allows us to display Material Icons supported by Google. Example values: **group**, **favorite**, **thumb_up** and many more. The complete list of the icons - [Google Icons](https://fonts.google.com/icons). ### `currencyConfig` Input diff --git a/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.ts b/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.ts index 34fed1a890..a460bf045d 100644 --- a/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.ts +++ b/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.ts @@ -15,15 +15,7 @@ * limitations under the License. */ -import { - ChangeDetectionStrategy, - Component, - ViewEncapsulation, - Input, - OnInit, - DEFAULT_CURRENCY_CODE, - inject -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, ViewEncapsulation, Input, OnInit, DEFAULT_CURRENCY_CODE, inject } from '@angular/core'; import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component'; import { CurrencyConfig } from '../../data/data-column.model'; import { CommonModule } from '@angular/common'; @@ -38,12 +30,10 @@ import { CommonModule } from '@angular/common'; changeDetection: ChangeDetectionStrategy.OnPush }) export class AmountCellComponent extends DataTableCellComponent implements OnInit { - @Input() currencyConfig: CurrencyConfig; private readonly defaultCurrencyCode: string = inject(DEFAULT_CURRENCY_CODE); - readonly defaultCurrencyConfig: CurrencyConfig = { code: this.defaultCurrencyCode, display: 'symbol', diff --git a/lib/core/src/lib/datatable/components/boolean-cell/boolean-cell.component.ts b/lib/core/src/lib/datatable/components/boolean-cell/boolean-cell.component.ts index 19cf3bd289..99d81053d4 100644 --- a/lib/core/src/lib/datatable/components/boolean-cell/boolean-cell.component.ts +++ b/lib/core/src/lib/datatable/components/boolean-cell/boolean-cell.component.ts @@ -27,7 +27,7 @@ import { BooleanPipe } from '../../../pipes/boolean.pipe'; changeDetection: ChangeDetectionStrategy.OnPush, template: ` - + {{ value }} diff --git a/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts b/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts index bffa987bad..2d512ee132 100644 --- a/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts +++ b/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts @@ -72,7 +72,6 @@ export class DataTableCellComponent implements OnInit, OnDestroy { protected onDestroy$ = new Subject(); protected dataTableService = inject(DataTableService, { optional: true }); - value$ = new BehaviorSubject(''); ngOnInit() { diff --git a/lib/core/src/lib/datatable/components/datatable/datatable.component.html b/lib/core/src/lib/datatable/components/datatable/datatable.component.html index aad769a2ac..818b4d1390 100644 --- a/lib/core/src/lib/datatable/components/datatable/datatable.component.html +++ b/lib/core/src/lib/datatable/components/datatable/datatable.component.html @@ -244,7 +244,13 @@
- {{ data.getValue(row, col) }} + +
diff --git a/lib/core/src/lib/datatable/components/datatable/datatable.component.spec.ts b/lib/core/src/lib/datatable/components/datatable/datatable.component.spec.ts index 41d23143f1..256d9f10e7 100644 --- a/lib/core/src/lib/datatable/components/datatable/datatable.component.spec.ts +++ b/lib/core/src/lib/datatable/components/datatable/datatable.component.spec.ts @@ -1240,6 +1240,17 @@ describe('DataTable', () => { expect(rows[1].getValue('production_start')).toBe('1998-06-25T12:25:20'); expect(rows[2].getValue('production_start')).toBe('2004-02-10T12:25:43.511Z'); }); + + it('should be able to display column of type icon', () => { + dataTable.data = new ObjectDataTableAdapter(mockCarsData, mockCarsSchemaDefinition); + + fixture.detectChanges(); + const rows = dataTable.data.getRows(); + + expect(rows[0].getValue('icon')).toBe('airport_shuttle'); + expect(rows[1].getValue('icon')).toBe('directions_car'); + expect(rows[2].getValue('icon')).toBe('local_shipping'); + }); }); describe('Accesibility', () => { diff --git a/lib/core/src/lib/datatable/components/filesize-cell/filesize-cell.component.ts b/lib/core/src/lib/datatable/components/filesize-cell/filesize-cell.component.ts index 01f82009cf..77371da09f 100644 --- a/lib/core/src/lib/datatable/components/filesize-cell/filesize-cell.component.ts +++ b/lib/core/src/lib/datatable/components/filesize-cell/filesize-cell.component.ts @@ -21,7 +21,7 @@ import { DataTableCellComponent } from '../datatable-cell/datatable-cell.compone @Component({ selector: 'adf-filesize-cell', template: ` - + {{ fileSize }} `, diff --git a/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.spec.ts b/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.spec.ts new file mode 100644 index 0000000000..aba9070beb --- /dev/null +++ b/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.spec.ts @@ -0,0 +1,102 @@ +/*! + * @license + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IconCellComponent } from './icon-cell.component'; +import { ObjectDataTableAdapter } from '../../data/object-datatable-adapter'; +import { ObjectDataColumn } from '../../data/object-datacolumn.model'; + +describe('IconCellComponent', () => { + let component: IconCellComponent; + let fixture: ComponentFixture; + const getIconElement = () => fixture.debugElement.nativeElement.querySelector('mat-icon'); + const renderAndCheckResult = (value: any, expectedOccurrence: boolean, expectedIconName?: string) => { + component.value$.next(value); + fixture.detectChanges(); + + const iconElement = getIconElement(); + + expectedOccurrence ? expect(iconElement).toBeTruthy() : expect(iconElement).toBeFalsy(); + if (expectedIconName) { + expect(iconElement.textContent.trim()).toBe(expectedIconName); + } + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [IconCellComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(IconCellComponent); + component = fixture.componentInstance; + }); + + describe('Initialization', () => { + let rowData: any; + let columnData: any; + let dataTableAdapter: ObjectDataTableAdapter; + let nextSpy: jasmine.Spy; + + beforeEach(() => { + rowData = { + id: '1', + value: 'group' + }; + columnData = { type: 'icon', key: 'value' }; + dataTableAdapter = new ObjectDataTableAdapter([rowData], [new ObjectDataColumn(columnData)]); + nextSpy = spyOn(component.value$, 'next'); + }); + + it('should setup inital value', () => { + component.column = dataTableAdapter.getColumns()[0]; + component.row = dataTableAdapter.getRows()[0]; + component.data = dataTableAdapter; + + component.ngOnInit(); + + expect(nextSpy).toHaveBeenCalledOnceWith(rowData.value); + }); + + it('should NOT setup inital value', () => { + component.ngOnInit(); + + expect(nextSpy).not.toHaveBeenCalled(); + }); + }); + + describe('UI', () => { + it('should render icon element in case of non-empty string (icon name validation NOT included)', () => { + renderAndCheckResult('group', true, 'group'); + renderAndCheckResult('groupe', true, 'groupe'); + renderAndCheckResult('0', true, '0'); + renderAndCheckResult('false', true, 'false'); + }); + + it('should NOT render icon element in case of empty string', () => { + renderAndCheckResult('', false); + }); + + it('should NOT render icon element in case of type different than string', () => { + renderAndCheckResult(0, false); + renderAndCheckResult({}, false); + renderAndCheckResult(null, false); + renderAndCheckResult(undefined, false); + renderAndCheckResult(NaN, false); + }); + }); +}); diff --git a/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.ts b/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.ts new file mode 100644 index 0000000000..74b398b701 --- /dev/null +++ b/lib/core/src/lib/datatable/components/icon-cell/icon-cell.component.ts @@ -0,0 +1,58 @@ +/*! + * @license + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * 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 { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { MatIconModule } from '@angular/material/icon'; +import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + standalone: true, + imports: [CommonModule, MatIconModule], + selector: 'adf-icon-cell', + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + + + `, + encapsulation: ViewEncapsulation.None, + host: { class: 'adf-datatable-content-cell' } +}) +export class IconCellComponent extends DataTableCellComponent implements OnInit { + icon: string = ''; + + constructor(private changeDetectorRef: ChangeDetectorRef) { + super(); + } + + ngOnInit(): void { + super.ngOnInit(); + this.value$.pipe(takeUntil(this.onDestroy$)).subscribe((value) => { + const newIcon = this.validateIconValue(value) ? value : ''; + if (this.icon !== newIcon) { + this.icon = newIcon; + this.changeDetectorRef.detectChanges(); + } + }); + } + + private validateIconValue(value: any): boolean { + return typeof value === 'string'; + } +} diff --git a/lib/core/src/lib/datatable/datatable.module.ts b/lib/core/src/lib/datatable/datatable.module.ts index 1808142d9d..233992a168 100644 --- a/lib/core/src/lib/datatable/datatable.module.ts +++ b/lib/core/src/lib/datatable/datatable.module.ts @@ -57,6 +57,7 @@ import { BooleanCellComponent } from './components/boolean-cell/boolean-cell.com import { AmountCellComponent } from './components/amount-cell/amount-cell.component'; import { NumberCellComponent } from './components/number-cell/number-cell.component'; import { LocalizedDatePipe } from '../pipes'; +import { IconCellComponent } from './components/icon-cell/icon-cell.component'; @NgModule({ imports: [ @@ -79,7 +80,8 @@ import { LocalizedDatePipe } from '../pipes'; NumberCellComponent, LocationCellComponent, DateCellComponent, - LocalizedDatePipe + LocalizedDatePipe, + IconCellComponent ], declarations: [ DataTableComponent,