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 0097393986..4848283c26 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 02a53f75fa..125e8781a4 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 336d1e676d..95993ae8cd 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 62ced1d9f5..2220aef24d 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 ec9a2ab96c..3a575be9c1 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: [