[columns]="imagesColumns">
</cd-table>
-<ng-template #statusColorTpl
- let-value="value">
- <span class="badge"
- [ngClass]="{'badge-success': 'up' == value, 'badge-danger': 'down' == value}">{{ value }}</span>
-</ng-template>
-
<ng-template #iscsiSparklineTpl
let-row="row"
let-value="value">
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';
styleUrls: ['./iscsi.component.scss']
})
export class IscsiComponent implements OnInit {
- @ViewChild('statusColorTpl', { static: true })
- statusColorTpl: TemplateRef<any>;
@ViewChild('iscsiSparklineTpl', { static: true })
iscsiSparklineTpl: TemplateRef<any>;
@ViewChild('iscsiPerSecondTpl', { static: true })
{
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'),
</cd-table-actions>
</cd-table>
-<ng-template #protectTpl
- let-value="value">
- <span *ngIf="value"
- class="badge badge-success"
- i18n>PROTECTED</span>
- <span *ngIf="!value"
- class="badge badge-info"
- i18n>UNPROTECTED</span>
-</ng-template>
-
<ng-template #rollbackTpl
let-value>
<ng-container i18n>You are about to rollback</ng-container>
rbdName: string;
@ViewChild('nameTpl', { static: false })
nameTpl: TemplateRef<any>;
- @ViewChild('protectTpl', { static: true })
- protectTpl: TemplateRef<any>;
@ViewChild('rollbackTpl', { static: true })
rollbackTpl: TemplateRef<any>;
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'),
</div>
</div>
</cd-table>
-
-<ng-template #osds
- let-value="value">
- <span *ngFor="let osdId of value; last as last">
- <span class="badge badge-dark">osd.{{ osdId }}</span>
- <span *ngIf="!last"> </span>
- </span>
-</ng-template>
-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';
styleUrls: ['./inventory-devices.component.scss']
})
export class InventoryDevicesComponent implements OnInit, OnChanges {
- @ViewChild('osds', { static: true })
- osds: TemplateRef<any>;
-
// Devices
@Input() devices: InventoryDevice[] = [];
{
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'),
name: this.i18n('OSDs'),
prop: 'osd_ids',
flexGrow: 1,
- cellTemplate: this.osds
+ cellClass: 'text-center',
+ cellTransformation: CellTemplate.badge,
+ customTemplateConfig: {
+ class: 'badge-dark',
+ prefix: 'osd.'
+ }
}
];
</cd-osd-details>
</cd-table>
- <ng-template #statusColor
- let-value="value">
- <span *ngFor="let state of value; last as last">
- <span class="badge"
- [ngClass]="{'badge-success': ['in', 'up'].includes(state), 'badge-danger': ['down', 'out', 'destroyed'].includes(state)}">{{ state }}</span>
- <span *ngIf="!last"> </span>
- </span>
- </ng-template>
-
<ng-template #osdUsageTpl
let-row="row">
<cd-usage-bar [totalBytes]="row.stats.stat_bytes"
providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
})
export class OsdListComponent implements OnInit {
- @ViewChild('statusColor', { static: true })
- statusColor: TemplateRef<any>;
@ViewChild('osdUsageTpl', { static: true })
osdUsageTpl: TemplateRef<any>;
@ViewChild('markOsdConfirmationTpl', { static: true })
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 },
let-value="value">
{{value}}
</ng-template>
-
-<ng-template #state
- let-value="value">
- <ng-container *ngIf="value === 'good'">
- <span class="badge badge-success"
- i18n>Good</span>
- </ng-container>
- <ng-container *ngIf="value === 'warning'">
- <span class="badge badge-warning"
- i18n>Warning</span>
- </ng-container>
- <ng-container *ngIf="value === 'bad'">
- <span class="badge badge-danger"
- i18n>Bad</span>
- </ng-container>
- <ng-container *ngIf="value === 'stale'">
- <span class="badge badge-info"
- i18n>Stale</span>
- </ng-container>
- <ng-container *ngIf="value === 'unknown'">
- <span class="badge badge-dark"
- i18n>Unknown</span>
- </ng-container>
-</ng-template>
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';
lifeExpectancyTemplate: TemplateRef<any>;
@ViewChild('lifeExpectancyTimestamp', { static: true })
lifeExpectancyTimestampTemplate: TemplateRef<any>;
- @ViewChild('state', { static: true })
- stateTemplate: TemplateRef<any>;
devices: CdDevice[] = null;
columns: CdTableColumn[] = [];
{
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',
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';
NgxDatatableModule,
ComponentsModule,
RouterTestingModule,
- BsDropdownModule.forRoot()
+ BsDropdownModule.forRoot(),
+ PipesModule
]
});
<ng-template #checkIconTpl
let-value="value">
<i [ngClass]="[icons.check]"
- [hidden]="!value"></i>
+ [hidden]="!(value | boolean)"></i>
</ng-template>
<ng-template #perSecondTpl
let-value="value">
<span class="{{useCustomClass(value)}}">{{ value }}</span>
</ng-template>
+
+<ng-template #badgeTpl
+ let-column="column"
+ let-value="value">
+ <span *ngFor="let item of (value | array); last as last">
+ <span class="badge"
+ [ngClass]="column?.customTemplateConfig?.map[item]?.class ? column.customTemplateConfig.map[item].class : (column?.customTemplateConfig?.class ? column.customTemplateConfig.class : 'badge-primary')">
+ {{ column?.customTemplateConfig?.map[item]?.value ? column.customTemplateConfig.map[item].value : column?.customTemplateConfig?.prefix ? column.customTemplateConfig.prefix + item : item }}
+ </span>
+ <span *ngIf="!last"> </span>
+ </span>
+</ng-template>
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', () => {
FormsModule,
ComponentsModule,
RouterTestingModule,
- BsDropdownModule.forRoot()
+ BsDropdownModule.forRoot(),
+ PipesModule
]
});
executingTpl: TemplateRef<any>;
@ViewChild('classAddingTpl', { static: true })
classAddingTpl: TemplateRef<any>;
+ @ViewChild('badgeTpl', { static: true })
+ badgeTpl: TemplateRef<any>;
// This is the array with the items to be shown.
@Input()
this.cellTemplates.perSecond = this.perSecondTpl;
this.cellTemplates.executing = this.executingTpl;
this.cellTemplates.classAdding = this.classAddingTpl;
+ this.cellTemplates.badge = this.badgeTpl;
}
useCustomClass(value: any): string {
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'
}
cellTransformation?: CellTemplate;
isHidden?: boolean;
prop: TableColumnProp; // Enforces properties to get sortable columns
+ customTemplateConfig?: any; // Custom configuration used by cell templates.
}
--- /dev/null
+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']);
+ });
+});
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+ });
+});
--- /dev/null
+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;
+ }
+}
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';
@NgModule({
imports: [CommonModule],
declarations: [
+ ArrayPipe,
+ BooleanPipe,
BooleanTextPipe,
DimlessBinaryPipe,
DimlessBinaryPerSecondPipe,
DurationPipe
],
exports: [
+ ArrayPipe,
+ BooleanPipe,
BooleanTextPipe,
DimlessBinaryPipe,
DimlessBinaryPerSecondPipe,
DurationPipe
],
providers: [
+ ArrayPipe,
+ BooleanPipe,
BooleanTextPipe,
DatePipe,
CephShortVersionPipe,
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;
+}