}
}
- /**
- * This was adapted for octopus, because row expansion doesn't exist in octopus.
- * By changing the method code, we prevent making more changes in the actual
- * test files.
- */
getExpandCollapseElement(content?: string) {
- return this.getFirstTableCell(content);
- }
+ this.waitDataTableToLoad();
+ if (content) {
+ return cy.contains('.datatable-body-row', content).find('.tc_expand-collapse');
+ } else {
+ return cy.get('.tc_expand-collapse').first();
+ }
+ }
/**
* Gets column headers of table
*/
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { SharedModule } from '../../../shared/shared.module';
import { IscsiTargetDetailsComponent } from './iscsi-target-details.component';
backstores: ['backstore:1', 'backstore:2'],
default_backstore: 'backstore:1'
};
- component.selection = new CdTableSelection();
- component.selection.selected = [
- {
- target_iqn: 'iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw',
- portals: [{ host: 'node1', ip: '192.168.100.201' }],
- disks: [
- {
- pool: 'rbd',
- image: 'disk_1',
- backstore: 'backstore:1',
- controls: { hw_max_sectors: 1 }
- }
- ],
- clients: [
- {
- client_iqn: 'iqn.1994-05.com.redhat:rh7-client',
- luns: [{ pool: 'rbd', image: 'disk_1' }],
- auth: {
- user: 'myiscsiusername'
- },
- info: {
- alias: 'myhost',
- ip_address: ['192.168.200.1'],
- state: { LOGGED_IN: ['node1'] }
- }
+ component.selection = undefined;
+ component.selection = {
+ target_iqn: 'iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw',
+ portals: [{ host: 'node1', ip: '192.168.100.201' }],
+ disks: [
+ {
+ pool: 'rbd',
+ image: 'disk_1',
+ backstore: 'backstore:1',
+ controls: { hw_max_sectors: 1 }
+ }
+ ],
+ clients: [
+ {
+ client_iqn: 'iqn.1994-05.com.redhat:rh7-client',
+ luns: [{ pool: 'rbd', image: 'disk_1' }],
+ auth: {
+ user: 'myiscsiusername'
+ },
+ info: {
+ alias: 'myhost',
+ ip_address: ['192.168.200.1'],
+ state: { LOGGED_IN: ['node1'] }
}
- ],
- groups: [],
- target_controls: { dataout_timeout: 2 }
- }
- ];
+ }
+ ],
+ groups: [],
+ target_controls: { dataout_timeout: 2 }
+ };
fixture.detectChanges();
});
import { TableComponent } from '../../../shared/datatable/table/table.component';
import { Icons } from '../../../shared/enum/icons.enum';
import { CdTableColumn } from '../../../shared/models/cd-table-column';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { BooleanTextPipe } from '../../../shared/pipes/boolean-text.pipe';
import { IscsiBackstorePipe } from '../../../shared/pipes/iscsi-backstore.pipe';
})
export class IscsiTargetDetailsComponent implements OnChanges, OnInit {
@Input()
- selection: CdTableSelection;
+ selection: any;
@Input()
settings: any;
@Input()
}
ngOnChanges() {
- if (this.selection.hasSelection) {
- this.selectedItem = this.selection.first();
+ if (this.selection) {
+ this.selectedItem = this.selection;
this.generateTree();
}
identifier="target_iqn"
forceIdentifier="true"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)">
<div class="table-actions btn-toolbar">
<cd-table-actions class="btn-group"
</div>
<cd-iscsi-target-details cdTableDetail
- *ngIf="selection.hasSingleSelection"
+ *ngIf="expandedRow"
[cephIscsiConfigVersion]="cephIscsiConfigVersion"
- [selection]="selection"
+ [selection]="expandedRow"
[settings]="settings"></cd-iscsi-target-details>
</cd-table>
import { Subscription } from 'rxjs';
import { IscsiService } from '../../../shared/api/iscsi.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
import { TableComponent } from '../../../shared/datatable/table/table.component';
styleUrls: ['./iscsi-target-list.component.scss'],
providers: [TaskListService]
})
-export class IscsiTargetListComponent implements OnInit, OnDestroy {
+export class IscsiTargetListComponent extends ListWithDetails implements OnInit, OnDestroy {
@ViewChild(TableComponent, { static: false })
table: TableComponent;
private taskWrapper: TaskWrapperService,
public actionLabels: ActionLabelsI18n
) {
+ super();
this.permission = this.authStorageService.getPermissions().iscsi;
this.tableActions = [
<ng-container i18n>Only available for RBD images with <strong>fast-diff</strong> enabled</ng-container>
</ng-template>
-<tabset *ngIf="selection?.hasSingleSelection">
+<tabset *ngIf="selection">
<tab i18n-heading
heading="Details">
<table class="table table-striped table-bordered">
<tr>
<td i18n
class="bold w-25">Name</td>
- <td class="w-75">{{ selectedItem.name }}</td>
+ <td class="w-75">{{ selection.name }}</td>
</tr>
<tr>
<td i18n
class="bold">Pool</td>
- <td>{{ selectedItem.pool_name }}</td>
+ <td>{{ selection.pool_name }}</td>
</tr>
<tr>
<td i18n
class="bold">Data Pool</td>
- <td>{{ selectedItem.data_pool | empty }}</td>
+ <td>{{ selection.data_pool | empty }}</td>
</tr>
<tr>
<td i18n
class="bold">Created</td>
- <td>{{ selectedItem.timestamp | cdDate }}</td>
+ <td>{{ selection.timestamp | cdDate }}</td>
</tr>
<tr>
<td i18n
class="bold">Size</td>
- <td>{{ selectedItem.size | dimlessBinary }}</td>
+ <td>{{ selection.size | dimlessBinary }}</td>
</tr>
<tr>
<td i18n
class="bold">Objects</td>
- <td>{{ selectedItem.num_objs | dimless }}</td>
+ <td>{{ selection.num_objs | dimless }}</td>
</tr>
<tr>
<td i18n
class="bold">Object size</td>
- <td>{{ selectedItem.obj_size | dimlessBinary }}</td>
+ <td>{{ selection.obj_size | dimlessBinary }}</td>
</tr>
<tr>
<td i18n
class="bold">Features</td>
<td>
- <span *ngFor="let feature of selectedItem.features_name">
+ <span *ngFor="let feature of selection.features_name">
<span class="badge badge-dark mr-2">{{ feature }}</span>
</span>
</td>
<td i18n
class="bold">Provisioned</td>
<td>
- <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') === -1">
+ <span *ngIf="selection.features_name?.indexOf('fast-diff') === -1">
<span class="form-text text-muted"
[tooltip]="usageNotAvailableTooltipTpl"
placement="right"
i18n>N/A</span>
</span>
- <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') !== -1">
- {{ selectedItem.disk_usage | dimlessBinary }}
+ <span *ngIf="selection.features_name?.indexOf('fast-diff') !== -1">
+ {{ selection.disk_usage | dimlessBinary }}
</span>
</td>
</tr>
<td i18n
class="bold">Total provisioned</td>
<td>
- <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') === -1">
+ <span *ngIf="selection.features_name?.indexOf('fast-diff') === -1">
<span class="form-text text-muted"
[tooltip]="usageNotAvailableTooltipTpl"
placement="right"
i18n>N/A</span>
</span>
- <span *ngIf="selectedItem.features_name?.indexOf('fast-diff') !== -1">
- {{ selectedItem.total_disk_usage | dimlessBinary }}
+ <span *ngIf="selection.features_name?.indexOf('fast-diff') !== -1">
+ {{ selection.total_disk_usage | dimlessBinary }}
</span>
</td>
</tr>
<tr>
<td i18n
class="bold">Striping unit</td>
- <td>{{ selectedItem.stripe_unit | dimlessBinary }}</td>
+ <td>{{ selection.stripe_unit | dimlessBinary }}</td>
</tr>
<tr>
<td i18n
class="bold">Striping count</td>
- <td>{{ selectedItem.stripe_count }}</td>
+ <td>{{ selection.stripe_count }}</td>
</tr>
<tr>
<td i18n
class="bold">Parent</td>
<td>
- <span *ngIf="selectedItem.parent">{{ selectedItem.parent.pool_name }}<span *ngIf="selectedItem.parent.pool_namespace">/{{ selectedItem.parent.pool_namespace }}</span>/{{ selectedItem.parent.image_name }}@{{ selectedItem.parent.snap_name }}</span>
- <span *ngIf="!selectedItem.parent">-</span>
+ <span *ngIf="selection.parent">{{ selection.parent.pool_name }}<span *ngIf="selection.parent.pool_namespace">/{{ selection.parent.pool_namespace }}</span>/{{ selection.parent.image_name }}@{{ selection.parent.snap_name }}</span>
+ <span *ngIf="!selection.parent">-</span>
</td>
</tr>
<tr>
<td i18n
class="bold">Block name prefix</td>
- <td>{{ selectedItem.block_name_prefix }}</td>
+ <td>{{ selection.block_name_prefix }}</td>
</tr>
<tr>
<td i18n
class="bold">Order</td>
- <td>{{ selectedItem.order }}</td>
+ <td>{{ selection.order }}</td>
</tr>
</tbody>
</table>
</tab>
<tab i18n-heading
heading="Snapshots">
- <cd-rbd-snapshot-list [snapshots]="selectedItem.snapshots"
- [featuresName]="selectedItem.features_name"
- [poolName]="selectedItem.pool_name"
- [namespace]="selectedItem.namespace"
- [rbdName]="selectedItem.name"></cd-rbd-snapshot-list>
+ <cd-rbd-snapshot-list [snapshots]="selection.snapshots"
+ [featuresName]="selection.features_name"
+ [poolName]="selection.pool_name"
+ [namespace]="selection.namespace"
+ [rbdName]="selection.name"></cd-rbd-snapshot-list>
</tab>
<tab i18n-heading
heading="Configuration">
- <cd-rbd-configuration-table [data]="selectedItem['configuration']"></cd-rbd-configuration-table>
+ <cd-rbd-configuration-table [data]="selection['configuration']"></cd-rbd-configuration-table>
</tab>
</tabset>
-import { Component, Input, OnChanges, TemplateRef, ViewChild } from '@angular/core';
+import { Component, Input, TemplateRef, ViewChild } from '@angular/core';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { RbdFormModel } from '../rbd-form/rbd-form.model';
@Component({
templateUrl: './rbd-details.component.html',
styleUrls: ['./rbd-details.component.scss']
})
-export class RbdDetailsComponent implements OnChanges {
+export class RbdDetailsComponent {
@Input()
- selection: CdTableSelection;
- selectedItem: RbdFormModel;
+ selection: RbdFormModel;
@Input()
images: any;
@ViewChild('poolConfigurationSourceTpl', { static: true })
poolConfigurationSourceTpl: TemplateRef<any>;
constructor() {}
-
- ngOnChanges() {
- if (this.selection.hasSelection) {
- this.selectedItem = this.selection.first();
- }
- }
}
[searchableObjects]="true"
forceIdentifier="true"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)">
<cd-table-actions class="table-actions"
[permission]="permission"
[tableActions]="tableActions">
</cd-table-actions>
<cd-rbd-details cdTableDetail
- [selection]="selection">
+ [selection]="expandedRow">
</cd-rbd-details>
</cd-table>
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { RbdService } from '../../../shared/api/rbd.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { ConfirmationModalComponent } from '../../../shared/components/confirmation-modal/confirmation-modal.component';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }
]
})
-export class RbdListComponent implements OnInit {
+export class RbdListComponent extends ListWithDetails implements OnInit {
@ViewChild(TableComponent, { static: true })
table: TableComponent;
@ViewChild('usageTpl', { static: false })
private urlBuilder: URLBuilderService,
public actionLabels: ActionLabelsI18n
) {
+ super();
this.permission = this.authStorageService.getPermissions().rbdImage;
const getImageUri = () =>
this.selection.first() &&
identifier="id"
forceIdentifier="true"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)">
<cd-cephfs-tabs cdTableDetail
- [selection]="selection">
+ [selection]="expandedRow">
</cd-cephfs-tabs>
</cd-table>
import { I18n } from '@ngx-translate/i18n-polyfill';
import { CephfsService } from '../../../shared/api/cephfs.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { CellTemplate } from '../../../shared/enum/cell-template.enum';
import { CdTableColumn } from '../../../shared/models/cd-table-column';
import { CdTableFetchDataContext } from '../../../shared/models/cd-table-fetch-data-context';
templateUrl: './cephfs-list.component.html',
styleUrls: ['./cephfs-list.component.scss']
})
-export class CephfsListComponent implements OnInit {
+export class CephfsListComponent extends ListWithDetails implements OnInit {
columns: CdTableColumn[];
filesystems: any = [];
selection = new CdTableSelection();
private cephfsService: CephfsService,
private cdDatePipe: CdDatePipe,
private i18n: I18n
- ) {}
+ ) {
+ super();
+ }
ngOnInit() {
this.columns = [
-<tabset *ngIf="selectedItem">
+<tabset *ngIf="selection">
<tab i18n-heading
(selectTab)="softRefresh()"
heading="Details">
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
import { CephfsService } from '../../../shared/api/cephfs.service';
import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { SharedModule } from '../../../shared/shared.module';
import { CephfsClientsComponent } from '../cephfs-clients/cephfs-clients.component';
import { CephfsDetailComponent } from '../cephfs-detail/cephfs-detail.component';
};
};
- const setSelection = (selection: object[]) => {
- component.selection.selected = selection;
+ const setSelection = (selection: any) => {
+ component.selection = selection;
component.ngOnChanges();
};
const selectFs = (id: number, name: string) => {
- setSelection([
- {
- id,
- mdsmap: {
- info: {
- something: {
- name
- }
+ setSelection({
+ id,
+ mdsmap: {
+ info: {
+ something: {
+ name
}
}
}
- ]);
+ });
};
const updateData = () => {
beforeEach(() => {
fixture = TestBed.createComponent(CephfsTabsComponent);
component = fixture.componentInstance;
- component.selection = new CdTableSelection();
+ component.selection = undefined;
data = {
standbys: 'b',
pools: [{}, {}],
});
it('should resist invalid mds info', () => {
- setSelection([
- {
- id: 3,
- mdsmap: {
- info: {}
- }
+ setSelection({
+ id: 3,
+ mdsmap: {
+ info: {}
}
- ]);
+ });
expect(component.grafanaId).toBe(undefined);
});
});
it('should should unsubscribe on deselect', () => {
- setSelection([]);
+ setSelection(undefined);
expect(old.unsubscribed).toBe(true);
expect(getReload()).toBe(undefined); // Cleared timer subscription
});
import { CephfsService } from '../../../shared/api/cephfs.service';
import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { Permission } from '../../../shared/models/permissions';
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
})
export class CephfsTabsComponent implements OnChanges, OnDestroy {
@Input()
- selection: CdTableSelection;
- selectedItem: any;
+ selection: any;
// Grafana tab
grafanaId: any;
}
ngOnChanges() {
- this.selectedItem = this.selection.first();
- if (!this.selectedItem) {
+ if (!this.selection) {
this.unsubscribeInterval();
return;
}
- if (this.selectedItem.id !== this.id) {
- this.setupSelected(this.selectedItem.id, this.selectedItem.mdsmap.info);
+ if (this.selection.id !== this.id) {
+ this.setupSelected(this.selection.id, this.selection.mdsmap.info);
}
}
-<tabset *ngIf="selection?.hasSingleSelection">
+<tabset *ngIf="selection">
<tab i18n-heading
heading="Details">
<table class="table table-striped table-bordered">
<tr>
<td i18n
class="bold w-25">Name</td>
- <td class="w-75">{{ selectedItem.name }}</td>
+ <td class="w-75">{{ selection.name }}</td>
</tr>
<tr>
<td i18n
class="bold">Description</td>
- <td>{{ selectedItem.desc }}</td>
+ <td>{{ selection.desc }}</td>
</tr>
<tr>
<td i18n
class="bold">Long description</td>
- <td>{{ selectedItem.long_desc }}</td>
+ <td>{{ selection.long_desc }}</td>
</tr>
<tr>
<td i18n
class="bold">Current values</td>
<td>
- <span *ngFor="let conf of selectedItem.value; last as isLast">
+ <span *ngFor="let conf of selection.value; last as isLast">
{{ conf.section }}: {{ conf.value }}{{ !isLast ? "," : "" }}<br />
</span>
</td>
<tr>
<td i18n
class="bold">Default</td>
- <td>{{ selectedItem.default }}</td>
+ <td>{{ selection.default }}</td>
</tr>
<tr>
<td i18n
class="bold">Daemon default</td>
- <td>{{ selectedItem.daemon_default }}</td>
+ <td>{{ selection.daemon_default }}</td>
</tr>
<tr>
<td i18n
class="bold">Type</td>
- <td>{{ selectedItem.type }}</td>
+ <td>{{ selection.type }}</td>
</tr>
<tr>
<td i18n
class="bold">Min</td>
- <td>{{ selectedItem.min }}</td>
+ <td>{{ selection.min }}</td>
</tr>
<tr>
<td i18n
class="bold">Max</td>
- <td>{{ selectedItem.max }}</td>
+ <td>{{ selection.max }}</td>
</tr>
<tr>
<td i18n
class="bold">Flags</td>
<td>
- <span *ngFor="let flag of selectedItem.flags">
+ <span *ngFor="let flag of selection.flags">
<span title="{{ flags[flag] }}">
<span class="badge badge-dark mr-2">{{ flag | uppercase }}</span>
</span>
<td i18n
class="bold">Services</td>
<td>
- <span *ngFor="let service of selectedItem.services">
+ <span *ngFor="let service of selection.services">
<span class="badge badge-dark mr-2">{{ service }}</span>
</span>
</td>
<tr>
<td i18n
class="bold">Source</td>
- <td>{{ selectedItem.source }}</td>
+ <td>{{ selection.source }}</td>
</tr>
<tr>
<td i18n
class="bold">Level</td>
- <td>{{ selectedItem.level }}</td>
+ <td>{{ selection.level }}</td>
</tr>
<tr>
<td i18n
class="bold">Can be updated at runtime (editable)</td>
- <td>{{ selectedItem.can_update_at_runtime | booleanText }}</td>
+ <td>{{ selection.can_update_at_runtime | booleanText }}</td>
</tr>
<tr>
<td i18n
class="bold">Tags</td>
- <td>{{ selectedItem.tags }}</td>
+ <td>{{ selection.tags }}</td>
</tr>
<tr>
<td i18n
class="bold">Enum values</td>
- <td>{{ selectedItem.enum_values }}</td>
+ <td>{{ selection.enum_values }}</td>
</tr>
<tr>
<td i18n
class="bold">See also</td>
- <td>{{ selectedItem.see_also }}</td>
+ <td>{{ selection.see_also }}</td>
</tr>
</tbody>
</table>
import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
-
@Component({
selector: 'cd-configuration-details',
templateUrl: './configuration-details.component.html',
})
export class ConfigurationDetailsComponent implements OnChanges {
@Input()
- selection: CdTableSelection;
- selectedItem: any;
+ selection: any;
flags = {
runtime: this.i18n('The value can be updated at runtime.'),
no_mon_update: this.i18n(`Daemons/clients do not pull this value from the
constructor(private i18n: I18n) {}
ngOnChanges() {
- if (this.selection.hasSelection) {
- this.selectedItem = this.selection.first();
- this.selectedItem.services = _.split(this.selectedItem.services, ',');
+ if (this.selection) {
+ this.selection.services = _.split(this.selection.services, ',');
}
}
}
[columns]="columns"
[extraFilterableColumns]="filters"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)">
<cd-table-actions class="table-actions"
[permission]="permission"
[tableActions]="tableActions">
</cd-table-actions>
<cd-configuration-details cdTableDetail
- [selection]="selection">
+ [selection]="expandedRow">
</cd-configuration-details>
</cd-table>
import { I18n } from '@ngx-translate/i18n-polyfill';
import { ConfigurationService } from '../../../shared/api/configuration.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
import { CellTemplate } from '../../../shared/enum/cell-template.enum';
import { Icons } from '../../../shared/enum/icons.enum';
templateUrl: './configuration.component.html',
styleUrls: ['./configuration.component.scss']
})
-export class ConfigurationComponent implements OnInit {
+export class ConfigurationComponent extends ListWithDetails implements OnInit {
permission: Permission;
tableActions: CdTableAction[];
data: any[] = [];
private i18n: I18n,
public actionLabels: ActionLabelsI18n
) {
+ super();
this.permission = this.authStorageService.getPermissions().configOpt;
const getConfigOptUri = () =>
this.selection.first() && `${encodeURIComponent(this.selection.first().name)}`;
-<tabset *ngIf="selection.hasSingleSelection">
+<tabset *ngIf="selection">
<tab i18n-heading
heading="Devices">
- <cd-device-list [hostname]="selection.first()['hostname']"></cd-device-list>
+ <cd-device-list [hostname]="selection['hostname']"></cd-device-list>
</tab>
<tab i18n-heading
heading="Inventory"
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
import { CoreModule } from '../../../../core/core.module';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { Permissions } from '../../../../shared/models/permissions';
import { SharedModule } from '../../../../shared/shared.module';
import { CephModule } from '../../../ceph.module';
beforeEach(() => {
fixture = TestBed.createComponent(HostDetailsComponent);
component = fixture.componentInstance;
- component.selection = new CdTableSelection();
+ component.selection = undefined;
component.permissions = new Permissions({
hosts: ['read'],
grafana: ['read']
describe('Host details tabset', () => {
beforeEach(() => {
- component.selection.selected = [{ hostname: 'localhost' }];
+ component.selection = { hostname: 'localhost' };
fixture.detectChanges();
});
import { TabsetComponent } from 'ngx-bootstrap/tabs';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { Permissions } from '../../../../shared/models/permissions';
@Component({
permissions: Permissions;
@Input()
- selection: CdTableSelection;
+ selection: any;
@ViewChild(TabsetComponent, { static: false })
tabsetChild: TabsetComponent;
get selectedHostname(): string {
- return this.selection.hasSelection ? this.selection.first()['hostname'] : null;
+ return this.selection !== undefined ? this.selection['hostname'] : null;
}
constructor() {}
columnMode="flex"
(fetchData)="getHosts($event)"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)">
<div class="table-actions btn-toolbar">
<cd-table-actions [permission]="permissions.hosts"
</ng-template>
<cd-host-details cdTableDetail
[permissions]="permissions"
- [selection]="selection">
+ [selection]="expandedRow">
</cd-host-details>
</cd-table>
</tab>
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
import { HostService } from '../../../shared/api/host.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
import { Icons } from '../../../shared/enum/icons.enum';
styleUrls: ['./hosts.component.scss'],
providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
})
-export class HostsComponent implements OnInit {
+export class HostsComponent extends ListWithDetails implements OnInit {
permissions: Permissions;
columns: Array<CdTableColumn> = [];
hosts: Array<object> = [];
private router: Router,
private depCheckerService: DepCheckerService
) {
+ super();
this.permissions = this.authStorageService.getPermissions();
this.tableActions = [
{
-<tabset *ngIf="selection.hasSingleSelection">
+<tabset *ngIf="selection">
<tab i18n-heading
heading="Details">
<cd-table-key-value [data]="module_config">
import { TabsModule } from 'ngx-bootstrap/tabs';
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { SharedModule } from '../../../../shared/shared.module';
import { MgrModuleDetailsComponent } from './mgr-module-details.component';
beforeEach(() => {
fixture = TestBed.createComponent(MgrModuleDetailsComponent);
component = fixture.componentInstance;
- component.selection = new CdTableSelection();
+ component.selection = undefined;
fixture.detectChanges();
});
import { Component, Input, OnChanges } from '@angular/core';
import { MgrModuleService } from '../../../../shared/api/mgr-module.service';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
@Component({
selector: 'cd-mgr-module-details',
module_config: any;
@Input()
- selection: CdTableSelection;
+ selection: any;
constructor(private mgrModuleService: MgrModuleService) {}
ngOnChanges() {
- if (this.selection.hasSelection) {
- const selectedItem = this.selection.first();
- this.mgrModuleService.getConfig(selectedItem.name).subscribe((resp: any) => {
+ if (this.selection) {
+ this.mgrModuleService.getConfig(this.selection.name).subscribe((resp: any) => {
this.module_config = resp;
});
}
[columns]="columns"
columnMode="flex"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)"
identifier="module"
(fetchData)="getModuleList($event)">
[tableActions]="tableActions">
</cd-table-actions>
<cd-mgr-module-details cdTableDetail
- [selection]="selection">
+ [selection]="expandedRow">
</cd-mgr-module-details>
</cd-table>
import { timer as observableTimer } from 'rxjs';
import { MgrModuleService } from '../../../../shared/api/mgr-module.service';
+import { ListWithDetails } from '../../../../shared/classes/list-with-details.class';
import { TableComponent } from '../../../../shared/datatable/table/table.component';
import { CellTemplate } from '../../../../shared/enum/cell-template.enum';
import { Icons } from '../../../../shared/enum/icons.enum';
templateUrl: './mgr-module-list.component.html',
styleUrls: ['./mgr-module-list.component.scss']
})
-export class MgrModuleListComponent {
+export class MgrModuleListComponent extends ListWithDetails {
@ViewChild(TableComponent, { static: true })
table: TableComponent;
@BlockUI()
private notificationService: NotificationService,
private i18n: I18n
) {
+ super();
this.permission = this.authStorageService.getPermissions().configOpt;
this.columns = [
{
-<tabset *ngIf="selection.hasSingleSelection"
+<tabset *ngIf="selection"
id="tabset-osd-details">
<tab heading="Devices"
i18n-heading>
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
import { OsdService } from '../../../../shared/api/osd.service';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { SharedModule } from '../../../../shared/shared.module';
import { TablePerformanceCounterComponent } from '../../../performance-counter/table-performance-counter/table-performance-counter.component';
import { DeviceListComponent } from '../../../shared/device-list/device-list.component';
fixture = TestBed.createComponent(OsdDetailsComponent);
component = fixture.componentInstance;
- component.selection = new CdTableSelection();
+ component.selection = undefined;
debugElement = fixture.debugElement;
osdService = debugElement.injector.get(OsdService);
import * as _ from 'lodash';
import { OsdService } from '../../../../shared/api/osd.service';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { Permission } from '../../../../shared/models/permissions';
import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
})
export class OsdDetailsComponent implements OnChanges {
@Input()
- selection: CdTableSelection;
+ selection: any;
osd: {
id?: number;
this.osd = {
loaded: false
};
- if (this.selection.hasSelection) {
- this.osd = this.selection.first();
+ if (this.selection) {
+ this.osd = this.selection;
this.refresh();
}
}
<tab i18n-heading
heading="OSDs List">
- <cd-table [data]="osds"
+ <cd-table [autoReload]="false"
+ [data]="osds"
(fetchData)="getOsdList()"
[columns]="columns"
selectionType="multiClick"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)"
[updateSelectionOnRefresh]="'never'">
</div>
<cd-osd-details cdTableDetail
- [selection]="selection">
+ [selection]="expandedRow">
</cd-osd-details>
</cd-table>
fixture.detectChanges();
expect(
component.columns
- .filter((column) => !column.checkboxable)
+ .filter((column) => !(column.prop === undefined))
.every((column) => Boolean(column.prop))
).toBeTruthy();
});
import { forkJoin as observableForkJoin, Observable } from 'rxjs';
import { OsdService } from '../../../../shared/api/osd.service';
+import { ListWithDetails } from '../../../../shared/classes/list-with-details.class';
import { ConfirmationModalComponent } from '../../../../shared/components/confirmation-modal/confirmation-modal.component';
import { CriticalConfirmationModalComponent } from '../../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { FormModalComponent } from '../../../../shared/components/form-modal/form-modal.component';
styleUrls: ['./osd-list.component.scss'],
providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
})
-export class OsdListComponent implements OnInit {
+export class OsdListComponent extends ListWithDetails implements OnInit {
@ViewChild('osdUsageTpl', { static: true })
osdUsageTpl: TemplateRef<any>;
@ViewChild('markOsdConfirmationTpl', { static: true })
public actionLabels: ActionLabelsI18n,
public notificationService: NotificationService
) {
+ super();
this.permissions = this.authStorageService.getPermissions();
this.tableActions = [
{
[forceIdentifier]="true"
[customCss]="customCss"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)">
<cd-table-actions class="table-actions"
[permission]="permission"
[selection]="selection"
[tableActions]="tableActions">
</cd-table-actions>
- <tabset cdTableDetail
- *ngIf="selection.hasSingleSelection">
- <tab i18n-heading
- heading="Details">
- <cd-table-key-value [renderObjects]="true"
- [hideEmpty]="true"
- [appendParentKey]="false"
- [data]="selection.first()"
- [customCss]="customCss"
- [autoReload]="false">
- </cd-table-key-value>
- </tab>
- </tabset>
+
+ <cd-table-key-value cdTableDetail
+ *ngIf="expandedRow"
+ [renderObjects]="true"
+ [hideEmpty]="true"
+ [appendParentKey]="false"
+ [data]="expandedRow"
+ [customCss]="customCss"
+ [autoReload]="false">
+ </cd-table-key-value>
</cd-table>
<ng-template #externalLinkTpl
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { I18n } from '@ngx-translate/i18n-polyfill';
+import { ListWithDetails } from '../../../../shared/classes/list-with-details.class';
import { CellTemplate } from '../../../../shared/enum/cell-template.enum';
import { Icons } from '../../../../shared/enum/icons.enum';
import { CdTableAction } from '../../../../shared/models/cd-table-action';
templateUrl: './active-alert-list.component.html',
styleUrls: ['./active-alert-list.component.scss']
})
-export class ActiveAlertListComponent implements OnInit {
+export class ActiveAlertListComponent extends ListWithDetails implements OnInit {
@ViewChild('externalLinkTpl', { static: true })
externalLinkTpl: TemplateRef<any>;
columns: CdTableColumn[];
private i18n: I18n,
private cdDatePipe: CdDatePipe
) {
+ super();
this.permission = this.authStorageService.getPermissions().prometheus;
this.tableActions = [
{
<cd-table [data]="data"
[columns]="columns"
- (updateSelection)="selectionUpdated($event)"
+ [hasDetails]="true"
+ (updateSelection)="setExpandedRow($event)"
[selectionType]="'single'">
<tabset cdTableDetail
- *ngIf="selectedRule">
+ *ngIf="expandedRow">
<tab i18n-heading
heading="Details">
- <cd-table-key-value [data]="selectedRule"
+ <cd-table-key-value [data]="expandedRow"
[renderObjects]="true"
[hideKeys]="hideKeys"></cd-table-key-value>
</tab>
import { I18n } from '@ngx-translate/i18n-polyfill';
+import { ListWithDetails } from '../../../../shared/classes/list-with-details.class';
import { CdTableColumn } from '../../../../shared/models/cd-table-column';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { PrometheusRule } from '../../../../shared/models/prometheus-alerts';
import { DurationPipe } from '../../../../shared/pipes/duration.pipe';
templateUrl: './rules-list.component.html',
styleUrls: ['./rules-list.component.scss']
})
-export class RulesListComponent implements OnInit {
+export class RulesListComponent extends ListWithDetails implements OnInit {
@Input()
data: any;
columns: CdTableColumn[];
- selectedRule: PrometheusRule;
+ expandedRow: PrometheusRule;
/**
* Hide active alerts in details of alerting rules as they are already shown
*/
hideKeys = ['alerts', 'type'];
- constructor(private i18n: I18n) {}
+ constructor(private i18n: I18n) {
+ super();
+ }
ngOnInit() {
this.columns = [
{ prop: 'annotations.description', name: this.i18n('Description') }
];
}
-
- selectionUpdated(selection: CdTableSelection) {
- this.selectedRule = selection.first();
- }
}
[customCss]="customCss"
[sorts]="sorts"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(fetchData)="refresh()"
(updateSelection)="updateSelection($event)">
<cd-table-actions class="table-actions"
[selection]="selection"
[tableActions]="tableActions">
</cd-table-actions>
- <tabset cdTableDetail
- *ngIf="selection.hasSingleSelection">
- <tab i18n-heading
- heading="Details">
- <cd-table-key-value [renderObjects]="true"
- [hideEmpty]="true"
- [appendParentKey]="false"
- [data]="selection.first()"
- [customCss]="customCss"
- [autoReload]="false">
- </cd-table-key-value>
- </tab>
- </tabset>
+ <cd-table-key-value cdTableDetail
+ *ngIf="expandedRow"
+ [renderObjects]="true"
+ [hideEmpty]="true"
+ [appendParentKey]="false"
+ [data]="expandedRow"
+ [customCss]="customCss"
+ [autoReload]="false">
+ </cd-table-key-value>
</cd-table>
import { Observable, Subscriber } from 'rxjs';
import { PrometheusService } from '../../../../shared/api/prometheus.service';
+import { ListWithDetails } from '../../../../shared/classes/list-with-details.class';
import { CriticalConfirmationModalComponent } from '../../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import {
ActionLabelsI18n,
templateUrl: './silence-list.component.html',
styleUrls: ['./silence-list.component.scss']
})
-export class SilenceListComponent {
+export class SilenceListComponent extends ListWithDetails {
silences: AlertmanagerSilence[] = [];
columns: CdTableColumn[];
tableActions: CdTableAction[];
private actionLabels: ActionLabelsI18n,
private succeededLabels: SucceededActionLabelsI18n
) {
+ super();
this.permission = this.authStorageService.getPermissions().prometheus;
const selectionExpired = (selection: CdTableSelection) =>
selection.first() && selection.first().status && selection.first().status.state === 'expired';
-<tabset *ngIf="selection?.hasSingleSelection">
+<tabset *ngIf="selection">
<tab heading="Details"
i18n-heading>
<cd-table-key-value [data]="data">
import { TabsModule } from 'ngx-bootstrap/tabs';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { SharedModule } from '../../../shared/shared.module';
import { NfsDetailsComponent } from './nfs-details.component';
fixture = TestBed.createComponent(NfsDetailsComponent);
component = fixture.componentInstance;
- component.selection = new CdTableSelection();
- component.selection.selected = [
- {
- export_id: 1,
- path: '/qwe',
- fsal: { name: 'CEPH', user_id: 'fs', fs_name: 1 },
- cluster_id: 'cluster1',
- daemons: ['node1', 'node2'],
- pseudo: '/qwe',
- tag: 'asd',
- access_type: 'RW',
- squash: 'no_root_squash',
- protocols: [3, 4],
- transports: ['TCP', 'UDP'],
- clients: [
- {
- addresses: ['192.168.0.10', '192.168.1.0/8'],
- access_type: 'RW',
- squash: 'root_id_squash'
- }
- ],
- id: 'cluster1:1',
- state: 'LOADING'
- }
- ];
+ component.selection = undefined;
+ component.selection = {
+ export_id: 1,
+ path: '/qwe',
+ fsal: { name: 'CEPH', user_id: 'fs', fs_name: 1 },
+ cluster_id: 'cluster1',
+ daemons: ['node1', 'node2'],
+ pseudo: '/qwe',
+ tag: 'asd',
+ access_type: 'RW',
+ squash: 'no_root_squash',
+ protocols: [3, 4],
+ transports: ['TCP', 'UDP'],
+ clients: [
+ {
+ addresses: ['192.168.0.10', '192.168.1.0/8'],
+ access_type: 'RW',
+ squash: 'root_id_squash'
+ }
+ ],
+ id: 'cluster1:1',
+ state: 'LOADING'
+ };
component.ngOnChanges();
fixture.detectChanges();
});
});
it('should prepare data if RGW', () => {
- const newData = _.assignIn(component.selection.first(), {
+ const newData = _.assignIn(component.selection, {
fsal: {
name: 'RGW',
rgw_user_id: 'rgw_user_id'
}
});
- component.selection.selected = [newData];
+ component.selection = newData;
component.ngOnChanges();
expect(component.data).toEqual({
'Access Type': 'RW',
import { Component, Input, OnChanges } from '@angular/core';
import { I18n } from '@ngx-translate/i18n-polyfill';
-import * as _ from 'lodash';
import { CdTableColumn } from '../../../shared/models/cd-table-column';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
@Component({
selector: 'cd-nfs-details',
})
export class NfsDetailsComponent implements OnChanges {
@Input()
- selection: CdTableSelection;
+ selection: any;
selectedItem: any;
data: any;
}
ngOnChanges() {
- if (this.selection.hasSelection) {
- this.selectedItem = this.selection.first();
+ if (this.selection) {
+ this.selectedItem = this.selection;
this.clients = this.selectedItem.clients;
identifier="id"
forceIdentifier="true"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)">
<div class="table-actions btn-toolbar">
<cd-table-actions class="btn-group"
</div>
<cd-nfs-details cdTableDetail
- [selection]="selection">
+ [selection]="expandedRow">
</cd-nfs-details>
</cd-table>
import { Subscription } from 'rxjs';
import { NfsService } from '../../../shared/api/nfs.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
import { TableComponent } from '../../../shared/datatable/table/table.component';
styleUrls: ['./nfs-list.component.scss'],
providers: [TaskListService]
})
-export class NfsListComponent implements OnInit, OnDestroy {
+export class NfsListComponent extends ListWithDetails implements OnInit, OnDestroy {
@ViewChild('nfsState', { static: false })
nfsState: TemplateRef<any>;
@ViewChild('nfsFsal', { static: true })
private taskWrapper: TaskWrapperService,
public actionLabels: ActionLabelsI18n
) {
+ super();
this.permission = this.authStorageService.getPermissions().nfs;
const getNfsUri = () =>
this.selection.first() &&
<tabset #tabsetChild
cdTableDetail
- *ngIf="selection.hasSingleSelection">
+ *ngIf="selection">
<tab i18n-heading
heading="Details">
<cd-table-key-value [renderObjects]="true"
- [data]="filterNonPoolData(selection.first())"
+ [data]="filterNonPoolData(selection)"
[autoReload]="false">
</cd-table-key-value>
</tab>
*ngIf="permissions.grafana.read"
heading="Performance Details">
<cd-grafana [grafanaPath]="'ceph-pool-detail?var-pool_name='
- + selection.first()['pool_name']"
+ + selection['pool_name']"
uid="-xyV8KCiz"
grafanaStyle="one">
</cd-grafana>
</tab>
- <tab *ngIf="selection.first().type === 'replicated'"
+ <tab *ngIf="selection.type === 'replicated'"
i18n-heading
heading="Configuration">
<cd-rbd-configuration-table [data]="selectedPoolConfiguration"></cd-rbd-configuration-table>
</tab>
<tab i18n-heading
- *ngIf="selection.first()['tiers']?.length > 0"
+ *ngIf="selection['tiers']?.length > 0"
heading="Cache Tiers Details">
<cd-table [data]="cacheTiers"
[columns]="cacheTierColumns"
import { TabsetComponent, TabsModule } from 'ngx-bootstrap/tabs';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { Permissions } from '../../../shared/models/permissions';
import { SharedModule } from '../../../shared/shared.module';
import { RbdConfigurationListComponent } from '../../block/rbd-configuration-list/rbd-configuration-list.component';
beforeEach(() => {
fixture = TestBed.createComponent(PoolDetailsComponent);
poolDetailsComponent = fixture.componentInstance;
- poolDetailsComponent.selection = new CdTableSelection();
+ poolDetailsComponent.selection = undefined;
poolDetailsComponent.permissions = new Permissions({
grafana: ['read']
});
describe('Pool details tabset', () => {
beforeEach(() => {
- poolDetailsComponent.selection.selected = [
- {
- tiers: [0],
- pool: 0
- }
- ];
+ poolDetailsComponent.selection = {
+ tiers: [0],
+ pool: 0
+ };
});
it('should recognize a tabset child', () => {
});
it('should not show "Cache Tiers Details" tab if selected pool has no "tiers"', () => {
- poolDetailsComponent.selection.selected = [
- {
- tiers: []
- }
- ];
+ poolDetailsComponent.selection = {
+ tiers: []
+ };
fixture.detectChanges();
const tabs = poolDetailsComponent.tabsetChild.tabs;
expect(tabs.length).toEqual(2);
import { PoolService } from '../../../shared/api/pool.service';
import { CdTableColumn } from '../../../shared/models/cd-table-column';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { RbdConfigurationEntry } from '../../../shared/models/configuration';
import { Permissions } from '../../../shared/models/permissions';
cacheTierColumns: Array<CdTableColumn> = [];
@Input()
- selection: CdTableSelection;
+ selection: any;
@Input()
permissions: Permissions;
@Input()
}
ngOnChanges() {
- if (this.selection.hasSingleSelection) {
- this.poolService.getConfiguration(this.selection.first().pool_name).subscribe((poolConf) => {
+ if (this.selection) {
+ this.poolService.getConfiguration(this.selection.pool_name).subscribe((poolConf) => {
this.selectedPoolConfiguration = poolConf;
});
}
[data]="pools"
[columns]="columns"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)">
<cd-table-actions id="pool-list-actions"
class="table-actions"
</cd-table-actions>
<cd-pool-details cdTableDetail
id="pool-list-details"
- [selection]="selection"
+ [selection]="expandedRow"
[permissions]="permissions"
- [cacheTiers]="selectionCacheTiers">
+ [cacheTiers]="cacheTiers">
</cd-pool-details>
</cd-table>
});
it('should have columns that are sortable', () => {
- expect(component.columns.every((column) => Boolean(column.prop))).toBeTruthy();
+ expect(
+ component.columns
+ .filter((column) => !(column.prop === undefined))
+ .every((column) => Boolean(column.prop))
+ ).toBeTruthy();
});
describe('monAllowPoolDelete', () => {
describe('getSelectionTiers', () => {
const setSelectionTiers = (tiers: number[]) => {
- component.selection.selected = [{ tiers }];
+ component.expandedRow = { tiers };
component.getSelectionTiers();
};
it('should select multiple existing cache tiers', () => {
setSelectionTiers([0, 1, 2]);
- expect(component.selectionCacheTiers).toEqual(getPoolList());
+ expect(component.cacheTiers).toEqual(getPoolList());
});
it('should select correct existing cache tier', () => {
setSelectionTiers([0]);
- expect(component.selectionCacheTiers).toEqual([createPool('a', 0)]);
+ expect(component.cacheTiers).toEqual([createPool('a', 0)]);
});
it('should not select cache tier if id is invalid', () => {
setSelectionTiers([-1]);
- expect(component.selectionCacheTiers).toEqual([]);
+ expect(component.cacheTiers).toEqual([]);
});
it('should not select cache tier if empty', () => {
setSelectionTiers([]);
- expect(component.selectionCacheTiers).toEqual([]);
+ expect(component.cacheTiers).toEqual([]);
});
it('should be able to selected one pool with multiple tiers, than with a single tier, than with no tiers', () => {
setSelectionTiers([0, 1, 2]);
- expect(component.selectionCacheTiers).toEqual(getPoolList());
+ expect(component.cacheTiers).toEqual(getPoolList());
setSelectionTiers([0]);
- expect(component.selectionCacheTiers).toEqual([createPool('a', 0)]);
+ expect(component.cacheTiers).toEqual([createPool('a', 0)]);
setSelectionTiers([]);
- expect(component.selectionCacheTiers).toEqual([]);
+ expect(component.cacheTiers).toEqual([]);
});
});
import { ConfigurationService } from '../../../shared/api/configuration.service';
import { PoolService } from '../../../shared/api/pool.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { ActionLabelsI18n, URLVerbs } from '../../../shared/constants/app.constants';
import { TableComponent } from '../../../shared/datatable/table/table.component';
],
styleUrls: ['./pool-list.component.scss']
})
-export class PoolListComponent implements OnInit {
+export class PoolListComponent extends ListWithDetails implements OnInit {
@ViewChild(TableComponent, { static: true })
table: TableComponent;
@ViewChild('poolUsageTpl', { static: true })
permissions: Permissions;
tableActions: CdTableAction[];
viewCacheStatusList: any[];
- selectionCacheTiers: any[] = [];
+ cacheTiers: any[] = [];
monAllowPoolDelete = false;
constructor(
private configurationService: ConfigurationService,
public actionLabels: ActionLabelsI18n
) {
+ super();
this.permissions = this.authStorageService.getPermissions();
this.tableActions = [
{
updateSelection(selection: CdTableSelection) {
this.selection = selection;
- this.getSelectionTiers();
}
deletePoolModal() {
}
getSelectionTiers() {
- const cacheTierIds = this.selection.hasSingleSelection
- ? this.selection.first()['tiers'] || []
- : [];
- this.selectionCacheTiers = this.pools.filter((pool) => cacheTierIds.includes(pool.pool));
+ if (typeof this.expandedRow !== 'undefined') {
+ const cacheTierIds = this.expandedRow['tiers'];
+ this.cacheTiers = this.pools.filter((pool) => cacheTierIds.includes(pool.pool));
+ }
}
getDisableDesc(): string | undefined {
return undefined;
}
+
+ setExpandedRow(expandedRow: any) {
+ super.setExpandedRow(expandedRow);
+ this.getSelectionTiers();
+ }
}
-<tabset *ngIf="selection.hasSingleSelection">
+<tabset *ngIf="selection">
<tab i18n-heading
heading="Details">
- <div *ngIf="bucket">
+ <div *ngIf="selection">
<table class="table table-striped table-bordered">
<tbody>
<tr>
<td i18n
class="bold w-25">Name</td>
- <td class="w-75">{{ bucket.bid }}</td>
+ <td class="w-75">{{ selection.bid }}</td>
</tr>
<tr>
<td i18n
class="bold">ID</td>
- <td>{{ bucket.id }}</td>
+ <td>{{ selection.id }}</td>
</tr>
<tr>
<td i18n
class="bold">Owner</td>
- <td>{{ bucket.owner }}</td>
+ <td>{{ selection.owner }}</td>
</tr>
<tr>
<td i18n
class="bold">Index type</td>
- <td>{{ bucket.index_type }}</td>
+ <td>{{ selection.index_type }}</td>
</tr>
<tr>
<td i18n
class="bold">Placement rule</td>
- <td>{{ bucket.placement_rule }}</td>
+ <td>{{ selection.placement_rule }}</td>
</tr>
<tr>
<td i18n
class="bold">Marker</td>
- <td>{{ bucket.marker }}</td>
+ <td>{{ selection.marker }}</td>
</tr>
<tr>
<td i18n
class="bold">Maximum marker</td>
- <td>{{ bucket.max_marker }}</td>
+ <td>{{ selection.max_marker }}</td>
</tr>
<tr>
<td i18n
class="bold">Version</td>
- <td>{{ bucket.ver }}</td>
+ <td>{{ selection.ver }}</td>
</tr>
<tr>
<td i18n
class="bold">Master version</td>
- <td>{{ bucket.master_ver }}</td>
+ <td>{{ selection.master_ver }}</td>
</tr>
<tr>
<td i18n
class="bold">Modification time</td>
- <td>{{ bucket.mtime | cdDate }}</td>
+ <td>{{ selection.mtime | cdDate }}</td>
</tr>
<tr>
<td i18n
class="bold">Zonegroup</td>
- <td>{{ bucket.zonegroup }}</td>
+ <td>{{ selection.zonegroup }}</td>
</tr>
<tr>
<td i18n
class="bold">Versioning</td>
- <td>{{ bucket.versioning }}</td>
+ <td>{{ selection.versioning }}</td>
</tr>
<tr>
<td i18n
class="bold">MFA Delete</td>
- <td>{{ bucket.mfa_delete }}</td>
+ <td>{{ selection.mfa_delete }}</td>
</tr>
</tbody>
</table>
<!-- Bucket quota -->
- <div *ngIf="bucket.bucket_quota">
+ <div *ngIf="selection.bucket_quota">
<legend i18n>Bucket quota</legend>
<table class="table table-striped table-bordered">
<tbody>
<tr>
<td i18n
class="bold w-25">Enabled</td>
- <td class="w-75">{{ bucket.bucket_quota.enabled | booleanText }}</td>
+ <td class="w-75">{{ selection.bucket_quota.enabled | booleanText }}</td>
</tr>
<tr>
<td i18n
class="bold">Maximum size</td>
- <td *ngIf="bucket.bucket_quota.max_size <= -1"
+ <td *ngIf="selection.bucket_quota.max_size <= -1"
i18n>Unlimited</td>
- <td *ngIf="bucket.bucket_quota.max_size > -1">
- {{ bucket.bucket_quota.max_size | dimless }}
+ <td *ngIf="selection.bucket_quota.max_size > -1">
+ {{ selection.bucket_quota.max_size | dimless }}
</td>
</tr>
<tr>
<td i18n
class="bold">Maximum objects</td>
- <td *ngIf="bucket.bucket_quota.max_objects <= -1"
+ <td *ngIf="selection.bucket_quota.max_objects <= -1"
i18n>Unlimited</td>
- <td *ngIf="bucket.bucket_quota.max_objects > -1">
- {{ bucket.bucket_quota.max_objects }}
+ <td *ngIf="selection.bucket_quota.max_objects > -1">
+ {{ selection.bucket_quota.max_objects }}
</td>
</tr>
</tbody>
<tr>
<td i18n
class="bold w-25">Enabled</td>
- <td class="w-75">{{ bucket.lock_enabled | booleanText }}</td>
+ <td class="w-75">{{ selection.lock_enabled | booleanText }}</td>
</tr>
- <ng-container *ngIf="bucket.lock_enabled">
+ <ng-container *ngIf="selection.lock_enabled">
<tr>
<td i18n
class="bold">Mode</td>
- <td>{{ bucket.lock_mode }}</td>
+ <td>{{ selection.lock_mode }}</td>
</tr>
<tr>
<td i18n
class="bold">Days</td>
- <td>{{ bucket.lock_retention_period_days }}</td>
+ <td>{{ selection.lock_retention_period_days }}</td>
</tr>
<tr>
<td i18n
class="bold">Years</td>
- <td>{{ bucket.lock_retention_period_years }}</td>
+ <td>{{ selection.lock_retention_period_years }}</td>
</tr>
</ng-container>
</tbody>
-import { Component, Input, OnChanges } from '@angular/core';
-
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
+import { Component, Input } from '@angular/core';
@Component({
selector: 'cd-rgw-bucket-details',
templateUrl: './rgw-bucket-details.component.html',
styleUrls: ['./rgw-bucket-details.component.scss']
})
-export class RgwBucketDetailsComponent implements OnChanges {
- bucket: any;
-
+export class RgwBucketDetailsComponent {
@Input()
- selection: CdTableSelection;
+ selection: any;
constructor() {}
-
- ngOnChanges() {
- if (this.selection.hasSelection) {
- this.bucket = this.selection.first();
- }
- }
}
[columns]="columns"
columnMode="flex"
selectionType="multiClick"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)"
identifier="bid"
(fetchData)="getBucketList($event)">
[tableActions]="tableActions">
</cd-table-actions>
<cd-rgw-bucket-details cdTableDetail
- [selection]="selection">
+ [selection]="expandedRow">
</cd-rgw-bucket-details>
</cd-table>
import { forkJoin as observableForkJoin, Observable, Subscriber } from 'rxjs';
import { RgwBucketService } from '../../../shared/api/rgw-bucket.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
import { TableComponent } from '../../../shared/datatable/table/table.component';
styleUrls: ['./rgw-bucket-list.component.scss'],
providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
})
-export class RgwBucketListComponent {
+export class RgwBucketListComponent extends ListWithDetails {
@ViewChild(TableComponent, { static: true })
table: TableComponent;
public actionLabels: ActionLabelsI18n,
private ngZone: NgZone
) {
+ super();
this.permission = this.authStorageService.getPermissions().rgw;
this.columns = [
{
-<tabset *ngIf="selection.hasSingleSelection">
+<tabset *ngIf="selection">
<tab i18n-heading
heading="Details">
<cd-table-key-value [data]="metadata"
<tab i18n-heading
*ngIf="grafanaPermission.read"
heading="Performance Details">
- <cd-grafana [grafanaPath]="'rgw-instance-detail?var-rgw_servers=rgw.' + this.selection.first().id"
+ <cd-grafana [grafanaPath]="'rgw-instance-detail?var-rgw_servers=rgw.' + this.serviceId"
uid="x5ARzZtmk"
grafanaStyle="one">
</cd-grafana>
import { TabsModule } from 'ngx-bootstrap/tabs';
import { configureTestBed } from '../../../../testing/unit-test-helper';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { SharedModule } from '../../../shared/shared.module';
import { PerformanceCounterModule } from '../../performance-counter/performance-counter.module';
import { RgwDaemonDetailsComponent } from './rgw-daemon-details.component';
beforeEach(() => {
fixture = TestBed.createComponent(RgwDaemonDetailsComponent);
component = fixture.componentInstance;
- component.selection = new CdTableSelection();
+ component.selection = undefined;
fixture.detectChanges();
});
import * as _ from 'lodash';
import { RgwDaemonService } from '../../../shared/api/rgw-daemon.service';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { Permission } from '../../../shared/models/permissions';
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
grafanaPermission: Permission;
@Input()
- selection: CdTableSelection;
+ selection: any;
constructor(
private rgwDaemonService: RgwDaemonService,
ngOnChanges() {
// Get the service id of the first selected row.
- if (this.selection.hasSelection) {
- this.serviceId = this.selection.first().id;
+ if (this.selection) {
+ this.serviceId = this.selection.id;
}
}
<cd-table [data]="daemons"
[columns]="columns"
columnMode="flex"
- selectionType="single"
- (updateSelection)="updateSelection($event)"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(fetchData)="getDaemonList($event)">
<cd-rgw-daemon-details cdTableDetail
- [selection]="selection">
+ [selection]="expandedRow">
</cd-rgw-daemon-details>
</cd-table>
</tab>
import { I18n } from '@ngx-translate/i18n-polyfill';
import { RgwDaemonService } from '../../../shared/api/rgw-daemon.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { CdTableColumn } from '../../../shared/models/cd-table-column';
import { CdTableFetchDataContext } from '../../../shared/models/cd-table-fetch-data-context';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { Permission } from '../../../shared/models/permissions';
import { CephShortVersionPipe } from '../../../shared/pipes/ceph-short-version.pipe';
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
templateUrl: './rgw-daemon-list.component.html',
styleUrls: ['./rgw-daemon-list.component.scss']
})
-export class RgwDaemonListComponent {
+export class RgwDaemonListComponent extends ListWithDetails {
columns: CdTableColumn[] = [];
daemons: object[] = [];
- selection: CdTableSelection = new CdTableSelection();
grafanaPermission: Permission;
constructor(
cephShortVersionPipe: CephShortVersionPipe,
private i18n: I18n
) {
+ super();
this.grafanaPermission = this.authStorageService.getPermissions().grafana;
this.columns = [
{
}
);
}
-
- updateSelection(selection: CdTableSelection) {
- this.selection = selection;
- }
}
-<tabset *ngIf="selection.hasSingleSelection">
+<tabset *ngIf="selection">
<tab i18n-heading
heading="Details">
<div *ngIf="user">
import { TabsModule } from 'ngx-bootstrap/tabs';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { SharedModule } from '../../../shared/shared.module';
import { RgwUserS3Key } from '../models/rgw-user-s3-key';
import { RgwUserDetailsComponent } from './rgw-user-details.component';
beforeEach(() => {
fixture = TestBed.createComponent(RgwUserDetailsComponent);
component = fixture.componentInstance;
- component.selection = new CdTableSelection();
+ component.selection = {};
fixture.detectChanges();
});
expect(component).toBeTruthy();
const detailsTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Details"]');
- expect(detailsTab).toBeFalsy();
+ expect(detailsTab).toBeTruthy();
const keysTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Keys"]');
expect(keysTab).toBeFalsy();
});
it('should show "Details" tab', () => {
- component.selection.selected = [{ uid: 'myUsername' }];
+ component.selection = { uid: 'myUsername' };
fixture.detectChanges();
const detailsTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Details"]');
it('should show "Keys" tab', () => {
const s3Key = new RgwUserS3Key();
- component.selection.selected = [{ keys: [s3Key] }];
+ component.selection = { keys: [s3Key] };
component.ngOnChanges();
fixture.detectChanges();
});
it('should show correct "System" info', () => {
- component.selection.selected = [
- { uid: '', email: '', system: 'true', keys: [], swift_keys: [] }
- ];
+ component.selection = { uid: '', email: '', system: 'true', keys: [], swift_keys: [] };
+
component.ngOnChanges();
fixture.detectChanges();
expect(detailsTab[6].textContent).toEqual('System');
expect(detailsTab[7].textContent).toEqual('Yes');
- component.selection.selected[0].system = 'false';
+ component.selection.system = 'false';
component.ngOnChanges();
fixture.detectChanges();
public secretKeyTpl: TemplateRef<any>;
@Input()
- selection: CdTableSelection;
+ selection: any;
// Details tab
user: any;
}
ngOnChanges() {
- if (this.selection.hasSelection) {
- this.user = this.selection.first();
+ if (this.selection) {
+ this.user = this.selection;
// Sort subusers and capabilities.
this.user.subusers = _.sortBy(this.user.subusers, 'id');
[columns]="columns"
columnMode="flex"
selectionType="multiClick"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)"
identifier="uid"
(fetchData)="getUserList($event)">
[tableActions]="tableActions">
</cd-table-actions>
<cd-rgw-user-details cdTableDetail
- [selection]="selection">
+ [selection]="expandedRow">
</cd-rgw-user-details>
</cd-table>
import { forkJoin as observableForkJoin, Observable, Subscriber } from 'rxjs';
import { RgwUserService } from '../../../shared/api/rgw-user.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
import { TableComponent } from '../../../shared/datatable/table/table.component';
styleUrls: ['./rgw-user-list.component.scss'],
providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
})
-export class RgwUserListComponent {
+export class RgwUserListComponent extends ListWithDetails {
@ViewChild(TableComponent, { static: true })
table: TableComponent;
permission: Permission;
public actionLabels: ActionLabelsI18n,
private ngZone: NgZone
) {
+ super();
this.permission = this.authStorageService.getPermissions().rgw;
this.columns = [
{
-<tabset *ngIf="selection?.hasSingleSelection">
+<tabset *ngIf="selection">
<tab heading="Details"
i18n-heading>
<cd-table [data]="scopes_permissions"
import { TabsModule } from 'ngx-bootstrap/tabs';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { SharedModule } from '../../../shared/shared.module';
import { RoleDetailsComponent } from './role-details.component';
it('should create scopes permissions [1/2]', () => {
component.scopes = ['log', 'rgw'];
- component.selection = new CdTableSelection([
- {
- description: 'RGW Manager',
- name: 'rgw-manager',
- scopes_permissions: {
- rgw: ['read', 'create', 'update', 'delete']
- },
- system: true
- }
- ]);
+ component.selection = {
+ description: 'RGW Manager',
+ name: 'rgw-manager',
+ scopes_permissions: {
+ rgw: ['read', 'create', 'update', 'delete']
+ },
+ system: true
+ };
expect(component.scopes_permissions.length).toBe(0);
component.ngOnChanges();
expect(component.scopes_permissions).toEqual([
it('should create scopes permissions [2/2]', () => {
component.scopes = ['cephfs', 'log', 'rgw'];
- component.selection = new CdTableSelection([
- {
- description: 'Test',
- name: 'test',
- scopes_permissions: {
- log: ['read', 'update'],
- rgw: ['read', 'create', 'update']
- },
- system: false
- }
- ]);
+ component.selection = {
+ description: 'Test',
+ name: 'test',
+ scopes_permissions: {
+ log: ['read', 'update'],
+ rgw: ['read', 'create', 'update']
+ },
+ system: false
+ };
expect(component.scopes_permissions.length).toBe(0);
component.ngOnChanges();
expect(component.scopes_permissions).toEqual([
import { CellTemplate } from '../../../shared/enum/cell-template.enum';
import { CdTableColumn } from '../../../shared/models/cd-table-column';
-import { CdTableSelection } from '../../../shared/models/cd-table-selection';
@Component({
selector: 'cd-role-details',
})
export class RoleDetailsComponent implements OnChanges, OnInit {
@Input()
- selection: CdTableSelection;
+ selection: any;
@Input()
scopes: Array<string>;
selectedItem: any;
}
ngOnChanges() {
- if (this.selection.hasSelection) {
- this.selectedItem = this.selection.first();
+ if (this.selection) {
+ this.selectedItem = this.selection;
// Build the scopes/permissions data used by the data table.
const scopes_permissions: any[] = [];
_.each(this.scopes, (scope) => {
[columns]="columns"
identifier="name"
selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
(fetchData)="getRoles()"
(updateSelection)="updateSelection($event)">
<cd-table-actions class="table-actions"
[tableActions]="tableActions">
</cd-table-actions>
<cd-role-details cdTableDetail
- [selection]="selection"
+ [selection]="expandedRow"
[scopes]="scopes">
</cd-role-details>
</cd-table>
import { RoleService } from '../../../shared/api/role.service';
import { ScopeService } from '../../../shared/api/scope.service';
+import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { FormModalComponent } from '../../../shared/components/form-modal/form-modal.component';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
styleUrls: ['./role-list.component.scss'],
providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }]
})
-export class RoleListComponent implements OnInit {
+export class RoleListComponent extends ListWithDetails implements OnInit {
permission: Permission;
tableActions: CdTableAction[];
columns: CdTableColumn[];
private urlBuilder: URLBuilderService,
public actionLabels: ActionLabelsI18n
) {
+ super();
this.permission = this.authStorageService.getPermissions().user;
const addAction: CdTableAction = {
permission: 'create',
--- /dev/null
+export class ListWithDetails {
+ expandedRow: any;
+
+ setExpandedRow(expandedRow: any) {
+ this.expandedRow = expandedRow;
+ }
+}
</a>
<ul *dropdownMenu
class="dropdown-menu px-3">
- <li *ngFor="let column of columns">
-
- <div class="custom-control custom-checkbox">
- <input class="custom-control-input"
- type="checkbox"
- (change)="toggleColumn($event)"
- [name]="column.prop"
- [id]="column.prop"
- [checked]="!column.isHidden">
- <label class="custom-control-label"
- [for]="column.prop">{{ column.name }}</label>
- </div>
- </li>
+ <ng-container *ngFor="let column of columns">
+ <li *ngIf="column.name !== ''" >
+ <div class="custom-control custom-checkbox">
+ <input class="custom-control-input"
+ type="checkbox"
+ (change)="toggleColumn($event)"
+ [name]="column.prop"
+ [id]="column.prop"
+ [checked]="!column.isHidden">
+ <label class="custom-control-label"
+ [for]="column.prop">{{ column.name }}</label>
+ </div>
+ </li>
+ </ng-container>
</ul>
</div>
</div>
[loadingIndicator]="loadingIndicator"
[rowIdentity]="rowIdentity()"
[rowHeight]="'auto'">
+
+ <!-- Row Detail Template -->
+ <ngx-datatable-row-detail rowHeight="auto"
+ #detailRow>
+ <ng-template let-row="row"
+ let-expanded="expanded"
+ ngx-datatable-row-detail-template>
+ <!-- Table Details -->
+ <ng-content select="[cdTableDetail]"></ng-content>
+ </ng-template>
+ </ngx-datatable-row-detail>
+
<ngx-datatable-footer>
<ng-template ngx-datatable-footer-template
let-rowCount="rowCount"
</ngx-datatable>
</div>
-<!-- Table Details -->
-<ng-content select="[cdTableDetail]"></ng-content>
-
<!-- cell templates that can be accessed from outside -->
<ng-template #tableCellBoldTpl
let-value="value">
<span data-toggle="tooltip"
[title]="value">{{ value | truncate:column?.customTemplateConfig?.length:column?.customTemplateConfig?.omission }}</span>
</ng-template>
+
+<ng-template #rowDetailsTpl
+ let-row="row"
+ let-isExpanded="expanded"
+ ngx-datatable-cell-template>
+ <a href="javascript:void(0)"
+ [class.expand-collapse-icon-right]="!isExpanded"
+ [class.expand-collapse-icon-down]="isExpanded"
+ class="expand-collapse-icon tc_expand-collapse"
+ title="Expand/Collapse Row"
+ i18n-title
+ (click)="toggleExpandRow(row, isExpanded)">
+ </a>
+</ng-template>
@import 'styles';
+@mixin row-details-icon {
+ font-family: 'ForkAwesome', sans-serif;
+ font-size: 1rem;
+ color: $gray-900;
+ line-height: 1;
+}
+
.dataTables_wrapper {
margin-bottom: 25px;
.separator {
.dropdown-menu {
white-space: nowrap;
- & > li {
- cursor: pointer;
- & > label {
+ & li {
+ cursor: default;
+ & label {
width: 100%;
margin-bottom: 0;
- padding-left: 20px;
- padding-right: 20px;
+ padding-left: 0;
+ padding-right: 0;
cursor: pointer;
&:hover {
background-color: $color-table-dropdown-bg;
}
- & > input {
+ & input {
cursor: pointer;
}
}
}
.datatable-body-cell-label {
display: block;
+ height: 100%;
}
}
}
+ .datatable-row-detail {
+ padding: 20px;
+ border-bottom: 2px solid $color-table-header-border;
+ }
+ .expand-collapse-icon {
+ display: block;
+ height: 100%;
+ text-align: center;
+ &:hover {
+ text-decoration: none;
+ }
+ }
+ .expand-collapse-icon-right:before {
+ @include row-details-icon;
+ content: '\f105';
+ }
+ .expand-collapse-icon-down:before {
+ @include row-details-icon;
+ content: '\f107';
+ }
}
.datatable-footer {
@extend .p-2;
expect(component.useCustomClass('https://secure.it')).toBe('btn secure');
});
});
+
+ describe('test expand and collapse feature', () => {
+ beforeEach(() => {
+ spyOn(component.setExpandedRow, 'emit');
+ component.table = {
+ rowDetail: { collapseAllRows: jest.fn(), toggleExpandRow: jest.fn() }
+ } as any;
+
+ // Setup table
+ component.identifier = 'a';
+ component.data = createFakeData(10);
+
+ // Select item
+ component.expanded = _.clone(component.data[1]);
+ });
+
+ describe('update expanded on refresh', () => {
+ const updateExpendedOnState = (state: 'always' | 'never' | 'onChange') => {
+ component.updateExpandedOnRefresh = state;
+ component.updateExpanded();
+ };
+
+ beforeEach(() => {
+ // Mock change
+ component.data[1].b = 'test';
+ });
+
+ it('refreshes "always"', () => {
+ updateExpendedOnState('always');
+ expect(component.expanded.b).toBe('test');
+ expect(component.setExpandedRow.emit).toHaveBeenCalled();
+ });
+
+ it('refreshes "onChange"', () => {
+ updateExpendedOnState('onChange');
+ expect(component.expanded.b).toBe('test');
+ expect(component.setExpandedRow.emit).toHaveBeenCalled();
+ });
+
+ it('does not refresh "onChange" if data is equal', () => {
+ component.data[1].b = 10; // Reverts change
+ updateExpendedOnState('onChange');
+ expect(component.expanded.b).toBe(10);
+ expect(component.setExpandedRow.emit).not.toHaveBeenCalled();
+ });
+
+ it('"never" refreshes', () => {
+ updateExpendedOnState('never');
+ expect(component.expanded.b).toBe(10);
+ expect(component.setExpandedRow.emit).not.toHaveBeenCalled();
+ });
+ });
+
+ it('should open the table details and close other expanded rows', () => {
+ component.toggleExpandRow(component.expanded, false);
+ expect(component.expanded).toEqual({ a: 1, b: 10, c: true });
+ expect(component.table.rowDetail.collapseAllRows).toHaveBeenCalled();
+ expect(component.setExpandedRow.emit).toHaveBeenCalledWith(component.expanded);
+ expect(component.table.rowDetail.toggleExpandRow).toHaveBeenCalled();
+ });
+
+ it('should close the current table details expansion', () => {
+ component.toggleExpandRow(component.expanded, true);
+ expect(component.expanded).toBeUndefined();
+ expect(component.setExpandedRow.emit).toHaveBeenCalledWith(undefined);
+ expect(component.table.rowDetail.toggleExpandRow).toHaveBeenCalled();
+ });
+ });
});
mapTpl: TemplateRef<any>;
@ViewChild('truncateTpl', { static: true })
truncateTpl: TemplateRef<any>;
+ @ViewChild('rowDetailsTpl', { static: true })
+ rowDetailsTpl: TemplateRef<any>;
// This is the array with the items to be shown.
@Input()
// Page size to show. Set to 0 to show unlimited number of rows.
@Input()
limit? = 10;
+ // Has the row details?
+ @Input()
+ hasDetails = false;
/**
* Auto reload time in ms - per default every 5s
// By default selected item details will be updated on table refresh, if data has changed
@Input()
updateSelectionOnRefresh: 'always' | 'never' | 'onChange' = 'onChange';
+ // By default expanded item details will be updated on table refresh, if data has changed
+ @Input()
+ updateExpandedOnRefresh: 'always' | 'never' | 'onChange' = 'onChange';
@Input()
autoSave = true;
@Output()
updateSelection = new EventEmitter();
+ @Output()
+ setExpandedRow = new EventEmitter();
+
/**
* This should be defined if you need access to the applied column filters.
*
*/
selection = new CdTableSelection();
+ /**
+ * Use this variable to access the expanded row
+ */
+ expanded: any = undefined;
+
tableColumns: CdTableColumn[];
icons = Icons;
cellTemplates: {
this.identifier = this.columns[0].prop + '';
}
}
+
this.initUserConfig();
this.columns.forEach((c) => {
if (c.cellTransformation) {
}
});
+ this.initExpandCollapseColumn(); // If rows have details, add a column to expand or collapse the rows
this.initCheckboxColumn();
this.filterHiddenColumns();
this.initColumnFilters();
}
}
+ /**
+ * Add a column to expand and collapse the table row if it 'hasDetails'
+ */
+ initExpandCollapseColumn() {
+ if (this.hasDetails) {
+ this.columns.unshift({
+ prop: undefined,
+ resizeable: false,
+ sortable: false,
+ draggable: false,
+ isHidden: false,
+ canAutoResize: false,
+ cellClass: 'cd-datatable-expand-collapse',
+ width: 40,
+ cellTemplate: this.rowDetailsTpl
+ });
+ }
+ }
+
filterHiddenColumns() {
this.tableColumns = this.columns.filter((c) => !c.isHidden);
}
this.updateFilter();
this.reset();
this.updateSelected();
+ this.updateExpanded();
}
/**
this.onSelect(this.selection);
}
+ updateExpanded() {
+ if (_.isUndefined(this.expanded) || this.updateExpandedOnRefresh === 'never') {
+ return;
+ }
+
+ const expandedId = this.expanded[this.identifier];
+ const newExpanded = _.find(this.data, (row) => expandedId === row[this.identifier]);
+
+ if (this.updateExpandedOnRefresh === 'onChange' && _.isEqual(this.expanded, newExpanded)) {
+ return;
+ }
+
+ this.expanded = newExpanded;
+ this.setExpandedRow.emit(newExpanded);
+ }
+
onSelect($event: any) {
this.selection.selected = $event['selected'];
this.updateSelection.emit(_.clone(this.selection));
};
};
}
+
+ toggleExpandRow(row: any, isExpanded: boolean) {
+ if (!isExpanded) {
+ // If current row isn't expanded, collapse others
+ this.expanded = row;
+ this.table.rowDetail.collapseAllRows();
+ this.setExpandedRow.emit(row);
+ } else {
+ // If all rows are closed, emit undefined
+ this.expanded = undefined;
+ this.setExpandedRow.emit(undefined);
+ }
+ this.table.rowDetail.toggleExpandRow(row);
+ }
}
@import 'vendor.variables';
+// Bootstrap defaults
+
+$gray-900: #212529 !default;
+
$screen-sm-min: 576px !default;
$screen-md-min: 768px !default;
$screen-lg-min: 992px !default;