Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions code/core/src/core-server/server-channel/telemetry-channel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { SIDEBAR_FILTER_CHANGED } from 'storybook/internal/core-events';

import { REVIEW_EVENTS } from '../../shared/review/events.ts';
import { initTelemetryChannel, makePayload } from './telemetry-channel.ts';

vi.mock('storybook/internal/telemetry', () => ({
Expand Down Expand Up @@ -42,6 +43,46 @@ describe('telemetry-channel', () => {
expect(telemetry).toHaveBeenCalledWith('sidebar-filter', payload);
});
});

describe('REVIEW_EVENTS.PAGEVIEW', () => {
it('forwards review summary pageview to telemetry', () => {
const listeners: Record<string, Function> = {};
const channel = {
on: (event: string, listener: Function) => {
listeners[event] = listener;
},
} as any;

initTelemetryChannel(channel);

listeners[REVIEW_EVENTS.PAGEVIEW]({ page: 'summary', reviewCreatedAt: 1700000000000 });
expect(telemetry).toHaveBeenCalledWith('review', {
action: 'pageview',
source: 'mcp-review',
page: 'summary',
reviewCreatedAt: 1700000000000,
});
Comment thread
yannbf marked this conversation as resolved.
});

it('forwards review detail pageview to telemetry', () => {
const listeners: Record<string, Function> = {};
const channel = {
on: (event: string, listener: Function) => {
listeners[event] = listener;
},
} as any;

initTelemetryChannel(channel);

listeners[REVIEW_EVENTS.PAGEVIEW]({ page: 'detail', reviewCreatedAt: 1700000000000 });
expect(telemetry).toHaveBeenCalledWith('review', {
action: 'pageview',
source: 'mcp-review',
page: 'detail',
reviewCreatedAt: 1700000000000,
});
});
});
});

describe('makePayload', () => {
Expand Down
19 changes: 15 additions & 4 deletions code/core/src/core-server/server-channel/telemetry-channel.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import type { Channel } from 'storybook/internal/channels';
import {
AI_PROMPT_NUDGE,
PREVIEW_INITIALIZED,
SHARE_ISOLATE_MODE,
SIDEBAR_FILTER_CHANGED,
AI_PROMPT_NUDGE,
} from 'storybook/internal/core-events';
import { type InitPayload, telemetry } from 'storybook/internal/telemetry';
import { type CacheEntry, getLastEvents } from 'storybook/internal/telemetry';
import { getSessionId } from 'storybook/internal/telemetry';
import {
type CacheEntry,
getLastEvents,
getSessionId,
type InitPayload,
telemetry,
} from 'storybook/internal/telemetry';

import { REVIEW_EVENTS, type ReviewPageviewPayload } from '../../shared/review/events.ts';

export const makePayload = (
userAgent: string,
Expand Down Expand Up @@ -50,4 +56,9 @@ export function initTelemetryChannel(channel: Channel) {
channel.on(AI_PROMPT_NUDGE, async ({ id, origin }: { id: string; origin: string }) => {
telemetry('ai-prompt-nudge', { id, origin });
});
channel.on(REVIEW_EVENTS.PAGEVIEW, ({ page, reviewCreatedAt }: ReviewPageviewPayload) => {
// Reviews are only produced by the MCP display-review tool today; other
// producers should report their own `source` under the same event.
telemetry('review', { action: 'pageview', source: 'mcp-review', page, reviewCreatedAt });
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export const ReviewProvider: FC<{ children: ReactNode }> = ({ children }) => {
const previousReviewStoryIdsRef = useRef<Set<string>>(new Set());
const displayedReviewRef = useRef<ReviewState | null>(null);
displayedReviewRef.current = state;
// Last review page reported to telemetry; dedupes pageviews across re-renders.
const lastPageviewKeyRef = useRef<string | null>(null);

const api = useStorybookApi();
const navigate = useNavigate();
Expand Down Expand Up @@ -194,6 +196,30 @@ export const ReviewProvider: FC<{ children: ReactNode }> = ({ children }) => {

const isSummaryVisible = isReviewSummaryPath(path);

// Report a "pageview" whenever the active review surface changes: the summary
// overlay, or a specific reviewed story's detail view. Keyed so re-renders that
// don't change the surface (or story) don't re-fire.
useEffect(() => {
if (!state) {
lastPageviewKeyRef.current = null;
return;
}
let page: 'summary' | 'detail' | null = null;
let key: string | null = null;
if (isSummaryVisible) {
page = 'summary';
key = 'summary';
} else if (isInReviewMode && activeEntry) {
page = 'detail';
key = `detail:${activeEntry.storyId}`;
}
if (!page || key === lastPageviewKeyRef.current) {
return;
}
lastPageviewKeyRef.current = key;
emit(EVENTS.PAGEVIEW, { page, reviewCreatedAt: state.createdAt });
}, [state, isSummaryVisible, isInReviewMode, activeEntry, emit]);

// Re-sync the persisted review-mode flag on every navigation. Enter/exit
// performed by the nav interceptor and shortcuts toggle it out of band before
// navigating, so a route change is the signal to re-read it.
Expand Down
13 changes: 13 additions & 0 deletions code/core/src/shared/review/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,17 @@ export const REVIEW_EVENTS = {
DISMISS_REVIEW: `${REVIEW_NAMESPACE}/dismiss-review`,
// core-server → tabs: the review was dismissed.
REVIEW_DISMISSED: `${REVIEW_NAMESPACE}/review-dismissed`,
// tab → core-server: a review page (summary or detail) was viewed; forwarded to telemetry.
PAGEVIEW: `${REVIEW_NAMESPACE}/pageview`,
} as const;

/** Page identifiers reported by the `PAGEVIEW` event. */
export type ReviewPage = 'summary' | 'detail';

/** Payload of the `PAGEVIEW` event. */
export interface ReviewPageviewPayload {
/** Which review surface was viewed. */
page: ReviewPage;
/** The viewed review's server `createdAt`, correlating pageviews to a review. */
reviewCreatedAt?: number;
}
1 change: 1 addition & 0 deletions code/core/src/shared/review/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { REVIEW_EVENTS, REVIEW_NAMESPACE } from './events.ts';
export type { ReviewPage, ReviewPageviewPayload } from './events.ts';
export type { ReviewCollection, ReviewState } from './review-state.ts';
export {
REVIEW_COLLECTION_QUERY_PARAM,
Expand Down
3 changes: 2 additions & 1 deletion code/core/src/telemetry/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import type { StorybookConfig, TypescriptOptions } from 'storybook/internal/type

import type { DetectResult } from 'package-manager-detector';

import type { MonorepoType } from '../shared/utils/get-monorepo-type.ts';
import type { AgentInfo } from './detect-agent.ts';
import type { KnownPackagesList } from './get-known-packages.ts';
import type { MonorepoType } from '../shared/utils/get-monorepo-type.ts';

export type EventType =
| 'boot'
Expand Down Expand Up @@ -43,6 +43,7 @@ export type EventType =
| 'migrate'
| 'preview-first-load'
| 'doctor'
| 'review'
| 'share'
| 'ghost-stories'
| 'sidebar-filter'
Expand Down
Loading