Skip to content

Commit b328b2a

Browse files
committed
refactor(browser-sdk): refactor overrides API
includes new example of how to make your own toolbar
1 parent b09adb3 commit b328b2a

6 files changed

Lines changed: 86 additions & 34 deletions

File tree

packages/browser-sdk/src/client.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ export type FeatureRemoteConfig =
319319
export interface Feature {
320320
/**
321321
* Result of feature flag evaluation.
322+
* Note: Does not take local overrides into account.
322323
*/
323324
isEnabled: boolean;
324325

@@ -338,6 +339,17 @@ export interface Feature {
338339
requestFeedback: (
339340
options: Omit<RequestFeedbackData, "featureKey" | "featureId">,
340341
) => void;
342+
343+
/**
344+
* The current override status of isEnabled for the feature.
345+
*/
346+
isEnabledOverride: boolean | null;
347+
348+
/**
349+
* Set the override status for isEnabled for the feature.
350+
* Set to `null` to remove the override.
351+
*/
352+
setIsEnabledOverride(isEnabled: boolean | null): void;
341353
}
342354

343355
function shouldShowToolbar(opts: InitOptions) {
@@ -778,23 +790,13 @@ export class BucketClient {
778790
...options,
779791
});
780792
},
793+
isEnabledOverride: this.featuresClient.getFeatureOverride(key),
794+
setIsEnabledOverride(isEnabled: boolean | null) {
795+
self.featuresClient.setFeatureOverride(key, isEnabled);
796+
},
781797
};
782798
}
783799

