Skip to content

Conversation

@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Oct 22, 2025

Closes #32644

What I did

Warning

The docs site must be redeployed after merging this PR, but before it is published in a release. Message @kylegach with any questions.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

  1. Initialize/upgrade with one of three variants:
    • npx storybook@<canary> init with onboarding (when prompted)
    • npx storybook@<canary> init without onboarding
    • npx storybook@<canary> upgrade (without onboarding)
  2. Start Storybook and (if included) run through the regular onboarding flow. Don't submit the survey (with onboarding only).
  3. In the sidebar, there should be a "Onboarding checklist" widget with (at most) 3 items. Try to complete these 3 items, as well as the next 3. Confirm you cannot undo the onboarding survey once submitted (with onboarding only).
  4. Open the "Onboarding guide" via the ⚙️ settings menu. The top items from each category should be completed now.
  5. At the bottom of the page there's a link to hide the checklist in the sidebar. Toggle it off and on to confirm the sidebar widget disappears and shows up again (with a 2s delay).
  6. Skip the remaining items (unless you want to test more) and confirm the sidebar widget disappears when everything is done.
  7. Open ~/.storybook/settings.json and verify it contains muted, accepted and skipped items. Remove the checklist property altogether and restart Storybook.
  8. Confirm the checklist has been reset, with the exception of some auto-completed items like "Render a component".
  9. Open node_modules/.cache/storybook/default/storybook-* (as JSON) and confirm it contains done items. Delete the cache file and restart Storybook.
  10. Confirm the checklist is fully reset now, including auto-completed items (though some may immediately auto-complete again).

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This pull request has been released as version 0.0.0-pr-32795-sha-2ff8d295. Try it out in a new sandbox by running npx [email protected] sandbox or in an existing project with npx [email protected] upgrade.

More information
Published version 0.0.0-pr-32795-sha-2ff8d295
Triggered by @ghengeveld
Repository storybookjs/storybook
Branch onboarding-checklist
Commit 2ff8d295
Datetime Fri Nov 21 07:45:39 UTC 2025 (1763711139)
Workflow run 19563645810

To request a new release of this pull request, mention the @storybookjs/core team.

core team members can create a new canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=32795

Summary by CodeRabbit

  • New Features

    • Many new UI components and Storybook stories (Card, Collapsible, Listbox, Optional, Focus, TextFlip, Particles, TourGuide, HighlightElement) plus a full Checklist UI, Checklist widget, and onboarding survey flow.
  • Improvements

    • Revamped onboarding/tours and checklist persistence with progress indicators, telemetry events, menu/sidebar refinements, Testing widget in sidebar, Button readOnly mode, modal header close support, and various layout/z-index tweaks.
  • Bug Fixes

    • Stricter viewMode URL handling, improved e2e test robustness, safer save guard, and other reliability fixes.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 22, 2025

📝 Walkthrough

Walkthrough

Adds an onboarding/checklist subsystem: settings schema, universal checklist store, server initializer, manager/store exports, client hook and UI (widget, guide, checklist), onboarding addon/survey, TourGuide/highlight primitives, many new components/stories, and Sidebar/Menu refactors plus assorted small UI/signature tweaks.

Changes

