This is an automated email from the ASF dual-hosted git repository.

chenyulin0719 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/yunikorn-web.git


The following commit(s) were added to refs/heads/master by this push:
     new c86fb1b  [YUNIKORN-2679] Add copy URL button on the allocations panel 
(#193)
c86fb1b is described below

commit c86fb1b38d8a2dd1fcd9f4232669708b88199159
Author: Denis Coric <[email protected]>
AuthorDate: Tue Jun 25 00:05:36 2024 +0800

    [YUNIKORN-2679] Add copy URL button on the allocations panel (#193)
    
    Implemented copy button on allocations sidebar view that copies the 
hot-link to that view.
    
    Closes: #193
    
    Signed-off-by: Yu-Lin Chen <[email protected]>
---
 .../components/apps-view/apps-view.component.html  |  6 +-
 .../components/apps-view/apps-view.component.scss  |  5 ++
 .../apps-view/apps-view.component.spec.ts          | 78 +++++++++++++---------
 .../components/apps-view/apps-view.component.ts    |  8 +++
 .../licenses-modal.component.spec.ts               |  4 +-
 5 files changed, 65 insertions(+), 36 deletions(-)

diff --git a/src/app/components/apps-view/apps-view.component.html 
b/src/app/components/apps-view/apps-view.component.html
index fb80eda..5e44df0 100644
--- a/src/app/components/apps-view/apps-view.component.html
+++ b/src/app/components/apps-view/apps-view.component.html
@@ -145,6 +145,7 @@
       <mat-drawer-content>
         <div class="header">
           <span>{{ selectedRow?.applicationId }} ({{ 
selectedRow?.allocations?.length }} allocations)</span>
+          <span class="far fa-clipboard copy-btn" 
(click)="copyLinkToClipboard()" matTooltip="Click to copy the URL to this view" 
matTooltipShowDelay="500"></span>
           <span class="far fa-solid fa-xmark close-btn" 
(click)="closeDrawer()"></span>
         </div>
         <div class="content">
@@ -162,7 +163,8 @@
               </ng-container>
 
               <ng-container *ngIf="columnDef.colId === 'resource'; else 
renderNext_3">
-                <mat-cell *matCellDef="let element" class="allocations-data" 
[style.flex]="columnDef?.colWidth || 1" >
+                <mat-cell *matCellDef="let element" class="allocations-data" 
[style.flex]="columnDef?.colWidth || 1" matTooltip="
+                          {{element[columnDef.colId]}}" 
matTooltipShowDelay="500" >
                   <ng-container *ngIf="columnDef.colFormatter; else 
showAllocRowData;">
                     <ng-container 
*ngIf="columnDef.colFormatter(element[columnDef.colId]) as colValue">
                       <ul class="mat-res-ul">
@@ -188,7 +190,7 @@
                           [class]="allocationsToggle ? '' : 'ellipsis'"
                           [style.flex]="columnDef?.colWidth || 1"
                           [style.min-height]="allocationsToggle ? '96px' : 
'unset'"
-                          [title]="element[columnDef.colId]"
+                          matTooltip="{{element[columnDef.colId]}}" 
matTooltipShowDelay="500"
                 >{{ element[columnDef.colId] || 'n/a' }}</mat-cell>
               </ng-template>
             </ng-container>
diff --git a/src/app/components/apps-view/apps-view.component.scss 
b/src/app/components/apps-view/apps-view.component.scss
index b2d4622..f88e974 100644
--- a/src/app/components/apps-view/apps-view.component.scss
+++ b/src/app/components/apps-view/apps-view.component.scss
@@ -189,6 +189,11 @@
       color: #f44336;
     }
   }
+  .copy-btn {
+    font-size: 1em;
+    cursor: pointer;
+    padding-left: 5px;
+  }
   .header {
     margin: 20px;
     font-weight: 100;
diff --git a/src/app/components/apps-view/apps-view.component.spec.ts 
b/src/app/components/apps-view/apps-view.component.spec.ts
index af004e6..951b932 100644
--- a/src/app/components/apps-view/apps-view.component.spec.ts
+++ b/src/app/components/apps-view/apps-view.component.spec.ts
@@ -16,26 +16,27 @@
  * limitations under the License.
  */
 
-import {DebugElement} from '@angular/core';
-import {ComponentFixture, TestBed} from '@angular/core/testing';
-import {FormsModule} from '@angular/forms';
-import {MatDividerModule} from '@angular/material/divider';
-import {MatInputModule} from '@angular/material/input';
-import {MatPaginatorModule} from '@angular/material/paginator';
-import {MatSelectModule} from '@angular/material/select';
-import {MatSortModule} from '@angular/material/sort';
-import {MatTableModule} from '@angular/material/table';
-import {MatTooltipModule} from '@angular/material/tooltip';
-import {By, HAMMER_LOADER} from '@angular/platform-browser';
-import {NoopAnimationsModule} from '@angular/platform-browser/animations';
-import {RouterTestingModule} from '@angular/router/testing';
-import {AppInfo} from '@app/models/app-info.model';
-import {SchedulerService} from '@app/services/scheduler/scheduler.service';
-import {MockNgxSpinnerService, MockSchedulerService} from '@app/testing/mocks';
-import {NgxSpinnerService} from 'ngx-spinner';
-import {of} from 'rxjs';
+import { DebugElement } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+import { MatDividerModule } from '@angular/material/divider';
+import { MatInputModule } from '@angular/material/input';
+import { MatPaginatorModule } from '@angular/material/paginator';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSidenavModule } from '@angular/material/sidenav';
+import { MatSortModule } from '@angular/material/sort';
+import { MatTableModule } from '@angular/material/table';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { By, HAMMER_LOADER } from '@angular/platform-browser';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { RouterTestingModule } from '@angular/router/testing';
+import { AppInfo } from '@app/models/app-info.model';
+import { SchedulerService } from '@app/services/scheduler/scheduler.service';
+import { MockNgxSpinnerService, MockSchedulerService } from 
'@app/testing/mocks';
+import { NgxSpinnerService } from 'ngx-spinner';
+import { of } from 'rxjs';
 
