]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Use table actions component for RBDs
authorStephan Müller <smueller@suse.com>
Mon, 20 Aug 2018 12:01:08 +0000 (14:01 +0200)
committerStephan Müller <smueller@suse.com>
Wed, 19 Sep 2018 13:00:51 +0000 (15:00 +0200)
Signed-off-by: Stephan Müller <smueller@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts

index 76321cc3ec4b9948ae915712191f246182273112..686c749fa9a84449f009977dde5e74007564fbfc 100644 (file)
           forceIdentifier="true"
           selectionType="single"
           (updateSelection)="updateSelection($event)">
-  <div class="table-actions">
-    <div class="btn-group"
-         dropdown>
-      <button type="button"
-              class="btn btn-sm btn-primary"
-              *ngIf="permission.create && (!permission.update || !selection.hasSingleSelection)"
-              routerLink="/block/rbd/add">
-        <i class="fa fa-fw fa-plus"></i><span i18n>Add</span>
-      </button>
-      <button type="button"
-              class="btn btn-sm btn-primary"
-              *ngIf="permission.update && (!permission.create || permission.create && selection.hasSingleSelection)"
-              [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().cdExecuting}"
-              routerLink="/block/rbd/edit/{{ selection.first()?.pool_name | encodeUri }}/{{ selection.first()?.name | encodeUri }}">
-        <i class="fa fa-fw fa-pencil"></i>
-        <span i18n>Edit</span>
-      </button>
-      <button type="button"
-              class="btn btn-sm btn-primary"
-              *ngIf="permission.delete && !permission.update && !permission.create"
-              [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().cdExecuting}"
-              (click)="deleteRbdModal()">
-        <i class="fa fa-fw fa-trash-o"></i><span i18n>Delete</span>
-      </button>
-      <button type="button"
-              dropdownToggle
-              class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split"
-              *ngIf="permission.create || permission.update">
-        <span class="caret"></span>
-        <span class="sr-only"></span>
-      </button>
-      <ul *dropdownMenu
-          class="dropdown-menu"
-          role="menu">
-        <li role="menuitem"
-            *ngIf="permission.create">
-          <a class="dropdown-item"
-             routerLink="/block/rbd/add">
-            <i class="fa fa-fw fa-plus"></i>
-            <span i18n>Add</span>
-          </a>
-        </li>
-        <li role="menuitem"
-            *ngIf="permission.update"
-            [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().cdExecuting}">
-          <a class="dropdown-item"
-             routerLink="/block/rbd/edit/{{ selection.first()?.pool_name | encodeUri }}/{{ selection.first()?.name | encodeUri }}">
-            <i class="fa fa-fw fa-pencil"></i>
-            <span i18n>Edit</span>
-          </a>
-        </li>
-        <li role="menuitem"
-            *ngIf="permission.create"
-            [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().cdExecuting}">
-          <a class="dropdown-item"
-             routerLink="/block/rbd/copy/{{ selection.first()?.pool_name | encodeUri }}/{{ selection.first()?.name | encodeUri }}">
-            <i class="fa fa-fw fa-copy"></i>
-            <span i18n>Copy</span>
-          </a>
-        </li>
-        <li role="menuitem"
-            *ngIf="permission.update"
-            [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().cdExecuting || !selection.first().parent}">
-          <a class="dropdown-item"
-             (click)="flattenRbdModal()">
-            <i class="fa fa-fw fa-chain-broken"></i>
-            <span i18n>Flatten</span>
-          </a>
-        </li>
-        <li role="menuitem"
-            *ngIf="permission.delete"
-            [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().cdExecuting}">
-          <a class="dropdown-item"
-             (click)="deleteRbdModal()">
-            <i class="fa fa-fw fa-trash-o"></i>
-            <span i18n>Delete</span>
-          </a>
-        </li>
-      </ul>
-    </div>
-  </div>
+  <cd-table-actions class="table-actions"
+    [permission]="permission"
+    [selection]="selection"
+    [tableActions]="tableActions">
+  </cd-table-actions>
   <cd-rbd-details cdTableDetail
                   [selection]="selection">
   </cd-rbd-details>
