From 7f7d140783c0fb109b107609408138916a9b09e6 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 23 Oct 2023 11:22:38 -0700 Subject: [PATCH] HParams: Load spinner in data table and do not reload table (#6658) ## Motivation for features / changes Issue number 5 in #6651. It is also just a better experience to keep previously loaded data intact while fetching new data. ## Technical description of changes In order to put the spinner below the headers I added the spinner to the data-table. It seems like a general feature that should be in that widget anyway. ## Screenshots of UI changes (or N/A) ![2023-10-20_15-13-34 (1)](https://github.com/tensorflow/tensorboard/assets/8672809/23bfa0ed-077c-45fb-9df5-a0c3db1db67d) --- .../views/runs_table/runs_data_table.ng.html | 5 +---- .../views/runs_table/runs_data_table.scss | 10 --------- .../data_table/data_table_component.ng.html | 3 +++ .../data_table/data_table_component.scss | 10 +++++++++ .../data_table/data_table_component.ts | 1 + .../widgets/data_table/data_table_module.ts | 2 ++ .../widgets/data_table/data_table_test.ts | 21 +++++++++++++++++++ 7 files changed, 38 insertions(+), 14 deletions(-) diff --git a/tensorboard/webapp/runs/views/runs_table/runs_data_table.ng.html b/tensorboard/webapp/runs/views/runs_table/runs_data_table.ng.html index 00973939862..4848283c262 100644 --- a/tensorboard/webapp/runs/views/runs_table/runs_data_table.ng.html +++ b/tensorboard/webapp/runs/views/runs_table/runs_data_table.ng.html @@ -23,17 +23,14 @@ placeholder="Filter runs (regex)" > -
- -
+
+ +
diff --git a/tensorboard/webapp/widgets/data_table/data_table_component.scss b/tensorboard/webapp/widgets/data_table/data_table_component.scss index 02a53f75fa4..125e8781a49 100644 --- a/tensorboard/webapp/widgets/data_table/data_table_component.scss +++ b/tensorboard/webapp/widgets/data_table/data_table_component.scss @@ -45,6 +45,16 @@ $_accent: map-get(mat.get-color-config($tb-theme), accent); } } +.loading { + align-items: center; + border: 0; + @include tb-theme-foreground-prop(border-bottom, border, 1px solid); + display: flex; + height: 48px; + padding: 0 24px; + justify-content: center; +} + .add-button-cell { display: table-cell; width: 40px; diff --git a/tensorboard/webapp/widgets/data_table/data_table_component.ts b/tensorboard/webapp/widgets/data_table/data_table_component.ts index 336d1e676de..95993ae8cd3 100644 --- a/tensorboard/webapp/widgets/data_table/data_table_component.ts +++ b/tensorboard/webapp/widgets/data_table/data_table_component.ts @@ -65,6 +65,7 @@ export class DataTableComponent implements OnDestroy, AfterContentInit { @Input() columnCustomizationEnabled!: boolean; @Input() selectableColumns?: ColumnHeader[]; @Input() columnFilters!: Map; + @Input() loading: boolean = false; @ContentChildren(HeaderCellComponent) headerCells!: QueryList; diff --git a/tensorboard/webapp/widgets/data_table/data_table_module.ts b/tensorboard/webapp/widgets/data_table/data_table_module.ts index 62ced1d9f5c..2220aef24da 100644 --- a/tensorboard/webapp/widgets/data_table/data_table_module.ts +++ b/tensorboard/webapp/widgets/data_table/data_table_module.ts @@ -17,6 +17,7 @@ import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {MatIconModule} from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button'; +import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {DataTableComponent} from './data_table_component'; import {HeaderCellComponent} from './header_cell_component'; import {DataTableHeaderModule} from './data_table_header_module'; @@ -43,6 +44,7 @@ import {FilterDialogModule} from './filter_dialog_module'; CommonModule, MatIconModule, MatButtonModule, + MatProgressSpinnerModule, DataTableHeaderModule, CustomModalModule, ColumnSelectorModule, diff --git a/tensorboard/webapp/widgets/data_table/data_table_test.ts b/tensorboard/webapp/widgets/data_table/data_table_test.ts index ec9a2ab96ca..3a575be9c1f 100644 --- a/tensorboard/webapp/widgets/data_table/data_table_test.ts +++ b/tensorboard/webapp/widgets/data_table/data_table_test.ts @@ -46,6 +46,7 @@ import {FilterDialog} from './filter_dialog_component'; [sortingInfo]="sortingInfo" [selectableColumns]="selectableColumns" [columnFilters]="columnFilters" + [loading]="loading" (sortDataBy)="sortDataBy($event)" (orderColumns)="orderColumns($event)" (addColumn)="addColumn.emit($event)" @@ -88,6 +89,7 @@ class TestableComponent { @Input() orderColumns!: (newOrder: ColumnHeaderType[]) => void; @Input() selectableColumns!: ColumnHeader[]; @Input() columnFilters!: Map; + @Input() loading!: boolean; @Output() addColumn = new EventEmitter<{ header: ColumnHeader; @@ -123,6 +125,7 @@ describe('data table', () => { data?: TableData[]; potentialColumns?: ColumnHeader[]; columnFilters?: Map; + loading?: boolean; }): ComponentFixture { const fixture = TestBed.createComponent(TestableComponent); @@ -140,6 +143,10 @@ describe('data table', () => { fixture.componentInstance.selectableColumns = input.potentialColumns; } + if (input.loading !== undefined) { + fixture.componentInstance.loading = input.loading; + } + fixture.componentInstance.columnFilters = input.columnFilters || new Map(); sortDataBySpy = jasmine.createSpy(); @@ -159,6 +166,20 @@ describe('data table', () => { expect(dataTable).toBeTruthy(); }); + it('renders spinner when loading', () => { + const fixture = createComponent({loading: true}); + fixture.detectChanges(); + const spinner = fixture.debugElement.query(By.css('.loading')); + expect(spinner).toBeTruthy(); + }); + + it('does not renders spinner when not loading', () => { + const fixture = createComponent({loading: false}); + fixture.detectChanges(); + const spinner = fixture.debugElement.query(By.css('.loading')); + expect(spinner).toBeFalsy(); + }); + it('emits sortDataBy event when header emits headerClicked event', () => { const fixture = createComponent({ headers: [