Cohort / File(s) Summary
Settings schema
code/core/src/cli/globalSettings.ts
Add strict statusValue and optional checklist schema; expose checklist in Settings.value; add invariant guard to Settings.save.
Universal checklist store & data
code/core/src/shared/checklist-store/*, .../checklistData.*, .../checklistData.state.ts
New store types, UNIVERSAL_CHECKLIST_STORE_OPTIONS, createChecklistStore, initialState, and comprehensive checklistData (sections, items, subscriptions, actions).
Server initializer & preset
code/core/src/core-server/utils/checklist.ts, code/core/src/core-server/presets/common-preset.ts
Implement initializeChecklist() to load/merge/persist checklist state, subscribe and emit telemetry; call from server preset.
Manager/store exports & packaging
code/core/src/manager-api/stores/checklist.ts, code/core/src/manager-api/index.ts, code/core/src/manager/manager-stores*.ts, code/core/src/manager/globals/exports.ts, code/core/build-config.ts, code/core/package.json
Add universalChecklistStore and checklistStore (internal_*), update build entries and package exports for manager-stores.
Client checklist logic & hook
code/core/src/manager/components/sidebar/useChecklist.ts
New useChecklist hook: flatten items, compute availability/locking/progress/nextItems, subscribe to per-item events, expose accept/done/skip/mute/disable actions.
Checklist UI & integration
code/core/src/manager/components/sidebar/ChecklistWidget.tsx, code/core/src/manager/settings/Checklist/Checklist.tsx, code/core/src/manager/settings/Checklist/*
Add ChecklistWidget and Checklist components, stories, transitions, progress UI; wire actions to checklistStore/useChecklist.
GuidePage & settings integration
code/core/src/manager/settings/GuidePage.tsx, code/core/src/manager/settings/index.tsx, code/core/src/manager/settings/whats_new.tsx
Add GuidePage rendering Checklist in DEV, replace dev WhatsNew tab with Guide, and minor styling tweaks.
Onboarding addon & survey
code/addons/onboarding/src/*, .storybook/main.ts, addons/onboarding/example-stories/*
Rework onboarding: ADDON_ID/ADDON_ONBOARDING_CHANNEL, Survey component, TourGuide-based flows, telemetry/dismissal changes, example stories, addon registration.
TourGuide / Highlight primitives
code/core/src/manager/components/TourGuide/*
Add TourGuide, HighlightElement, TourTooltip updates and stories; include TourGuide.render helper.
Manager UI primitives
code/core/src/manager/components/{TextFlip,Optional,Particles,Focus}/*
Add TextFlip, Optional, Particles, and Focus components with stories.
Core components & stories
code/core/src/components/components/{Card,Collapsible,Listbox}/*, code/core/src/components/index.ts
Add Card (outline animations), Collapsible (with hook), Listbox components and stories; export from components index.
Checklist store wiring (manager & mocks)
code/core/src/manager/manager-stores.mock.ts, code/core/src/manager/manager-stores.ts, code/core/src/manager-api/stores/__mocks__/checklist.ts
Add internal_universalChecklistStore and internal_checklistStore mocks/exports for manager tests and stories.
Menu & Sidebar refactor
code/core/src/manager/container/Menu.tsx, code/core/src/manager/container/Sidebar.tsx, code/core/src/manager/components/sidebar/Menu.tsx, stories
Change useMenu to object param, switch menu rendering to Listbox/MenuItem shape, add onboarding guide/docs item; update story mocks to provide ManagerContext and checklist store.
Testing widget & sidebar placement
code/core/src/manager/components/sidebar/TestingWidget.tsx, .../TestingWidget.stories.tsx, SidebarBottom.tsx
Replace TestingModule with new TestingWidget, update stories and SidebarBottom usage.
Preview/URL & tests
code/core/src/preview-api/modules/preview-web/{UrlStore.ts,UrlStore.test.ts}, code/e2e-tests/manager.spec.ts, code/vitest-setup.ts, code/addons/vitest/src/*
Tighten viewMode validation (invalid non-empty → null) and tests; e2e adjustments for fullscreen/menu flows; vitest ignore predicate and test-channel wiring.
Manager API updates
code/core/src/manager-api/modules/{stories.ts,versions.ts}, code/core/src/manager-api/tests/versions.test.js
Add getIndex() to stories SubAPI; extend getDocsUrl with asset option and tests.
Misc UI & small refactors
assorted files (Modal.Header onClose, ScrollArea zIndex, layout PagesContainer flex, Tree isMain removal, TreeNode styling, ts-expect-error removals, Story updates)
Multiple small style/prop/signature tweaks and removal of ts-expect-error comments.
Removed legacy onboarding bits
code/addons/onboarding/src/features/GuidedTour/*, code/addons/onboarding/src/components/{HighlightElement,Button}/*, related stories
Remove legacy onboarding GuidedTour, HighlightElement, Button and their stories (replaced by core TourGuide/HighlightElement and new flows).

Sequence Diagram(s)

sequenceDiagram
    participant Server
    participant UniversalStore as UniversalChecklistStore
    participant Manager
    participant Widget as ChecklistWidget/GuidePage

    Server->>UniversalStore: initializeChecklist()
    UniversalStore->>UniversalStore: load user & project caches, merge, mark loaded
    UniversalStore->>Server: persist project/user updates & emit telemetry
    Manager->>UniversalStore: subscribe to changes
    Widget->>UniversalStore: dispatch accept/done/skip/mute/disable
    UniversalStore->>Manager: notify subscribers
    Manager->>Widget: UI re-renders with updated state
Loading
sequenceDiagram
    participant User
    participant Sidebar
    participant Menu
    participant Hook as useChecklist
    participant Store as ChecklistStore

    User->>Sidebar: open menu
    Sidebar->>Menu: useMenu({ api, showToolbar, isPanelShown, isNavShown, enableShortcuts })
    Menu->>Hook: request available/next/progress
    Hook->>Store: subscribe & compute derived state
    Hook-->>Menu: return collections/actions
    User->>Menu: click guide item
    Menu->>Store: accept(id) / navigate
    Store->>Sidebar: notify -> widget progress updates
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Areas meriting focused review:

  • checklist store and initializeChecklist: immutability, persistence, telemetry emission, error handling
  • useChecklist: availability/locking recursion, subscription lifecycle and leaks
  • ChecklistWidget and Checklist UI: transitions, debouncing, accessibility, hash/focus navigation
  • TourGuide / HighlightElement overlay: ResizeObserver, scroll listeners, injected styles cleanup
  • Manager API/store exports and packaging: new internal_* exports and build/package entries
  • Menu/Sidebar signature and structural changes affecting multiple call sites

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a65a429 and 2ff8d29.

📒 Files selected for processing (1)
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: daily
  • GitHub Check: Core Unit Tests, windows-latest

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Oct 22, 2025

View your CI Pipeline Execution ↗ for commit 2ff8d29

Command Status Duration Result
nx run-many -t build --parallel=3 ✅ Succeeded 50s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-20 23:53:47 UTC

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/core/src/manager/components/sidebar/TestingModule.tsx (1)

147-154: Clear pending timeout on unmount to avoid setState after unmount

timeoutRef isn’t cleared in a cleanup, which can fire after unmount.

   const timeoutRef = useRef<null | ReturnType<typeof setTimeout>>(null);
@@
   const toggleCollapsed = useCallback((event?: SyntheticEvent, value?: boolean) => {
@@
     timeoutRef.current = setTimeout(() => {
       setChangingCollapse(false);
     }, 250);
   }, []);
+
+  useEffect(() => {
+    return () => {
+      if (timeoutRef.current) clearTimeout(timeoutRef.current);
+    };
+  }, []);

Also applies to: 187-197

🧹 Nitpick comments (14)
code/core/src/manager/settings/whats_new.tsx (1)

34-46: LGTM! Footer styling updates align with theme tokens.

The updated styling correctly uses theme tokens for typography and borders, consistent with the broader PR's theming approach. The footer height reduction from 80px to 40px is properly coordinated with the iframe height calculation.

Minor note: Line 37 uses a numeric value (height: 40) which defaults to pixels, while other dimensions use string literals (e.g., line 101: 'calc(100% - 40px)'). Consider height: '40px' for consistency, though the current approach is valid.

For consistency:

-  height: 40,
+  height: '40px',
code/core/src/preview-api/modules/preview-web/UrlStore.ts (1)

77-80: Consider using an anchored regex for exact matching.

The logic correctly distinguishes between missing/empty viewMode (defaults to 'story') and invalid non-empty viewMode (returns null). However, the regex /docs|story/ could match partial strings like "stories" or "documentation".

Consider using an anchored regex for more precise validation:

-    } else if (!viewMode.match(/docs|story/)) {
+    } else if (!viewMode.match(/^(docs|story)$/)) {
       return null;

This ensures only exact matches for "docs" or "story" are accepted.

code/core/src/manager/settings/GuidedTourPage.tsx (1)

40-43: Consider refining the description text tone.

The current description has an informal, slightly dismissive tone: "This isn't your first time setting up software so get to it!" Consider whether this matches your product's voice and user experience goals for onboarding.

If a more professional tone is preferred:

       <p>
-        Learn the basics. Set up Storybook. You know the drill. This isn't your first time setting
-        up software so get to it!
+        Learn the basics and get started with Storybook. Follow the guided tour to set up your project and explore key features.
       </p>
code/core/src/manager/manager-stores.ts (1)

19-23: Consider making debug flag conditional on environment.

The debug: true flag is hardcoded, which means debug logging will be active in all environments. Consider making this conditional based on CONFIG_TYPE or a separate debug environment variable to reduce noise in production.

 export const universalChecklistStore = experimental_UniversalStore.create<StoreState, StoreEvent>({
   ...UNIVERSAL_CHECKLIST_STORE_OPTIONS,
   leader: globalThis.CONFIG_TYPE === 'PRODUCTION',
-  debug: true,
+  debug: globalThis.CONFIG_TYPE !== 'PRODUCTION',
 });
code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx (1)

8-13: Consider typing the mock context more precisely.

The any type for managerContext weakens type safety. If you have access to the proper ManagerContext type, consider using it or at least a partial type that defines the required shape.

Apply this diff to improve type safety:

-const managerContext: any = {
+const managerContext: { state: object; api: { navigateUrl: ReturnType<typeof fn> } } = {
   state: {},
   api: {
     navigateUrl: fn().mockName('api::navigateUrl'),
   },
 };
code/core/src/core-server/utils/checklist.ts (1)

10-27: Consider error handling for settings operations.

The globalSettings() and settings.save() calls lack error handling. If settings fail to load or save (e.g., file system permissions, disk full, corrupted JSON), the checklist initialization will silently fail, potentially leading to state loss or confusion.

Consider adding error handling:

 export function initializeChecklist() {
   const store = experimental_UniversalStore.create<StoreState, StoreEvent>({
     ...UNIVERSAL_CHECKLIST_STORE_OPTIONS,
     leader: true,
   });

-  globalSettings().then((settings) => {
+  globalSettings()
+    .then((settings) => {
       store.setState({
         completed: settings.value.checklist?.completed ?? [],
         skipped: settings.value.checklist?.skipped ?? [],
       });

       store.onStateChange((state) => {
         settings.value.checklist = state;
-        settings.save();
+        settings.save().catch((error) => {
+          // Log error without throwing to avoid disrupting the store
+          console.error('Failed to save checklist state:', error);
+        });
       });
+    })
+    .catch((error) => {
+      console.error('Failed to initialize checklist from settings:', error);
     });
 }

Note: Based on coding guidelines, consider using Storybook's node-logger instead of console.error for server-side code.

code/core/src/manager/components/sidebar/ChecklistModule.tsx (2)

29-33: Make CSSTransition nodeRefs stable across renders.

Recreating refs each render can break enter/exit animations and cause warnings. Memoize per id.

Apply:

-import React, { createRef } from 'react';
+import React, { createRef, useRef } from 'react';
@@
-  const nextTasks = allTasks
-    .filter(({ id }) => !completed.includes(id) && !skipped.includes(id))
-    .map((task) => ({ ...task, nodeRef: createRef<HTMLLIElement>() }));
+  const refMap = useRef(new Map<string, React.RefObject<HTMLLIElement>>());
+  const getRef = (id: string) => {
+    let ref = refMap.current.get(id);
+    if (!ref) {
+      ref = createRef<HTMLLIElement>();
+      refMap.current.set(id, ref);
+    }
+    return ref;
+  };
+  const nextTasks = allTasks
+    .filter(({ id }) => !completed.includes(id) && !skipped.includes(id))
+    .map((task) => ({ ...task, nodeRef: getRef(task.id) }));

Also applies to: 52-54


45-47: Guard progress calculation (division-by-zero) and improve clarity.

Minor safety/readability tweak.

Apply:

-          <ListboxButton onClick={() => api.navigateUrl('/settings/guided-tour', { plain: false })}>
-            {Math.round(((allTasks.length - nextTasks.length) / allTasks.length) * 100)}%
-          </ListboxButton>
+          <ListboxButton onClick={() => api.navigateUrl('/settings/guided-tour', { plain: false })}>
+            {allTasks.length
+              ? Math.round(((allTasks.length - nextTasks.length) / allTasks.length) * 100)
+              : 0}
+            %
+          </ListboxButton>
code/core/src/components/components/Collapsible/Collapsible.stories.tsx (1)

6-13: Add minimal a11y to toggle button (aria-expanded/aria-controls).

Expose and pass contentId from state to the button for better semantics.

Example:

-const toggle = ({ isCollapsed, toggleCollapsed }: { isCollapsed: boolean; toggleCollapsed: () => void }) =>
-  <button onClick={toggleCollapsed}>{isCollapsed ? 'Open' : 'Close'}</button>;
+const toggle = ({ isCollapsed, toggleCollapsed, contentId }: { isCollapsed: boolean; toggleCollapsed: () => void; contentId: string }) =>
+  <button
+    aria-expanded={!isCollapsed}
+    aria-controls={contentId}
+    onClick={toggleCollapsed}
+  >
+    {isCollapsed ? 'Open' : 'Close'}
+  </button>;

Also applies to: 36-46

code/core/src/manager/settings/Checklist/checklistData.tsx (1)

29-33: Avoid arbitrary setTimeout completion in sample predicate.

Auto-completing after 3s may confuse users. Consider gating completion on an actual user action or detectable condition.

code/core/src/components/components/Listbox/Listbox.tsx (1)

56-63: Forward refs through ListboxButton for parity with Button and better composability.

This enables parent code to focus/measure the underlying button.

Apply:

-import React, { type ComponentProps } from 'react';
+import React, { forwardRef, type ComponentProps } from 'react';
@@
-export const ListboxButton = ({
-  padding = 'small',
-  size = 'medium',
-  variant = 'ghost',
-  ...props
-}: ComponentProps<typeof Button>) => (
-  <Button {...props} variant={variant} padding={padding} size={size} />
-);
+export const ListboxButton = forwardRef<HTMLButtonElement, ComponentProps<typeof Button>>(
+  ({ padding = 'small', size = 'medium', variant = 'ghost', ...props }, ref) => (
+    <Button ref={ref} {...props} variant={variant} padding={padding} size={size} />
+  )
+);
code/core/src/components/components/Card/Card.tsx (1)

64-97: Prevent the animated outline from intercepting pointer events

The absolutely‑positioned &:before can sit above content and block clicks. Make it inert to pointer events.

   '&:before': {
     content: '""',
     display: animation === 'none' ? 'none' : 'block',
     position: 'absolute',
     left: 0,
     top: 0,
     width: '100%',
     height: '100%',
     opacity: 1,
+    pointerEvents: 'none',
code/core/src/manager/components/sidebar/TestingModule.tsx (1)

30-34: Avoid naming collision with exported Collapsible component

This local styled Collapsible can be confused with the exported components/Collapsible. Consider CollapsibleContainer.

code/core/src/manager/settings/Checklist/Checklist.tsx (1)

246-249: Remove unused nodeRef (or wire it up)

nodeRef is created but never read; drop it or use it for scrolling/focus.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0e3884 and 5110838.

📒 Files selected for processing (31)
  • code/core/src/cli/globalSettings.ts (1 hunks)
  • code/core/src/components/components/Button/Button.tsx (3 hunks)
  • code/core/src/components/components/Card/Card.stories.tsx (1 hunks)
  • code/core/src/components/components/Card/Card.tsx (1 hunks)
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx (1 hunks)
  • code/core/src/components/components/Collapsible/Collapsible.tsx (1 hunks)
  • code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx (1 hunks)
  • code/core/src/components/components/FocusProxy/FocusProxy.tsx (1 hunks)
  • code/core/src/components/components/Listbox/Listbox.stories.tsx (1 hunks)
  • code/core/src/components/components/Listbox/Listbox.tsx (1 hunks)
  • code/core/src/components/index.ts (1 hunks)
  • code/core/src/core-server/presets/common-preset.ts (2 hunks)
  • code/core/src/core-server/utils/checklist.ts (1 hunks)
  • code/core/src/manager/components/Transition.tsx (1 hunks)
  • code/core/src/manager/components/layout/Layout.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/Explorer.tsx (2 hunks)
  • code/core/src/manager/components/sidebar/TestingModule.tsx (3 hunks)
  • code/core/src/manager/globals/exports.ts (2 hunks)
  • code/core/src/manager/manager-stores.mock.ts (2 hunks)
  • code/core/src/manager/manager-stores.ts (2 hunks)
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx (1 hunks)
  • code/core/src/manager/settings/Checklist/Checklist.tsx (1 hunks)
  • code/core/src/manager/settings/Checklist/checklistData.tsx (1 hunks)
  • code/core/src/manager/settings/GuidedTourPage.tsx (1 hunks)
  • code/core/src/manager/settings/index.tsx (3 hunks)
  • code/core/src/manager/settings/whats_new.tsx (2 hunks)
  • code/core/src/preview-api/modules/preview-web/UrlStore.test.ts (1 hunks)
  • code/core/src/preview-api/modules/preview-web/UrlStore.ts (1 hunks)
  • code/core/src/shared/checklist-store/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/components/index.ts
  • code/core/src/manager/manager-stores.ts
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/components/components/FocusProxy/FocusProxy.tsx
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/cli/globalSettings.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/settings/whats_new.tsx
  • code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/manager/manager-stores.mock.ts
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/components/sidebar/TestingModule.tsx
  • code/core/src/manager/settings/GuidedTourPage.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/manager/components/Transition.tsx
  • code/core/src/preview-api/modules/preview-web/UrlStore.ts
  • code/core/src/components/components/Collapsible/Collapsible.tsx
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/components/index.ts
  • code/core/src/manager/manager-stores.ts
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/components/components/FocusProxy/FocusProxy.tsx
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/cli/globalSettings.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/settings/whats_new.tsx
  • code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/manager/manager-stores.mock.ts
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/components/sidebar/TestingModule.tsx
  • code/core/src/manager/settings/GuidedTourPage.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/manager/components/Transition.tsx
  • code/core/src/preview-api/modules/preview-web/UrlStore.ts
  • code/core/src/components/components/Collapsible/Collapsible.tsx
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/components/index.ts
  • code/core/src/manager/manager-stores.ts
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/components/components/FocusProxy/FocusProxy.tsx
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/cli/globalSettings.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/settings/whats_new.tsx
  • code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/manager/manager-stores.mock.ts
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/components/sidebar/TestingModule.tsx
  • code/core/src/manager/settings/GuidedTourPage.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/manager/components/Transition.tsx
  • code/core/src/preview-api/modules/preview-web/UrlStore.ts
  • code/core/src/components/components/Collapsible/Collapsible.tsx
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/core-server/presets/common-preset.ts
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/components/index.ts
  • code/core/src/manager/manager-stores.ts
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/components/components/FocusProxy/FocusProxy.tsx
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/cli/globalSettings.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/settings/whats_new.tsx
  • code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/manager/manager-stores.mock.ts
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/components/sidebar/TestingModule.tsx
  • code/core/src/manager/settings/GuidedTourPage.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/manager/components/Transition.tsx
  • code/core/src/preview-api/modules/preview-web/UrlStore.ts
  • code/core/src/components/components/Collapsible/Collapsible.tsx
code/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

code/**/*.{test,spec}.{ts,tsx}: Place all test files under the code/ directory
Name test files as *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx

