From ac2a894a4d752f021f022dce38d007826151f53f Mon Sep 17 00:00:00 2001 From: Volker Theile Date: Thu, 28 Nov 2019 16:54:06 +0100 Subject: [PATCH] mgr/dashboard: Eye candy updates to datatable pages - Introduce boolean and array pipes - Introduce configurable 'badge' datatable column - Refactor the Inventory, OSD, RBD, iSCSI, ... datatable pages Signed-off-by: Volker Theile --- .../app/ceph/block/iscsi/iscsi.component.html | 6 -- .../app/ceph/block/iscsi/iscsi.component.ts | 13 ++++- .../rbd-snapshot-list.component.html | 10 ---- .../rbd-snapshot-list.component.ts | 10 +++- .../inventory-devices.component.html | 8 --- .../inventory-devices.component.ts | 32 ++++++----- .../osd/osd-list/osd-list.component.html | 9 --- .../osd/osd-list/osd-list.component.ts | 17 +++++- .../device-list/device-list.component.html | 24 -------- .../device-list/device-list.component.ts | 15 ++++- .../table-key-value.component.spec.ts | 4 +- .../datatable/table/table.component.html | 14 ++++- .../datatable/table/table.component.spec.ts | 4 +- .../shared/datatable/table/table.component.ts | 3 + .../src/app/shared/enum/cell-template.enum.ts | 16 +++++- .../src/app/shared/models/cd-table-column.ts | 1 + .../src/app/shared/pipes/array.pipe.spec.ts | 21 +++++++ .../src/app/shared/pipes/array.pipe.ts | 26 +++++++++ .../src/app/shared/pipes/boolean.pipe.spec.ts | 57 +++++++++++++++++++ .../src/app/shared/pipes/boolean.pipe.ts | 26 +++++++++ .../src/app/shared/pipes/pipes.module.ts | 8 +++ .../mgr/dashboard/frontend/src/styles.scss | 10 ++++ 22 files changed, 246 insertions(+), 88 deletions(-) create mode 100755 src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/array.pipe.spec.ts create mode 100755 src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/array.pipe.ts create mode 100755 src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/boolean.pipe.spec.ts create mode 100755 src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/boolean.pipe.ts mode change 100644 => 100755 src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/pipes.module.ts diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.html index 4f65b3f2c54d0..5ccdbf03807bc 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.html @@ -11,12 +11,6 @@ [columns]="imagesColumns"> - - {{ value }} - - diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.ts index 210309652253e..7ba012f58a566 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi/iscsi.component.ts @@ -3,6 +3,7 @@ import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { I18n } from '@ngx-translate/i18n-polyfill'; import { IscsiService } from '../../../shared/api/iscsi.service'; +import { CellTemplate } from '../../../shared/enum/cell-template.enum'; import { DimlessPipe } from '../../../shared/pipes/dimless.pipe'; import { IscsiBackstorePipe } from '../../../shared/pipes/iscsi-backstore.pipe'; @@ -12,8 +13,6 @@ import { IscsiBackstorePipe } from '../../../shared/pipes/iscsi-backstore.pipe'; styleUrls: ['./iscsi.component.scss'] }) export class IscsiComponent implements OnInit { - @ViewChild('statusColorTpl', { static: true }) - statusColorTpl: TemplateRef; @ViewChild('iscsiSparklineTpl', { static: true }) iscsiSparklineTpl: TemplateRef; @ViewChild('iscsiPerSecondTpl', { static: true }) @@ -42,7 +41,15 @@ export class IscsiComponent implements OnInit { { name: this.i18n('State'), prop: 'state', - cellTemplate: this.statusColorTpl + flexGrow: 1, + cellClass: 'text-center', + cellTransformation: CellTemplate.badge, + customTemplateConfig: { + map: { + up: { class: 'badge-success' }, + down: { class: 'badge-danger' } + } + } }, { name: this.i18n('# Targets'), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.html index b0a5abbbeb729..90fbf53841ad5 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.html @@ -10,16 +10,6 @@ - - PROTECTED - UNPROTECTED - - You are about to rollback diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts index 225354329cbe1..ae71c7ff83a7a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.ts @@ -47,8 +47,6 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges { rbdName: string; @ViewChild('nameTpl', { static: false }) nameTpl: TemplateRef; - @ViewChild('protectTpl', { static: true }) - protectTpl: TemplateRef; @ViewChild('rollbackTpl', { static: true }) rollbackTpl: TemplateRef; @@ -113,7 +111,13 @@ export class RbdSnapshotListComponent implements OnInit, OnChanges { prop: 'is_protected', flexGrow: 1, cellClass: 'text-center', - cellTemplate: this.protectTpl + cellTransformation: CellTemplate.badge, + customTemplateConfig: { + map: { + true: { value: this.i18n('PROTECTED'), class: 'badge-success' }, + false: { value: this.i18n('UNPROTECTED'), class: 'badge-info' } + } + } }, { name: this.i18n('Created'), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.html index 334dcfce2311f..208bb0d34f19f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.html @@ -34,11 +34,3 @@ - - - - osd.{{ osdId }} -   - - diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts index 1b8a00bb96bfe..b54d846eef1cc 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory-devices/inventory-devices.component.ts @@ -1,18 +1,10 @@ -import { - Component, - EventEmitter, - Input, - OnChanges, - OnInit, - Output, - TemplateRef, - ViewChild -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; import { I18n } from '@ngx-translate/i18n-polyfill'; import { getterForProp } from '@swimlane/ngx-datatable/release/utils'; import * as _ from 'lodash'; +import { CellTemplate } from '../../../../shared/enum/cell-template.enum'; import { Icons } from '../../../../shared/enum/icons.enum'; import { CdTableColumn } from '../../../../shared/models/cd-table-column'; import { DimlessBinaryPipe } from '../../../../shared/pipes/dimless-binary.pipe'; @@ -26,9 +18,6 @@ import { InventoryDevice } from './inventory-device.model'; styleUrls: ['./inventory-devices.component.scss'] }) export class InventoryDevicesComponent implements OnInit, OnChanges { - @ViewChild('osds', { static: true }) - osds: TemplateRef; - // Devices @Input() devices: InventoryDevice[] = []; @@ -74,7 +63,15 @@ export class InventoryDevicesComponent implements OnInit, OnChanges { { name: this.i18n('Type'), prop: 'human_readable_type', - flexGrow: 1 + flexGrow: 1, + cellClass: 'text-center', + cellTransformation: CellTemplate.badge, + customTemplateConfig: { + map: { + hdd: { value: 'HDD', class: 'badge-hdd' }, + 'ssd/nvme': { value: 'SSD', class: 'badge-ssd' } + } + } }, { name: this.i18n('Available'), @@ -101,7 +98,12 @@ export class InventoryDevicesComponent implements OnInit, OnChanges { name: this.i18n('OSDs'), prop: 'osd_ids', flexGrow: 1, - cellTemplate: this.osds + cellClass: 'text-center', + cellTransformation: CellTemplate.badge, + customTemplateConfig: { + class: 'badge-dark', + prefix: 'osd.' + } } ]; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html index 45c488fb1bff8..f2f0d2fabb3f4 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html @@ -31,15 +31,6 @@ - - - {{ state }} -   - - - ; @ViewChild('osdUsageTpl', { static: true }) osdUsageTpl: TemplateRef; @ViewChild('markOsdConfirmationTpl', { static: true }) @@ -207,7 +205,20 @@ export class OsdListComponent implements OnInit { this.columns = [ { prop: 'host.name', name: this.i18n('Host') }, { prop: 'id', name: this.i18n('ID'), cellTransformation: CellTemplate.bold }, - { prop: 'collectedStates', name: this.i18n('Status'), cellTemplate: this.statusColor }, + { + prop: 'collectedStates', + name: this.i18n('Status'), + cellTransformation: CellTemplate.badge, + customTemplateConfig: { + map: { + in: { class: 'badge-success' }, + up: { class: 'badge-success' }, + down: { class: 'badge-danger' }, + out: { class: 'badge-danger' }, + destroyed: { class: 'badge-danger' } + } + } + }, { prop: 'stats.numpg', name: this.i18n('PGs') }, { prop: 'stats.stat_bytes', name: this.i18n('Size'), pipe: this.dimlessBinaryPipe }, { prop: 'stats.usage', name: this.i18n('Usage'), cellTemplate: this.osdUsageTpl }, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/device-list/device-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/device-list/device-list.component.html index 79eb99b455013..66c18df61b316 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/device-list/device-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/device-list/device-list.component.html @@ -22,27 +22,3 @@ let-value="value"> {{value}} - - - - Good - - - Warning - - - Bad - - - Stale - - - Unknown - - diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/device-list/device-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/device-list/device-list.component.ts index 3aeab98c5ac75..2626c168e2b38 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/device-list/device-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/device-list/device-list.component.ts @@ -3,6 +3,7 @@ import { Component, Input, OnInit, TemplateRef, ViewChild } from '@angular/core' import { I18n } from '@ngx-translate/i18n-polyfill'; import { HostService } from '../../../shared/api/host.service'; import { OsdService } from '../../../shared/api/osd.service'; +import { CellTemplate } from '../../../shared/enum/cell-template.enum'; import { CdTableColumn } from '../../../shared/models/cd-table-column'; import { CdDevice } from '../../../shared/models/devices'; @@ -23,8 +24,6 @@ export class DeviceListComponent implements OnInit { lifeExpectancyTemplate: TemplateRef; @ViewChild('lifeExpectancyTimestamp', { static: true }) lifeExpectancyTimestampTemplate: TemplateRef; - @ViewChild('state', { static: true }) - stateTemplate: TemplateRef; devices: CdDevice[] = null; columns: CdTableColumn[] = []; @@ -52,7 +51,17 @@ export class DeviceListComponent implements OnInit { { prop: 'state', name: this.i18n('State of Health'), - cellTemplate: this.stateTemplate + flexGrow: 1, + cellTransformation: CellTemplate.badge, + customTemplateConfig: { + map: { + good: { value: this.i18n('Good'), class: 'badge-success' }, + warning: { value: this.i18n('Warning'), class: 'badge-warning' }, + bad: { value: this.i18n('Bad'), class: 'badge-danger' }, + stale: { value: this.i18n('Stale'), class: 'badge-info' }, + unknown: { value: this.i18n('Unknown'), class: 'badge-dark' } + } + } }, { prop: 'life_expectancy_weeks', diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.spec.ts index 77affe22fedd8..965418ddbf3b1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.spec.ts @@ -10,6 +10,7 @@ import { ComponentsModule } from '../../components/components.module'; import { CellTemplate } from '../../enum/cell-template.enum'; import { CdTableColumn } from '../../models/cd-table-column'; import { CdDatePipe } from '../../pipes/cd-date.pipe'; +import { PipesModule } from '../../pipes/pipes.module'; import { TableComponent } from '../table/table.component'; import { TableKeyValueComponent } from './table-key-value.component'; @@ -24,7 +25,8 @@ describe('TableKeyValueComponent', () => { NgxDatatableModule, ComponentsModule, RouterTestingModule, - BsDropdownModule.forRoot() + BsDropdownModule.forRoot(), + PipesModule ] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html index b029f64ac9522..7b3fb44a183bd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html @@ -171,7 +171,7 @@ + [hidden]="!(value | boolean)"> {{ value }} + + + + + {{ column?.customTemplateConfig?.map[item]?.value ? column.customTemplateConfig.map[item].value : column?.customTemplateConfig?.prefix ? column.customTemplateConfig.prefix + item : item }} + +   + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts index 46baec7e139cf..32c095fb51a2b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts @@ -9,6 +9,7 @@ import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; import { configureTestBed } from '../../../../testing/unit-test-helper'; import { ComponentsModule } from '../../components/components.module'; import { CdTableFetchDataContext } from '../../models/cd-table-fetch-data-context'; +import { PipesModule } from '../../pipes/pipes.module'; import { TableComponent } from './table.component'; describe('TableComponent', () => { @@ -38,7 +39,8 @@ describe('TableComponent', () => { FormsModule, ComponentsModule, RouterTestingModule, - BsDropdownModule.forRoot() + BsDropdownModule.forRoot(), + PipesModule ] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts index e2a1d08e63e9a..e0053fc4a6003 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts @@ -53,6 +53,8 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O executingTpl: TemplateRef; @ViewChild('classAddingTpl', { static: true }) classAddingTpl: TemplateRef; + @ViewChild('badgeTpl', { static: true }) + badgeTpl: TemplateRef; // This is the array with the items to be shown. @Input() @@ -370,6 +372,7 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O this.cellTemplates.perSecond = this.perSecondTpl; this.cellTemplates.executing = this.executingTpl; this.cellTemplates.classAdding = this.classAddingTpl; + this.cellTemplates.badge = this.badgeTpl; } useCustomClass(value: any): string { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/cell-template.enum.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/cell-template.enum.ts index cf3e11a7ecfb9..76746b3bd030a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/cell-template.enum.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/enum/cell-template.enum.ts @@ -5,5 +5,19 @@ export enum CellTemplate { checkIcon = 'checkIcon', routerLink = 'routerLink', executing = 'executing', - classAdding = 'classAdding' + classAdding = 'classAdding', + // Display the cell value as a badge. The template + // supports an optional custom configuration: + // { + // ... + // customTemplateConfig: { + // class?: string; // Additional class name. + // prefix?: any; // Prefix of the value to be displayed. + // // 'map' and 'prefix' exclude each other. + // map?: { + // [key: any]: { value: any, class?: string } + // } + // } + // } + badge = 'badge' } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column.ts index 69194e8b88d77..64cd7db402ef6 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-column.ts @@ -6,4 +6,5 @@ export interface CdTableColumn extends TableColumn { cellTransformation?: CellTemplate; isHidden?: boolean; prop: TableColumnProp; // Enforces properties to get sortable columns + customTemplateConfig?: any; // Custom configuration used by cell templates. } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/array.pipe.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/array.pipe.spec.ts new file mode 100755 index 0000000000000..610e22c4397ee --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/array.pipe.spec.ts @@ -0,0 +1,21 @@ +import { ArrayPipe } from './array.pipe'; + +describe('ArrayPipe', () => { + const pipe = new ArrayPipe(); + + it('create an instance', () => { + expect(pipe).toBeTruthy(); + }); + + it('transforms string to array', () => { + expect(pipe.transform('foo')).toStrictEqual(['foo']); + }); + + it('transforms array to array', () => { + expect(pipe.transform(['foo'], true)).toStrictEqual([['foo']]); + }); + + it('do not transforms array to array', () => { + expect(pipe.transform(['foo'])).toStrictEqual(['foo']); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/array.pipe.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/array.pipe.ts new file mode 100755 index 0000000000000..b61b0b023e70e --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/array.pipe.ts @@ -0,0 +1,26 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +import * as _ from 'lodash'; + +/** + * Convert the given value to an array. + */ +@Pipe({ + name: 'array' +}) +export class ArrayPipe implements PipeTransform { + /** + * Convert the given value into an array. If the value is already an + * array, then nothing happens, except the `force` flag is set. + * @param value The value to process. + * @param force Convert the specified value to an array, either it is + * already an array. + */ + transform(value: any, force = false): any[] { + let result = value; + if (!_.isArray(value) || (_.isArray(value) && force)) { + result = [value]; + } + return result; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/boolean.pipe.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/boolean.pipe.spec.ts new file mode 100755 index 0000000000000..36c5ed021d778 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/boolean.pipe.spec.ts @@ -0,0 +1,57 @@ +import { BooleanPipe } from './boolean.pipe'; + +describe('BooleanPipe', () => { + const pipe = new BooleanPipe(); + + it('create an instance', () => { + expect(pipe).toBeTruthy(); + }); + + it('transforms to false [1/4]', () => { + expect(pipe.transform('n')).toBe(false); + }); + + it('transforms to false [2/4]', () => { + expect(pipe.transform(false)).toBe(false); + }); + + it('transforms to false [3/4]', () => { + expect(pipe.transform('bar')).toBe(false); + }); + + it('transforms to false [4/4]', () => { + expect(pipe.transform(2)).toBe(false); + }); + + it('transforms to true [1/8]', () => { + expect(pipe.transform(true)).toBe(true); + }); + + it('transforms to true [2/8]', () => { + expect(pipe.transform(1)).toBe(true); + }); + + it('transforms to true [3/8]', () => { + expect(pipe.transform('y')).toBe(true); + }); + + it('transforms to true [4/8]', () => { + expect(pipe.transform('yes')).toBe(true); + }); + + it('transforms to true [5/8]', () => { + expect(pipe.transform('t')).toBe(true); + }); + + it('transforms to true [6/8]', () => { + expect(pipe.transform('true')).toBe(true); + }); + + it('transforms to true [7/8]', () => { + expect(pipe.transform('on')).toBe(true); + }); + + it('transforms to true [8/8]', () => { + expect(pipe.transform('1')).toBe(true); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/boolean.pipe.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/boolean.pipe.ts new file mode 100755 index 0000000000000..b94a40bc4fc01 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/boolean.pipe.ts @@ -0,0 +1,26 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +/** + * Convert the given value to a boolean value. + */ +@Pipe({ + name: 'boolean' +}) +export class BooleanPipe implements PipeTransform { + transform(value: any): boolean { + let result = false; + switch (value) { + case true: + case 1: + case 'y': + case 'yes': + case 't': + case 'true': + case 'on': + case '1': + result = true; + break; + } + return result; + } +} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/pipes.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/pipes.module.ts old mode 100644 new mode 100755 index 898316892d065..44994663e6562 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/pipes.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/pipes/pipes.module.ts @@ -1,7 +1,9 @@ import { CommonModule, DatePipe } from '@angular/common'; import { NgModule } from '@angular/core'; +import { ArrayPipe } from './array.pipe'; import { BooleanTextPipe } from './boolean-text.pipe'; +import { BooleanPipe } from './boolean.pipe'; import { CdDatePipe } from './cd-date.pipe'; import { CephReleaseNamePipe } from './ceph-release-name.pipe'; import { CephShortVersionPipe } from './ceph-short-version.pipe'; @@ -27,6 +29,8 @@ import { UpperFirstPipe } from './upper-first.pipe'; @NgModule({ imports: [CommonModule], declarations: [ + ArrayPipe, + BooleanPipe, BooleanTextPipe, DimlessBinaryPipe, DimlessBinaryPerSecondPipe, @@ -51,6 +55,8 @@ import { UpperFirstPipe } from './upper-first.pipe'; DurationPipe ], exports: [ + ArrayPipe, + BooleanPipe, BooleanTextPipe, DimlessBinaryPipe, DimlessBinaryPerSecondPipe, @@ -75,6 +81,8 @@ import { UpperFirstPipe } from './upper-first.pipe'; DurationPipe ], providers: [ + ArrayPipe, + BooleanPipe, BooleanTextPipe, DatePipe, CephShortVersionPipe, diff --git a/src/pybind/mgr/dashboard/frontend/src/styles.scss b/src/pybind/mgr/dashboard/frontend/src/styles.scss index cdff4f4c9ad52..ef3cbb0f8bfeb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/styles.scss +++ b/src/pybind/mgr/dashboard/frontend/src/styles.scss @@ -431,3 +431,13 @@ bfv-messages { background: $color-green; } } + +// Custom badges. +.badge-hdd { + color: $color-solid-white; + background-color: $color-blue-gray; +} +.badge-ssd { + color: $color-solid-white; + background-color: $color-blue; +} -- 2.39.5