Skip to content

Commit

Permalink
[YUNIKORN-2679] Add copy URL button on the allocations panel (#193)
Browse files Browse the repository at this point in the history
Implemented copy button on allocations sidebar view that copies the hot-link to that view.

Closes: #193

Signed-off-by: Yu-Lin Chen <chenyulin0719@apache.org>
  • Loading branch information
dcoric authored and chenyulin0719 committed Jun 24, 2024
1 parent f5df16e commit c86fb1b
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 36 deletions.
6 changes: 4 additions & 2 deletions src/app/components/apps-view/apps-view.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand All @@ -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">
Expand All @@ -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>
Expand Down
5 changes: 5 additions & 0 deletions src/app/components/apps-view/apps-view.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@
color: #f44336;
}
}
.copy-btn {
font-size: 1em;
cursor: pointer;
padding-left: 5px;
}
.header {
margin: 20px;
font-weight: 100;
Expand Down
78 changes: 46 additions & 32 deletions src/app/components/apps-view/apps-view.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -55,6 +56,7 @@ describe('AppsViewComponent', () => {
MatInputModule,
MatTooltipModule,
MatSelectModule,
MatSidenavModule,
],
providers: [
{ provide: SchedulerService, useValue: MockSchedulerService },
Expand All @@ -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();
});
});
8 changes: 8 additions & 0 deletions src/app/components/apps-view/apps-view.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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;
Expand Down

0 comments on commit c86fb1b

Please sign in to comment.