From fdd8ec8ca9e38bc0cf9a7b31f232b0322b46782a Mon Sep 17 00:00:00 2001 From: Tiago Melo Date: Fri, 22 May 2020 15:41:58 +0000 Subject: [PATCH] mgr/dashboard: Migrate Tabs from ngx-bootstrap to ng-bootstrap Using ng-bootstrap for Tabs will allow us to easily implement some new features like only loading 1 tab at a time (already implemented here) and saving/restoring last opened tab. Modified the table component to use a clone of the columns list. Making changes directly to columns var was causing problem when the table was loaded a second time. Fixes: https://tracker.ceph.com/issues/45017 Signed-off-by: Tiago Melo --- .../integration/cluster/osds.e2e-spec.ts | 4 +- .../cypress/integration/rgw/daemons.po.ts | 18 +- .../mgr/dashboard/frontend/package-lock.json | 14 +- .../dashboard/frontend/src/app/app.module.ts | 2 - .../src/app/ceph/block/block.module.ts | 4 +- .../iscsi-tabs/iscsi-tabs.component.html | 26 +- .../iscsi-tabs/iscsi-tabs.component.spec.ts | 4 +- .../block/iscsi-tabs/iscsi-tabs.component.ts | 16 +- .../iscsi-target-details.component.ts | 10 +- .../iscsi-target-list.component.scss | 9 - .../iscsi-target-list.component.spec.ts | 6 +- .../daemon-list/daemon-list.component.spec.ts | 2 - .../image-list/image-list.component.html | 74 +++-- .../image-list/image-list.component.spec.ts | 4 +- .../ceph/block/mirroring/mirroring.module.ts | 4 +- .../overview/overview.component.spec.ts | 4 +- .../pool-list/pool-list.component.spec.ts | 2 - .../rbd-details/rbd-details.component.html | 270 +++++++-------- .../rbd-details/rbd-details.component.spec.ts | 4 +- .../rbd-details/rbd-details.component.ts | 6 + .../block/rbd-list/rbd-list.component.spec.ts | 4 +- .../rbd-namespace-list.component.spec.ts | 4 +- .../rbd-performance.component.spec.ts | 4 +- .../rbd-snapshot-list.component.spec.ts | 4 +- .../block/rbd-tabs/rbd-tabs.component.html | 46 +-- .../block/rbd-tabs/rbd-tabs.component.spec.ts | 4 +- .../ceph/block/rbd-tabs/rbd-tabs.component.ts | 14 +- .../rbd-trash-list.component.spec.ts | 4 +- .../cephfs-tabs/cephfs-tabs.component.html | 80 +++-- .../cephfs-tabs/cephfs-tabs.component.spec.ts | 4 +- .../src/app/ceph/cephfs/cephfs.module.ts | 4 +- .../src/app/ceph/cluster/cluster.module.ts | 5 +- .../configuration-details.component.html | 225 +++++++------ .../configuration-details.component.spec.ts | 4 +- .../configuration.component.spec.ts | 4 +- .../crushmap/crushmap.component.spec.ts | 3 +- .../host-details/host-details.component.html | 81 +++-- .../host-details.component.spec.ts | 16 +- .../host-details/host-details.component.ts | 9 +- .../ceph/cluster/hosts/hosts.component.html | 107 +++--- .../cluster/hosts/hosts.component.spec.ts | 2 - .../app/ceph/cluster/logs/logs.component.html | 71 ++-- .../ceph/cluster/logs/logs.component.spec.ts | 4 +- .../mgr-module-details.component.html | 23 +- .../mgr-module-details.component.spec.ts | 4 +- .../mgr-module-list.component.spec.ts | 4 +- .../cluster/mgr-modules/mgr-modules.module.ts | 4 +- .../osd-details/osd-details.component.html | 158 +++++---- .../osd-details/osd-details.component.spec.ts | 4 +- .../osd/osd-list/osd-list.component.html | 113 ++++--- .../osd/osd-list/osd-list.component.spec.ts | 2 - .../active-alert-list.component.spec.ts | 4 +- .../monitoring-list.component.html | 86 ++--- .../monitoring-list.component.ts | 25 +- .../rules-list/rules-list.component.html | 27 +- .../rules-list/rules-list.component.spec.ts | 4 +- .../silence-list.component.spec.ts | 2 - .../service-details.component.html | 23 +- .../service-details.component.spec.ts | 19 +- .../service-details.component.ts | 12 +- .../app/ceph/dashboard/dashboard.module.ts | 4 +- .../dashboard/dashboard.component.html | 33 +- .../dashboard/dashboard.component.spec.ts | 3 + .../nfs-details/nfs-details.component.html | 48 ++- .../nfs-details/nfs-details.component.spec.ts | 6 +- .../nfs/nfs-list/nfs-list.component.spec.ts | 6 +- .../frontend/src/app/ceph/nfs/nfs.module.ts | 5 +- .../pool-details/pool-details.component.html | 84 +++-- .../pool-details.component.spec.ts | 27 +- .../pool-details/pool-details.component.ts | 5 +- .../pool/pool-form/pool-form.component.html | 134 ++++---- .../pool/pool-form/pool-form.component.scss | 3 - .../pool-form/pool-form.component.spec.ts | 20 +- .../pool/pool-form/pool-form.component.ts | 17 +- .../pool/pool-list/pool-list.component.html | 96 +++--- .../pool-list/pool-list.component.spec.ts | 4 +- .../frontend/src/app/ceph/pool/pool.module.ts | 4 +- .../rgw-bucket-details.component.html | 241 +++++++------- .../rgw-bucket-details.component.spec.ts | 4 +- .../rgw-bucket-list.component.spec.ts | 4 +- .../rgw-daemon-details.component.html | 59 ++-- .../rgw-daemon-details.component.spec.ts | 4 +- .../rgw-daemon-list.component.html | 75 +++-- .../rgw-daemon-list.component.spec.ts | 12 +- .../rgw-user-details.component.html | 307 +++++++++--------- .../rgw-user-details.component.spec.ts | 27 +- .../frontend/src/app/ceph/rgw/rgw.module.ts | 4 +- .../src/app/ceph/shared/ceph-shared.module.ts | 7 +- .../smart-list/smart-list.component.html | 120 ++++--- .../smart-list/smart-list.component.spec.ts | 6 +- .../frontend/src/app/core/auth/auth.module.ts | 4 +- .../role-details/role-details.component.html | 37 ++- .../role-details.component.spec.ts | 4 +- .../role-list/role-list.component.spec.ts | 4 +- .../auth/user-form/user-form.component.ts | 2 +- .../user-list/user-list.component.spec.ts | 4 +- .../auth/user-tabs/user-tabs.component.html | 26 +- .../user-tabs/user-tabs.component.spec.ts | 4 +- .../auth/user-tabs/user-tabs.component.ts | 14 +- .../datatable/table/table.component.spec.ts | 16 +- .../shared/datatable/table/table.component.ts | 36 +- .../frontend/src/testing/unit-test-helper.ts | 25 +- 102 files changed, 1748 insertions(+), 1497 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/osds.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/osds.e2e-spec.ts index 15bc30f1f3b..7b610fb39d4 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/osds.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/cluster/osds.e2e-spec.ts @@ -42,8 +42,8 @@ describe('OSDs page', () => { }); 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', diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/daemons.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/daemons.po.ts index 0ca60665db4..eab4818743f 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/daemons.po.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/daemons.po.ts @@ -5,12 +5,12 @@ export class DaemonsPageHelper extends PageHelper { 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'); } @@ -20,23 +20,15 @@ export class DaemonsPageHelper extends PageHelper { // 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'); } } diff --git a/src/pybind/mgr/dashboard/frontend/package-lock.json b/src/pybind/mgr/dashboard/frontend/package-lock.json index 57b383da505..2f300863c30 100644 --- a/src/pybind/mgr/dashboard/frontend/package-lock.json +++ b/src/pybind/mgr/dashboard/frontend/package-lock.json @@ -12613,6 +12613,12 @@ "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", @@ -12811,8 +12817,7 @@ "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", @@ -13882,11 +13887,6 @@ "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==" } } }, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/app.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/app.module.ts index b50107a8706..350ba718f7c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/app.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/app.module.ts @@ -14,7 +14,6 @@ import { I18n } from '@ngx-translate/i18n-polyfill'; 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'; @@ -47,7 +46,6 @@ export function jwtTokenGetter() { SharedModule, CephModule, BsDropdownModule.forRoot(), - TabsModule.forRoot(), JwtModule.forRoot({ config: { tokenGetter: jwtTokenGetter diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts index 8a483b1395d..c568b0bcd1a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts @@ -3,13 +3,13 @@ import { NgModule } from '@angular/core'; 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'; @@ -60,7 +60,7 @@ import { RbdTrashRestoreModalComponent } from './rbd-trash-restore-modal/rbd-tra MirroringModule, FormsModule, ReactiveFormsModule, - TabsModule.forRoot(), + NgbNavModule, ProgressbarModule.forRoot(), BsDropdownModule.forRoot(), BsDatepickerModule.forRoot(), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.html index e1e43c291a5..3d328cb6bf3 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.html @@ -1,12 +1,14 @@ - - - - - - + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.spec.ts index e51c9fad306..456c930b0ab 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.spec.ts @@ -1,7 +1,7 @@ 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'; @@ -12,7 +12,7 @@ describe('IscsiTabsComponent', () => { let fixture: ComponentFixture; configureTestBed({ - imports: [SharedModule, TabsModule.forRoot(), RouterTestingModule], + imports: [SharedModule, RouterTestingModule, NgbNavModule], declarations: [IscsiTabsComponent] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.ts index 06835d3c827..6f729bc8384 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-tabs/iscsi-tabs.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { Router } from '@angular/router'; @@ -7,16 +7,6 @@ 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) {} } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts index 42d9ccefbd3..a4b883fcb6c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-details/iscsi-target-details.component.ts @@ -280,7 +280,7 @@ export class IscsiTargetDetailsComponent implements OnChanges, OnInit { 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 { @@ -300,7 +300,7 @@ export class IscsiTargetDetailsComponent implements OnChanges, OnInit { }); } } 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 { @@ -326,7 +326,7 @@ export class IscsiTargetDetailsComponent implements OnChanges, OnInit { } }); } else { - this.columns[2].isHidden = true; + this.detailTable?.toggleColumn({ target: { name: 'default', checked: false } }); this.data = _.map(tempData, (value, key) => { return { displayName: key, @@ -339,9 +339,7 @@ export class IscsiTargetDetailsComponent implements OnChanges, OnInit { this.data = undefined; } - if (this.detailTable) { - this.detailTable.updateColumns(); - } + this.detailTable?.updateColumns(); } onUpdateData() { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.scss index 1335bf2d9f8..e69de29bb2d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.scss +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.scss @@ -1,9 +0,0 @@ -::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; -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.spec.ts index 50d636febe5..f8ba0fe77f7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.spec.ts @@ -3,8 +3,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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'; @@ -40,9 +40,9 @@ describe('IscsiTargetListComponent', () => { HttpClientTestingModule, RouterTestingModule, SharedModule, - TabsModule.forRoot(), TreeModule, - ToastrModule.forRoot() + ToastrModule.forRoot(), + NgbNavModule ], declarations: [IscsiTargetListComponent, IscsiTabsComponent, IscsiTargetDetailsComponent], providers: [TaskListService, i18nProviders] diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/daemon-list/daemon-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/daemon-list/daemon-list.component.spec.ts index fb427fd6431..3bd0d8c899c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/daemon-list/daemon-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/daemon-list/daemon-list.component.spec.ts @@ -4,7 +4,6 @@ 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 { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper'; import { SharedModule } from '../../../../shared/shared.module'; @@ -21,7 +20,6 @@ describe('DaemonListComponent', () => { BrowserAnimationsModule, SharedModule, BsDropdownModule.forRoot(), - TabsModule.forRoot(), ProgressbarModule.forRoot(), HttpClientTestingModule ], diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/image-list/image-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/image-list/image-list.component.html index b8278b48653..51f82e647af 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/image-list/image-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/image-list/image-list.component.html @@ -1,35 +1,45 @@ - - - - - - - - - - - - - - + + +
{ BrowserAnimationsModule, SharedModule, BsDropdownModule.forRoot(), - TabsModule.forRoot(), + NgbNavModule, ProgressbarModule.forRoot(), HttpClientTestingModule ], diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/mirroring.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/mirroring.module.ts index a051cf99b0f..b15b69a6c33 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/mirroring.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/mirroring.module.ts @@ -3,12 +3,12 @@ import { NgModule } from '@angular/core'; 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'; @@ -35,8 +35,8 @@ import { PoolListComponent } from './pool-list/pool-list.component'; ], imports: [ CommonModule, - TabsModule.forRoot(), SharedModule, + NgbNavModule, RouterModule, FormsModule, ReactiveFormsModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.spec.ts index fc1cd20fc9c..d3e06ecb3e3 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/overview/overview.component.spec.ts @@ -3,9 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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'; @@ -32,7 +32,7 @@ describe('OverviewComponent', () => { BrowserAnimationsModule, SharedModule, BsDropdownModule.forRoot(), - TabsModule.forRoot(), + NgbNavModule, ProgressbarModule.forRoot(), HttpClientTestingModule, RouterTestingModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/pool-list/pool-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/pool-list/pool-list.component.spec.ts index 06e21fc0d9f..a9be82b7633 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/pool-list/pool-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/mirroring/pool-list/pool-list.component.spec.ts @@ -5,7 +5,6 @@ import { RouterTestingModule } from '@angular/router/testing'; 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'; @@ -23,7 +22,6 @@ describe('PoolListComponent', () => { BrowserAnimationsModule, SharedModule, BsDropdownModule.forRoot(), - TabsModule.forRoot(), ProgressbarModule.forRoot(), HttpClientTestingModule, RouterTestingModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html index fee853628f3..6189e97ebf2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.html @@ -2,134 +2,149 @@ Only available for RBD images with fast-diff enabled - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name{{ selection.name }}
Pool{{ selection.pool_name }}
Data Pool{{ selection.data_pool | empty }}
Created{{ selection.timestamp | cdDate }}
Size{{ selection.size | dimlessBinary }}
Objects{{ selection.num_objs | dimless }}
Object size{{ selection.obj_size | dimlessBinary }}
Features - - {{ feature }} - -
Provisioned - - N/A - - - {{ selection.disk_usage | dimlessBinary }} - -
Total provisioned - - N/A - - - {{ selection.total_disk_usage | dimlessBinary }} - -
Striping unit{{ selection.stripe_unit | dimlessBinary }}
Striping count{{ selection.stripe_count }}
Parent - {{ selection.parent.pool_name }}/{{ selection.parent.pool_namespace }}/{{ selection.parent.image_name }}@{{ selection.parent.snap_name }} - - -
Block name prefix{{ selection.block_name_prefix }}
Order{{ selection.order }}
-
- - - - - - -
+ + - +
+
+ + Global - diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.spec.ts index 44646595e81..c555cb10254 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.spec.ts @@ -1,7 +1,7 @@ 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'; @@ -16,7 +16,7 @@ describe('RbdDetailsComponent', () => { configureTestBed({ declarations: [RbdDetailsComponent, RbdSnapshotListComponent, RbdConfigurationListComponent], - imports: [SharedModule, TabsModule.forRoot(), TooltipModule.forRoot(), RouterTestingModule] + imports: [SharedModule, TooltipModule.forRoot(), RouterTestingModule, NgbNavModule] }); beforeEach(() => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.ts index 0916391e066..46f95d1449c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-details/rbd-details.component.ts @@ -1,5 +1,7 @@ import { Component, Input, TemplateRef, ViewChild } from '@angular/core'; +import { NgbNav } from '@ng-bootstrap/ng-bootstrap'; + import { RbdFormModel } from '../rbd-form/rbd-form.model'; @Component({ @@ -12,8 +14,12 @@ export class RbdDetailsComponent { selection: RbdFormModel; @Input() images: any; + @ViewChild('poolConfigurationSourceTpl', { static: true }) poolConfigurationSourceTpl: TemplateRef; + @ViewChild(NgbNav, { static: true }) + nav: NgbNav; + constructor() {} } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts index f9ad4cf7cb7..4a63ed0f973 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts @@ -3,10 +3,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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'; @@ -46,7 +46,7 @@ describe('RbdListComponent', () => { BrowserAnimationsModule, SharedModule, BsDropdownModule.forRoot(), - TabsModule.forRoot(), + NgbNavModule, ModalModule.forRoot(), TooltipModule.forRoot(), ToastrModule.forRoot(), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-namespace-list/rbd-namespace-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-namespace-list/rbd-namespace-list.component.spec.ts index 7abb507ca4d..294a598e251 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-namespace-list/rbd-namespace-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-namespace-list/rbd-namespace-list.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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'; @@ -24,7 +24,7 @@ describe('RbdNamespaceListComponent', () => { HttpClientTestingModule, RouterTestingModule, ToastrModule.forRoot(), - TabsModule.forRoot() + NgbNavModule ], providers: [TaskListService, i18nProviders] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-performance/rbd-performance.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-performance/rbd-performance.component.spec.ts index 1bb1e9b0cbf..bec05b924ef 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-performance/rbd-performance.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-performance/rbd-performance.component.spec.ts @@ -2,7 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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'; @@ -14,7 +14,7 @@ describe('RbdPerformanceComponent', () => { let fixture: ComponentFixture; configureTestBed({ - imports: [HttpClientTestingModule, RouterTestingModule, SharedModule, TabsModule.forRoot()], + imports: [HttpClientTestingModule, RouterTestingModule, SharedModule, NgbNavModule], declarations: [RbdPerformanceComponent, RbdTabsComponent], providers: i18nProviders }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts index c032d0d522b..9adf9c3b703 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts @@ -3,9 +3,9 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testin 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'; @@ -57,7 +57,7 @@ describe('RbdSnapshotListComponent', () => { HttpClientTestingModule, PipesModule, RouterTestingModule, - TabsModule.forRoot(), + NgbNavModule, ToastrModule.forRoot() ], providers: [ diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.html index bc241a5bfde..657568c2230 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.html @@ -1,23 +1,23 @@ - - - - - - - - - - + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.spec.ts index 7d7b15d0fdd..52065ff5f09 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.spec.ts @@ -1,7 +1,7 @@ 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'; @@ -11,7 +11,7 @@ describe('RbdTabsComponent', () => { let fixture: ComponentFixture; configureTestBed({ - imports: [TabsModule.forRoot(), RouterTestingModule], + imports: [RouterTestingModule, NgbNavModule], declarations: [RbdTabsComponent] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.ts index 36923dffc78..0f77d14731d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-tabs/rbd-tabs.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { Permission } from '../../../shared/models/permissions'; @@ -9,19 +9,11 @@ import { AuthStorageService } from '../../../shared/services/auth-storage.servic 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]); - } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.spec.ts index 557ab5ec76b..3706a9ca50f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.spec.ts @@ -4,8 +4,8 @@ import { By } from '@angular/platform-browser'; 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'; @@ -37,7 +37,7 @@ describe('RbdTrashListComponent', () => { HttpClientTestingModule, RouterTestingModule, SharedModule, - TabsModule.forRoot(), + NgbNavModule, ToastrModule.forRoot() ], providers: [TaskListService, i18nProviders] diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.html index cd3800af5e1..1dd6b430475 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.html @@ -1,34 +1,48 @@ - - - - - - - - Clients - {{ clients.data.length }} - - - - - - - - - - - - - + + +
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.spec.ts index 28886337467..c026306cc69 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-tabs/cephfs-tabs.component.spec.ts @@ -2,9 +2,9 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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'; @@ -80,7 +80,7 @@ describe('CephfsTabsComponent', () => { configureTestBed({ imports: [ SharedModule, - TabsModule.forRoot(), + NgbNavModule, HttpClientTestingModule, TreeModule, ToastrModule.forRoot() diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts index 4601d5b093e..24a1cd98340 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs.module.ts @@ -1,10 +1,10 @@ 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'; @@ -23,7 +23,7 @@ import { CephfsTabsComponent } from './cephfs-tabs/cephfs-tabs.component'; ChartsModule, TreeModule.forRoot(), ProgressbarModule.forRoot(), - TabsModule.forRoot() + NgbNavModule ], declarations: [ CephfsDetailComponent, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts index 3bc76953b81..45a620aff1e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts @@ -3,14 +3,13 @@ import { NgModule } from '@angular/core'; 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'; @@ -70,7 +69,7 @@ import { TelemetryComponent } from './telemetry/telemetry.component'; imports: [ CommonModule, PerformanceCounterModule, - TabsModule.forRoot(), + NgbNavModule, SharedModule, RouterModule, FormsModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-details/configuration-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-details/configuration-details.component.html index e690b4a91c1..93c375d70d6 100755 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-details/configuration-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-details/configuration-details.component.html @@ -1,108 +1,117 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name{{ selection.name }}
Description{{ selection.desc }}
Long description{{ selection.long_desc }}
Current values - - {{ conf.section }}: {{ conf.value }}{{ !isLast ? "," : "" }}
-
-
Default{{ selection.default }}
Daemon default{{ selection.daemon_default }}
Type{{ selection.type }}
Min{{ selection.min }}
Max{{ selection.max }}
Flags - - - {{ flag | uppercase }} - - -
Services - - {{ service }} - -
Source{{ selection.source }}
Level{{ selection.level }}
Can be updated at runtime (editable){{ selection.can_update_at_runtime | booleanText }}
Tags{{ selection.tags }}
Enum values{{ selection.enum_values }}
See also{{ selection.see_also }}
-
-
+ + + +
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-details/configuration-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-details/configuration-details.component.spec.ts index 8c50cb8fb31..f94224736a9 100755 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-details/configuration-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration-details/configuration-details.component.spec.ts @@ -1,6 +1,6 @@ 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'; @@ -13,7 +13,7 @@ describe('ConfigurationDetailsComponent', () => { configureTestBed({ declarations: [ConfigurationDetailsComponent], - imports: [DataTableModule, SharedModule, TabsModule.forRoot()], + imports: [DataTableModule, SharedModule, NgbNavModule], providers: [i18nProviders] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration.component.spec.ts index 817ce39c3bb..32cee6c2a3f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/configuration/configuration.component.spec.ts @@ -5,7 +5,7 @@ import { By } from '@angular/platform-browser'; 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'; @@ -22,7 +22,7 @@ describe('ConfigurationComponent', () => { BrowserAnimationsModule, SharedModule, FormsModule, - TabsModule.forRoot(), + NgbNavModule, HttpClientTestingModule, RouterTestingModule ], diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/crushmap/crushmap.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/crushmap/crushmap.component.spec.ts index a6706ddfebf..94331a6bf6d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/crushmap/crushmap.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/crushmap/crushmap.component.spec.ts @@ -3,7 +3,6 @@ import { DebugElement } from '@angular/core'; 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'; @@ -16,7 +15,7 @@ describe('CrushmapComponent', () => { let fixture: ComponentFixture; let debugElement: DebugElement; configureTestBed({ - imports: [HttpClientTestingModule, TreeModule.forRoot(), TabsModule.forRoot(), SharedModule], + imports: [HttpClientTestingModule, TreeModule.forRoot(), SharedModule], declarations: [CrushmapComponent], providers: [HealthService] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.html index ef072c8af68..6c71d59b93e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.html @@ -1,33 +1,54 @@ - - - - - - - - - - - - - - - - - - - + + + +
+
{ imports: [ BrowserAnimationsModule, HttpClientTestingModule, - TabsModule.forRoot(), BsDropdownModule.forRoot(), NgBootstrapFormValidationModule.forRoot(), RouterTestingModule, @@ -59,17 +61,17 @@ describe('HostDetailsComponent', () => { }); 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' ]); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.ts index d8c50ff3dfd..15ef44b2a2c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.ts @@ -1,6 +1,4 @@ -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'; @@ -16,12 +14,7 @@ export class HostDetailsComponent { @Input() selection: any; - @ViewChild(TabsetComponent) - tabsetChild: TabsetComponent; - get selectedHostname(): string { return this.selection !== undefined ? this.selection['hostname'] : null; } - - constructor() {} } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.html index fa67d8c59de..5d94dd5ab2e 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.html @@ -1,48 +1,59 @@ - - - -
- - -
- - - {{ service.type }}.{{ service.id }} - - - {{ service.type }}.{{ service.id }} - - {{ !isLast ? ", " : "" }} - - - - -
-
- - - - -
+ + +
+ + + + {{ service.type }}.{{ service.id }} + + + {{ service.type }}.{{ service.id }} + + {{ !isLast ? ", " : "" }} + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.spec.ts index 1844f70ab03..6fe6984dbfe 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.spec.ts @@ -4,7 +4,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 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'; @@ -35,7 +34,6 @@ describe('HostsComponent', () => { CephSharedModule, SharedModule, HttpClientTestingModule, - TabsModule.forRoot(), BsDropdownModule.forRoot(), RouterTestingModule, ToastrModule.forRoot(), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.html index 00be424f378..a5900e50201 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.html @@ -1,41 +1,50 @@
- - -
-
-

- {{ line.stamp | cdDate }} - {{ line.priority }} - {{ line.message }} -

+
- - - -
-
-

- {{ line.stamp | cdDate }} - {{ line.priority }} - {{ line.message }} -

+ + +
  • + Audit Logs + +
    +
    +

    + {{ line.stamp | cdDate }} + {{ line.priority }} + {{ line.message }} +

    - No entries found + No entries found +
    -
  • - - + + + + +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.spec.ts index e4f69fc920d..31cbc790b50 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.spec.ts @@ -2,8 +2,8 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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'; @@ -17,7 +17,7 @@ describe('LogsComponent', () => { configureTestBed({ imports: [ HttpClientTestingModule, - TabsModule.forRoot(), + NgbNavModule, SharedModule, BsDatepickerModule.forRoot(), TimepickerModule.forRoot(), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-details/mgr-module-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-details/mgr-module-details.component.html index 6ed9b65da39..861575d0a14 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-details/mgr-module-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-details/mgr-module-details.component.html @@ -1,7 +1,16 @@ - - - - - - + + + +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-details/mgr-module-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-details/mgr-module-details.component.spec.ts index 1e0575e71a6..d28861eeedf 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-details/mgr-module-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-details/mgr-module-details.component.spec.ts @@ -1,7 +1,7 @@ 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'; @@ -13,7 +13,7 @@ describe('MgrModuleDetailsComponent', () => { configureTestBed({ declarations: [MgrModuleDetailsComponent], - imports: [HttpClientTestingModule, SharedModule, TabsModule.forRoot()], + imports: [HttpClientTestingModule, SharedModule, NgbNavModule], providers: [i18nProviders] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-list/mgr-module-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-list/mgr-module-list.component.spec.ts index 1072a9e449e..de3f0acbe19 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-list/mgr-module-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-module-list/mgr-module-list.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testin 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'; @@ -33,7 +33,7 @@ describe('MgrModuleListComponent', () => { RouterTestingModule, SharedModule, HttpClientTestingModule, - TabsModule.forRoot(), + NgbNavModule, ToastrModule.forRoot() ], providers: [MgrModuleService, NotificationService, i18nProviders] diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-modules.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-modules.module.ts index 9dd94c36055..e78136e4ceb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-modules.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/mgr-modules/mgr-modules.module.ts @@ -2,8 +2,8 @@ import { CommonModule } from '@angular/common'; 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'; @@ -17,7 +17,7 @@ import { MgrModuleListComponent } from './mgr-module-list/mgr-module-list.compon CommonModule, ReactiveFormsModule, SharedModule, - TabsModule.forRoot(), + NgbNavModule, NgBootstrapFormValidationModule ], declarations: [MgrModuleListComponent, MgrModuleFormComponent, MgrModuleDetailsComponent] diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-details/osd-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-details/osd-details.component.html index 3430d00b48a..569062d66ff 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-details/osd-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-details/osd-details.component.html @@ -1,71 +1,91 @@ - - - - + + - - - - - Metadata not available - - - - - - - - - - - - - - Histogram not available: {{ osd.histogram_failed }} - -
    -
    -

    Writes

    - - -
    -
    -

    Reads

    - - -
    -
    -
    - - - - - - -
    +
    + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-details/osd-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-details/osd-details.component.spec.ts index 0a9bc9d49a0..0ed14a1aea9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-details/osd-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-details/osd-details.component.spec.ts @@ -2,7 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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'; @@ -22,7 +22,7 @@ describe('OsdDetailsComponent', () => { let getDetailsSpy: jasmine.Spy; configureTestBed({ - imports: [HttpClientTestingModule, TabsModule.forRoot(), SharedModule], + imports: [HttpClientTestingModule, NgbNavModule, SharedModule], declarations: [ OsdDetailsComponent, DeviceListComponent, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html index 196a07e2ffb..d1a5fb653dd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html @@ -1,60 +1,63 @@ - - + + +
    OSD(s) {{ getSelectedOsdIds() | join }} will be marked -{{ markActionDescription }} if you proceed. + {{ markActionDescription }} if you proceed. The {selection.hasSingleSelection, select, 1 {OSD is} 0 {OSDs are}} not safe to be {{ actionDescription }}! {{ message }} + i18n>The {selection.hasSingleSelection, select, 1 {OSD is} 0 {OSDs are}} not safe to be + {{ actionDescription }}! {{ message }}
    OSD {{ getSelectedOsdIds() | join }} will be -{{ actionDescription }} if you proceed. + {{ actionDescription }} if you proceed. + + + + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.spec.ts index 22ac804fa82..43ad9f5938a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.spec.ts @@ -7,7 +7,6 @@ import { RouterTestingModule } from '@angular/router/testing'; 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'; @@ -93,7 +92,6 @@ describe('OsdListComponent', () => { BrowserAnimationsModule, HttpClientTestingModule, PerformanceCounterModule, - TabsModule.forRoot(), ToastrModule.forRoot(), CephModule, ReactiveFormsModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts index ba736415afc..c551d599bb2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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 { @@ -27,7 +27,7 @@ describe('ActiveAlertListComponent', () => { imports: [ BrowserAnimationsModule, HttpClientTestingModule, - TabsModule.forRoot(), + NgbNavModule, RouterTestingModule, ToastrModule.forRoot(), SharedModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html index f54c1fe515f..d63d70dd42a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html @@ -1,38 +1,48 @@ - - - - To see all active Prometheus alerts, please - provide the URL to the API of Prometheus' Alertmanager as described in the - documentation. - - - - To see all configured Prometheus alerts, please provide the URL to - the API of Prometheus as described in the - documentation. - - - - To enable Silences, please provide the URL to the API of the Prometheus' Alertmanager as - described in the - documentation. - - + + +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts index e08e235fe75..03325f530e0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts @@ -1,8 +1,6 @@ -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'; @@ -17,14 +15,11 @@ export class MonitoringListComponent implements OnInit { 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; @@ -42,21 +37,9 @@ export class MonitoringListComponent implements OnInit { 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 }); } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.component.html index c0050ac7dae..8a23ed67ebf 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.component.html @@ -3,13 +3,22 @@ [hasDetails]="true" (updateSelection)="setExpandedRow($event)" [selectionType]="'single'"> - - - - - + + + +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.component.spec.ts index 27581aae7dc..74073fcf081 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.component.spec.ts @@ -2,7 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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'; @@ -16,7 +16,7 @@ describe('RulesListComponent', () => { configureTestBed({ declarations: [RulesListComponent], - imports: [HttpClientTestingModule, SharedModule, TabsModule.forRoot(), BrowserAnimationsModule], + imports: [HttpClientTestingModule, SharedModule, NgbNavModule, BrowserAnimationsModule], providers: [PrometheusService, SettingsService, i18nProviders] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.spec.ts index 6a60f83f6e4..9e7921b308f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.spec.ts @@ -5,7 +5,6 @@ import { RouterTestingModule } from '@angular/router/testing'; 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'; @@ -32,7 +31,6 @@ describe('SilenceListComponent', () => { BrowserAnimationsModule, SharedModule, BsDropdownModule.forRoot(), - TabsModule.forRoot(), ModalModule.forRoot(), ToastrModule.forRoot(), RouterTestingModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.html index 9849ccad22e..46d3affd0d0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.html @@ -1,7 +1,16 @@ - - - - - - + + + +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.spec.ts index 97dfe0d03a8..dc2ec0b66c2 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.spec.ts @@ -2,9 +2,13 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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'; @@ -16,7 +20,7 @@ describe('ServiceDetailsComponent', () => { let fixture: ComponentFixture; configureTestBed({ - imports: [HttpClientTestingModule, RouterTestingModule, TabsModule.forRoot(), SharedModule], + imports: [HttpClientTestingModule, RouterTestingModule, SharedModule, NgbNavModule], declarations: [ServiceDetailsComponent, ServiceDaemonListComponent], providers: [ i18nProviders, @@ -46,13 +50,14 @@ describe('ServiceDetailsComponent', () => { 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']); }); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.ts index 4f16127e25a..09eb4d2f599 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.ts @@ -1,5 +1,4 @@ -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'; @@ -9,17 +8,10 @@ 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() {} } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts index 49630da4ec5..60134defb33 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard.module.ts @@ -2,9 +2,9 @@ import { CommonModule } from '@angular/common'; 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'; @@ -22,7 +22,7 @@ import { OsdSummaryPipe } from './osd-summary.pipe'; imports: [ CephSharedModule, CommonModule, - TabsModule.forRoot(), + NgbNavModule, SharedModule, ChartsModule, RouterModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.html index c59717c524f..2d03ea3e6e9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.html @@ -1,13 +1,28 @@
    - - - - - - - + + + + +
    +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.spec.ts index 079ae3e1cfb..468719ea957 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/dashboard/dashboard.component.spec.ts @@ -1,6 +1,8 @@ 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'; @@ -9,6 +11,7 @@ describe('DashboardComponent', () => { let fixture: ComponentFixture; configureTestBed({ + imports: [NgbNavModule], declarations: [DashboardComponent], schemas: [NO_ERRORS_SCHEMA] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-details/nfs-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-details/nfs-details.component.html index 5bb97e5eb22..4fc2aa2c9cd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-details/nfs-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-details/nfs-details.component.html @@ -1,19 +1,31 @@ - - - - - + + + +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-details/nfs-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-details/nfs-details.component.spec.ts index f1f3b49b683..9b8f7da73e0 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-details/nfs-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-details/nfs-details.component.spec.ts @@ -3,8 +3,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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'; @@ -18,7 +18,7 @@ describe('NfsDetailsComponent', () => { configureTestBed({ declarations: [NfsDetailsComponent], - imports: [BrowserAnimationsModule, SharedModule, TabsModule.forRoot(), HttpClientTestingModule], + imports: [BrowserAnimationsModule, SharedModule, HttpClientTestingModule, NgbNavModule], providers: i18nProviders }); @@ -98,7 +98,7 @@ describe('NfsDetailsComponent', () => { }); 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', diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.spec.ts index b4811722a85..e789046148b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-list/nfs-list.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testin 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'; @@ -41,8 +41,8 @@ describe('NfsListComponent', () => { HttpClientTestingModule, RouterTestingModule, SharedModule, - ToastrModule.forRoot(), - TabsModule.forRoot() + NgbNavModule, + ToastrModule.forRoot() ], providers: [TaskListService, i18nProviders] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs.module.ts index ac66522bb44..568e47280f1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs.module.ts @@ -3,9 +3,8 @@ import { NgModule } from '@angular/core'; 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'; @@ -19,7 +18,7 @@ import { NfsListComponent } from './nfs-list/nfs-list.component'; ReactiveFormsModule, RouterModule, SharedModule, - TabsModule.forRoot(), + NgbNavModule, CommonModule, NgbTypeaheadModule, NgBootstrapFormValidationModule diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.html index a1515e02b78..c5a8a0b1a40 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.html @@ -1,34 +1,50 @@ - - - - - - - - - - - - - - - - - + + + +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.spec.ts index f6330b2d034..9bb3f2422df 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.spec.ts @@ -3,9 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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'; @@ -18,7 +18,7 @@ describe('PoolDetailsComponent', () => { configureTestBed({ imports: [ BrowserAnimationsModule, - TabsModule.forRoot(), + NgbNavModule, SharedModule, HttpClientTestingModule, RouterTestingModule @@ -51,16 +51,17 @@ describe('PoolDetailsComponent', () => { 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"', () => { @@ -68,17 +69,19 @@ describe('PoolDetailsComponent', () => { 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(); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.ts index 9deb29198d3..bf517a84dcc 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-details/pool-details.component.ts @@ -1,8 +1,7 @@ -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'; @@ -23,8 +22,6 @@ export class PoolDetailsComponent implements OnChanges { permissions: Permissions; @Input() cacheTiers: any[]; - @ViewChild(TabsetComponent) - tabsetChild: TabsetComponent; selectedPoolConfiguration: RbdConfigurationEntry[]; constructor(private i18n: I18n, private poolService: PoolService) { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html index 1de6332b70c..8e6fd0aa6b3 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html @@ -246,29 +246,37 @@ - - - - - - - - Profile is not in use. + + +
    @@ -337,42 +345,54 @@ - - - - - - - -
      -
    1. - {{ describeCrushStep(step) }} -
    2. -
    -
    - - - Rule is not in use. + +
    + + +
    +
    This field is required! diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.scss index 29a4cf72e9b..e69de29bb2d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.scss +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.scss @@ -1,3 +0,0 @@ -.crush-rule-steps { - margin-top: 10px; -} diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.spec.ts index 9d91e08dd6a..50bfb2359e1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.spec.ts @@ -2,17 +2,17 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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, @@ -137,7 +137,7 @@ describe('PoolFormComponent', () => { HttpClientTestingModule, RouterTestingModule.withRoutes(routes), ToastrModule.forRoot(), - TabsModule.forRoot(), + NgbNavModule, PoolModule, NgBootstrapFormValidationModule.forRoot() ], @@ -888,7 +888,6 @@ describe('PoolFormComponent', () => { 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(); @@ -900,12 +899,6 @@ describe('PoolFormComponent', () => { 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); @@ -1051,7 +1044,6 @@ describe('PoolFormComponent', () => { 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(); @@ -1067,12 +1059,6 @@ describe('PoolFormComponent', () => { 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); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts index 8bdc4f06622..b06f6d054a4 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts @@ -2,10 +2,11 @@ import { Component, EventEmitter, OnInit, Type, ViewChild } from '@angular/core' 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'; @@ -54,9 +55,9 @@ interface FormFieldDescription { 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; @@ -632,7 +633,7 @@ export class PoolFormComponent extends CdForm implements OnInit { 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(), @@ -658,8 +659,8 @@ export class PoolFormComponent extends CdForm implements OnInit { usage: string[]; deletionBtn: TooltipDirective; dataName: string; - getTabs: () => TabsetComponent; - tabPosition: number; + getTabs: () => NgbNav; + tabPosition: string; nameAttribute: string; itemDescription: string; reloadFn: Function; @@ -675,7 +676,7 @@ export class PoolFormComponent extends CdForm implements OnInit { setTimeout(() => { const tabs = getTabs(); if (tabs) { - tabs.tabs[tabPosition].active = true; + tabs.select(tabPosition); } }, 50); return; @@ -722,7 +723,7 @@ export class PoolFormComponent extends CdForm implements OnInit { 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(), diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.html index 8d6205817d8..02a98a5de7c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.html @@ -1,47 +1,57 @@ - - - + + +
    - - - - -
    + + + + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts index dbe4ec6f209..43b4cd74b9d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts @@ -3,9 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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'; @@ -54,7 +54,7 @@ describe('PoolListComponent', () => { SharedModule, ToastrModule.forRoot(), RouterTestingModule, - TabsModule.forRoot(), + NgbNavModule, HttpClientTestingModule ], providers: [i18nProviders, PgCategoryService] diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.module.ts index 98fd4360a0e..566b33c5996 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool.module.ts @@ -3,10 +3,10 @@ import { NgModule } from '@angular/core'; 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'; @@ -23,7 +23,7 @@ import { PoolListComponent } from './pool-list/pool-list.component'; imports: [ CephSharedModule, CommonModule, - TabsModule, + NgbNavModule, PopoverModule.forRoot(), SharedModule, RouterModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html index 761c41b32b0..1bb5bca2c61 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.html @@ -1,137 +1,144 @@ - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Name{{ selection.bid }}
    ID{{ selection.id }}
    Owner{{ selection.owner }}
    Index type{{ selection.index_type }}
    Placement rule{{ selection.placement_rule }}
    Marker{{ selection.marker }}
    Maximum marker{{ selection.max_marker }}
    Version{{ selection.ver }}
    Master version{{ selection.master_ver }}
    Modification time{{ selection.mtime | cdDate }}
    Zonegroup{{ selection.zonegroup }}
    Versioning{{ selection.versioning }}
    MFA Delete{{ selection.mfa_delete }}
    - - -
    - Bucket quota + +
    - - - Locking - - - - - - - - + class="bold">Master version + - + class="bold">Modification time + - + class="bold">Zonegroup + - - -
    Enabled{{ selection.lock_enabled | booleanText }}
    Mode{{ selection.lock_mode }}{{ selection.master_ver }}
    Days{{ selection.lock_retention_period_days }}{{ selection.mtime | cdDate }}
    Years{{ selection.lock_retention_period_years }}{{ selection.zonegroup }}
    -
    -
    -
    + + Versioning + {{ selection.versioning }} + + + MFA Delete + {{ selection.mfa_delete }} + + + + + +
    + Bucket quota + + + + + + + + + + + + + + + + + +
    Enabled{{ selection.bucket_quota.enabled | booleanText }}
    Maximum sizeUnlimited + {{ selection.bucket_quota.max_size | dimless }} +
    Maximum objectsUnlimited + {{ selection.bucket_quota.max_objects }} +
    +
    + + + Locking + + + + + + + + + + + + + + + + + + + + + +
    Enabled{{ selection.lock_enabled | booleanText }}
    Mode{{ selection.lock_mode }}
    Days{{ selection.lock_retention_period_days }}
    Years{{ selection.lock_retention_period_years }}
    +
    + + + +
    + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.spec.ts index a2ede8d3f3f..3fa79f21050 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-details/rgw-bucket-details.component.spec.ts @@ -1,6 +1,6 @@ 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'; @@ -13,7 +13,7 @@ describe('RgwBucketDetailsComponent', () => { configureTestBed({ declarations: [RgwBucketDetailsComponent], - imports: [SharedModule, TabsModule.forRoot()], + imports: [SharedModule, NgbNavModule], providers: [i18nProviders] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.spec.ts index 98cd5fed526..435a151ad38 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.spec.ts @@ -3,8 +3,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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, @@ -27,7 +27,7 @@ describe('RgwBucketListComponent', () => { RouterTestingModule, ModalModule.forRoot(), SharedModule, - TabsModule.forRoot(), + NgbNavModule, HttpClientTestingModule ], providers: i18nProviders diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html index b7cea04f21c..77292949b2a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.html @@ -1,22 +1,37 @@ - - - - - - - - - - - - - - + + + +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.spec.ts index f185f2133d9..3d2709cdfc1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-details/rgw-daemon-details.component.spec.ts @@ -1,7 +1,7 @@ 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'; @@ -14,7 +14,7 @@ describe('RgwDaemonDetailsComponent', () => { configureTestBed({ declarations: [RgwDaemonDetailsComponent], - imports: [SharedModule, PerformanceCounterModule, TabsModule.forRoot(), HttpClientTestingModule] + imports: [SharedModule, PerformanceCounterModule, HttpClientTestingModule, NgbNavModule] }); beforeEach(() => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.html index 71d9ab67c94..38683a6f6d8 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.html @@ -1,33 +1,46 @@ - - - - - - - + + +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.spec.ts index 19aacc0acd9..49f41bc6031 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.spec.ts @@ -3,10 +3,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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'; @@ -21,11 +21,11 @@ describe('RgwDaemonListComponent', () => { 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({ @@ -33,7 +33,7 @@ describe('RgwDaemonListComponent', () => { imports: [ BrowserAnimationsModule, HttpClientTestingModule, - TabsModule.forRoot(), + NgbNavModule, PerformanceCounterModule, SharedModule, RouterTestingModule diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html index e4080a26362..c081756061a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.html @@ -1,151 +1,162 @@ - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Username{{ user.uid }}
    Full name{{ user.display_name }}
    Email address{{ user.email }}
    Suspended{{ user.suspended | booleanText }}
    System{{ user.system === 'true' | booleanText }}
    Maximum buckets{{ user.max_buckets | map:maxBucketsMap }}
    Subusers -
    - {{ subuser.id }} ({{ subuser.permissions }}) -
    -
    Capabilities -
    - {{ cap.type }} ({{ cap.perm }}) -
    -
    + + + +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.spec.ts index 1b29228c38c..11a647690c3 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-details/rgw-user-details.component.spec.ts @@ -2,10 +2,10 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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'; @@ -16,7 +16,7 @@ describe('RgwUserDetailsComponent', () => { configureTestBed({ declarations: [RgwUserDetailsComponent], - imports: [BrowserAnimationsModule, HttpClientTestingModule, SharedModule, TabsModule.forRoot()], + imports: [BrowserAnimationsModule, HttpClientTestingModule, SharedModule, NgbNavModule], providers: [BsModalService, i18nProviders] }); @@ -30,20 +30,18 @@ describe('RgwUserDetailsComponent', () => { 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', () => { @@ -52,10 +50,9 @@ describe('RgwUserDetailsComponent', () => { 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', () => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts index 62f849d7bc2..0429da0f53d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts @@ -3,11 +3,11 @@ import { NgModule } from '@angular/core'; 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'; @@ -46,7 +46,7 @@ import { RgwUserSwiftKeyModalComponent } from './rgw-user-swift-key-modal/rgw-us PerformanceCounterModule, AlertModule.forRoot(), BsDropdownModule.forRoot(), - TabsModule.forRoot(), + NgbNavModule, TooltipModule.forRoot(), ModalModule.forRoot(), RouterModule, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/ceph-shared.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/ceph-shared.module.ts index 9f523ca232b..2ca947a6120 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/ceph-shared.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/ceph-shared.module.ts @@ -1,12 +1,15 @@ 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] }) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html index 3bc29c518dc..2179c049d34 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.html @@ -4,63 +4,85 @@ i18n>Failed to retrieve SMART data. The data received has the JSON format version 2.x and is currently incompatible with the dashboard. + i18n>The data received has the JSON format version 2.x and is currently incompatible with the + dashboard. No SMART data available. - - - - {{ device.value.userMessage }} - - - - - passed - - - failed - + + - - - - No SMART data available for this device. - - - - - +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts index 395608b3432..482200041e1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/shared/smart-list/smart-list.component.spec.ts @@ -4,8 +4,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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'; @@ -81,8 +81,8 @@ describe('OsdSmartListComponent', () => { configureTestBed({ declarations: [SmartListComponent], - imports: [BrowserAnimationsModule, TabsModule, SharedModule, HttpClientTestingModule], - providers: [i18nProviders, TabsetComponent, TabsetConfig] + imports: [BrowserAnimationsModule, SharedModule, HttpClientTestingModule, NgbNavModule], + providers: [i18nProviders] }); beforeEach(() => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/auth.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/auth.module.ts index 9171e24e453..a4244149c4c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/auth.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/auth.module.ts @@ -3,11 +3,11 @@ import { NgModule } from '@angular/core'; 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'; @@ -30,7 +30,7 @@ import { UserTabsComponent } from './user-tabs/user-tabs.component'; PopoverModule.forRoot(), ReactiveFormsModule, SharedModule, - TabsModule.forRoot(), + NgbNavModule, RouterModule, NgBootstrapFormValidationModule, BsDatepickerModule.forRoot() diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-details/role-details.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-details/role-details.component.html index ec054c67fbb..0bb532ddfa1 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-details/role-details.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-details/role-details.component.html @@ -1,14 +1,23 @@ - - - - - - + + + +
    +
    diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-details/role-details.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-details/role-details.component.spec.ts index d07ef6fe447..7d1fe5b80bb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-details/role-details.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-details/role-details.component.spec.ts @@ -2,7 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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'; @@ -13,7 +13,7 @@ describe('RoleDetailsComponent', () => { let fixture: ComponentFixture; configureTestBed({ - imports: [SharedModule, TabsModule.forRoot(), RouterTestingModule, HttpClientTestingModule], + imports: [SharedModule, RouterTestingModule, HttpClientTestingModule, NgbNavModule], declarations: [RoleDetailsComponent], providers: i18nProviders }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-list/role-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-list/role-list.component.spec.ts index 9b04f771b44..dc3c71c38ed 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-list/role-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/role-list/role-list.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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 { @@ -27,7 +27,7 @@ describe('RoleListComponent', () => { BrowserAnimationsModule, SharedModule, ToastrModule.forRoot(), - TabsModule.forRoot(), + NgbNavModule, RouterTestingModule, HttpClientTestingModule ], diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts index 9f2f4de895c..e48bb99f5ce 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts @@ -63,7 +63,7 @@ export class UserFormComponent extends CdForm implements OnInit { private authService: AuthService, private authStorageService: AuthStorageService, private route: ActivatedRoute, - private router: Router, + public router: Router, private modalService: BsModalService, private roleService: RoleService, private userService: UserService, diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.spec.ts index 0f0f5e8a396..f5ead615831 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; 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 { @@ -25,7 +25,7 @@ describe('UserListComponent', () => { BrowserAnimationsModule, SharedModule, ToastrModule.forRoot(), - TabsModule.forRoot(), + NgbNavModule, RouterTestingModule, HttpClientTestingModule ], diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.html index b707992a963..3c102cf6676 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.html @@ -1,12 +1,14 @@ - - - - - - + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.spec.ts index d73395141d0..7e0af5980e3 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.spec.ts @@ -2,7 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; 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'; @@ -13,7 +13,7 @@ describe('UserTabsComponent', () => { let fixture: ComponentFixture; configureTestBed({ - imports: [SharedModule, TabsModule.forRoot(), RouterTestingModule, HttpClientTestingModule], + imports: [SharedModule, RouterTestingModule, HttpClientTestingModule, NgbNavModule], declarations: [UserTabsComponent] }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.ts index c1302a5ccc6..a143c1670fc 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-tabs/user-tabs.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { Router } from '@angular/router'; @@ -7,16 +7,8 @@ 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) {} } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts index a3ac199a4ba..d5f598777fd 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts @@ -52,7 +52,7 @@ describe('TableComponent', () => { 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 } @@ -295,7 +295,7 @@ describe('TableComponent', () => { 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', () => { @@ -366,7 +366,7 @@ describe('TableComponent', () => { }); it('should search through arrays', () => { - component.columns = [ + component.localColumns = [ { prop: 'a', name: 'Index' }, { prop: 'b', name: 'ArrayColumn' } ]; @@ -454,15 +454,15 @@ describe('TableComponent', () => { }); 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', () => { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts index 9efb7c91915..35b750d029a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.ts @@ -190,6 +190,12 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O */ 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: { @@ -237,27 +243,29 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O } 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]; } @@ -298,7 +306,7 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O initUserConfig() { if (this.autoSave) { - this.tableName = this._calculateUniqueTableName(this.columns); + this.tableName = this._calculateUniqueTableName(this.localColumns); this._loadUserConfig(); this._initUserConfigAutoSave(); } @@ -311,7 +319,7 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O if (!this.userConfig.columns) { this.updateUserColumns(); } else { - this.columns.forEach((c, i) => { + this.localColumns.forEach((c, i) => { c.isHidden = this.userConfig.columns[i].isHidden; }); } @@ -364,7 +372,7 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O } updateUserColumns() { - this.userConfig.columns = this.columns.map((c) => ({ + this.userConfig.columns = this.localColumns.map((c) => ({ prop: c.prop, name: c.name, isHidden: !!c.isHidden @@ -376,7 +384,7 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O */ initCheckboxColumn() { if (this.selectionType === 'multiClick') { - this.columns.unshift({ + this.localColumns.unshift({ prop: undefined, resizeable: false, sortable: false, @@ -394,7 +402,7 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O */ initExpandCollapseColumn() { if (this.hasDetails) { - this.columns.unshift({ + this.localColumns.unshift({ prop: undefined, resizeable: false, sortable: false, @@ -409,11 +417,11 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O } 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 { @@ -692,7 +700,7 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O $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(); } @@ -737,7 +745,9 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O 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 diff --git a/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts b/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts index 81047f90637..e6869c5ee19 100644 --- a/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts +++ b/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts @@ -1,4 +1,4 @@ -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'; @@ -7,6 +7,7 @@ import { I18n } from '@ngx-translate/i18n-polyfill'; 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'; @@ -542,3 +543,25 @@ export class Mocks { return rule; } } + +export class TabHelper { + static getNgbNav(fixture: ComponentFixture) { + const debugElem: DebugElement = fixture.debugElement; + return debugElem.query(By.directive(NgbNav)).injector.get(NgbNav); + } + + static getNgbNavItems(fixture: ComponentFixture) { + const debugElems = this.getNgbNavItemsDebugElems(fixture); + return debugElems.map((de) => de.injector.get(NgbNavItem)); + } + + static getTextContents(fixture: ComponentFixture) { + const debugElems = this.getNgbNavItemsDebugElems(fixture); + return debugElems.map((de) => de.nativeElement.textContent); + } + + private static getNgbNavItemsDebugElems(fixture: ComponentFixture) { + const debugElem: DebugElement = fixture.debugElement; + return debugElem.queryAll(By.directive(NgbNavItem)); + } +} -- 2.39.5