]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Use pipe instead of calling function within template wherever possible 38154/head
authorVolker Theile <vtheile@suse.com>
Wed, 11 Nov 2020 08:02:23 +0000 (09:02 +0100)
committerVolker Theile <vtheile@suse.com>
Wed, 25 Nov 2020 10:28:00 +0000 (11:28 +0100)
https://tracker.ceph.com/issues/48181

Signed-off-by: Volker Theile <vtheile@suse.com>
29 files changed:
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/block.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-configuration-list/rbd-configuration-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-configuration-list/rbd-configuration-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-trash-list/rbd-trash-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cephfs/cephfs-directories/cephfs-directories.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-list/osd-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-daemon-list/service-daemon-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-daemon-list/service-daemon-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-daemon-list/service-daemon-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/service-details/service-details.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-user-form/rgw-user-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw.module.ts
src/pybind/mgr/dashboard/frontend/src/app/core/auth/auth.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/datatable.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts

index e6c5e68ca0d63b780abd53117389b8a568c68c90..472fe37f19fa60fe67f552d94eb7fa4ff2140471 100644 (file)
@@ -5,6 +5,7 @@ import { RouterModule, Routes } from '@angular/router';
 
 import { TreeModule } from '@circlon/angular-tree-component';
 import { NgbNavModule, NgbPopoverModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 
 import { ActionLabels, URLVerbs } from '~/app/shared/constants/app.constants';
 import { FeatureTogglesGuardService } from '~/app/shared/services/feature-toggles-guard.service';
@@ -45,6 +46,7 @@ import { RbdTrashRestoreModalComponent } from './rbd-trash-restore-modal/rbd-tra
     NgbNavModule,
     NgbPopoverModule,
     NgbTooltipModule,
+    NgxPipeFunctionModule,
     SharedModule,
     RouterModule,
     TreeModule
index 64364f67da63ce5a56918c0c6e1ccea35a76c130..6c3e8c0278ca7a9dbabe785e9df5cea14cacc1fb 100644 (file)
@@ -5,7 +5,6 @@
 </cd-table>
 
 <ng-template #configurationSourceTpl
-             let-row="row"
              let-value="value">
 
   <div [ngSwitch]="value">
index ae1a52657351efb04b5fb9c9d8869e20ec4ff809..f54ad02720c5276d19956d62195a8b12f1c72085 100644 (file)
@@ -8,11 +8,10 @@ import { NgxDatatableModule } from '@swimlane/ngx-datatable';
 import { ChartsModule } from 'ng2-charts';
 
 import { ComponentsModule } from '~/app/shared/components/components.module';
-import { TableComponent } from '~/app/shared/datatable/table/table.component';
 import { RbdConfigurationEntry } from '~/app/shared/models/configuration';
-import { PipesModule } from '~/app/shared/pipes/pipes.module';
 import { FormatterService } from '~/app/shared/services/formatter.service';
 import { RbdConfigurationService } from '~/app/shared/services/rbd-configuration.service';
+import { SharedModule } from '~/app/shared/shared.module';
 import { configureTestBed } from '~/testing/unit-test-helper';
 import { RbdConfigurationListComponent } from './rbd-configuration-list.component';
 
@@ -29,10 +28,10 @@ describe('RbdConfigurationListComponent', () => {
       ComponentsModule,
       NgbDropdownModule,
       ChartsModule,
-      PipesModule,
+      SharedModule,
       NgbTooltipModule
     ],
-    declarations: [RbdConfigurationListComponent, TableComponent],
+    declarations: [RbdConfigurationListComponent],
     providers: [FormatterService, RbdConfigurationService]
   });
 
index 340d0d0d4993a2dee54ff733ffbb20e706461896..044a1e9ac0abfa92a947ea7e7508f4b80d3df98e 100644 (file)
 </ng-template>
 
 <ng-template #deleteTpl
-             let-expiresAt>
+             let-expiresAt="expiresAt"
+             let-isExpired="isExpired">
   <p class="text-danger"
-     *ngIf="!isExpired(expiresAt)">
+     *ngIf="!isExpired">
     <strong>
       <ng-container i18n>This image is protected until {{ expiresAt | cdDate }}.</ng-container>
     </strong>