Files:

  • code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access mocked functions
Implement mock behaviors in beforeEach blocks
Mock all required dependencies that the test subject uses
Mock implementations should be placed in beforeEach blocks
Each mock implementation should return a Promise for async functions
Mock implementations should match the expected return type of the original function
Use vi.mocked() to access and implement mock behaviors
Mock all required properties and methods that the test subject uses
Avoid direct function mocking without vi.mocked()
Avoid mock implementations outside of beforeEach blocks
Avoid mocking without the spy: true option
Avoid inline mock implementations within test cases
Avoid mocking only a subset of required dependencies
Mock at the highest level of abstraction needed
Keep mock implementations simple and focused
Use type-safe mocking with vi.mocked()
Document complex mock behaviors
Group related mocks together

Files:

  • code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.@(test|spec).{ts,tsx,js,jsx}: Unit tests should import and execute the functions under test rather than only asserting on syntax patterns
Mock external dependencies in tests using vi.mock() (e.g., filesystem, loggers)

Files:

  • code/core/src/preview-api/modules/preview-web/UrlStore.test.ts
🧬 Code graph analysis (22)
code/core/src/core-server/presets/common-preset.ts (1)
code/core/src/core-server/utils/checklist.ts (1)
  • initializeChecklist (10-27)
code/core/src/components/components/Card/Card.stories.tsx (1)
code/core/src/components/components/Card/Card.tsx (1)
  • Card (29-42)
code/core/src/manager/settings/Checklist/Checklist.stories.tsx (3)
code/core/src/manager/settings/Checklist/Checklist.tsx (1)
  • Checklist (217-409)
code/core/src/manager-api/root.tsx (1)
  • ManagerContext (78-78)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
  • checklistData (7-173)
code/core/src/manager/settings/index.tsx (1)
code/core/src/manager/settings/GuidedTourPage.tsx (1)
  • GuidedTourPage (35-48)
code/core/src/manager/components/sidebar/ChecklistModule.tsx (6)
code/core/src/manager-api/root.tsx (1)
  • useStorybookApi (294-297)
code/core/src/manager/manager-stores.mock.ts (2)
  • universalChecklistStore (48-57)
  • checklistStore (59-59)
code/core/src/manager/manager-stores.ts (2)
  • universalChecklistStore (19-23)
  • checklistStore (25-25)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
  • checklistData (7-173)
code/core/src/components/components/Card/Card.tsx (1)
  • Card (29-42)
code/core/src/manager/components/Transition.tsx (1)
  • Transition (6-12)
code/core/src/manager/manager-stores.ts (2)
code/core/src/manager/manager-stores.mock.ts (2)
  • universalChecklistStore (48-57)
  • checklistStore (59-59)
code/core/src/shared/checklist-store/index.ts (4)
  • StoreState (6-9)
  • StoreEvent (11-14)
  • UNIVERSAL_CHECKLIST_STORE_OPTIONS (16-19)
  • createChecklistStore (29-50)
code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx (2)
code/core/src/manager/components/sidebar/ChecklistModule.tsx (1)
  • ChecklistModule (25-78)
code/core/src/manager-api/root.tsx (1)
  • ManagerContext (78-78)
code/core/src/components/components/FocusProxy/FocusProxy.tsx (1)
code/core/src/components/index.ts (1)
  • FocusProxy (46-46)
code/core/src/components/components/Listbox/Listbox.stories.tsx (2)
code/core/src/components/components/Listbox/Listbox.tsx (5)
  • Listbox (7-15)
  • ListboxItem (17-54)
  • ListboxText (76-90)
  • ListboxButton (56-63)
  • ListboxAction (65-74)
code/core/src/manager/container/Menu.tsx (1)
  • Shortcut (44-50)
code/core/src/components/components/Card/Card.tsx (1)
code/core/src/components/index.ts (1)
  • Card (45-45)
code/core/src/manager/components/sidebar/Explorer.tsx (1)
code/core/src/manager/components/sidebar/ChecklistModule.tsx (1)
  • ChecklistModule (25-78)
code/core/src/preview-api/modules/preview-web/UrlStore.test.ts (1)
code/core/src/preview-api/modules/preview-web/UrlStore.ts (1)
  • getSelectionSpecifierFromPath (69-92)
code/core/src/manager/manager-stores.mock.ts (2)
code/core/src/manager/manager-stores.ts (2)
  • universalChecklistStore (19-23)
  • checklistStore (25-25)
code/core/src/shared/checklist-store/index.ts (4)
  • StoreState (6-9)
  • StoreEvent (11-14)
  • UNIVERSAL_CHECKLIST_STORE_OPTIONS (16-19)
  • createChecklistStore (29-50)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
code/core/src/manager/settings/Checklist/Checklist.tsx (1)
  • ChecklistData (11-24)
code/core/src/components/components/Collapsible/Collapsible.stories.tsx (3)
code/core/src/components/components/Collapsible/Collapsible.tsx (1)
  • Collapsible (6-31)
code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx (1)
  • Default (10-21)
code/core/src/test/testing-library.ts (1)
  • userEvent (120-123)
code/core/src/core-server/utils/checklist.ts (2)
code/core/src/shared/checklist-store/index.ts (3)
  • StoreState (6-9)
  • StoreEvent (11-14)
  • UNIVERSAL_CHECKLIST_STORE_OPTIONS (16-19)
code/core/src/cli/globalSettings.ts (1)
  • globalSettings (22-41)
code/core/src/manager/components/sidebar/TestingModule.tsx (1)
code/core/src/components/components/Card/Card.tsx (2)
  • Card (29-42)
  • Content (44-48)
code/core/src/manager/settings/GuidedTourPage.tsx (2)
code/core/src/manager/settings/Checklist/Checklist.tsx (1)
  • Checklist (217-409)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
  • checklistData (7-173)
code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx (3)
code/core/src/components/components/FocusProxy/FocusProxy.tsx (1)
  • FocusProxy (3-15)
code/core/src/components/index.ts (1)
  • FocusProxy (46-46)
code/core/src/components/components/Collapsible/Collapsible.stories.tsx (1)
  • Default (24-24)
code/core/src/manager/settings/Checklist/Checklist.tsx (5)
code/core/src/manager-api/root.tsx (2)
  • API (94-107)
  • useStorybookApi (294-297)
code/core/src/components/components/Modal/Modal.styled.tsx (1)
  • Actions (112-116)
code/core/src/manager/manager-stores.mock.ts (2)
  • universalChecklistStore (48-57)
  • checklistStore (59-59)
code/core/src/manager/manager-stores.ts (2)
  • universalChecklistStore (19-23)
  • checklistStore (25-25)
code/core/src/components/components/FocusProxy/FocusProxy.tsx (1)
  • FocusProxy (3-15)
code/core/src/components/components/Listbox/Listbox.tsx (1)
code/core/src/components/components/Button/Button.tsx (1)
  • Button (20-76)
