Skip to content

Commit ad1da0c

Browse files
authored
Makes data table add column sticky (#6839)
## Motivation for features / changes The add column button in the runs table will be hidden when the table is wide, which will often be the case. This makes the "add" feature very difficult to discover. context: b/332788091 ## Technical description of changes - Moves the add button column outside of and adjacent to the data table (previously it was a column of the table) and makes it sticky - Adds additional configuration options to data table to allow runs and scalar tables to have slightly different designs ## Screenshots of UI changes (or N/A) Notice the add column is fixed to the right: ![sticky](https://github.com/tensorflow/tensorboard/assets/736199/0f7aca5c-0903-438a-bafd-ddc033e262f7) Scalar table add column is slightly narrower: ![image](https://github.com/tensorflow/tensorboard/assets/736199/e8c37505-4c3f-41bd-98f3-3b31f99e206e) ## Alternate designs / implementations considered (or N/A) - Tried adding the add columns to the consumer components of the data table (runs data table and scalar card data table) instead. But this led to too much code duplication in the components and templates. - Tried making the add column a regular table column, instead of adding it outside in a separate div. But making the column sticky requires styling the table rows, which data table doesn't provide access to (rows are configured solely in the consumer components). Also, the chosen implementation is arguably more consistent with the current design as the add "column" doesn't actually function as a table column at all
1 parent e9d0009 commit ad1da0c

10 files changed

+90
-33
lines changed

Diff for: tensorboard/webapp/metrics/views/card_renderer/scalar_card_data_table.ng.html

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
[selectableColumns]="selectableColumns"
2323
[numColumnsLoaded]="numColumnsLoaded"
2424
[hasMoreColumnsToLoad]="numColumnsLoaded === numColumnsToLoad"
25+
[addColumnSize]="AddColumnSize.SMALL"
2526
(sortDataBy)="sortDataBy.emit($event)"
2627
(orderColumns)="onOrderColumns($event)"
2728
(addColumn)="addColumn.emit($event)"

Diff for: tensorboard/webapp/metrics/views/card_renderer/scalar_card_data_table.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
FilterAddedEvent,
4242
ReorderColumnEvent,
4343
AddColumnEvent,
44+
AddColumnSize,
4445
} from '../../../widgets/data_table/types';
4546
import {isDatumVisible} from './utils';
4647
import {memoize} from '../../../util/memoize';
@@ -73,7 +74,8 @@ export class ScalarCardDataTable {
7374
@Output() addFilter = new EventEmitter<FilterAddedEvent>();
7475
@Output() loadAllColumns = new EventEmitter<null>();
7576

76-
ColumnHeaderType = ColumnHeaderType;
77+
readonly ColumnHeaderType = ColumnHeaderType;
78+
readonly AddColumnSize = AddColumnSize;
7779

7880
// Columns must be memoized to stop needless re-rendering of the content and headers in these
7981
// columns. This has been known to cause problems with the controls in these columns,

Diff for: tensorboard/webapp/runs/views/runs_table/runs_data_table.ng.html

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
[hasMoreColumnsToLoad]="numColumnsLoaded === numColumnsToLoad"
3434
[columnFilters]="columnFilters"
3535
[loading]="loading"
36+
[shouldAddBorders]="true"
3637
(sortDataBy)="sortDataBy.emit($event)"
3738
(orderColumns)="orderColumns.emit($event)"
3839
(addColumn)="addColumn.emit($event)"

Diff for: tensorboard/webapp/runs/views/runs_table/runs_data_table.scss

-12
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ $_circle-size: 20px;
1818

1919
:host {
2020
min-width: 100%;
21-
22-
& ::ng-deep tb-data-table .data-table {
23-
@include tb-theme-foreground-prop(border-top, border, 1px solid);
24-
}
2521
}
2622

2723
.color-container {
@@ -55,14 +51,6 @@ tb-data-table-header-cell {
5551
padding: 0 4px;
5652
vertical-align: middle;
5753
@include tb-theme-foreground-prop(border-bottom, border, 1px solid);
58-
59-
&:last-child {
60-
@include tb-theme-foreground-prop(border-right, border, 1px solid);
61-
}
62-
}
63-
64-
tb-data-table-header-cell:last-of-type {
65-
@include tb-theme-foreground-prop(border-right, border, 1px solid);
6654
}
6755

6856
.table-column-selected,

Diff for: tensorboard/webapp/theme/_tb_theme.template.scss

+4-2
Original file line numberDiff line numberDiff line change
@@ -312,14 +312,16 @@ $tb-dark-theme: map_merge(
312312
button.mat-mdc-button-base {
313313
--tb-icon-width: 24px;
314314
--tb-icon-height: 24px;
315+
--tb-icon-button-width: 40px;
316+
--tb-icon-button-height: 40px;
315317
--mdc-text-button-label-text-tracking: normal;
316318
--mdc-filled-button-label-text-tracking: normal;
317319
--mdc-outlined-button-label-text-tracking: normal;
318320
--mdc-protected-button-label-text-tracking: normal;
319321

320322
&[mat-icon-button].mat-mdc-icon-button {
321-
width: 40px;
322-
height: 40px;
323+
width: var(--tb-icon-button-width);
324+
height: var(--tb-icon-button-height);
323325
display: inline-flex;
324326
justify-content: center;
325327
align-items: center;

Diff for: tensorboard/webapp/widgets/data_table/data_table_component.ng.html

+16-7
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,33 @@
4343
></tb-data-table-filter>
4444
</ng-template>
4545

46-
<div class="data-table">
47-
<div class="header">
48-
<ng-content select="[header]"></ng-content>
46+
<div class="data-table-wrapper" [class.should-add-borders]="shouldAddBorders">
47+
<div class="left-section">
48+
<div class="data-table">
49+
<div class="header">
50+
<ng-content select="[header]"></ng-content>
51+
</div>
52+
<ng-content select="[content]"></ng-content>
53+
</div>
54+
</div>
55+
<div
56+
class="right-section"
57+
*ngIf="selectableColumns && selectableColumns.length"
58+
>
4959
<div
50-
class="add-button-cell"
51-
*ngIf="selectableColumns && selectableColumns.length"
60+
class="add-button-column"
61+
[class.small-add-button]="addColumnSize === AddColumnSize.SMALL"
5262
>
5363
<button
5464
mat-icon-button
55-
class="add-column-btn"
65+
class="add-button"
5666
(click)="openColumnSelector({event: $event})"
5767
title="Add Column"
5868
>
5969
<mat-icon svgIcon="add_24px"></mat-icon>
6070
</button>
6171
</div>
6272
</div>
63-
<ng-content select="[content]"></ng-content>
6473
</div>
6574
<div *ngIf="loading" class="loading">
6675
<mat-spinner mode="indeterminate" diameter="28"></mat-spinner>

Diff for: tensorboard/webapp/widgets/data_table/data_table_component.scss

+52-9
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,35 @@ limitations under the License.
1717

1818
$_accent: map-get(mat.get-color-config($tb-theme), accent);
1919

20+
.data-table-wrapper {
21+
display: flex;
22+
23+
&.should-add-borders {
24+
.left-section,
25+
.right-section {
26+
@include tb-theme-foreground-prop(border-top, border, 1px solid);
27+
}
28+
29+
.add-button-column {
30+
@include tb-theme-foreground-prop(border-left, border, 1px solid);
31+
}
32+
}
33+
}
34+
2035
.data-table {
2136
font-size: 13px;
2237
display: table;
2338
width: 100%;
2439

25-
.header {
26-
display: table-row;
27-
z-index: 1;
28-
}
29-
3040
.header {
3141
background-color: mat.get-color-from-palette($tb-background, background);
42+
display: table-row;
43+
font-weight: bold;
3244
position: sticky;
3345
text-align: left;
3446
top: 0;
35-
font-weight: bold;
3647
vertical-align: bottom;
48+
z-index: 1;
3749

3850
&:hover {
3951
cursor: pointer;
@@ -55,7 +67,38 @@ $_accent: map-get(mat.get-color-config($tb-theme), accent);
5567
justify-content: center;
5668
}
5769

58-
.add-button-cell {
59-
display: table-cell;
60-
width: 40px;
70+
.left-section {
71+
flex-grow: 1;
72+
}
73+
74+
.right-section {
75+
background-color: mat.get-color-from-palette($tb-background, background);
76+
position: sticky;
77+
right: -1px; // Prevent fractional width from creating a gap.
78+
z-index: 1;
79+
80+
@include tb-dark-theme {
81+
background-color: map-get($tb-dark-background, background);
82+
}
83+
84+
.add-button-column {
85+
width: 40px;
86+
height: 100%;
87+
88+
.add-button {
89+
position: sticky;
90+
top: 0;
91+
}
92+
}
93+
}
94+
95+
.right-section .add-button-column.small-add-button {
96+
display: flex;
97+
justify-content: center;
98+
width: 24px;
99+
100+
.add-button {
101+
--tb-icon-button-width: 20px;
102+
--tb-icon-button-height: 20px;
103+
}
61104
}

Diff for: tensorboard/webapp/widgets/data_table/data_table_component.ts

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
ReorderColumnEvent,
3939
Side,
4040
AddColumnEvent,
41+
AddColumnSize,
4142
} from './types';
4243
import {HeaderCellComponent} from './header_cell_component';
4344
import {Subscription} from 'rxjs';
@@ -67,6 +68,8 @@ export class DataTableComponent implements OnDestroy, AfterContentInit {
6768
@Input() hasMoreColumnsToLoad!: boolean;
6869
@Input() columnFilters!: Map<string, DiscreteFilter | IntervalFilter>;
6970
@Input() loading: boolean = false;
71+
@Input() shouldAddBorders: boolean = false;
72+
@Input() addColumnSize: AddColumnSize = AddColumnSize.DEFAULT;
7073

7174
@ContentChildren(HeaderCellComponent)
7275
headerCells!: QueryList<HeaderCellComponent>;
@@ -102,6 +105,7 @@ export class DataTableComponent implements OnDestroy, AfterContentInit {
102105

103106
readonly SortingOrder = SortingOrder;
104107
readonly Side = Side;
108+
readonly AddColumnSize = AddColumnSize;
105109

106110
constructor(
107111
private readonly customModal: CustomModal,

Diff for: tensorboard/webapp/widgets/data_table/data_table_test.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ import {ColumnSelectorModule} from './column_selector_module';
4545
import {FilterDialog} from './filter_dialog_component';
4646
import {CustomModal} from '../custom_modal/custom_modal';
4747

48+
const ADD_BUTTON_PREDICATE = By.css('.add-button');
49+
4850
@Component({
4951
selector: 'testable-comp',
5052
template: `
@@ -595,7 +597,7 @@ describe('data table', () => {
595597

596598
it('does not show add button when there are no selectable columns', () => {
597599
const fixture = createComponent({});
598-
expect(fixture.debugElement.query(By.css('.add-column-button'))).toBeNull();
600+
expect(fixture.debugElement.query(ADD_BUTTON_PREDICATE)).toBeNull();
599601
});
600602

601603
it('renders column selector when + button is clicked', () => {
@@ -612,7 +614,7 @@ describe('data table', () => {
612614
expect(
613615
fixture.debugElement.query(By.directive(ColumnSelectorComponent))
614616
).toBeNull();
615-
const addBtn = fixture.debugElement.query(By.css('.add-column-btn'));
617+
const addBtn = fixture.debugElement.query(ADD_BUTTON_PREDICATE);
616618
addBtn.nativeElement.click();
617619
expect(
618620
fixture.debugElement.query(By.directive(ColumnSelectorComponent))

Diff for: tensorboard/webapp/widgets/data_table/types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,8 @@ export enum ColumnGroup {
136136
HPARAM = 'HPARAM',
137137
OTHER = 'OTHER',
138138
}
139+
140+
export enum AddColumnSize {
141+
DEFAULT = 'DEFAULT',
142+
SMALL = 'SMALL',
143+
}

0 commit comments

Comments
 (0)