index 939f04e66f8f7cd02bec4a26607ab3588545819f..17d8eed0fb68616faca6eca23c43fd7ce2bc7613 100644 (file)
@@ -6,6 +6,7 @@ import { RouterTestingModule } from '@angular/router/testing';
 
 import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
 import moment from 'moment';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 import { ToastrModule } from 'ngx-toastr';
 import { of } from 'rxjs';
 
@@ -34,6 +35,7 @@ describe('RbdTrashListComponent', () => {
       RouterTestingModule,
       SharedModule,
       NgbNavModule,
+      NgxPipeFunctionModule,
       ToastrModule.forRoot()
     ],
     providers: [TaskListService]
index a0fafc192382fbe7f1c4b3cb2df2dd0457728297..43fe42b99fa3895fb50470604b082b399e568908 100644 (file)
@@ -201,13 +201,14 @@ export class RbdTrashListComponent implements OnInit {
     const namespace = this.selection.first().namespace;
     const imageId = this.selection.first().id;
     const expiresAt = this.selection.first().deferment_end_time;
+    const isExpired = moment().isAfter(expiresAt);
     const imageIdSpec = new ImageSpec(poolName, namespace, imageId);
 
     this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
       itemDescription: 'RBD',
       itemNames: [imageIdSpec],
       bodyTemplate: this.deleteTpl,
-      bodyContext: { $implicit: expiresAt },
+      bodyContext: { expiresAt, isExpired },
       submitActionObservable: () =>
         this.taskWrapper.wrapTaskAroundCall({
           task: new FinishedTask('rbd/trash/remove', {
@@ -218,10 +219,6 @@ export class RbdTrashListComponent implements OnInit {
     });
   }
 
-  isExpired(expiresAt: string): boolean {
-    return moment().isAfter(expiresAt);
-  }
-
   purgeModal() {
     this.modalService.show(RbdTrashPurgeModalComponent);
   }
index f23ae749f5bd4440571732cde293860bee96eaf3..c8f6c22a14b9a093ae846e1de101d99732cbf883 100644 (file)
@@ -936,7 +936,10 @@ describe('CephfsDirectoriesComponent', () => {
     });
 
     it('should test all quota table actions permission combinations', () => {
-      const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
+      const permissionHelper: PermissionHelper = new PermissionHelper(component.permission, {
+        single: { dirValue: 0 },
+        multiple: [{ dirValue: 0 }, {}]
+      });
       const tableActions = permissionHelper.setPermissionsAndGetActions(
         component.quota.tableActions
       );
index 62464cadefe947f276e45cebd8a040898d4abf55..e52aa979081b4030d115b98dae8d8b6879902939 100644 (file)
@@ -13,6 +13,7 @@ import {
   NgbTooltipModule,
   NgbTypeaheadModule
 } from '@ng-bootstrap/ng-bootstrap';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 
 import { SharedModule } from '~/app/shared/shared.module';
 import { PerformanceCounterModule } from '../performance-counter/performance-counter.module';
@@ -70,7 +71,8 @@ import { TelemetryComponent } from './telemetry/telemetry.component';
     CephSharedModule,
     NgbDatepickerModule,
     NgbPopoverModule,
-    NgbDropdownModule
+    NgbDropdownModule,
+    NgxPipeFunctionModule
   ],
   declarations: [
     HostsComponent,
index fe236902e978128fbdfb681bf758aeaef4fe9a1c..9f4f3e2152cf4067322cb6e23ebe89a68a25b2f6 100644 (file)
@@ -54,8 +54,9 @@
 <div [ngbNavOutlet]="nav"></div>
 
 <ng-template #markOsdConfirmationTpl
-             let-markActionDescription="markActionDescription">
-  <ng-container i18n><strong>OSD(s) {{  getSelectedOsdIds() | join }}</strong> will be marked
+             let-markActionDescription="markActionDescription"
+             let-osdIds="osdIds">
+  <ng-container i18n><strong>OSD(s) {{ osdIds | join }}</strong> will be marked
   <strong>{{ markActionDescription }}</strong> if you proceed.</ng-container>
 </ng-template>
 
index 7d4c85e44f57ba8a1f97ff0a7c1535b014404ab9..d6f865471481bf7f26eb34af75d38eddfc6223f7 100644 (file)
@@ -425,7 +425,7 @@ describe('OsdListComponent', () => {
       const tableActionElement = fixture.debugElement.query(By.directive(TableActionsComponent));
       const toClassName = TestBed.inject(TableActionsComponent).toClassName;
       const getActionClasses = (action: CdTableAction) =>
-        tableActionElement.query(By.css(`[ngbDropdownItem].${toClassName(action.name)}`)).classes;
+        tableActionElement.query(By.css(`[ngbDropdownItem].${toClassName(action)}`)).classes;
 
       component.tableActions.forEach((action) => {
         if (action.name === 'Create') {
index b03daa392df7191468f54766258eb98218118a52..45dc840655ddd727c3bd12f4e85389114e56a2aa 100644 (file)
@@ -373,7 +373,10 @@ export class OsdListComponent extends ListWithDetails implements OnInit {
    */
   getSelectedOsdIds(): number[] {
     const osdIds = this.osds.map((osd) => osd.id);
-    return this.selection.selected.map((row) => row.id).filter((id) => osdIds.includes(id));
+    return this.selection.selected
+      .map((row) => row.id)
+      .filter((id) => osdIds.includes(id))
+      .sort();
   }
 
   getSelectedOsds(): any[] {
@@ -483,12 +486,14 @@ export class OsdListComponent extends ListWithDetails implements OnInit {
   }
 
   showConfirmationModal(markAction: string, onSubmit: (id: number) => Observable<any>) {
+    const osdIds = this.getSelectedOsdIds();
     this.bsModalRef = this.modalService.show(ConfirmationModalComponent, {
       titleText: $localize`Mark OSD ${markAction}`,
       buttonText: $localize`Mark ${markAction}`,
       bodyTpl: this.markOsdConfirmationTpl,
       bodyContext: {
-        markActionDescription: markAction
+        markActionDescription: markAction,
+        osdIds
       },
       onSubmit: () => {
         observableForkJoin(
index 078ac04d42ed0badfa3ba1e2c7d9591bd8082f9e..f649726a3fe76640fdc577af73b9d5fceb13bcdc 100644 (file)
@@ -11,7 +11,7 @@
 <ng-template #statusTpl
              let-row="row">
   <span class="badge"
-        [ngClass]="getStatusClass(row.status)">
+        [ngClass]="row | pipeFunction:getStatusClass">
     {{ row.status_desc }}
   </span>
 </ng-template>
index 89b06b9791016af1187e66dda29f66ca3e1450d6..184e2e7ef6fa894b13a759fca44d513cfcf5eb43 100644 (file)
@@ -2,6 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 import _ from 'lodash';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 import { of } from 'rxjs';
 
 import { CephModule } from '~/app/ceph/ceph.module';
@@ -77,7 +78,7 @@ describe('ServiceDaemonListComponent', () => {
   };
 
   configureTestBed({
-    imports: [HttpClientTestingModule, CephModule, CoreModule, SharedModule]
+    imports: [HttpClientTestingModule, CephModule, CoreModule, NgxPipeFunctionModule, SharedModule]
   });
 
   beforeEach(() => {
index 5a7b223bc874801bc3f75788d7d4a32829bcdc29..95795f6228512c62db1205958a70ea9c9337136a 100644 (file)
@@ -149,14 +149,14 @@ export class ServiceDaemonListComponent implements OnInit, OnChanges, AfterViewI
     }
   }
 
-  getStatusClass(status: number) {
+  getStatusClass(row: Daemon): string {
     return _.get(
       {
         '-1': 'badge-danger',
         '0': 'badge-warning',
         '1': 'badge-success'
       },
-      status,
+      row.status,
       'badge-dark'
     );
   }
index 23bb679414b15241e1507f493c900371c428bdf5..6be3b2689526f06e6c54352e27775dfa8f4d2d3f 100644 (file)
@@ -3,6 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { RouterTestingModule } from '@angular/router/testing';
 
 import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 
 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
 import { SummaryService } from '~/app/shared/services/summary.service';
@@ -16,7 +17,13 @@ describe('ServiceDetailsComponent', () => {
   let fixture: ComponentFixture<ServiceDetailsComponent>;
 
   configureTestBed({
-    imports: [HttpClientTestingModule, RouterTestingModule, SharedModule, NgbNavModule],
+    imports: [
+      HttpClientTestingModule,
+      RouterTestingModule,
+      SharedModule,
+      NgbNavModule,
+      NgxPipeFunctionModule
+    ],
     declarations: [ServiceDetailsComponent, ServiceDaemonListComponent],
     providers: [{ provide: SummaryService, useValue: { subscribeOnce: jest.fn() } }]
   });
index bf4c206e2f4e1db3ab79a85aa29e94f3d3d0f0d8..c1b6ae5db835d471e155160185054fceab5005b3 100644 (file)
@@ -484,7 +484,7 @@ export class PoolFormComponent extends CdForm implements OnInit {
     return (ecpControl.valid || ecpControl.disabled) && ecp ? pgs / (ecp.k + ecp.m) : 0;
   }
 
-  private alignPgs(pgs = this.form.getValue('pgNum')) {
+  alignPgs(pgs = this.form.getValue('pgNum')) {
     this.setPgs(Math.round(this.calculatePgPower(pgs < 1 ? 1 : pgs)));
   }
 
index 164f6c2b3e8f91109fdadf659a084ea11419eddf..acd6c02fd60a0e378f82ab1e30914b34c0e8de23 100644 (file)
                 <div class="col-12">
                   <button type="button"
                           class="btn btn-light float-right tc_addCapButton"
-                          [disabled]="hasAllCapabilities()"
+                          [disabled]="capabilities | pipeFunction:hasAllCapabilities"
                           i18n-ngbTooltip
                           ngbTooltip="All capabilities are already added."
-                          [disableTooltip]="!hasAllCapabilities()"
+                          [disableTooltip]="!(capabilities | pipeFunction:hasAllCapabilities)"
                           triggers="pointerenter:pointerleave"
                           (click)="showCapabilityModal()">
                     <i [ngClass]="[icons.add]"></i>
index 987fd62d3a80114048202c72d4a8fe4b4b06b29f..091e7d124531c326bd4991e02a3a4546784bc4ec 100644 (file)
@@ -5,6 +5,7 @@ import { Router } from '@angular/router';
 import { RouterTestingModule } from '@angular/router/testing';
 
 import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 import { ToastrModule } from 'ngx-toastr';
 import { of as observableOf } from 'rxjs';
 
@@ -32,7 +33,8 @@ describe('RgwUserFormComponent', () => {
       RouterTestingModule,
       SharedModule,
       ToastrModule.forRoot(),
-      NgbTooltipModule
+      NgbTooltipModule,
+      NgxPipeFunctionModule
     ]
   });
 
@@ -322,7 +324,7 @@ describe('RgwUserFormComponent', () => {
 
       fixture.detectChanges();
 
-      expect(component.hasAllCapabilities()).toBeTruthy();
+      expect(component.hasAllCapabilities(component.capabilities)).toBeTruthy();
       const capabilityButton = fixture.debugElement.nativeElement.querySelector('.tc_addCapButton');
       expect(capabilityButton.disabled).toBeTruthy();
     });
index 5fbbb9face9376d39f763666b1a7f3997d06156e..0b3103c9f8de9b9d89fbceab8eddb0c4a46b355b 100644 (file)
@@ -378,7 +378,7 @@ export class RgwUserFormComponent extends CdForm implements OnInit {
       // Add
       // Create an observable to add the capability when the form is submitted.
       this.submitObservables.push(this.rgwUserService.addCapability(uid, cap.type, cap.perm));
-      this.capabilities.push(cap);
+      this.capabilities = [...this.capabilities, cap]; // Notify Angular CD
     }
     // Mark the form as dirty to be able to submit it.
     this.userForm.markAsDirty();
@@ -398,12 +398,13 @@ export class RgwUserFormComponent extends CdForm implements OnInit {
     );
     // Remove the capability to update the UI.
     this.capabilities.splice(index, 1);
+    this.capabilities = [...this.capabilities]; // Notify Angular CD
     // Mark the form as dirty to be able to submit it.
     this.userForm.markAsDirty();
   }
 
-  hasAllCapabilities() {
-    return !_.difference(RgwUserCapabilities.getAll(), _.map(this.capabilities, 'type')).length;
+  hasAllCapabilities(capabilities: RgwUserCapability[]) {
+    return !_.difference(RgwUserCapabilities.getAll(), _.map(capabilities, 'type')).length;
   }
 
   /**
index 02f5392302877d5ca3cde97d45943dc8a76f29ab..00ef8bd49e512acdaaab02d294e07e99c5efa13d 100644 (file)
@@ -4,6 +4,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { RouterModule, Routes } from '@angular/router';
 
 import { NgbNavModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 
 import { ActionLabels, URLVerbs } from '~/app/shared/constants/app.constants';
 import { AuthGuardService } from '~/app/shared/services/auth-guard.service';
@@ -32,7 +33,8 @@ import { RgwUserSwiftKeyModalComponent } from './rgw-user-swift-key-modal/rgw-us
     PerformanceCounterModule,
     NgbNavModule,
     RouterModule,
-    NgbTooltipModule
+    NgbTooltipModule,
+    NgxPipeFunctionModule
   ],
   exports: [
     Rgw501Component,
index dc17a215bfcaef07d1bd2d214acc0416cdbfc2ab..56b92e26377a5adda031a453b371d193e4210ea5 100644 (file)
@@ -4,6 +4,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { RouterModule, Routes } from '@angular/router';
 
 import { NgbNavModule, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 
 import { ActionLabels, URLVerbs } from '~/app/shared/constants/app.constants';
 import { SharedModule } from '~/app/shared/shared.module';
@@ -26,6 +27,7 @@ import { UserTabsComponent } from './user-tabs/user-tabs.component';
     SharedModule,
     NgbNavModule,
     NgbPopoverModule,
+    NgxPipeFunctionModule,
     RouterModule
   ],
   declarations: [
index b32ee065589b62d47a5d66a7909931fbd9d89949..ede8f2368b70f2405fc4ed9c40eef6a71d1025c3 100644 (file)
@@ -5,6 +5,7 @@ import { RouterModule } from '@angular/router';
 
 import { NgbDropdownModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
 import { NgxDatatableModule } from '@swimlane/ngx-datatable';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 
 import { ComponentsModule } from '../components/components.module';
 import { PipesModule } from '../pipes/pipes.module';
@@ -16,6 +17,7 @@ import { TableComponent } from './table/table.component';
   imports: [
     CommonModule,
     NgxDatatableModule,
+    NgxPipeFunctionModule,
     FormsModule,
     NgbDropdownModule,
     NgbTooltipModule,
index c8061057154e45fcb24348563b18bf6cb09fe79d..0f7c1a9cd995c3f08834848aadf181e659323a76 100644 (file)
@@ -1,14 +1,14 @@
 <div class="btn-group">
-  <ng-container *ngIf="getCurrentButton() as action">
+  <ng-container *ngIf="currentAction">
     <button type="button"
-            title="{{ useDisableDesc(action) }}"
+            title="{{ useDisableDesc(currentAction) }}"
             class="btn btn-{{btnColor}}"
-            [ngClass]="{'disabled': disableSelectionAction(action)}"
-            (click)="useClickAction(action)"
-            [routerLink]="useRouterLink(action)"
-            [preserveFragment]="action.preserveFragment ? '' : null">
-      <i [ngClass]="[action.icon]"></i>
-      <span>{{ action.name }}</span>
+            [ngClass]="{'disabled': disableSelectionAction(currentAction)}"
+            (click)="useClickAction(currentAction)"
+            [routerLink]="useRouterLink(currentAction)"
+            [preserveFragment]="currentAction.preserveFragment ? '' : null">
+      <i [ngClass]="[currentAction.icon]"></i>
+      <span>{{ currentAction.name }}</span>
     </button>
   </ng-container>
   <div class="btn-group"
@@ -17,7 +17,7 @@
        role="group"
        aria-label="Button group with nested dropdown">
     <button class="btn btn-{{btnColor}} dropdown-toggle-split"
-            *ngIf="showDropDownActions()"
+            *ngIf="dropDownActions.length > 1"
             ngbDropdownToggle>
       <ng-container *ngIf="dropDownOnly">{{ dropDownOnly }} </ng-container>
       <span *ngIf="!dropDownOnly"
@@ -27,7 +27,7 @@
          ngbDropdownMenu>
       <ng-container *ngFor="let action of dropDownActions">
         <button ngbDropdownItem
-                class="{{ toClassName(action['name']) }}"
+                class="{{ toClassName(action) }}"
                 title="{{ useDisableDesc(action) }}"
                 (click)="useClickAction(action)"
                 [routerLink]="useRouterLink(action)"
index d88a3be292c0692dc41618d381bb990383124083..81cc1b9720793b431d442f9b7616ef06f3d95e3a 100644 (file)
@@ -1,6 +1,8 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { RouterTestingModule } from '@angular/router/testing';
 
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
+
 import { ComponentsModule } from '~/app/shared/components/components.module';
 import { CdTableAction } from '~/app/shared/models/cd-table-action';
 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
@@ -21,7 +23,7 @@ describe('TableActionsComponent', () => {
 
   configureTestBed({
     declarations: [TableActionsComponent],
-    imports: [ComponentsModule, RouterTestingModule]
+    imports: [ComponentsModule, NgxPipeFunctionModule, RouterTestingModule]
   });
 
   beforeEach(() => {
@@ -157,9 +159,9 @@ describe('TableActionsComponent', () => {
   });
 
   it('should convert any name to a proper CSS class', () => {
-    expect(component.toClassName('Create')).toBe('create');
-    expect(component.toClassName('Mark x down')).toBe('mark-x-down');
-    expect(component.toClassName('?Su*per!')).toBe('super');
+    expect(component.toClassName({ name: 'Create' } as CdTableAction)).toBe('create');
+    expect(component.toClassName({ name: 'Mark x down' } as CdTableAction)).toBe('mark-x-down');
+    expect(component.toClassName({ name: '?Su*per!' } as CdTableAction)).toBe('super');
   });
 
   describe('useDisableDesc', () => {
index fc951233ac4c5cf7e27231da799b5054e72a067e..0497f930193a726103a3d94d34057520b68d26f1 100644 (file)
@@ -1,4 +1,4 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
 
 import _ from 'lodash';
 
@@ -12,7 +12,7 @@ import { Permission } from '~/app/shared/models/permissions';
   templateUrl: './table-actions.component.html',
   styleUrls: ['./table-actions.component.scss']
 })
-export class TableActionsComponent implements OnInit {
+export class TableActionsComponent implements OnChanges, OnInit {
   @Input()
   permission: Permission;
   @Input()
@@ -28,6 +28,7 @@ export class TableActionsComponent implements OnInit {
   @Input()
   dropDownOnly?: string;
 
+  currentAction?: CdTableAction;
   // Array with all visible actions
   dropDownActions: CdTableAction[] = [];
 
@@ -35,11 +36,22 @@ export class TableActionsComponent implements OnInit {
 
   ngOnInit() {
     this.removeActionsWithNoPermissions();
+    this.onSelectionChange();
+  }
+
+  ngOnChanges(changes: SimpleChanges) {
+    if (changes.selection) {
+      this.onSelectionChange();
+    }
+  }
+
+  onSelectionChange(): void {
     this.updateDropDownActions();
+    this.updateCurrentAction();
   }
 
-  toClassName(name: string): string {
-    return name
+  toClassName(action: CdTableAction): string {
+    return action.name
       .replace(/ /g, '-')
       .replace(/[^a-z-]/gi, '')
       .toLowerCase();
@@ -59,7 +71,7 @@ export class TableActionsComponent implements OnInit {
     );
   }
 
-  private updateDropDownActions() {
+  private updateDropDownActions(): void {
     this.dropDownActions = this.tableActions.filter((action) =>
       action.visible ? action.visible(this.selection) : action
     );
@@ -73,18 +85,17 @@ export class TableActionsComponent implements OnInit {
    * Default button conditions of actions:
    * - 'create' actions can be used with no or multiple selections
    * - 'update' and 'delete' actions can be used with one selection
-   *
-   * @returns {CdTableAction}
    */
-  getCurrentButton(): CdTableAction {
+  private updateCurrentAction(): void {
     if (this.dropDownOnly) {
-      return undefined;
+      this.currentAction = undefined;
+      return;
     }
     let buttonAction = this.dropDownActions.find((tableAction) => this.showableAction(tableAction));
     if (!buttonAction && this.dropDownActions.length > 0) {
       buttonAction = this.dropDownActions[0];
     }
-    return buttonAction;
+    this.currentAction = buttonAction;
   }
 
   /**
@@ -129,11 +140,6 @@ export class TableActionsComponent implements OnInit {
     );
   }
 
-  showDropDownActions() {
-    this.updateDropDownActions();
-    return this.dropDownActions.length > 1;
-  }
-
   useClickAction(action: CdTableAction) {
     /**
      * In order to show tooltips for deactivated menu items, the class
@@ -150,7 +156,6 @@ export class TableActionsComponent implements OnInit {
       const result = action.disable(this.selection);
       return _.isString(result) ? result : undefined;
     }
-
     return undefined;
   }
 }
index 7beb142ba48716b52bb9c682811d496a39209b77..150d44241051e23a09a0216ebd59a7205dca495a 100644 (file)
@@ -4,6 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing';
 
 import { NgbDropdownModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
 import { NgxDatatableModule } from '@swimlane/ngx-datatable';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 
 import { ComponentsModule } from '~/app/shared/components/components.module';
 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
@@ -27,7 +28,8 @@ describe('TableKeyValueComponent', () => {
       RouterTestingModule,
       NgbDropdownModule,
       PipesModule,
-      NgbTooltipModule
+      NgbTooltipModule,
+      NgxPipeFunctionModule
     ]
   });
 
index 0d5460a989e80482359f32042c91f130aa42a3c2..71ba156cf3f607cb108325d0a1565a4b015a2e9c 100644 (file)
 
 <ng-template #classAddingTpl
              let-value="value">
-  <span class="{{useCustomClass(value)}}">{{ value }}</span>
+  <span class="{{ value | pipeFunction:useCustomClass:this }}">{{ value }}</span>
 </ng-template>
 
 <ng-template #badgeTpl
index f3d4f4e36c5c6b03e5807c2209fa82391f62f9eb..a2a329ab645646a4c7a32a5dfbcab66a1a1bb97a 100644 (file)
@@ -7,6 +7,7 @@ import { RouterTestingModule } from '@angular/router/testing';
 import { NgbDropdownModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
 import { NgxDatatableModule } from '@swimlane/ngx-datatable';
 import _ from 'lodash';
+import { NgxPipeFunctionModule } from 'ngx-pipe-function';
 
 import { ComponentsModule } from '~/app/shared/components/components.module';
 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
@@ -42,6 +43,7 @@ describe('TableComponent', () => {
     imports: [
       BrowserAnimationsModule,
       NgxDatatableModule,
+      NgxPipeFunctionModule,
       FormsModule,
       ComponentsModule,
       RouterTestingModule,
index 9e8442da1858fdb34b6aee982ddf0638fa970527..28f88e1fac86cd9f131aecdf9df92cb87bf90b00 100644 (file)
@@ -45,9 +45,18 @@ export function configureTestBed(configuration: any, entryComponents?: any) {
 export class PermissionHelper {
   tac: TableActionsComponent;
   permission: Permission;
+  selection: { single: object; multiple: object[] };
 
-  constructor(permission: Permission) {
+  /**
+   * @param permission The permissions used by this test.
+   * @param selection The selection used by this test. Configure this if
+   *   the table actions require a more complex selection object to perform
+   *   a correct test run.
+   *   Defaults to `{ single: {}, multiple: [{}, {}] }`.
+   */
+  constructor(permission: Permission, selection?: { single: object; multiple: object[] }) {
     this.permission = permission;
+    this.selection = _.defaultTo(selection, { single: {}, multiple: [{}, {}] });
   }
 
   setPermissionsAndGetActions(tableActions: CdTableAction[]): any {
@@ -91,11 +100,13 @@ export class PermissionHelper {
   testScenarios() {
     const result: any = {};
     // 'multiple selections'
-    result.multiple = this.testScenario([{}, {}]);
+    result.multiple = this.testScenario(this.selection.multiple);
     // 'select executing item'
-    result.executing = this.testScenario([{ cdExecuting: 'someAction' }]);
+    result.executing = this.testScenario([
+      _.merge({ cdExecuting: 'someAction' }, this.selection.single)
+    ]);
     // 'select non-executing item'
-    result.single = this.testScenario([{}]);
+    result.single = this.testScenario([this.selection.single]);
     // 'no selection'
     result.no = this.testScenario([]);
 
@@ -104,12 +115,13 @@ export class PermissionHelper {
 
   private testScenario(selection: object[]) {
     this.setSelection(selection);
-    const btn = this.tac.getCurrentButton();
-    return btn ? btn.name : '';
+    const action: CdTableAction = this.tac.currentAction;
+    return action ? action.name : '';
   }
 
   setSelection(selection: object[]) {
     this.tac.selection.selected = selection;
+    this.tac.onSelectionChange();
   }
 }
 
@@ -638,7 +650,7 @@ export class TableActionHelper {
     const tableActionElement = fixture.debugElement.query(By.directive(TableActionsComponent));
     const toClassName = TestBed.inject(TableActionsComponent).toClassName;
     const getActionElement = (action: CdTableAction) =>
-      tableActionElement.query(By.css(`[ngbDropdownItem].${toClassName(action.name)}`));
+      tableActionElement.query(By.css(`[ngbDropdownItem].${toClassName(action)}`));
 
     const actions = {};
     tableActions.forEach((action) => {