Skip to content

Commit 68d94b7

Browse files
authored
feat: add ignoreDisplayInterval debug setting to guide toolbar (#878)
1 parent 84357ad commit 68d94b7

5 files changed

Lines changed: 134 additions & 7 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@ export class KnockGuideClient {
590590
...state,
591591
debug: {
592592
skipEngagementTracking: true,
593+
ignoreDisplayInterval: true,
593594
...debugOpts,
594595
debugging: true,
595596
},

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ export const findDefaultGroup = (guideGroups: GuideGroupData[]) =>
6464
);
6565

6666
export const checkStateIfThrottled = (state: StoreState) => {
67+
if (state.debug?.ignoreDisplayInterval) {
68+
return false;
69+
}
70+
6771
const defaultGroup = findDefaultGroup(state.guideGroups);
6872
const throttleWindowStartedAt =
6973
state.guideGroupDisplayLogs[DEFAULT_GROUP_KEY];

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ export type DebugState = {
234234
forcedGuideKey?: string | null;
235235
previewSessionId?: string | null;
236236
skipEngagementTracking?: boolean;
237+
ignoreDisplayInterval?: boolean;
237238
};
238239

239240
export type StoreState = {

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

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2363,6 +2363,71 @@ describe("KnockGuideClient", () => {
23632363
expect(result2!.type).toBe("banner");
23642364
});
23652365

2366+
test("returns a guide inside a throttle window when ignoreDisplayInterval is true", () => {
2367+
const stateWithGuides = {
2368+
guideGroups: [
2369+
{
2370+
...mockDefaultGroup,
2371+
display_interval: 5 * 60, // 5 minutes
2372+
},
2373+
],
2374+
guideGroupDisplayLogs: {
2375+
default: new Date().toISOString(),
2376+
},
2377+
guides: mockGuides,
2378+
ineligibleGuides: {},
2379+
previewGuides: {},
2380+
queries: {},
2381+
location: undefined,
2382+
counter: 0,
2383+
debug: {
2384+
debugging: true,
2385+
ignoreDisplayInterval: true,
2386+
forcedGuideKey: null,
2387+
previewSessionId: null,
2388+
},
2389+
};
2390+
2391+
const client = new KnockGuideClient(mockKnock, channelId);
2392+
const result = client["_selectGuide"](stateWithGuides);
2393+
2394+
// Even though we are inside the configured throttle window,
2395+
// ignoreDisplayInterval bypasses it.
2396+
expect(result).toBeDefined();
2397+
expect(result!.key).toBe("feature_tour");
2398+
});
2399+
2400+
test("does not return a guide inside a throttle window when ignoreDisplayInterval is false", () => {
2401+
const stateWithGuides = {
2402+
guideGroups: [
2403+
{
2404+
...mockDefaultGroup,
2405+
display_interval: 5 * 60, // 5 minutes
2406+
},
2407+
],
2408+
guideGroupDisplayLogs: {
2409+
default: new Date().toISOString(),
2410+
},
2411+
guides: mockGuides,
2412+
ineligibleGuides: {},
2413+
previewGuides: {},
2414+
queries: {},
2415+
location: undefined,
2416+
counter: 0,
2417+
debug: {
2418+
debugging: true,
2419+
ignoreDisplayInterval: false,
2420+
forcedGuideKey: null,
2421+
previewSessionId: null,
2422+
},
2423+
};
2424+
2425+
const client = new KnockGuideClient(mockKnock, channelId);
2426+
const result = client["_selectGuide"](stateWithGuides);
2427+
2428+
expect(result).toBeUndefined();
2429+
});
2430+
23662431
test("skips ineligible guides during selection", () => {
23672432
const stateWithGuides = {
23682433
guideGroups: [mockDefaultGroup],
@@ -4234,6 +4299,34 @@ describe("KnockGuideClient", () => {
42344299

42354300
expect(client.store.state.debug!.skipEngagementTracking).toBe(false);
42364301
});
4302+
4303+
test("defaults ignoreDisplayInterval to true", () => {
4304+
const client = new KnockGuideClient(mockKnock, channelId);
4305+
client.store.state.debug = undefined;
4306+
4307+
vi.spyOn(client, "fetch").mockImplementation(() =>
4308+
Promise.resolve({ status: "ok" }),
4309+
);
4310+
vi.spyOn(client, "subscribe").mockImplementation(() => {});
4311+
4312+
client.setDebug();
4313+
4314+
expect(client.store.state.debug!.ignoreDisplayInterval).toBe(true);
4315+
});
4316+
4317+
test("allows overriding ignoreDisplayInterval to false", () => {
4318+
const client = new KnockGuideClient(mockKnock, channelId);
4319+
client.store.state.debug = undefined;
4320+
4321+
vi.spyOn(client, "fetch").mockImplementation(() =>
4322+
Promise.resolve({ status: "ok" }),
4323+
);
4324+
vi.spyOn(client, "subscribe").mockImplementation(() => {});
4325+
4326+
client.setDebug({ ignoreDisplayInterval: false });
4327+
4328+
expect(client.store.state.debug!.ignoreDisplayInterval).toBe(false);
4329+
});
42374330
});
42384331

42394332
describe("unsetDebug", () => {

packages/react/src/modules/guide/components/Toolbar/V2/GuideContextDetails.tsx

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const GuideContextDetails = () => {
1616
defaultGroup: state.guideGroups[0],
1717
debugSettings: {
1818
skipEngagementTracking: !!state.debug?.skipEngagementTracking,
19+
ignoreDisplayInterval: !!state.debug?.ignoreDisplayInterval,
1920
},
2021
};
2122
});
@@ -70,13 +71,40 @@ export const GuideContextDetails = () => {
7071
</Button>
7172
</Stack>
7273

73-
<Stack direction="column" gap="0_5" py="1" px="2" borderTop="px">
74-
<Text as="span" size="0" weight="medium">
75-
Throttle
76-
</Text>
77-
<Text as="code" size="0">
78-
{displayInterval === null ? "-" : `Every ${displayInterval}s`}
79-
</Text>
74+
<Stack direction="column" py="1" px="2">
75+
<Stack align="center" justify="space-between">
76+
<Stack align="center" gap="1">
77+
<Text as="span" size="0" weight="medium">
78+
Suspend throttling
79+
</Text>
80+
<Tooltip label="Suspend throttling during preview, and show next guide immediately">
81+
<Icon icon={Info} size="0" color="gray" aria-hidden />
82+
</Tooltip>
83+
</Stack>
84+
<Button
85+
size="0"
86+
variant="soft"
87+
color={debugSettings.ignoreDisplayInterval ? "green" : "gray"}
88+
onClick={() =>
89+
client.setDebug({
90+
...debugSettings,
91+
ignoreDisplayInterval: !debugSettings.ignoreDisplayInterval,
92+
})
93+
}
94+
>
95+
{debugSettings.ignoreDisplayInterval ? "On" : "Off"}
96+
</Button>
97+
</Stack>
98+
<Stack direction="row" gap="0_5" py="1">
99+
<Text as="span" size="0" color="gray">
100+
Throttle:{" "}
101+
{debugSettings.ignoreDisplayInterval
102+
? "(ignored)"
103+
: displayInterval === null
104+
? "(none)"
105+
: `Every ${displayInterval}s`}
106+
</Text>
107+
</Stack>
80108
</Stack>
81109

82110
<Stack direction="column" py="1" px="2" borderTop="px">

0 commit comments

Comments
 (0)