Skip to content

Commit 47fb18f

Browse files
feat: add Guide dev tools component (#721)
### Description Adds a minimal Guides DevTool component for displaying debug info. Right now, this just shows the guide key that's selected and lets the user exit debug mode which clears the query param. ### Checklist - [x] Tests have been added for new features or major refactors to existing features. ### Screenshots or videos <img width="782" height="154" alt="CleanShot 2025-08-19 at 13 51 45@2x" src="https://github.com/user-attachments/assets/c638eb45-61a3-4d97-b8e0-f6c808c751e2" />
1 parent d4ba462 commit 47fb18f

File tree

17 files changed

+268
-7
lines changed

17 files changed

+268
-7
lines changed

packages/react-core/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ export {
2929
export {
3030
KnockGuideProvider,
3131
KnockGuideContext,
32+
type KnockGuideProviderProps,
3233
useGuide,
3334
useGuides,
35+
useGuideContext,
3436
} from "./modules/guide";
3537
export {
3638
type MsTeamsChannelQueryOptions,

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const KnockGuideContext = React.createContext<
1616
KnockGuideProviderValue | undefined
1717
>(undefined);
1818

19-
type Props = {
19+
export type KnockGuideProviderProps = {
2020
channelId: string;
2121
readyToTarget: boolean;
2222
listenForUpdates?: boolean;
@@ -27,7 +27,9 @@ type Props = {
2727
throttleCheckInterval?: number; // in milliseconds
2828
};
2929

30-
export const KnockGuideProvider: React.FC<React.PropsWithChildren<Props>> = ({
30+
export const KnockGuideProvider: React.FC<
31+
React.PropsWithChildren<KnockGuideProviderProps>
32+
> = ({
3133
channelId,
3234
readyToTarget,
3335
listenForUpdates = true,
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
export { KnockGuideProvider, KnockGuideContext } from "./KnockGuideProvider";
1+
export {
2+
KnockGuideProvider,
3+
KnockGuideContext,
4+
type KnockGuideProviderProps,
5+
} from "./KnockGuideProvider";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { useGuide } from "./useGuide";
22
export { useGuides } from "./useGuides";
3+
export { useGuideContext } from "./useGuideContext";
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1-
export { KnockGuideProvider, KnockGuideContext } from "./context";
2-
export { useGuide, useGuides } from "./hooks";
1+
export {
2+
KnockGuideProvider,
3+
KnockGuideContext,
4+
type KnockGuideProviderProps,
5+
} from "./context";
6+
export { useGuide, useGuides, useGuideContext } from "./hooks";

packages/react/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export {
4242
BannerView,
4343
Card,
4444
CardView,
45+
KnockGuideProvider,
4546
Modal,
4647
ModalView,
4748
} from "./modules/guide";
@@ -88,7 +89,6 @@ export {
8889
useKnockFeed,
8990
useNotificationStore,
9091
useNotifications,
91-
KnockGuideProvider,
9292
KnockGuideContext,
9393
useGuide,
9494
useGuides,

packages/react/src/modules/core/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export {
77
} from "./components/Icons";
88
export { Spinner, type SpinnerProps } from "./components/Spinner";
99
export { useOnBottomScroll } from "./hooks";
10+
export { checkForWindow } from "./utils";

packages/react/src/modules/core/utils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ export const openPopupWindow = (url: string) => {
2020

2121
window.open(url, "_blank", features);
2222
};
23+
24+
export const checkForWindow = () => {
25+
if (typeof window !== "undefined") {
26+
return window;
27+
}
28+
};
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { useGuideContext } from "@knocklabs/react-core";
2+
import { useStore } from "@tanstack/react-store";
3+
import { Button } from "@telegraph/button";
4+
import { Stack } from "@telegraph/layout";
5+
import { Tag } from "@telegraph/tag";
6+
import { Text } from "@telegraph/typography";
7+
import { Minimize2, Undo2, Wrench } from "lucide-react";
8+
import { useState } from "react";
9+
10+
import { checkForWindow } from "../../../core";
11+
12+
import "./styles.css";
13+
14+
export const GuideToolbar = () => {
15+
const [isCollapsed, setIsCollapsed] = useState(false);
16+
17+
const { client } = useGuideContext();
18+
const debugState = useStore(client.store, (state) => state.debug);
19+
20+
if (!debugState?.forcedGuideKey) {
21+
return null;
22+
}
23+
24+
const handleExit = () => {
25+
const window = checkForWindow();
26+
if (!window) {
27+
return;
28+
}
29+
30+
const url = new URL(window.location.href);
31+
url.searchParams.delete("knock_guide_key");
32+
window.location.href = url.toString();
33+
};
34+
35+
const handleToggleCollapse = () => {
36+
setIsCollapsed(!isCollapsed);
37+
};
38+
39+
if (isCollapsed) {
40+
return (
41+
<Button
42+
onClick={handleToggleCollapse}
43+
position="fixed"
44+
top="4"
45+
right="4"
46+
zIndex="sticky"
47+
bg="surface-2"
48+
shadow="3"
49+
rounded="3"
50+
w="10"
51+
h="10"
52+
variant="soft"
53+
data-tgph-appearance="dark"
54+
aria-label="Expand guide toolbar"
55+
>
56+
<svg
57+
width="40"
58+
height="40"
59+
viewBox="0 0 40 40"
60+
fill="none"
61+
xmlns="http://www.w3.org/2000/svg"
62+
style={{
63+
position: "absolute",
64+
top: "50%",
65+
left: "50%",
66+
transform: "translate(-50%, -50%)",
67+
}}
68+
>
69+
<path
70+
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"
71+
fill="#EDEEEF"
72+
/>
73+
<path
74+
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"
75+
fill="#FF573A"
76+
/>
77+
</svg>
78+
</Button>
79+
);
80+
}
81+
82+
return (
83+
<Stack
84+
gap="2"
85+
align="center"
86+
position="fixed"
87+
top="4"
88+
right="4"
89+
zIndex="sticky"
90+
backgroundColor="surface-2"
91+
bg="surface-2"
92+
shadow="3"
93+
rounded="3"
94+
py="2"
95+
px="3"
96+
data-tgph-appearance="dark"
97+
>
98+
<Stack gap="2" align="center" direction="row">
99+
<Tag
100+
color="green"
101+
variant="soft"
102+
icon={{ icon: Wrench, "aria-hidden": true }}
103+
>
104+
Debug
105+
</Tag>
106+
107+
<Text
108+
as="div"
109+
size="1"
110+
weight="medium"
111+
w="full"
112+
maxWidth="40"
113+
style={{
114+
overflow: "hidden",
115+
textOverflow: "ellipsis",
116+
whiteSpace: "nowrap",
117+
}}
118+
>
119+
{debugState.forcedGuideKey}
120+
</Text>
121+
122+
<Button
123+
onClick={handleExit}
124+
size="1"
125+
variant="soft"
126+
trailingIcon={{ icon: Undo2, "aria-hidden": true }}
127+
>
128+
Exit
129+
</Button>
130+
131+
<Button
132+
onClick={handleToggleCollapse}
133+
size="1"
134+
variant="soft"
135+
leadingIcon={{ icon: Minimize2, alt: "Collapse guide toolbar" }}
136+
/>
137+
</Stack>
138+
</Stack>
139+
);
140+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { GuideToolbar } from "./GuideToolbar";

0 commit comments

Comments
 (0)