Skip to content

Commit b306e0d

Browse files
committed
split out toolbar into v1 and v2
1 parent 52ff4da commit b306e0d

20 files changed

Lines changed: 436 additions & 30 deletions

File tree

examples/guide-example/src/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ function App() {
6565
readyToTarget={true}
6666
listenForUpdates={true}
6767
colorMode={colorMode}
68+
toolbar="v2"
6869
>
6970
<div style={{ padding: "1rem 2rem" }}>
7071
<h1>Knock In-App Guide Example</h1>

packages/client/src/clients/guide/client.ts

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,15 @@ export class KnockGuideClient {
270270
) {
271271
const {
272272
trackLocationFromWindow = true,
273+
// TODO: Remove this option once we ship guide toolbar v2, and offload
274+
// as much debugging specific logic and responsibilities to toolbar.
275+
trackDebugParams = false,
273276
throttleCheckInterval = DEFAULT_COUNTER_INCREMENT_INTERVAL,
274277
} = options;
275278
const win = checkForWindow();
276279

277280
const location = trackLocationFromWindow ? win?.location?.href : undefined;
278-
279-
const debug = detectDebugParams();
281+
const debug = trackDebugParams ? detectDebugParams() : undefined;
280282

281283
this.store = new Store<StoreState>({
282284
guideGroups: [],
@@ -412,8 +414,8 @@ export class KnockGuideClient {
412414
const params = {
413415
...this.targetParams,
414416
user_id: this.knock.userId,
415-
force_all_guides: debugState.forcedGuideKey ? true : undefined,
416-
preview_session_id: debugState.previewSessionId || undefined,
417+
force_all_guides: debugState?.forcedGuideKey ? true : undefined,
418+
preview_session_id: debugState?.previewSessionId || undefined,
417419
};
418420

419421
const newChannel = this.socket.channel(this.socketChannelTopic, params);
@@ -561,6 +563,39 @@ export class KnockGuideClient {
561563
}
562564
}
563565

566+
setDebug(debugOpts?: Omit<DebugState, "debugging">) {
567+
this.knock.log("[Guide] .setDebug()");
568+
const shouldRefetch = !this.store.state.debug?.debugging;
569+
570+
this.store.setState((state) => ({
571+
...state,
572+
debug: { ...debugOpts, debugging: true },
573+
}));
574+
575+
if (shouldRefetch) {
576+
this.knock.log(
577+
`[Guide] Start debugging, refetching guides and resubscribing to the websocket channel`,
578+
);
579+
this.fetch();
580+
this.subscribe();
581+
}
582+
}
583+
584+
unsetDebug() {
585+
this.knock.log("[Guide] .unsetDebug()");
586+
const shouldRefetch = this.store.state.debug?.debugging;
587+
588+
this.store.setState((state) => ({ ...state, debug: undefined }));
589+
590+
if (shouldRefetch) {
591+
this.knock.log(
592+
`[Guide] Stop debugging, refetching guides and resubscribing to the websocket channel`,
593+
);
594+
this.fetch();
595+
this.subscribe();
596+
}
597+
}
598+
564599
//
565600
// Store selector
566601
//
@@ -924,7 +959,7 @@ export class KnockGuideClient {
924959
// Get the next unarchived step.
925960
getStep() {
926961
// If debugging this guide, return the first step regardless of archive status
927-
if (self.store.state.debug.forcedGuideKey === this.key) {
962+
if (self.store.state.debug?.forcedGuideKey === this.key) {
928963
return this.steps[0];
929964
}
930965

@@ -981,7 +1016,7 @@ export class KnockGuideClient {
9811016

9821017
// Append debug params
9831018
const debugState = this.store.state.debug;
984-
if (debugState.forcedGuideKey) {
1019+
if (debugState?.forcedGuideKey || debugState?.debugging) {
9851020
combinedParams.force_all_guides = true;
9861021
}
9871022

@@ -1150,8 +1185,15 @@ export class KnockGuideClient {
11501185

11511186
this.knock.log(`[Guide] Detected a location change: ${href}`);
11521187

1188+
if (!this.options.trackDebugParams) {
1189+
this.setLocation(href);
1190+
return;
1191+
}
1192+
1193+
// TODO: Remove below once we ship toolbar v2.
1194+
11531195
// If entering debug mode, fetch all guides.
1154-
const currentDebugParams = this.store.state.debug;
1196+
const currentDebugParams = this.store.state.debug || {};
11551197
const newDebugParams = detectDebugParams();
11561198

11571199
this.setLocation(href, { debug: newDebugParams });

packages/client/src/clients/guide/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ export type QueryStatus = {
202202
};
203203

204204
export type DebugState = {
205+
debugging?: boolean;
205206
forcedGuideKey?: string | null;
206207
previewSessionId?: string | null;
207208
};
@@ -218,7 +219,7 @@ export type StoreState = {
218219
queries: Record<QueryKey, QueryStatus>;
219220
location: string | undefined;
220221
counter: number;
221-
debug: DebugState;
222+
debug?: DebugState;
222223
};
223224

224225
export type QueryFilterParams = Pick<GetGuidesQueryParams, "type">;
@@ -241,6 +242,7 @@ export type TargetParams = {
241242

242243
export type ConstructorOpts = {
243244
trackLocationFromWindow?: boolean;
245+
trackDebugParams?: boolean;
244246
orderResolutionDuration?: number;
245247
throttleCheckInterval?: number;
246248
};

packages/client/test/clients/guide/guide.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ describe("KnockGuideClient", () => {
165165
queries: {},
166166
location: undefined,
167167
counter: 0,
168-
debug: { forcedGuideKey: null, previewSessionId: null },
168+
debug: undefined,
169169
});
170170
});
171171

@@ -194,7 +194,7 @@ describe("KnockGuideClient", () => {
194194
queries: {},
195195
location: "https://example.com",
196196
counter: 0,
197-
debug: { forcedGuideKey: null, previewSessionId: null },
197+
debug: undefined,
198198
});
199199
});
200200

@@ -211,7 +211,7 @@ describe("KnockGuideClient", () => {
211211
queries: {},
212212
location: undefined,
213213
counter: 0,
214-
debug: { forcedGuideKey: null, previewSessionId: null },
214+
debug: undefined,
215215
});
216216
});
217217

@@ -234,7 +234,7 @@ describe("KnockGuideClient", () => {
234234
});
235235

236236
expect(() => {
237-
new KnockGuideClient(mockKnock, channelId);
237+
new KnockGuideClient(mockKnock, channelId, {}, { trackDebugParams: true });
238238
}).not.toThrow();
239239

240240
expect(mockLocalStorageWithErrors.setItem).toHaveBeenCalled();
@@ -2959,7 +2959,7 @@ describe("KnockGuideClient", () => {
29592959
mockKnock,
29602960
channelId,
29612961
{},
2962-
{ trackLocationFromWindow: true },
2962+
{ trackLocationFromWindow: true, trackDebugParams: true },
29632963
);
29642964

29652965
client.store.state.debug = { forcedGuideKey: null };
@@ -3016,7 +3016,7 @@ describe("KnockGuideClient", () => {
30163016
mockKnock,
30173017
channelId,
30183018
{},
3019-
{ trackLocationFromWindow: true },
3019+
{ trackLocationFromWindow: true, trackDebugParams: true },
30203020
);
30213021

30223022
client.store.state.debug = { forcedGuideKey: "test_guide" };

packages/react-core/src/modules/guide/context/KnockGuideProvider.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export type KnockGuideProviderProps = {
2323
colorMode?: ColorMode;
2424
targetParams?: KnockGuideTargetParams;
2525
trackLocationFromWindow?: boolean;
26+
trackDebugParams?: boolean;
2627
orderResolutionDuration?: number; // in milliseconds
2728
throttleCheckInterval?: number; // in milliseconds
2829
};
@@ -36,6 +37,10 @@ export const KnockGuideProvider: React.FC<
3637
colorMode = "light",
3738
targetParams = {},
3839
trackLocationFromWindow = true,
40+
// Whether the guide client should look for debug params from url or local
41+
// storage to launch guide toolbar. Set to true when using toolbar v1, but
42+
// plan to phase this out going forward with v2.
43+
trackDebugParams = false,
3944
// Default to 0 which works well for react apps as this "yields" to react for
4045
// one render cyle first and close the group stage.
4146
orderResolutionDuration = 0,
@@ -55,6 +60,7 @@ export const KnockGuideProvider: React.FC<
5560
const knockGuideClient = React.useMemo(() => {
5661
return new KnockGuideClient(knock, channelId, stableTargetParams, {
5762
trackLocationFromWindow,
63+
trackDebugParams,
5864
orderResolutionDuration,
5965
throttleCheckInterval,
6066
});
@@ -63,6 +69,7 @@ export const KnockGuideProvider: React.FC<
6369
channelId,
6470
stableTargetParams,
6571
trackLocationFromWindow,
72+
trackDebugParams,
6673
orderResolutionDuration,
6774
throttleCheckInterval,
6875
]);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// vite.config.mts
2+
import { codecovVitePlugin } from "file:///Users/tsyy/work/javascript/node_modules/@codecov/vite-plugin/dist/index.mjs";
3+
import react from "file:///Users/tsyy/work/javascript/node_modules/@vitejs/plugin-react/dist/index.mjs";
4+
import { resolve } from "path";
5+
import { defineConfig, loadEnv } from "file:///Users/tsyy/work/javascript/node_modules/vite/dist/node/index.js";
6+
import dts from "file:///Users/tsyy/work/javascript/node_modules/vite-plugin-dts/dist/index.mjs";
7+
import noBundlePlugin from "file:///Users/tsyy/work/javascript/node_modules/vite-plugin-no-bundle/dist/index.js";
8+
var __vite_injected_original_dirname = "/Users/tsyy/work/javascript/packages/react-core";
9+
var vite_config_default = defineConfig(({ mode }) => {
10+
const env = loadEnv(mode, process.cwd(), "");
11+
const CJS = env.BUILD_TARGET?.toLocaleLowerCase()?.match("cjs");
12+
const formats = CJS ? ["cjs"] : ["es"];
13+
return {
14+
plugins: [
15+
dts({
16+
outDir: "dist/types"
17+
}),
18+
react({
19+
jsxRuntime: "classic",
20+
babel: {
21+
plugins: ["react-require"]
22+
}
23+
}),
24+
noBundlePlugin({ root: "./src" }),
25+
codecovVitePlugin({
26+
enableBundleAnalysis: process.env.CODECOV_TOKEN !== void 0,
27+
bundleName: "@knocklabs/react-core",
28+
uploadToken: process.env.CODECOV_TOKEN
29+
})
30+
],
31+
build: {
32+
outDir: CJS ? "dist/cjs" : "dist/esm",
33+
sourcemap: true,
34+
lib: {
35+
entry: resolve(__vite_injected_original_dirname, "src"),
36+
fileName: `[name]`,
37+
name: "react-core",
38+
formats
39+
},
40+
rollupOptions: {
41+
// External packages that should not be bundled
42+
external: ["react"],
43+
output: {
44+
interop: "compat",
45+
globals: {
46+
react: "React"
47+
},
48+
entryFileNames: () => {
49+
return `[name].${CJS ? "js" : "mjs"}`;
50+
}
51+
}
52+
}
53+
},
54+
test: {
55+
global: true,
56+
environment: "jsdom",
57+
setupFiles: "./setupTest.ts"
58+
}
59+
};
60+
});
61+
export {
62+
vite_config_default as default
63+
};
64+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcubXRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL3RzeXkvd29yay9qYXZhc2NyaXB0L3BhY2thZ2VzL3JlYWN0LWNvcmVcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9Vc2Vycy90c3l5L3dvcmsvamF2YXNjcmlwdC9wYWNrYWdlcy9yZWFjdC1jb3JlL3ZpdGUuY29uZmlnLm10c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vVXNlcnMvdHN5eS93b3JrL2phdmFzY3JpcHQvcGFja2FnZXMvcmVhY3QtY29yZS92aXRlLmNvbmZpZy5tdHNcIjsvLy8gPHJlZmVyZW5jZSB0eXBlcz1cInZpdGVzdFwiIC8+XG5pbXBvcnQgeyBjb2RlY292Vml0ZVBsdWdpbiB9IGZyb20gXCJAY29kZWNvdi92aXRlLXBsdWdpblwiO1xuaW1wb3J0IHJlYWN0IGZyb20gXCJAdml0ZWpzL3BsdWdpbi1yZWFjdFwiO1xuaW1wb3J0IHsgcmVzb2x2ZSB9IGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBMaWJyYXJ5Rm9ybWF0cywgZGVmaW5lQ29uZmlnLCBsb2FkRW52IH0gZnJvbSBcInZpdGVcIjtcbmltcG9ydCBkdHMgZnJvbSBcInZpdGUtcGx1Z2luLWR0c1wiO1xuaW1wb3J0IG5vQnVuZGxlUGx1Z2luIGZyb20gXCJ2aXRlLXBsdWdpbi1uby1idW5kbGVcIjtcblxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZygoeyBtb2RlIH0pID0+IHtcbiAgY29uc3QgZW52ID0gbG9hZEVudihtb2RlLCBwcm9jZXNzLmN3ZCgpLCBcIlwiKTtcbiAgY29uc3QgQ0pTID0gZW52LkJVSUxEX1RBUkdFVD8udG9Mb2NhbGVMb3dlckNhc2UoKT8ubWF0Y2goXCJjanNcIik7XG4gIGNvbnN0IGZvcm1hdHM6IExpYnJhcnlGb3JtYXRzW10gPSBDSlMgPyBbXCJjanNcIl0gOiBbXCJlc1wiXTtcblxuICByZXR1cm4ge1xuICAgIHBsdWdpbnM6IFtcbiAgICAgIGR0cyh7XG4gICAgICAgIG91dERpcjogXCJkaXN0L3R5cGVzXCIsXG4gICAgICB9KSxcbiAgICAgIHJlYWN0KHtcbiAgICAgICAganN4UnVudGltZTogXCJjbGFzc2ljXCIsXG4gICAgICAgIGJhYmVsOiB7XG4gICAgICAgICAgcGx1Z2luczogW1wicmVhY3QtcmVxdWlyZVwiXSxcbiAgICAgICAgfSxcbiAgICAgIH0pLFxuICAgICAgbm9CdW5kbGVQbHVnaW4oeyByb290OiBcIi4vc3JjXCIgfSksXG4gICAgICBjb2RlY292Vml0ZVBsdWdpbih7XG4gICAgICAgIGVuYWJsZUJ1bmRsZUFuYWx5c2lzOiBwcm9jZXNzLmVudi5DT0RFQ09WX1RPS0VOICE9PSB1bmRlZmluZWQsXG4gICAgICAgIGJ1bmRsZU5hbWU6IFwiQGtub2NrbGFicy9yZWFjdC1jb3JlXCIsXG4gICAgICAgIHVwbG9hZFRva2VuOiBwcm9jZXNzLmVudi5DT0RFQ09WX1RPS0VOLFxuICAgICAgfSksXG4gICAgXSxcbiAgICBidWlsZDoge1xuICAgICAgb3V0RGlyOiBDSlMgPyBcImRpc3QvY2pzXCIgOiBcImRpc3QvZXNtXCIsXG4gICAgICBzb3VyY2VtYXA6IHRydWUsXG4gICAgICBsaWI6IHtcbiAgICAgICAgZW50cnk6IHJlc29sdmUoX19kaXJuYW1lLCBcInNyY1wiKSxcbiAgICAgICAgZmlsZU5hbWU6IGBbbmFtZV1gLFxuICAgICAgICBuYW1lOiBcInJlYWN0LWNvcmVcIixcbiAgICAgICAgZm9ybWF0cyxcbiAgICAgIH0sXG4gICAgICByb2xsdXBPcHRpb25zOiB7XG4gICAgICAgIC8vIEV4dGVybmFsIHBhY2thZ2VzIHRoYXQgc2hvdWxkIG5vdCBiZSBidW5kbGVkXG4gICAgICAgIGV4dGVybmFsOiBbXCJyZWFjdFwiXSxcbiAgICAgICAgb3V0cHV0OiB7XG4gICAgICAgICAgaW50ZXJvcDogXCJjb21wYXRcIixcbiAgICAgICAgICBnbG9iYWxzOiB7XG4gICAgICAgICAgICByZWFjdDogXCJSZWFjdFwiLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgZW50cnlGaWxlTmFtZXM6ICgpID0+IHtcbiAgICAgICAgICAgIHJldHVybiBgW25hbWVdLiR7Q0pTID8gXCJqc1wiIDogXCJtanNcIn1gO1xuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0sXG4gICAgdGVzdDoge1xuICAgICAgZ2xvYmFsOiB0cnVlLFxuICAgICAgZW52aXJvbm1lbnQ6IFwianNkb21cIixcbiAgICAgIHNldHVwRmlsZXM6IFwiLi9zZXR1cFRlc3QudHNcIixcbiAgICB9LFxuICB9O1xufSk7XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQ0EsU0FBUyx5QkFBeUI7QUFDbEMsT0FBTyxXQUFXO0FBQ2xCLFNBQVMsZUFBZTtBQUN4QixTQUF5QixjQUFjLGVBQWU7QUFDdEQsT0FBTyxTQUFTO0FBQ2hCLE9BQU8sb0JBQW9CO0FBTjNCLElBQU0sbUNBQW1DO0FBU3pDLElBQU8sc0JBQVEsYUFBYSxDQUFDLEVBQUUsS0FBSyxNQUFNO0FBQ3hDLFFBQU0sTUFBTSxRQUFRLE1BQU0sUUFBUSxJQUFJLEdBQUcsRUFBRTtBQUMzQyxRQUFNLE1BQU0sSUFBSSxjQUFjLGtCQUFrQixHQUFHLE1BQU0sS0FBSztBQUM5RCxRQUFNLFVBQTRCLE1BQU0sQ0FBQyxLQUFLLElBQUksQ0FBQyxJQUFJO0FBRXZELFNBQU87QUFBQSxJQUNMLFNBQVM7QUFBQSxNQUNQLElBQUk7QUFBQSxRQUNGLFFBQVE7QUFBQSxNQUNWLENBQUM7QUFBQSxNQUNELE1BQU07QUFBQSxRQUNKLFlBQVk7QUFBQSxRQUNaLE9BQU87QUFBQSxVQUNMLFNBQVMsQ0FBQyxlQUFlO0FBQUEsUUFDM0I7QUFBQSxNQUNGLENBQUM7QUFBQSxNQUNELGVBQWUsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUFBLE1BQ2hDLGtCQUFrQjtBQUFBLFFBQ2hCLHNCQUFzQixRQUFRLElBQUksa0JBQWtCO0FBQUEsUUFDcEQsWUFBWTtBQUFBLFFBQ1osYUFBYSxRQUFRLElBQUk7QUFBQSxNQUMzQixDQUFDO0FBQUEsSUFDSDtBQUFBLElBQ0EsT0FBTztBQUFBLE1BQ0wsUUFBUSxNQUFNLGFBQWE7QUFBQSxNQUMzQixXQUFXO0FBQUEsTUFDWCxLQUFLO0FBQUEsUUFDSCxPQUFPLFFBQVEsa0NBQVcsS0FBSztBQUFBLFFBQy9CLFVBQVU7QUFBQSxRQUNWLE1BQU07QUFBQSxRQUNOO0FBQUEsTUFDRjtBQUFBLE1BQ0EsZUFBZTtBQUFBO0FBQUEsUUFFYixVQUFVLENBQUMsT0FBTztBQUFBLFFBQ2xCLFFBQVE7QUFBQSxVQUNOLFNBQVM7QUFBQSxVQUNULFNBQVM7QUFBQSxZQUNQLE9BQU87QUFBQSxVQUNUO0FBQUEsVUFDQSxnQkFBZ0IsTUFBTTtBQUNwQixtQkFBTyxVQUFVLE1BQU0sT0FBTyxLQUFLO0FBQUEsVUFDckM7QUFBQSxRQUNGO0FBQUEsTUFDRjtBQUFBLElBQ0Y7QUFBQSxJQUNBLE1BQU07QUFBQSxNQUNKLFFBQVE7QUFBQSxNQUNSLGFBQWE7QUFBQSxNQUNiLFlBQVk7QUFBQSxJQUNkO0FBQUEsRUFDRjtBQUNGLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==

packages/react/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ export {
4343
Card,
4444
CardView,
4545
KnockGuideProvider,
46-
GuideToolbar as KnockGuideToolbar,
4746
Modal,
4847
ModalView,
4948
} from "./modules/guide";
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Button } from "@telegraph/button";
2+
3+
import { MAX_Z_INDEX } from "./shared";
4+
import "./styles.css";
5+
6+
type Props = {
7+
onClick: () => void;
8+
};
9+
10+
export const KnockButton = ({ onClick }: Props) => {
11+
return (
12+
<Button
13+
onClick={onClick}
14+
position="fixed"
15+
top="4"
16+
right="4"
17+
bg="surface-2"
18+
shadow="3"
19+
rounded="3"
20+
w="10"
21+
h="10"
22+
variant="soft"
23+
data-tgph-appearance="dark"
24+
aria-label="Expand guide toolbar"
25+
style={{ zIndex: MAX_Z_INDEX }}
26+
>
27+
<svg
28+
width="40"
29+
height="40"
30+
viewBox="0 0 40 40"
31+
fill="none"
32+
xmlns="http://www.w3.org/2000/svg"
33+
style={{
34+
position: "absolute",
35+
top: "50%",
36+
left: "50%",
37+
transform: "translate(-50%, -50%)",
38+
}}
39+
>
40+
<path
41+
d="M11.6001 32.4V7.59998H16.6365V21.8219H16.7774L22.3067 14.8525H27.9418L21.8138 22.0696L28.4001 32.4H22.7996L18.8555 25.572L16.6365 28.0839V32.4H11.6001Z"
42+
fill="#EDEEEF"
43+
/>
44+
<path
45+
d="M28.4 10.4C28.4 11.9464 27.1467 13.2 25.6 13.2C24.0534 13.2 22.8 11.9464 22.8 10.4C22.8 8.85358 24.0534 7.59998 25.6 7.59998C27.1467 7.59998 28.4 8.85358 28.4 10.4Z"
46+
fill="#FF573A"
47+
/>
48+
</svg>
49+
</Button>
50+
);
51+
};

0 commit comments

Comments
 (0)