diff --git a/docs/core/components/data-column.component.md b/docs/core/components/data-column.component.md index 770c8369d47..2af1d94344e 100644 --- a/docs/core/components/data-column.component.md +++ b/docs/core/components/data-column.component.md @@ -59,8 +59,35 @@ Defines column properties for DataTable, Tasklist, Document List and other compo | sortingKey | `string` | | When using server side sorting the column used by the api call where the sorting will be performed | | srTitle | `string` | | Title to be used for screen readers. | | title | `string` | "" | Display title of the column, typically used for column headers. You can use the i18n resource key to get it translated automatically. | -| type | `string` | "text" | Value type for the column. Possible settings are 'text', 'boolean', 'icon', 'image', 'date', 'fileSize', 'location', and 'json'. | +| type | `string` | "text" | Value type for the column. Possible settings are 'text', 'boolean', 'icon', 'image', 'date', 'fileSize', 'location', 'json' and 'amount'. | | order | `number` | | Sets position of column. | +| currencyConfig | `CurrencyConfig` | [Default currency config](#default-currency-config) | Currency configuration to customize the formatting and display of currency values within the component. | + +## Properties configuration + +### `currencyConfig` Input + +The `currencyConfig` input allows you to customize the formatting and display of currency values within the component. It accepts an object of type `CurrencyConfig` with optional properties for specifying the currency code, display format, decimal precision, and locale. + +#### Properties + +- `code` (optional): A string representing the currency code (e.g., "USD" for US Dollars). +- `display` (optional): A string specifying how the currency value should be displayed. +- `digitsInfo` (optional): A string determining the number of decimal places and other formatting details. +- `locale` (optional): A string indicating the locale or region-specific formatting to use for the currency. + +#### Default currency config + +By default, the `currencyConfig` input is not required. If not provided, the component will use the following default values: + +- `code`: "USD" +- `display`: "symbol" +- `digitsInfo`: undefined +- `locale`: undefined + +These properties offer flexibility in customizing how currency values are presented within the component. + +For more details on the possible use cases of the above properties, see the [official Angular documents](https://angular.io/api/common/CurrencyPipe). ## Properties configuration @@ -76,7 +103,7 @@ The `type` input allows us to specify the type of hosted values for a given colu - `fileSize` - TODO - `location` - TODO - `json` - TODO -- `currency` - This column is responsible for displaying currencies. It expects numerals represented by a string or a number. This type comes with `currencyConfig` (TODO: Add ref). The default setup use **USD** `code` with a **symbol** as a `display` option. Example output: $10.26 (for number 10.26) +- `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). ## Details diff --git a/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.html b/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.html new file mode 100644 index 00000000000..3b04817cce0 --- /dev/null +++ b/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.html @@ -0,0 +1,10 @@ + + + {{ amount | currency: + (currencyConfig?.code || defaultCurrencyConfig.code): + (currencyConfig?.display || defaultCurrencyConfig.display): + (currencyConfig?.digitsInfo || defaultCurrencyConfig.digitsInfo): + (currencyConfig?.locale || defaultCurrencyConfig.locale) + }} + + diff --git a/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.spec.ts b/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.spec.ts new file mode 100644 index 00000000000..3f1b216179b --- /dev/null +++ b/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.spec.ts @@ -0,0 +1,96 @@ +/*! + * @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 { AmountCellComponent } from './amount-cell.component'; +import { CurrencyConfig } from '../../data/data-column.model'; +import { BehaviorSubject } from 'rxjs'; +import { LOCALE_ID } from '@angular/core'; +import { registerLocaleData } from '@angular/common'; +import localePL from '@angular/common/locales/pl'; + +describe('AmountCellComponent', () => { + let component: AmountCellComponent; + let fixture: ComponentFixture; + + const renderAndCheckCurrencyValue = (currencyConfig: CurrencyConfig, value: number, expectedResult: string) => { + component.value$ = new BehaviorSubject(value); + component.currencyConfig = currencyConfig; + + fixture.detectChanges(); + const displayedAmount = fixture.nativeElement.querySelector('span'); + + expect(displayedAmount).toBeTruthy(); + expect(displayedAmount.textContent.trim()).toBe(expectedResult); + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AmountCellComponent] + }); + fixture = TestBed.createComponent(AmountCellComponent); + component = fixture.componentInstance; + }); + + it('should set default currency config', () => { + expect(component.defaultCurrencyConfig.code).toBe('USD'); + expect(component.defaultCurrencyConfig.display).toBe('symbol'); + expect(component.defaultCurrencyConfig.digitsInfo).toBeUndefined(); + expect(component.defaultCurrencyConfig.locale).toBeUndefined(); + }); + + it('should render currency value', () => { + renderAndCheckCurrencyValue(component.currencyConfig, 123.45, '$123.45'); + }); + + it('should render currency value with custom currency code', () => { + renderAndCheckCurrencyValue({ code: 'MY CUSTOM CURRENCY', display: 'symbol' }, 123.45, 'MY CUSTOM CURRENCY123.45'); + }); + + it('should render currency value with custom display code', () => { + renderAndCheckCurrencyValue({ code: 'EUR', display: 'symbol' }, 123.45, '€123.45'); + }); + + it('should render currency value with custom digitsInfo', () => { + renderAndCheckCurrencyValue({ code: 'USD', display: 'symbol', digitsInfo: '1.2-2' }, 123.456789, '$123.46'); + }); +}); + +describe('AmountCellComponent locale', () => { + let component: AmountCellComponent; + let fixture: ComponentFixture; + + it('should render currency value with custom locale', () => { + TestBed.configureTestingModule({ + imports: [AmountCellComponent], + providers: [{ provide: LOCALE_ID, useValue: 'pl-PL' }] + }); + + fixture = TestBed.createComponent(AmountCellComponent); + component = fixture.componentInstance; + registerLocaleData(localePL); + + component.value$ = new BehaviorSubject(123.45); + component.currencyConfig = { code: 'PLN', display: 'symbol', locale: 'pl-PL' }; + + fixture.detectChanges(); + const displayedAmount = fixture.nativeElement.querySelector('span'); + + expect(displayedAmount).toBeTruthy(); + expect(displayedAmount.textContent.trim()).toMatch(/123,45\s?zł/); + }); +}); 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 new file mode 100644 index 00000000000..4fd3bd8db16 --- /dev/null +++ b/lib/core/src/lib/datatable/components/amount-cell/amount-cell.component.ts @@ -0,0 +1,66 @@ +/*! + * @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 { + ChangeDetectionStrategy, + Component, + ViewEncapsulation, + Input, + Optional, + OnInit, + DEFAULT_CURRENCY_CODE, + Inject +} from '@angular/core'; +import { DataTableCellComponent } from '../datatable-cell/datatable-cell.component'; +import { DataTableService } from '../../services/datatable.service'; +import { CurrencyConfig } from '../../data/data-column.model'; +import { CommonModule } from '@angular/common'; + +@Component({ + standalone: true, + imports: [CommonModule], + selector: 'adf-amount-cell', + templateUrl: './amount-cell.component.html', + host: { class: 'adf-datatable-content-cell' }, + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class AmountCellComponent extends DataTableCellComponent implements OnInit { + + @Input() + currencyConfig: CurrencyConfig; + + readonly defaultCurrencyConfig: CurrencyConfig = { + code: this.defaultCurrencyCode, + display: 'symbol', + digitsInfo: undefined, + locale: undefined + }; + + constructor( + @Optional() dataTableService: DataTableService, + @Inject(DEFAULT_CURRENCY_CODE) private readonly defaultCurrencyCode: string + ) { + super(dataTableService); + } + + ngOnInit() { + if (this.column?.key && this.row && this.data) { + this.value$.next(this.data.getValue(this.row, this.column, this.resolverFn)); + } + } +} 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 910908dc8b9..9359754a13d 100644 --- a/lib/core/src/lib/datatable/components/datatable/datatable.component.html +++ b/lib/core/src/lib/datatable/components/datatable/datatable.component.html @@ -306,6 +306,18 @@ [row]="row"> +
+ + +
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 40625032dbe..1f3c9944682 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 @@ -1207,6 +1207,17 @@ describe('DataTable', () => { expect(rows[1].getValue('is_available')).toBe('true'); expect(rows[2].getValue('is_available')).toBe('true'); }); + + it('should be able to display column of type amount', () => { + dataTable.data = new ObjectDataTableAdapter(mockCarsData, mockCarsSchemaDefinition); + + fixture.detectChanges(); + const rows = dataTable.data.getRows(); + + expect(rows[0].getValue('car_price')).toBe(599); + expect(rows[1].getValue('car_price')).toBe(15000.12345); + expect(rows[2].getValue('car_price')).toBe(10000); + }); }); describe('Accesibility', () => { diff --git a/lib/core/src/lib/datatable/components/mocks/datatable.mock.ts b/lib/core/src/lib/datatable/components/mocks/datatable.mock.ts index c7f1e747f19..629618ac215 100644 --- a/lib/core/src/lib/datatable/components/mocks/datatable.mock.ts +++ b/lib/core/src/lib/datatable/components/mocks/datatable.mock.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import { DataColumn } from '../../data/data-column.model'; + export const mockCarsData: any = [ { car_id: 1, @@ -48,7 +50,7 @@ export const mockCarsData: any = [ } ]; -export const mockCarsSchemaDefinition: any[] = [ +export const mockCarsSchemaDefinition: DataColumn[] = [ { type: 'icon', key: 'icon', diff --git a/lib/core/src/lib/datatable/data/data-column.model.ts b/lib/core/src/lib/datatable/data/data-column.model.ts index 2602f779091..5ca85701a42 100644 --- a/lib/core/src/lib/datatable/data/data-column.model.ts +++ b/lib/core/src/lib/datatable/data/data-column.model.ts @@ -27,6 +27,7 @@ export interface DataColumnTypes { location: string; // eslint-disable-next-line id-blacklist boolean: string; + amount: string; } export type DataColumnType = keyof DataColumnTypes; @@ -52,4 +53,12 @@ export interface DataColumn { width?: number; customData?: T; order?: number; + currencyConfig?: CurrencyConfig; +} + +export interface CurrencyConfig { + code?: string; + display?: string; + digitsInfo?: string; + locale?: string; } diff --git a/lib/core/src/lib/datatable/data/object-datacolumn.model.ts b/lib/core/src/lib/datatable/data/object-datacolumn.model.ts index 5ae054f955f..7a77a516feb 100644 --- a/lib/core/src/lib/datatable/data/object-datacolumn.model.ts +++ b/lib/core/src/lib/datatable/data/object-datacolumn.model.ts @@ -16,7 +16,7 @@ */ import { TemplateRef } from '@angular/core'; -import { DataColumn, DataColumnType } from './data-column.model'; +import { CurrencyConfig, DataColumn, DataColumnType } from './data-column.model'; // Simple implementation of the DataColumn interface. export class ObjectDataColumn implements DataColumn { @@ -38,6 +38,7 @@ export class ObjectDataColumn implements DataColumn { customData?: T; width?: number; order?: number; + currencyConfig?: CurrencyConfig; constructor(input: any) { this.id = input.id ?? ''; @@ -58,5 +59,6 @@ export class ObjectDataColumn implements DataColumn { this.customData = input.customData; this.width = input.width; this.order = input.order; + this.currencyConfig = input.currencyConfig; } } diff --git a/lib/core/src/lib/datatable/datatable.module.ts b/lib/core/src/lib/datatable/datatable.module.ts index 5e73768cef2..e4db022e172 100644 --- a/lib/core/src/lib/datatable/datatable.module.ts +++ b/lib/core/src/lib/datatable/datatable.module.ts @@ -54,6 +54,7 @@ import { DataColumnComponent, DataColumnListComponent, DateColumnHeaderComponent import { ResizableModule } from './directives/resizable/resizable.module'; import { DataColumnModule } from './data-column/data-column.module'; import { BooleanCellComponent } from './components/boolean-cell/boolean-cell.component'; +import { AmountCellComponent } from './components/amount-cell/amount-cell.component'; @NgModule({ imports: [ @@ -71,7 +72,8 @@ import { BooleanCellComponent } from './components/boolean-cell/boolean-cell.com ReactiveFormsModule, ResizableModule, DataColumnModule, - BooleanCellComponent + BooleanCellComponent, + AmountCellComponent ], declarations: [ DataTableComponent,