code/core/src/components/components/Collapsible/Collapsible.tsx (1)
code/core/src/components/index.ts (1)
  • Collapsible (44-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Danger JS
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (25)
code/core/src/manager/components/layout/Layout.tsx (1)

261-262: LGTM! Flex layout enables vertical stacking for pages content.

The addition of flex properties to PagesContainer is a straightforward CSS change that enables its children to be arranged in a vertical column. This complements the existing grid positioning properties without conflict.

code/core/src/manager/settings/whats_new.tsx (1)

90-105: LGTM! Iframe height calculation correctly updated.

The height calculation properly accounts for the new 40px footer height, preventing visual gaps or overlaps. This change is correctly coordinated with the Container component updates.

code/core/src/preview-api/modules/preview-web/UrlStore.test.ts (3)

75-81: LGTM! Test correctly verifies explicit viewMode=story.

The test properly validates that when viewMode is explicitly set to 'story' in the URL, the function returns the expected storySpecifier and viewMode.


82-88: LGTM! Test correctly verifies explicit viewMode=docs.

The test properly validates that when viewMode is explicitly set to 'docs' in the URL, the function returns the expected storySpecifier and viewMode.


89-92: LGTM! Test correctly verifies rejection of unsupported viewModes.

The test properly validates that invalid viewMode values cause the function to return null, preventing navigation to stories with unsupported view modes.

code/core/src/manager/settings/index.tsx (1)

16-16: LGTM! Guided tour integration follows established patterns.

The new guided tour tab and route follow the same structure as existing settings pages. The implementation is consistent with the codebase conventions.

Also applies to: 95-95, 115-117

code/core/src/manager/globals/exports.ts (1)

489-489: LGTM! Generated export additions are correct.

The new component exports (Card, Collapsible, FocusProxy, Listbox family) are properly added to the public API surface. Since this file is auto-generated (as noted in the header comment), these changes reflect the intended public exports for the new checklist components.

Also applies to: 492-492, 499-499, 513-517

code/core/src/cli/globalSettings.ts (1)

18-18: LGTM! Schema extension is forward-compatible.

The new checklist field is correctly defined as optional, following the established pattern for forward compatibility (as noted in line 14's comment). The structure with completed and skipped arrays aligns with the store state types used throughout the feature.

code/core/src/manager/components/sidebar/Explorer.tsx (1)

4-4: LGTM! Checklist module integration is clean.

The ChecklistModule is properly integrated into the Explorer component. Based on the implementation in code/core/src/manager/components/sidebar/ChecklistModule.tsx, the module returns null when there are no pending tasks, ensuring it doesn't affect the layout when inactive.

Also applies to: 42-42

code/core/src/manager/settings/Checklist/Checklist.stories.tsx (1)

1-46: LGTM! Story setup properly mocks dependencies.

The story correctly provides the required ManagerContext and initializes the mock checklist store with sample data. The use of fn().mockName() for the API mock and the beforeEach hook for state setup follow Storybook best practices.

code/core/src/manager/manager-stores.ts (1)

1-25: Store initialization follows established patterns.

The checklist stores are properly initialized using the universal store pattern, which enables state synchronization across contexts. The conditional leader flag based on CONFIG_TYPE aligns with the mock setup in code/core/src/manager/manager-stores.mock.ts.

code/core/src/core-server/presets/common-preset.ts (1)

256-256: The review comment contains incorrect premises and lacks supporting evidence.

The stated concern assumes initializeChecklist() can be awaited, but the function is synchronous (no async keyword) and therefore cannot be awaited. The async initialization via globalSettings().then() is fire-and-forget by design, matching the pattern used by initializeWhatsNew() and initializeSaveStory().

Additionally, the claim that "other code attempts to access checklist state before initialization completes" lacks supporting evidence. All actual checklist store accesses found in the codebase occur in user interaction handlers (onClick), not in synchronous startup code paths. The UniversalStore class provides untilReady() for scenarios requiring readiness guarantees, and the commented code in Checklist.tsx demonstrates developer awareness of this pattern.

Likely an incorrect or invalid review comment.

code/core/src/components/components/Card/Card.stories.tsx (1)

1-63: LGTM! Story comprehensively demonstrates Card variations.

The story file is well-structured and demonstrates the Card component's various states including different outline animations (rainbow, spin) and outline colors. The grid layout provides clear visual comparison of all variants.

code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx (1)

24-29: LGTM! Mock store initialization is well-structured.

The beforeEach hook properly initializes the mock store state with realistic completed and skipped items, ensuring a consistent starting state for the story.

code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx (1)

10-21: LGTM! FocusProxy story effectively demonstrates focus behavior.

The story correctly demonstrates the FocusProxy component's focus behavior with a play function that automatically focuses the button on canvas load. The htmlFor prop properly targets the button element by id.

code/core/src/components/components/Button/Button.tsx (2)

9-9: LGTM! Polymorphic as prop adds flexibility.

The addition of the as prop enables the Button to be rendered as different HTML elements (button, a, label) or as a Slot for composition. This is a useful enhancement for accessibility and flexibility.


36-36: Nice simplification of polymorphic rendering logic.

The new implementation const Comp = asChild ? Slot : as is more concise and clearer than conditional branching. It properly handles the asChild case while respecting the as prop.

code/core/src/manager/manager-stores.mock.ts (1)

48-59: LGTM! Mock store setup mirrors production implementation.

The mock checklist store setup correctly mirrors the production implementation from manager-stores.ts, using the same configuration options and factory pattern. The use of testUtils.mocked() ensures proper mock tracking for tests.

code/core/src/manager/components/Transition.tsx (1)

6-12: LGTM! Excellent accessibility consideration.

The Transition component properly respects the prefers-reduced-motion media query by setting the transition duration to 0ms, ensuring a better experience for users with motion sensitivity. The CSS variable approach is clean and allows consuming components to reference --transition-duration.

code/core/src/core-server/utils/checklist.ts (1)

10-14: This review comment is incorrect and should be dismissed.

The store instance in initializeChecklist() is properly stored in UniversalStore's global instances registry using its ID 'storybook/checklist' and does not need to be returned. More importantly, the store is actively used within the function: it subscribes to state changes and persists them to global settings. The server-side store (created with leader: true) synchronizes with the manager-side store via the UniversalStore's channel mechanism.

code/core/src/components/index.ts (1)

44-53: Exports look correct and consistent with existing structure.

Public surface updates for Collapsible/Card/FocusProxy/Listbox* LGTM.

code/core/src/components/components/Listbox/Listbox.stories.tsx (1)

8-11: Stories read well and cover main compositions.

No issues spotted.

Also applies to: 15-50, 52-81

code/core/src/manager/settings/Checklist/checklistData.tsx (1)

51-57: Open-in-new-tab links: confirm rel is set for security.

If Link doesn’t auto-apply rel="noopener noreferrer" when target="_blank", add it to avoid opener leaks.

Can you confirm the internal Link component behavior?

Also applies to: 71-77, 116-122, 139-145

code/core/src/components/components/Collapsible/Collapsible.tsx (1)

6-31: LGTM: Accessible, controlled/uncontrolled API with sensible defaults

Good ARIA wiring (aria-controls, aria-expanded, aria-hidden), reduced‑motion handling, and render‑prop support.

Also applies to: 33-50, 52-70

code/core/src/manager/components/sidebar/TestingModule.tsx (1)

373-401: Code is appropriate as-is; no fallback needed.

Storybook targets modern evergreen browsers (latest 1–2 stable versions, Chrome ≥100), and the inert attribute is natively supported in all current releases of Chrome (~v102+), Firefox (~v112+), and Safari (~v15.5+). There is no browser coverage gap within Storybook's supported scope. The use of inert without a fallback is safe.

@storybook-app-bot
Copy link

storybook-app-bot bot commented Oct 22, 2025

Package Benchmarks

Commit: 2ff8d29, ran on 20 November 2025 at 23:40:50 UTC

The following packages have significant changes to their size or dependencies:

storybook

Before After Difference
Dependency count 39 39 0
Self size 19.20 MB 19.65 MB 🚨 +448 KB 🚨
Dependency size 16.40 MB 16.40 MB 0 B
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 173 173 0
Self size 773 KB 773 KB 🎉 -84 B 🎉
Dependency size 66.17 MB 66.62 MB 🚨 +449 KB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 166 166 0
Self size 30 KB 30 KB 🚨 +36 B 🚨
Dependency size 64.74 MB 65.19 MB 🚨 +448 KB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 40 40 0
Self size 999 KB 999 KB 🚨 +42 B 🚨
Dependency size 35.60 MB 36.05 MB 🚨 +448 KB 🚨
Bundle Size Analyzer node node

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
code/core/src/manager/settings/Checklist/Checklist.tsx (2)

222-228: Guard window access and replace polling with hashchange event.

This issue has already been flagged in previous reviews. The code directly accesses globalThis.window without checking if it exists (causing potential SSR/test crashes) and uses 100ms polling instead of the native hashchange event.


265-276: Fix focus selector - wrong attribute.

This issue has already been flagged in previous reviews. The [for="toggle-${targetHash}"] selector doesn't match the button elements. Should use getElementById or querySelector('#toggle-...') instead.

🧹 Nitpick comments (3)
code/core/src/manager/settings/Checklist/Checklist.tsx (3)

230-242: Remove commented-out code.

This large block of commented code should either be uncommented if needed or removed entirely. Dead code adds confusion and maintenance burden.

-  // universalChecklistStore.untilReady().then(() => checklistStore.complete('whats-new-sb-9'));
-
-  // useEffect(() => {
-  //   // const componentTestStatusStore = experimental_getStatusStore(STATUS_TYPE_ID_COMPONENT_TEST);
-  //   // const a11yStatusStore = experimental_getStatusStore(STATUS_TYPE_ID_A11Y);
-
-  //   data.sections.forEach((section) => {
-  //     section.items.forEach((item) => {
-  //       const complete = () => setCompleted((completed) => new Set([...completed, item.id]));
-  //       item.predicate?.({ api, complete });
-  //     });
-  //   });
-  // }, [data, api]);
-

244-259: Optimize state computation with useMemo and fix ref creation.

The state object is recomputed on every render, and createRef is called for each item on every render, breaking referential equality and potentially causing issues with focus management.

+  const state: ChecklistState = React.useMemo(() => ({
-  const state: ChecklistState = {
     next: 0,
     progress: 0,
     sections: data.sections.map((section) => ({
       ...section,
-      items: section.items.map((item) => ({ ...item, nodeRef: createRef<HTMLLIElement>() })),
+      items: section.items.map((item) => ({ 
+        ...item, 
+        nodeRef: item.nodeRef ?? createRef<HTMLLIElement>() 
+      })),
       open: false,
       progress:
         (section.items.reduce(
           (a, b) => (completed.includes(b.id) || skipped.includes(b.id) ? a + 1 : a),
           0
         ) /
           section.items.length) *
         100,
     })),
-  };
+  }), [data, completed, skipped]);

350-360: Consider letting the start handler control completion.

The Start button marks the item as complete before calling the start handler. This assumes "starting" equals "completing," which may not be the intended behavior. Consider letting the start handler decide when to mark the item as complete.

 <Button
   variant="solid"
   size="small"
   onClick={() => {
-    checklistStore.complete(item.id);
-    item.start?.({ api });
+    item.start?.({ api });
   }}
 >
   Start
 </Button>

The start handler can then call the complete callback passed via the predicate pattern if needed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5110838 and b91e701.

📒 Files selected for processing (2)
  • code/core/src/manager/manager-stores.mock.ts (2 hunks)
  • code/core/src/manager/settings/Checklist/Checklist.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier formatting on changed files before committing
Run ESLint on changed files and fix all errors/warnings before committing (use yarn lint:js:cmd <file>)

Files:

  • code/core/src/manager/manager-stores.mock.ts
  • code/core/src/manager/settings/Checklist/Checklist.tsx
**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions from modules when they need to be unit-tested

Files:

  • code/core/src/manager/manager-stores.mock.ts
  • code/core/src/manager/settings/Checklist/Checklist.tsx
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In application code, use Storybook loggers instead of console.* (client code: storybook/internal/client-logger; server code: storybook/internal/node-logger)

Files:

  • code/core/src/manager/manager-stores.mock.ts
  • code/core/src/manager/settings/Checklist/Checklist.tsx
{code/**,scripts/**}/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/manager-stores.mock.ts
  • code/core/src/manager/settings/Checklist/Checklist.tsx
🧬 Code graph analysis (2)
code/core/src/manager/manager-stores.mock.ts (2)
code/core/src/manager/manager-stores.ts (2)
  • universalChecklistStore (19-23)
  • checklistStore (25-25)
code/core/src/shared/checklist-store/index.ts (4)
  • StoreState (6-9)
  • StoreEvent (11-14)
  • UNIVERSAL_CHECKLIST_STORE_OPTIONS (16-19)
  • createChecklistStore (29-50)
code/core/src/manager/settings/Checklist/Checklist.tsx (4)
code/core/src/manager-api/root.tsx (2)
  • API (94-107)
  • useStorybookApi (294-297)
code/core/src/components/components/Modal/Modal.styled.tsx (1)
  • Actions (112-116)
code/core/src/manager/manager-stores.ts (2)
  • universalChecklistStore (19-23)
  • checklistStore (25-25)
code/core/src/components/components/FocusProxy/FocusProxy.tsx (1)
  • FocusProxy (3-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (4)
code/core/src/manager/manager-stores.mock.ts (2)

7-8: LGTM!

The imports align with the checklist store integration and mirror the pattern used in the real manager-stores.ts implementation.


48-57: LGTM!

The mock store implementation correctly mirrors the pattern used for other stores in this file and aligns with the real implementation in manager-stores.ts.

code/core/src/manager/settings/Checklist/Checklist.tsx (2)

11-32: LGTM!

The data contracts are well-structured and provide clear interfaces for the checklist feature.


320-325: Verify keyboard navigation accessibility.

The tabIndex={-1} and onMouseDown preventDefault remove the FocusProxy from keyboard navigation. While this appears intentional for programmatic focus via hash changes, ensure keyboard users have an alternative way to navigate to and interact with checklist items.

@ghengeveld ghengeveld mentioned this pull request Oct 28, 2025
8 tasks
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
code/core/src/manager/components/sidebar/Menu.stories.tsx (3)

26-35: Consider providing a complete api stub in managerContext to match useMenu's requirements.

The managerContext.api only includes five methods (getData, getIndex, getUrlState, navigate, on), but components within the ManagerContext.Provider may attempt to access other methods. While the story-specific useMenu calls provide their own api objects, any component that directly accesses the ManagerContext would receive this incomplete stub.

This is related to the incomplete API surface already flagged in the previous review for the story-specific useMenu calls.


51-62: beforeEach pattern issue already flagged in previous review.

The beforeEach hook should return a cleanup function rather than directly calling mockStore.setState, as noted in the previous review comment. This ensures proper state isolation between story renders.

Per the previous review:

beforeEach: () => {
  return () => {
    mockStore.setState(/* initial state */);
  };
}

