Skip to content

Commit af78290

Browse files
authored
color grouping: add group by regex under feature flag (#5117)
We want to bring up color group by regex gradually by adding a feature flag `enableColorGroupByRegex` The feature flag is added in global (also shown in OSS) and we want to flip the flag in .corp only later (no concrete plan to bring color grouping in OSS yet)
1 parent 0725301 commit af78290

14 files changed

+123
-17
lines changed

tensorboard/webapp/feature_flag/store/feature_flag_selectors.ts

+7
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ export const getEnabledColorGroup = createSelector(getFeatureFlags, (flags) => {
9191
return flags.enabledColorGroup;
9292
});
9393

94+
export const getEnabledColorGroupByRegex = createSelector(
95+
getFeatureFlags,
96+
(flags) => {
97+
return flags.enabledColorGroupByRegex;
98+
}
99+
);
100+
94101
export const getIsMetricsImageSupportEnabled = createSelector(
95102
getFeatureFlags,
96103
(flags) => {

tensorboard/webapp/feature_flag/store/feature_flag_selectors_test.ts

+25
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,31 @@ describe('feature_flag_selectors', () => {
216216
});
217217
});
218218

219+
describe('#getEnabledColorGroupByRegex', () => {
220+
it('returns the proper value', () => {
221+
let state = buildState(
222+
buildFeatureFlagState({
223+
defaultFlags: buildFeatureFlag({
224+
enabledColorGroupByRegex: false,
225+
}),
226+
})
227+
);
228+
expect(selectors.getEnabledColorGroupByRegex(state)).toEqual(false);
229+
230+
state = buildState(
231+
buildFeatureFlagState({
232+
defaultFlags: buildFeatureFlag({
233+
enabledColorGroupByRegex: false,
234+
}),
235+
flagOverrides: {
236+
enabledColorGroupByRegex: true,
237+
},
238+
})
239+
);
240+
expect(selectors.getEnabledColorGroupByRegex(state)).toEqual(true);
241+
});
242+
});
243+
219244
describe('#getIsMetricsImageSupportEnabled', () => {
220245
it('returns the proper value', () => {
221246
let state = buildState(

tensorboard/webapp/feature_flag/store/feature_flag_store_config_provider.ts

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const initialState: FeatureFlagState = {
2424
defaultEnableDarkMode: false,
2525
enableDarkModeOverride: null,
2626
enabledColorGroup: false,
27+
enabledColorGroupByRegex: false,
2728
enabledExperimentalPlugins: [],
2829
inColab: false,
2930
scalarsBatchSize: undefined,

tensorboard/webapp/feature_flag/testing.ts

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export function buildFeatureFlag(
2323
defaultEnableDarkMode: false,
2424
enableDarkModeOverride: null,
2525
enabledColorGroup: false,
26+
enabledColorGroupByRegex: false,
2627
enabledExperimentalPlugins: [],
2728
inColab: false,
2829
scalarsBatchSize: undefined,

tensorboard/webapp/feature_flag/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export interface FeatureFlags {
2727
isAutoDarkModeAllowed: boolean;
2828
// Whether to enable experimental semantic color grouping feature.
2929
enabledColorGroup: boolean;
30+
// Whether to enable color grouping by regex.
31+
enabledColorGroupByRegex: boolean;
3032
// experimental plugins to manually enable.
3133
enabledExperimentalPlugins: string[];
3234
// Whether the TensorBoard is being run inside Colab output cell.

tensorboard/webapp/routes/dashboard_deeplink_provider.ts

+9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {GroupBy, GroupByKey} from '../runs/types';
3030
import * as selectors from '../selectors';
3131
import {
3232
ENABLE_COLOR_GROUP_QUERY_PARAM_KEY,
33+
ENABLE_COLOR_GROUP_BY_REGEX_QUERY_PARAM_KEY,
3334
EXPERIMENTAL_PLUGIN_QUERY_PARAM_KEY,
3435
} from '../webapp_data_source/tb_feature_flag_data_source_types';
3536
import {
@@ -98,6 +99,14 @@ export class DashboardDeepLinkProvider extends DeepLinkProvider {
9899
value: String(overriddenFeatureFlags.enabledColorGroup),
99100
});
100101
}
102+
if (
103+
typeof overriddenFeatureFlags.enabledColorGroupByRegex === 'boolean'
104+
) {
105+
queryParams.push({
106+
key: ENABLE_COLOR_GROUP_BY_REGEX_QUERY_PARAM_KEY,
107+
value: String(overriddenFeatureFlags.enabledColorGroupByRegex),
108+
});
109+
}
101110
return queryParams;
102111
})
103112
);

tensorboard/webapp/routes/dashboard_deeplink_provider_test.ts

+27
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,33 @@ describe('core deeplink provider', () => {
347347
[]
348348
);
349349
});
350+
351+
it('serializes enableColorGroupByRegex state', () => {
352+
store.overrideSelector(selectors.getOverriddenFeatureFlags, {
353+
enabledColorGroupByRegex: true,
354+
});
355+
store.refreshState();
356+
357+
expect(queryParamsSerialized[queryParamsSerialized.length - 1]).toEqual([
358+
{key: 'enableColorGroupByRegex', value: 'true'},
359+
]);
360+
361+
store.overrideSelector(selectors.getOverriddenFeatureFlags, {
362+
enabledColorGroupByRegex: false,
363+
});
364+
store.refreshState();
365+
366+
expect(queryParamsSerialized[queryParamsSerialized.length - 1]).toEqual([
367+
{key: 'enableColorGroupByRegex', value: 'false'},
368+
]);
369+
370+
store.overrideSelector(selectors.getOverriddenFeatureFlags, {});
371+
store.refreshState();
372+
373+
expect(queryParamsSerialized[queryParamsSerialized.length - 1]).toEqual(
374+
[]
375+
);
376+
});
350377
});
351378

352379
describe('runs', () => {

tensorboard/webapp/runs/views/runs_table/runs_group_menu_button_component.ng.html

+1-2
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@
5151
</span>
5252
<label>Run</label>
5353
</button>
54-
<!--
5554
<button
5655
mat-menu-item
5756
role="menuitemradio"
57+
*ngIf="showGroupByRegex"
5858
[attr.aria-checked]="selectedGroupBy.key === GroupByKey.REGEX"
5959
(click)="onGroupByChange.emit({key: GroupByKey.REGEX, regexString: ''})"
6060
>
@@ -66,5 +66,4 @@
6666
</span>
6767
<label>Regex</label>
6868
</button>
69-
-->
7069
</mat-menu>

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import {GroupBy, GroupByKey} from '../../types';
3131
export class RunsGroupMenuButtonComponent {
3232
readonly GroupByKey = GroupByKey;
3333

34-
@Input()
35-
selectedGroupBy!: GroupBy;
34+
@Input() selectedGroupBy!: GroupBy;
35+
@Input() showGroupByRegex!: boolean;
3636

3737
@Output()
3838
onGroupByChange = new EventEmitter<GroupBy>();

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

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {Store} from '@ngrx/store';
1717
import {Observable} from 'rxjs';
1818

1919
import {State} from '../../../app_state';
20+
import {getEnabledColorGroupByRegex} from '../../../selectors';
2021
import {runGroupByChanged} from '../../actions';
2122
import {getRunGroupBy} from '../../store/runs_selectors';
2223
import {GroupBy} from '../../types';
@@ -29,12 +30,17 @@ import {GroupBy} from '../../types';
2930
template: `
3031
<runs-group-menu-button-component
3132
[selectedGroupBy]="selectedGroupBy$ | async"
33+
[showGroupByRegex]="showGroupByRegex$ | async"
3234
(onGroupByChange)="onGroupByChange($event)"
3335
></runs-group-menu-button-component>
3436
`,
3537
changeDetection: ChangeDetectionStrategy.OnPush,
3638
})
3739
export class RunsGroupMenuButtonContainer {
40+
showGroupByRegex$: Observable<boolean> = this.store.select(
41+
getEnabledColorGroupByRegex
42+
);
43+
3844
@Input() experimentIds!: string[];
3945

4046
constructor(private readonly store: Store<State>) {}

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

+12-13
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import {DiscreteFilter, IntervalFilter} from '../../../hparams/types';
5454
import {
5555
getCurrentRouteRunSelection,
5656
getEnabledColorGroup,
57+
getEnabledColorGroupByRegex,
5758
getExperiment,
5859
getExperimentIdToAliasMap,
5960
getRouteId,
@@ -249,6 +250,7 @@ describe('runs_table', () => {
249250
);
250251
store.overrideSelector(getRouteId, '123');
251252
store.overrideSelector(getEnabledColorGroup, false);
253+
store.overrideSelector(getEnabledColorGroupByRegex, false);
252254
store.overrideSelector(getRunGroupBy, {key: GroupByKey.RUN});
253255
dispatchSpy = spyOn(store, 'dispatch').and.callFake((action: Action) => {
254256
actualActions.push(action);
@@ -559,9 +561,9 @@ describe('runs_table', () => {
559561
expect(menuButton).toBeTruthy();
560562
});
561563

562-
/* TODO(japie1235813): Brings back group by regex. */
563-
it('renders "Experiment" and "Run"', () => {
564+
it('renders "Experiment", "Run", and "Regex"', () => {
564565
store.overrideSelector(getEnabledColorGroup, true);
566+
store.overrideSelector(getEnabledColorGroupByRegex, true);
565567
const fixture = createComponent(
566568
['book'],
567569
[RunsTableColumn.RUN_NAME, RunsTableColumn.RUN_COLOR]
@@ -577,14 +579,15 @@ describe('runs_table', () => {
577579

578580
expect(
579581
items.map((element) => element.querySelector('label')!.textContent)
580-
).toEqual(['Experiment', 'Run']);
582+
).toEqual(['Experiment', 'Run', 'Regex']);
581583
});
582584

583585
it(
584586
'renders a check icon and aria-checked for the current groupBy menu ' +
585587
'item',
586588
() => {
587589
store.overrideSelector(getEnabledColorGroup, true);
590+
store.overrideSelector(getEnabledColorGroupByRegex, true);
588591
store.overrideSelector(getRunGroupBy, {key: GroupByKey.EXPERIMENT});
589592
const fixture = createComponent(
590593
['book'],
@@ -599,15 +602,13 @@ describe('runs_table', () => {
599602

600603
const items = getOverlayMenuItems();
601604

602-
/* TODO(japie1235813): Brings back group by regex. */
603605
expect(
604606
items.map((element) => element.getAttribute('aria-checked'))
605-
).toEqual(['true', 'false']);
607+
).toEqual(['true', 'false', 'false']);
606608
expect(
607609
items.map((element) => Boolean(element.querySelector('mat-icon')))
608-
).toEqual([true, false]);
610+
).toEqual([true, false, false]);
609611

610-
/* TODO(japie1235813): Brings back group by regex.
611612
store.overrideSelector(getRunGroupBy, {
612613
key: GroupByKey.REGEX,
613614
regexString: 'hello',
@@ -621,12 +622,12 @@ describe('runs_table', () => {
621622
expect(
622623
items.map((element) => Boolean(element.querySelector('mat-icon')))
623624
).toEqual([false, false, true]);
624-
*/
625625
}
626626
);
627627

628628
it('dispatches `runGroupByChanged` when a menu item is clicked', () => {
629629
store.overrideSelector(getEnabledColorGroup, true);
630+
store.overrideSelector(getEnabledColorGroupByRegex, true);
630631
store.overrideSelector(getRunGroupBy, {key: GroupByKey.EXPERIMENT});
631632
const fixture = createComponent(
632633
['book'],
@@ -641,7 +642,7 @@ describe('runs_table', () => {
641642

642643
const items = getOverlayMenuItems();
643644

644-
const [experiments, runs] = items as HTMLElement[];
645+
const [experiments, runs, regex] = items as HTMLElement[];
645646
experiments.click();
646647

647648
expect(dispatchSpy).toHaveBeenCalledWith(
@@ -659,17 +660,15 @@ describe('runs_table', () => {
659660
})
660661
);
661662

662-
/* TODO(japie1235813): Brings back group by regex.
663663
regex.click();
664664
expect(dispatchSpy).toHaveBeenCalledWith(
665665
runGroupByChanged({
666666
experimentIds: ['book'],
667-
// regexString is hardcoded to '' for now; should be fixed when
668-
// regex support is properly implemented.
667+
// TODO(japie1235813): regexString is hardcoded to '' for now;
668+
// should be fixed when regex support is properly implemented.
669669
groupBy: {key: GroupByKey.REGEX, regexString: ''},
670670
})
671671
);
672-
*/
673672
});
674673

675674
it(

tensorboard/webapp/webapp_data_source/tb_feature_flag_data_source.ts

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {Injectable} from '@angular/core';
1616

1717
import {
1818
ENABLE_COLOR_GROUP_QUERY_PARAM_KEY,
19+
ENABLE_COLOR_GROUP_BY_REGEX_QUERY_PARAM_KEY,
1920
ENABLE_DARK_MODE_QUERY_PARAM_KEY,
2021
EXPERIMENTAL_PLUGIN_QUERY_PARAM_KEY,
2122
SCALARS_BATCH_SIZE_PARAM_KEY,
@@ -68,6 +69,11 @@ export class QueryParamsFeatureFlagDataSource extends TBFeatureFlagDataSource {
6869
params.get(ENABLE_COLOR_GROUP_QUERY_PARAM_KEY) !== 'false';
6970
}
7071

72+
if (params.has(ENABLE_COLOR_GROUP_BY_REGEX_QUERY_PARAM_KEY)) {
73+
featureFlags.enabledColorGroupByRegex =
74+
params.get(ENABLE_COLOR_GROUP_BY_REGEX_QUERY_PARAM_KEY) !== 'false';
75+
}
76+
7177
if (params.has(ENABLE_DARK_MODE_QUERY_PARAM_KEY)) {
7278
featureFlags.defaultEnableDarkMode =
7379
params.get(ENABLE_DARK_MODE_QUERY_PARAM_KEY) !== 'false';

tensorboard/webapp/webapp_data_source/tb_feature_flag_data_source_test.ts

+21
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,27 @@ describe('tb_feature_flag_data_source', () => {
9595
expect(dataSource.getFeatures()).toEqual({enabledColorGroup: true});
9696
});
9797

98+
it('returns enableColorGroupByRegex from the query params', () => {
99+
spyOn(TEST_ONLY.util, 'getParams').and.returnValues(
100+
new URLSearchParams('enableColorGroupByRegex=false'),
101+
new URLSearchParams('enableColorGroupByRegex='),
102+
new URLSearchParams('enableColorGroupByRegex=true'),
103+
new URLSearchParams('enableColorGroupByRegex=foo')
104+
);
105+
expect(dataSource.getFeatures()).toEqual({
106+
enabledColorGroupByRegex: false,
107+
});
108+
expect(dataSource.getFeatures()).toEqual({
109+
enabledColorGroupByRegex: true,
110+
});
111+
expect(dataSource.getFeatures()).toEqual({
112+
enabledColorGroupByRegex: true,
113+
});
114+
expect(dataSource.getFeatures()).toEqual({
115+
enabledColorGroupByRegex: true,
116+
});
117+
});
118+
98119
it('returns all flag values when they are all set', () => {
99120
spyOn(TEST_ONLY.util, 'getParams').and.returnValue(
100121
new URLSearchParams(

tensorboard/webapp/webapp_data_source/tb_feature_flag_data_source_types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,7 @@ export const SCALARS_BATCH_SIZE_PARAM_KEY = 'scalarsBatchSize';
3737

3838
export const ENABLE_COLOR_GROUP_QUERY_PARAM_KEY = 'enableColorGroup';
3939

40+
export const ENABLE_COLOR_GROUP_BY_REGEX_QUERY_PARAM_KEY =
41+
'enableColorGroupByRegex';
42+
4043
export const ENABLE_DARK_MODE_QUERY_PARAM_KEY = 'darkMode';

0 commit comments

Comments
 (0)