Skip to content

Commit eaa8353

Browse files
authored
Feature: Update the showFlags feature to allow filtering (#6245)
## Motivation for features / changes As we add more feature flags it's starting to get hard to find them. This is particularly true when trying to help someone else find a feature flag in the modal. The `enableShowFlags` feature was a weird edge case that wasn't being fully utilized. Now it supports string values. When a string value is supplied it filters the feature flags shown. ## Technical description of changes I changed the name and value of `enableShowFlags: boolean` to `showFlags: string`. I then updated the `feature_flag_page_container` to filter the feature flags rendered based on the value of the `showFlags` feature. ## Screenshots of UI changes Modal With a Filter ![image](https://user-images.githubusercontent.com/78179109/225770098-5460517e-8946-42f2-8f6a-b26980b63ffc.png) Modal without a filter ![image](https://user-images.githubusercontent.com/78179109/225770279-034c0eb7-a566-4376-b265-39d6adae4b9d.png) ## Detailed steps to verify changes work correctly (as executed by you) 1) Start tensorboard 2) Navigate to localhost:6006 3) Assert the feature flags modal does not appear 4) Navigate to localhost:6006?showFlags 5) Assert the feature flags modal appears 6) Assert the no filter is applied 7) Navigate to localhost:6006?showFlags=enable 8) Assert the feature flags modal appears and has a filter applied ## Alternate designs / implementations considered Adding the filter could automatically toggle a feature? Adding an input field to filter things would make sense...
1 parent 6f28538 commit eaa8353

11 files changed

+134
-31
lines changed

tensorboard/webapp/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ tf_ng_web_test_suite(
268268
"//tensorboard/webapp/core/views:test_lib",
269269
"//tensorboard/webapp/customization:customization_test_lib",
270270
"//tensorboard/webapp/deeplink:deeplink_test_lib",
271+
"//tensorboard/webapp/feature_flag/views:views_test",
271272
"//tensorboard/webapp/header:test_lib",
272273
"//tensorboard/webapp/metrics:integration_test",
273274
"//tensorboard/webapp/metrics:test_lib",

tensorboard/webapp/feature_flag/store/feature_flag_metadata.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ export const FeatureFlagMetadataMap: FeatureFlagMetadataMapType<FeatureFlags> =
110110
defaultValue: true,
111111
queryParamOverride: null,
112112
},
113-
enableShowFlags: {
114-
defaultValue: false,
113+
showFlags: {
114+
defaultValue: undefined,
115115
queryParamOverride: 'showFlags',
116-
parseValue: parseBoolean,
116+
parseValue: (str) => str,
117117
},
118118
allowRangeSelection: {
119119
defaultValue: true,

tensorboard/webapp/feature_flag/store/feature_flag_selectors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ export const getIsDataTableEnabled = createSelector(
150150
export const getShowFlagsEnabled = createSelector(
151151
getFeatureFlags,
152152
(flags: FeatureFlags): boolean => {
153-
return flags.enableShowFlags;
153+
return flags.showFlags !== undefined;
154154
}
155155
);
156156

tensorboard/webapp/feature_flag/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ export interface FeatureFlags {
4444
forceSvg: boolean;
4545
// Whether to enable the "sticky" data table in scalar cards.
4646
enabledScalarDataTable: boolean;
47-
// If enabled causes the feature flags modal to appear.
48-
enableShowFlags: boolean;
47+
// If defined causes the feature flags modal to appear.
48+
showFlags: string | undefined;
4949
// Adds check box in settings which allows users to enter step selection range.
5050
allowRangeSelection: boolean;
5151
// In Linked Time, if enabled, show a prospective fob user to turn on the feature or select a step.

tensorboard/webapp/feature_flag/views/feature_flag_modal_trigger_container.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ export class FeatureFlagModalTriggerContainer implements OnInit {
4949
// dialog from appearing again after the page is refreshed.
5050
this.store.dispatch(
5151
featureFlagOverridesReset({
52-
flags: ['enableShowFlags'],
52+
flags: ['showFlags'],
5353
})
5454
);
5555
// Reload the page so that the application restarts with stable
5656
// feature flag values.
57-
// Wait one tick before reloading the page so the 'enableShowFlags'
57+
// Wait one tick before reloading the page so the 'showFlags'
5858
// reset has a chance to be reflected in the URL before page reload.
5959
setTimeout(() => {
6060
util.reloadWindow();

tensorboard/webapp/feature_flag/views/feature_flag_modal_trigger_container_test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe('feature_flag_modal_trigger_container', () => {
7777
rootLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);
7878
}
7979

80-
it('creates modal when enableShowFlags is true', async () => {
80+
it('creates modal when showFlags is true', async () => {
8181
store.overrideSelector(getDefaultFeatureFlags, {} as FeatureFlags);
8282
store.overrideSelector(getOverriddenFeatureFlags, {});
8383
store.overrideSelector(getShowFlagsEnabled, true);
@@ -90,7 +90,7 @@ describe('feature_flag_modal_trigger_container', () => {
9090
expect(dialog).toBeDefined();
9191
});
9292

93-
it('does not create modal when enableShowFlags is false', async () => {
93+
it('does not create modal when showFlags is false', async () => {
9494
store.overrideSelector(getDefaultFeatureFlags, {} as FeatureFlags);
9595
store.overrideSelector(getOverriddenFeatureFlags, {});
9696
store.overrideSelector(getShowFlagsEnabled, false);

tensorboard/webapp/feature_flag/views/feature_flag_modal_trigger_module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {FeatureFlagPageModule} from './feature_flag_module';
1919

2020
/**
2121
* Provides the wrapper component that triggers the opening of the feature flag modal.
22-
* The modal appears based on the value of the enableShowFlags feature flag.
22+
* The modal appears based on the value of the showFlags feature flag.
2323
*/
2424
@NgModule({
2525
declarations: [FeatureFlagModalTriggerContainer],

tensorboard/webapp/feature_flag/views/feature_flag_page_component.ng.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ <h2 class="warning">WARNING: EXPERIMENTAL FEATURES AHEAD!</h2>
3030
Only flags with non default values are sent to the backend.
3131
</div>
3232
</ng-container>
33+
<ng-container *ngIf="showFlagsFilter">
34+
<div class="message">
35+
Feature Flags are filtered to only show features containing
36+
"{{showFlagsFilter}}"
37+
</div>
38+
</ng-container>
3339
<table class="feature-flag-table">
3440
<ng-container *ngFor="let flagStatus of featureFlagStatuses;">
3541
<tr>

tensorboard/webapp/feature_flag/views/feature_flag_page_component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export class FeatureFlagPageComponent {
2727

2828
@Input() hasFlagsSentToServer: boolean = false;
2929

30+
@Input() showFlagsFilter: string | undefined;
31+
3032
@Output() flagChanged = new EventEmitter<FeatureFlagStatusEvent>();
3133

3234
@Output() allFlagsReset = new EventEmitter();

tensorboard/webapp/feature_flag/views/feature_flag_page_container.ts

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,20 @@ import {
4444
template: `<feature-flag-page-component
4545
[featureFlagStatuses]="featureFlags$ | async"
4646
[hasFlagsSentToServer]="hasFlagsSentToServer$ | async"
47+
[showFlagsFilter]="showFlagsFilter$ | async"
4748
(flagChanged)="onFlagChanged($event)"
4849
(allFlagsReset)="onAllFlagsReset()"
4950
></feature-flag-page-component>`,
5051
})
5152
export class FeatureFlagPageContainer {
5253
constructor(private readonly store: Store<State>) {}
5354

55+
readonly showFlagsFilter$ = this.store.select(getOverriddenFeatureFlags).pipe(
56+
map((overriddenFeatureFlags) => {
57+
return overriddenFeatureFlags.showFlags?.toLowerCase();
58+
})
59+
);
60+
5461
readonly hasFlagsSentToServer$: Observable<boolean> = this.store
5562
.select(getFeatureFlagsMetadata)
5663
.pipe(
@@ -66,27 +73,40 @@ export class FeatureFlagPageContainer {
6673
this.store.select(getOverriddenFeatureFlags).pipe(
6774
withLatestFrom(
6875
this.store.select(getDefaultFeatureFlags),
69-
this.store.select(getFeatureFlagsMetadata)
76+
this.store.select(getFeatureFlagsMetadata),
77+
this.showFlagsFilter$
7078
),
71-
map(([overriddenFeatureFlags, defaultFeatureFlags, flagMetadata]) => {
72-
return Object.entries(defaultFeatureFlags).map(
73-
([flagName, defaultValue]) => {
74-
const status = getFlagStatus(
75-
flagName as keyof FeatureFlags,
76-
overriddenFeatureFlags
77-
);
78-
const metadata = flagMetadata[flagName as keyof FeatureFlags];
79-
return {
80-
flag: flagName,
81-
defaultValue,
82-
status,
83-
sendToServerWhenOverridden: (
84-
metadata as AdvancedFeatureFlagMetadata<FeatureFlagType>
85-
).sendToServerWhenOverridden,
86-
} as FeatureFlagStatus<keyof FeatureFlags>;
87-
}
88-
);
89-
})
79+
map(
80+
([
81+
overriddenFeatureFlags,
82+
defaultFeatureFlags,
83+
flagMetadata,
84+
showFlagsFilter,
85+
]) => {
86+
return Object.entries(defaultFeatureFlags)
87+
.filter(([flagName]) => {
88+
if (!showFlagsFilter) {
89+
return true;
90+
}
91+
return flagName.toLowerCase().includes(showFlagsFilter);
92+
})
93+
.map(([flagName, defaultValue]) => {
94+
const status = getFlagStatus(
95+
flagName as keyof FeatureFlags,
96+
overriddenFeatureFlags
97+
);
98+
const metadata = flagMetadata[flagName as keyof FeatureFlags];
99+
return {
100+
flag: flagName,
101+
defaultValue,
102+
status,
103+
sendToServerWhenOverridden: (
104+
metadata as AdvancedFeatureFlagMetadata<FeatureFlagType>
105+
).sendToServerWhenOverridden,
106+
} as FeatureFlagStatus<keyof FeatureFlags>;
107+
});
108+
}
109+
)
90110
);
91111

92112
onFlagChanged({flag, status}: FeatureFlagStatusEvent) {

0 commit comments

Comments
 (0)