89-102: Incomplete api object already flagged in previous review.

The api object passed to useMenu only includes read-only helper methods but omits the action methods (changeSettingsTab, toggleNav, togglePanel, toggleToolbar, jumpToComponent, jumpToStory, emit) that menu items will invoke. Clicking those menu items in the story will throw runtime errors.

The previous review suggested providing a complete stub api with all required methods. This issue applies to all three Expanded* story renderers (lines 89-102, 139-165, 191-204).

code/core/src/manager/components/sidebar/Sidebar.stories.tsx (1)

5-6: Use globalThis instead of importing from @storybook/global.

This import is only used to access CONFIG_TYPE in the SimpleInProduction story (lines 191-192). As previously noted, using globalThis.CONFIG_TYPE directly would avoid adding the @storybook/global dependency here.

Apply this diff to remove the unnecessary import:

-import { global } from '@storybook/global';
-

Then update lines 191-192:

-    const configType = global.CONFIG_TYPE;
-    global.CONFIG_TYPE = 'PRODUCTION';
+    const configType = globalThis.CONFIG_TYPE;
+    globalThis.CONFIG_TYPE = 'PRODUCTION';

And update line 194:

-      global.CONFIG_TYPE = configType;
+      globalThis.CONFIG_TYPE = configType;
code/core/src/core-server/utils/checklist.ts (1)

80-86: Handle async telemetry failures and avoid emitting empty events.

telemetry is async and awaited internally; calling it here without await/.catch() risks unhandled promise rejections if sending telemetry fails, and you also invoke it even when neither items nor the widget changed. This was called out in a previous review and remains unresolved.

Consider building a payload first, skipping the call when it’s empty, and catching errors so checklist state changes can’t be disrupted by telemetry:

-      const changedValues = Object.entries(state.items).filter(
-        ([key, value]) => value !== previousState.items[key]
-      );
-      telemetry('onboarding-checklist', {
-        ...(changedValues.length > 0 ? { items: Object.fromEntries(changedValues) } : {}),
-        ...(!deepEqual(state.widget, previousState.widget) ? { widget: state.widget } : {}),
-      });
+      const changedValues = Object.entries(state.items).filter(
+        ([key, value]) => value !== previousState.items[key]
+      );
+      const payload: Record<string, unknown> = {
+        ...(changedValues.length > 0 ? { items: Object.fromEntries(changedValues) } : {}),
+        ...(!deepEqual(state.widget, previousState.widget) ? { widget: state.widget } : {}),
+      };
+      if (Object.keys(payload).length > 0) {
+        telemetry('onboarding-checklist', payload).catch((error) => {
+          logger.error('Failed to send onboarding-checklist telemetry');
+          logger.error(error);
+        });
+      }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26b351c and 6925f08.

📒 Files selected for processing (9)
  • code/core/src/core-server/utils/checklist.ts (1 hunks)
  • code/core/src/manager-api/modules/versions.ts (3 hunks)
  • code/core/src/manager/components/TourGuide/HighlightElement.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/ChecklistWidget.stories.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/ChecklistWidget.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/Menu.stories.tsx (6 hunks)
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx (4 hunks)
  • code/core/src/manager/container/Menu.stories.tsx (2 hunks)
  • code/core/src/manager/settings/GuidePage.stories.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • code/core/src/manager-api/modules/versions.ts
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/components/TourGuide/HighlightElement.tsx
  • code/core/src/manager/components/sidebar/ChecklistWidget.stories.tsx
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistWidget.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
📚 Learning: 2025-11-05T09:36:55.944Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistWidget.tsx
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistWidget.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
🧬 Code graph analysis (4)
code/core/src/core-server/utils/checklist.ts (3)
code/core/src/shared/checklist-store/index.ts (4)
  • StoreState (6-10)
  • StoreEvent (15-21)
  • UNIVERSAL_CHECKLIST_STORE_OPTIONS (23-26)
  • ItemState (13-13)
code/core/src/cli/globalSettings.ts (1)
  • globalSettings (34-53)
code/core/src/telemetry/index.ts (1)
  • telemetry (29-70)
code/core/src/manager/components/sidebar/ChecklistWidget.tsx (5)
code/core/src/manager-api/root.tsx (1)
  • useStorybookApi (295-298)
code/core/src/manager/components/sidebar/useChecklist.ts (2)
  • useChecklist (109-225)
  • ChecklistItem (28-39)
code/core/src/manager/components/Optional/Optional.tsx (1)
  • Optional (19-54)
code/core/src/manager/components/TextFlip.tsx (1)
  • TextFlip (63-100)
code/core/src/manager/components/Particles/Particles.tsx (1)
  • Particles (113-165)
code/core/src/manager/components/sidebar/Menu.stories.tsx (3)
code/core/src/manager-api/root.tsx (1)
  • ManagerContext (79-79)
code/core/src/shared/checklist-store/checklistData.state.ts (1)
  • initialState (3-98)
code/core/src/manager/container/Menu.tsx (1)
  • useMenu (64-271)
code/core/src/manager/components/sidebar/Sidebar.stories.tsx (2)
code/core/src/manager/manager-stores.mock.ts (1)
  • internal_universalChecklistStore (52-61)
code/core/src/shared/checklist-store/checklistData.state.ts (1)
  • initialState (3-98)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: daily
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (4)
code/core/src/manager/components/sidebar/Sidebar.stories.tsx (3)

13-17: LGTM!

The checklist-related imports are correctly integrated for test setup. The mock stores and initial state are properly used in the beforeEach hook to initialize the checklist feature state for the Sidebar stories.


118-129: LGTM!

The beforeEach hook properly initializes the checklist store state for testing. The status overrides (accepted, done, skipped) create a realistic test scenario with a partially-completed checklist, which is appropriate for visual testing of the Sidebar component.


190-196: LGTM!

The beforeEach hook correctly implements the cleanup pattern to temporarily switch to production mode for this specific story. The use of a return function to restore the original CONFIG_TYPE ensures proper test isolation.

code/core/src/manager/components/sidebar/ChecklistWidget.tsx (1)

49-76: Checklist widget state and transitions look solid.

The useTransitionArray + renderItems pattern, the 2‑second delayed swap to nextItems, and the event handling (including stopPropagation on nested buttons) all look correct and align with the intended animations and navigation behavior. No further changes needed from my side here.

Also applies to: 167-325

