selectionType="single"
(updateSelection)="updateSelection($event)"
[columns]="columns">
- <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)"
- (click)="openCreateSnapshotModal()">
- <i class="fa fa-fw fa-plus"></i>
- <span i18n>Create</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().executing}"
- (click)="openEditSnapshotModal()">
- <i class="fa fa-fw fa-pencil"></i>
- <span i18n>Rename</span>
- </button>
- <button type="button"
- class="btn btn-sm btn-primary"
- *ngIf="permission.delete && !permission.update && !permission.create"
- [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}"
- (click)="deleteSnapshotModal()">
- <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"
- (click)="openCreateSnapshotModal()">
- <i class="fa fa-fw fa-plus"></i>
- <span i18n>Create</span>
- </a>
- </li>
- <li role="menuitem"
- *ngIf="permission.update"
- [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
- <a class="dropdown-item" (click)="openEditSnapshotModal()">
- <i class="fa fa-fw fa-pencil"></i>
- <span i18n>Rename</span>
- </a>
- </li>
- <li role="menuitem"
- *ngIf="permission.update"
- [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
- <a class="dropdown-item" (click)="toggleProtection()">
- <span *ngIf="!selection.first()?.is_protected">
- <i class="fa fa-fw fa-lock"></i>
- <span i18n>Protect</span>
- </span>
- <span *ngIf="selection.first()?.is_protected">
- <i class="fa fa-fw fa-unlock"></i>
- <span i18n>Unprotect</span>
- </span>
- </a>
- </li>
- <li role="menuitem"
- *ngIf="permission.create"
- [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
- <a class="dropdown-item"
- routerLink="/block/rbd/clone/{{ poolName | encodeUri }}/{{ rbdName | encodeUri }}/{{ selection.first()?.name | encodeUri }}">
- <i class="fa fa-fw fa-clone"></i>
- <span i18n>Clone</span>
- </a>
- </li>
- <li role="menuitem"
- *ngIf="permission.create"
- [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing}">
- <a class="dropdown-item"
- routerLink="/block/rbd/copy/{{ poolName | encodeUri }}/{{ rbdName | 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().executing}">
- <a class="dropdown-item"
- (click)="rollbackModal()">
- <i class="fa fa-fw fa-undo"></i>
- <span i18n>Rollback</span>
- </a>
- </li>
- <li role="menuitem"
- *ngIf="permission.delete"
- [ngClass]="{'disabled': !selection.hasSingleSelection || selection.first().executing || selection.first().is_protected}">
- <a class="dropdown-item"
- (click)="deleteSnapshotModal()">
- <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-table>
<ng-template #protectTpl
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { ToastModule } from 'ng2-toastr';
import { BsModalRef, BsModalService } from 'ngx-bootstrap';
import { Subject, throwError as observableThrowError } from 'rxjs';
-import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { configureTestBed, PermissionHelper } from '../../../../testing/unit-test-helper';
import { ApiModule } from '../../../shared/api/api.module';
import { RbdService } from '../../../shared/api/rbd.service';
import { ComponentsModule } from '../../../shared/components/components.module';
import { DataTableModule } from '../../../shared/datatable/datatable.module';
+import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
import { ExecutingTask } from '../../../shared/models/executing-task';
import { Permissions } from '../../../shared/models/permissions';
import { PipesModule } from '../../../shared/pipes/pipes.module';
fixture = TestBed.createComponent(RbdSnapshotListComponent);
component = fixture.componentInstance;
summaryService = TestBed.get(SummaryService);
- fixture.detectChanges();
});
it('should create', () => {
+ fixture.detectChanges();
expect(component).toBeTruthy();
});
let authStorageService: AuthStorageService;
beforeEach(() => {
+ fixture.detectChanges();
called = false;
rbdService = new RbdService(null);
notificationService = new NotificationService(null, null);
};
beforeEach(() => {
+ fixture.detectChanges();
snapshots = [];
addSnapshot('a');
addSnapshot('b');
);
});
});
+
+ 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: 'Rename',
+ empty: 'Create'
+ };
+ });
+
+ describe('with all', () => {
+ beforeEach(() => {
+ tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
+ });
+
+ it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
+ permissionHelper.testScenarios(scenario));
+
+ it('shows all actions', () => {
+ expect(tableActions.tableActions.length).toBe(8);
+ expect(tableActions.tableActions).toEqual(component.tableActions);
+ });
+ });
+
+ describe('with read, create and update', () => {
+ beforeEach(() => {
+ tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
+ });
+
+ it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
+ permissionHelper.testScenarios(scenario));
+
+ it(`shows all actions except for 'Delete'`, () => {
+ expect(tableActions.tableActions.length).toBe(7);
+ component.tableActions.pop();
+ expect(tableActions.tableActions).toEqual(component.tableActions);
+ });
+ });
+
+ describe('with read, create and delete', () => {
+ beforeEach(() => {
+ tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 1);
+ });
+
+ it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
+ scenario.single = 'Clone';
+ permissionHelper.testScenarios(scenario);
+ });
+
+ it(`shows 'Create', 'Clone', 'Copy' and 'Delete' action`, () => {
+ expect(tableActions.tableActions.length).toBe(4);
+ expect(tableActions.tableActions).toEqual([
+ component.tableActions[0],
+ component.tableActions[4],
+ component.tableActions[5],
+ component.tableActions[7]
+ ]);
+ });
+ });
+
+ describe('with read, edit and delete', () => {
+ beforeEach(() => {
+ tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
+ });
+
+ it(`shows always 'Rename' as main action`, () => {
+ scenario.empty = 'Rename';
+ permissionHelper.testScenarios(scenario);
+ });
+
+ it(`shows 'Rename', 'Protect', 'Unprotect', 'Rollback' and 'Delete' action`, () => {
+ expect(tableActions.tableActions.length).toBe(5);
+ expect(tableActions.tableActions).toEqual([
+ component.tableActions[1],
+ component.tableActions[2],
+ component.tableActions[3],
+ component.tableActions[6],
+ component.tableActions[7]
+ ]);
+ });
+ });
+
+ describe('with read and create', () => {
+ beforeEach(() => {
+ tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 0);
+ });
+
+ it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
+ scenario.single = 'Clone';
+ permissionHelper.testScenarios(scenario);
+ });
+
+ it(`shows 'Create', 'Clone' and 'Copy' actions`, () => {
+ expect(tableActions.tableActions.length).toBe(3);
+ expect(tableActions.tableActions).toEqual([
+ component.tableActions[0],
+ component.tableActions[4],
+ component.tableActions[5]
+ ]);
+ });
+ });
+
+ describe('with read and edit', () => {
+ beforeEach(() => {
+ tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
+ });
+
+ it(`shows always 'Rename' as main action`, () => {
+ scenario.empty = 'Rename';
+ permissionHelper.testScenarios(scenario);
+ });
+
+ it(`shows 'Rename', 'Protect', 'Unprotect' and 'Rollback' actions`, () => {
+ expect(tableActions.tableActions.length).toBe(4);
+ expect(tableActions.tableActions).toEqual([
+ component.tableActions[1],
+ component.tableActions[2],
+ component.tableActions[3],
+ component.tableActions[6]
+ ]);
+ });
+ });
+
+ 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[7]]);
+ });
+ });
+
+ 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([]);
+ });
+ });
+
+ describe('test unprotected and protected action cases', () => {
+ beforeEach(() => {
+ tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
+ });
+
+ it(`shows none of them if nothing is selected`, () => {
+ permissionHelper.setSelection([]);
+ fixture.detectChanges();
+ expect(tableActions.dropDownActions).toEqual([
+ component.tableActions[1],
+ component.tableActions[6]
+ ]);
+ });
+
+ it(`shows 'Protect' of them if nothing is selected`, () => {
+ permissionHelper.setSelection([{ is_protected: false }]);
+ fixture.detectChanges();
+ expect(tableActions.dropDownActions).toEqual([
+ component.tableActions[1],
+ component.tableActions[2],
+ component.tableActions[6]
+ ]);
+ });
+
+ it(`shows 'Unprotect' of them if nothing is selected`, () => {
+ permissionHelper.setSelection([{ is_protected: true }]);
+ fixture.detectChanges();
+ expect(tableActions.dropDownActions).toEqual([
+ component.tableActions[1],
+ component.tableActions[3],
+ component.tableActions[6]
+ ]);
+ });
+ });
+ });
});
import { ConfirmationModalComponent } from '../../../shared/components/confirmation-modal/confirmation-modal.component';
import { DeletionModalComponent } from '../../../shared/components/deletion-modal/deletion-modal.component';
import { CellTemplate } from '../../../shared/enum/cell-template.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 { ExecutingTask } from '../../../shared/models/executing-task';
import { TaskListService } from '../../../shared/services/task-list.service';
import { TaskManagerService } from '../../../shared/services/task-manager.service';
import { RbdSnapshotFormComponent } from '../rbd-snapshot-form/rbd-snapshot-form.component';
+import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model';
import { RbdSnapshotModel } from './rbd-snapshot.model';
@Component({
rollbackTpl: TemplateRef<any>;
permission: Permission;
+ selection = new CdTableSelection();
+ tableActions: CdTableAction[];
data: RbdSnapshotModel[];
modalRef: BsModalRef;
- selection = new CdTableSelection();
-
builders = {
'rbd/snap/create': (metadata) => {
const model = new RbdSnapshotModel();
private taskListService: TaskListService
) {
this.permission = this.authStorageService.getPermissions().rbdImage;
+ const actions = new RbdSnapshotActionsModel();
+ actions.create.click = () => this.openCreateSnapshotModal();
+ actions.rename.click = () => this.openEditSnapshotModal();
+ actions.protect.click = () => this.toggleProtection();
+ actions.unprotect.click = () => this.toggleProtection();
+ const getImageUri = () =>
+ this.selection.first() &&
+ `${encodeURI(this.poolName)}/${encodeURI(this.rbdName)}/${encodeURI(
+ this.selection.first().name
+ )}`;
+ actions.clone.routerLink = () => `/block/rbd/clone/${getImageUri()}`;
+ actions.copy.routerLink = () => `/block/rbd/copy/${getImageUri()}`;
+ actions.rollback.click = () => this.rollbackModal();
+ actions.deleteSnap.click = () => this.deleteSnapshotModal();
+ this.tableActions = actions.ordering;
}
ngOnInit() {