784-
/**
785-
* @internal
786-
*/
787-
setFeatureOverride(key: string, isEnabled: boolean | null) {
788-
this.featuresClient.setFeatureOverride(key, isEnabled);
789-
}
790-
791-
/**
792-
* @internal
793-
*/
794-
getFeatureOverride(key: string): boolean | null {
795-
return this.featuresClient.getFeatureOverride(key);
796-
}
797-
798800
private sendCheckEvent(checkEvent: CheckEvent) {
799801
return this.featuresClient.sendCheckEvent(checkEvent, () => {
800802
this.hooks.trigger(

packages/browser-sdk/src/toolbar/Features.tsx

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ import { FeatureItem } from "./Toolbar";
99
export function FeaturesTable({
1010
features,
1111
searchQuery,
12-
setEnabledOverride,
1312
appBaseUrl,
1413
isOpen,
14+
setIsEnabledOverride,
1515
}: {
1616
features: FeatureItem[];
1717
searchQuery: string | null;
18-
setEnabledOverride: (key: string, value: boolean | null) => void;
1918
appBaseUrl: string;
2019
isOpen: boolean;
20+
setIsEnabledOverride: (key: string, isEnabled: boolean | null) => void;
2121
}) {
2222
const searchedFeatures =
2323
searchQuery === null
@@ -43,7 +43,7 @@ export function FeaturesTable({
4343
.includes(searchQuery.toLocaleLowerCase())
4444
}
4545
isOpen={isOpen}
46-
setEnabledOverride={setEnabledOverride}
46+
setEnabledOverride={() => setIsEnabledOverride(feature.key, null)}
4747
/>
4848
))}
4949
</tbody>
@@ -61,7 +61,7 @@ function FeatureRow({
6161
}: {
6262
feature: FeatureItem;
6363
appBaseUrl: string;
64-
setEnabledOverride: (key: string, value: boolean | null) => void;
64+
setEnabledOverride: (isEnabled: boolean | null) => void;
6565
isOpen: boolean;
6666
index: number;
6767
isNotVisible: boolean;
@@ -94,11 +94,7 @@ function FeatureRow({
9494
</td>
9595
<td class="feature-reset-cell">
9696
{feature.localOverride !== null ? (
97-
<Reset
98-
featureKey={feature.key}
99-
setEnabledOverride={setEnabledOverride}
100-
tabIndex={index + 1}
101-
/>
97+
<Reset setEnabledOverride={setEnabledOverride} tabIndex={index + 1} />
10298
) : null}
10399
</td>
104100
<td class="feature-switch-cell">
@@ -111,7 +107,7 @@ function FeatureRow({
111107
onChange={(e) => {
112108
const isChecked = e.currentTarget.checked;
113109
const isOverridden = isChecked !== feature.isEnabled;
114-
setEnabledOverride(feature.key, isOverridden ? isChecked : null);
110+
setEnabledOverride(isOverridden ? isChecked : null);
115111
}}
116112
/>
117113
</td>
@@ -138,19 +134,17 @@ export function FeatureSearch({
138134

139135
function Reset({
140136
setEnabledOverride,
141-
featureKey,
142137
...props
143138
}: {
144-
setEnabledOverride: (key: string, value: boolean | null) => void;
145-
featureKey: string;
139+
setEnabledOverride: (isEnabled: boolean | null) => void;
146140
} & h.JSX.HTMLAttributes<HTMLAnchorElement>) {
147141
return (
148142
<a
149143
class="reset"
150144
href=""
151145
onClick={(e) => {
152146
e.preventDefault();
153-
setEnabledOverride(featureKey, null);
147+
setEnabledOverride(null);
154148
}}
155149
{...props}
156150
>

packages/browser-sdk/src/toolbar/Toolbar.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default function Toolbar({
4848
(feature) =>
4949
({
5050
key: feature.key,
51-
localOverride: bucketClient.getFeatureOverride(feature?.key),
51+
localOverride: feature.isEnabledOverride,
5252
isEnabled: feature.isEnabled,
5353
}) satisfies FeatureItem,
5454
),
@@ -108,9 +108,9 @@ export default function Toolbar({
108108
features={sortedFeatures}
109109
isOpen={isOpen}
110110
searchQuery={search}
111-
setEnabledOverride={bucketClient.setFeatureOverride.bind(
112-
bucketClient,
113-
)}
111+
setIsEnabledOverride={(key, isEnabled) =>
112+
bucketClient.getFeature(key).setIsEnabledOverride(isEnabled)
113+
}
114114
/>
115115
</DialogContent>
116116
</Dialog>

packages/browser-sdk/test/client.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe("BucketClient", () => {
7070
await client.initialize();
7171
expect(featuresResult["featureA"].isEnabled).toBe(true);
7272
expect(client.getFeature("featureA").isEnabled).toBe(true);
73-
client.setFeatureOverride("featureA", false);
73+
client.getFeature("featureA").setIsEnabledOverride(false);
7474
expect(client.getFeature("featureA").isEnabled).toBe(false);
7575
});
7676
});

packages/browser-sdk/test/usage.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ describe("usage", () => {
8080
track: expect.any(Function),
8181
requestFeedback: expect.any(Function),
8282
config: { key: undefined, payload: undefined },
83+
isEnabledOverride: null,
84+
setIsEnabledOverride: expect.any(Function),
8385
});
8486
});
8587

@@ -413,6 +415,8 @@ describe(`sends "check" events `, () => {
413415
config: { key: undefined, payload: undefined },
414416
track: expect.any(Function),
415417
requestFeedback: expect.any(Function),
418+
isEnabledOverride: null,
419+
setIsEnabledOverride: expect.any(Function),
416420
});
417421

418422
expect(client.getFeature("featureB")).toStrictEqual({
@@ -426,13 +430,17 @@ describe(`sends "check" events `, () => {
426430
},
427431
track: expect.any(Function),
428432
requestFeedback: expect.any(Function),
433+
isEnabledOverride: null,
434+
setIsEnabledOverride: expect.any(Function),
429435
});
430436

431437
expect(client.getFeature("featureC")).toStrictEqual({
432438
isEnabled: false,
433439
config: { key: undefined, payload: undefined },
434440
track: expect.any(Function),
435441
requestFeedback: expect.any(Function),
442+
isEnabledOverride: null,
443+
setIsEnabledOverride: expect.any(Function),
436444
});
437445
});
438446

@@ -567,6 +575,8 @@ describe(`sends "check" events `, () => {
567575
track: expect.any(Function),
568576
requestFeedback: expect.any(Function),
569577
config: { key: undefined, payload: undefined },
578+
isEnabledOverride: null,
579+
setIsEnabledOverride: expect.any(Function),
570580
});
571581

572582
vi.spyOn(client, "track");
@@ -586,6 +596,8 @@ describe(`sends "check" events `, () => {
586596
track: expect.any(Function),
587597
requestFeedback: expect.any(Function),
588598
config: { key: undefined, payload: undefined },
599+
isEnabledOverride: null,
600+
setIsEnabledOverride: expect.any(Function),
589601
});
590602

591603
vi.spyOn(client, "requestFeedback");

packages/react-sdk/dev/plain/app.tsx

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
useUpdateCompany,
1010
useUpdateOtherContext,
1111
useUpdateUser,
12+
useClient,
1213
} from "../../src";
1314

1415
// Extending the Features interface to define the available features
@@ -187,6 +188,7 @@ function Demos() {
187188
<UpdateContext />
188189
<Feedback />
189190
<SendEvent />
191+
<CustomToolbar />
190192
</main>
191193
);
192194
}
@@ -223,6 +225,49 @@ function FeatureOptIn({
223225
);
224226
}
225227

228+
function CustomToolbar() {
229+
const client = useClient();
230+
231+
if (!client) {
232+
return null;
233+
}
234+
235+
return (
236+
<div>
237+
<h2>Custom toolbar</h2>
238+
<ul>
239+
{Object.entries(client.getFeatures()).map(([featureKey, feature]) => (
240+
<li key={featureKey}>
241+
{featureKey} -
242+
{(feature.isEnabledOverride ?? feature.isEnabled)
243+
? "Enabled"
244+
: "Disabled"}{" "}
245+
{feature.isEnabledOverride !== null && (
246+
<button
247+
onClick={() => {
248+
client.getFeature(featureKey).setIsEnabledOverride(null);
249+
}}
250+
>
251+
Reset
252+
</button>
253+
)}
254+
<input
255+
checked={feature.isEnabledOverride ?? false}
256+
type="checkbox"
257+
onChange={(e) => {
258+
console.log("onChange", e.target.checked);
259+
client
260+
.getFeature(featureKey)
261+
.setIsEnabledOverride(e.target.checked ?? false);
262+
}}
263+
/>
264+
</li>
265+
))}
266+
</ul>
267+
</div>
268+
);
269+
}
270+
226271
export function App() {
227272
return (
228273
<BucketProvider
@@ -233,7 +278,6 @@ export function App() {
233278
apiBaseUrl={apiBaseUrl}
234279
>
235280
<Demos />
236-
{}
237281
</BucketProvider>
238282
);
239283
}

0 commit comments

Comments
 (0)