Skip to content

Commit 7fc7f28

Browse files
authored
HParam UI: Replace RunsTableComponent with DataTable widget behind flag (#6394)
## Motivation for features / changes We are adding lots of functionality to the scalar data table and the runs table. For consistency and reuse we are replacing the table in the runs table with a DataTable widget. This is the first step towards that change. ## Technical description of changes Besides the data all the values which are passed in are hard coded into the runs_table_container. This is done so that we have a data table to work with quickly and unblock some other work. These values will be set up in ngrx in future PRs. ## Screenshots of UI changes (or N/A) We have a plan to restyle this table for the Runs Table. However, that will come later. For now it is just the standard data table which looks like this. <img width="775" alt="Screenshot 2023-05-17 at 4 13 21 PM" src="https://github.com/tensorflow/tensorboard/assets/8672809/57cb2919-c046-40e0-8057-b753fac87176"> ## Alternate designs / implementations considered (or N/A) We considered leaving the runs table and adding all features to both.
1 parent d333cf5 commit 7fc7f28

File tree

4 files changed

+138
-1
lines changed

4 files changed

+138
-1
lines changed

tensorboard/webapp/runs/views/runs_table/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ tf_ng_module(
8282
"//tensorboard/webapp/app_routing",
8383
"//tensorboard/webapp/app_routing:types",
8484
"//tensorboard/webapp/experiments:types",
85+
"//tensorboard/webapp/feature_flag/store",
8586
"//tensorboard/webapp/hparams",
8687
"//tensorboard/webapp/hparams:types",
8788
"//tensorboard/webapp/runs:types",
@@ -95,6 +96,8 @@ tf_ng_module(
9596
"//tensorboard/webapp/types:ui",
9697
"//tensorboard/webapp/util:colors",
9798
"//tensorboard/webapp/util:matcher",
99+
"//tensorboard/webapp/widgets/data_table",
100+
"//tensorboard/webapp/widgets/data_table:types",
98101
"//tensorboard/webapp/widgets/experiment_alias",
99102
"//tensorboard/webapp/widgets/filter_input",
100103
"//tensorboard/webapp/widgets/range_input",
@@ -135,6 +138,7 @@ tf_ts_library(
135138
"//tensorboard/webapp/app_routing:testing",
136139
"//tensorboard/webapp/app_routing:types",
137140
"//tensorboard/webapp/experiments/store:testing",
141+
"//tensorboard/webapp/feature_flag/store",
138142
"//tensorboard/webapp/hparams",
139143
"//tensorboard/webapp/hparams:testing",
140144
"//tensorboard/webapp/hparams:types",
@@ -150,6 +154,7 @@ tf_ts_library(
150154
"//tensorboard/webapp/testing:utils",
151155
"//tensorboard/webapp/types",
152156
"//tensorboard/webapp/types:ui",
157+
"//tensorboard/webapp/widgets/data_table",
153158
"//tensorboard/webapp/widgets/experiment_alias",
154159
"//tensorboard/webapp/widgets/filter_input",
155160
"//tensorboard/webapp/widgets/range_input",

tensorboard/webapp/runs/views/runs_table/runs_table_container.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
OnInit,
2121
} from '@angular/core';
2222
import {createSelector, Store} from '@ngrx/store';
23-
import {combineLatest, Observable, of, Subject} from 'rxjs';
23+
import {BehaviorSubject, combineLatest, Observable, of, Subject} from 'rxjs';
2424
import {
2525
distinctUntilChanged,
2626
filter,
@@ -61,6 +61,12 @@ import {
6161
import {DataLoadState, LoadState} from '../../../types/data';
6262
import {SortDirection} from '../../../types/ui';
6363
import {matchRunToRegex} from '../../../util/matcher';
64+
import {getEnableHparamsInTimeSeries} from '../../../feature_flag/store/feature_flag_selectors';
65+
import {
66+
ColumnHeaderType,
67+
SortingOrder,
68+
TableData,
69+
} from '../../../widgets/data_table/types';
6470
import {
6571
runColorChanged,
6672
runPageSelectionToggled,
@@ -194,6 +200,7 @@ function matchFilter(
194200
selector: 'runs-table',
195201
template: `
196202
<runs-table-component
203+
*ngIf="!HParamsEnabled.value"
197204
[experimentIds]="experimentIds"
198205
[useFlexibleLayout]="useFlexibleLayout"
199206
[numSelectedItems]="numSelectedItems$ | async"
@@ -220,6 +227,16 @@ function matchFilter(
220227
(onHparamDiscreteFilterChanged)="onHparamDiscreteFilterChanged($event)"
221228
(onMetricFilterChanged)="onMetricFilterChanged($event)"
222229
></runs-table-component>
230+
<tb-data-table
231+
*ngIf="HParamsEnabled.value"
232+
[headers]="runsColumns"
233+
[data]="allRunsTableData$ | async"
234+
[sortingInfo]="sortingInfo"
235+
[columnCustomizationEnabled]="columnCustomizationEnabled"
236+
[smoothingEnabled]="smoothingEnabled"
237+
(sortDataBy)="sortDataBy($event)"
238+
(orderColumns)="orderColumns($event)"
239+
></tb-data-table>
223240
`,
224241
host: {
225242
'[class.flex-layout]': 'useFlexibleLayout',
@@ -239,12 +256,30 @@ function matchFilter(
239256
})
240257
export class RunsTableContainer implements OnInit, OnDestroy {
241258
private allUnsortedRunTableItems$?: Observable<RunTableItem[]>;
259+
allRunsTableData$: Observable<TableData[]> = of([]);
242260
loading$: Observable<boolean> | null = null;
243261
filteredItemsLength$?: Observable<number>;
244262
allItemsLength$?: Observable<number>;
245263
pageItems$?: Observable<RunTableItem[]>;
246264
numSelectedItems$?: Observable<number>;
247265

266+
// TODO(jameshollyer): Move these values to ngrx and make these Observables.
267+
runsColumns = [
268+
{
269+
type: ColumnHeaderType.RUN,
270+
name: 'run',
271+
displayName: 'Run',
272+
enabled: true,
273+
},
274+
];
275+
sortingInfo = {
276+
header: ColumnHeaderType.RUN,
277+
name: 'run',
278+
order: SortingOrder.ASCENDING,
279+
};
280+
columnCustomizationEnabled = true;
281+
smoothingEnabled = false;
282+
248283
hparamColumns$: Observable<HparamColumn[]> = of([]);
249284
metricColumns$: Observable<MetricColumn[]> = of([]);
250285

@@ -275,6 +310,7 @@ export class RunsTableContainer implements OnInit, OnDestroy {
275310
sortOption$ = this.store.select(getRunSelectorSort);
276311
paginationOption$ = this.store.select(getRunSelectorPaginationOption);
277312
regexFilter$ = this.store.select(getRunSelectorRegexFilter);
313+
HParamsEnabled = new BehaviorSubject<boolean>(false);
278314
private readonly ngUnsubscribe = new Subject<void>();
279315

280316
constructor(private readonly store: Store<State>) {}
@@ -286,10 +322,24 @@ export class RunsTableContainer implements OnInit, OnDestroy {
286322
}
287323

288324
ngOnInit() {
325+
this.store.select(getEnableHparamsInTimeSeries).subscribe((enabled) => {
326+
this.HParamsEnabled.next(enabled);
327+
});
289328
const getRunTableItemsPerExperiment = this.experimentIds.map((id) =>
290329
this.getRunTableItemsForExperiment(id)
291330
);
292331

332+
const getRunTableDataPerExperiment$ = this.experimentIds.map((id) =>
333+
this.getRunTableDataForExperiment(id)
334+
);
335+
336+
this.allRunsTableData$ = combineLatest(getRunTableDataPerExperiment$).pipe(
337+
map((itemsForExperiments: TableData[][]) => {
338+
const items = [] as TableData[];
339+
return items.concat(...itemsForExperiments);
340+
})
341+
);
342+
293343
const rawAllUnsortedRunTableItems$ = combineLatest(
294344
getRunTableItemsPerExperiment
295345
).pipe(
@@ -519,6 +569,34 @@ export class RunsTableContainer implements OnInit, OnDestroy {
519569
return slicedItems;
520570
}
521571

572+
private getRunTableDataForExperiment(
573+
experimentId: string
574+
): Observable<TableData[]> {
575+
return combineLatest([
576+
this.store.select(getRuns, {experimentId}),
577+
this.store.select(getRunColorMap),
578+
]).pipe(
579+
map(([runs, colorMap]) => {
580+
return runs.map((run) => {
581+
const tableData: TableData = {
582+
id: run.id,
583+
color: colorMap[run.id],
584+
};
585+
this.runsColumns.forEach((column) => {
586+
switch (column.type) {
587+
case ColumnHeaderType.RUN:
588+
tableData[column.name!] = run.name;
589+
break;
590+
default:
591+
break;
592+
}
593+
});
594+
return tableData;
595+
});
596+
})
597+
);
598+
}
599+
522600
private getRunTableItemsForExperiment(
523601
experimentId: string
524602
): Observable<RunTableItem[]> {

tensorboard/webapp/runs/views/runs_table/runs_table_module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {MatSortModule} from '@angular/material/sort';
3131
import {MatTableModule} from '@angular/material/table';
3232
import {ColorPickerModule} from 'ngx-color-picker';
3333
import {AlertModule} from '../../../alert/alert_module';
34+
import {DataTableModule} from '../../../widgets/data_table/data_table_module';
3435
import {ExperimentAliasModule} from '../../../widgets/experiment_alias/experiment_alias_module';
3536
import {FilterInputModule} from '../../../widgets/filter_input/filter_input_module';
3637
import {RangeInputModule} from '../../../widgets/range_input/range_input_module';
@@ -45,6 +46,7 @@ import {RunsTableContainer} from './runs_table_container';
4546
imports: [
4647
ColorPickerModule,
4748
CommonModule,
49+
DataTableModule,
4850
ExperimentAliasModule,
4951
FilterInputModule,
5052
MatFormFieldModule,

tensorboard/webapp/runs/views/runs_table/runs_table_test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {buildExperimentRouteFromId} from '../../../app_routing/testing';
4444
import {RouteKind} from '../../../app_routing/types';
4545
import {State} from '../../../app_state';
4646
import {buildExperiment} from '../../../experiments/store/testing';
47+
import {getEnableHparamsInTimeSeries} from '../../../feature_flag/store/feature_flag_selectors';
4748
import {
4849
actions as hparamsActions,
4950
selectors as hparamsSelectors,
@@ -79,6 +80,8 @@ import {MatIconTestingModule} from '../../../testing/mat_icon_module';
7980
import {provideMockTbStore} from '../../../testing/utils';
8081
import {DataLoadState} from '../../../types/data';
8182
import {SortDirection} from '../../../types/ui';
83+
import {DataTableModule} from '../../../widgets/data_table/data_table_module';
84+
import {DataTableComponent} from '../../../widgets/data_table/data_table_component';
8285
import {ExperimentAliasModule} from '../../../widgets/experiment_alias/experiment_alias_module';
8386
import {FilterInputModule} from '../../../widgets/filter_input/filter_input_module';
8487
import {RangeInputModule} from '../../../widgets/range_input/range_input_module';
@@ -239,6 +242,7 @@ describe('runs_table', () => {
239242
FilterInputModule,
240243
RangeInputModule,
241244
ExperimentAliasModule,
245+
DataTableModule,
242246
],
243247
declarations: [
244248
RunsGroupMenuButtonComponent,
@@ -305,6 +309,7 @@ describe('runs_table', () => {
305309
settingsSelectors.getColorPalette,
306310
buildColorPalette()
307311
);
312+
store.overrideSelector(getEnableHparamsInTimeSeries, false);
308313
dispatchSpy = spyOn(store, 'dispatch').and.callFake((action: Action) => {
309314
actualActions.push(action);
310315
});
@@ -3169,4 +3174,51 @@ describe('runs_table', () => {
31693174
});
31703175
});
31713176
});
3177+
3178+
describe('runs data table', () => {
3179+
beforeEach(() => {
3180+
store.overrideSelector(getEnableHparamsInTimeSeries, true);
3181+
});
3182+
3183+
it('renders data table when hparam flag is on', () => {
3184+
const fixture = createComponent(['book']);
3185+
fixture.detectChanges();
3186+
3187+
expect(
3188+
fixture.debugElement.query(By.directive(DataTableComponent))
3189+
).toBeTruthy();
3190+
expect(
3191+
fixture.nativeElement.querySelector('runs-table-component')
3192+
).toBeFalsy();
3193+
});
3194+
3195+
it('passes run name and color to data table', () => {
3196+
// To make sure we only return the runs when called with the right props.
3197+
const selectSpy = spyOn(store, 'select').and.callThrough();
3198+
selectSpy
3199+
.withArgs(getRuns, {experimentId: 'book'})
3200+
.and.returnValue(
3201+
of([
3202+
buildRun({id: 'book1', name: "The Philosopher's Stone"}),
3203+
buildRun({id: 'book2', name: 'The Chamber Of Secrets'}),
3204+
])
3205+
);
3206+
3207+
store.overrideSelector(getRunColorMap, {
3208+
book1: '#000',
3209+
book2: '#111',
3210+
});
3211+
3212+
const fixture = createComponent(['book']);
3213+
fixture.detectChanges();
3214+
const dataTableComponent = fixture.debugElement.query(
3215+
By.directive(DataTableComponent)
3216+
);
3217+
3218+
expect(dataTableComponent.componentInstance.data).toEqual([
3219+
{id: 'book1', color: '#000', run: "The Philosopher's Stone"},
3220+
{id: 'book2', color: '#111', run: 'The Chamber Of Secrets'},
3221+
]);
3222+
});
3223+
});
31723224
});

0 commit comments

Comments
 (0)