<cd-table-actions [permission]="permissions.osd"
[selection]="selection"
class="btn-group"
+ id="osd-actions"
[tableActions]="tableActions">
</cd-table-actions>
-
- <div class="btn-group"
- dropdown
- *ngIf="advancedTableActions.length > 0">
- <button type="button"
- class="btn btn-sm btn-default btn-label tc_configureCluster"
- (click)="advancedTableActions[0].click()">
- <i class="fa fa-fw {{ advancedTableActions[0].icon }}"></i><span>{{ advancedTableActions[0].name }}</span>
- </button>
- <button type="button"
- dropdownToggle
- class="btn btn-sm btn-default dropdown-toggle dropdown-toggle-split"
- *ngIf="advancedTableActions.length > 1">
- <span class="caret caret-black"></span>
- </button>
- <ul *dropdownMenu
- class="dropdown-menu"
- role="menu">
- <ng-container *ngFor="let action of advancedTableActions | slice:1">
- <li role="menuitem">
- <a class="dropdown-item"
- (click)="action.click()">
- <i class="fa fa-fw {{ action.icon }}"
- aria-hidden="true">
- </i>
- <ng-container>{{ action.name }}</ng-container>
- </a>
- </li>
- </ng-container>
- </ul>
- </div>
+ <cd-table-actions [permission]="{read: true}"
+ [selection]="selection"
+ dropDownOnly="Cluster-wide configuration"
+ btnColor="default"
+ class="btn-group"
+ id="cluster-wide-actions"
+ [tableActions]="clusterWideActions">
+ </cd-table-actions>
</div>
<cd-osd-details cdTableDetail
-.caret.caret-black {
- color: #000000;
-}
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']
+ });
}
};
beforeEach(() => {
fixture = TestBed.createComponent(OsdListComponent);
- fixture.detectChanges();
component = fixture.componentInstance;
osdService = TestBed.get(OsdService);
modalServiceShowSpy = spyOn(TestBed.get(BsModalService), 'show').and.stub();
});
it('should have columns that are sortable', () => {
+ fixture.detectChanges();
expect(component.columns.every((column) => Boolean(column.prop))).toBeTruthy();
});
});
});
+ 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 };
const getTableActionComponent = () => {
fixture.detectChanges();
- return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
+ return fixture.debugElement.query(By.css('#osd-actions')).componentInstance;
};
beforeEach(() => {
});
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'));
tableActions: CdTableAction[];
bsModalRef: BsModalRef;
columns: CdTableColumn[];
- advancedTableActions: any[];
+ clusterWideActions: CdTableAction[];
osds = [];
selection = new CdTableSelection();
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 },
cellTransformation: CellTemplate.perSecond
}
];
-
- this.removeActionsWithNoPermissions();
}
get hasOsdSelected() {
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);
- }
}
dropdown>
<ng-container *ngIf="getCurrentButton() as action">
<button type="button"
- class="btn btn-sm btn-primary"
+ class="btn btn-sm btn-{{btnColor}}"
[ngClass]="{'disabled': disableSelectionAction(action)}"
(click)="useClickAction(action)"
[routerLink]="useRouterLink(action)">
<button type="button"
dropdownToggle
*ngIf="showDropDownActions()"
- class="btn btn-sm btn-primary dropdown-toggle dropdown-toggle-split">
- <ng-container *ngIf="onlyDropDown">{{ onlyDropDown }}</ng-container>
+ class="btn btn-sm btn-{{btnColor}} dropdown-toggle dropdown-toggle-split">
+ <ng-container *ngIf="dropDownOnly">{{ dropDownOnly }} </ng-container>
<span class="caret"></span>
- <span *ngIf="!onlyDropDown"
+ <span *ngIf="!dropDownOnly"
class="sr-only"></span>
</button>
<ul *dropdownMenu
let scenario;
let permissionHelper: PermissionHelper;
- const setUpTableActions = () => {
+ const getTableActionComponent = (): TableActionsComponent => {
component.tableActions = [
addAction,
editAction,
copyAction,
deleteAction
];
- };
-
- const getTableActionComponent = (): TableActionsComponent => {
- setUpTableActions();
component.ngOnInit();
return component;
};
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);
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);
});
});
});
- 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([]);
});
});
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[] = [];
* @returns {CdTableAction}
*/
getCurrentButton(): CdTableAction {
- if (this.onlyDropDown) {
+ if (this.dropDownOnly) {
return;
}
let buttonAction = this.dropDownActions.find((tableAction) => this.showableAction(tableAction));
* @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)
color: $color-primary;
background-color: $color-button-badge;
}
-.btn-primary .caret {
- color: $color-button-caret;
-}
.btn-default {
border-radius: $button-radius;
}
padding-left: 30px;
padding-right: 30px;
}
-/* Caret */
-.caret {
- color: $color-caret-text;
-}
/* Progressbar */
.progress-bar {
background-image: none !important;