Comment on lines 35 to 44
const setState = ({
items = state.items,
widget = state.widget,
}: {
items?: typeof state.items;
widget?: typeof state.widget;
}) => {
settings.value.checklist = { items: items, widget };
settings.save();
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid unhandled rejections from settings.save() and cache.set() in persistence helpers.

Both settings.save() (awaited in globalSettings) and cache.set() from the filesystem cache are very likely async. Here they’re called without await or .catch(), so any write error (permissions, disk issues) can surface as unhandled promise rejections, even though you already protect initialization with a top-level try/catch.

You can keep the fire‑and‑forget behavior but explicitly swallow/log failures inside the persistence lambdas:

-        const setState = ({
-          items = state.items,
-          widget = state.widget,
-        }: {
-          items?: typeof state.items;
-          widget?: typeof state.widget;
-        }) => {
-          settings.value.checklist = { items: items, widget };
-          settings.save();
-        };
+        const setState = ({
+          items = state.items,
+          widget = state.widget,
+        }: {
+          items?: typeof state.items;
+          widget?: typeof state.widget;
+        }) => {
+          settings.value.checklist = { items, widget };
+          void settings.save().catch((error) => {
+            logger.error('Failed to persist user checklist state');
+            logger.error(error);
+          });
+        };
@@
-        const setState = ({ items }: Pick<StoreState, 'items'>) => cache.set('state', { items });
+        const setState = ({ items }: Pick<StoreState, 'items'>) => {
+          void cache.set('state', { items }).catch((error: unknown) => {
+            logger.error('Failed to persist project checklist state');
+            logger.error(error);
+          });
+        };
@@
-      saveProjectState({ items: projectValues });
-      saveUserState({ items: userValues, widget: state.widget });
+      saveProjectState({ items: projectValues });
+      saveUserState({ items: userValues, widget: state.widget });

This keeps the callback sync from the store’s perspective while making persistence failures explicit and non‑fatal.

Also applies to: 48-52, 77-78

🤖 Prompt for AI Agents
code/core/src/core-server/utils/checklist.ts lines 35-44 (also apply same change
to 48-52 and 77-78): the persistence calls (settings.save() and cache.set()) are
invoked fire-and-forget and can produce unhandled promise rejections; change
them to explicitly handle async failures by attaching a rejection handler (e.g.,
call the promise and use .catch(...) or prefix with void and .catch(...)) that
logs or swallows errors so failures are non-fatal while keeping the surrounding
API synchronous; apply the same pattern to every settings.save() and cache.set()
invocation in the listed ranges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
code/core/src/shared/checklist-store/index.ts (1)

59-76: disable unconditionally clears mutedAt for all items; consider conditioning on the flag

Right now disable always rewrites every item with mutedAt: undefined, regardless of whether value is true or false. That means any per‑item mute/snooze state is discarded both when disabling and when re‑enabling the widget, which may not be what you want if mutedAt is meant to persist user intent.

If the intent is “disable = hide the widget, but keep per‑item mute state” and “re‑enable = clear mutes so everything can resurface”, you could gate the items update on the flag and avoid touching items when simply turning the widget off:

-  disable: (value: boolean) => {
-    universalChecklistStore.setState((state) => ({
-      ...state,
-      widget: { ...state.widget, disable: value },
-      items: Object.entries(state.items).reduce(
-        (acc, [id, value]) => ({ ...acc, [id]: { ...value, mutedAt: undefined } }),
-        state.items
-      ),
-    }));
-  },
+  disable: (disabled: boolean) => {
+    universalChecklistStore.setState((state) => ({
+      ...state,
+      widget: { ...state.widget, disable: disabled },
+      items: disabled
+        ? state.items
+        : Object.entries(state.items).reduce(
+            (acc, [id, item]) => ({ ...acc, [id]: { ...item, mutedAt: undefined } }),
+            state.items
+          ),
+    }));
+  },

This keeps existing mute state when you flip the global “disable” on, and only clears mutedAt when you explicitly re‑enable the widget. Please adjust the condition if your intended semantics differ.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6925f08 and 1cfaf81.

📒 Files selected for processing (2)
  • code/core/src/cli/globalSettings.ts (1 hunks)
  • code/core/src/shared/checklist-store/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/core/src/cli/globalSettings.ts
🧰 Additional context used
🧬 Code graph analysis (1)
code/core/src/shared/checklist-store/index.ts (2)
code/core/src/cli/globalSettings.ts (1)
  • globalSettings (57-76)
code/core/src/manager-api/stores/checklist.ts (1)
  • universalChecklistStore (9-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: daily
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (1)
code/core/src/shared/checklist-store/index.ts (1)

6-26: Universal checklist store typing and core operations look solid

Deriving StoreState from the persisted globalSettings().value.checklist shape and exposing it via UNIVERSAL_CHECKLIST_STORE_OPTIONS gives a single source of truth for checklist state, while the createChecklistStore helpers perform focused, immutable updates for accept/done/skip/reset and preserve all unrelated fields. This wiring matches how UniversalStore.create is used in manager-api/stores/checklist.ts, and nothing here stands out as a correctness or layering problem.

Also applies to: 30-57

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
code/core/src/manager-api/stores/__mocks__/checklist.ts (1)

1-6: Consolidate imports from the same module.

Lines 1-5 and line 6 both import from '../../../shared/checklist-store'. Consider merging them into a single import statement for better readability.

Apply this diff:

-import {
-  type StoreEvent,
-  type StoreState,
-  createChecklistStore,
-} from '../../../shared/checklist-store';
-import { UNIVERSAL_CHECKLIST_STORE_OPTIONS } from '../../../shared/checklist-store';
+import {
+  type StoreEvent,
+  type StoreState,
+  createChecklistStore,
+  UNIVERSAL_CHECKLIST_STORE_OPTIONS,
+} from '../../../shared/checklist-store';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cfaf81 and 2d2f717.

📒 Files selected for processing (1)
  • code/core/src/manager-api/stores/__mocks__/checklist.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
code/core/src/manager-api/stores/__mocks__/checklist.ts (1)
code/core/src/shared/checklist-store/index.ts (4)
  • StoreState (6-10)
  • StoreEvent (15-21)
  • UNIVERSAL_CHECKLIST_STORE_OPTIONS (23-26)
  • createChecklistStore (30-78)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: daily
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (1)
code/core/src/manager-api/stores/__mocks__/checklist.ts (1)

9-13: LGTM!

The mock correctly creates a functional UniversalStore instance with production options and wires it to the checklistStore via the factory function. This approach enables integration-style testing with real store behavior.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
code/core/src/manager/components/sidebar/Menu.stories.tsx (2)

26-35: Provide a shared, fuller api stub for useMenu to avoid runtime errors and duplication

The api objects passed into useMenu only define shortcut/docs helpers, but useMenu also builds menu items that call methods like changeSettingsTab, toggleNav, toggleToolbar, togglePanel, jumpToComponent, jumpToStory, and emit (per Menu.tsx). Clicking those items in these stories will currently throw, and the three inline api literals duplicate a lot of logic.

Consider defining a single shared managerApi stub that covers the Menu-related API surface and reusing it in both ManagerContext and all useMenu calls, overriding only what each story cares about (e.g., shortcuts or WhatsNew):

-const managerContext: any = {
-  state: {},
-  api: {
-    getData: fn().mockName('api::getData'),
-    getIndex: fn().mockName('api::getIndex'),
-    getUrlState: fn().mockName('api::getUrlState'),
-    navigate: fn().mockName('api::navigate'),
-    on: fn().mockName('api::on'),
-  },
-};
+const managerApi = {
+  getData: fn().mockName('api::getData'),
+  getIndex: fn().mockName('api::getIndex'),
+  getUrlState: fn().mockName('api::getUrlState'),
+  navigate: fn().mockName('api::navigate'),
+  on: fn().mockName('api::on'),
+  changeSettingsTab: fn().mockName('api::changeSettingsTab'),
+  toggleNav: fn().mockName('api::toggleNav'),
+  togglePanel: fn().mockName('api::togglePanel'),
+  toggleToolbar: fn().mockName('api::toggleToolbar'),
+  jumpToComponent: fn().mockName('api::jumpToComponent'),
+  jumpToStory: fn().mockName('api::jumpToStory'),
+  emit: fn().mockName('api::emit'),
+  getShortcutKeys: () => ({}),
+  getAddonsShortcuts: () => ({}),
+  versionUpdateAvailable: () => false,
+  isWhatsNewUnread: () => false,
+  getDocsUrl: () => 'https://storybook.js.org/docs/',
+};
+
+const managerContext: any = {
+  state: {},
+  api: managerApi,
+};

Then reuse it in the stories, overriding only what differs:

-    const menu = useMenu({
-      api: {
-        // @ts-expect-error (Converted from ts-ignore)
-        getShortcutKeys: () => ({}),
-        getAddonsShortcuts: () => ({}),
-        versionUpdateAvailable: () => false,
-        isWhatsNewUnread: () => false,
-        getDocsUrl: () => 'https://storybook.js.org/docs/',
-      },
+    const menu = useMenu({
+      api: managerApi,
       showToolbar: false,
       isPanelShown: false,
       isNavShown: false,
       enableShortcuts: false,
     });
-    const menu = useMenu({
-      api: {
-        // @ts-expect-error (invalid)
-        getShortcutKeys: () => ({
+    const menu = useMenu({
+      api: {
+        ...managerApi,
+        // Override shortcuts for this story
+        getShortcutKeys: () => ({
           shortcutsPage: ['⌘', '⇧​', ','],
           toggleNav: ['⌥', 'S'],
           togglePanel: ['⌥', 'A'],
           toolbar: ['⌥', 'T'],
           panelPosition: ['⌥', 'D'],
           fullScreen: ['⌥', 'F'],
           search: ['⌥', 'K'],
           prevComponent: ['⌥', '↑'],
           nextComponent: ['⌥', '↓'],
           prevStory: ['⌥', '←'],
           nextStory: ['⌥', '→'],
           collapseAll: ['⌥', '⇧', '↑'],
         }),
-        getAddonsShortcuts: () => ({}),
-        versionUpdateAvailable: () => false,
-        isWhatsNewUnread: () => false,
-        getDocsUrl: () => 'https://storybook.js.org/docs/',
-      },
+      },
       showToolbar: false,
       isPanelShown: false,
       isNavShown: false,
       enableShortcuts: true,
     });
-    const menu = useMenu({
-      api: {
-        // @ts-expect-error (invalid)
-        getShortcutKeys: () => ({}),
-        getAddonsShortcuts: () => ({}),
-        versionUpdateAvailable: () => false,
-        isWhatsNewUnread: () => true,
-        getDocsUrl: () => 'https://storybook.js.org/docs/',
-      },
+    const menu = useMenu({
+      api: {
+        ...managerApi,
+        isWhatsNewUnread: () => true,
+      },
       showToolbar: false,
       isPanelShown: false,
       isNavShown: false,
       enableShortcuts: false,
     });

This keeps the stories robust if useMenu starts using more of the API, and reduces duplication.

Also applies to: 89-102, 139-165, 191-204


44-62: Reset mockStore between story runs instead of only mutating it in beforeEach

beforeEach mutates the shared internal_universalChecklistStore without resetting it, so checklist state can leak across story renders. Also, async isn’t needed here. Prefer seeding the store and returning a cleanup function that restores the initial checklist state after each run.

Example adjustment (tweak the reset state if your real initial loaded value differs):

-  beforeEach: async () => {
-    mockStore.setState({
-      loaded: true,
-      widget: {},
-      items: {
-        ...initialState.items,
-        controls: { status: 'accepted' },
-        renderComponent: { status: 'done' },
-        viewports: { status: 'skipped' },
-      },
-    });
-  },
+  beforeEach: () => {
+    mockStore.setState({
+      loaded: true,
+      widget: {},
+      items: {
+        ...initialState.items,
+        controls: { status: 'accepted' },
+        renderComponent: { status: 'done' },
+        viewports: { status: 'skipped' },
+      },
+    });
+
+    return () => {
+      // Reset checklist store for subsequent stories
+      mockStore.setState({
+        loaded: false,
+        ...initialState,
+      });
+    };
+  },
code/core/src/core-server/utils/checklist.ts (2)

84-90: Guard telemetry calls and handle rejections to avoid unhandled promise errors

telemetry('onboarding-checklist', …) is called on every state change without error handling, and it may be invoked with an empty payload when neither items nor widget actually changed. If telemetry rejects, this can lead to unhandled promise rejections from within the store callback.

You can both avoid no-op telemetry and make failures non-fatal:

-      const changedValues = Object.entries(state.items).filter(
-        ([key, value]) => value !== previousState.items[key as keyof typeof state.items]
-      );
-      telemetry('onboarding-checklist', {
-        ...(changedValues.length > 0 ? { items: Object.fromEntries(changedValues) } : {}),
-        ...(!deepEqual(state.widget, previousState.widget) ? { widget: state.widget } : {}),
-      });
+      const changedValues = Object.entries(state.items).filter(
+        ([key, value]) => value !== previousState.items[key as keyof typeof state.items]
+      );
+      const payload: Record<string, unknown> = {
+        ...(changedValues.length > 0 ? { items: Object.fromEntries(changedValues) } : {}),
+        ...(!deepEqual(state.widget, previousState.widget) ? { widget: state.widget } : {}),
+      };
+
+      if (Object.keys(payload).length > 0) {
+        void telemetry('onboarding-checklist', payload).catch((error) => {
+          logger.error('Failed to send onboarding checklist telemetry');
+          logger.error(error);
+        });
+      }

This keeps telemetry fully fire‑and‑forget while ensuring errors are contained and noisy no-op calls are skipped. Based on learnings


35-44: Handle async persistence errors in setState helpers to avoid unhandled rejections

settings.save() is async (it’s awaited in globalSettings), and cache.set() from the filesystem cache is also very likely async. Calling them without await or .catch() inside the onStateChange path means any write failure (permissions, disk issues) can surface as an unhandled promise rejection, even though initialization itself is protected by the outer try/catch.

You can keep these as fire‑and‑forget writes but explicitly swallow/log failures:

       const setState = ({
         items = state.items,
         widget = state.widget,
       }: {
         items?: typeof state.items;
         widget?: typeof state.widget;
       }) => {
         settings.value.checklist = { items, widget };
-        settings.save();
+        void settings.save().catch((error) => {
+          logger.error('Failed to persist user checklist state');
+          logger.error(error);
+        });
       };
@@
       cache.get<Pick<ChecklistState, 'items'>>('state').then((cachedState) => {
         const state = { items: cachedState?.items ?? {} };
-        const setState = ({ items }: Pick<ChecklistState, 'items'>) =>
-          cache.set('state', { items });
+        const setState = ({ items }: Pick<ChecklistState, 'items'>) => {
+          void cache.set('state', { items }).catch((error: unknown) => {
+            logger.error('Failed to persist project checklist state');
+            logger.error(error);
+          });
+        };
         return [state, setState] as const;
       }),

This matches the repo’s preference for .catch() on fire‑and‑forget side effects and prevents noisy/unhandled rejections while keeping the store callback sync. Based on learnings

Also applies to: 48-52

🧹 Nitpick comments (1)
code/core/src/shared/checklist-store/checklistData.tsx (1)

34-52: Avoid recomputing the dark theme for every CodeSnippet render

CodeSnippet calls convert(themes.dark) on every render. It’s cheap, but since the theme is static you can hoist it once to avoid unnecessary work and object identity changes:

-const CodeSnippet = (props: ComponentProps<typeof SyntaxHighlighter>) => (
-  <ThemeProvider theme={convert(themes.dark)}>
+const darkCodeTheme = convert(themes.dark);
+
+const CodeSnippet = (props: ComponentProps<typeof SyntaxHighlighter>) => (
+  <ThemeProvider theme={darkCodeTheme}>
     <CodeWrapper>
       <SyntaxHighlighter {...props} />
     </CodeWrapper>
   </ThemeProvider>
 );

This keeps behavior the same while reducing redundant conversions.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08bdbd0 and bf693a3.

📒 Files selected for processing (14)
  • code/addons/onboarding/src/manager.tsx (1 hunks)
  • code/core/src/cli/globalSettings.ts (3 hunks)
  • code/core/src/core-server/utils/checklist.ts (1 hunks)
  • code/core/src/manager-api/modules/versions.ts (3 hunks)
  • code/core/src/manager-api/tests/versions.test.js (2 hunks)
  • code/core/src/manager/components/sidebar/ChecklistWidget.stories.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/Menu.stories.tsx (6 hunks)
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx (4 hunks)
  • code/core/src/manager/container/Menu.stories.tsx (2 hunks)
  • code/core/src/manager/settings/GuidePage.stories.tsx (1 hunks)
  • code/core/src/manager/settings/GuidePage.tsx (1 hunks)
  • code/core/src/shared/checklist-store/checklistData.state.ts (1 hunks)
  • code/core/src/shared/checklist-store/checklistData.tsx (1 hunks)
  • code/core/src/shared/checklist-store/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/shared/checklist-store/checklistData.state.ts
  • code/core/src/manager/components/sidebar/ChecklistWidget.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/GuidePage.tsx
  • code/core/src/cli/globalSettings.ts
  • code/core/src/shared/checklist-store/index.ts
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-09-17T07:32:14.512Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32484
File: code/core/src/core-server/utils/open-in-browser.ts:6-6
Timestamp: 2025-09-17T07:32:14.512Z
Learning: When reviewing async functions that perform side effects like opening browsers, `.catch()` is preferred over `await` if the operation should be non-blocking. This prevents unhandled Promise rejections while keeping the operation as fire-and-forget.

Applied to files:

  • code/core/src/core-server/utils/checklist.ts
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/core/src/shared/checklist-store/checklistData.tsx
  • code/addons/onboarding/src/manager.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-11-05T09:36:55.944Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.

Applied to files:

  • code/core/src/shared/checklist-store/checklistData.tsx
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/addons/onboarding/src/manager.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
🧬 Code graph analysis (5)
code/core/src/manager-api/tests/versions.test.js (1)
code/core/src/manager-api/modules/versions.ts (1)
  • init (72-186)
code/core/src/core-server/utils/checklist.ts (2)
code/core/src/shared/checklist-store/index.ts (4)
  • StoreState (12-15)
  • StoreEvent (20-26)
  • UNIVERSAL_CHECKLIST_STORE_OPTIONS (28-31)
  • ChecklistState (7-9)
code/core/src/cli/globalSettings.ts (1)
  • globalSettings (62-81)
code/core/src/shared/checklist-store/checklistData.tsx (7)
code/core/src/theming/convert.ts (1)
  • convert (78-201)
code/core/src/shared/checklist-store/index.ts (1)
  • ItemId (17-17)
code/core/src/shared/checklist-store/checklistData.state.ts (1)
  • initialState (3-29)
code/core/src/manager-api/root.tsx (2)
  • API (95-108)
  • API_StoryEntry (308-308)
code/addons/onboarding/src/constants.ts (1)
  • ADDON_ONBOARDING_CHANNEL (2-2)
code/core/src/manager/components/TourGuide/TourGuide.tsx (1)
  • TourGuide (38-171)
code/addons/vitest/src/constants.ts (1)
  • STORYBOOK_ADDON_TEST_CHANNEL (13-13)
code/addons/onboarding/src/manager.tsx (5)
code/addons/onboarding/src/Onboarding.tsx (1)
  • Onboarding (47-331)
code/addons/onboarding/src/Survey.tsx (1)
  • Survey (11-47)
code/addons/onboarding/src/constants.ts (2)
  • ADDON_ID (1-1)
  • ADDON_CONTROLS_ID (5-5)
code/core/src/manager-api/stores/checklist.ts (1)
  • checklistStore (14-14)
code/core/src/manager-api/index.ts (1)
  • checklistStore (22-22)
code/core/src/manager/components/sidebar/Menu.stories.tsx (4)
code/core/src/manager-api/root.tsx (1)
  • ManagerContext (79-79)
code/core/src/manager/components/layout/LayoutProvider.tsx (1)
  • LayoutProvider (29-63)
code/core/src/shared/checklist-store/checklistData.state.ts (1)
  • initialState (3-29)
code/core/src/manager/container/Menu.tsx (1)
  • useMenu (64-271)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: daily
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (18)
code/core/src/manager-api/tests/versions.test.js (3)

229-229: LGTM: Minor capitalization consistency fix.

Lowercasing "url" improves consistency with other test descriptions in the suite.


256-278: LGTM: Asset path test coverage added.

This test correctly verifies that asset: true produces a docs-assets URL with the major.minor version segment. The test addresses the past review comment requesting coverage for the new assets parameter.


280-304: LGTM: Subpath handling with asset flag verified.

This test correctly validates that subpaths are appended directly (without a trailing slash) when asset: true, which is appropriate for file paths like images. The version segment (7.2) is correctly included based on the current version (7.2.5).

code/core/src/manager-api/modules/versions.ts (2)

51-63: LGTM: Clear documentation added for the asset parameter.

The JSDoc comment now clearly documents the asset option and its purpose, addressing the past review request from ndelangen. The signature change to an options object provides better extensibility and clarity.


102-127: LGTM: Asset URL logic correctly implemented.

The implementation correctly handles the asset flag:

  • Base URL branches to docs-assets when asset: true (line 107)
  • Version segment appended for assets using major.minor format (lines 109-111)
  • Subpath handling distinguishes between file paths (assets, no trailing slash) and directory paths (non-assets, trailing slash) (line 127)

The logic is clean, maintains backward compatibility, and the test coverage confirms correctness.

code/addons/onboarding/src/manager.tsx (3)

1-11: LGTM! Proper React 18 upgrade and modular imports.

The migration to React 18's createRoot API, lazy-loaded components, and constants import are all appropriate improvements.


13-23: LGTM! Clean React 18 rendering implementation.

The persistent root pattern and idempotent container creation are appropriate. The Suspense wrapper correctly handles the lazy-loaded components.


46-67: LGTM! Well-structured onboarding flow with appropriate guards.

The flow correctly:

  • Waits for stories to load before checking existence
  • Provides helpful warnings when button stories are missing
  • Guards against narrow screens for better UX
  • Passes survey completion state to the Onboarding component

The getElementById fallback at line 50 is a reasonable defensive check, though it may not always find the element depending on render timing.

code/core/src/manager/components/sidebar/Menu.stories.tsx (1)

9-16: Imports for ManagerContext, test helpers, and checklist store are consistent with usage

The new imports cleanly match how ManagerContext, storybook/test helpers, initialState, and mockStore are used later in the stories; no changes suggested here.

code/core/src/core-server/utils/checklist.ts (3)

56-62: State merge and loaded flag wiring looks correct

Merging userState and projectState into the universal store via toMerged and then marking { loaded: true } is a clean way to overlay persisted state on the initial options while keeping typing honest with satisfies StoreState. No changes needed here.


64-83: Good separation of user-local vs project-local persistence

The split of state.items into projectValues (status === 'done') and userValues (accepted/skipped plus mutedAt for any item) nicely enforces the intended persistence boundary between project and user settings. The handling of mutedAt as user-local even for done items is especially clear. Looks solid.


17-27: Initialization error handling is appropriate

Wrapping the entire initialization flow in a try/catch and logging failures via logger.error ensures that checklist setup issues don’t break server startup, while still surfacing diagnostics. This is a reasonable failure mode for an auxiliary feature like onboarding.

Also applies to: 92-95

code/core/src/shared/checklist-store/checklistData.tsx (6)

54-120: Checklist data typing is robust and future-proof

Deriving ItemId from typeof initialState['items'] and then expressing ChecklistData in terms of those item IDs (including after, available, and subscribe signatures) is a solid way to keep the data structure aligned with the store’s state shape. This should make future item additions and refactors much safer.


122-134: subscribeToIndex helper correctly handles initial and subsequent index availability

The helper’s pattern of checking the condition immediately, then once on PREVIEW_INITIALIZED, and then on each STORY_INDEX_INVALIDATED (via the returned unsubscribe) is a clean abstraction for index-based completion. Using api.getIndex()?.entries || {} also guards nicely against undefined index instances. No changes needed.


136-345: Basics section logic aligns with described criteria

The basics items (“guided tour”, survey, initial render, more components/stories, and “What’s new”) wire their available, subscribe, and criteria fields consistently:

  • Gating the onboarding items on both example stories and addon registration is sensible.
  • The STORY_FINISHED-based completion for renderComponent matches the description.
  • Index-based counts for components and stories correctly ignore example entries.

Content and links look coherent and framework-agnostic. No functional issues spotted here.


348-477: Development section events and content look consistent

The development items hook into the right core events:

  • controls listens to STORY_ARGS_UPDATED, which matches “Change a story with Controls”.
  • viewports listens to UPDATE_GLOBALS and checks globals.viewport, correctly reflecting viewport changes.
  • organizeStories uses subscribeToIndex and checks for titles containing /, which is a reasonable heuristic for grouped components.

The accompanying copy and examples (including the hierarchy snippet) are accurate and nicely aligned with current Storybook patterns.


479-987: Testing section wiring matches intended behaviors

The testing section’s items map well to their criteria:

  • Install steps (installVitest, installA11y, installChromatic, installDocs) use experimental_getRegisteredAddons() to detect registration, which is consistent with the rest of the codebase, and the copy explicitly tells users to restart Storybook after installation.
  • runTests leverages internal_universalTestProviderStore.onStateChange to detect a succeeded test-provider state.
  • accessibilityTests and coverage subscribe to STORYBOOK_ADDON_TEST_CHANNEL and check payload.config.a11y / payload.config.coverage, matching how the Vitest addon reports its config.
  • visualTests uses the Chromatic addon’s event channel for build starts, consistent with the install criteria and UI copy.

Overall, the event wiring, addon IDs, and user-facing descriptions are coherent; no changes recommended here.


989-1209: Documentation section items correctly reflect docs and index behavior

The documentation items:

  • Detect addon-docs installation via experimental_getRegisteredAddons and the docs addon ID.
  • Use subscribeToIndex to look for autodocs tags and MDX doc entries (type === 'docs'), which matches how the index is structured.
  • Provide MDX and autodocs snippets plus links to the relevant docs, and “publish Storybook” guidance is consistent with current workflows.

The structure and wiring look sound; no functional or semantic HTML issues stand out in this section.

Comment on lines +33 to 40
const hasCompletedSurvey = await new Promise<boolean>((resolve) => {
const unsubscribe = checklistStore.onStateChange(({ loaded, items }) => {
if (loaded) {
unsubscribe();
resolve(items.onboardingSurvey.status === 'accepted');
}
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add defensive checks and timeout for store loading.

This promise-based store check has two critical issues:

  1. Line 37: Direct property access to items.onboardingSurvey without existence check could throw a runtime error if the property is missing or undefined.
  2. No timeout: If the store never emits a loaded state, this promise will hang indefinitely, blocking the addon registration.

Apply this diff to add defensive checks and a timeout:

-  const hasCompletedSurvey = await new Promise<boolean>((resolve) => {
+  const hasCompletedSurvey = await Promise.race([
+    new Promise<boolean>((resolve) => {
-    const unsubscribe = checklistStore.onStateChange(({ loaded, items }) => {
-      if (loaded) {
-        unsubscribe();
-        resolve(items.onboardingSurvey.status === 'accepted');
-      }
-    });
+      const unsubscribe = checklistStore.onStateChange(({ loaded, items }) => {
+        if (loaded) {
+          unsubscribe();
+          resolve(items.onboardingSurvey?.status === 'accepted');
+        }
+      });
+    }),
+    new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 5000))
-  });
+  ]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const hasCompletedSurvey = await new Promise<boolean>((resolve) => {
const unsubscribe = checklistStore.onStateChange(({ loaded, items }) => {
if (loaded) {
unsubscribe();
resolve(items.onboardingSurvey.status === 'accepted');
}
});
});
const hasCompletedSurvey = await Promise.race([
new Promise<boolean>((resolve) => {
const unsubscribe = checklistStore.onStateChange(({ loaded, items }) => {
if (loaded) {
unsubscribe();
resolve(items.onboardingSurvey?.status === 'accepted');
}
});
}),
new Promise<boolean>((resolve) => setTimeout(() => resolve(false), 5000))
]);

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (5)
code/core/src/manager/components/sidebar/Menu.stories.tsx (4)

141-167: Incomplete api stubs will cause runtime errors when menu items are clicked.

Same issue as lines 91-104: the api object is missing action methods that useMenu tries to call. This story will also throw at runtime if menu items are clicked.


53-64: beforeEach hook still doesn't follow cleanup pattern and is unnecessarily async.

This duplicates the previous review's concern: the beforeEach hook should return a cleanup function rather than directly calling setState to ensure proper state reset between story renders.

Additionally, the async keyword is unnecessary since there are no await calls inside the function.

Apply this diff:

-  beforeEach: async () => {
-    mockStore.setState({
+  beforeEach: () => {
+    return () => {
+      mockStore.setState({
+        loaded: true,
+        widget: {},
+        items: {
+          ...initialState.items,
+          controls: { status: 'accepted' },
+          renderComponent: { status: 'done' },
+          viewports: { status: 'skipped' },
+        },
+      });
+    };
-      loaded: true,
-      widget: {},
-      items: {
-        ...initialState.items,
-        controls: { status: 'accepted' },
-        renderComponent: { status: 'done' },
-        viewports: { status: 'skipped' },
-      },
-    });
   },

91-104: Incomplete api stubs will cause runtime errors when menu items are clicked.

This duplicates the previous review's concern: the api object passed to useMenu is missing action methods (changeSettingsTab, toggleNav, toggleToolbar, togglePanel, jumpToComponent, jumpToStory, emit) that useMenu tries to call in menu item onClick handlers. Clicking those menu items in this story will throw at runtime.


193-206: Incomplete api stubs will cause runtime errors when menu items are clicked.

Same issue as lines 91-104 and 141-167: the api object is missing action methods. This story will also throw at runtime if menu items are clicked.

code/core/src/manager/components/sidebar/Sidebar.stories.tsx (1)

5-5: Use globalThis instead of importing from @storybook/global.

To avoid adding unnecessary dependencies, use globalThis directly instead of importing global from @storybook/global. This import is used on lines 192-193 and 195 for accessing CONFIG_TYPE.

Based on learnings.

Apply this diff:

-import { global } from '@storybook/global';

Then update the usage on lines 192-193 and 195:

-    const configType = global.CONFIG_TYPE;
-    global.CONFIG_TYPE = 'PRODUCTION';
+    const configType = globalThis.CONFIG_TYPE;
+    globalThis.CONFIG_TYPE = 'PRODUCTION';
     return () => {
-      global.CONFIG_TYPE = configType;
+      globalThis.CONFIG_TYPE = configType;
     };
🧹 Nitpick comments (1)
code/core/src/manager/settings/GuidePage.stories.tsx (1)

14-18: Address the TODO comment for hard-coded version.

The hard-coded version "10.0" in the docs URL should be resolved to use the actual major.minor version of the latest release dynamically.

Do you want me to open a new issue to track this task, or would you like suggestions on how to resolve this dynamically?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf693a3 and 5a6b7fa.

📒 Files selected for processing (3)
  • code/core/src/manager/components/sidebar/Menu.stories.tsx (6 hunks)
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx (4 hunks)
  • code/core/src/manager/settings/GuidePage.stories.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-10-01T15:24:01.060Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
🧬 Code graph analysis (3)
code/core/src/manager/components/sidebar/Sidebar.stories.tsx (2)
code/core/src/manager/manager-stores.mock.ts (1)
  • internal_universalChecklistStore (52-61)
code/core/src/shared/checklist-store/checklistData.state.ts (1)
  • initialState (3-29)
code/core/src/manager/settings/GuidePage.stories.tsx (2)
code/core/src/manager/settings/GuidePage.tsx (1)
  • GuidePage (37-67)
code/core/src/shared/checklist-store/checklistData.state.ts (1)
  • initialState (3-29)
code/core/src/manager/components/sidebar/Menu.stories.tsx (3)
code/core/src/manager/components/layout/LayoutProvider.tsx (1)
  • LayoutProvider (29-63)
code/core/src/shared/checklist-store/checklistData.state.ts (1)
  • initialState (3-29)
code/core/src/manager/container/Menu.tsx (1)
  • useMenu (64-271)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: daily
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (5)
code/core/src/manager/components/sidebar/Menu.stories.tsx (3)

9-15: LGTM: Imports support new ManagerContext and checklist integration.

The added imports are correctly used throughout the file to support the new ManagerContext decoration and mock store initialization.


26-37: LGTM: Manager context mock is appropriate for test isolation.

The mock managerContext provides necessary API method stubs for ManagerContext.Provider and is correctly separated from the api objects passed directly to useMenu.


46-52: LGTM: Decorator structure correctly provides contexts.

The decorator nesting provides ManagerContext and LayoutProvider in the correct order for stories to access necessary contexts.

code/core/src/manager/components/sidebar/Sidebar.stories.tsx (2)

13-17: LGTM! Necessary imports for checklist functionality.

The imports of initialState and internal_universalChecklistStore are required to support the new onboarding checklist feature in the sidebar.


119-131: LGTM! Checklist store initialization is appropriate.

The beforeEach setup properly initializes the checklist store with test data, consistent with the pattern used in GuidePage.stories.tsx. The mock state includes realistic item statuses (accepted, done, skipped) for testing the sidebar's checklist functionality.

@ghengeveld ghengeveld dismissed ndelangen’s stale review November 20, 2025 23:51

Feedback has been processed.

@storybook-bot
Copy link
Contributor

Failed to publish canary version of this pull request, triggered by @yannbf. See the failed workflow run at: https://github.com/storybookjs/storybook/actions/runs/19563973710

@ghengeveld ghengeveld merged commit b5f93c3 into next Nov 21, 2025
124 of 126 checks passed
@ghengeveld ghengeveld deleted the onboarding-checklist branch November 21, 2025 08:14
@ghengeveld ghengeveld added the needs qa Indicates that this needs manual QA during the upcoming minor/major release label Nov 22, 2025
@yannbf yannbf removed the needs qa Indicates that this needs manual QA during the upcoming minor/major release label Nov 25, 2025
@coderabbitai coderabbitai bot mentioned this pull request Dec 16, 2025
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:daily Run the CI jobs that normally run in the daily job. feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Tracking]: Onboarding for Testing

8 participants