Skip to content

Commit 02e33f8

Browse files
committed
Fix preview-mode walkthrough after recent dashboard refactors
The 7-step walkthrough tour shown in preview mode (NEXT_PUBLIC_STACK_IS_PREVIEW) had several silently-skipping steps after recent dashboard changes: - Overview revamp (#1238) dropped the data-walkthrough hooks for the globe and metrics widgets — re-attach them to the new GlobeSectionWithData column and the AnalyticsChartWidget column in metrics-page.tsx. - Session-replays/data-grid PR (#1424) renamed the anchor to "analytics-replays" and the sidebar nav was relabeled from "Session Replays" to "Replays" (under a collapsed Analytics group) — update the step id and sidebarNavLabel. - The sidebar's collapsible-group expand button is now a <button aria-expanded> *nested inside* the prev-sibling header div, not the prev sibling itself — navigateViaSidebar's expansion logic only matched the prevSibling directly, so step 7 silently skipped. Look inside prevSibling for the chevron too. - The cmdkSearch strings "Authentication" / "Emails" / "Payments" were matching the wrong CmdK result first (auth-methods / email-themes / payments/products/new) — tighten them to "Users" / "Emails sent" / "Products" so they land on the pages that actually have the spotlight anchors. - Loop the walkthrough back to step 1 after step 7 instead of stopping; add sidebarNavLabel "Overview" to step 1 so the loop can navigate back from /session-replays. Verified end-to-end in a real browser: all 7 spotlights render and the loop continues indefinitely across iterations.
1 parent f0cc9b7 commit 02e33f8

3 files changed

Lines changed: 83 additions & 76 deletions

File tree

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,7 @@ function MetricsContent({
12341234
)}
12351235
>
12361236
{shouldShowGlobe && (
1237-
<div className={cn(
1237+
<div data-walkthrough="overview-globe" className={cn(
12381238
"hidden lg:flex lg:col-span-5 h-full relative items-center justify-center overflow-hidden",
12391239
"rounded-2xl bg-white/90 backdrop-blur-xl ring-1 ring-black/[0.06] shadow-sm",
12401240
"dark:bg-transparent dark:backdrop-blur-none dark:ring-0 dark:shadow-none dark:rounded-none",
@@ -1256,7 +1256,7 @@ function MetricsContent({
12561256
</div>
12571257
)}
12581258

1259-
<div className={cn(
1259+
<div data-walkthrough="overview-metrics" className={cn(
12601260
"h-full",
12611261
shouldShowGlobe ? "lg:col-span-7" : "lg:col-span-12",
12621262
)}>

apps/dashboard/src/components/walkthrough/walkthrough-provider.tsx

Lines changed: 75 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,17 @@ function WalkthroughEngine() {
240240
let el = link.parentElement;
241241
while (el && el.tagName !== 'ASIDE') {
242242
const prevSibling = el.previousElementSibling;
243-
if (prevSibling?.tagName === 'BUTTON' && prevSibling.getAttribute('aria-expanded') === 'false') {
244-
(prevSibling as HTMLElement).click();
245-
break;
243+
if (prevSibling) {
244+
// The expand button may be the prevSibling itself, or nested
245+
// inside it (the sidebar wraps the chevron toggle in a header
246+
// div that also contains the section's main <a>).
247+
const expandButton = (prevSibling.tagName === 'BUTTON' && prevSibling.getAttribute('aria-expanded') === 'false')
248+
? prevSibling as HTMLElement
249+
: prevSibling.querySelector('button[aria-expanded="false"]') as HTMLElement | null;
250+
if (expandButton) {
251+
expandButton.click();
252+
break;
253+
}
246254
}
247255
el = el.parentElement;
248256
}
@@ -283,80 +291,78 @@ function WalkthroughEngine() {
283291
};
284292

285293
const runWalkthrough = async () => {
286-
for (let i = 0; i < WALKTHROUGH_STEPS.length; i++) {
287-
if (isCancelled()) return;
288-
289-
const step = WALKTHROUGH_STEPS[i];
290-
setStepIndex(i);
291-
setShowSpotlight(false);
292-
293-
const needsNavigation = currentPathRef.current !== step.path;
294-
295-
// Phase 1: Navigate if needed
296-
if (needsNavigation) {
297-
let success = false;
298-
if (step.cmdkSearch) {
299-
success = await navigateViaCmdK(step.cmdkSearch);
300-
} else if (step.sidebarNavLabel) {
301-
success = await navigateViaSidebar(step.sidebarNavLabel);
302-
}
294+
while (!isCancelled()) {
295+
for (let i = 0; i < WALKTHROUGH_STEPS.length; i++) {
303296
if (isCancelled()) return;
304-
if (success) {
305-
currentPathRef.current = step.path;
306-
await sleep(300);
307-
if (isCancelled()) return;
308-
}
309-
}
310297

311-
// Phase 2: Wait for target element
312-
const targetEl = await waitForElement(`[data-walkthrough="${step.id}"]`);
313-
if (isCancelled()) return;
314-
if (!targetEl) continue;
298+
const step = WALKTHROUGH_STEPS[i];
299+
setStepIndex(i);
300+
setShowSpotlight(false);
315301

316-
// Phase 3: Animate mouse to target element
317-
const targetRect = targetEl.getBoundingClientRect();
318-
setCursorPosition({
319-
x: targetRect.left + targetRect.width / 2,
320-
y: targetRect.top + targetRect.height / 2,
321-
});
322-
await sleep(600);
323-
if (isCancelled()) return;
324-
325-
// Phase 4: Show spotlight
326-
setSpotlightRect({
327-
top: targetRect.top,
328-
left: targetRect.left,
329-
width: targetRect.width,
330-
height: targetRect.height,
331-
});
332-
setShowSpotlight(true);
302+
const needsNavigation = currentPathRef.current !== step.path;
333303

334-
// Continuously track element position
335-
const trackElement = () => {
336-
if (isCancelled()) return;
337-
const el = document.querySelector(`[data-walkthrough="${step.id}"]`);
338-
if (el) {
339-
const rect = el.getBoundingClientRect();
340-
setSpotlightRect({
341-
top: rect.top,
342-
left: rect.left,
343-
width: rect.width,
344-
height: rect.height,
345-
});
304+
// Phase 1: Navigate if needed
305+
if (needsNavigation) {
306+
let success = false;
307+
if (step.cmdkSearch) {
308+
success = await navigateViaCmdK(step.cmdkSearch);
309+
} else if (step.sidebarNavLabel) {
310+
success = await navigateViaSidebar(step.sidebarNavLabel);
311+
}
312+
if (isCancelled()) return;
313+
if (success) {
314+
currentPathRef.current = step.path;
315+
await sleep(300);
316+
if (isCancelled()) return;
317+
}
346318
}
319+
320+
// Phase 2: Wait for target element
321+
const targetEl = await waitForElement(`[data-walkthrough="${step.id}"]`);
322+
if (isCancelled()) return;
323+
if (!targetEl) continue;
324+
325+
// Phase 3: Animate mouse to target element
326+
const targetRect = targetEl.getBoundingClientRect();
327+
setCursorPosition({
328+
x: targetRect.left + targetRect.width / 2,
329+
y: targetRect.top + targetRect.height / 2,
330+
});
331+
await sleep(600);
332+
if (isCancelled()) return;
333+
334+
// Phase 4: Show spotlight
335+
setSpotlightRect({
336+
top: targetRect.top,
337+
left: targetRect.left,
338+
width: targetRect.width,
339+
height: targetRect.height,
340+
});
341+
setShowSpotlight(true);
342+
343+
// Continuously track element position
344+
const trackElement = () => {
345+
if (isCancelled()) return;
346+
const el = document.querySelector(`[data-walkthrough="${step.id}"]`);
347+
if (el) {
348+
const rect = el.getBoundingClientRect();
349+
setSpotlightRect({
350+
top: rect.top,
351+
left: rect.left,
352+
width: rect.width,
353+
height: rect.height,
354+
});
355+
}
356+
rafRef.current = requestAnimationFrame(trackElement);
357+
};
347358
rafRef.current = requestAnimationFrame(trackElement);
348-
};
349-
rafRef.current = requestAnimationFrame(trackElement);
350359

351-
// Phase 5: Wait at this step
352-
await sleep(8000);
353-
cancelAnimationFrame(rafRef.current);
354-
if (isCancelled()) return;
360+
// Phase 5: Wait at this step
361+
await sleep(8000);
362+
cancelAnimationFrame(rafRef.current);
363+
if (isCancelled()) return;
364+
}
355365
}
356-
357-
// Walkthrough complete
358-
setShowSpotlight(false);
359-
setIsRunning(false);
360366
};
361367

362368
// eslint-disable-next-line @typescript-eslint/no-floating-promises

apps/dashboard/src/components/walkthrough/walkthrough-steps.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const WALKTHROUGH_STEPS: WalkthroughStep[] = [
1919
{
2020
id: 'overview-globe',
2121
path: '/',
22+
sidebarNavLabel: 'Overview',
2223
title: 'Global User Map',
2324
description: 'See where your users are around the world.',
2425
spotlightPadding: 12,
@@ -33,7 +34,7 @@ export const WALKTHROUGH_STEPS: WalkthroughStep[] = [
3334
{
3435
id: 'users-table',
3536
path: '/users',
36-
cmdkSearch: 'Authentication',
37+
cmdkSearch: 'Users',
3738
title: 'User Management',
3839
description: 'Manage all your users — search, export, or create new ones.',
3940
},
@@ -47,21 +48,21 @@ export const WALKTHROUGH_STEPS: WalkthroughStep[] = [
4748
{
4849
id: 'emails-sent',
4950
path: '/email-sent',
50-
cmdkSearch: 'Emails',
51+
cmdkSearch: 'Emails sent',
5152
title: 'Email Logs',
5253
description: 'Monitor sent emails, delivery status, and domain reputation.',
5354
},
5455
{
5556
id: 'payments-products',
5657
path: '/payments/products',
57-
cmdkSearch: 'Payments',
58+
cmdkSearch: 'Products',
5859
title: 'Products & Pricing',
5960
description: 'Define products, pricing, and subscriptions.',
6061
},
6162
{
62-
id: 'session-replays',
63+
id: 'analytics-replays',
6364
path: '/session-replays',
64-
sidebarNavLabel: 'Session Replays',
65+
sidebarNavLabel: 'Replays',
6566
title: 'Session Replays',
6667
description: 'Watch real user sessions to understand how people use your app.',
6768
},

0 commit comments

Comments
 (0)