import { DashboardComponent } from './dashboard/dashboard.component';
import { HealthPieComponent } from './health-pie/health-pie.component';
import { InputModule, ModalModule, SelectModule, ToggletipModule } from 'carbon-components-angular';
+import { OverviewComponent } from '../overview/overview.component';
@NgModule({
imports: [
ToggletipModule,
ModalModule,
InputModule,
- SelectModule
+ SelectModule,
+ OverviewComponent
],
+ exports: [OverviewComponent],
declarations: [DashboardComponent, HealthPieComponent, FeedbackComponent],
providers: [provideCharts(withDefaultRegisterables())]
})
-<main aria-label="Dashboard">
- <a href="#main"
- class="sr-only">skip to content</a>
-
- <ng-container class="main-padding">
- <cd-dashboard-v3></cd-dashboard-v3>
- </ng-container>
+<main aria-label="Cluster Overview">
+@if (useDeprecated === true) {
+<!-- OLD OVERVIEW -->
+<cd-dashboard-v3 data-testid="cd-dashboard-v3"></cd-dashboard-v3>
+} @else {
+<!-- NEWER OVERVIEW -->
+<cd-overview data-testid="cd-overview"></cd-overview>
+}
</main>
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { FeatureTogglesService } from '~/app/shared/services/feature-toggles.service';
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
+ let featureTogglesService: FeatureTogglesService;
configureTestBed({
imports: [NgbNavModule, HttpClientTestingModule],
});
beforeEach(() => {
+ featureTogglesService = TestBed.inject(FeatureTogglesService);
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
- fixture.detectChanges();
});
it('should create', () => {
+ spyOn(featureTogglesService, 'isFeatureEnabled').and.returnValue(true);
+
+ fixture.detectChanges();
expect(component).toBeTruthy();
});
+
+ it('should call featureTogglesService.isFeatureEnabled() on initialization', () => {
+ const spy = spyOn(featureTogglesService, 'isFeatureEnabled').and.returnValue(true);
+ fixture.detectChanges();
+ expect(spy).toHaveBeenCalled();
+ expect(spy).toHaveBeenCalledWith('dashboard');
+ });
+
+ it('should set useDeprecated based on feature toggle', () => {
+ spyOn(featureTogglesService, 'isFeatureEnabled').and.returnValue(true);
+ fixture.detectChanges();
+ expect(component.useDeprecated).toBe(true);
+ });
+
+ describe('when dashboard feature is enabled (new dashboard)', () => {
+ beforeEach(() => {
+ spyOn(featureTogglesService, 'isFeatureEnabled').and.returnValue(true);
+ fixture.detectChanges();
+ });
+
+ it('should show cd-dashboard-v3 in template when dashboard feature is enabled', () => {
+ const overviewElement = fixture.debugElement.query(By.css('[data-testid="cd-overview"]'));
+ const dashboardV3Element = fixture.debugElement.query(
+ By.css('[data-testid="cd-dashboard-v3"]')
+ );
+
+ expect(overviewElement).toBeNull();
+ expect(dashboardV3Element).toBeTruthy();
+ });
+
+ it('should set useDeprecated to false when feature is enabled', () => {
+ expect(component.useDeprecated).toBe(true);
+ });
+ });
+
+ describe('when dashboard feature is disabled (old dashboard)', () => {
+ beforeEach(() => {
+ spyOn(featureTogglesService, 'isFeatureEnabled').and.returnValue(false);
+ fixture.detectChanges();
+ });
+
+ it('should show cd-overview in template when dashboard feature is disabled', () => {
+ const overviewElement = fixture.debugElement.query(By.css('[data-testid="cd-overview"]'));
+ const dashboardV3Element = fixture.debugElement.query(
+ By.css('[data-testid="cd-dashboard-v3"]')
+ );
+
+ expect(overviewElement).toBeTruthy();
+ expect(dashboardV3Element).toBeNull();
+ });
+
+ it('should set useDeprecated to true when feature is disabled', () => {
+ expect(component.useDeprecated).toBe(false);
+ });
+ });
});
-import { Component } from '@angular/core';
+import { Component, inject, OnInit } from '@angular/core';
+import { FeatureTogglesService } from '~/app/shared/services/feature-toggles.service';
@Component({
selector: 'cd-dashboard',
styleUrls: ['./dashboard.component.scss'],
standalone: false
})
-export class DashboardComponent {}
+export class DashboardComponent implements OnInit {
+ useDeprecated: boolean = true;
+
+ private featureToggles = inject(FeatureTogglesService);
+
+ ngOnInit() {
+ this.useDeprecated = this.featureToggles.isFeatureEnabled('dashboard');
+ }
+}
--- /dev/null
+<div cdsGrid
+ [narrow]="true"
+ [condensed]="false"
+ [fullWidth]="true"
+ class="overview">
+ <div cdsRow
+ [narrow]="true"
+ class="overview-row">
+ <div cdsCol
+ [columnNumbers]="{md: 16, lg: 11}">
+ <cds-tile>Health card</cds-tile>
+ </div>
+ <div cdsCol
+ [columnNumbers]="{md: 16, lg: 5}">
+ <cds-tile>Alerts card</cds-tile>
+ </div>
+ </div>
+ <div cdsRow
+ [narrow]="true"
+ class="overview-row">
+ <div cdsCol
+ [columnNumbers]="{md: 16, lg: 11}">
+ <cds-tile>Storage card</cds-tile>
+ </div>
+ <div cdsCol
+ [columnNumbers]="{md: 16, lg: 5}">
+ <cds-tile>Docs card</cds-tile>
+ </div>
+ </div>
+ <div cdsRow>
+ <div cdsCol
+ [columnNumbers]="{md: 16, lg: 16}">
+ <cds-tile>Performance card</cds-tile>
+ </div>
+ </div>
+</div>
--- /dev/null
+.overview {
+ margin-top: var(--cds-spacing-05);
+ margin-bottom: var(--cds-spacing-05);
+}
+
+.overview-row {
+ margin-bottom: var(--cds-spacing-05);
+}
--- /dev/null
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { OverviewComponent } from './overview.component';
+
+describe('OverviewComponent', () => {
+ let component: OverviewComponent;
+ let fixture: ComponentFixture<OverviewComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [OverviewComponent]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(OverviewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
--- /dev/null
+import { Component } from '@angular/core';
+import { GridModule, TilesModule } from 'carbon-components-angular';
+
+@Component({
+ selector: 'cd-overview',
+ imports: [GridModule, TilesModule],
+ standalone: true,
+ templateUrl: './overview.component.html',
+ styleUrl: './overview.component.scss'
+})
+export class OverviewComponent {}
cephfs = true;
rgw = true;
nfs = true;
+ dashboard = true;
}
export type Features = keyof FeatureTogglesMap;
export type FeatureTogglesMap$ = Observable<FeatureTogglesMap>;
get(): FeatureTogglesMap$ {
return this.featureToggleMap$;
}
+
+ isFeatureEnabled(feature: string): boolean {
+ return this.http.get<FeatureTogglesMap>(this.API_URL)?.[feature];
+ }
}
cephfs:
description: ''
type: boolean
+ dashboard:
+ description: ''
+ type: boolean
iscsi:
description: ''
type: boolean
- cephfs
- rgw
- nfs
+ - dashboard
type: object
description: OK
'400':
# -*- coding: utf-8 -*-
from enum import Enum
-from typing import Dict, List, Optional, Set, no_type_check
+from typing import Dict, List, Optional, Set
import cherrypy
from mgr_module import CLICommand, Option
CEPHFS = 'cephfs'
RGW = 'rgw'
NFS = 'nfs'
+ DASHBOARD = 'dashboard'
# if we want to add any custom warning message when enabling a feature
# we can add it here as key-value pair in warn_msg.
return ret, '\n'.join(msg), ''
return {'handle_command': cmd}
- @no_type_check # https://github.com/python/mypy/issues/7806
def _get_feature_from_request(self, request):
try:
return self.Controller2Feature[
return None
@ttl_cache(ttl=CACHE_TTL, maxsize=CACHE_MAX_SIZE)
- @no_type_check # https://github.com/python/mypy/issues/7806
def _is_feature_enabled(self, feature):
return self.mgr.get_module_option(self.OPTION_FMT.format(feature))
"cephfs": (bool, ''),
"rgw": (bool, ''),
"nfs": (bool, ''),
+ "dashboard": (bool, '')
}
@APIRouter('/feature_toggles')