[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
This commit is contained in:
Wiktor Danielewski 2023-11-17 12:49:00 +01:00 committed by GitHub
parent 4af945123b
commit 606abdb4fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 185 additions and 16 deletions

View File

@ -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

View File

@ -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',

View File

@ -27,7 +27,7 @@ import { BooleanPipe } from '../../../pipes/boolean.pipe';
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<ng-container *ngIf="value$ | async | adfBoolean as value">
<span [title]="value">
<span [title]="tooltip">
{{ value }}
</span>
</ng-container>

View File

@ -72,7 +72,6 @@ export class DataTableCellComponent implements OnInit, OnDestroy {
protected onDestroy$ = new Subject<boolean>();
protected dataTableService = inject(DataTableService, { optional: true });
value$ = new BehaviorSubject<any>('');
ngOnInit() {

View File

@ -244,7 +244,13 @@
</ng-template>
</div>
<div *ngSwitchCase="'icon'" class="adf-cell-value">
<mat-icon>{{ data.getValue(row, col) }}</mat-icon>
<adf-icon-cell
[data]="data"
[column]="col"
[row]="row"
[resolverFn]="resolverFn"
[tooltip]="getCellTooltip(row, col)">
</adf-icon-cell>
</div>
<div *ngSwitchCase="'date'" class="adf-cell-value" [attr.tabindex]="data.getValue(row, col, resolverFn)? 0 : -1"
[attr.data-automation-id]="'date_' + (data.getValue(row, col, resolverFn) | adfLocalizedDate: 'medium') ">

View File

@ -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', () => {

View File

@ -21,7 +21,7 @@ import { DataTableCellComponent } from '../datatable-cell/datatable-cell.compone
@Component({
selector: 'adf-filesize-cell',
template: `
<ng-container *ngIf="(value$ | async | adfFileSize) as fileSize">
<ng-container *ngIf="value$ | async | adfFileSize as fileSize">
<span [title]="tooltip">{{ fileSize }}</span>
</ng-container>
`,

View File

@ -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<IconCellComponent>;
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);
});
});
});

View File

@ -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: `
<ng-container *ngIf="icon">
<mat-icon [title]="tooltip" aria-hidden="true">{{ icon }}</mat-icon>
</ng-container>
`,
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';
}
}

View File

@ -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,