-import {AppsViewComponent} from './apps-view.component';
+import { AppsViewComponent } from './apps-view.component';
 
 describe('AppsViewComponent', () => {
   let component: AppsViewComponent;
@@ -55,6 +56,7 @@ describe('AppsViewComponent', () => {
         MatInputModule,
         MatTooltipModule,
         MatSelectModule,
+        MatSidenavModule,
       ],
       providers: [
         { provide: SchedulerService, useValue: MockSchedulerService },
@@ -75,23 +77,35 @@ describe('AppsViewComponent', () => {
     let service: SchedulerService;
     service = TestBed.inject(SchedulerService);
     let appInfo = new AppInfo(
-        'app1',
-        "Memory: 500.0 KB, CPU: 10, pods: 1",
-        "Memory: 0.0 bytes, CPU: 0, pods: n/a",
-        '',
-        1,
-        2,
-        [],
-        2,
-        'RUNNING',
-        []
+      'app1',
+      'Memory: 500.0 KB, CPU: 10, pods: 1',
+      'Memory: 0.0 bytes, CPU: 0, pods: n/a',
+      '',
+      1,
+      2,
+      [],
+      2,
+      'RUNNING',
+      []
     );
     spyOn(service, 'fetchAppList').and.returnValue(of([appInfo]));
-    component.fetchAppListForPartitionAndQueue("default", "root");
+    component.fetchAppListForPartitionAndQueue('default', 'root');
     component.toggle();
     fixture.detectChanges();
     const debugEl: DebugElement = fixture.debugElement;
-    
expect(debugEl.query(By.css('mat-cell.mat-column-usedResource')).nativeElement.innerText).toContain('Memory:
 500.0 KB\nCPU: 10\npods: 1');
-    
expect(debugEl.query(By.css('mat-cell.mat-column-pendingResource')).nativeElement.innerText).toContain('Memory:
 0.0 bytes\nCPU: 0\npods: n/a');
+    expect(
+      
debugEl.query(By.css('mat-cell.mat-column-usedResource')).nativeElement.innerText
+    ).toContain('Memory: 500.0 KB\nCPU: 10\npods: 1');
+    expect(
+      
debugEl.query(By.css('mat-cell.mat-column-pendingResource')).nativeElement.innerText
+    ).toContain('Memory: 0.0 bytes\nCPU: 0\npods: n/a');
+  });
+
+  it('should copy the allocations URL to clipboard', () => {
+    const debugEl: DebugElement = fixture.debugElement;
+    const copyButton = debugEl.query(By.css('.copy-btn'));
+    const copyButtonSpy = spyOn(component, 'copyLinkToClipboard');
+    copyButton.triggerEventHandler('click', null);
+    expect(copyButtonSpy).toHaveBeenCalled();
   });
 });
diff --git a/src/app/components/apps-view/apps-view.component.ts 
b/src/app/components/apps-view/apps-view.component.ts
index d16e811..d742aac 100644
--- a/src/app/components/apps-view/apps-view.component.ts
+++ b/src/app/components/apps-view/apps-view.component.ts
@@ -404,4 +404,12 @@ export class AppsViewComponent implements OnInit {
     this.matDrawer.close();
     this.removeRowSelection();
   }
+
+  copyLinkToClipboard() {
+    const url = window.location.href.split('?')[0];
+    const copyString = 
`${url}?partition=${this.partitionSelected}&queue=${this.leafQueueSelected}&applicationId=${this?.selectedRow?.applicationId}`;
+    navigator.clipboard
+      .writeText(copyString)
+      .catch((error) => console.error('Writing to the clipboard is not 
allowed. ', error));
+  }
 }
diff --git a/src/app/components/licenses-modal/licenses-modal.component.spec.ts 
b/src/app/components/licenses-modal/licenses-modal.component.spec.ts
index 37af6c7..5e924e2 100644
--- a/src/app/components/licenses-modal/licenses-modal.component.spec.ts
+++ b/src/app/components/licenses-modal/licenses-modal.component.spec.ts
@@ -19,7 +19,7 @@
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { LicensesModalComponent } from './licenses-modal.component';
-import { MatDialogRef } from '@angular/material/dialog';
+import { MatDialogRef, MatDialogModule } from '@angular/material/dialog';
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 
 describe('LicensesModalComponent', () => {
@@ -30,7 +30,7 @@ describe('LicensesModalComponent', () => {
     await TestBed.configureTestingModule({
       declarations: [LicensesModalComponent],
       providers: [{ provide: MatDialogRef, useValue: {} }],
-      imports: [HttpClientTestingModule],
+      imports: [HttpClientTestingModule, MatDialogModule],
     }).compileComponents();
     fixture = TestBed.createComponent(LicensesModalComponent);
     component = fixture.componentInstance;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to