From 27198db3460fd4b449bbe91ff2146b65f6371259 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Stephan=20M=C3=BCller?= Date: Wed, 17 Apr 2019 14:17:44 +0200 Subject: [PATCH] mgr/dashboard: OSD custom action button removal MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Now the custom action dropdown is removed from the OSD component. It was nearly a clone of the table actions button component, but it had a different behavior as it did not show the main action inside the dropdown. Fixes: https://tracker.ceph.com/issues/39702 Signed-off-by: Stephan Müller --- .../osd/osd-list/osd-list.component.html | 40 +++--------- .../osd/osd-list/osd-list.component.scss | 3 - .../osd/osd-list/osd-list.component.spec.ts | 56 ++++++++++++++++- .../osd/osd-list/osd-list.component.ts | 37 ++++------- .../table-actions.component.html | 8 +-- .../table-actions.component.spec.ts | 63 +++++++++++++++---- .../table-actions/table-actions.component.ts | 8 ++- .../mgr/dashboard/frontend/src/styles.scss | 7 --- 8 files changed, 135 insertions(+), 87 deletions(-) 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 54182338efd85..34a099cbcd4b0 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 @@ -11,39 +11,17 @@ - -
- - - -
+ + { const fakeAuthStorageService = { getPermissions: () => { - return new Permissions({ osd: ['read', 'update', 'create', 'delete'] }); + return new Permissions({ + 'config-opt': ['read', 'update', 'create', 'delete'], + osd: ['read', 'update', 'create', 'delete'] + }); } }; @@ -92,7 +95,6 @@ describe('OsdListComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(OsdListComponent); - fixture.detectChanges(); component = fixture.componentInstance; osdService = TestBed.get(OsdService); modalServiceShowSpy = spyOn(TestBed.get(BsModalService), 'show').and.stub(); @@ -104,6 +106,7 @@ describe('OsdListComponent', () => { }); it('should have columns that are sortable', () => { + fixture.detectChanges(); expect(component.columns.every((column) => Boolean(column.prop))).toBeTruthy(); }); @@ -170,6 +173,49 @@ describe('OsdListComponent', () => { }); }); + describe('show osd actions as defined', () => { + const getOsdActions = () => { + fixture.detectChanges(); + return fixture.debugElement.query(By.css('#cluster-wide-actions')).componentInstance + .dropDownActions; + }; + + it('shows osd actions after osd-actions', () => { + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('#cluster-wide-actions'))).toBe( + fixture.debugElement.queryAll(By.directive(TableActionsComponent))[1] + ); + }); + + it('shows both osd actions', () => { + const osdActions = getOsdActions(); + expect(osdActions).toEqual(component.clusterWideActions); + expect(osdActions.length).toBe(3); + }); + + it('shows only "Flags" action', () => { + component.permissions.configOpt.read = false; + const osdActions = getOsdActions(); + expect(osdActions[0].name).toBe('Flags'); + expect(osdActions.length).toBe(1); + }); + + it('shows only "Recovery Priority" action', () => { + component.permissions.osd.read = false; + const osdActions = getOsdActions(); + expect(osdActions[0].name).toBe('Recovery Priority'); + expect(osdActions[1].name).toBe('PG scrub'); + expect(osdActions.length).toBe(2); + }); + + it('shows no osd actions', () => { + component.permissions.configOpt.read = false; + component.permissions.osd.read = false; + const osdActions = getOsdActions(); + expect(osdActions).toEqual([]); + }); + }); + describe('show table actions as defined', () => { let tableActions: TableActionsComponent; let scenario: { fn; empty; single }; @@ -177,7 +223,7 @@ describe('OsdListComponent', () => { const getTableActionComponent = () => { fixture.detectChanges(); - return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance; + return fixture.debugElement.query(By.css('#osd-actions')).componentInstance; }; beforeEach(() => { @@ -201,6 +247,10 @@ describe('OsdListComponent', () => { }); describe('test table actions in submenu', () => { + beforeEach(() => { + fixture.detectChanges(); + }); + beforeEach(fakeAsync(() => { // The menu needs a click to render the dropdown! const dropDownToggle = fixture.debugElement.query(By.css('.dropdown-toggle')); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.ts index 15d685a3c69f6..d4d5f2945a1d6 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.ts @@ -47,7 +47,7 @@ export class OsdListComponent implements OnInit { tableActions: CdTableAction[]; bsModalRef: BsModalRef; columns: CdTableColumn[]; - advancedTableActions: any[]; + clusterWideActions: CdTableAction[]; osds = []; selection = new CdTableSelection(); @@ -148,29 +148,32 @@ export class OsdListComponent implements OnInit { icon: 'fa-remove' } ]; - this.advancedTableActions = [ + } + + ngOnInit() { + this.clusterWideActions = [ { - name: this.i18n('Cluster-wide Flags'), + name: this.i18n('Flags'), icon: 'fa-flag', click: () => this.configureFlagsAction(), - permission: this.permissions.osd.read + permission: 'read', + visible: () => this.permissions.osd.read }, { - name: this.i18n('Cluster-wide Recovery Priority'), + name: this.i18n('Recovery Priority'), icon: 'fa-cog', click: () => this.configureQosParamsAction(), - permission: this.permissions.configOpt.read + permission: 'read', + visible: () => this.permissions.configOpt.read }, { name: this.i18n('PG scrub'), icon: 'fa-stethoscope', click: () => this.configurePgScrubAction(), - permission: this.permissions.configOpt.read + permission: 'read', + visible: () => this.permissions.configOpt.read } ]; - } - - ngOnInit() { this.columns = [ { prop: 'host.name', name: this.i18n('Host') }, { prop: 'id', name: this.i18n('ID'), cellTransformation: CellTemplate.bold }, @@ -199,8 +202,6 @@ export class OsdListComponent implements OnInit { cellTransformation: CellTemplate.perSecond } ]; - - this.removeActionsWithNoPermissions(); } get hasOsdSelected() { @@ -336,16 +337,4 @@ export class OsdListComponent implements OnInit { configurePgScrubAction() { this.bsModalRef = this.modalService.show(OsdPgScrubModalComponent, { class: 'modal-lg' }); } - - /** - * Removes all actions from 'advancedTableActions' that need a permission the user doesn't have. - */ - private removeActionsWithNoPermissions() { - if (!this.permissions) { - this.advancedTableActions = []; - return; - } - - this.advancedTableActions = this.advancedTableActions.filter((action) => action.permission); - } } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html index e0d6c209f923c..57761eb1f8f28 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html @@ -2,7 +2,7 @@ dropdown>
    { let scenario; let permissionHelper: PermissionHelper; - const setUpTableActions = () => { + const getTableActionComponent = (): TableActionsComponent => { component.tableActions = [ addAction, editAction, @@ -29,10 +29,6 @@ describe('TableActionsComponent', () => { copyAction, deleteAction ]; - }; - - const getTableActionComponent = (): TableActionsComponent => { - setUpTableActions(); component.ngOnInit(); return component; }; @@ -236,7 +232,7 @@ describe('TableActionsComponent', () => { permissionHelper.testScenarios(scenario); }); - it('should not get any button with no permissions', () => { + it('should not get any button with no permissions, except the true action', () => { hiddenScenario(); permissionHelper.setPermissionsAndGetActions(0, 0, 0); permissionHelper.testScenarios(scenario); @@ -244,7 +240,7 @@ describe('TableActionsComponent', () => { it('should not get any button if only a drop down should be shown', () => { hiddenScenario(); - component.onlyDropDown = 'Drop down label'; + component.dropDownOnly = 'Drop down label that is shown'; permissionHelper.setPermissionsAndGetActions(1, 1, 1); permissionHelper.testScenarios(scenario); }); @@ -269,13 +265,56 @@ describe('TableActionsComponent', () => { }); }); - describe('with drop down only', () => { - beforeEach(() => { - component.onlyDropDown = 'displayMe'; + describe('all visible actions with all different permissions', () => { + it('with create, update and delete', () => { + permissionHelper.setPermissionsAndGetActions(1, 1, 1); + expect(component.dropDownActions).toEqual([ + addAction, + editAction, + unprotectAction, + copyAction, + deleteAction + ]); }); - it('should not return any button with getCurrentButton', () => { - expect(component.getCurrentButton()).toBeFalsy(); + it('with create and delete', () => { + permissionHelper.setPermissionsAndGetActions(1, 0, 1); + expect(component.dropDownActions).toEqual([addAction, copyAction, deleteAction]); + }); + + it('with create and update', () => { + permissionHelper.setPermissionsAndGetActions(1, 1, 0); + expect(component.dropDownActions).toEqual([ + addAction, + editAction, + unprotectAction, + copyAction + ]); + }); + + it('with create', () => { + permissionHelper.setPermissionsAndGetActions(1, 0, 0); + expect(component.dropDownActions).toEqual([addAction, copyAction]); + }); + + it('with update and delete', () => { + permissionHelper.setPermissionsAndGetActions(0, 1, 1); + expect(component.dropDownActions).toEqual([editAction, unprotectAction, deleteAction]); + }); + + it('with update', () => { + permissionHelper.setPermissionsAndGetActions(0, 1, 0); + expect(component.dropDownActions).toEqual([editAction, unprotectAction]); + }); + + it('with delete', () => { + permissionHelper.setPermissionsAndGetActions(0, 0, 1); + expect(component.dropDownActions).toEqual([deleteAction]); + }); + + it('without any', () => { + permissionHelper.setPermissionsAndGetActions(0, 0, 0); + expect(component.dropDownActions).toEqual([]); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts index 62f33acac1866..034092862a33c 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts @@ -18,12 +18,14 @@ export class TableActionsComponent implements OnInit { selection: CdTableSelection; @Input() tableActions: CdTableAction[]; + @Input() + btnColor = 'primary'; // Use this if you just want to display a drop down button, // labeled with the given text, with all actions in it. // This disables the main action button. @Input() - onlyDropDown?: string; + dropDownOnly?: string; // Array with all visible actions dropDownActions: CdTableAction[] = []; @@ -74,7 +76,7 @@ export class TableActionsComponent implements OnInit { * @returns {CdTableAction} */ getCurrentButton(): CdTableAction { - if (this.onlyDropDown) { + if (this.dropDownOnly) { return; } let buttonAction = this.dropDownActions.find((tableAction) => this.showableAction(tableAction)); @@ -115,11 +117,11 @@ export class TableActionsComponent implements OnInit { * @returns {Boolean} */ disableSelectionAction(action: CdTableAction): Boolean { - const permission = action.permission; const disable = action.disable; if (disable) { return Boolean(disable(this.selection)); } + const permission = action.permission; const selected = this.selection.hasSingleSelection && this.selection.first(); return Boolean( ['update', 'delete'].includes(permission) && (!selected || selected.cdExecuting) diff --git a/src/pybind/mgr/dashboard/frontend/src/styles.scss b/src/pybind/mgr/dashboard/frontend/src/styles.scss index 2e14f32978453..1a267838cc425 100644 --- a/src/pybind/mgr/dashboard/frontend/src/styles.scss +++ b/src/pybind/mgr/dashboard/frontend/src/styles.scss @@ -141,9 +141,6 @@ fieldset[disabled] .btn-primary.active { color: $color-primary; background-color: $color-button-badge; } -.btn-primary .caret { - color: $color-button-caret; -} .btn-default { border-radius: $button-radius; } @@ -244,10 +241,6 @@ button.btn.btn-label > i.fa { padding-left: 30px; padding-right: 30px; } -/* Caret */ -.caret { - color: $color-caret-text; -} /* Progressbar */ .progress-bar { background-image: none !important; -- 2.39.5