Skip to content

Commit 5ff07ed

Browse files
committed
feature search, click feature to open, small visual tweaks
1 parent ee74cda commit 5ff07ed

File tree

6 files changed

+139
-30
lines changed

6 files changed

+139
-30
lines changed

packages/browser-sdk/src/client.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
} from "./feedback/feedback";
1616
import * as feedbackLib from "./feedback/ui";
1717
import { ToolbarPosition } from "./toolbar/Toolbar";
18-
import { API_BASE_URL, SSE_REALTIME_BASE_URL } from "./config";
18+
import { API_BASE_URL, APP_BASE_URL, SSE_REALTIME_BASE_URL } from "./config";
1919
import { BucketContext, CompanyContext, UserContext } from "./context";
2020
import { HttpClient } from "./httpClient";
2121
import { Logger, loggerWithPrefix, quietConsoleLogger } from "./logger";
@@ -93,6 +93,7 @@ export type PayloadContext = {
9393

9494
interface Config {
9595
apiBaseUrl: string;
96+
appBaseUrl: string;
9697
sseBaseUrl: string;
9798
enableTracking: boolean;
9899
}
@@ -151,6 +152,11 @@ export interface InitOptions {
151152
*/
152153
apiBaseUrl?: string;
153154

155+
/**
156+
* Base URL of the Bucket web app. Links open ín this app by default.
157+
*/
158+
appBaseUrl?: string;
159+
154160
/**
155161
* @deprecated
156162
* Use `sseBaseUrl` instead.
@@ -184,6 +190,7 @@ export interface InitOptions {
184190

185191
const defaultConfig: Config = {
186192
apiBaseUrl: API_BASE_URL,
193+
appBaseUrl: APP_BASE_URL,
187194
sseBaseUrl: SSE_REALTIME_BASE_URL,
188195
enableTracking: true,
189196
};
@@ -243,6 +250,7 @@ export class BucketClient {
243250

244251
this.config = {
245252
apiBaseUrl: opts?.apiBaseUrl ?? opts?.host ?? defaultConfig.apiBaseUrl,
253+
appBaseUrl: opts?.appBaseUrl ?? opts?.host ?? defaultConfig.appBaseUrl,
246254
sseBaseUrl: opts?.sseBaseUrl ?? opts?.sseHost ?? defaultConfig.sseBaseUrl,
247255
enableTracking: opts?.enableTracking ?? defaultConfig.enableTracking,
248256
} satisfies Config;
@@ -332,6 +340,13 @@ export class BucketClient {
332340
}
333341
}
334342

343+
/**
344+
* Get the current configuration.
345+
*/
346+
getConfig() {
347+
return this.config;
348+
}
349+
335350
/**
336351
* Update the user context.
337352
* Performs a shallow merge with the existing user context.

packages/browser-sdk/src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { version } from "../package.json";
22

33
export const API_BASE_URL = "https://front.bucket.co";
4+
export const APP_BASE_URL = "https://app.bucket.co";
45
export const SSE_REALTIME_BASE_URL = "https://livemessaging.bucket.co";
56

67
export const SDK_VERSION_HEADER_NAME = "bucket-sdk-version";

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

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,13 @@
4949
#toolbarRoot {
5050
--brand300: #a39dfc;
5151
--brand400: #847cfb;
52+
--gray500: #787c91;
5253
--black: #1e1f24;
5354

5455
--bg-color: #1e1f24;
5556
--border-color: #292b32;
5657
--logo-color: white;
58+
--text-color: white;
5759
font-family: monospace;
5860
font-size: 12px;
5961
}
@@ -62,10 +64,6 @@
6264
outline: none;
6365
}
6466

65-
.reset {
66-
color: var(--brand300);
67-
}
68-
6967
.dialog {
7068
color: #ffffff;
7169
box-sizing: border-box;
@@ -79,10 +77,6 @@
7977
min-width: 200px;
8078
}
8179

82-
.table tr td {
83-
padding: 3px;
84-
}
85-
8680
.toolbar-toggle {
8781
width: 32px;
8882
height: 32px;
@@ -148,3 +142,73 @@
148142
box-shadow: #d8d9df -1px 1px 1px 0px;
149143
}
150144
}
145+
146+
.search-input {
147+
background: transparent;
148+
border: none;
149+
border-bottom: 1px solid var(--border-color);
150+
color: white;
151+
padding: 0 0 6px 0;
152+
font-family: monospace;
153+
margin-bottom: 4px;
154+
width: 100%;
155+
}
156+
157+
.table {
158+
width: 100%;
159+
border-collapse: collapse;
160+
}
161+
162+
.table tr td {
163+
padding: 6px 6px 4px 0;
164+
}
165+
166+
.reset {
167+
color: var(--brand300);
168+
}
169+
170+
input[type="search"]::-webkit-search-cancel-button {
171+
-webkit-appearance: none;
172+
display: inline-block;
173+
width: 8px;
174+
height: 8px;
175+
margin-left: 10px;
176+
background: linear-gradient(
177+
45deg,
178+
rgba(0, 0, 0, 0) 0%,
179+
rgba(0, 0, 0, 0) 43%,
180+
#fff 45%,
181+
#fff 55%,
182+
rgba(0, 0, 0, 0) 57%,
183+
rgba(0, 0, 0, 0) 100%
184+
),
185+
linear-gradient(
186+
135deg,
187+
transparent 0%,
188+
transparent 43%,
189+
#fff 45%,
190+
#fff 55%,
191+
transparent 57%,
192+
transparent 100%
193+
);
194+
cursor: pointer;
195+
}
196+
197+
.feature-name-cell {
198+
white-space: nowrap;
199+
overflow: hidden;
200+
text-overflow: ellipsis;
201+
width: auto;
202+
}
203+
204+
.feature-reset-cell {
205+
min-width: "38px";
206+
}
207+
208+
.feature-link {
209+
color: var(--text-color);
210+
text-decoration: none;
211+
&:hover {
212+
text-decoration: underline;
213+
}
214+
}

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

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ export default function Toolbar({
6969
return bucketClient.onFeaturesUpdated(updateFeatures);
7070
}, [bucketClient]);
7171

72+
const [search, setSearch] = useState<string | null>(null);
73+
const onSearch = (val: string) => {
74+
setSearch(val === "" ? null : val);
75+
};
76+
77+
const searchedFeatures =
78+
search === null ? features : features.filter((f) => f.key.includes(search));
79+
80+
const appBaseUrl = bucketClient.getConfig().appBaseUrl;
81+
7282
return (
7383
<div id="toolbarRoot">
7484
<style dangerouslySetInnerHTML={{ __html: styles }}></style>
@@ -88,11 +98,13 @@ export default function Toolbar({
8898
}}
8999
onClose={closeToolbar}
90100
>
101+
<FeatureSearch onSearch={onSearch} />
91102
<FeatureTable
92-
features={features}
103+
features={searchedFeatures}
93104
setEnabledOverride={bucketClient.setFeatureOverride.bind(
94105
bucketClient,
95106
)}
107+
appBaseUrl={appBaseUrl}
96108
/>
97109
</Dialog>
98110
</div>
@@ -130,6 +142,18 @@ function ToolbarToggle({
130142
);
131143
}
132144

145+
function FeatureSearch({ onSearch }: { onSearch: (val: string) => void }) {
146+
return (
147+
<input
148+
type="search"
149+
placeholder="Search"
150+
onInput={(s) => onSearch(s.currentTarget.value)}
151+
autoFocus
152+
class="search-input"
153+
/>
154+
);
155+
}
156+
133157
function Reset({
134158
setEnabledOverride,
135159
featureKey,
@@ -154,49 +178,53 @@ function Reset({
154178
function FeatureTable({
155179
features,
156180
setEnabledOverride,
181+
appBaseUrl,
157182
}: {
158183
features: {
159184
key: string;
160185
localOverride: boolean | null;
161186
isEnabled: boolean;
162187
}[];
163188
setEnabledOverride: (key: string, value: boolean | null) => void;
189+
appBaseUrl: string;
164190
}) {
191+
if (features.length === 0) {
192+
return <div style={{ color: "var(--gray500)" }}>No features found</div>;
193+
}
165194
return (
166195
<table class="table">
167196
<tbody>
168-
{features.map((feat) => {
197+
{features.map((feature) => {
169198
return (
170-
<tr key={feat!.key}>
171-
<td
172-
style={{
173-
whiteSpace: "nowrap",
174-
overflow: "hidden",
175-
textOverflow: "ellipsis",
176-
width: "auto",
177-
}}
178-
>
179-
{feat!.key}
199+
<tr key={feature.key}>
200+
<td class="feature-name-cell">
201+
<a
202+
href={`${appBaseUrl}/envs/current/features/by-key/${feature.key}`}
203+
target="_blank"
204+
class="feature-link"
205+
>
206+
{feature.key}
207+
</a>
180208
</td>
181-
<td style={{ minWidth: "38px" }}>
182-
{feat?.localOverride !== null ? (
209+
<td class="feature-reset-cell">
210+
{feature.localOverride !== null ? (
183211
<Reset
184212
setEnabledOverride={setEnabledOverride}
185-
featureKey={feat!.key}
213+
featureKey={feature.key}
186214
/>
187215
) : null}
188216
</td>
189217
<td>
190218
<Switch
191219
isOn={
192-
(feat?.localOverride === null && feat!.isEnabled) ||
193-
feat?.localOverride === true
220+
(feature.localOverride === null && feature.isEnabled) ||
221+
feature.localOverride === true
194222
}
195223
onChange={(e) => {
196224
const isChecked = e.currentTarget.checked;
197-
const isOverridden = isChecked !== feat!.isEnabled;
225+
const isOverridden = isChecked !== feature.isEnabled;
198226
setEnabledOverride(
199-
feat!.key,
227+
feature.key,
200228
isOverridden ? isChecked : null,
201229
);
202230
}}

packages/browser-sdk/src/ui/Switch.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@
1818
position: absolute;
1919
top: 1px;
2020
transition: transform 0.1s ease-in-out;
21+
box-shadow: 0 0px 5px rgba(0, 0, 0, 0.2);
2122
}

packages/browser-sdk/src/ui/Switch.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ const gutter = 1;
1212

1313
export function Switch({
1414
isOn,
15-
width = 30,
16-
height = 15,
15+
width = 24,
16+
height = 14,
1717
...props
1818
}: SwitchProps) {
1919
return (

0 commit comments

Comments
 (0)