index 24b28a9dfcaa61167489e2141e1b7c0d707a71e8..7a985f61dc9e6f1d8dd67cb1a70046b53b7aca39 100644 (file)
@@ -1,5 +1,6 @@
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
 import { RouterTestingModule } from '@angular/router/testing';
 
 import { ToastModule } from 'ng2-toastr';
@@ -12,9 +13,10 @@ import {
 } from 'ngx-bootstrap';
 import { BehaviorSubject, of } from 'rxjs';
 
-import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { configureTestBed, PermissionHelper } from '../../../../testing/unit-test-helper';
 import { RbdService } from '../../../shared/api/rbd.service';
 import { ComponentsModule } from '../../../shared/components/components.module';
+import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
 import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
 import { ExecutingTask } from '../../../shared/models/executing-task';
 import { SummaryService } from '../../../shared/services/summary.service';
@@ -175,4 +177,169 @@ describe('RbdListComponent', () => {
       expectImageTasks(component.images[2], 'Flattening');
     });
   });
+
+  describe('show action buttons and drop down actions depending on permissions', () => {
+    let tableActions: TableActionsComponent;
+    let scenario: { fn; empty; single };
+    let permissionHelper: PermissionHelper;
+
+    const getTableActionComponent = (): TableActionsComponent => {
+      fixture.detectChanges();
+      return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
+    };
+
+    beforeEach(() => {
+      permissionHelper = new PermissionHelper(component.permission, () =>
+        getTableActionComponent()
+      );
+      scenario = {
+        fn: () => tableActions.getCurrentButton().name,
+        single: 'Edit',
+        empty: 'Add'
+      };
+    });
+
+    describe('with all', () => {
+      beforeEach(() => {
+        tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
+      });
+
+      it(`shows 'Edit' for single selection else 'Add' as main action`, () =>
+        permissionHelper.testScenarios(scenario));
+
+      it('shows all actions', () => {
+        expect(tableActions.tableActions.length).toBe(5);
+        expect(tableActions.tableActions).toEqual(component.tableActions);
+      });
+    });
+
+    describe('with read, create and update', () => {
+      beforeEach(() => {
+        tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
+      });
+
+      it(`shows 'Edit' for single selection else 'Add' as main action`, () =>
+        permissionHelper.testScenarios(scenario));
+
+      it(`shows all actions except for 'Delete'`, () => {
+        expect(tableActions.tableActions.length).toBe(4);
+        component.tableActions.pop();
+        expect(tableActions.tableActions).toEqual(component.tableActions);
+      });
+    });
+
+    describe('with read, create and delete', () => {
+      beforeEach(() => {
+        tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 1);
+      });
+
+      it(`shows 'Copy' for single selection else 'Add' as main action`, () => {
+        scenario.single = 'Copy';
+        permissionHelper.testScenarios(scenario);
+      });
+
+      it(`shows 'Add', 'Copy' and 'Delete' action`, () => {
+        expect(tableActions.tableActions.length).toBe(3);
+        expect(tableActions.tableActions).toEqual([
+          component.tableActions[0],
+          component.tableActions[2],
+          component.tableActions[4]
+        ]);
+      });
+    });
+
+    describe('with read, edit and delete', () => {
+      beforeEach(() => {
+        tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
+      });
+
+      it(`shows always 'Edit' as main action`, () => {
+        scenario.empty = 'Edit';
+        permissionHelper.testScenarios(scenario);
+      });
+
+      it(`shows 'Edit', 'Flatten' and 'Delete' action`, () => {
+        expect(tableActions.tableActions.length).toBe(3);
+        expect(tableActions.tableActions).toEqual([
+          component.tableActions[1],
+          component.tableActions[3],
+          component.tableActions[4]
+        ]);
+      });
+    });
+
+    describe('with read and create', () => {
+      beforeEach(() => {
+        tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 0);
+      });
+
+      it(`shows 'Copy' for single selection else 'Add' as main action`, () => {
+        scenario.single = 'Copy';
+        permissionHelper.testScenarios(scenario);
+      });
+
+      it(`shows 'Copy' and 'Add' actions`, () => {
+        expect(tableActions.tableActions.length).toBe(2);
+        expect(tableActions.tableActions).toEqual([
+          component.tableActions[0],
+          component.tableActions[2]
+        ]);
+      });
+    });
+
+    describe('with read and edit', () => {
+      beforeEach(() => {
+        tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
+      });
+
+      it(`shows always 'Edit' as main action`, () => {
+        scenario.empty = 'Edit';
+        permissionHelper.testScenarios(scenario);
+      });
+
+      it(`shows 'Edit' and 'Flatten' actions`, () => {
+        expect(tableActions.tableActions.length).toBe(2);
+        expect(tableActions.tableActions).toEqual([
+          component.tableActions[1],
+          component.tableActions[3]
+        ]);
+      });
+    });
+
+    describe('with read and delete', () => {
+      beforeEach(() => {
+        tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 1);
+      });
+
+      it(`shows always 'Delete' as main action`, () => {
+        scenario.single = 'Delete';
+        scenario.empty = 'Delete';
+        permissionHelper.testScenarios(scenario);
+      });
+
+      it(`shows only 'Delete' action`, () => {
+        expect(tableActions.tableActions.length).toBe(1);
+        expect(tableActions.tableActions).toEqual([component.tableActions[4]]);
+      });
+    });
+
+    describe('with only read', () => {
+      beforeEach(() => {
+        tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 0);
+      });
+
+      it('shows no main action', () => {
+        permissionHelper.testScenarios({
+          fn: () => tableActions.getCurrentButton(),
+          single: undefined,
+          empty: undefined
+        });
+      });
+
+      it('shows no actions', () => {
+        expect(tableActions.tableActions.length).toBe(0);
+        expect(tableActions.tableActions).toEqual([]);
+      });
+    });
+  });
 });
