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]