});
it('should show the correct text for the tab labels', () => {
- cy.get('#tabset-osd-details > div > tab').then(($tabs) => {
- const tabHeadings = $tabs.map((_i, e) => e.getAttribute('heading')).get();
+ cy.get('#tabset-osd-details > li > a').then(($tabs) => {
+ const tabHeadings = $tabs.map((_i, e) => e.textContent).get();
expect(tabHeadings).to.eql([
'Devices',
index: { url: '#/rgw/daemon', id: 'cd-rgw-daemon-list' }
};
- getTableCell(tableIndex: number) {
+ getTableCell() {
return cy
- .get('.tab-container')
+ .get('.tab-content')
.its(1)
.find('cd-table')
- .its(tableIndex)
+ .should('have.length', 1) // Only 1 table should be renderer
.find('datatable-body-cell');
}
// check details table is visible
// check at least one field is present
- this.getTableCell(0).should('visible').should('contain.text', 'ceph_version');
- // check performance counters table is not currently visible
- this.getTableCell(1).should('not.be.visible');
+ this.getTableCell().should('visible').should('contain.text', 'ceph_version');
// click on performance counters tab and check table is loaded
cy.contains('.nav-link', 'Performance Counters').click();
// check at least one field is present
- this.getTableCell(1).should('be.visible').should('contain.text', 'objecter.op_r');
- // check details table is not currently visible
- this.getTableCell(0).should('not.be.visible');
+ this.getTableCell().should('be.visible').should('contain.text', 'objecter.op_r');
// click on performance details tab
cy.contains('.nav-link', 'Performance Details').click();
-
- // checks the other tabs' content isn't visible
- this.getTableCell(0).should('not.be.visible');
- this.getTableCell(1).should('not.be.visible');
}
}
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"dev": true
},
+ "mem": {
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ },
+ "version": "4.3.0"
+ },
"memory-fs": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
"mimic-fn": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
- "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
- "dev": true
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
},
"mini-css-extract-plugin": {
"version": "0.9.0",
"dependencies": {
"mem": {
"version": "4.3.0"
- },
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
}
}
},
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { AppRoutingModule } from './app-routing.module';
SharedModule,
CephModule,
BsDropdownModule.forRoot(),
- TabsModule.forRoot(),
JwtModule.forRoot({
config: {
tokenGetter: jwtTokenGetter
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule, Routes } from '@angular/router';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { TreeModule } from 'angular-tree-component';
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ModalModule } from 'ngx-bootstrap/modal';
import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { ActionLabels, URLVerbs } from '../../shared/constants/app.constants';
MirroringModule,
FormsModule,
ReactiveFormsModule,
- TabsModule.forRoot(),
+ NgbNavModule,
ProgressbarModule.forRoot(),
BsDropdownModule.forRoot(),
BsDatepickerModule.forRoot(),
-<tabset>
- <tab heading="Overview"
- i18n-heading
- [active]="url === '/block/iscsi/overview'"
- (selectTab)="navigateTo('/block/iscsi/overview')">
- </tab>
- <tab heading="Targets"
- i18n-heading
- [active]="url === '/block/iscsi/targets'"
- (selectTab)="navigateTo('/block/iscsi/targets')">
- </tab>
-</tabset>
+<ul ngbNav
+ #nav="ngbNav"
+ [activeId]="router.url"
+ (navChange)="router.navigate([$event.nextId])"
+ class="nav-tabs">
+ <li ngbNavItem="/block/iscsi/overview">
+ <a ngbNavLink
+ i18n>Overview</a>
+ </li>
+ <li ngbNavItem="/block/iscsi/targets">
+ <a ngbNavLink
+ i18n>Targets</a>
+ </li>
+</ul>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed } from '../../../../testing/unit-test-helper';
import { SharedModule } from '../../../shared/shared.module';
let fixture: ComponentFixture<IscsiTabsComponent>;
configureTestBed({
- imports: [SharedModule, TabsModule.forRoot(), RouterTestingModule],
+ imports: [SharedModule, RouterTestingModule, NgbNavModule],
declarations: [IscsiTabsComponent]
});
-import { Component, OnInit } from '@angular/core';
+import { Component } from '@angular/core';
import { Router } from '@angular/router';
templateUrl: './iscsi-tabs.component.html',
styleUrls: ['./iscsi-tabs.component.scss']
})
-export class IscsiTabsComponent implements OnInit {
- url: string;
-
- constructor(private router: Router) {}
-
- ngOnInit() {
- this.url = this.router.url;
- }
-
- navigateTo(url: string) {
- this.router.navigate([url]);
- }
+export class IscsiTabsComponent {
+ constructor(public router: Router) {}
}
const tempData = this.metadata[node.data.cdId] || {};
if (node.data.cdId === 'root') {
- this.columns[2].isHidden = false;
+ this.detailTable?.toggleColumn({ target: { name: 'default', checked: true } });
this.data = _.map(this.settings.target_default_controls, (value, key) => {
value = this.format(value);
return {
});
}
} else if (node.data.cdId.toString().startsWith('disk_')) {
- this.columns[2].isHidden = false;
+ this.detailTable?.toggleColumn({ target: { name: 'default', checked: true } });
this.data = _.map(this.settings.disk_default_controls[tempData.backstore], (value, key) => {
value = this.format(value);
return {
}
});
} else {
- this.columns[2].isHidden = true;
+ this.detailTable?.toggleColumn({ target: { name: 'default', checked: false } });
this.data = _.map(tempData, (value, key) => {
return {
displayName: key,
this.data = undefined;
}
- if (this.detailTable) {
- this.detailTable.updateColumns();
- }
+ this.detailTable?.updateColumns();
}
onUpdateData() {
-::ng-deep tabset.tabset > ul {
- border-bottom: 1px solid #ddd;
- float: left;
- display: block;
- margin-right: 20px;
- border-bottom: 0;
- border-right: 1px solid #ddd;
- padding-right: 15px;
-}
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { TreeModule } from 'angular-tree-component';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { BehaviorSubject, of } from 'rxjs';
HttpClientTestingModule,
RouterTestingModule,
SharedModule,
- TabsModule.forRoot(),
TreeModule,
- ToastrModule.forRoot()
+ ToastrModule.forRoot(),
+ NgbNavModule
],
declarations: [IscsiTargetListComponent, IscsiTabsComponent, IscsiTargetDetailsComponent],
providers: [TaskListService, i18nProviders]
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
import { SharedModule } from '../../../../shared/shared.module';
BrowserAnimationsModule,
SharedModule,
BsDropdownModule.forRoot(),
- TabsModule.forRoot(),
ProgressbarModule.forRoot(),
HttpClientTestingModule
],
-<tabset #tabset>
- <tab heading="Issues"
- i18n-heading>
- <cd-table *ngIf="tabset.tabs[0]?.active"
- [data]="image_error.data"
- columnMode="flex"
- [columns]="image_error.columns"
- [autoReload]="-1"
- (fetchData)="refresh()">
- </cd-table>
- </tab>
- <tab heading="Syncing"
- i18n-heading>
- <cd-table *ngIf="tabset.tabs[1]?.active"
- [data]="image_syncing.data"
- columnMode="flex"
- [columns]="image_syncing.columns"
- [autoReload]="-1"
- (fetchData)="refresh()">
- </cd-table>
- </tab>
- <tab heading="Ready"
- i18n-heading>
- <cd-table *ngIf="tabset.tabs[2]?.active"
- [data]="image_ready.data"
- columnMode="flex"
- [columns]="image_ready.columns"
- [autoReload]="-1"
- (fetchData)="refresh()">
- </cd-table>
- </tab>
-</tabset>
+<ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Issues</a>
+ <ng-template ngbNavContent>
+ <cd-table [data]="image_error.data"
+ columnMode="flex"
+ [columns]="image_error.columns"
+ [autoReload]="-1"
+ (fetchData)="refresh()">
+ </cd-table>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Syncing</a>
+ <ng-template ngbNavContent>
+ <cd-table [data]="image_syncing.data"
+ columnMode="flex"
+ [columns]="image_syncing.columns"
+ [autoReload]="-1"
+ (fetchData)="refresh()">
+ </cd-table>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Ready</a>
+ <ng-template ngbNavContent>
+ <cd-table [data]="image_ready.data"
+ columnMode="flex"
+ [columns]="image_ready.columns"
+ [autoReload]="-1"
+ (fetchData)="refresh()">
+ </cd-table>
+ </ng-template>
+ </li>
+</ul>
+
+<div [ngbNavOutlet]="nav"></div>
<ng-template #stateTmpl
let-row="row"
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
import { SharedModule } from '../../../../shared/shared.module';
BrowserAnimationsModule,
SharedModule,
BsDropdownModule.forRoot(),
- TabsModule.forRoot(),
+ NgbNavModule,
ProgressbarModule.forRoot(),
HttpClientTestingModule
],
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
import { AlertModule } from 'ngx-bootstrap/alert';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ModalModule } from 'ngx-bootstrap/modal';
import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { SharedModule } from '../../../shared/shared.module';
],
imports: [
CommonModule,
- TabsModule.forRoot(),
SharedModule,
+ NgbNavModule,
RouterModule,
FormsModule,
ReactiveFormsModule,
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
BrowserAnimationsModule,
SharedModule,
BsDropdownModule.forRoot(),
- TabsModule.forRoot(),
+ NgbNavModule,
ProgressbarModule.forRoot(),
HttpClientTestingModule,
RouterTestingModule,
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
BrowserAnimationsModule,
SharedModule,
BsDropdownModule.forRoot(),
- TabsModule.forRoot(),
ProgressbarModule.forRoot(),
HttpClientTestingModule,
RouterTestingModule,
<ng-container i18n>Only available for RBD images with <strong>fast-diff</strong> enabled</ng-container>
</ng-template>
-<tabset *ngIf="selection">
- <tab i18n-heading
- heading="Details">
- <table class="table table-striped table-bordered">
- <tbody>
- <tr>
- <td i18n
- class="bold w-25">Name</td>
- <td class="w-75">{{ selection.name }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Pool</td>
- <td>{{ selection.pool_name }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Data Pool</td>
- <td>{{ selection.data_pool | empty }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Created</td>
- <td>{{ selection.timestamp | cdDate }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Size</td>
- <td>{{ selection.size | dimlessBinary }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Objects</td>
- <td>{{ selection.num_objs | dimless }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Object size</td>
- <td>{{ selection.obj_size | dimlessBinary }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Features</td>
- <td>
- <span *ngFor="let feature of selection.features_name">
- <span class="badge badge-dark mr-2">{{ feature }}</span>
- </span>
- </td>
- </tr>
- <tr>
- <td i18n
- class="bold">Provisioned</td>
- <td>
- <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="selection.features_name?.indexOf('fast-diff') !== -1">
- {{ selection.disk_usage | dimlessBinary }}
- </span>
- </td>
- </tr>
- <tr>
- <td i18n
- class="bold">Total provisioned</td>
- <td>
- <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="selection.features_name?.indexOf('fast-diff') !== -1">
- {{ selection.total_disk_usage | dimlessBinary }}
- </span>
- </td>
- </tr>
- <tr>
- <td i18n
- class="bold">Striping unit</td>
- <td>{{ selection.stripe_unit | dimlessBinary }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Striping count</td>
- <td>{{ selection.stripe_count }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Parent</td>
- <td>
- <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>{{ selection.block_name_prefix }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Order</td>
- <td>{{ selection.order }}</td>
- </tr>
- </tbody>
- </table>
- </tab>
- <tab i18n-heading
- heading="Snapshots">
- <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]="selection['configuration']"></cd-rbd-configuration-table>
- </tab>
-</tabset>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <table class="table table-striped table-bordered">
+ <tbody>
+ <tr>
+ <td i18n
+ class="bold w-25">Name</td>
+ <td class="w-75">{{ selection.name }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Pool</td>
+ <td>{{ selection.pool_name }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Data Pool</td>
+ <td>{{ selection.data_pool | empty }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Created</td>
+ <td>{{ selection.timestamp | cdDate }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Size</td>
+ <td>{{ selection.size | dimlessBinary }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Objects</td>
+ <td>{{ selection.num_objs | dimless }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Object size</td>
+ <td>{{ selection.obj_size | dimlessBinary }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Features</td>
+ <td>
+ <span *ngFor="let feature of selection.features_name">
+ <span class="badge badge-dark mr-2">{{ feature }}</span>
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Provisioned</td>
+ <td>
+ <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="selection.features_name?.indexOf('fast-diff') !== -1">
+ {{ selection.disk_usage | dimlessBinary }}
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Total provisioned</td>
+ <td>
+ <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="selection.features_name?.indexOf('fast-diff') !== -1">
+ {{ selection.total_disk_usage | dimlessBinary }}
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Striping unit</td>
+ <td>{{ selection.stripe_unit | dimlessBinary }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Striping count</td>
+ <td>{{ selection.stripe_count }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Parent</td>
+ <td>
+ <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>{{ selection.block_name_prefix }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Order</td>
+ <td>{{ selection.order }}</td>
+ </tr>
+ </tbody>
+ </table>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Snapshots</a>
+ <ng-template ngbNavContent>
+ <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>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Configuration</a>
+ <ng-template ngbNavContent>
+ <cd-rbd-configuration-table [data]="selection['configuration']"></cd-rbd-configuration-table>
+ </ng-template>
+ </li>
+ </ul>
-<ng-template
- #poolConfigurationSourceTpl
- let-row="row"
- let-value="value">
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
+
+<ng-template #poolConfigurationSourceTpl
+ let-row="row"
+ let-value="value">
<ng-container *ngIf="+value; else global">
<strong i18n
i18n-tooltip
tooltip="This is the global value. No value for this option has been set for this image.">Global</span>
</ng-template>
</ng-template>
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { configureTestBed } from '../../../../testing/unit-test-helper';
configureTestBed({
declarations: [RbdDetailsComponent, RbdSnapshotListComponent, RbdConfigurationListComponent],
- imports: [SharedModule, TabsModule.forRoot(), TooltipModule.forRoot(), RouterTestingModule]
+ imports: [SharedModule, TooltipModule.forRoot(), RouterTestingModule, NgbNavModule]
});
beforeEach(() => {
import { Component, Input, TemplateRef, ViewChild } from '@angular/core';
+import { NgbNav } from '@ng-bootstrap/ng-bootstrap';
+
import { RbdFormModel } from '../rbd-form/rbd-form.model';
@Component({
selection: RbdFormModel;
@Input()
images: any;
+
@ViewChild('poolConfigurationSourceTpl', { static: true })
poolConfigurationSourceTpl: TemplateRef<any>;
+ @ViewChild(NgbNav, { static: true })
+ nav: NgbNav;
+
constructor() {}
}
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { AlertModule } from 'ngx-bootstrap/alert';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ModalModule } from 'ngx-bootstrap/modal';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { ToastrModule } from 'ngx-toastr';
import { BehaviorSubject, of } from 'rxjs';
BrowserAnimationsModule,
SharedModule,
BsDropdownModule.forRoot(),
- TabsModule.forRoot(),
+ NgbNavModule,
ModalModule.forRoot(),
TooltipModule.forRoot(),
ToastrModule.forRoot(),
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { ToastrModule } from 'ngx-toastr';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
HttpClientTestingModule,
RouterTestingModule,
ToastrModule.forRoot(),
- TabsModule.forRoot()
+ NgbNavModule
],
providers: [TaskListService, i18nProviders]
});
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
import { SharedModule } from '../../../shared/shared.module';
let fixture: ComponentFixture<RbdPerformanceComponent>;
configureTestBed({
- imports: [HttpClientTestingModule, RouterTestingModule, SharedModule, TabsModule.forRoot()],
+ imports: [HttpClientTestingModule, RouterTestingModule, SharedModule, NgbNavModule],
declarations: [RbdPerformanceComponent, RbdTabsComponent],
providers: i18nProviders
});
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { I18n } from '@ngx-translate/i18n-polyfill';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { Subject, throwError as observableThrowError } from 'rxjs';
HttpClientTestingModule,
PipesModule,
RouterTestingModule,
- TabsModule.forRoot(),
+ NgbNavModule,
ToastrModule.forRoot()
],
providers: [
-<tabset>
- <tab heading="Images"
- i18n-heading
- [active]="url === '/block/rbd'"
- (selectTab)="navigateTo('/block/rbd')">
- </tab>
- <tab heading="Namespaces"
- i18n-heading
- [active]="url === '/block/rbd/namespaces'"
- (selectTab)="navigateTo('/block/rbd/namespaces')">
- </tab>
- <tab heading="Trash"
- i18n-heading
- [active]="url === '/block/rbd/trash'"
- (selectTab)="navigateTo('/block/rbd/trash')">
- </tab>
- <tab heading="Overall Performance"
- i18n-heading
- *ngIf="grafanaPermission.read"
- [active]="url === '/block/rbd/performance'"
- (selectTab)="navigateTo('/block/rbd/performance')">
- </tab>
-</tabset>
+<ul ngbNav
+ #nav="ngbNav"
+ [activeId]="router.url"
+ (navChange)="router.navigate([$event.nextId])"
+ class="nav-tabs">
+ <li ngbNavItem="/block/rbd">
+ <a ngbNavLink
+ i18n>Images</a>
+ </li>
+ <li ngbNavItem="/block/rbd/namespaces">
+ <a ngbNavLink
+ i18n>Namespaces</a>
+ </li>
+ <li ngbNavItem="/block/rbd/trash">
+ <a ngbNavLink
+ i18n>Trash</a>
+ </li>
+ <li ngbNavItem="/block/rbd/performance"
+ *ngIf="grafanaPermission.read">
+ <a ngbNavLink
+ i18n>Overall Performance</a>
+ </li>
+</ul>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed } from '../../../../testing/unit-test-helper';
import { RbdTabsComponent } from './rbd-tabs.component';
let fixture: ComponentFixture<RbdTabsComponent>;
configureTestBed({
- imports: [TabsModule.forRoot(), RouterTestingModule],
+ imports: [RouterTestingModule, NgbNavModule],
declarations: [RbdTabsComponent]
});
-import { Component, OnInit } from '@angular/core';
+import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { Permission } from '../../../shared/models/permissions';
templateUrl: './rbd-tabs.component.html',
styleUrls: ['./rbd-tabs.component.scss']
})
-export class RbdTabsComponent implements OnInit {
+export class RbdTabsComponent {
grafanaPermission: Permission;
url: string;
- constructor(private authStorageService: AuthStorageService, private router: Router) {
+ constructor(private authStorageService: AuthStorageService, public router: Router) {
this.grafanaPermission = this.authStorageService.getPermissions().grafana;
}
-
- ngOnInit() {
- this.url = this.router.url;
- }
-
- navigateTo(url: string) {
- this.router.navigate([url]);
- }
}
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import * as moment from 'moment';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { of } from 'rxjs';
HttpClientTestingModule,
RouterTestingModule,
SharedModule,
- TabsModule.forRoot(),
+ NgbNavModule,
ToastrModule.forRoot()
],
providers: [TaskListService, i18nProviders]
-<tabset *ngIf="selection">
- <tab i18n-heading
- (selectTab)="softRefresh()"
- heading="Details">
- <cd-cephfs-detail [data]="details">
- </cd-cephfs-detail>
- </tab>
- <tab (selectTab)="softRefresh()">
- <ng-template tabHeading>
- <ng-container i18n>Clients</ng-container>
- <span class="badge badge-pill badge-tab ml-1">{{ clients.data.length }}</span>
- </ng-template>
- <cd-cephfs-clients [id]="id"
- [clients]="clients"
- (triggerApiUpdate)="refresh()">
- </cd-cephfs-clients>
- </tab>
- <tab i18n-heading
- (selectTab)="directoriesSelected = true"
- heading="Directories">
- <cd-cephfs-directories *ngIf="directoriesSelected"
- [id]="id">
- </cd-cephfs-directories>
- </tab>
- <tab i18n-heading
- *ngIf="grafanaPermission.read && grafanaId"
- heading="Performance Details">
- <cd-grafana [grafanaPath]="'mds-performance?var-mds_servers=mds.' + grafanaId"
- uid="tbO9LAiZz"
- grafanaStyle="one">
- </cd-grafana>
- </tab>
-</tabset>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ (navChange)="softRefresh()"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <cd-cephfs-detail [data]="details">
+ </cd-cephfs-detail>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink>
+ <ng-container i18n>Clients</ng-container>
+ <span class="badge badge-pill badge-tab ml-1">{{ clients.data.length }}</span>
+ </a>
+ <ng-template ngbNavContent>
+ <cd-cephfs-clients [id]="id"
+ [clients]="clients"
+ (triggerApiUpdate)="refresh()">
+ </cd-cephfs-clients>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Directories</a>
+ <ng-template ngbNavContent>
+ <cd-cephfs-directories *ngIf="directoriesSelected"
+ [id]="id">
+ </cd-cephfs-directories>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Performance Details</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'mds-performance?var-mds_servers=mds.' + grafanaId"
+ uid="tbO9LAiZz"
+ grafanaStyle="one">
+ </cd-grafana>
+ </ng-template>
+ </li>
+ </ul>
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { Component, Input } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { TreeModule } from 'angular-tree-component';
import * as _ from 'lodash';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { of } from 'rxjs';
configureTestBed({
imports: [
SharedModule,
- TabsModule.forRoot(),
+ NgbNavModule,
HttpClientTestingModule,
TreeModule,
ToastrModule.forRoot()
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { TreeModule } from 'angular-tree-component';
import { ChartsModule } from 'ng2-charts';
import { ProgressbarModule } from 'ngx-bootstrap/progressbar';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { AppRoutingModule } from '../../app-routing.module';
import { SharedModule } from '../../shared/shared.module';
ChartsModule,
TreeModule.forRoot(),
ProgressbarModule.forRoot(),
- TabsModule.forRoot()
+ NgbNavModule
],
declarations: [
CephfsDetailComponent,
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
-import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgbNavModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { TreeModule } from 'angular-tree-component';
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
import { AlertModule } from 'ngx-bootstrap/alert';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ModalModule } from 'ngx-bootstrap/modal';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { TimepickerModule } from 'ngx-bootstrap/timepicker';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
imports: [
CommonModule,
PerformanceCounterModule,
- TabsModule.forRoot(),
+ NgbNavModule,
SharedModule,
RouterModule,
FormsModule,
-<tabset *ngIf="selection">
- <tab i18n-heading
- heading="Details">
- <table class="table table-striped table-bordered">
- <tbody>
- <tr>
- <td i18n
- class="bold w-25">Name</td>
- <td class="w-75">{{ selection.name }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Description</td>
- <td>{{ selection.desc }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Long description</td>
- <td>{{ selection.long_desc }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Current values</td>
- <td>
- <span *ngFor="let conf of selection.value; last as isLast">
- {{ conf.section }}: {{ conf.value }}{{ !isLast ? "," : "" }}<br />
- </span>
- </td>
- </tr>
- <tr>
- <td i18n
- class="bold">Default</td>
- <td>{{ selection.default }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Daemon default</td>
- <td>{{ selection.daemon_default }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Type</td>
- <td>{{ selection.type }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Min</td>
- <td>{{ selection.min }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Max</td>
- <td>{{ selection.max }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Flags</td>
- <td>
- <span *ngFor="let flag of selection.flags">
- <span title="{{ flags[flag] }}">
- <span class="badge badge-dark mr-2">{{ flag | uppercase }}</span>
- </span>
- </span>
- </td>
- </tr>
- <tr>
- <td i18n
- class="bold">Services</td>
- <td>
- <span *ngFor="let service of selection.services">
- <span class="badge badge-dark mr-2">{{ service }}</span>
- </span>
- </td>
- </tr>
- <tr>
- <td i18n
- class="bold">Source</td>
- <td>{{ selection.source }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Level</td>
- <td>{{ selection.level }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Can be updated at runtime (editable)</td>
- <td>{{ selection.can_update_at_runtime | booleanText }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Tags</td>
- <td>{{ selection.tags }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Enum values</td>
- <td>{{ selection.enum_values }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">See also</td>
- <td>{{ selection.see_also }}</td>
- </tr>
- </tbody>
- </table>
- </tab>
-</tabset>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <table class="table table-striped table-bordered">
+ <tbody>
+ <tr>
+ <td i18n
+ class="bold w-25">Name</td>
+ <td class="w-75">{{ selection.name }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Description</td>
+ <td>{{ selection.desc }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Long description</td>
+ <td>{{ selection.long_desc }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Current values</td>
+ <td>
+ <span *ngFor="let conf of selection.value; last as isLast">
+ {{ conf.section }}: {{ conf.value }}{{ !isLast ? "," : "" }}<br />
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Default</td>
+ <td>{{ selection.default }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Daemon default</td>
+ <td>{{ selection.daemon_default }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Type</td>
+ <td>{{ selection.type }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Min</td>
+ <td>{{ selection.min }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Max</td>
+ <td>{{ selection.max }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Flags</td>
+ <td>
+ <span *ngFor="let flag of selection.flags">
+ <span title="{{ flags[flag] }}">
+ <span class="badge badge-dark mr-2">{{ flag | uppercase }}</span>
+ </span>
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Services</td>
+ <td>
+ <span *ngFor="let service of selection.services">
+ <span class="badge badge-dark mr-2">{{ service }}</span>
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Source</td>
+ <td>{{ selection.source }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Level</td>
+ <td>{{ selection.level }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Can be updated at runtime (editable)</td>
+ <td>{{ selection.can_update_at_runtime | booleanText }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Tags</td>
+ <td>{{ selection.tags }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Enum values</td>
+ <td>{{ selection.enum_values }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">See also</td>
+ <td>{{ selection.see_also }}</td>
+ </tr>
+ </tbody>
+ </table>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
import { DataTableModule } from '../../../../shared/datatable/datatable.module';
configureTestBed({
declarations: [ConfigurationDetailsComponent],
- imports: [DataTableModule, SharedModule, TabsModule.forRoot()],
+ imports: [DataTableModule, SharedModule, NgbNavModule],
providers: [i18nProviders]
});
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
import { SharedModule } from '../../../shared/shared.module';
BrowserAnimationsModule,
SharedModule,
FormsModule,
- TabsModule.forRoot(),
+ NgbNavModule,
HttpClientTestingModule,
RouterTestingModule
],
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TreeModule } from 'angular-tree-component';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { of } from 'rxjs';
import { configureTestBed } from '../../../../testing/unit-test-helper';
let fixture: ComponentFixture<CrushmapComponent>;
let debugElement: DebugElement;
configureTestBed({
- imports: [HttpClientTestingModule, TreeModule.forRoot(), TabsModule.forRoot(), SharedModule],
+ imports: [HttpClientTestingModule, TreeModule.forRoot(), SharedModule],
declarations: [CrushmapComponent],
providers: [HealthService]
});
-<tabset *ngIf="selection">
- <tab i18n-heading
- heading="Devices">
- <cd-device-list [hostname]="selection['hostname']"></cd-device-list>
- </tab>
- <tab i18n-heading
- heading="Inventory"
- *ngIf="permissions.hosts.read">
- <cd-inventory [hostname]="selectedHostname"></cd-inventory>
- </tab>
- <tab i18n-heading
- heading="Daemons"
- *ngIf="permissions.hosts.read">
- <cd-service-daemon-list [hostname]="selectedHostname">
- </cd-service-daemon-list>
- </tab>
- <tab i18n-heading
- heading="Performance Details"
- *ngIf="permissions.grafana.read">
- <cd-grafana [grafanaPath]="'host-details?var-ceph_hosts=' + selectedHostname"
- uid="rtOg0AiWz"
- grafanaStyle="three">
- </cd-grafana>
- </tab>
- <tab heading="Device health"
- i18n-heading>
- <cd-smart-list *ngIf="selectedHostname; else noHostname"
- [hostname]="selectedHostname"></cd-smart-list>
- </tab>
-</tabset>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Devices</a>
+ <ng-template ngbNavContent>
+ <cd-device-list [hostname]="selection['hostname']"></cd-device-list>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="permissions.hosts.read">
+ <a ngbNavLink
+ i18n>Inventory</a>
+ <ng-template ngbNavContent>
+ <cd-inventory [hostname]="selectedHostname"></cd-inventory>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="permissions.hosts.read">
+ <a ngbNavLink
+ i18n>Daemons</a>
+ <ng-template ngbNavContent>
+ <cd-service-daemon-list [hostname]="selectedHostname">
+ </cd-service-daemon-list>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="permissions.grafana.read">
+ <a ngbNavLink
+ i18n>Performance Details</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'host-details?var-ceph_hosts=' + selectedHostname"
+ uid="rtOg0AiWz"
+ grafanaStyle="three">
+ </cd-grafana>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Device health</a>
+ <ng-template ngbNavContent>
+ <cd-smart-list *ngIf="selectedHostname; else noHostname"
+ [hostname]="selectedHostname"></cd-smart-list>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
<ng-template #noHostname>
<cd-alert-panel type="error"
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
-import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
+import {
+ configureTestBed,
+ i18nProviders,
+ TabHelper
+} from '../../../../../testing/unit-test-helper';
import { CoreModule } from '../../../../core/core.module';
import { Permissions } from '../../../../shared/models/permissions';
import { SharedModule } from '../../../../shared/shared.module';
imports: [
BrowserAnimationsModule,
HttpClientTestingModule,
- TabsModule.forRoot(),
BsDropdownModule.forRoot(),
NgBootstrapFormValidationModule.forRoot(),
RouterTestingModule,
});
it('should recognize a tabset child', () => {
- const tabsetChild = component.tabsetChild;
+ const tabsetChild = TabHelper.getNgbNav(fixture);
expect(tabsetChild).toBeDefined();
});
it('should show tabs', () => {
- expect(component.tabsetChild.tabs.map((t) => t.heading)).toEqual([
+ expect(TabHelper.getTextContents(fixture)).toEqual([
'Devices',
- 'Device health',
'Inventory',
'Daemons',
- 'Performance Details'
+ 'Performance Details',
+ 'Device health'
]);
});
});
-import { Component, Input, ViewChild } from '@angular/core';
-
-import { TabsetComponent } from 'ngx-bootstrap/tabs';
+import { Component, Input } from '@angular/core';
import { Permissions } from '../../../../shared/models/permissions';
@Input()
selection: any;
- @ViewChild(TabsetComponent)
- tabsetChild: TabsetComponent;
-
get selectedHostname(): string {
return this.selection !== undefined ? this.selection['hostname'] : null;
}
-
- constructor() {}
}
-<tabset>
- <tab i18n-heading
- heading="Hosts List">
- <cd-table [data]="hosts"
- [columns]="columns"
- 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"
- [selection]="selection"
- class="btn-group"
- id="host-actions"
- [tableActions]="tableActions">
- </cd-table-actions>
- </div>
- <ng-template #servicesTpl
- let-value="value">
- <span *ngFor="let service of value; last as isLast">
- <a class="service-link"
- [routerLink]="[service.cdLink]"
- [queryParams]="cdParams"
- *ngIf="service.canRead">{{ service.type }}.{{ service.id }}
- </a>
- <span *ngIf="!service.canRead">
- {{ service.type }}.{{ service.id }}
- </span>
- {{ !isLast ? ", " : "" }}
- </span>
- </ng-template>
- <cd-host-details cdTableDetail
- [permissions]="permissions"
- [selection]="expandedRow">
- </cd-host-details>
- </cd-table>
- </tab>
- <tab i18n-heading
- *ngIf="permissions.grafana.read"
- heading="Overall Performance">
- <cd-grafana [grafanaPath]="'host-overview?'"
- uid="y0KGL0iZz"
- grafanaStyle="two">
- </cd-grafana>
- </tab>
-</tabset>
+<ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Hosts List</a>
+ <ng-template ngbNavContent>
+ <cd-table [data]="hosts"
+ [columns]="columns"
+ 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"
+ [selection]="selection"
+ class="btn-group"
+ id="host-actions"
+ [tableActions]="tableActions">
+ </cd-table-actions>
+ </div>
+ <cd-host-details cdTableDetail
+ [permissions]="permissions"
+ [selection]="expandedRow">
+ </cd-host-details>
+ </cd-table>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="permissions.grafana.read">
+ <a ngbNavLink
+ i18n>Overall Performance</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'host-overview?'"
+ uid="y0KGL0iZz"
+ grafanaStyle="two">
+ </cd-grafana>
+ </ng-template>
+ </li>
+</ul>
+
+<div [ngbNavOutlet]="nav"></div>
+
+<ng-template #servicesTpl
+ let-value="value">
+ <span *ngFor="let service of value; last as isLast">
+ <a class="service-link"
+ [routerLink]="[service.cdLink]"
+ [queryParams]="cdParams"
+ *ngIf="service.canRead">{{ service.type }}.{{ service.id }}
+ </a>
+ <span *ngIf="!service.canRead">
+ {{ service.type }}.{{ service.id }}
+ </span>
+ {{ !isLast ? ", " : "" }}
+ </span>
+</ng-template>
import { RouterTestingModule } from '@angular/router/testing';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { of } from 'rxjs';
CephSharedModule,
SharedModule,
HttpClientTestingModule,
- TabsModule.forRoot(),
BsDropdownModule.forRoot(),
RouterTestingModule,
ToastrModule.forRoot(),
<div *ngIf="contentData">
<ng-container *ngTemplateOutlet="logFiltersTpl"></ng-container>
- <tabset>
- <tab i18n-heading
- heading="Cluster Logs">
- <div class="card bg-light mb-3"
- *ngIf="clog">
- <div class="card-body">
- <p *ngFor="let line of clog">
- <span class="timestamp">{{ line.stamp | cdDate }}</span>
- <span class="priority {{ line.priority | logPriority }}">{{ line.priority }}</span>
- <span class="message">{{ line.message }}</span>
- </p>
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Cluster Logs</a>
+ <ng-template ngbNavContent>
+ <div class="card bg-light mb-3"
+ *ngIf="clog">
+ <div class="card-body">
+ <p *ngFor="let line of clog">
+ <span class="timestamp">{{ line.stamp | cdDate }}</span>
+ <span class="priority {{ line.priority | logPriority }}">{{ line.priority }}</span>
+ <span class="message">{{ line.message }}</span>
+ </p>
- <span *ngIf="contentData.clog.length === 0"
- i18n>No entries found</span>
+ <span *ngIf="contentData.clog.length === 0"
+ i18n>No entries found</span>
+ </div>
</div>
- </div>
- </tab>
-
- <tab i18n-heading
- heading="Audit Logs">
- <div class="card bg-light mb-3"
- *ngIf="audit_log">
- <div class="card-body">
- <p *ngFor="let line of audit_log">
- <span class="timestamp">{{ line.stamp | cdDate }}</span>
- <span class="priority {{ line.priority | logPriority }}">{{ line.priority }}</span>
- <span class="message">{{ line.message }}</span>
- </p>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Audit Logs</a>
+ <ng-template ngbNavContent>
+ <div class="card bg-light mb-3"
+ *ngIf="audit_log">
+ <div class="card-body">
+ <p *ngFor="let line of audit_log">
+ <span class="timestamp">{{ line.stamp | cdDate }}</span>
+ <span class="priority {{ line.priority | logPriority }}">{{ line.priority }}</span>
+ <span class="message">{{ line.message }}</span>
+ </p>
- <span *ngIf="contentData.audit_log.length === 0"
- i18n>No entries found</span>
+ <span *ngIf="contentData.audit_log.length === 0"
+ i18n>No entries found</span>
+ </div>
</div>
- </div>
- </tab>
- </tabset>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
</div>
<ng-template #logFiltersTpl>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { TimepickerModule } from 'ngx-bootstrap/timepicker';
import { configureTestBed } from '../../../../testing/unit-test-helper';
configureTestBed({
imports: [
HttpClientTestingModule,
- TabsModule.forRoot(),
+ NgbNavModule,
SharedModule,
BsDatepickerModule.forRoot(),
TimepickerModule.forRoot(),
-<tabset *ngIf="selection">
- <tab i18n-heading
- heading="Details">
- <cd-table-key-value [data]="module_config">
- </cd-table-key-value>
- </tab>
-</tabset>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value [data]="module_config">
+ </cd-table-key-value>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
import { SharedModule } from '../../../../shared/shared.module';
configureTestBed({
declarations: [MgrModuleDetailsComponent],
- imports: [HttpClientTestingModule, SharedModule, TabsModule.forRoot()],
+ imports: [HttpClientTestingModule, SharedModule, NgbNavModule],
providers: [i18nProviders]
});
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { ToastrModule } from 'ngx-toastr';
import { of as observableOf, throwError as observableThrowError } from 'rxjs';
RouterTestingModule,
SharedModule,
HttpClientTestingModule,
- TabsModule.forRoot(),
+ NgbNavModule,
ToastrModule.forRoot()
],
providers: [MgrModuleService, NotificationService, i18nProviders]
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { AppRoutingModule } from '../../../app-routing.module';
import { SharedModule } from '../../../shared/shared.module';
CommonModule,
ReactiveFormsModule,
SharedModule,
- TabsModule.forRoot(),
+ NgbNavModule,
NgBootstrapFormValidationModule
],
declarations: [MgrModuleListComponent, MgrModuleFormComponent, MgrModuleDetailsComponent]
-<tabset *ngIf="selection"
- id="tabset-osd-details">
- <tab heading="Devices"
- i18n-heading>
- <cd-device-list *ngIf="osd.loaded && osd.id !== null"
- [osdId]="osd.id"></cd-device-list>
- </tab>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ id="tabset-osd-details"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Devices</a>
+ <ng-template ngbNavContent>
+ <cd-device-list *ngIf="osd.loaded && osd.id !== null"
+ [osdId]="osd.id"></cd-device-list>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Attributes (OSD map)</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value *ngIf="osd.loaded"
+ [data]="osd.details.osd_map">
+ </cd-table-key-value>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Metadata</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value *ngIf="osd.loaded && osd.details.osd_metadata; else noMetaData"
+ (fetchData)="refresh()"
+ [data]="osd.details.osd_metadata">
+ </cd-table-key-value>
+ <ng-template #noMetaData>
+ <cd-alert-panel type="warning"
+ i18n>Metadata not available</cd-alert-panel>
+ </ng-template>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Device health</a>
+ <ng-template ngbNavContent>
+ <cd-smart-list [osdId]="osd.id"></cd-smart-list>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Performance counter</a>
+ <ng-template ngbNavContent>
+ <cd-table-performance-counter *ngIf="osd.loaded"
+ serviceType="osd"
+ [serviceId]="osd.id">
+ </cd-table-performance-counter>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Histogram</a>
+ <ng-template ngbNavContent>
+ <cd-alert-panel *ngIf="osd.loaded && osd.histogram_failed"
+ type="warning"
+ i18n>Histogram not available: {{ osd.histogram_failed }}</cd-alert-panel>
- <tab heading="Attributes (OSD map)"
- i18n-heading>
- <cd-table-key-value *ngIf="osd.loaded"
- [data]="osd.details.osd_map">
- </cd-table-key-value>
- </tab>
+ <div class="row"
+ *ngIf="osd.loaded && osd.details.histogram">
+ <div class="col-md-6">
+ <h4 i18n>Writes</h4>
+ <cd-osd-performance-histogram [histogram]="osd.details.histogram.osd.op_w_latency_in_bytes_histogram">
+ </cd-osd-performance-histogram>
+ </div>
+ <div class="col-md-6">
+ <h4 i18n>Reads</h4>
+ <cd-osd-performance-histogram [histogram]="osd.details.histogram.osd.op_r_latency_out_bytes_histogram">
+ </cd-osd-performance-histogram>
+ </div>
+ </div>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="grafanaPermission.read">
+ <a ngbNavLink
+ i18n>Performance Details</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'osd-device-details?var-osd=osd.' + osd['id']"
+ uid="CrAHE0iZz"
+ grafanaStyle="GrafanaStyles.two">
+ </cd-grafana>
+ </ng-template>
+ </li>
+ </ul>
- <tab heading="Metadata"
- i18n-heading>
- <cd-table-key-value *ngIf="osd.loaded && osd.details.osd_metadata; else noMetaData"
- (fetchData)="refresh()"
- [data]="osd.details.osd_metadata">
- </cd-table-key-value>
- <ng-template #noMetaData>
- <cd-alert-panel type="warning"
- i18n>Metadata not available</cd-alert-panel>
- </ng-template>
- </tab>
-
- <tab heading="Device health"
- i18n-heading>
- <cd-smart-list [osdId]="osd.id"></cd-smart-list>
- </tab>
-
- <tab heading="Performance counter"
- i18n-heading>
- <cd-table-performance-counter *ngIf="osd.loaded"
- serviceType="osd"
- [serviceId]="osd.id">
- </cd-table-performance-counter>
- </tab>
-
- <tab heading="Histogram"
- i18n-heading>
- <cd-alert-panel *ngIf="osd.loaded && osd.histogram_failed"
- type="warning"
- i18n>Histogram not available: {{ osd.histogram_failed }}</cd-alert-panel>
-
- <div class="row"
- *ngIf="osd.loaded && osd.details.histogram">
- <div class="col-md-6">
- <h4 i18n>Writes</h4>
- <cd-osd-performance-histogram [histogram]="osd.details.histogram.osd.op_w_latency_in_bytes_histogram">
- </cd-osd-performance-histogram>
- </div>
- <div class="col-md-6">
- <h4 i18n>Reads</h4>
- <cd-osd-performance-histogram [histogram]="osd.details.histogram.osd.op_r_latency_out_bytes_histogram">
- </cd-osd-performance-histogram>
- </div>
- </div>
- </tab>
-
- <tab i18n-heading
- *ngIf="grafanaPermission.read"
- heading="Performance Details">
- <cd-grafana [grafanaPath]="'osd-device-details?var-osd=osd.' + osd['id']"
- uid="CrAHE0iZz"
- grafanaStyle="GrafanaStyles.two">
- </cd-grafana>
- </tab>
-
-</tabset>
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { DebugElement } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { of } from 'rxjs';
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
let getDetailsSpy: jasmine.Spy;
configureTestBed({
- imports: [HttpClientTestingModule, TabsModule.forRoot(), SharedModule],
+ imports: [HttpClientTestingModule, NgbNavModule, SharedModule],
declarations: [
OsdDetailsComponent,
DeviceListComponent,
-<tabset>
- <tab i18n-heading
- heading="OSDs List">
+<ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>OSDs List</a>
+ <ng-template ngbNavContent>
+ <cd-table [autoReload]="false"
+ [data]="osds"
+ (fetchData)="getOsdList()"
+ [columns]="columns"
+ selectionType="multiClick"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
+ (updateSelection)="updateSelection($event)"
+ [updateSelectionOnRefresh]="'never'">
- <cd-table [autoReload]="false"
- [data]="osds"
- (fetchData)="getOsdList()"
- [columns]="columns"
- selectionType="multiClick"
- [hasDetails]="true"
- (setExpandedRow)="setExpandedRow($event)"
- (updateSelection)="updateSelection($event)"
- [updateSelectionOnRefresh]="'never'">
+ <div class="table-actions btn-toolbar">
+ <cd-table-actions [permission]="permissions.osd"
+ [selection]="selection"
+ class="btn-group"
+ id="osd-actions"
+ [tableActions]="tableActions">
+ </cd-table-actions>
+ <cd-table-actions [permission]="{read: true}"
+ [selection]="selection"
+ dropDownOnly="Cluster-wide configuration"
+ btnColor="light"
+ class="btn-group"
+ id="cluster-wide-actions"
+ [tableActions]="clusterWideActions">
+ </cd-table-actions>
+ </div>
- <div class="table-actions btn-toolbar">
- <cd-table-actions [permission]="permissions.osd"
- [selection]="selection"
- class="btn-group"
- id="osd-actions"
- [tableActions]="tableActions">
- </cd-table-actions>
- <cd-table-actions [permission]="{read: true}"
- [selection]="selection"
- dropDownOnly="Cluster-wide configuration"
- btnColor="light"
- class="btn-group"
- id="cluster-wide-actions"
- [tableActions]="clusterWideActions">
- </cd-table-actions>
- </div>
-
- <cd-osd-details cdTableDetail
- [selection]="expandedRow">
- </cd-osd-details>
- </cd-table>
+ <cd-osd-details cdTableDetail
+ [selection]="expandedRow">
+ </cd-osd-details>
+ </cd-table>
+ </ng-template>
+ </li>
- <ng-template #osdUsageTpl
- let-row="row">
- <cd-usage-bar [totalBytes]="row.stats.stat_bytes"
- [usedBytes]="row.stats.stat_bytes_used">
- </cd-usage-bar>
+ <li ngbNavItem
+ *ngIf="permissions.grafana.read">
+ <a ngbNavLink
+ i18n>Overall Performance</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'osd-overview?'"
+ uid="lo02I1Aiz"
+ grafanaStyle="three">
+ </cd-grafana>
</ng-template>
- </tab>
- <tab i18n-heading
- *ngIf="permissions.grafana.read"
- heading="Overall Performance">
- <cd-grafana [grafanaPath]="'osd-overview?'"
- uid="lo02I1Aiz"
- grafanaStyle="three">
- </cd-grafana>
- </tab>
-</tabset>
+ </li>
+</ul>
+
+<div [ngbNavOutlet]="nav"></div>
<ng-template #markOsdConfirmationTpl
let-markActionDescription="markActionDescription">
<ng-container i18n><strong>OSD(s) {{ getSelectedOsdIds() | join }}</strong> will be marked
-<strong>{{ markActionDescription }}</strong> if you proceed.</ng-container>
+ <strong>{{ markActionDescription }}</strong> if you proceed.</ng-container>
</ng-template>
<ng-template #criticalConfirmationTpl
<div *ngIf="!safeToPerform"
class="danger">
<cd-alert-panel type="warning"
- i18n>The {selection.hasSingleSelection, select, 1 {OSD is} 0 {OSDs are}} not safe to be {{ actionDescription }}! {{ message }}</cd-alert-panel>
+ i18n>The {selection.hasSingleSelection, select, 1 {OSD is} 0 {OSDs are}} not safe to be
+ {{ actionDescription }}! {{ message }}</cd-alert-panel>
</div>
<ng-container i18n><strong>OSD {{ getSelectedOsdIds() | join }}</strong> will be
-<strong>{{ actionDescription }}</strong> if you proceed.</ng-container>
+ <strong>{{ actionDescription }}</strong> if you proceed.</ng-container>
+</ng-template>
+
+<ng-template #osdUsageTpl
+ let-row="row">
+ <cd-usage-bar [totalBytes]="row.stats.stat_bytes"
+ [usedBytes]="row.stats.stat_bytes_used">
+ </cd-usage-bar>
</ng-template>
import * as _ from 'lodash';
import { BsModalService } from 'ngx-bootstrap/modal';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { EMPTY, of } from 'rxjs';
BrowserAnimationsModule,
HttpClientTestingModule,
PerformanceCounterModule,
- TabsModule.forRoot(),
ToastrModule.forRoot(),
CephModule,
ReactiveFormsModule,
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { ToastrModule } from 'ngx-toastr';
import {
imports: [
BrowserAnimationsModule,
HttpClientTestingModule,
- TabsModule.forRoot(),
+ NgbNavModule,
RouterTestingModule,
ToastrModule.forRoot(),
SharedModule,
-<tabset #tabs>
- <tab id="active-alerts"
- heading="Active Alerts"
- (selectTab)="setFragment($event)"
- i18n-heading>
- <cd-active-alert-list *ngIf="isAlertmanagerConfigured"></cd-active-alert-list>
- <cd-alert-panel type="info"
- i18n
- *ngIf="!isAlertmanagerConfigured">To see all active Prometheus alerts, please
- provide the URL to the API of Prometheus' Alertmanager as described in the
- <a href="{{docsUrl}}"
- target="_blank">documentation</a>.</cd-alert-panel>
- </tab>
- <tab id="all-alerts"
- heading="All Alerts"
- (selectTab)="setFragment($event)"
- i18n-heading>
- <cd-rules-list *ngIf="isPrometheusConfigured"
- [data]="prometheusAlertService.rules"></cd-rules-list>
- <cd-alert-panel type="info"
- *ngIf="!isPrometheusConfigured">To see all configured Prometheus alerts, please provide the URL to
- the API of Prometheus as described in the
- <a href="{{docsUrl}}"
- target="_blank">documentation</a>.</cd-alert-panel>
- </tab>
- <tab id="silences"
- heading="Silences"
- (selectTab)="setFragment($event)"
- i18n-heading>
- <cd-silences-list *ngIf="isAlertmanagerConfigured"></cd-silences-list>
- <cd-alert-panel *ngIf="!isAlertmanagerConfigured"
- type="info"
- i18n>To enable Silences, please provide the URL to the API of the Prometheus' Alertmanager as
- described in the
- <a href="{{docsUrl}}"
- target="_blank">documentation</a>.</cd-alert-panel>
- </tab>
-</tabset>
+<ul ngbNav
+ #nav="ngbNav"
+ [activeId]="route.snapshot.fragment"
+ (navChange)="setFragment($event.nextId)"
+ class="nav-tabs">
+ <li ngbNavItem="active-alerts">
+ <a ngbNavLink
+ i18n>Active Alerts</a>
+ <ng-template ngbNavContent>
+ <cd-active-alert-list *ngIf="isAlertmanagerConfigured"></cd-active-alert-list>
+ <cd-alert-panel type="info"
+ i18n
+ *ngIf="!isAlertmanagerConfigured">To see all active Prometheus alerts, please
+ provide the URL to the API of Prometheus' Alertmanager as described in the
+ <a href="{{docsUrl}}"
+ target="_blank">documentation</a>.</cd-alert-panel>
+ </ng-template>
+ </li>
+ <li ngbNavItem="all-alerts">
+ <a ngbNavLink
+ i18n>All Alerts</a>
+ <ng-template ngbNavContent>
+ <cd-rules-list *ngIf="isPrometheusConfigured"
+ [data]="prometheusAlertService.rules"></cd-rules-list>
+ <cd-alert-panel type="info"
+ *ngIf="!isPrometheusConfigured">To see all configured Prometheus alerts,
+ please provide the URL to
+ the API of Prometheus as described in the
+ <a href="{{docsUrl}}"
+ target="_blank">documentation</a>.</cd-alert-panel>
+ </ng-template>
+ </li>
+ <li ngbNavItem="silences">
+ <a ngbNavLink
+ i18n>Silences</a>
+ <ng-template ngbNavContent>
+ <cd-silences-list *ngIf="isAlertmanagerConfigured"></cd-silences-list>
+ <cd-alert-panel *ngIf="!isAlertmanagerConfigured"
+ type="info"
+ i18n>To enable Silences, please provide the URL to the API of the Prometheus' Alertmanager as
+ described in the
+ <a href="{{docsUrl}}"
+ target="_blank">documentation</a>.</cd-alert-panel>
+ </ng-template>
+ </li>
+</ul>
+
+<div [ngbNavOutlet]="nav"></div>
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
-import { TabDirective, TabsetComponent } from 'ngx-bootstrap/tabs';
-
import { PrometheusService } from '../../../../shared/api/prometheus.service';
import { CephReleaseNamePipe } from '../../../../shared/pipes/ceph-release-name.pipe';
import { PrometheusAlertService } from '../../../../shared/services/prometheus-alert.service';
constructor(
public prometheusAlertService: PrometheusAlertService,
private prometheusService: PrometheusService,
- private route: ActivatedRoute,
+ public route: ActivatedRoute,
private router: Router,
private summaryService: SummaryService,
private cephReleaseNamePipe: CephReleaseNamePipe
) {}
- @ViewChild('tabs', { static: true })
- tabs: TabsetComponent;
-
isPrometheusConfigured = false;
isAlertmanagerConfigured = false;
const releaseName = this.cephReleaseNamePipe.transform(summary.version);
this.docsUrl = `https://docs.ceph.com/docs/${releaseName}/mgr/dashboard/#enabling-prometheus-alerting`;
});
-
- // Activate tab according to given fragment
- if (this.route.snapshot.fragment) {
- const tab = this.tabs.tabs.find(
- (t) => t.elementRef.nativeElement.id === this.route.snapshot.fragment
- );
- if (tab) {
- tab.active = true;
- }
- // Ensure fragment is not removed, so page can always be reloaded with the same tab open.
- this.router.navigate([], { fragment: this.route.snapshot.fragment });
- }
}
- setFragment(element: TabDirective) {
- this.router.navigate([], { fragment: element.id });
+ setFragment(element: string) {
+ this.router.navigate([], { fragment: element });
}
}
[hasDetails]="true"
(updateSelection)="setExpandedRow($event)"
[selectionType]="'single'">
- <tabset cdTableDetail
- *ngIf="expandedRow">
- <tab i18n-heading
- heading="Details">
- <cd-table-key-value [data]="expandedRow"
- [renderObjects]="true"
- [hideKeys]="hideKeys"></cd-table-key-value>
- </tab>
- </tabset>
+ <ng-container cdTableDetail
+ *ngIf="expandedRow">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value [data]="expandedRow"
+ [renderObjects]="true"
+ [hideKeys]="hideKeys"></cd-table-key-value>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+ </ng-container>
</cd-table>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
import { PrometheusService } from '../../../../shared/api/prometheus.service';
configureTestBed({
declarations: [RulesListComponent],
- imports: [HttpClientTestingModule, SharedModule, TabsModule.forRoot(), BrowserAnimationsModule],
+ imports: [HttpClientTestingModule, SharedModule, NgbNavModule, BrowserAnimationsModule],
providers: [PrometheusService, SettingsService, i18nProviders]
});
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap/modal';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { of } from 'rxjs';
BrowserAnimationsModule,
SharedModule,
BsDropdownModule.forRoot(),
- TabsModule.forRoot(),
ModalModule.forRoot(),
ToastrModule.forRoot(),
RouterTestingModule,
-<tabset *ngIf="selection">
- <tab i18n-heading
- heading="Daemons">
- <cd-service-daemon-list [serviceName]="selection['service_name']">
- </cd-service-daemon-list>
- </tab>
-</tabset>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Daemons</a>
+ <ng-template ngbNavContent>
+ <cd-service-daemon-list [serviceName]="selection['service_name']">
+ </cd-service-daemon-list>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
-import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
+import {
+ configureTestBed,
+ i18nProviders,
+ TabHelper
+} from '../../../../../testing/unit-test-helper';
import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { SummaryService } from '../../../../shared/services/summary.service';
import { SharedModule } from '../../../../shared/shared.module';
let fixture: ComponentFixture<ServiceDetailsComponent>;
configureTestBed({
- imports: [HttpClientTestingModule, RouterTestingModule, TabsModule.forRoot(), SharedModule],
+ imports: [HttpClientTestingModule, RouterTestingModule, SharedModule, NgbNavModule],
declarations: [ServiceDetailsComponent, ServiceDaemonListComponent],
providers: [
i18nProviders,
fixture.detectChanges();
});
- it('should recognize a tabset child', () => {
- const tabsetChild = component.tabsetChild;
- expect(tabsetChild).toBeDefined();
+ it('should have a NgbNav', () => {
+ const ngbNav = TabHelper.getNgbNav(fixture);
+ expect(ngbNav).toBeDefined();
});
it('should show tabs', () => {
- expect(component.tabsetChild.tabs.map((t) => t.heading)).toEqual(['Daemons']);
+ const ngbNavItems = TabHelper.getTextContents(fixture);
+ expect(ngbNavItems).toEqual(['Daemons']);
});
});
});
-import { Component, Input, OnInit, ViewChild } from '@angular/core';
-import { TabsetComponent } from 'ngx-bootstrap/tabs';
+import { Component, Input } from '@angular/core';
import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { Permissions } from '../../../../shared/models/permissions';
templateUrl: './service-details.component.html',
styleUrls: ['./service-details.component.scss']
})
-export class ServiceDetailsComponent implements OnInit {
- @ViewChild(TabsetComponent)
- tabsetChild: TabsetComponent;
-
+export class ServiceDetailsComponent {
@Input()
permissions: Permissions;
@Input()
selection: CdTableSelection;
-
- constructor() {}
-
- ngOnInit() {}
}
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { ChartsModule } from 'ng2-charts';
import { PopoverModule } from 'ngx-bootstrap/popover';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { SharedModule } from '../../shared/shared.module';
import { CephSharedModule } from '../shared/ceph-shared.module';
imports: [
CephSharedModule,
CommonModule,
- TabsModule.forRoot(),
+ NgbNavModule,
SharedModule,
ChartsModule,
RouterModule,
<div>
<cd-refresh-selector></cd-refresh-selector>
- <tabset *ngIf="hasGrafana">
- <tab i18n-heading
- heading="Health">
- <cd-health></cd-health>
- </tab>
- <tab i18n-heading
- heading="Statistics">
- </tab>
- </tabset>
+
+ <ng-container *ngIf="hasGrafana">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Health</a>
+ <ng-template ngbNavContent>
+ <cd-health></cd-health>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Statistics</a>
+ <ng-template ngbNavContent>
+ </ng-template>
+ </li>
+
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+ </ng-container>
+
<cd-health *ngIf="!hasGrafana"></cd-health>
</div>
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
+
import { configureTestBed } from '../../../../testing/unit-test-helper';
import { DashboardComponent } from './dashboard.component';
let fixture: ComponentFixture<DashboardComponent>;
configureTestBed({
+ imports: [NgbNavModule],
declarations: [DashboardComponent],
schemas: [NO_ERRORS_SCHEMA]
});
-<tabset *ngIf="selection">
- <tab heading="Details"
- i18n-heading>
- <cd-table-key-value [data]="data">
- </cd-table-key-value>
- </tab>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value [data]="data">
+ </cd-table-key-value>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Clients ({{ clients.length }})</a>
+ <ng-template ngbNavContent>
- <tab heading="Clients ({{ clients.length }})"
- i18n-heading>
- <cd-table #table
- [data]="clients"
- columnMode="flex"
- [columns]="clientsColumns"
- identifier="addresses"
- forceIdentifier="true"
- selectionType="">
- </cd-table>
- </tab>
-</tabset>
+ <cd-table #table
+ [data]="clients"
+ columnMode="flex"
+ [columns]="clientsColumns"
+ identifier="addresses"
+ forceIdentifier="true"
+ selectionType="">
+ </cd-table>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { By } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import * as _ from 'lodash';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
import { SharedModule } from '../../../shared/shared.module';
configureTestBed({
declarations: [NfsDetailsComponent],
- imports: [BrowserAnimationsModule, SharedModule, TabsModule.forRoot(), HttpClientTestingModule],
+ imports: [BrowserAnimationsModule, SharedModule, HttpClientTestingModule, NgbNavModule],
providers: i18nProviders
});
});
it('should have 1 client', () => {
- expect(elem('li.nav-item:nth-of-type(2) span').nativeElement.textContent).toBe('Clients (1)');
+ expect(elem('ul.nav-tabs li:nth-of-type(2) a').nativeElement.textContent).toBe('Clients (1)');
expect(component.clients).toEqual([
{
access_type: 'RW',
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { ToastrModule } from 'ngx-toastr';
import { of } from 'rxjs';
HttpClientTestingModule,
RouterTestingModule,
SharedModule,
- ToastrModule.forRoot(),
- TabsModule.forRoot()
+ NgbNavModule,
+ ToastrModule.forRoot()
],
providers: [TaskListService, i18nProviders]
});
import { ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
-import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgbNavModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { SharedModule } from '../../shared/shared.module';
import { Nfs501Component } from './nfs-501/nfs-501.component';
ReactiveFormsModule,
RouterModule,
SharedModule,
- TabsModule.forRoot(),
+ NgbNavModule,
CommonModule,
NgbTypeaheadModule,
NgBootstrapFormValidationModule
-<tabset #tabsetChild
- cdTableDetail
- *ngIf="selection">
- <tab i18n-heading
- heading="Details">
- <cd-table-key-value [renderObjects]="true"
- [data]="filterNonPoolData(selection)"
- [autoReload]="false">
- </cd-table-key-value>
- </tab>
- <tab i18n-heading
- *ngIf="permissions.grafana.read"
- heading="Performance Details">
- <cd-grafana [grafanaPath]="'ceph-pool-detail?var-pool_name='
- + selection['pool_name']"
- uid="-xyV8KCiz"
- grafanaStyle="one">
- </cd-grafana>
- </tab>
- <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['tiers']?.length > 0"
- heading="Cache Tiers Details">
- <cd-table [data]="cacheTiers"
- [columns]="cacheTierColumns"
- [autoSave]="false"
- columnMode="flex">
- </cd-table>
- </tab>
-</tabset>
+<ng-container *ngIf="selection"
+ cdTableDetail>
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value [renderObjects]="true"
+ [data]="filterNonPoolData(selection)"
+ [autoReload]="false">
+ </cd-table-key-value>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="permissions.grafana.read">
+ <a ngbNavLink
+ i18n>Performance Details</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'ceph-pool-detail?var-pool_name='+ selection['pool_name']"
+ uid="-xyV8KCiz"
+ grafanaStyle="one">
+ </cd-grafana>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="selection.type === 'replicated'">
+ <a ngbNavLink
+ i18n>Configuration</a>
+ <ng-template ngbNavContent>
+ <cd-rbd-configuration-table [data]="selectedPoolConfiguration"></cd-rbd-configuration-table>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="selection['tiers']?.length > 0">
+ <a ngbNavLink
+ i18n>Cache Tiers Details</a>
+ <ng-template ngbNavContent>
+ <cd-table [data]="cacheTiers"
+ [columns]="cacheTierColumns"
+ [autoSave]="false"
+ columnMode="flex">
+ </cd-table>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsetComponent, TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
-import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
+import { configureTestBed, i18nProviders, TabHelper } from '../../../../testing/unit-test-helper';
import { Permissions } from '../../../shared/models/permissions';
import { SharedModule } from '../../../shared/shared.module';
import { RbdConfigurationListComponent } from '../../block/rbd-configuration-list/rbd-configuration-list.component';
configureTestBed({
imports: [
BrowserAnimationsModule,
- TabsModule.forRoot(),
+ NgbNavModule,
SharedModule,
HttpClientTestingModule,
RouterTestingModule
it('should recognize a tabset child', () => {
fixture.detectChanges();
- const tabsetChild: TabsetComponent = poolDetailsComponent.tabsetChild;
- expect(tabsetChild).toBeDefined();
+ const ngbNav = TabHelper.getNgbNav(fixture);
+ expect(ngbNav).toBeDefined();
});
it('should show "Cache Tiers Details" tab if selected pool has "tiers"', () => {
fixture.detectChanges();
- const tabs = poolDetailsComponent.tabsetChild.tabs;
- expect(tabs.length).toBe(3);
- expect(tabs[2].heading).toBe('Cache Tiers Details');
- expect(tabs[0].active).toBeTruthy();
+ const tabsItem = TabHelper.getNgbNavItems(fixture);
+ const tabsText = TabHelper.getTextContents(fixture);
+ expect(tabsItem.length).toBe(3);
+ expect(tabsText[2]).toBe('Cache Tiers Details');
+ expect(tabsItem[0].active).toBeTruthy();
});
it('should not show "Cache Tiers Details" tab if selected pool has no "tiers"', () => {
tiers: []
};
fixture.detectChanges();
- const tabs = poolDetailsComponent.tabsetChild.tabs;
+ const tabs = TabHelper.getNgbNavItems(fixture);
expect(tabs.length).toEqual(2);
expect(tabs[0].active).toBeTruthy();
});
it('current active status of tabs should not change when selection is the same as previous selection', () => {
fixture.detectChanges();
- const tabs = poolDetailsComponent.tabsetChild.tabs;
+ const tabs = TabHelper.getNgbNavItems(fixture);
expect(tabs[0].active).toBeTruthy();
- tabs[1].active = true;
+ const ngbNav = TabHelper.getNgbNav(fixture);
+ ngbNav.select(tabs[1].id);
+
fixture.detectChanges();
expect(tabs[1].active).toBeTruthy();
});
-import { Component, Input, OnChanges, ViewChild } from '@angular/core';
+import { Component, Input, OnChanges } from '@angular/core';
import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
-import { TabsetComponent } from 'ngx-bootstrap/tabs';
import { PoolService } from '../../../shared/api/pool.service';
import { CdTableColumn } from '../../../shared/models/cd-table-column';
permissions: Permissions;
@Input()
cacheTiers: any[];
- @ViewChild(TabsetComponent)
- tabsetChild: TabsetComponent;
selectedPoolConfiguration: RbdConfigurationEntry[];
constructor(private i18n: I18n, private poolService: PoolService) {
<span class="form-text text-muted"
id="ecp-info-block"
*ngIf="data.erasureInfo && form.getValue('erasureProfile')">
- <tabset #ecpInfoTabs>
- <tab i18n-heading
- heading="Profile"
- class="ecp-info">
- <cd-table-key-value [renderObjects]="true"
- [hideKeys]="['name']"
- [data]="form.getValue('erasureProfile')"
- [autoReload]="false">
- </cd-table-key-value>
- </tab>
- <tab i18n-heading
- heading="Used by pools"
- class="used-by-pools">
- <ng-template #ecpIsNotUsed>
- <span i18n>Profile is not in use.</span>
+ <ul ngbNav
+ #ecpInfoTabs="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem="ecp-info">
+ <a ngbNavLink
+ i18n>Profile</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value [renderObjects]="true"
+ [hideKeys]="['name']"
+ [data]="form.getValue('erasureProfile')"
+ [autoReload]="false">
+ </cd-table-key-value>
</ng-template>
- <ul *ngIf="ecpUsage; else ecpIsNotUsed">
- <li *ngFor="let pool of ecpUsage">
- {{ pool }}
- </li>
- </ul>
- </tab>
- </tabset>
+ </li>
+ <li ngbNavItem="used-by-pools">
+ <a ngbNavLink
+ i18n>Used by pools</a>
+ <ng-template ngbNavContent>
+ <ng-template #ecpIsNotUsed>
+ <span i18n>Profile is not in use.</span>
+ </ng-template>
+ <ul *ngIf="ecpUsage; else ecpIsNotUsed">
+ <li *ngFor="let pool of ecpUsage">
+ {{ pool }}
+ </li>
+ </ul>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="ecpInfoTabs"></div>
</span>
</div>
</div>
</button>
</span>
</div>
- <span class="form-text text-muted"
- id="crush-info-block"
- *ngIf="data.crushInfo && form.getValue('crushRule')">
- <tabset #crushInfoTabs>
- <tab i18n-heading
- heading="Crush rule"
- class="crush-rule-info">
- <cd-table-key-value [renderObjects]="false"
- [hideKeys]="['steps', 'ruleset', 'type', 'rule_name']"
- [data]="form.getValue('crushRule')"
- [autoReload]="false">
- </cd-table-key-value>
- </tab>
- <tab i18n-heading
- heading="Crush steps"
- class="crush-rule-steps">
- <ol>
- <li *ngFor="let step of form.get('crushRule').value.steps">
- {{ describeCrushStep(step) }}
- </li>
- </ol>
- </tab>
- <tab i18n-heading
- heading="Used by pools"
- class="used-by-pools">
- <ng-template #ruleIsNotUsed>
- <span i18n>Rule is not in use.</span>
+
+ <div class="form-text text-muted"
+ id="crush-info-block"
+ *ngIf="data.crushInfo && form.getValue('crushRule')">
+ <ul ngbNav
+ #crushInfoTabs="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem="crush-rule-info">
+ <a ngbNavLink
+ i18n>Crush rule</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value [renderObjects]="false"
+ [hideKeys]="['steps', 'ruleset', 'type', 'rule_name']"
+ [data]="form.getValue('crushRule')"
+ [autoReload]="false">
+ </cd-table-key-value>
</ng-template>
- <ul *ngIf="crushUsage; else ruleIsNotUsed">
- <li *ngFor="let pool of crushUsage">
- {{ pool }}
- </li>
- </ul>
- </tab>
- </tabset>
- </span>
+ </li>
+ <li ngbNavItem="crush-rule-steps">
+ <a ngbNavLink
+ i18n>Crush steps</a>
+ <ng-template ngbNavContent>
+ <ol>
+ <li *ngFor="let step of form.get('crushRule').value.steps">
+ {{ describeCrushStep(step) }}
+ </li>
+ </ol>
+ </ng-template>
+ </li>
+ <li ngbNavItem="used-by-pools">
+ <a ngbNavLink
+ i18n>Used by pools</a>
+ <ng-template ngbNavContent>
+
+ <ng-template #ruleIsNotUsed>
+ <span i18n>Rule is not in use.</span>
+ </ng-template>
+ <ul *ngIf="crushUsage; else ruleIsNotUsed">
+ <li *ngFor="let pool of crushUsage">
+ {{ pool }}
+ </li>
+ </ul>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="crushInfoTabs"></div>
+ </div>
<span class="invalid-feedback"
*ngIf="form.showError('crushRule', formDir, 'required')"
i18n>This field is required!</span>
-.crush-rule-steps {
- margin-top: 10px;
-}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AbstractControl } from '@angular/forms';
import { By } from '@angular/platform-browser';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ActivatedRoute, Router, Routes } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import * as _ from 'lodash';
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
-import { TabsetComponent, TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { of } from 'rxjs';
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {
configureTestBed,
FixtureHelper,
HttpClientTestingModule,
RouterTestingModule.withRoutes(routes),
ToastrModule.forRoot(),
- TabsModule.forRoot(),
+ NgbNavModule,
PoolModule,
NgBootstrapFormValidationModule.forRoot()
],
describe('rule in use', () => {
beforeEach(() => {
spyOn(global, 'setTimeout').and.callFake((fn: Function) => fn());
- component.crushInfoTabs = { tabs: [{}, {}, {}] } as TabsetComponent; // Mock it
deleteSpy.calls.reset();
selectRuleByIndex(2);
component.deleteCrushRule();
expect(component.data.crushInfo).toBe(true);
});
- it('should open the third crush info tab', () => {
- expect(component.crushInfoTabs).toEqual({
- tabs: [{}, {}, { active: true }]
- } as TabsetComponent);
- });
-
it('should hide the tooltip when clicking on delete again', () => {
component.deleteCrushRule();
expect(component.crushDeletionBtn.isOpen).toBe(false);
describe('rule in use', () => {
beforeEach(() => {
spyOn(global, 'setTimeout').and.callFake((fn: Function) => fn());
- component.ecpInfoTabs = { tabs: [{}, {}] } as TabsetComponent; // Mock it
deleteSpy.calls.reset();
setSelectedEcp('ecp1');
component.deleteErasureCodeProfile();
expect(component.data.erasureInfo).toBe(true);
});
- it('should open the third crush info tab', () => {
- expect(component.ecpInfoTabs).toEqual({
- tabs: [{}, { active: true }]
- } as TabsetComponent);
- });
-
it('should hide the tooltip when clicking on delete again', () => {
component.deleteErasureCodeProfile();
expect(component.ecpDeletionBtn.isOpen).toBe(false);
import { FormControl, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
+import { NgbNav } from '@ng-bootstrap/ng-bootstrap';
+
import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
import { BsModalService } from 'ngx-bootstrap/modal';
-import { TabsetComponent } from 'ngx-bootstrap/tabs';
import { TooltipDirective } from 'ngx-bootstrap/tooltip';
import { Observable, Subscription } from 'rxjs';
styleUrls: ['./pool-form.component.scss']
})
export class PoolFormComponent extends CdForm implements OnInit {
- @ViewChild('crushInfoTabs') crushInfoTabs: TabsetComponent;
+ @ViewChild('crushInfoTabs') crushInfoTabs: NgbNav;
@ViewChild('crushDeletionBtn') crushDeletionBtn: TooltipDirective;
- @ViewChild('ecpInfoTabs') ecpInfoTabs: TabsetComponent;
+ @ViewChild('ecpInfoTabs') ecpInfoTabs: NgbNav;
@ViewChild('ecpDeletionBtn') ecpDeletionBtn: TooltipDirective;
permission: Permission;
deletionBtn: this.ecpDeletionBtn,
dataName: 'erasureInfo',
getTabs: () => this.ecpInfoTabs,
- tabPosition: 1,
+ tabPosition: 'used-by-pools',
nameAttribute: 'name',
itemDescription: this.i18n('erasure code profile'),
reloadFn: () => this.reloadECPs(),
usage: string[];
deletionBtn: TooltipDirective;
dataName: string;
- getTabs: () => TabsetComponent;
- tabPosition: number;
+ getTabs: () => NgbNav;
+ tabPosition: string;
nameAttribute: string;
itemDescription: string;
reloadFn: Function;
setTimeout(() => {
const tabs = getTabs();
if (tabs) {
- tabs.tabs[tabPosition].active = true;
+ tabs.select(tabPosition);
}
}, 50);
return;
deletionBtn: this.crushDeletionBtn,
dataName: 'crushInfo',
getTabs: () => this.crushInfoTabs,
- tabPosition: 2,
+ tabPosition: 'used-by-pools',
nameAttribute: 'rule_name',
itemDescription: this.i18n('crush rule'),
reloadFn: () => this.reloadCrushRules(),
-<tabset>
- <tab i18n-heading
- heading="Pools List">
- <cd-view-cache *ngFor="let viewCacheStatus of viewCacheStatusList"
- [status]="viewCacheStatus.status"
- [statusFor]="viewCacheStatus.statusFor"></cd-view-cache>
+<ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Pools List</a>
+ <ng-template ngbNavContent>
+ <cd-view-cache *ngFor="let viewCacheStatus of viewCacheStatusList"
+ [status]="viewCacheStatus.status"
+ [statusFor]="viewCacheStatus.statusFor"></cd-view-cache>
- <cd-table #table
- id="pool-list"
- [data]="pools"
- [columns]="columns"
- selectionType="single"
- [hasDetails]="true"
- (setExpandedRow)="setExpandedRow($event)"
- (updateSelection)="updateSelection($event)">
- <cd-table-actions id="pool-list-actions"
- class="table-actions"
- [permission]="permissions.pool"
- [selection]="selection"
- [tableActions]="tableActions">
- </cd-table-actions>
- <cd-pool-details cdTableDetail
- id="pool-list-details"
- [selection]="expandedRow"
- [permissions]="permissions"
- [cacheTiers]="cacheTiers">
- </cd-pool-details>
- </cd-table>
+ <cd-table #table
+ id="pool-list"
+ [data]="pools"
+ [columns]="columns"
+ selectionType="single"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
+ (updateSelection)="updateSelection($event)">
+ <cd-table-actions id="pool-list-actions"
+ class="table-actions"
+ [permission]="permissions.pool"
+ [selection]="selection"
+ [tableActions]="tableActions">
+ </cd-table-actions>
+ <cd-pool-details cdTableDetail
+ id="pool-list-details"
+ [selection]="expandedRow"
+ [permissions]="permissions"
+ [cacheTiers]="cacheTiers">
+ </cd-pool-details>
+ </cd-table>
+ </ng-template>
+ </li>
- <ng-template #poolUsageTpl
- let-row="row">
- <cd-usage-bar *ngIf="row.stats?.max_avail?.latest"
- [totalBytes]="row.stats.bytes_used.latest + row.stats.max_avail.latest"
- [usedBytes]="row.stats.bytes_used.latest">
- </cd-usage-bar>
+ <li ngbNavItem
+ *ngIf="permissions.grafana.read">
+ <a ngbNavLink
+ i18n>Overall Performance</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'ceph-pools-overview?'"
+ uid="z99hzWtmk"
+ grafanaStyle="two">
+ </cd-grafana>
</ng-template>
- </tab>
+ </li>
+</ul>
+
+<div [ngbNavOutlet]="nav"></div>
- <tab i18n-heading
- *ngIf="permissions.grafana.read"
- heading="Overall Performance">
- <cd-grafana [grafanaPath]="'ceph-pools-overview?'"
- uid="z99hzWtmk"
- grafanaStyle="two">
- </cd-grafana>
- </tab>
-</tabset>
+<ng-template #poolUsageTpl
+ let-row="row">
+ <cd-usage-bar *ngIf="row.stats?.max_avail?.latest"
+ [totalBytes]="row.stats.bytes_used.latest + row.stats.max_avail.latest"
+ [usedBytes]="row.stats.bytes_used.latest">
+ </cd-usage-bar>
+</ng-template>
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import * as _ from 'lodash';
import { BsModalService } from 'ngx-bootstrap/modal';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ToastrModule } from 'ngx-toastr';
import { of } from 'rxjs';
SharedModule,
ToastrModule.forRoot(),
RouterTestingModule,
- TabsModule.forRoot(),
+ NgbNavModule,
HttpClientTestingModule
],
providers: [i18nProviders, PgCategoryService]
import { ReactiveFormsModule } from '@angular/forms';
import { RouterModule, Routes } from '@angular/router';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { PopoverModule } from 'ngx-bootstrap/popover';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { ActionLabels, URLVerbs } from '../../shared/constants/app.constants';
imports: [
CephSharedModule,
CommonModule,
- TabsModule,
+ NgbNavModule,
PopoverModule.forRoot(),
SharedModule,
RouterModule,
-<tabset *ngIf="selection">
- <tab i18n-heading
- heading="Details">
- <div *ngIf="selection">
- <table class="table table-striped table-bordered">
- <tbody>
- <tr>
- <td i18n
- class="bold w-25">Name</td>
- <td class="w-75">{{ selection.bid }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">ID</td>
- <td>{{ selection.id }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Owner</td>
- <td>{{ selection.owner }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Index type</td>
- <td>{{ selection.index_type }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Placement rule</td>
- <td>{{ selection.placement_rule }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Marker</td>
- <td>{{ selection.marker }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Maximum marker</td>
- <td>{{ selection.max_marker }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Version</td>
- <td>{{ selection.ver }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Master version</td>
- <td>{{ selection.master_ver }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Modification time</td>
- <td>{{ selection.mtime | cdDate }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Zonegroup</td>
- <td>{{ selection.zonegroup }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Versioning</td>
- <td>{{ selection.versioning }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">MFA Delete</td>
- <td>{{ selection.mfa_delete }}</td>
- </tr>
- </tbody>
- </table>
-
- <!-- Bucket quota -->
- <div *ngIf="selection.bucket_quota">
- <legend i18n>Bucket quota</legend>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
<table class="table table-striped table-bordered">
<tbody>
<tr>
<td i18n
- class="bold w-25">Enabled</td>
- <td class="w-75">{{ selection.bucket_quota.enabled | booleanText }}</td>
+ class="bold w-25">Name</td>
+ <td class="w-75">{{ selection.bid }}</td>
</tr>
<tr>
<td i18n
- class="bold">Maximum size</td>
- <td *ngIf="selection.bucket_quota.max_size <= -1"
- i18n>Unlimited</td>
- <td *ngIf="selection.bucket_quota.max_size > -1">
- {{ selection.bucket_quota.max_size | dimless }}
- </td>
+ class="bold">ID</td>
+ <td>{{ selection.id }}</td>
</tr>
<tr>
<td i18n
- class="bold">Maximum objects</td>
- <td *ngIf="selection.bucket_quota.max_objects <= -1"
- i18n>Unlimited</td>
- <td *ngIf="selection.bucket_quota.max_objects > -1">
- {{ selection.bucket_quota.max_objects }}
- </td>
+ class="bold">Owner</td>
+ <td>{{ selection.owner }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Index type</td>
+ <td>{{ selection.index_type }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Placement rule</td>
+ <td>{{ selection.placement_rule }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Marker</td>
+ <td>{{ selection.marker }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Maximum marker</td>
+ <td>{{ selection.max_marker }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Version</td>
+ <td>{{ selection.ver }}</td>
</tr>
- </tbody>
- </table>
- </div>
-
- <!-- Locking -->
- <legend i18n>Locking</legend>
- <table class="table table-striped table-bordered">
- <tbody>
- <tr>
- <td i18n
- class="bold w-25">Enabled</td>
- <td class="w-75">{{ selection.lock_enabled | booleanText }}</td>
- </tr>
- <ng-container *ngIf="selection.lock_enabled">
<tr>
<td i18n
- class="bold">Mode</td>
- <td>{{ selection.lock_mode }}</td>
+ class="bold">Master version</td>
+ <td>{{ selection.master_ver }}</td>
</tr>
<tr>
<td i18n
- class="bold">Days</td>
- <td>{{ selection.lock_retention_period_days }}</td>
+ class="bold">Modification time</td>
+ <td>{{ selection.mtime | cdDate }}</td>
</tr>
<tr>
<td i18n
- class="bold">Years</td>
- <td>{{ selection.lock_retention_period_years }}</td>
+ class="bold">Zonegroup</td>
+ <td>{{ selection.zonegroup }}</td>
</tr>
- </ng-container>
- </tbody>
- </table>
- </div>
- </tab>
-</tabset>
+ <tr>
+ <td i18n
+ class="bold">Versioning</td>
+ <td>{{ selection.versioning }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">MFA Delete</td>
+ <td>{{ selection.mfa_delete }}</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <!-- 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">{{ selection.bucket_quota.enabled | booleanText }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Maximum size</td>
+ <td *ngIf="selection.bucket_quota.max_size <= -1"
+ i18n>Unlimited</td>
+ <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="selection.bucket_quota.max_objects <= -1"
+ i18n>Unlimited</td>
+ <td *ngIf="selection.bucket_quota.max_objects > -1">
+ {{ selection.bucket_quota.max_objects }}
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <!-- Locking -->
+ <legend i18n>Locking</legend>
+ <table class="table table-striped table-bordered">
+ <tbody>
+ <tr>
+ <td i18n
+ class="bold w-25">Enabled</td>
+ <td class="w-75">{{ selection.lock_enabled | booleanText }}</td>
+ </tr>
+ <ng-container *ngIf="selection.lock_enabled">
+ <tr>
+ <td i18n
+ class="bold">Mode</td>
+ <td>{{ selection.lock_mode }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Days</td>
+ <td>{{ selection.lock_retention_period_days }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Years</td>
+ <td>{{ selection.lock_retention_period_years }}</td>
+ </tr>
+ </ng-container>
+ </tbody>
+ </table>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
import { CdTableSelection } from '../../../shared/models/cd-table-selection';
configureTestBed({
declarations: [RgwBucketDetailsComponent],
- imports: [SharedModule, TabsModule.forRoot()],
+ imports: [SharedModule, NgbNavModule],
providers: [i18nProviders]
});
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { ModalModule } from 'ngx-bootstrap/modal';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import {
configureTestBed,
RouterTestingModule,
ModalModule.forRoot(),
SharedModule,
- TabsModule.forRoot(),
+ NgbNavModule,
HttpClientTestingModule
],
providers: i18nProviders
-<tabset *ngIf="selection">
- <tab i18n-heading
- heading="Details">
- <cd-table-key-value [data]="metadata"
- (fetchData)="getMetaData()">
- </cd-table-key-value>
- </tab>
- <tab i18n-heading
- heading="Performance Counters">
- <cd-table-performance-counter serviceType="rgw"
- [serviceId]="serviceId">
- </cd-table-performance-counter>
- </tab>
- <tab i18n-heading
- *ngIf="grafanaPermission.read"
- heading="Performance Details">
- <cd-grafana [grafanaPath]="'rgw-instance-detail?var-rgw_servers=rgw.' + this.serviceId"
- uid="x5ARzZtmk"
- grafanaStyle="one">
- </cd-grafana>
- </tab>
-</tabset>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value [data]="metadata"
+ (fetchData)="getMetaData()">
+ </cd-table-key-value>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Performance Counters</a>
+ <ng-template ngbNavContent>
+ <cd-table-performance-counter serviceType="rgw"
+ [serviceId]="serviceId">
+ </cd-table-performance-counter>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="grafanaPermission.read">
+ <a ngbNavLink
+ i18n>Performance Details</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'rgw-instance-detail?var-rgw_servers=rgw.' + this.serviceId"
+ uid="x5ARzZtmk"
+ grafanaStyle="one">
+ </cd-grafana>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed } from '../../../../testing/unit-test-helper';
import { SharedModule } from '../../../shared/shared.module';
configureTestBed({
declarations: [RgwDaemonDetailsComponent],
- imports: [SharedModule, PerformanceCounterModule, TabsModule.forRoot(), HttpClientTestingModule]
+ imports: [SharedModule, PerformanceCounterModule, HttpClientTestingModule, NgbNavModule]
});
beforeEach(() => {
-<tabset>
- <tab i18n-heading
- heading="Daemons List">
- <cd-table [data]="daemons"
- [columns]="columns"
- columnMode="flex"
- [hasDetails]="true"
- (setExpandedRow)="setExpandedRow($event)"
- (fetchData)="getDaemonList($event)">
- <cd-rgw-daemon-details cdTableDetail
- [selection]="expandedRow">
- </cd-rgw-daemon-details>
- </cd-table>
- </tab>
+<ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Daemons List</a>
+ <ng-template ngbNavContent>
+ <cd-table [data]="daemons"
+ [columns]="columns"
+ columnMode="flex"
+ [hasDetails]="true"
+ (setExpandedRow)="setExpandedRow($event)"
+ (fetchData)="getDaemonList($event)">
+ <cd-rgw-daemon-details cdTableDetail
+ [selection]="expandedRow">
+ </cd-rgw-daemon-details>
+ </cd-table>
+ </ng-template>
+ </li>
- <tab i18n-heading
- *ngIf="grafanaPermission.read"
- heading="Overall Performance">
- <cd-grafana [grafanaPath]="'rgw-overview?'"
- uid="WAkugZpiz"
- grafanaStyle="two">
- </cd-grafana>
- </tab>
+ <li ngbNavItem
+ *ngIf="grafanaPermission.read">
+ <a ngbNavLink
+ i18n>Overall Performance</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'rgw-overview?'"
+ uid="WAkugZpiz"
+ grafanaStyle="two">
+ </cd-grafana>
+ </ng-template>
+ </li>
- <tab i18n-heading
- *ngIf="grafanaPermission.read && isMultiSite"
- heading="Sync Performance">
- <cd-grafana [grafanaPath]="'radosgw-sync-overview?'"
- uid="rgw-sync-overview"
- grafanaStyle="two">
- </cd-grafana>
- </tab>
-</tabset>
+ <li ngbNavItem
+ *ngIf="grafanaPermission.read && isMultiSite">
+ <a ngbNavLink
+ i18n>Sync Performance</a>
+ <ng-template ngbNavContent>
+ <cd-grafana [grafanaPath]="'radosgw-sync-overview?'"
+ uid="rgw-sync-overview"
+ grafanaStyle="two">
+ </cd-grafana>
+ </ng-template>
+ </li>
+</ul>
+
+<div [ngbNavOutlet]="nav"></div>
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { of } from 'rxjs';
-import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
+import { configureTestBed, i18nProviders, TabHelper } from '../../../../testing/unit-test-helper';
import { RgwSiteService } from '../../../shared/api/rgw-site.service';
import { Permissions } from '../../../shared/models/permissions';
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
let getPermissionsSpy: jasmine.Spy;
let getRealmsSpy: jasmine.Spy;
const permissions = new Permissions({ grafana: ['read'] });
- const expectTabsAndHeading = (length: number, heading: string) => {
- const tabs = fixture.debugElement.nativeElement.querySelectorAll('tab');
+ const expectTabsAndHeading = (length: number, heading: string) => {
+ const tabs = TabHelper.getTextContents(fixture);
expect(tabs.length).toEqual(length);
- expect(tabs[length - 1].getAttribute('heading')).toEqual(heading);
+ expect(tabs[length - 1]).toEqual(heading);
};
configureTestBed({
imports: [
BrowserAnimationsModule,
HttpClientTestingModule,
- TabsModule.forRoot(),
+ NgbNavModule,
PerformanceCounterModule,
SharedModule,
RouterTestingModule
-<tabset *ngIf="selection">
- <tab i18n-heading
- heading="Details">
- <div *ngIf="user">
- <table class="table table-striped table-bordered">
- <tbody>
- <tr>
- <td i18n
- class="bold w-25">Username</td>
- <td class="w-75">{{ user.uid }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Full name</td>
- <td>{{ user.display_name }}</td>
- </tr>
- <tr *ngIf="user.email?.length">
- <td i18n
- class="bold">Email address</td>
- <td>{{ user.email }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Suspended</td>
- <td>{{ user.suspended | booleanText }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">System</td>
- <td>{{ user.system === 'true' | booleanText }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Maximum buckets</td>
- <td>{{ user.max_buckets | map:maxBucketsMap }}</td>
- </tr>
- <tr *ngIf="user.subusers && user.subusers.length">
- <td i18n
- class="bold">Subusers</td>
- <td>
- <div *ngFor="let subuser of user.subusers">
- {{ subuser.id }} ({{ subuser.permissions }})
- </div>
- </td>
- </tr>
- <tr *ngIf="user.caps && user.caps.length">
- <td i18n
- class="bold">Capabilities</td>
- <td>
- <div *ngFor="let cap of user.caps">
- {{ cap.type }} ({{ cap.perm }})
- </div>
- </td>
- </tr>
- </tbody>
- </table>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <div *ngIf="user">
+ <table class="table table-striped table-bordered">
+ <tbody>
+ <tr>
+ <td i18n
+ class="bold w-25">Username</td>
+ <td class="w-75">{{ user.uid }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Full name</td>
+ <td>{{ user.display_name }}</td>
+ </tr>
+ <tr *ngIf="user.email?.length">
+ <td i18n
+ class="bold">Email address</td>
+ <td>{{ user.email }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Suspended</td>
+ <td>{{ user.suspended | booleanText }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">System</td>
+ <td>{{ user.system === 'true' | booleanText }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Maximum buckets</td>
+ <td>{{ user.max_buckets | map:maxBucketsMap }}</td>
+ </tr>
+ <tr *ngIf="user.subusers && user.subusers.length">
+ <td i18n
+ class="bold">Subusers</td>
+ <td>
+ <div *ngFor="let subuser of user.subusers">
+ {{ subuser.id }} ({{ subuser.permissions }})
+ </div>
+ </td>
+ </tr>
+ <tr *ngIf="user.caps && user.caps.length">
+ <td i18n
+ class="bold">Capabilities</td>
+ <td>
+ <div *ngFor="let cap of user.caps">
+ {{ cap.type }} ({{ cap.perm }})
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
- <!-- User quota -->
- <div *ngIf="user.user_quota">
- <legend i18n>User quota</legend>
- <table class="table table-striped table-bordered">
- <tbody>
- <tr>
- <td i18n
- class="bold w-25">Enabled</td>
- <td class="w-75">{{ user.user_quota.enabled | booleanText }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Maximum size</td>
- <td *ngIf="!user.user_quota.enabled">-</td>
- <td *ngIf="user.user_quota.enabled && user.user_quota.max_size <= -1"
- i18n>Unlimited</td>
- <td *ngIf="user.user_quota.enabled && user.user_quota.max_size > -1">
- {{ user.user_quota.max_size | dimlessBinary }}
- </td>
- </tr>
- <tr>
- <td i18n
- class="bold">Maximum objects</td>
- <td *ngIf="!user.user_quota.enabled">-</td>
- <td *ngIf="user.user_quota.enabled && user.user_quota.max_objects <= -1"
- i18n>Unlimited</td>
- <td *ngIf="user.user_quota.enabled && user.user_quota.max_objects > -1">
- {{ user.user_quota.max_objects }}
- </td>
- </tr>
- </tbody>
- </table>
- </div>
+ <!-- User quota -->
+ <div *ngIf="user.user_quota">
+ <legend i18n>User quota</legend>
+ <table class="table table-striped table-bordered">
+ <tbody>
+ <tr>
+ <td i18n
+ class="bold w-25">Enabled</td>
+ <td class="w-75">{{ user.user_quota.enabled | booleanText }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Maximum size</td>
+ <td *ngIf="!user.user_quota.enabled">-</td>
+ <td *ngIf="user.user_quota.enabled && user.user_quota.max_size <= -1"
+ i18n>Unlimited</td>
+ <td *ngIf="user.user_quota.enabled && user.user_quota.max_size > -1">
+ {{ user.user_quota.max_size | dimlessBinary }}
+ </td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Maximum objects</td>
+ <td *ngIf="!user.user_quota.enabled">-</td>
+ <td *ngIf="user.user_quota.enabled && user.user_quota.max_objects <= -1"
+ i18n>Unlimited</td>
+ <td *ngIf="user.user_quota.enabled && user.user_quota.max_objects > -1">
+ {{ user.user_quota.max_objects }}
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
- <!-- Bucket quota -->
- <div *ngIf="user.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">{{ user.bucket_quota.enabled | booleanText }}</td>
- </tr>
- <tr>
- <td i18n
- class="bold">Maximum size</td>
- <td *ngIf="!user.bucket_quota.enabled">-</td>
- <td *ngIf="user.bucket_quota.enabled && user.bucket_quota.max_size <= -1"
- i18n>Unlimited</td>
- <td *ngIf="user.bucket_quota.enabled && user.bucket_quota.max_size > -1">
- {{ user.bucket_quota.max_size | dimlessBinary }}
- </td>
- </tr>
- <tr>
- <td i18n
- class="bold">Maximum objects</td>
- <td *ngIf="!user.bucket_quota.enabled">-</td>
- <td *ngIf="user.bucket_quota.enabled && user.bucket_quota.max_objects <= -1"
- i18n>Unlimited</td>
- <td *ngIf="user.bucket_quota.enabled && user.bucket_quota.max_objects > -1">
- {{ user.bucket_quota.max_objects }}
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- </tab>
-
- <tab *ngIf="keys.length"
- i18n-heading
- heading="Keys">
- <cd-table [data]="keys"
- [columns]="keysColumns"
- columnMode="flex"
- selectionType="multi"
- forceIdentifier="true"
- (updateSelection)="updateKeysSelection($event)">
- <div class="table-actions">
- <div class="btn-group"
- dropdown>
- <button type="button"
- class="btn btn-secondary"
- [disabled]="!keysSelection.hasSingleSelection"
- (click)="showKeyModal()">
- <i [ngClass]="[icons.show]"></i>
- <ng-container i18n>Show</ng-container>
- </button>
+ <!-- Bucket quota -->
+ <div *ngIf="user.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">{{ user.bucket_quota.enabled | booleanText }}</td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Maximum size</td>
+ <td *ngIf="!user.bucket_quota.enabled">-</td>
+ <td *ngIf="user.bucket_quota.enabled && user.bucket_quota.max_size <= -1"
+ i18n>Unlimited</td>
+ <td *ngIf="user.bucket_quota.enabled && user.bucket_quota.max_size > -1">
+ {{ user.bucket_quota.max_size | dimlessBinary }}
+ </td>
+ </tr>
+ <tr>
+ <td i18n
+ class="bold">Maximum objects</td>
+ <td *ngIf="!user.bucket_quota.enabled">-</td>
+ <td *ngIf="user.bucket_quota.enabled && user.bucket_quota.max_objects <= -1"
+ i18n>Unlimited</td>
+ <td *ngIf="user.bucket_quota.enabled && user.bucket_quota.max_objects > -1">
+ {{ user.bucket_quota.max_objects }}
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
</div>
- </div>
- </cd-table>
- </tab>
-</tabset>
+ </ng-template>
+ </li>
+ <li ngbNavItem
+ *ngIf="keys.length">
+ <a ngbNavLink
+ i18n>Keys</a>
+ <ng-template ngbNavContent>
+ <cd-table [data]="keys"
+ [columns]="keysColumns"
+ columnMode="flex"
+ selectionType="multi"
+ forceIdentifier="true"
+ (updateSelection)="updateKeysSelection($event)">
+ <div class="table-actions">
+ <div class="btn-group"
+ dropdown>
+ <button type="button"
+ class="btn btn-secondary"
+ [disabled]="!keysSelection.hasSingleSelection"
+ (click)="showKeyModal()">
+ <i [ngClass]="[icons.show]"></i>
+ <ng-container i18n>Show</ng-container>
+ </button>
+ </div>
+ </div>
+ </cd-table>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { BsModalService } from 'ngx-bootstrap/modal';
-import { TabsModule } from 'ngx-bootstrap/tabs';
-import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
+import { configureTestBed, i18nProviders, TabHelper } from '../../../../testing/unit-test-helper';
import { SharedModule } from '../../../shared/shared.module';
import { RgwUserS3Key } from '../models/rgw-user-s3-key';
import { RgwUserDetailsComponent } from './rgw-user-details.component';
configureTestBed({
declarations: [RgwUserDetailsComponent],
- imports: [BrowserAnimationsModule, HttpClientTestingModule, SharedModule, TabsModule.forRoot()],
+ imports: [BrowserAnimationsModule, HttpClientTestingModule, SharedModule, NgbNavModule],
providers: [BsModalService, i18nProviders]
});
it('should create', () => {
expect(component).toBeTruthy();
- const detailsTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Details"]');
- expect(detailsTab).toBeTruthy();
- const keysTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Keys"]');
- expect(keysTab).toBeFalsy();
+ const tabs = TabHelper.getTextContents(fixture);
+ expect(tabs).toContain('Details');
+ expect(tabs).not.toContain('Keys');
});
it('should show "Details" tab', () => {
component.selection = { uid: 'myUsername' };
fixture.detectChanges();
- const detailsTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Details"]');
- expect(detailsTab).toBeTruthy();
- const keysTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Keys"]');
- expect(keysTab).toBeFalsy();
+ const tabs = TabHelper.getTextContents(fixture);
+ expect(tabs).toContain('Details');
+ expect(tabs).not.toContain('Keys');
});
it('should show "Keys" tab', () => {
component.ngOnChanges();
fixture.detectChanges();
- const detailsTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Details"]');
- expect(detailsTab).toBeTruthy();
- const keysTab = fixture.debugElement.nativeElement.querySelector('tab[heading="Keys"]');
- expect(keysTab).toBeTruthy();
+ const tabs = TabHelper.getTextContents(fixture);
+ expect(tabs).toContain('Details');
+ expect(tabs).toContain('Keys');
});
it('should show correct "System" info', () => {
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule, Routes } from '@angular/router';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
import { AlertModule } from 'ngx-bootstrap/alert';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { ModalModule } from 'ngx-bootstrap/modal';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { ActionLabels, URLVerbs } from '../../shared/constants/app.constants';
PerformanceCounterModule,
AlertModule.forRoot(),
BsDropdownModule.forRoot(),
- TabsModule.forRoot(),
+ NgbNavModule,
TooltipModule.forRoot(),
ModalModule.forRoot(),
RouterModule,
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
+
import { DataTableModule } from '../../shared/datatable/datatable.module';
import { SharedModule } from '../../shared/shared.module';
import { DeviceListComponent } from './device-list/device-list.component';
import { SmartListComponent } from './smart-list/smart-list.component';
+
@NgModule({
- imports: [CommonModule, DataTableModule, SharedModule, TabsModule],
+ imports: [CommonModule, DataTableModule, SharedModule, NgbNavModule],
exports: [DeviceListComponent, SmartListComponent],
declarations: [DeviceListComponent, SmartListComponent]
})
i18n>Failed to retrieve SMART data.</cd-alert-panel>
<cd-alert-panel *ngIf="incompatible"
type="warning"
- i18n>The data received has the JSON format version 2.x and is currently incompatible with the dashboard.</cd-alert-panel>
+ i18n>The data received has the JSON format version 2.x and is currently incompatible with the
+ dashboard.</cd-alert-panel>
<ng-container *ngIf="!error && !incompatible">
<cd-alert-panel *ngIf="!(data | keyvalue).length"
type="info"
i18n>No SMART data available.</cd-alert-panel>
- <tabset *ngFor="let device of data | keyvalue">
- <tab [heading]="device.value.device + ' (' + device.value.identifier + ')'">
- <ng-container *ngIf="device.value.error; else noError">
- <cd-alert-panel id="alert-error"
- type="warning">{{ device.value.userMessage }}</cd-alert-panel>
- </ng-container>
- <ng-template #noError>
- <!-- HDD/NVMe self test -->
- <ng-container *ngIf="device.value.info.smart_status.passed; else selfTestFailed">
- <cd-alert-panel id="alert-self-test-passed"
- size="slim"
- type="info"
- i18n-title
- title="SMART overall-health self-assessment test result"
- i18n>passed</cd-alert-panel>
- </ng-container>
- <ng-template #selfTestFailed>
- <cd-alert-panel id="alert-self-test-failed"
- size="slim"
- type="warning"
- i18n-title
- title="SMART overall-health self-assessment test result"
- i18n>failed</cd-alert-panel>
- </ng-template>
+ <ng-container *ngFor="let device of data | keyvalue">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>{{ device.value.device }} ({{ device.value.identifier }})</a>
+ <ng-template ngbNavContent>
+
+ <ng-container *ngIf="device.value.error; else noError">
+ <cd-alert-panel id="alert-error"
+ type="warning">{{ device.value.userMessage }}</cd-alert-panel>
+ </ng-container>
+
+ <ng-template #noError>
+ <!-- HDD/NVMe self test -->
+ <ng-container *ngIf="device.value.info.smart_status.passed; else selfTestFailed">
+ <cd-alert-panel id="alert-self-test-passed"
+ size="slim"
+ type="info"
+ i18n-title
+ title="SMART overall-health self-assessment test result"
+ i18n>passed</cd-alert-panel>
+ </ng-container>
+ <ng-template #selfTestFailed>
+ <cd-alert-panel id="alert-self-test-failed"
+ size="slim"
+ type="warning"
+ i18n-title
+ title="SMART overall-health self-assessment test result"
+ i18n>failed</cd-alert-panel>
+ </ng-template>
+ </ng-template>
- <tabset>
- <tab i18n-heading
- heading="Device Information">
- <cd-table-key-value [renderObjects]="true"
- [data]="device.value.info"></cd-table-key-value>
- </tab>
+ <ul ngbNav
+ #innerNav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Device Information</a>
+ <ng-template ngbNavContent>
+ <cd-table-key-value [renderObjects]="true"
+ [data]="device.value.info"></cd-table-key-value>
+ </ng-template>
+ </li>
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>SMART</a>
+ <ng-template ngbNavContent>
+ <cd-table *ngIf="device.value.smart.attributes"
+ [data]="device.value.smart.attributes.table"
+ updateSelectionOnRefresh="never"
+ [columns]="smartDataColumns"></cd-table>
+ <cd-table-key-value *ngIf="device.value.smart.nvmeData"
+ [renderObjects]="true"
+ [data]="device.value.smart.nvmeData"
+ updateSelectionOnRefresh="never"></cd-table-key-value>
+ <cd-alert-panel *ngIf="!device.value.smart.attributes && !device.value.smart.nvmeData"
+ type="info"
+ i18n>No SMART data available for this device.</cd-alert-panel>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="innerNav"></div>
+ </ng-template>
+ </li>
+ </ul>
- <tab i18n-heading
- heading="SMART">
- <cd-table *ngIf="device.value.smart.attributes"
- [data]="device.value.smart.attributes.table"
- updateSelectionOnRefresh="never"
- [columns]="smartDataColumns"></cd-table>
- <cd-table-key-value *ngIf="device.value.smart.nvmeData"
- [renderObjects]="true"
- [data]="device.value.smart.nvmeData"
- updateSelectionOnRefresh="never"></cd-table-key-value>
- <cd-alert-panel *ngIf="!device.value.smart.attributes && !device.value.smart.nvmeData"
- type="info"
- i18n>No SMART data available for this device.</cd-alert-panel>
- </tab>
- </tabset>
- </ng-template>
- </tab>
- </tabset>
+ <div [ngbNavOutlet]="nav"></div>
+ </ng-container>
</ng-container>
</ng-container>
<ng-template #isLoading>
import { By } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import * as _ from 'lodash';
-import { TabsetComponent, TabsetConfig, TabsModule } from 'ngx-bootstrap/tabs';
import { of } from 'rxjs';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
configureTestBed({
declarations: [SmartListComponent],
- imports: [BrowserAnimationsModule, TabsModule, SharedModule, HttpClientTestingModule],
- providers: [i18nProviders, TabsetComponent, TabsetConfig]
+ imports: [BrowserAnimationsModule, SharedModule, HttpClientTestingModule, NgbNavModule],
+ providers: [i18nProviders]
});
beforeEach(() => {
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterModule, Routes } from '@angular/router';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
import { ButtonsModule } from 'ngx-bootstrap/buttons';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
import { PopoverModule } from 'ngx-bootstrap/popover';
-import { TabsModule } from 'ngx-bootstrap/tabs';
import { ActionLabels, URLVerbs } from '../../shared/constants/app.constants';
import { SharedModule } from '../../shared/shared.module';
PopoverModule.forRoot(),
ReactiveFormsModule,
SharedModule,
- TabsModule.forRoot(),
+ NgbNavModule,
RouterModule,
NgBootstrapFormValidationModule,
BsDatepickerModule.forRoot()
-<tabset *ngIf="selection">
- <tab heading="Details"
- i18n-heading>
- <cd-table [data]="scopes_permissions"
- [columns]="columns"
- columnMode="flex"
- [toolHeader]="false"
- [autoReload]="false"
- [autoSave]="false"
- [footer]="false"
- [limit]="0">
- </cd-table>
- </tab>
-</tabset>
+<ng-container *ngIf="selection">
+ <ul ngbNav
+ #nav="ngbNav"
+ class="nav-tabs">
+ <li ngbNavItem>
+ <a ngbNavLink
+ i18n>Details</a>
+ <ng-template ngbNavContent>
+ <cd-table [data]="scopes_permissions"
+ [columns]="columns"
+ columnMode="flex"
+ [toolHeader]="false"
+ [autoReload]="false"
+ [autoSave]="false"
+ [footer]="false"
+ [limit]="0">
+ </cd-table>
+ </ng-template>
+ </li>
+ </ul>
+
+ <div [ngbNavOutlet]="nav"></div>
+</ng-container>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
import { SharedModule } from '../../../shared/shared.module';
let fixture: ComponentFixture<RoleDetailsComponent>;
configureTestBed({
- imports: [SharedModule, TabsModule.forRoot(), RouterTestingModule, HttpClientTestingModule],
+ imports: [SharedModule, RouterTestingModule, HttpClientTestingModule, NgbNavModule],
declarations: [RoleDetailsComponent],
providers: i18nProviders
});
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { ToastrModule } from 'ngx-toastr';
import {
BrowserAnimationsModule,
SharedModule,
ToastrModule.forRoot(),
- TabsModule.forRoot(),
+ NgbNavModule,
RouterTestingModule,
HttpClientTestingModule
],
private authService: AuthService,
private authStorageService: AuthStorageService,
private route: ActivatedRoute,
- private router: Router,
+ public router: Router,
private modalService: BsModalService,
private roleService: RoleService,
private userService: UserService,
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { ToastrModule } from 'ngx-toastr';
import {
BrowserAnimationsModule,
SharedModule,
ToastrModule.forRoot(),
- TabsModule.forRoot(),
+ NgbNavModule,
RouterTestingModule,
HttpClientTestingModule
],
-<tabset>
- <tab heading="Users"
- i18n-heading
- [active]="url === '/user-management/users'"
- (selectTab)="navigateTo('/user-management/users')">
- </tab>
- <tab heading="Roles"
- i18n-heading
- [active]="url === '/user-management/roles'"
- (selectTab)="navigateTo('/user-management/roles')">
- </tab>
-</tabset>
+<ul ngbNav
+ #nav="ngbNav"
+ [activeId]="router.url"
+ (navChange)="router.navigate([$event.nextId])"
+ class="nav-tabs">
+ <li ngbNavItem="/user-management/users">
+ <a ngbNavLink
+ i18n>Users</a>
+ </li>
+ <li ngbNavItem="/user-management/roles">
+ <a ngbNavLink
+ i18n>Roles</a>
+ </li>
+</ul>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { configureTestBed } from '../../../../testing/unit-test-helper';
import { SharedModule } from '../../../shared/shared.module';
let fixture: ComponentFixture<UserTabsComponent>;
configureTestBed({
- imports: [SharedModule, TabsModule.forRoot(), RouterTestingModule, HttpClientTestingModule],
+ imports: [SharedModule, RouterTestingModule, HttpClientTestingModule, NgbNavModule],
declarations: [UserTabsComponent]
});
-import { Component, OnInit } from '@angular/core';
+import { Component } from '@angular/core';
import { Router } from '@angular/router';
templateUrl: './user-tabs.component.html',
styleUrls: ['./user-tabs.component.scss']
})
-export class UserTabsComponent implements OnInit {
+export class UserTabsComponent {
url: string;
- constructor(private router: Router) {}
-
- ngOnInit() {
- this.url = this.router.url;
- }
-
- navigateTo(url: string) {
- this.router.navigate([url]);
- }
+ constructor(public router: Router) {}
}
component = fixture.componentInstance;
component.data = createFakeData(10);
- component.columns = [
+ component.localColumns = component.columns = [
{ prop: 'a', name: 'Index', filterable: true },
{ prop: 'b', name: 'Index times ten' },
{ prop: 'c', name: 'Odd?', filterable: true }
beforeEach(() => {
component.data = [testObject];
- component.columns = [{ prop: 'obj', name: 'Object' }];
+ component.localColumns = [{ prop: 'obj', name: 'Object' }];
});
it('should not search through objects as default case', () => {
});
it('should search through arrays', () => {
- component.columns = [
+ component.localColumns = [
{ prop: 'a', name: 'Index' },
{ prop: 'b', name: 'ArrayColumn' }
];
});
it('should have updated the column definitions', () => {
- expect(component.columns[0].flexGrow).toBe(1);
- expect(component.columns[1].flexGrow).toBe(2);
- expect(component.columns[2].flexGrow).toBe(2);
- expect(component.columns[2].resizeable).toBe(false);
+ expect(component.localColumns[0].flexGrow).toBe(1);
+ expect(component.localColumns[1].flexGrow).toBe(2);
+ expect(component.localColumns[2].flexGrow).toBe(2);
+ expect(component.localColumns[2].resizeable).toBe(false);
});
it('should have table columns', () => {
expect(component.tableColumns.length).toBe(3);
- expect(component.tableColumns).toEqual(component.columns);
+ expect(component.tableColumns).toEqual(component.localColumns);
});
it('should have a unique identifier which it searches for', () => {
*/
expanded: any = undefined;
+ /**
+ * To prevent making changes to the original columns list, that might change
+ * how the table is renderer a second time, we now clone that list into a
+ * local variable and only use the clone.
+ */
+ localColumns: CdTableColumn[];
tableColumns: CdTableColumn[];
icons = Icons;
cellTemplates: {
}
ngOnInit() {
+ this.localColumns = _.clone(this.columns);
+
// ngx-datatable triggers calculations each time mouse enters a row,
// this will prevent that.
this.table.element.addEventListener('mouseenter', (e) => e.stopPropagation(), true);
this._addTemplates();
if (!this.sorts) {
// Check whether the specified identifier exists.
- const exists = _.findIndex(this.columns, ['prop', this.identifier]) !== -1;
+ const exists = _.findIndex(this.localColumns, ['prop', this.identifier]) !== -1;
// Auto-build the sorting configuration. If the specified identifier doesn't exist,
// then use the property of the first column.
this.sorts = this.createSortingDefinition(
- exists ? this.identifier : this.columns[0].prop + ''
+ exists ? this.identifier : this.localColumns[0].prop + ''
);
// If the specified identifier doesn't exist and it is not forced to use it anyway,
// then use the property of the first column.
if (!exists && !this.forceIdentifier) {
- this.identifier = this.columns[0].prop + '';
+ this.identifier = this.localColumns[0].prop + '';
}
}
this.initUserConfig();
- this.columns.forEach((c) => {
+ this.localColumns.forEach((c) => {
if (c.cellTransformation) {
c.cellTemplate = this.cellTemplates[c.cellTransformation];
}
initUserConfig() {
if (this.autoSave) {
- this.tableName = this._calculateUniqueTableName(this.columns);
+ this.tableName = this._calculateUniqueTableName(this.localColumns);
this._loadUserConfig();
this._initUserConfigAutoSave();
}
if (!this.userConfig.columns) {
this.updateUserColumns();
} else {
- this.columns.forEach((c, i) => {
+ this.localColumns.forEach((c, i) => {
c.isHidden = this.userConfig.columns[i].isHidden;
});
}
}
updateUserColumns() {
- this.userConfig.columns = this.columns.map((c) => ({
+ this.userConfig.columns = this.localColumns.map((c) => ({
prop: c.prop,
name: c.name,
isHidden: !!c.isHidden
*/
initCheckboxColumn() {
if (this.selectionType === 'multiClick') {
- this.columns.unshift({
+ this.localColumns.unshift({
prop: undefined,
resizeable: false,
sortable: false,
*/
initExpandCollapseColumn() {
if (this.hasDetails) {
- this.columns.unshift({
+ this.localColumns.unshift({
prop: undefined,
resizeable: false,
sortable: false,
}
filterHiddenColumns() {
- this.tableColumns = this.columns.filter((c) => !c.isHidden);
+ this.tableColumns = this.localColumns.filter((c) => !c.isHidden);
}
initColumnFilters() {
- let filterableColumns = _.filter(this.columns, { filterable: true });
+ let filterableColumns = _.filter(this.localColumns, { filterable: true });
filterableColumns = [...filterableColumns, ...this.extraFilterableColumns];
this.columnFilters = filterableColumns.map((col: CdTableColumn) => {
return {
$event.target.checked = true;
return;
}
- _.find(this.columns, (c: CdTableColumn) => c.prop === prop).isHidden = hide;
+ _.find(this.localColumns, (c: CdTableColumn) => c.prop === prop).isHidden = hide;
this.updateColumns();
}
let rows = this.columnFilters.length !== 0 ? this.doColumnFiltering() : this.data;
if (this.search.length > 0 && rows) {
- const columns = this.columns.filter((c) => c.cellTransformation !== CellTemplate.sparkline);
+ const columns = this.localColumns.filter(
+ (c) => c.cellTransformation !== CellTemplate.sparkline
+ );
// update the rows
rows = this.subSearch(rows, TableComponent.prepareSearch(this.search), columns);
// Whenever the filter changes, always go back to the first page
-import { LOCALE_ID, TRANSLATIONS, TRANSLATIONS_FORMAT, Type } from '@angular/core';
+import { DebugElement, LOCALE_ID, TRANSLATIONS, TRANSLATIONS_FORMAT, Type } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AbstractControl } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { configureTestSuite } from 'ng-bullet';
import { BsModalRef } from 'ngx-bootstrap/modal';
+import { NgbNav, NgbNavItem } from '@ng-bootstrap/ng-bootstrap';
import { TableActionsComponent } from '../app/shared/datatable/table-actions/table-actions.component';
import { Icons } from '../app/shared/enum/icons.enum';
import { CdFormGroup } from '../app/shared/forms/cd-form-group';
return rule;
}
}
+
+export class TabHelper {
+ static getNgbNav(fixture: ComponentFixture<any>) {
+ const debugElem: DebugElement = fixture.debugElement;
+ return debugElem.query(By.directive(NgbNav)).injector.get(NgbNav);
+ }
+
+ static getNgbNavItems(fixture: ComponentFixture<any>) {
+ const debugElems = this.getNgbNavItemsDebugElems(fixture);
+ return debugElems.map((de) => de.injector.get(NgbNavItem));
+ }
+
+ static getTextContents(fixture: ComponentFixture<any>) {
+ const debugElems = this.getNgbNavItemsDebugElems(fixture);
+ return debugElems.map((de) => de.nativeElement.textContent);
+ }
+
+ private static getNgbNavItemsDebugElems(fixture: ComponentFixture<any>) {
+ const debugElem: DebugElement = fixture.debugElement;
+ return debugElem.queryAll(By.directive(NgbNavItem));
+ }
+}