index eafb9bda73fdb4a193e935c43227876869622dc1..62a552f2d575a22752687651889b5c88ce3eb523 100644 (file)
@@ -9,6 +9,7 @@ import { DeletionModalComponent } from '../../../shared/components/deletion-moda
 import { TableComponent } from '../../../shared/datatable/table/table.component';
 import { CellTemplate } from '../../../shared/enum/cell-template.enum';
 import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
+import { CdTableAction } from '../../../shared/models/cd-table-action';
 import { CdTableColumn } from '../../../shared/models/cd-table-column';
 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
 import { FinishedTask } from '../../../shared/models/finished-task';
@@ -40,6 +41,7 @@ export class RbdListComponent implements OnInit {
   flattenTpl: TemplateRef<any>;
 
   permission: Permission;
+  tableActions: CdTableAction[];
   images: any;
   columns: CdTableColumn[];
   retries: number;
@@ -75,6 +77,45 @@ export class RbdListComponent implements OnInit {
     private taskListService: TaskListService
   ) {
     this.permission = this.authStorageService.getPermissions().rbdImage;
+    const getImageUri = () =>
+      this.selection.first() &&
+      `${encodeURI(this.selection.first().pool_name)}/${encodeURI(this.selection.first().name)}`;
+    const addAction: CdTableAction = {
+      permission: 'create',
+      icon: 'fa-plus',
+      routerLink: () => '/block/rbd/add',
+      buttonCondition: (selection: CdTableSelection) => !selection.hasSingleSelection,
+      name: 'Add'
+    };
+    const editAction: CdTableAction = {
+      permission: 'update',
+      icon: 'fa-pencil',
+      routerLink: () => `/block/rbd/edit/${getImageUri()}`,
+      name: 'Edit'
+    };
+    const deleteAction: CdTableAction = {
+      permission: 'delete',
+      icon: 'fa-trash-o',
+      click: () => this.deleteRbdModal(),
+      name: 'Delete'
+    };
+    const copyAction: CdTableAction = {
+      permission: 'create',
+      buttonCondition: (selection: CdTableSelection) => selection.hasSingleSelection,
+      disable: (selection: CdTableSelection) => !selection.hasSingleSelection,
+      icon: 'fa-copy',
+      routerLink: () => `/block/rbd/copy/${getImageUri()}`,
+      name: 'Copy'
+    };
+    const flattenAction: CdTableAction = {
+      permission: 'update',
+      disable: (selection: CdTableSelection) =>
+        !selection.hasSingleSelection || selection.first().cdExecuting || !selection.first().parent,
+      icon: 'fa-chain-broken',
+      click: () => this.flattenRbdModal(),
+      name: 'Flatten'
+    };
+    this.tableActions = [addAction, editAction, copyAction, flattenAction, deleteAction];
   }
 
   ngOnInit() {