Skip to content

Conversation

@ghengeveld
Copy link
Member

@ghengeveld ghengeveld commented Oct 28, 2025

See #32644
Telescopes on #32795

What I did

This PR handles all the UI improvements made for the onboarding guide / checklist since #32795.
Checklist data and completion behavior goes in #32799.

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

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

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-32874-sha-fd398904. 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-32874-sha-fd398904
Triggered by @ghengeveld
Repository storybookjs/storybook
Branch onboarding-guide
Commit fd398904
Datetime Mon Nov 3 13:59:26 UTC 2025 (1762178366)
Workflow run 19037159322

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=32874

Summary by CodeRabbit

  • New Features

    • Read-only button variant for non-interactive controls
    • FocusRing family (outline, proxy, target) for focus highlighting and hash-target focus
    • TextFlip animated text transitions
    • Hook-driven Checklist UI with progress, mute/accept/done controls and development-only Guide page
  • Improvements

    • Collapsible supports disabled and external control
    • Card visuals, outline animations and lifecycle refined
    • Listbox accessibility, icon handling and layout improved; menu/sidebar UI reorganized and integrated with onboarding

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This PR restructures the onboarding/checklist system and store schema, introduces a FocusRing family with hash-based focus navigation and useLocationHash, updates Listbox and Button APIs, refactors sidebar/menu construction and many manager UI components, and adds several new UI primitives and stories.

Changes

Cohort / File(s) Summary
Settings & Checklist persistence
code/core/src/cli/globalSettings.ts, code/core/src/core-server/utils/checklist.ts
Checklist schema changed: completed removed and replaced by muted (boolean
Checklist store & API
code/core/src/shared/checklist-store/index.ts, code/core/src/manager/manager-stores.ts, code/core/src/manager/manager-stores.mock.ts
Store state reshaped: added loaded, muted, accepted, done; removed completed. Events and public methods updated (added accept, done, mute; removed complete). Factory createChecklistStore removed; stores implemented as explicit objects.
Checklist UI / Data / Hook
code/core/src/manager/components/sidebar/useChecklist.ts, code/core/src/manager/components/sidebar/ChecklistModule.tsx, code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx, code/core/src/manager/settings/Checklist/Checklist.tsx, code/core/src/manager/settings/Checklist/checklistData.tsx, code/core/src/manager/settings/Checklist/Checklist.stories.tsx
Rewrote checklist UI to be hook-driven: new useChecklist (exports ChecklistItem type) derives open/ready/next items, progress, and exposes accept/skip/reset/done/mute flows; Checklist component and checklistData adapted to new item shape (after, action, subscribe); ChecklistModule rebuilt as a collapsible, interactive panel; stories updated.
Core settings export
code/core/src/cli/globalSettings.ts
userSettingSchema updated so inferred public Settings.value type includes optional checklist.muted and checklist.accepted (checklist object optional); completed removed.
Focus ring & hash navigation
code/core/src/components/components/FocusRing/FocusRing.tsx, code/core/src/components/components/FocusRing/FocusRing.stories.tsx, code/core/src/components/shared/useLocationHash.ts
Added FocusOutline, FocusProxy (hash-aware), FocusRing (visibility lifecycle), FocusTarget (hash-driven focus/scroll) and useLocationHash hook (poll-based hash monitor). New stories added.
FocusProxy removal
code/core/src/components/components/FocusProxy/FocusProxy.tsx, code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx
Removed standalone FocusProxy implementation and its story; functionality consolidated in FocusRing module.
Components exports updated
code/core/src/components/index.ts, code/core/src/manager/globals/exports.ts
Re-export FocusProxy/FocusOutline/FocusRing/FocusTarget and add useLocationHash and ListboxIcon to public/global exports.
Menu / Sidebar / useMenu refactor
code/core/src/manager/container/Menu.tsx, code/core/src/manager/container/Menu.stories.tsx, code/core/src/manager/container/Sidebar.tsx, code/core/src/manager/components/sidebar/Menu.tsx, code/core/src/manager/components/sidebar/Menu.stories.tsx, code/core/src/manager/components/sidebar/Sidebar.tsx, code/core/src/manager/components/sidebar/Sidebar.stories.tsx
useMenu signature refactored to object param { api, showToolbar, isPanelShown, isNavShown, enableShortcuts }; return type changed to NormalLink[][]; menu construction moved into Consumer; Sidebar now accepts menu prop; SidebarMenuList switched from TooltipLinkList to Listbox-based rendering; stories updated with beforeEach store initializations.
Listbox internals & stories
code/core/src/components/components/Listbox/Listbox.tsx, code/core/src/components/components/Listbox/Listbox.stories.tsx
Listbox container changed (ul -> div), ListboxItem gained active? prop and styling, ListboxAction/ListboxText simplified, new ListboxIcon export added; stories updated with new items and inline actions.
Button changes
code/core/src/components/components/Button/Button.tsx
ButtonProps extended: added readOnly?: boolean, as widened to include 'div'; interactive styles and pointer behavior gated by readOnly; onClick typing normalized to ButtonHTMLAttributes.
Card forwardRef & stories split
code/core/src/components/components/Card/Card.tsx, code/core/src/components/components/Card/Card.stories.tsx
Card converted to forwardRef with CardProps (outlineAnimation/color/outlineStyles), Outline styling/lifecycle classes added; stories split from one All into many named exports.
Collapsible API & stories
code/core/src/components/components/Collapsible/Collapsible.tsx, code/core/src/components/components/Collapsible/Collapsible.stories.tsx
useCollapsible signature changed to (collapsed?, disabled?), returns expanded object (isDisabled, toggleProps); Collapsible accepts disabled and external state; stories renamed/added and play interactions updated.
Explorer / Tree / Search / RefBlocks / Testing tweaks
code/core/src/manager/components/sidebar/Explorer.tsx, code/core/src/manager/components/sidebar/Tree.tsx, code/core/src/manager/components/sidebar/TreeNode.tsx, code/core/src/manager/components/sidebar/SearchResults.tsx, code/core/src/manager/components/sidebar/RefBlocks.tsx, code/core/src/manager/components/sidebar/TestingModule.tsx
Minor structural and styling adjustments: removed ChecklistModule render in Explorer; removed isMain prop and Container wrapper in Tree; simplified RootNode styling; removed top margin in SearchResults title; removed ts-expect-error in RefBlocks; adjusted HoverCard and removed will-change from some styles.
TextFlip component
code/core/src/manager/components/TextFlip.tsx, code/core/src/manager/components/TextFlip.stories.tsx
New TextFlip animation component added with stories (Default, Increasing, Decreasing).
Guide / Settings routing & page
code/core/src/manager/settings/GuidePage.tsx, code/core/src/manager/settings/GuidePage.stories.tsx, code/core/src/manager/settings/index.tsx
GuidedTourPage renamed to GuidePage; GuidePage now uses useChecklist and renders Checklist; route/tab moved from guided-tour to guide and gated by CONFIG_TYPE === 'DEVELOPMENT'; stories updated.
Core server checklist persistence update
code/core/src/core-server/utils/checklist.ts
Persistence now initializes and persists only muted, accepted, and skipped fields to settings.value.checklist and sets loaded = true in the in-memory store state.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant HashMonitor
    participant useLocationHash
    participant FocusTarget
    participant FocusRing
    participant DOM

    Browser->>HashMonitor: location.hash polled (100ms)
    HashMonitor->>useLocationHash: emit current hash
    useLocationHash->>FocusTarget: update hash value
    FocusTarget->>FocusTarget: compare hash -> targetHash?
    alt match
        FocusTarget->>DOM: focus(targetElement) and scrollIntoView()
        FocusTarget->>FocusRing: set active = true
        FocusRing->>FocusRing: visible = true (start highlight timer)
        Note over FocusRing: highlightDuration timer set
        FocusRing->>DOM: render outline
        FocusRing-->>FocusRing: on timeout -> visible = false
        FocusRing->>DOM: hide outline
    else no match
        FocusTarget->>FocusRing: ensure active = false
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Areas needing extra attention:
    • useChecklist hook: subscription lifecycle, cleanup, memoization and derived nextItems logic.
    • ChecklistModule / Checklist component and checklistData mapping to new item shape (after, action, subscribe) and acceptance/done/mute flows.
    • Checklist store + core-server persistence: migration, interplay between saved settings shape and in-memory store.
    • FocusRing / useLocationHash: polling approach, timing of focus/scroll, reduced-motion handling.
    • useMenu/sidebar changes: verify the new NormalLink[][] shape matches all consumers and menu prop wiring through Consumer.
    • Public API surface changes: Button readOnly/as widening, Card forwardRef/props, Collapsible hook signature updates.

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 990d21e and 3cac5c1.

📒 Files selected for processing (4)
  • code/core/src/components/components/FocusRing/FocusRing.tsx (1 hunks)
  • code/core/src/manager/manager-stores.mock.ts (2 hunks)
  • code/core/src/manager/manager-stores.ts (2 hunks)
  • code/core/src/shared/checklist-store/index.ts (1 hunks)

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

@nx-cloud
Copy link

nx-cloud bot commented Oct 28, 2025

View your CI Pipeline Execution ↗ for commit 3cac5c1

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

☁️ Nx Cloud last updated this comment at 2025-11-14 16:38:23 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: 15

Caution

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

⚠️ Outside diff range comments (7)
code/core/src/components/components/Button/Button.tsx (4)

42-51: Guard clicks when disabled/readOnly (also for as="a").

Currently clicks still propagate, animate, and call onClick. Block interaction at the handler level.

Apply this diff:

-    const handleClick = (event: SyntheticEvent) => {
-      if (onClick) {
-        onClick(event);
-      }
-
-      if (animation === 'none') {
-        return;
-      }
-      setIsAnimating(true);
-    };
+    const handleClick = (event: SyntheticEvent) => {
+      if (disabled || readOnly) {
+        event.preventDefault?.();
+        event.stopPropagation?.();
+        return;
+      }
+      onClick?.(event);
+      if (animation !== 'none') setIsAnimating(true);
+    };

63-76: Wire a11y and interactivity props (aria-disabled, tabIndex, role) and drop handler when not interactive.

Make non-interactive elements non-focusable; add semantics for div/label.

Apply this diff:

-      <StyledButton
+      {/*
+        Treat readOnly as non-interactive UI: aria-disabled + no handler.
+        For div/label, expose role=button only when interactive.
+      */}
+      <StyledButton
         as={Comp}
         ref={ref}
         variant={variant}
         size={size}
         padding={padding}
         disabled={disabled}
-        readOnly={readOnly}
+        readOnly={readOnly}
         active={active}
         animating={isAnimating}
         animation={animation}
-        onClick={handleClick}
+        role={as === 'div' || as === 'label' ? 'button' : undefined}
+        tabIndex={disabled || readOnly ? -1 : undefined}
+        aria-disabled={disabled || readOnly || undefined}
+        onClick={disabled || readOnly ? undefined : handleClick}
         {...props}
       />

83-90: Prevent leaking styling props (incl. readOnly) to the DOM.

isPropValid doesn’t filter tag-specific props; readOnly and size may end up on a <button>/<div>. Explicitly exclude styling-only props.

Apply this diff:

-const StyledButton = styled('button', {
-  shouldForwardProp: (prop) => isPropValid(prop),
-})<
+const FILTER_PROPS = new Set([
+  'variant',
+  'size',
+  'padding',
+  'active',
+  'animating',
+  'animation',
+  'readOnly',
+]);
+
+const StyledButton = styled('button', {
+  shouldForwardProp: (prop) => isPropValid(prop) && !FILTER_PROPS.has(String(prop)),
+})<

62-77: Stories and RTL tests for readOnly behavior not yet implemented—required before merge.

The review requirements have not been met:

  1. Missing readOnly stories: Button.stories.tsx lacks stories demonstrating disabled vs readOnly behavior across as variants (existing stories are Base, Variants, PseudoStates, Active, WithIcon, IconOnly, Sizes, Disabled, WithHref, Animated—none cover readOnly).

  2. Missing RTL tests: No test files exist for the Button component. RTL tests asserting onClick is prevented when readOnly or disabled are required.

  3. onClick handler issue: The handleClick function (lines 42–45) does not check the readOnly or disabled state before invoking the callback, allowing onClick to fire even when readOnly is true. This must be guarded.

  4. No tabIndex implementation: No tabIndex handling found in the component for focus behavior tests.

Add the missing stories and tests, and prevent onClick execution when readOnly or disabled is true.

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

321-331: Aria label inverted (Expand/Collapse) on root toggle

Label reads “Expand” when fully expanded and “Collapse” when not, which is reversed.

Apply:

-            aria-label={isFullyExpanded ? 'Expand' : 'Collapse'}
+            aria-label={isFullyExpanded ? 'Collapse' : 'Expand'}

398-405: Guard PRELOAD_ENTRIES against empty children

When a component/story has no children, children[0] is undefined and will be emitted.

Apply:

-          onMouseEnter={() => {
-            if (item.type === 'component' || item.type === 'story') {
-              api.emit(PRELOAD_ENTRIES, {
-                ids: [children[0]],
-                options: { target: refId },
-              });
-            }
-          }}
+          onMouseEnter={() => {
+            if ((item.type === 'component' || item.type === 'story') && children.length > 0) {
+              api.emit(PRELOAD_ENTRIES, {
+                ids: [children[0]],
+                options: { target: refId },
+              });
+            }
+          }}
code/core/src/components/components/Card/Card.stories.tsx (1)

8-10: Add React type-only import for ReactNode

Avoid relying on a global React namespace in types.

Apply:

-import preview from '../../../../../.storybook/preview';
-import { Card } from './Card';
+import preview from '../../../../../.storybook/preview';
+import { Card } from './Card';
+import type { ReactNode } from 'react';
 
-const Contents = ({ children }: { children: React.ReactNode }) => (
+const Contents = ({ children }: { children: ReactNode }) => (
   <div style={{ padding: 16 }}>{children}</div>
 );
🧹 Nitpick comments (22)
code/core/src/components/components/Button/Button.tsx (5)

21-21: Ref type is too narrow for polymorphic “as”.

When as="a" | "div" | "label", the ref isn’t an HTMLButtonElement. Widen to HTMLElement (or adopt a proper polymorphic typing later).

Apply this diff:

-export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
+export const Button = forwardRef<HTMLElement, ButtonProps>(

103-103: Cursor for readOnly should be ‘default’, not ‘inherit’.

‘inherit’ can pick up odd parents. Use ‘default’ to signal non-interactive.

Apply this diff:

-    cursor: disabled ? 'not-allowed' : readOnly ? 'inherit' : 'pointer',
+    cursor: disabled ? 'not-allowed' : readOnly ? 'default' : 'pointer',

184-187: Keep focus-visible even when readOnly, or remove from tab order.

You gate &:focus under !readOnly, yet the element may still be focusable. Either always render a focus ring or set tabIndex={-1} (proposed above) so it’s not focusable.

Apply this diff to always allow the focus ring:

-            ...(!readOnly && {
-              '&:hover': {
+            ...(!readOnly && {
+              '&:hover': {
                 color: theme.barHoverColor,
                 background: transparentize(0.86, theme.barHoverColor),
               },
-
-              '&:active': {
+              '&:active': {
                 color: theme.barSelectedColor,
                 background: transparentize(0.9, theme.barSelectedColor),
               },
-
-              '&:focus': {
-                boxShadow: `${rgba(theme.barHoverColor, 1)} 0 0 0 1px inset`,
-                outline: 'none',
-              },
             }),
+            '&:focus': {
+              boxShadow: `${rgba(theme.barHoverColor, 1)} 0 0 0 1px inset`,
+              outline: 'none',
+            },
-    ...(!readOnly && {
-      '&:hover': {
+    ...(!readOnly && {
+      '&:hover': {
         /* ... */
       },
-      '&:active': {
+      '&:active': {
         /* ... */
       },
-
-      '&:focus': {
-        boxShadow: `${rgba(theme.color.secondary, 1)} 0 0 0 1px inset`,
-        outline: 'none',
-      },
     }),
+    '&:focus': {
+      boxShadow: `${rgba(theme.color.secondary, 1)} 0 0 0 1px inset`,
+      outline: 'none',
+    },

Also applies to: 256-259


262-265: Don’t animate icons on readOnly.

Skip SVG animation when non-interactive.

Apply this diff:

-      animation:
-        animating && animation !== 'none' ? `${theme.animation[animation]} 1000ms ease-out` : '',
+      animation:
+        !readOnly && animating && animation !== 'none'
+          ? `${theme.animation[animation]} 1000ms ease-out`
+          : '',

8-19: Consider proper polymorphic typing for as + element-specific props.

With as="a", consumers can’t pass href (not in ButtonHTMLAttributes<HTMLButtonElement>). A discriminated union or a polymorphic helper type would improve DX and safety.

If you want, I can provide a minimal PolymorphicComponentProps<As, OwnProps> pattern.

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

10-10: Consider removing debug styling.

The red border appears to be for development/debugging purposes. Consider removing it or making it conditional for Chromatic visual testing only.

Apply this diff if the border is not needed:

-    <div style={{ display: 'inline-block', border: '1px solid red' }}>
+    <div style={{ display: 'inline-block' }}>
       <TextFlip {...args} />
     </div>

Or keep it only for visual testing:

-    <div style={{ display: 'inline-block', border: '1px solid red' }}>
+    <div style={{ display: 'inline-block' }}>
       <TextFlip {...args} />
     </div>
code/core/src/manager/components/sidebar/Sidebar.tsx (1)

161-175: Use isDevelopment prop consistently for gating

You compute isDevelopment already; use it instead of referencing global.CONFIG_TYPE directly.

Apply:

-            {!isLoading && global.CONFIG_TYPE === 'DEVELOPMENT' && <ChecklistModule />}
+            {!isLoading && isDevelopment && <ChecklistModule />}
code/core/src/manager/container/Menu.stories.tsx (1)

9-37: Ensure mock store init actually runs in stories

beforeEach on CSF meta may not execute in Storybook runtime. Prefer a decorator that seeds the store per render.

Apply:

 export default {
   component: TooltipLinkList,
   decorators: [
-    (storyFn) => (
+    (storyFn) => {
+      // Seed mock store before each story render
+      mockStore.setState({
+        loaded: true,
+        muted: false,
+        completed: ['add-component'],
+        skipped: ['add-5-10-components'],
+      });
+      return (
         <div
           style={{
             height: '300px',
           }}
         >
           <WithTooltip placement="top" startOpen tooltip={storyFn()}>
             <div>Tooltip</div>
           </WithTooltip>
         </div>
-    ),
+      );
+    },
   ],
-  beforeEach: async () => {
-    mockStore.setState({
-      loaded: true,
-      muted: false,
-      completed: ['add-component'],
-      skipped: ['add-5-10-components'],
-    });
-  },
   excludeStories: ['links'],
 } satisfies Meta<typeof TooltipLinkList>;
code/core/src/manager/settings/GuidePage.tsx (1)

50-63: Replace obsolete

; keep semantic action

Use CSS centering instead of <center>. Keep Link if you want inline style, but avoid obsolete tags.

Apply:

+const FooterNote = styled.div({
+  textAlign: 'center',
+});
@@
-      {checklist.muted ? (
-        <center>
+      {checklist.muted ? (
+        <FooterNote>
           Want to see this in the sidebar?{' '}
           <Link onClick={() => checklist.mute(false)}>Show in sidebar</Link>
-        </center>
+        </FooterNote>
       ) : (
-        <center>
+        <FooterNote>
           Don&apos;t want to see this in the sidebar?{' '}
           <Link onClick={() => checklist.mute(checklist.allItems.map(({ id }) => id))}>
             Remove from sidebar
           </Link>
-        </center>
+        </FooterNote>
       )}
code/core/src/components/components/FocusRing/FocusRing.stories.tsx (1)

35-46: Consider renaming "Proxy" story to avoid shadowing global.

The export name Proxy shadows the JavaScript global Proxy object. While this is a named export and less problematic than a local variable, it could still cause confusion.

Consider renaming:

-export const Proxy = meta.story({
+export const ProxyExample = meta.story({
   render: () => (
code/core/src/manager/settings/index.tsx (2)

46-53: Remove meaningless key prop on TabButton.

key="id" is a literal string and not needed outside list rendering.

Apply:

-            key="id"

122-124: Gate the route with enableWhatsNew to avoid dangling navigation.

The tab is hidden when enableWhatsNew is false, but the route remains active. Keep them consistent.

-        <Route path="whats-new">
-          <WhatsNewPage key="whats-new" />
-        </Route>
+        {enableWhatsNew && (
+          <Route path="whats-new">
+            <WhatsNewPage key="whats-new" />
+          </Route>
+        )}
code/core/src/components/components/Card/Card.tsx (1)

52-56: Avoid leaking color prop to the DOM; use a non-DOM prop name.

Using color as a custom prop risks forwarding it to the DOM. The diff above renames it to outlineColorKey. Keep non-DOM prop names (prefixed or renamed) to avoid React warnings.

Also applies to: 63-66

code/core/src/manager/container/Menu.tsx (1)

47-50: Avoid readOnly on a button; use a non-interactive container.

Buttons without actions are confusing for a11y and keyboard users. The diff above swaps it for a non-interactive div with status semantics.

Also applies to: 91-96

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

55-70: Use isomorphic timeout type

NodeJS.Timeout can conflict in DOM builds. Prefer ReturnType<typeof setTimeout>.

Apply:

-    let timeout: NodeJS.Timeout;
+    let timeout: ReturnType<typeof setTimeout>;
code/core/src/manager/settings/Checklist/Checklist.tsx (2)

269-285: Remove unused nodeRef to reduce allocations

nodeRef is created but never attached or read. Drop it and the related import.

Apply:

-import React, { createRef, useMemo } from 'react';
+import React, { useEffect, useMemo } from 'react';
@@
 type ChecklistItem = ChecklistData['sections'][number]['items'][number] & {
   isCompleted: boolean;
   isLockedBy: string[];
   isSkipped: boolean;
-  nodeRef?: React.RefObject<HTMLLIElement>;
 };
@@
-            const nodeRef = createRef<HTMLLIElement>();
-            return [item.id, { ...item, isCompleted, isLockedBy, isSkipped, nodeRef }];
+            return [item.id, { ...item, isCompleted, isLockedBy, isSkipped }];

333-336: Optional: reflect hash-target state in ListboxItem active styling

Passing active improves affordance when an item is hash-targeted.

Apply:

-                      <ListboxItem key={item.id}>
+                      <ListboxItem key={item.id} active={itemId === locationHash}>
code/core/src/components/components/Collapsible/Collapsible.tsx (4)

25-26: Avoid requiring collapsed via type intersection.

Intersecting with ComponentProps<typeof CollapsibleContent> can accidentally make collapsed required. Using Omit<..., 'id' | 'collapsed' | 'aria-hidden'> (as in the diff above) prevents this and keeps collapsed optional.

Please build TS to ensure Collapsed story (which omits collapsed) type-checks.


29-41: Remove duplicate aria-hidden.

aria-hidden is set in both Collapsible and CollapsibleContent. Keep it in one place to avoid confusion (the diff above keeps it in CollapsibleContent only).


78-84: Enhance a11y and flexibility of toggleProps.

Spreading disabled on non-button elements is ineffective. Add aria-disabled alongside disabled so it’s useful for non-native controls.

-  const toggleProps = {
-    disabled,
-    onClick: toggleCollapsed,
+  const toggleProps = {
+    disabled,
+    'aria-disabled': !!disabled,
+    onClick: toggleCollapsed,
     'aria-controls': contentId,
     'aria-expanded': !isCollapsed,
   } as const;

67-75: Reconsider default stopPropagation().

Always stopping propagation can interfere with parent click handlers. Either make this behavior opt-in (e.g., a parameter) or document it clearly.

Confirm no parent components rely on click bubbling from summary controls.

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

39-41: Interaction sanity-check.

Toggled clicks “Close” (default open) which collapses content; good smoke test. Consider adding an assertion on aria-expanded for robustness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

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

📒 Files selected for processing (40)
  • code/core/src/cli/globalSettings.ts (1 hunks)
  • code/core/src/components/components/Button/Button.tsx (4 hunks)
  • code/core/src/components/components/Card/Card.stories.tsx (1 hunks)
  • code/core/src/components/components/Card/Card.tsx (4 hunks)
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx (3 hunks)
  • code/core/src/components/components/Collapsible/Collapsible.tsx (2 hunks)
  • code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx (0 hunks)
  • code/core/src/components/components/FocusProxy/FocusProxy.tsx (0 hunks)
  • code/core/src/components/components/FocusRing/FocusRing.stories.tsx (1 hunks)
  • code/core/src/components/components/FocusRing/FocusRing.tsx (1 hunks)
  • code/core/src/components/components/Listbox/Listbox.stories.tsx (3 hunks)
  • code/core/src/components/components/Listbox/Listbox.tsx (4 hunks)
  • code/core/src/components/components/shared/useLocationHash.ts (1 hunks)
  • code/core/src/components/index.ts (2 hunks)
  • code/core/src/core-server/utils/checklist.ts (1 hunks)
  • code/core/src/manager/components/TextFlip.stories.tsx (1 hunks)
  • code/core/src/manager/components/TextFlip.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 (0 hunks)
  • code/core/src/manager/components/sidebar/Menu.stories.tsx (6 hunks)
  • code/core/src/manager/components/sidebar/Menu.tsx (2 hunks)
  • code/core/src/manager/components/sidebar/SearchResults.tsx (0 hunks)
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx (3 hunks)
  • code/core/src/manager/components/sidebar/Sidebar.tsx (5 hunks)
  • code/core/src/manager/components/sidebar/TestingModule.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/Tree.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/TreeNode.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/useChecklist.ts (1 hunks)
  • code/core/src/manager/container/Menu.stories.tsx (2 hunks)
  • code/core/src/manager/container/Menu.tsx (12 hunks)
  • code/core/src/manager/container/Sidebar.tsx (2 hunks)
  • code/core/src/manager/globals/exports.ts (3 hunks)
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx (2 hunks)
  • code/core/src/manager/settings/Checklist/Checklist.tsx (5 hunks)
  • code/core/src/manager/settings/Checklist/checklistData.tsx (7 hunks)
  • code/core/src/manager/settings/GuidePage.stories.tsx (1 hunks)
  • code/core/src/manager/settings/GuidePage.tsx (2 hunks)
  • code/core/src/manager/settings/index.tsx (3 hunks)
  • code/core/src/shared/checklist-store/index.ts (2 hunks)
💤 Files with no reviewable changes (4)
  • code/core/src/components/components/FocusProxy/FocusProxy.tsx
  • code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/SearchResults.tsx
🧰 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/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
  • code/core/src/components/components/shared/useLocationHash.ts
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.tsx
  • code/core/src/manager/settings/GuidePage.tsx
  • code/core/src/manager/components/sidebar/useChecklist.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/manager/components/sidebar/TestingModule.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/components/TextFlip.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/container/Sidebar.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/cli/globalSettings.ts
  • code/core/src/components/index.ts
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.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/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
  • code/core/src/components/components/shared/useLocationHash.ts
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.tsx
  • code/core/src/manager/settings/GuidePage.tsx
  • code/core/src/manager/components/sidebar/useChecklist.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/manager/components/sidebar/TestingModule.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/components/TextFlip.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/container/Sidebar.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/cli/globalSettings.ts
  • code/core/src/components/index.ts
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.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/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
  • code/core/src/components/components/shared/useLocationHash.ts
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.tsx
  • code/core/src/manager/settings/GuidePage.tsx
  • code/core/src/manager/components/sidebar/useChecklist.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/manager/components/sidebar/TestingModule.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/components/TextFlip.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/container/Sidebar.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/cli/globalSettings.ts
  • code/core/src/components/index.ts
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.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/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
  • code/core/src/components/components/shared/useLocationHash.ts
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.tsx
  • code/core/src/manager/settings/GuidePage.tsx
  • code/core/src/manager/components/sidebar/useChecklist.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/manager/components/sidebar/TestingModule.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/components/TextFlip.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/container/Sidebar.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/cli/globalSettings.ts
  • code/core/src/components/index.ts
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
PR: storybookjs/storybook#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to test-storybooks/** : Maintain test Storybook configurations under `test-storybooks/` for E2E and visual testing scenarios

Applied to files:

  • code/core/src/manager/settings/GuidePage.stories.tsx
🧬 Code graph analysis (23)
code/core/src/manager/components/sidebar/Sidebar.tsx (1)
code/core/src/manager/components/sidebar/ChecklistModule.tsx (1)
  • ChecklistModule (67-205)
code/core/src/manager/components/TextFlip.stories.tsx (1)
code/core/src/manager/components/TextFlip.tsx (1)
  • TextFlip (57-104)
code/core/src/components/components/shared/useLocationHash.ts (2)
code/renderers/web-components/template/components/Html.js (1)
  • globalThis (5-5)
code/core/src/components/index.ts (1)
  • useLocationHash (96-96)
code/core/src/manager/settings/Checklist/Checklist.stories.tsx (3)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
  • checklistData (7-243)
code/core/src/manager/manager-stores.mock.ts (1)
  • checklistStore (57-57)
code/core/src/manager/manager-stores.ts (1)
  • checklistStore (25-25)
code/core/src/components/components/FocusRing/FocusRing.stories.tsx (1)
code/core/src/components/components/FocusRing/FocusRing.tsx (3)
  • FocusRing (23-41)
  • FocusProxy (17-21)
  • FocusTarget (43-73)
code/core/src/manager/settings/GuidePage.tsx (2)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (12-57)
code/core/src/manager/settings/Checklist/Checklist.tsx (1)
  • Checklist (243-446)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
  • checklistData (7-243)
code/core/src/components/components/FocusRing/FocusRing.tsx (1)
code/core/src/components/components/shared/useLocationHash.ts (1)
  • useLocationHash (37-41)
code/core/src/manager/components/sidebar/TestingModule.tsx (1)
code/core/src/components/components/Card/Card.tsx (1)
  • Card (35-44)
code/core/src/manager/components/sidebar/ChecklistModule.tsx (6)
code/core/src/components/components/Collapsible/Collapsible.tsx (1)
  • Collapsible (12-37)
code/core/src/components/components/Listbox/Listbox.tsx (6)
  • ListboxButton (61-68)
  • Listbox (7-15)
  • ListboxItem (17-59)
  • ListboxAction (70-79)
  • ListboxIcon (96-103)
  • ListboxText (81-94)
code/core/src/manager-api/root.tsx (1)
  • useStorybookApi (294-297)
code/core/src/manager/components/TextFlip.tsx (1)
  • TextFlip (57-104)
code/core/src/manager/components/Transition.tsx (1)
  • Transition (6-12)
code/core/src/manager/manager-stores.ts (1)
  • checklistStore (25-25)
code/core/src/manager/components/sidebar/Menu.stories.tsx (1)
code/core/src/manager/container/Menu.tsx (1)
  • useMenu (59-257)
code/core/src/manager/components/sidebar/Menu.tsx (2)
code/core/src/components/components/Listbox/Listbox.tsx (5)
  • Listbox (7-15)
  • ListboxItem (17-59)
  • ListboxAction (70-79)
  • ListboxIcon (96-103)
  • ListboxText (81-94)
code/core/src/components/index.ts (5)
  • Listbox (48-48)
  • ListboxItem (52-52)
  • ListboxAction (49-49)
  • ListboxIcon (51-51)
  • ListboxText (53-53)
code/core/src/manager/components/TextFlip.tsx (1)
code/core/src/manager/components/Transition.tsx (1)
  • Transition (6-12)
code/core/src/components/components/Collapsible/Collapsible.tsx (1)
code/core/src/components/index.ts (1)
  • Collapsible (44-44)
code/core/src/manager/settings/GuidePage.stories.tsx (3)
code/core/src/manager/settings/GuidePage.tsx (1)
  • GuidePage (37-65)
code/core/src/manager-api/root.tsx (1)
  • ManagerContext (78-78)
code/core/src/manager/settings/Checklist/Checklist.stories.tsx (1)
  • Default (36-45)
code/core/src/manager/container/Menu.tsx (3)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (12-57)
code/core/src/components/components/Listbox/Listbox.tsx (2)
  • ListboxButton (61-68)
  • ListboxIcon (96-103)
code/core/src/manager-api/modules/shortcuts.ts (1)
  • getAddonsShortcuts (187-189)
code/core/src/manager/settings/index.tsx (1)
code/core/src/manager/settings/GuidePage.tsx (1)
  • GuidePage (37-65)
code/core/src/manager/settings/Checklist/Checklist.tsx (6)
code/core/src/manager/components/sidebar/useChecklist.ts (2)
  • ChecklistItem (8-8)
  • useChecklist (12-57)
code/core/src/components/components/shared/useLocationHash.ts (1)
  • useLocationHash (37-41)
code/core/src/components/components/FocusRing/FocusRing.tsx (2)
  • FocusProxy (17-21)
  • FocusTarget (43-73)
code/core/src/components/components/Listbox/Listbox.tsx (1)
  • ListboxItem (17-59)
code/core/src/components/components/Modal/Modal.styled.tsx (1)
  • Actions (112-116)
code/core/src/components/components/Button/Button.tsx (1)
  • Button (21-79)
code/core/src/components/components/Card/Card.stories.tsx (1)
code/core/src/components/components/Card/Card.tsx (1)
  • Card (35-44)
code/core/src/manager/container/Sidebar.tsx (2)
code/core/src/manager/container/Menu.tsx (1)
  • useMenu (59-257)
code/core/src/manager/manager-stores.ts (1)
  • experimental_useStatusStore (13-13)
code/core/src/components/components/Listbox/Listbox.tsx (1)
code/core/src/components/index.ts (5)
  • ListboxItem (52-52)
  • ListboxAction (49-49)
  • ListboxButton (50-50)
  • ListboxText (53-53)
  • ListboxIcon (51-51)
code/core/src/components/components/Collapsible/Collapsible.stories.tsx (1)
code/core/src/components/components/Collapsible/Collapsible.tsx (1)
  • useCollapsible (58-93)
code/core/src/components/components/Listbox/Listbox.stories.tsx (2)
code/core/src/components/components/Listbox/Listbox.tsx (4)
  • ListboxButton (61-68)
  • ListboxItem (17-59)
  • ListboxAction (70-79)
  • ListboxText (81-94)
code/core/src/components/index.ts (7)
  • ListboxButton (50-50)
  • ListboxItem (52-52)
  • ListboxAction (49-49)
  • ProgressSpinner (92-92)
  • Badge (28-28)
  • Form (66-66)
  • ListboxText (53-53)
🪛 Biome (2.1.2)
code/core/src/components/components/FocusRing/FocusRing.stories.tsx

[error] 35-35: Do not shadow the global "Proxy" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

code/core/src/manager/components/sidebar/Menu.tsx

[error] 80-80: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

code/core/src/components/components/Collapsible/Collapsible.tsx

[error] 17-17: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 59-59: This hook is being called indirectly and conditionally, but all hooks must be called in the exact same order in every component render.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 61-61: This hook is being called indirectly and conditionally, but all hooks must be called in the exact same order in every component render.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 67-67: This hook is being called indirectly and conditionally, but all hooks must be called in the exact same order in every component render.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 77-77: This hook is being called indirectly and conditionally, but all hooks must be called in the exact same order in every component render.

This is the call path until the hook.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

code/core/src/manager/container/Sidebar.tsx

[error] 59-59: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ 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: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (36)
code/core/src/manager/components/sidebar/TestingModule.tsx (1)

21-29: LGTM! Theme-aware styling improvement.

The refactoring to use a function-based style with the theme parameter enables dynamic shadow rendering that adapts to the theme. The CSS variable --card-box-shadow allows child components to access the computed shadow value.

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

18-24: Union type for muted field is correctly handled with proper type narrowing.

The schema extension is sound. The muted field's union type (boolean | string[]) is being properly consumed throughout the codebase:

  • The store's mute function uses Array.isArray(value) to distinguish between boolean (blanket muting) and array (selective item muting), properly merging array values with existing state
  • The UI component uses Array.isArray(muted) before accessing array methods like includes()
  • Initialization defaults to false when the value is undefined

The dual-use pattern (boolean for whole-checklist muting, array for selective item muting) is intentional and correctly implemented with type guards.

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

5-6: LGTM: Checklist store integration follows established patterns.

The addition of universalChecklistStore initialization in the global beforeEach hook and the import of global for production-mode testing are both consistent with the broader checklist integration across the codebase.

Also applies to: 13-13, 107-112


158-164: LGTM: Proper cleanup of global state modification.

The production-mode simulation correctly mutates global.CONFIG_TYPE and restores the original value via the cleanup function returned from beforeEach. This ensures test isolation.

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

1-104: LGTM: Well-implemented text flip animation component.

The component correctly uses react-transition-group to animate between text values, with proper support for:

  • Direction detection via localeCompare with numeric sorting
  • Accessibility via aria-hidden on stale content
  • Reduced motion preferences
  • Ref forwarding to avoid findDOMNode warnings

The dual-Transition pattern (one for entering, one for exiting) is the correct approach for simultaneous enter/exit animations.

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

91-93: LGTM: Styling adjustment for first tree node.

The CSS rule removes the top margin from the first RootNode, which aligns with the layout changes for the onboarding guide integration.

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

1-36: LGTM: Story setup follows established patterns.

The GuidePage story properly mocks the ManagerContext and initializes the checklist store state, consistent with other checklist-related story files in the codebase.

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

24-31: LGTM: Complete checklist store initialization.

The addition of loaded and muted fields aligns with the expanded checklist store schema and is consistent with other story files in this PR.

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

17-22: LGTM: Complete checklist store initialization with new fields.

The addition of loaded: true and muted (with proper fallback) extends the store state to match the updated schema. The immediate setting of loaded: true is appropriate since settings are loaded synchronously from the settings file.

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

1-3: LGTM: Public API expanded with new components and utilities.

The additions of FocusOutline, FocusRing, FocusTarget, ListboxIcon, and useLocationHash to the public exports are correctly present in the generated file. These exports are automatically discovered since they are exported from code/core/src/components/index.ts, which is bundled as part of the auto-generation process in code/core/scripts/generate-source-files.ts. No manual generator configuration changes are required—the mechanism is fully automatic.

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

37-50: Nice: unified state via useChecklist

Switching to useChecklist keeps Guide and Sidebar in sync. Looks good.

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

46-54: Public API expansion looks good

Exporting Focus components, ListboxIcon, and useLocationHash aligns with usage across manager UI. No issues spotted.

Also applies to: 96-97

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

716-720: Stale hasOrphans references resolved; spacing regression requires manual verification

  • ✓ No stale hasOrphans prop references found in codebase
  • ✓ Orphan hoisting logic and isOrphan prop remain intact
  • ⚠ Cannot verify whether styled Container had conditional orphan margin or if visual regression occurs — the original Container definition is not accessible. Manual testing of sidebar spacing with hoisted orphans is required to confirm no regression.
code/core/src/components/components/shared/useLocationHash.ts (1)

37-41: LGTM!

The hook correctly subscribes to the hash monitor, handles cleanup, and returns the hash without the leading # character.

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

4-12: LGTM!

The new Listbox-related imports are correctly added and align with the refactored menu rendering.

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

3-3: LGTM!

The checklistStore import is correctly added and used in the story configuration.


36-44: LGTM!

The story args correctly merge checklist data and store, with explicit state fields for testing different scenarios.

code/core/src/components/components/FocusRing/FocusRing.stories.tsx (3)

20-33: LGTM!

The Active and Temporary stories correctly demonstrate the FocusRing component with different duration behaviors.


48-64: LGTM!

The Target story correctly demonstrates hash-based focus behavior with appropriate timing for the play function.


65-67: LGTM!

The beforeEach hook correctly resets the hash state between story runs to ensure consistent test behavior.

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

13-13: LGTM!

The mock store setup correctly initializes checklist state for consistent story behavior across all tests.

Also applies to: 32-39


66-79: LGTM!

All three stories correctly updated to use the new useMenu object-based signature, consistently passing the required properties.

Also applies to: 108-134, 157-170

code/core/src/manager/container/Sidebar.tsx (2)

36-53: LGTM!

The mapper refactor correctly exposes the necessary properties for menu construction while maintaining clean separation of concerns.


57-73: LGTM!

The Consumer render correctly computes the menu using the new useMenu signature and passes all required props to SidebarComponent. The Biome warning about hook usage is a false positive—the Consumer render prop is effectively the component body, and hook usage here is valid.

code/core/src/manager/components/sidebar/useChecklist.ts (3)

8-10: LGTM!

The type extraction and allItems computation are clean and efficient.


12-27: LGTM!

The hook setup correctly handles checklist state with proper memoization. The Array.isArray(muted) check on line 24 defensively handles cases where muted might have different types.


29-56: LGTM with consideration for property spreading.

The computed values correctly implement the round-robin selection logic for next items and progress calculation. The return statement spreads multiple objects (checklistData, checklistStore, state) which could potentially cause property name collisions, but this appears intentional for the consumer API.

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

3-3: LGTM!

The Badge and ProgressSpinner imports are correctly added and used in the story examples.


20-22: LGTM!

The aria-label addition improves accessibility for the icon-only button.


35-49: LGTM!

The new story items effectively demonstrate integration with Badge and ProgressSpinner components.


50-62: LGTM!

The active prop correctly moved to ListboxItem and the checkbox label text is properly wrapped in ListboxText for consistent styling.

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

12-15: Widened sibling selector may cause unintended borders

Changing to '& + *' affects any following sibling, not just another Listbox. If the intent was only stacked Listboxes, revert to a narrower selector.

Apply if appropriate:

-  '& + *': {
+  '& + &': {
     borderTop: `1px solid ${theme.appBorderColor}`,
   },
code/core/src/components/components/FocusRing/FocusRing.tsx (1)

17-21: Confirm :has() support or add a non-:has fallback

&:has(#id:focus-visible) is great but not universally reliable in older engines. Consider a fallback (e.g., also styling when the proxy itself is focus-visible).

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

14-20: Structured, content‑rich checklist with sequencing looks good

Action objects, after dependencies, and JSX content improve UX and clarity. Ensure the consumer invokes predicate functions; otherwise these items won’t auto-complete. See my Checklist.tsx comment for a concrete fix.

Also applies to: 24-41, 45-75, 83-104, 106-125, 127-148, 150-172, 174-195, 203-214, 216-227, 229-239

code/core/src/components/components/Collapsible/Collapsible.stories.tsx (2)

7-14: Stories align with new API; type-only import is correct.

Use of toggleProps and ReturnType<typeof useCollapsible> is tidy and keeps runtime bundle clean.


43-54: Controlled story looks good.

Local state toggles and play action validate the controlled path.

Run:

@yannbf yannbf added ci:merged Run the CI jobs that normally run when merged. bug feature request core and removed bug labels Nov 3, 2025
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: 5

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/components/components/Button/Button.tsx (1)

41-74: Guard clicks when readOnly is true.

readOnly currently only changes styles; the handler still fires, so consumers can’t rely on it to freeze actions. Bail out (and suppress animation) when readOnly is set to prevent unintended side effects.

-    const handleClick: ButtonProps['onClick'] = (event) => {
-      if (onClick) {
-        onClick(event);
-      }
+    const handleClick: ButtonProps['onClick'] = (event) => {
+      if (disabled || readOnly) {
+        event.preventDefault();
+        return;
+      }
+      onClick?.(event);
♻️ Duplicate comments (8)
code/core/src/components/components/Collapsible/Collapsible.tsx (2)

62-97: Controlled/uncontrolled behavior remains unclear.

The hook still mixes controlled and uncontrolled patterns: useEffect syncs when collapsed changes (suggesting controlled), but toggleCollapsed directly mutates local state even when collapsed is provided (suggesting uncontrolled). This hybrid approach can lead to confusing behavior where prop updates and local toggles compete.

Past review recommended choosing one of:

  1. Fully controlled: Accept collapsed + onCollapsedChange, remove local state
  2. Fully uncontrolled: Use defaultCollapsed for initial value only, remove sync effect
  3. Controllable: Support both via standard useControllableState pattern

The current implementation may work for this PR's scope, but consider clarifying the intended pattern before broader adoption.


32-35: Prevent prop collisions and remove duplicate aria-hidden.

Spreading {...props} before id, collapsed, and aria-hidden allows callers to override these critical props, which could break the component's controlled behavior. Additionally, aria-hidden is set both here (line 35) and in CollapsibleContent (line 44), creating a redundant duplicate.

The past review suggested using Omit<ComponentProps<typeof CollapsibleContent>, 'id' | 'collapsed' | 'aria-hidden'> in the type definition (line 25) and removing the duplicate aria-hidden here.

Apply this diff to prevent collisions and remove the duplicate:

 } & ComponentProps<typeof CollapsibleContent>) => {
+} & Omit<ComponentProps<typeof CollapsibleContent>, 'id' | 'collapsed' | 'aria-hidden'>) => {
   const internalState = useCollapsible(collapsed, disabled);
-  const state = providedState || internalState;
+  const state = providedState ?? internalState;
   return (
     <>
       {typeof summary === 'function' ? summary(state) : summary}
       <CollapsibleContent
         {...props}
         id={state.contentId}
         collapsed={state.isCollapsed}
-        aria-hidden={state.isCollapsed}
       >
         {typeof children === 'function' ? children(state) : children}
       </CollapsibleContent>
code/core/src/components/components/FocusRing/FocusRing.tsx (1)

34-38: Guard timeout creation when highlightDuration is undefined.

This issue was flagged in a previous review but remains unresolved. Currently, a timeout is created even when highlightDuration is undefined, causing setTimeout to schedule with a 0ms delay. This creates unnecessary overhead and unexpected behavior.

Apply this diff to guard the timeout:

  useEffect(() => {
    setVisible(active);
-   const timeout = setTimeout(setVisible, highlightDuration, false);
-   return () => clearTimeout(timeout);
+   if (typeof highlightDuration === 'number' && highlightDuration > 0) {
+     const timeout = setTimeout(() => setVisible(false), highlightDuration);
+     return () => clearTimeout(timeout);
+   }
  }, [active, highlightDuration]);
code/core/src/components/components/Button/Button.tsx (1)

9-10: Don’t add div to as without button semantics.

Rendering the control as a <div> means it no longer has a native button role or keyboard activation (Enter/Space), which breaks WCAG 2.1.1. Please either drop 'div' from the union or normalize the props so non-button hosts get role="button", tabIndex=0, and key handlers (and remove them when the control is non-interactive). (bocoup.github.io)

@@
   const Comp = asChild ? Slot : as;
+  const isNativeButtonHost = Comp === 'button' || Comp === Slot;
+  const interactive = !disabled && !readOnly;
@@
-      <StyledButton
+      <StyledButton
         as={Comp}
         ref={ref}
@@
         onClick={handleClick}
+        {...(!isNativeButtonHost &&
+          interactive && {
+            role: props.role ?? 'button',
+            tabIndex: props.tabIndex ?? 0,
+            onKeyDown: (event) => {
+              if (event.key === 'Enter' || event.key === ' ') {
+                event.preventDefault();
+                handleClick?.(event);
+              }
+            },
+          })}
+        {...(!isNativeButtonHost && !interactive && {
+          role: props.role,
+          tabIndex: props.tabIndex ?? -1,
+        })}
         {...props}
       />
code/core/src/manager/components/sidebar/ChecklistModule.tsx (2)

85-171: Fix invalid nested list semantics (li inside li).

Lines 85-98 nest ListboxItem components inside another ListboxItem. Since ListboxItem renders <li> elements (per the relevant code snippets), this creates invalid HTML and breaks accessibility. This issue was flagged in a previous review and remains unaddressed.

Flatten the structure so each ListboxItem corresponds to a single <li>. Use non-list containers (e.g., div or wrapper elements) for inner layout grouping.

Also applies to: 127-156


67-91: Add production guard for dev-only checklist module.

The module links to /settings/guide (lines 91, 132, 180), which is a dev-only route according to the previous review. The module should either be hidden in production or link to an alternative destination. This issue was flagged previously and remains unaddressed.

Apply this diff to hide the module in production:

+import { global } from '@storybook/global';
+
 export const ChecklistModule = () => {
   const api = useStorybookApi();
   const { loaded, allItems, nextItems, progress, mute } = useChecklist();
+
+  if (global.CONFIG_TYPE !== 'DEVELOPMENT') return null;

Also applies to: 132-134, 180-180

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

6-12: Clarify muted semantics; boolean vs list leads to inconsistent behavior.

The union type boolean | Array<TaskId> for muted creates ambiguity. Based on the useChecklist.isReady implementation (which only checks Array.isArray(muted)), a boolean true won't suppress items as intended. Consider using a discriminated union or separate flags (mutedAll: boolean and mutedIds: TaskId[]) to make the intent explicit and ensure all consumers handle both cases correctly.

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

17-27: Prevent active prop from leaking to the DOM.

The active prop is style-only but will be forwarded to the <div> element, causing React DOM attribute warnings. Use shouldForwardProp to filter it out.

Apply this diff:

-export const ListboxItem = styled.div<{ active?: boolean }>(({ active, theme }) => ({
+export const ListboxItem = styled('div', {
+  shouldForwardProp: (prop) => prop !== 'active',
+})<{ active?: boolean }>(({ active, theme }) => ({
   display: 'flex',
   alignItems: 'center',
🧹 Nitpick comments (5)
code/core/src/components/components/FocusRing/FocusRing.tsx (1)

55-55: Consider using explicit number type for browser context.

ReturnType<typeof setTimeout> may resolve to NodeJS.Timeout in some TypeScript configurations. Since this is browser-only code, explicitly typing as number | undefined is clearer and avoids Node.js type references.

Apply this diff:

- let timeout: ReturnType<typeof setTimeout>;
+ let timeout: number | undefined;

As per a past review comment by valentinpalkovic.

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

38-42: Clarify prop spreading order and potential conflicts.

The story spreads both checklistData and checklistStore, then adds explicit accepted, done, and skipped fields. If checklistStore already contains these fields, the explicit values will override them. Verify this is the intended behavior for story controls.

code/core/src/manager/components/sidebar/useChecklist.ts (1)

42-70: Consider including callback dependencies instead of the callback itself.

The dependency array includes isOpen, which is a callback function. Typically, you should include the dependencies of the callback (accepted, done, skipped) rather than the function itself. While React will detect the function reference change when its dependencies change, it's more explicit to include the actual dependencies.

Apply this diff:

-  }, [api, index, loaded, allItems, isOpen]);
+  }, [api, index, loaded, allItems, accepted, done, skipped]);
code/core/src/manager/settings/Checklist/Checklist.tsx (1)

276-276: Remove unused nodeRef creation.

The nodeRef is created for each item but never consumed in the component rendering logic. Creating refs in useMemo is also problematic as they'll be recreated whenever dependencies change, breaking ref stability.

Apply this diff to remove the unused code:

           const isLockedBy =
             item.after?.filter((id) => !accepted.includes(id) && !done.includes(id)) ?? [];
           const isSkipped = skipped.includes(item.id);
-          const nodeRef = createRef<HTMLLIElement>();
-          return [item.id, { ...item, isAccepted, isDone, isLockedBy, isSkipped, nodeRef }];
+          return [item.id, { ...item, isAccepted, isDone, isLockedBy, isSkipped }];

And update the type definition:

 type ChecklistItem = ChecklistData['sections'][number]['items'][number] & {
   isAccepted: boolean;
   isDone: boolean;
   isLockedBy: string[];
   isSkipped: boolean;
-  nodeRef?: React.RefObject<HTMLLIElement>;
 };
code/core/src/manager/container/Menu.tsx (1)

92-96: Consider using a non-interactive element for progress display.

While as="div" changes the element type, the readOnly prop is not a valid div attribute and ListboxButton still applies button-like styling. For a truly non-interactive progress indicator, consider using a plain styled element or add appropriate ARIA attributes.

Replace with a semantic status indicator:

-      right: progress < 100 && (
-        <ListboxButton as="div" readOnly padding="none">
-          <ProgressCircle percentage={progress} running={false} size={16} width={1.5} />
-          {progress}%
-        </ListboxButton>
-      ),
+      right:
+        progress < 100 && (
+          <div
+            role="status"
+            aria-label={`Onboarding progress ${progress}%`}
+            style={{ display: 'flex', alignItems: 'center', gap: 4, padding: '4px 0' }}
+          >
+            <ProgressCircle percentage={progress} running={false} size={16} width={1.5} />
+            {progress}%
+          </div>
+        ),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38bfbc1 and fd39890.

📒 Files selected for processing (25)
  • code/core/src/cli/globalSettings.ts (1 hunks)
  • code/core/src/components/components/Button/Button.tsx (5 hunks)
  • code/core/src/components/components/Collapsible/Collapsible.tsx (3 hunks)
  • code/core/src/components/components/FocusRing/FocusRing.tsx (1 hunks)
  • code/core/src/components/components/Listbox/Listbox.stories.tsx (3 hunks)
  • code/core/src/components/components/Listbox/Listbox.tsx (4 hunks)
  • code/core/src/core-server/utils/checklist.ts (1 hunks)
  • code/core/src/manager/components/TextFlip.stories.tsx (1 hunks)
  • code/core/src/manager/components/TextFlip.tsx (1 hunks)
  • code/core/src/manager/components/preview/tools/zoom.tsx (0 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/Menu.stories.tsx (6 hunks)
  • code/core/src/manager/components/sidebar/Menu.tsx (2 hunks)
  • code/core/src/manager/components/sidebar/RefBlocks.tsx (0 hunks)
  • code/core/src/manager/components/sidebar/SearchResults.tsx (0 hunks)
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx (3 hunks)
  • code/core/src/manager/components/sidebar/useChecklist.ts (1 hunks)
  • code/core/src/manager/container/Menu.stories.tsx (2 hunks)
  • code/core/src/manager/container/Menu.tsx (12 hunks)
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx (2 hunks)
  • code/core/src/manager/settings/Checklist/Checklist.tsx (6 hunks)
  • code/core/src/manager/settings/Checklist/checklistData.tsx (8 hunks)
  • code/core/src/manager/settings/GuidePage.stories.tsx (1 hunks)
  • code/core/src/shared/checklist-store/index.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • code/core/src/manager/components/sidebar/SearchResults.tsx
  • code/core/src/manager/components/sidebar/RefBlocks.tsx
  • code/core/src/manager/components/preview/tools/zoom.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/cli/globalSettings.ts
  • code/core/src/manager/components/TextFlip.tsx
  • code/core/src/manager/settings/GuidePage.stories.tsx
🧰 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/components/sidebar/useChecklist.ts
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/components/TextFlip.stories.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/components/sidebar/useChecklist.ts
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/components/TextFlip.stories.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/components/sidebar/useChecklist.ts
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/components/TextFlip.stories.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/components/sidebar/useChecklist.ts
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
🧠 Learnings (19)
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to test-storybooks/** : Maintain test Storybook configurations under `test-storybooks/` for E2E and visual testing scenarios

Applied to files:

  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/container/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/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors

Applied to files:

  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in beforeEach blocks

Applied to files:

  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock implementations should be placed in beforeEach blocks

Applied to files:

  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to code/.storybook/** : Place internal UI Storybook configuration in `code/.storybook/` and maintain it there

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
📚 Learning: 2025-10-03T07:55:42.639Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/manager/container/Menu.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/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/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of beforeEach blocks

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : In application code, use Storybook loggers instead of `console.*` (client code: `storybook/internal/client-logger`; server code: `storybook/internal/node-logger`)

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to **/*.@(test|spec).{ts,tsx,js,jsx} : Unit tests should import and execute the functions under test rather than only asserting on syntax patterns

Applied to files:

  • code/core/src/manager/settings/Checklist/checklistData.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused

Applied to files:

  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together

Applied to files:

  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock at the highest level of abstraction needed

Applied to files:

  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies

Applied to files:

  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases

Applied to files:

  • code/core/src/manager/container/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required dependencies that the test subject uses

Applied to files:

  • code/core/src/manager/container/Menu.stories.tsx
🧬 Code graph analysis (12)
code/core/src/manager/components/sidebar/useChecklist.ts (3)
code/core/src/manager/settings/Checklist/checklistData.tsx (2)
  • ChecklistData (8-30)
  • checklistData (32-268)
code/core/src/manager-api/root.tsx (2)
  • useStorybookApi (294-297)
  • useStorybookState (290-293)
code/core/src/manager/components/sidebar/mockdata.large.ts (1)
  • index (12-26726)
code/core/src/manager/settings/Checklist/Checklist.stories.tsx (3)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
  • checklistData (32-268)
code/core/src/manager/manager-stores.mock.ts (1)
  • checklistStore (57-57)
code/core/src/manager/manager-stores.ts (1)
  • checklistStore (25-25)
code/core/src/manager/components/sidebar/ChecklistModule.tsx (6)
code/core/src/components/components/Collapsible/Collapsible.tsx (1)
  • Collapsible (12-41)
code/core/src/components/components/Listbox/Listbox.tsx (6)
  • ListboxButton (61-68)
  • Listbox (7-15)
  • ListboxItem (17-59)
  • ListboxAction (70-79)
  • ListboxIcon (96-103)
  • ListboxText (81-94)
code/core/src/manager-api/root.tsx (1)
  • useStorybookApi (294-297)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (17-100)
code/core/src/manager/components/TextFlip.tsx (1)
  • TextFlip (63-100)
code/core/src/manager/components/Transition.tsx (1)
  • Transition (6-12)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
code/core/src/manager-api/root.tsx (1)
  • API (94-107)
code/core/src/manager/components/sidebar/Menu.stories.tsx (1)
code/core/src/manager/container/Menu.tsx (1)
  • useMenu (60-262)
code/core/src/components/components/Listbox/Listbox.tsx (1)
code/core/src/components/index.ts (6)
  • Listbox (48-48)
  • ListboxItem (52-52)
  • ListboxAction (49-49)
  • ListboxButton (50-50)
  • ListboxText (53-53)
  • ListboxIcon (51-51)
code/core/src/components/components/FocusRing/FocusRing.tsx (2)
code/core/src/components/index.ts (5)
  • FocusOutline (46-46)
  • FocusProxy (46-46)
  • FocusRing (46-46)
  • FocusTarget (46-46)
  • useLocationHash (96-96)
code/core/src/components/components/shared/useLocationHash.ts (1)
  • useLocationHash (37-41)
code/core/src/core-server/utils/checklist.ts (1)
code/core/src/manager-api/tests/url.test.js (1)
  • store (143-151)
code/core/src/manager/settings/Checklist/Checklist.tsx (6)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
  • ChecklistData (8-30)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (17-100)
code/core/src/components/components/shared/useLocationHash.ts (1)
  • useLocationHash (37-41)
code/core/src/components/components/FocusRing/FocusRing.tsx (2)
  • FocusProxy (17-21)
  • FocusTarget (43-73)
code/core/src/components/components/Listbox/Listbox.tsx (1)
  • ListboxItem (17-59)
code/core/src/components/components/Button/Button.tsx (1)
  • Button (20-78)
code/core/src/components/components/Listbox/Listbox.stories.tsx (2)
code/core/src/components/components/Listbox/Listbox.tsx (4)
  • ListboxButton (61-68)
  • ListboxItem (17-59)
  • ListboxAction (70-79)
  • ListboxText (81-94)
code/core/src/components/index.ts (7)
  • ListboxButton (50-50)
  • ListboxItem (52-52)
  • ListboxAction (49-49)
  • ProgressSpinner (92-92)
  • Badge (28-28)
  • Form (66-66)
  • ListboxText (53-53)
code/core/src/manager/container/Menu.tsx (3)
code/core/src/manager-api/root.tsx (1)
  • API (94-107)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (17-100)
code/core/src/components/components/Listbox/Listbox.tsx (2)
  • ListboxButton (61-68)
  • ListboxIcon (96-103)
code/core/src/manager/components/TextFlip.stories.tsx (1)
code/core/src/manager/components/TextFlip.tsx (1)
  • TextFlip (63-100)
🔇 Additional comments (18)
code/core/src/manager/components/sidebar/Sidebar.stories.tsx (2)

107-113: Well-structured test initialization.

The checklist store initialization ensures all stories start with a consistent, known state. The setup is comprehensive and follows the established pattern for state management in this test file.


159-165: Excellent cleanup pattern for production mode simulation.

The production mode simulation properly captures and restores the original CONFIG_TYPE value, ensuring test isolation and preventing state pollution across stories.

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

1-8: LGTM: Imports support new functionality.

The addition of SyntheticEvent and useCallback appropriately supports the updated event handling and memoization in toggleCollapsed.


47-60: LGTM: Optional collapsed prop with sensible defaults.

The typed optional collapsed prop with a default value and the CSS transitions (including prefers-reduced-motion support) are well-implemented.

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

17-21: :has() selector is acceptable for Storybook core components.

Storybook targets modern browsers by default, and :has() is supported in current major browsers (Chrome/Edge v105+, Safari 15.4+, Firefox v121+). Since FocusRing is part of Storybook's internal UI, no additional compatibility measures are needed.

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

29-37: LGTM! Mock setup follows best practices.

The beforeEach hook properly initializes the mock store state with all required fields. This follows the learned pattern of placing mock implementations in beforeEach blocks.

Based on learnings

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

67-80: LGTM! useMenu API migration is consistent.

The refactored useMenu hook now accepts a single destructured object parameter instead of multiple arguments. All required parameters (api, showToolbar, isPanelShown, isNavShown, enableShortcuts) are provided correctly across all stories.

Also applies to: 109-135, 158-171


32-40: LGTM! Mock setup follows best practices.

The beforeEach hook properly initializes the mock store state, following the learned pattern for mock implementations.

Based on learnings

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

20-22: LGTM! Accessibility improvement.

Adding aria-label="Options" to the ellipsis button improves screen reader support for icon-only buttons.


35-49: LGTM! New story items demonstrate expanded component capabilities.

The new items showcase inline buttons with progress indicators and badges, properly demonstrating the extended public API surface of Listbox components.


56-62: LGTM! Active state refactor aligns with component updates.

Moving the active prop from ListboxAction to ListboxItem is consistent with the component API changes visible in the relevant code snippets, where ListboxItem now accepts and styles the active prop.

code/core/src/manager/components/sidebar/useChecklist.ts (3)

79-86: LGTM! Round-robin next items selection is well-implemented.

The logic to select up to 3 ready items in a round-robin fashion across sections is clear and correct. It ensures users can "choose their own adventure" across different sections.


55-68: LGTM! Subscription management handles cleanup correctly.

The effect properly subscribes when items become open and cleans up subscriptions (if returned) when items are no longer open. The subscription return type void | (() => void) is handled correctly.


29-35: No clarification needed—the type is already consistent and properly handled.

The muted field type is already defined as boolean | Array<TaskId> in the store (code/core/src/shared/checklist-store/index.ts line 8), which is exactly the union type the review comment suggests should exist. The initialization to false aligns with the boolean part of the union, and the code properly guards with Array.isArray(muted) before calling .includes(). The update logic and CLI schema consistently support both boolean and array values. The implementation is correct as-is.

Likely an incorrect or invalid review comment.

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

71-74: LGTM! nextItems memo properly creates node refs for transitions.

The memoized next array correctly attaches a createRef to each next item for use in the TransitionGroup, enabling proper transition animations.

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

8-30: LGTM! ChecklistData interface is well-structured.

The interface properly defines the hierarchical checklist data model with sections and items. The optional fields (after, content, action, subscribe) provide flexibility, and the subscribe function signature correctly allows optional cleanup by returning void | (() => void).


41-44: LGTM! Action-based flow is well-designed.

The action field with onClick receiving both api and accept callback enables flexible item completion flows with user interaction.

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

20-22: Verify if the done field should be persisted—current implementation appears intentional but warrants confirmation.

Based on code analysis, the done field is deliberately not being persisted: it's neither loaded from settings during initialization nor saved to settings on state changes. Additionally, the reset action excludes done from its cleanup (removing only from accepted and skipped), suggesting it may have different semantics than the other persistent fields.

However, the asymmetry is worth confirming: accept and done both track task completion in similar ways, yet only accept is persisted. This could indicate done is intentionally session-only state, or it could be an oversight where both should be persisted together.

state?: ReturnType<typeof useCollapsible>;
} & ComponentProps<typeof CollapsibleContent>) => {
const internalState = useCollapsible(collapsed, disabled);
const state = providedState || internalState;
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 | 🟡 Minor

Use nullish coalescing (??) instead of logical OR (||).

Line 27 uses || which will fall back to internalState if providedState is any falsy value (including 0, false, ''). Since state objects could theoretically have falsy properties that are valid, use the nullish coalescing operator ?? to only fall back when providedState is null or undefined.

Apply this diff:

-  const state = providedState || internalState;
+  const state = providedState ?? internalState;
🤖 Prompt for AI Agents
In code/core/src/components/components/Collapsible/Collapsible.tsx around line
27, the assignment uses logical OR which treats any falsy providedState (0,
false, '', etc.) as absent; change the fallback to use nullish coalescing so
providedState is only ignored when it is null or undefined (replace the "||"
usage with "??" so state = providedState ?? internalState).

</p>
</>
),
// subscribe: ({ done }) => done(),
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove commented code.

The commented-out subscribe line should either be implemented or removed to maintain code cleanliness.

-          // subscribe: ({ done }) => done(),
📝 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
// subscribe: ({ done }) => done(),
🤖 Prompt for AI Agents
In code/core/src/manager/settings/Checklist/checklistData.tsx around line 66,
there's a commented-out line "// subscribe: ({ done }) => done()," that should
be removed (or implemented if subscription behavior is needed); remove the
commented code to clean up the file, or replace it with a proper subscribe
implementation consistent with the surrounding API and ensure any required
imports/types are added when implementing.

@storybook-app-bot
Copy link

storybook-app-bot bot commented Nov 3, 2025

Package Benchmarks

Commit: 990d21e, ran on 14 November 2025 at 14:31:35 UTC

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

storybook

Before After Difference
Dependency count 44 44 0
Self size 21.75 MB 23.10 MB 🚨 +1.35 MB 🚨
Dependency size 17.16 MB 17.16 MB 0 B
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 188 188 0
Self size 838 KB 838 KB 0 B
Dependency size 71.39 MB 72.74 MB 🚨 +1.35 MB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 170 170 0
Self size 30 KB 30 KB 0 B
Dependency size 67.92 MB 69.27 MB 🚨 +1.35 MB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 45 45 0
Self size 1.45 MB 1.45 MB 0 B
Dependency size 38.91 MB 40.26 MB 🚨 +1.35 MB 🚨
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: 2

♻️ Duplicate comments (8)
code/core/src/components/components/Card/Card.tsx (4)

35-44: Prop naming is inconsistent with Outline component.

The outlineColor prop from CardProps is passed as color to the Outline component (line 40), but Outline expects a prop named color (line 54). While this works, the previous review recommended renaming the Outline prop to outlineColorKey to avoid collision with CSS property names and maintain consistency with the CardProps interface.


52-56: Critical: Fix Outline color prop type.

The color?: keyof typeof color type is invalid due to the broken type import. This was flagged in a previous review. Change to outlineColorKey?: keyof Theme['color'] (or similar) after importing the Theme type, and update the prop usage accordingly.


3-3: Critical: Fix broken type import.

The import type { color } import is invalid and was flagged in a previous review. The color export from 'storybook/theming' is likely a runtime object, not a type. Import Theme type instead and use keyof Theme['color'] for typing color keys throughout this file.


29-33: Critical: Fix outlineColor type definition.

The outlineColor?: keyof typeof color type is invalid due to the broken color type import. This was flagged in a previous review. Change to keyof Theme['color'] after importing the Theme type.

code/core/src/shared/checklist-store/index.ts (3)

6-12: Muted field semantics remain unclear.

The muted field still mixes boolean and TaskId[] types, which can lead to inconsistent behavior in UI code. Past review comment already flagged this issue.


59-64: Skip method doesn't clear done state.

The skip method removes the item from accepted but not from done. If an item was automatically marked as done via subscribe, skipping it should likely clear that state for consistency. Past review comment already raised this concern.


66-72: Reset method doesn't clear done state.

The reset method clears accepted and skipped but leaves done intact. If reset is meant to return an item to its initial state, it should also clear the done array. Past review comment already flagged this.

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

17-59: [Duplicate] Prevent active prop from leaking to the DOM.

The past review comment correctly identified that the active prop will be forwarded to the <div> element, potentially causing React DOM warnings. This issue remains unaddressed.

Apply the fix from the previous review:

-export const ListboxItem = styled.div<{ active?: boolean }>(({ active, theme }) => ({
+export const ListboxItem = styled('div', {
+  shouldForwardProp: (prop) => prop !== 'active',
+})<{ active?: boolean }>(({ active, theme }) => ({
🧹 Nitpick comments (5)
code/core/src/components/components/FocusRing/FocusRing.stories.tsx (1)

35-35: Consider renaming the story export to avoid shadowing the global Proxy.

The export name Proxy shadows the JavaScript global Proxy object. While this is story-file-scoped and unlikely to cause issues in practice, consider a clearer name like ProxyExample or WithFocusProxy for better clarity.

Apply this diff:

-export const Proxy = meta.story({
+export const ProxyExample = meta.story({
   render: () => (
code/core/src/components/components/Listbox/Listbox.tsx (1)

7-15: Semantic HTML consideration: replacing ul with div.

Changing the container from <ul> to <div> (and ListboxItem from <li> to <div> on line 17) removes the native list semantics. If this Listbox is used to render navigational menus or lists of options, consider adding ARIA attributes like role="list" to the container and role="listitem" to items to preserve accessibility semantics.

Verify whether ARIA roles are needed based on the usage context. If the Listbox is used for navigation or option lists, apply this diff:

-export const Listbox = styled.div(({ theme }) => ({
+export const Listbox = styled.div(({ theme }) => ({
+  role: 'list',
   listStyle: 'none',

And update ListboxItem:

-export const ListboxItem = styled.div<{ active?: boolean }>(({ active, theme }) => ({
+export const ListboxItem = styled('div', {
+  shouldForwardProp: (prop) => prop !== 'active',
+})<{ active?: boolean }>(({ active, theme }) => ({
+  // Add role="listitem" where ListboxItem is used, or via defaultProps

Based on learnings

code/core/src/manager/container/Menu.tsx (1)

120-120: Consider using null instead of empty fragments for icons.

Several menu items use <></> for the icon when inactive (e.g., lines 120, 132, 144, 155, 166, 177, 188, 199). Using null or undefined would be more idiomatic and slightly more efficient than rendering empty React fragments.

Apply this pattern:

-      icon: isNavShown ? <CheckIcon /> : <></>,
+      icon: isNavShown ? <CheckIcon /> : null,

Also applies to: 132-132, 144-144, 155-155, 166-166, 177-177, 188-188, 199-199

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

276-276: Refactor createRef to useRef for better performance.

Using createRef inside a useMemo (line 276) creates a new ref object on every dependency change, which may not be necessary. If the ref needs to be stable across renders for the same item, consider using a Map with useRef or accepting that refs are recreated when dependencies change (which appears intentional here based on the useMemo dependencies).

If refs should be stable per item ID across renders:

+  const refsMap = useRef<Map<string, React.RefObject<HTMLLIElement>>>(new Map());
+
   const itemsById: Record<ChecklistItem['id'], ChecklistItem> = useMemo(
     () =>
       Object.fromEntries(
         sections.flatMap(({ items }) =>
           items.map((item) => {
             const isAccepted = accepted.includes(item.id);
             const isDone = done.includes(item.id);
             const isLockedBy =
               item.after?.filter((id) => !accepted.includes(id) && !done.includes(id)) ?? [];
             const isSkipped = skipped.includes(item.id);
-            const nodeRef = createRef<HTMLLIElement>();
+            if (!refsMap.current.has(item.id)) {
+              refsMap.current.set(item.id, createRef<HTMLLIElement>());
+            }
+            const nodeRef = refsMap.current.get(item.id)!;
             return [item.id, { ...item, isAccepted, isDone, isLockedBy, isSkipped, nodeRef }];
           })
         )
       ),
     [sections, accepted, done, skipped]
   );

However, given that refs are tied to specific item state and the current pattern works, this refactor may not be necessary.


336-445: Complex nesting: verify focus behavior and keyboard navigation.

The nesting of ListboxItemFocusTargetFocusProxyCollapsibleItemSummary creates multiple interactive layers. Ensure keyboard navigation (Tab, Enter, Space) and screen reader announcements work correctly across collapsed/expanded states, especially when items are locked or completed.

Manual testing is recommended for:

  • Tab order through checklist items
  • Space/Enter behavior on collapsed vs expanded items
  • Screen reader announcements for locked, completed, and skipped states
  • Focus indicator visibility during keyboard navigation

If you'd like, I can generate a testing checklist for these accessibility scenarios.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd39890 and 405b934.

📒 Files selected for processing (42)
  • code/core/src/cli/globalSettings.ts (1 hunks)
  • code/core/src/components/components/Button/Button.tsx (5 hunks)
  • code/core/src/components/components/Card/Card.stories.tsx (1 hunks)
  • code/core/src/components/components/Card/Card.tsx (4 hunks)
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx (3 hunks)
  • code/core/src/components/components/Collapsible/Collapsible.tsx (3 hunks)
  • code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx (0 hunks)
  • code/core/src/components/components/FocusProxy/FocusProxy.tsx (0 hunks)
  • code/core/src/components/components/FocusRing/FocusRing.stories.tsx (1 hunks)
  • code/core/src/components/components/FocusRing/FocusRing.tsx (1 hunks)
  • code/core/src/components/components/Listbox/Listbox.stories.tsx (3 hunks)
  • code/core/src/components/components/Listbox/Listbox.tsx (4 hunks)
  • code/core/src/components/components/shared/useLocationHash.ts (1 hunks)
  • code/core/src/components/index.ts (2 hunks)
  • code/core/src/core-server/utils/checklist.ts (1 hunks)
  • code/core/src/manager/components/TextFlip.stories.tsx (1 hunks)
  • code/core/src/manager/components/TextFlip.tsx (1 hunks)
  • code/core/src/manager/components/preview/tools/zoom.tsx (0 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 (0 hunks)
  • code/core/src/manager/components/sidebar/Menu.stories.tsx (6 hunks)
  • code/core/src/manager/components/sidebar/Menu.tsx (2 hunks)
  • code/core/src/manager/components/sidebar/RefBlocks.tsx (0 hunks)
  • code/core/src/manager/components/sidebar/SearchResults.tsx (0 hunks)
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx (3 hunks)
  • code/core/src/manager/components/sidebar/Sidebar.tsx (5 hunks)
  • code/core/src/manager/components/sidebar/TestingModule.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/Tree.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/TreeNode.tsx (1 hunks)
  • code/core/src/manager/components/sidebar/useChecklist.ts (1 hunks)
  • code/core/src/manager/container/Menu.stories.tsx (2 hunks)
  • code/core/src/manager/container/Menu.tsx (12 hunks)
  • code/core/src/manager/container/Sidebar.tsx (2 hunks)
  • code/core/src/manager/globals/exports.ts (3 hunks)
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx (2 hunks)
  • code/core/src/manager/settings/Checklist/Checklist.tsx (6 hunks)
  • code/core/src/manager/settings/Checklist/checklistData.tsx (8 hunks)
  • code/core/src/manager/settings/GuidePage.stories.tsx (1 hunks)
  • code/core/src/manager/settings/GuidePage.tsx (2 hunks)
  • code/core/src/manager/settings/index.tsx (3 hunks)
  • code/core/src/shared/checklist-store/index.ts (1 hunks)
💤 Files with no reviewable changes (6)
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/preview/tools/zoom.tsx
  • code/core/src/manager/components/sidebar/RefBlocks.tsx
  • code/core/src/components/components/FocusProxy/FocusProxy.tsx
  • code/core/src/manager/components/sidebar/SearchResults.tsx
  • code/core/src/components/components/FocusProxy/FocusProxy.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • code/core/src/components/index.ts
  • code/core/src/manager/components/TextFlip.tsx
  • code/core/src/manager/components/sidebar/TestingModule.tsx
  • code/core/src/core-server/utils/checklist.ts
  • code/core/src/manager/settings/GuidePage.stories.tsx
  • code/core/src/manager/container/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/settings/Checklist/checklistData.tsx
  • code/core/src/components/components/Listbox/Listbox.stories.tsx
🧰 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/settings/GuidePage.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/useChecklist.ts
  • code/core/src/cli/globalSettings.ts
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/manager/container/Sidebar.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/components/components/shared/useLocationHash.ts
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.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/settings/GuidePage.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/useChecklist.ts
  • code/core/src/cli/globalSettings.ts
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/manager/container/Sidebar.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/components/components/shared/useLocationHash.ts
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.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/settings/GuidePage.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/useChecklist.ts
  • code/core/src/cli/globalSettings.ts
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/manager/container/Sidebar.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/components/components/shared/useLocationHash.ts
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.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/settings/GuidePage.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/useChecklist.ts
  • code/core/src/cli/globalSettings.ts
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/manager/container/Sidebar.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/components/components/shared/useLocationHash.ts
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/shared/checklist-store/index.ts
  • code/core/src/manager/settings/Checklist/Checklist.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/components/sidebar/Menu.tsx
  • code/core/src/components/components/Card/Card.stories.tsx
  • code/core/src/manager/components/TextFlip.stories.tsx
  • code/core/src/manager/settings/index.tsx
  • code/core/src/components/components/FocusRing/FocusRing.tsx
  • code/core/src/components/components/Collapsible/Collapsible.tsx
  • code/core/src/manager/components/sidebar/TreeNode.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/Button/Button.tsx
  • code/core/src/components/components/Listbox/Listbox.tsx
  • code/core/src/components/components/Collapsible/Collapsible.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.tsx
🧠 Learnings (20)
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in beforeEach blocks

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.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/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/globals/exports.ts
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/manager/container/Menu.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.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/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock implementations should be placed in beforeEach blocks

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of beforeEach blocks

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.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/components/sidebar/Menu.stories.tsx
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to test-storybooks/** : Maintain test Storybook configurations under `test-storybooks/` for E2E and visual testing scenarios

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.tsx
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
  • code/core/src/manager/settings/Checklist/Checklist.stories.tsx
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to code/.storybook/** : Place internal UI Storybook configuration in `code/.storybook/` and maintain it there

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
📚 Learning: 2025-10-13T13:33:14.659Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-13T13:33:14.659Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : In application code, use Storybook loggers instead of `console.*` (client code: `storybook/internal/client-logger`; server code: `storybook/internal/node-logger`)

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses

Applied to files:

  • code/core/src/manager/components/sidebar/Menu.stories.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
📚 Learning: 2025-10-03T07:55:42.639Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.tsx
  • code/core/src/components/components/Button/Button.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/components/components/Card/Card.tsx
  • code/core/src/components/components/FocusRing/FocusRing.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
📚 Learning: 2025-09-17T08:11:47.197Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.197Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Each mock implementation should return a Promise for async functions

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required dependencies that the test subject uses

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
📚 Learning: 2025-09-17T08:11:47.196Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-09-17T08:11:47.196Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use vi.mocked() to access and implement mock behaviors

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.stories.tsx
🧬 Code graph analysis (19)
code/core/src/manager/settings/GuidePage.tsx (2)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (17-100)
code/core/src/manager/settings/Checklist/Checklist.tsx (1)
  • Checklist (233-456)
code/core/src/manager/components/sidebar/useChecklist.ts (2)
code/core/src/manager/settings/Checklist/checklistData.tsx (2)
  • ChecklistData (8-30)
  • checklistData (32-268)
code/core/src/manager-api/root.tsx (2)
  • useStorybookApi (294-297)
  • useStorybookState (290-293)
code/core/src/manager/components/sidebar/Menu.stories.tsx (1)
code/core/src/manager/container/Menu.tsx (1)
  • useMenu (60-262)
code/core/src/manager/container/Sidebar.tsx (2)
code/core/src/manager/container/Menu.tsx (1)
  • useMenu (60-262)
code/core/src/manager/manager-stores.ts (1)
  • experimental_useStatusStore (13-13)
code/core/src/manager/components/sidebar/ChecklistModule.tsx (6)
code/core/src/components/components/Collapsible/Collapsible.tsx (1)
  • Collapsible (12-41)
code/core/src/components/components/Listbox/Listbox.tsx (6)
  • ListboxButton (61-68)
  • Listbox (7-15)
  • ListboxItem (17-59)
  • ListboxAction (70-79)
  • ListboxIcon (96-103)
  • ListboxText (81-94)
code/core/src/manager-api/root.tsx (1)
  • useStorybookApi (294-297)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (17-100)
code/core/src/manager/components/TextFlip.tsx (1)
  • TextFlip (63-100)
code/core/src/manager/components/Transition.tsx (1)
  • Transition (6-12)
code/core/src/manager/components/sidebar/Tree.tsx (1)
code/core/src/manager/components/sidebar/IconSymbols.tsx (1)
  • IconSymbols (28-109)
code/core/src/components/components/Card/Card.tsx (1)
code/core/src/components/index.ts (1)
  • Card (45-45)
code/core/src/components/components/shared/useLocationHash.ts (1)
code/core/src/components/index.ts (1)
  • useLocationHash (96-96)
code/core/src/manager/settings/Checklist/Checklist.tsx (7)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
  • ChecklistData (8-30)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (17-100)
code/core/src/manager-api/root.tsx (1)
  • useStorybookApi (294-297)
code/core/src/components/components/shared/useLocationHash.ts (1)
  • useLocationHash (37-41)
code/core/src/components/components/FocusRing/FocusRing.tsx (2)
  • FocusProxy (17-21)
  • FocusTarget (43-73)
code/core/src/components/components/Listbox/Listbox.tsx (1)
  • ListboxItem (17-59)
code/core/src/components/components/Button/Button.tsx (1)
  • Button (20-78)
code/core/src/manager/container/Menu.tsx (3)
code/core/src/manager-api/root.tsx (1)
  • API (94-107)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (17-100)
code/core/src/components/components/Listbox/Listbox.tsx (2)
  • ListboxButton (61-68)
  • ListboxIcon (96-103)
code/core/src/manager/components/sidebar/Menu.tsx (2)
code/core/src/components/components/Listbox/Listbox.tsx (5)
  • Listbox (7-15)
  • ListboxItem (17-59)
  • ListboxAction (70-79)
  • ListboxIcon (96-103)
  • ListboxText (81-94)
code/core/src/components/index.ts (5)
  • Listbox (48-48)
  • ListboxItem (52-52)
  • ListboxAction (49-49)
  • ListboxIcon (51-51)
  • ListboxText (53-53)
code/core/src/components/components/Card/Card.stories.tsx (1)
code/core/src/components/components/Card/Card.tsx (1)
  • Card (35-44)
code/core/src/manager/components/TextFlip.stories.tsx (1)
code/core/src/manager/components/TextFlip.tsx (1)
  • TextFlip (63-100)
code/core/src/manager/settings/index.tsx (1)
code/core/src/manager/settings/GuidePage.tsx (1)
  • GuidePage (37-65)
code/core/src/components/components/FocusRing/FocusRing.tsx (1)
code/core/src/components/components/shared/useLocationHash.ts (1)
  • useLocationHash (37-41)
code/core/src/manager/settings/Checklist/Checklist.stories.tsx (3)
code/core/src/manager/settings/Checklist/checklistData.tsx (1)
  • checklistData (32-268)
code/core/src/manager/manager-stores.mock.ts (1)
  • checklistStore (57-57)
code/core/src/manager/manager-stores.ts (1)
  • checklistStore (25-25)
code/core/src/components/components/Listbox/Listbox.tsx (1)
code/core/src/components/index.ts (6)
  • Listbox (48-48)
  • ListboxItem (52-52)
  • ListboxAction (49-49)
  • ListboxButton (50-50)
  • ListboxText (53-53)
  • ListboxIcon (51-51)
code/core/src/components/components/Collapsible/Collapsible.stories.tsx (2)
code/core/src/components/components/Collapsible/Collapsible.tsx (1)
  • useCollapsible (62-97)
code/core/src/test/testing-library.ts (1)
  • userEvent (123-126)
code/core/src/components/components/FocusRing/FocusRing.stories.tsx (1)
code/core/src/components/components/FocusRing/FocusRing.tsx (3)
  • FocusRing (23-41)
  • FocusProxy (17-21)
  • FocusTarget (43-73)
🪛 Biome (2.1.2)
code/core/src/manager/container/Sidebar.tsx

[error] 59-59: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

code/core/src/components/components/FocusRing/FocusRing.stories.tsx

[error] 35-35: Do not shadow the global "Proxy" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ 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: merged
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (31)
code/core/src/manager/components/sidebar/TreeNode.tsx (1)

90-93: LGTM! Coordinated styling adjustment.

This first-of-type margin reset properly compensates for the removal of the Container's top margin in Tree.tsx, ensuring consistent spacing for the first RootNode.

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

716-719: LGTM! Code simplification.

Replacing the styled Container with a plain div simplifies the code while maintaining functionality. The spacing previously handled by Container's conditional top margin is now managed by the first-of-type rule in RootNode (TreeNode.tsx).

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

15-15: LGTM: readOnly implementation is thorough and correct.

The readOnly prop is properly integrated throughout the component:

  • Correctly gates cursor behavior (line 102)
  • Excludes readOnly buttons from disabled opacity dimming (line 136)
  • Consistently prevents interactive styling (:hover, :active, :focus) when readOnly is true (lines 172-187, 214-259)

Also applies to: 30-30, 69-69, 95-259

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

1-38: LGTM: Clean story implementation.

The TextFlip stories follow standard Storybook patterns with clear examples demonstrating the component's animation behavior for increasing and decreasing values.

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

62-96: LGTM: useCollapsible hook implementation is correct.

The hook properly:

  • Maintains internal collapsed state with external prop synchronization
  • Guards toggle behavior with disabled check (line 74)
  • Provides comprehensive toggleProps with proper ARIA attributes (lines 82-87)
  • Returns all necessary state and handlers for external usage
code/core/src/components/components/Collapsible/Collapsible.stories.tsx (1)

1-54: LGTM: Stories properly reflect the updated Collapsible API.

The updated stories correctly demonstrate:

  • New toggleProps pattern (line 13)
  • Collapsed state control (lines 27-31)
  • Disabled behavior (lines 33-37)
  • Interactive examples via play functions (lines 40-41, 53)
code/core/src/manager/globals/exports.ts (1)

500-503: LGTM: Public API exports properly updated.

The new exports (FocusOutline, FocusRing, FocusTarget, ListboxIcon, useLocationHash) are correctly added to the globals manifest, aligning with the PR's expansion of the public component surface.

Also applies to: 520-520, 559-559

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

48-68: LGTM: Target story properly demonstrates hash-driven focus behavior.

The story correctly:

  • Sets up hash navigation trigger (line 51)
  • Uses a delay before interaction to allow initialization (line 62)
  • Cleans up hash state between test runs (lines 65-67)
code/core/src/components/components/Card/Card.tsx (4)

63-76: Excellent accessibility and progressive enhancement.

The implementation includes several best practices:

  • CSS variable fallback for extensibility (line 63)
  • Progressive enhancement with interpolate-size support (lines 67-72)
  • Respects prefers-reduced-motion (lines 74-76)

These additions improve both accessibility and user experience.


78-97: Well-structured lifecycle animation states.

The enter/exit lifecycle classes provide a clean API for animation libraries. Using modern CSS properties like contentVisibility and blockSize with proper fallbacks for interpolate-size ensures smooth animations while maintaining performance.


125-128: Update prop reference if renamed.

If the color prop is renamed to outlineColorKey (as suggested in the previous review), this conditional check on line 125 will need to be updated accordingly.


131-131: Good extensibility pattern for pseudo-element styles.

The conditional merge of styles['&:before'] allows consumers to customize the pseudo-element while safely handling cases where the property doesn't exist or isn't an object.

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

12-106: Approved: well-structured story refactoring with valid color values.

The refactoring from a single "All" story to individual named stories improves testability and documentation clarity. All 13 color values used in the stories (positive, warning, negative, primary, secondary, ancillary, orange, gold, green, seafoam, purple, ultraviolet, mediumdark) are valid theme colors from storybook/theming/base.ts, and the Card component's outlineColor type definition (keyof typeof color) provides compile-time safety.

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

105-114: LGTM! Proper checklist state initialization.

The beforeEach hook correctly initializes the universalChecklistStore with the new state model (loaded, muted, accepted, done, skipped), providing consistent test setup across stories.


159-165: LGTM! Proper global state cleanup.

The beforeEach hook correctly saves and restores global.CONFIG_TYPE, ensuring test isolation and preventing side effects between stories.

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

32-40: LGTM! Consistent mock store initialization.

The beforeEach hook properly initializes the mock checklist store with the new state model, ensuring consistent test setup for all menu stories.


67-80: LGTM! Updated to new useMenu signature.

The story correctly uses the new useMenu API with a single object parameter containing all required props (api, showToolbar, isPanelShown, isNavShown, enableShortcuts).

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

24-32: LGTM! Story properly initialized with new state model.

The beforeEach hook correctly initializes the mock store with the new checklist state structure (loaded, muted, accepted, done, skipped), providing appropriate test data for the ChecklistModule component.

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

36-44: LGTM! Story properly merges data and store.

The Default story correctly combines checklistData (sections/items), checklistStore (methods), and explicit state fields (accepted, done, skipped) to provide complete props for the Checklist component. This approach gives explicit control over the checklist state for testing.

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

18-24: The done field is intentionally session-only and should not be persisted.

The core-server initialization loads and saves only muted, accepted, and skipped from globalSettings—not done. The done array resets each session and is not meant to persist across app restarts. The schema is correct as written.

Likely an incorrect or invalid review comment.

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

73-82: No issues found after verification.

The mute() method's type coercion is intentional design. The type annotation muted: boolean | Array<TaskId> explicitly allows both modes, and actual usage patterns confirm:

  • mute(false) clears all mutes (switch to boolean mode)
  • mute(array) sets specific muted items (switch to array mode)

The merge logic correctly handles the state transition: when state.muted is false and mute(array) is called, coercing false to [] is semantically correct (nothing muted = no items). The theoretical edge case (state.muted = true with mute(array)) does not occur in the codebase. No mute(true) calls were found.

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

50-62: No functional bug, but the truthiness check lacks precision for maintainability.

The conditional checklist.muted ? works correctly for the actual state transitions in current code—false vs. an array of all item IDs—but it's inconsistent with the explicit type-checking pattern used in useChecklist.ts (line 32: !(Array.isArray(muted) && muted.includes(item.id))).

Consider making the check more explicit for clarity:

{Array.isArray(checklist.muted) && checklist.muted.length > 0 ? (
  // show "Show in sidebar"
) : (
  // show "Remove from sidebar"
)}

This prevents confusion about edge cases and aligns with the pattern already established in the codebase.

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

96-103: LGTM: New ListboxIcon component is well-structured.

The new ListboxIcon component uses the CSS variable pattern consistently with the rest of the Listbox API and provides proper centering for icon content.


12-14: Selector change appears safe in current codebase but is architecturally broader than necessary.

Verification found that all Listbox elements in the codebase only have Listbox siblings or no siblings. The & + * selector applies borders only between consecutive Listbox groups (Menu.tsx, ChecklistModule.tsx) and in the Groups story (Listbox.stories.tsx lines 70–93), where all three adjacent elements are Listbox components. No unintended borders are occurring.

However, the concern about broadening the selector is valid: the change from & + & to & + * makes the rule apply to any adjacent sibling, not just Listbox. While this poses no immediate risk, it could cause unintended styling if a non-Listbox element is placed as a sibling to Listbox in the future. If the selector broadening was intentional, confirm the rationale; otherwise, consider reverting to & + & to maintain the original restrictive behavior.

code/core/src/manager/container/Menu.tsx (3)

86-100: LGTM: Previous concern about exposing the guide in production has been addressed.

The guide menu item is now correctly gated with CONFIG_TYPE === 'DEVELOPMENT' (line 238), and the progress indicator uses a non-interactive ListboxButton with as="div" and readOnly, addressing the past review comment.

Also applies to: 238-238


48-50: Styled component for ProgressCircle is concise and correct.

The ProgressCircle styled component correctly applies the theme's secondary color for visual consistency with the active state.


204-218: No changes required.

The getDocsUrl implementation in code/core/src/manager-api/modules/versions.ts (lines 92-130) is well-tested and robust. It correctly handles the versioned: true and renderer: true parameters used in Menu.tsx, always returning a valid URL. The method includes defensive checks (e.g., verifying global.STORYBOOK_RENDERER exists before using it) and covers all supported environments. Comprehensive test coverage confirms expected behavior across various version and renderer scenarios.

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

367-389: Accessibility of lock state is well-implemented.

The lock state uses a WithTooltip with an informative message listing prerequisite items, and the button has proper aria-label, disabled, and readOnly attributes. This provides good accessibility for users who need to understand why an item is locked.


248-251: LGTM: isCompleted helper is correctly implemented.

The isCompleted callback correctly checks if an item is in any of the completion states (accepted, done, or skipped) and is properly memoized with all necessary dependencies.


266-282: Lock state computation is correct.

The isLockedBy logic (lines 273-274) correctly filters prerequisite items that are neither accepted nor done, ensuring items are only unlocked when all dependencies are satisfied. This aligns with the isReady logic in the useChecklist hook.


390-416: No issues found — the subscribe completion mechanism works correctly.

Items with subscribe callbacks are properly managed through the auto-completion path: the subscribe callback is invoked in the useChecklist hook (lines 57–62), calls done() when ready, which updates the universal store's done array, and the component's isCompleted check includes items from the done list. The absence of a manual "Mark as complete" button for subscribed items (line 407) is intentional—these items are self-managed, not user-managed.

Likely an incorrect or invalid review comment.

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 (3)
code/core/src/components/components/Listbox/Listbox.tsx (1)

18-18: Prevent active prop from leaking to the DOM.

The active prop is style-only but is still being forwarded to the <li> element, which will cause React DOM attribute warnings. This issue was previously flagged.

Apply this diff to filter the prop:

-export const ListboxItem = styled.li<{ active?: boolean; transitionStatus?: TransitionStatus }>(
+export const ListboxItem = styled('li', {
+  shouldForwardProp: (prop) => prop !== 'active',
+})<{ active?: boolean; transitionStatus?: TransitionStatus }>(
   ({ active, theme }) => ({
code/core/src/manager/components/sidebar/ChecklistModule.tsx (2)

101-189: Invalid list semantics persist: <li> nested inside <li> and <li> inside <div>.

The current structure creates invalid HTML in two ways:

  1. Listbox at line 101 is a <div> (no as="ul" prop) containing <li> elements (ListboxItem) — list items must be children of <ul> or <ol>.
  2. Lines 102-188 nest ListboxItem (<li>) inside another ListboxItem (<li>) — this violates HTML semantics and breaks accessibility.

This matches the structure flagged in previous review but appears unresolved in the current code.

Apply this fix to flatten the structure and add proper list semantics:

-          summary={({ isCollapsed, toggleCollapsed, toggleProps }) => (
-            <Listbox onClick={toggleCollapsed}>
-              <ListboxItem>
-                <ListboxItem>
+          summary={({ isCollapsed, toggleCollapsed, toggleProps }) => (
+            <Listbox as="ul" onClick={toggleCollapsed}>
+              <ListboxItem as="li">
                   {loaded && (
                     <ListboxAction
                       onClick={(e) => {
                         e.stopPropagation();
                         api.navigateUrl('/settings/guide', { plain: false });
                       }}
                     >
                       <strong>{title(progress)}</strong>
                     </ListboxAction>
                   )}
-                </ListboxItem>
-                <ListboxItem>
+                <div style={{ display: 'flex', alignItems: 'center', gap: 4, marginLeft: 'auto' }}>
                   <WithTooltip
                     hasChrome={false}
                     tooltip={
                       <TooltipNote
                         note={`${isCollapsed ? 'Expand' : 'Collapse'} onboarding checklist`}
                       />
                     }
                     trigger="hover"
                   >
                     <CollapseToggle
                       {...toggleProps}
                       id="checklist-module-collapse-toggle"
                       aria-label={`${isCollapsed ? 'Expand' : 'Collapse'} onboarding checklist`}
                     >
                       <ChevronSmallUpIcon
                         style={{
                           transform: isCollapsed ? 'rotate(180deg)' : 'none',
                           transition: 'transform 250ms',
-                          willChange: 'auto',
+                          willChange: 'transform',
                         }}
                       />
                     </CollapseToggle>
                   </WithTooltip>
                   {loaded && (
                     <WithTooltip
                       as="div"
                       closeOnOutsideClick
                       tooltip={({ onHide }) => (
                         <Listbox as="ul">
                           <ListboxItem as="li">
                             <ListboxAction
                               onClick={(e) => {
                                 e.stopPropagation();
                                 api.navigateUrl('/settings/guide', { plain: false });
                                 onHide();
                               }}
                             >
                               <ListboxIcon>
                                 <ListUnorderedIcon />
                               </ListboxIcon>
                               <ListboxText>Open full guide</ListboxText>
                             </ListboxAction>
                           </ListboxItem>
                           <ListboxItem as="li">
                             <ListboxAction
                               onClick={(e) => {
                                 e.stopPropagation();
                                 mute(allItems.map(({ id }) => id));
                                 onHide();
                               }}
                             >
                               <ListboxIcon>
                                 <EyeCloseIcon />
                               </ListboxIcon>
                               <ListboxText>Remove from sidebar</ListboxText>
                             </ListboxAction>
                           </ListboxItem>
                         </Listbox>
                       )}
                     >
-                      <ListboxButton onClick={(e) => e.stopPropagation()}>
+                      <ListboxButton 
+                        ariaLabel="View onboarding progress and options"
+                        onClick={(e) => e.stopPropagation()}
+                      >
                         <ProgressCircle
                           percentage={progress}
                           running={false}
                           size={16}
                           width={1.5}
                         />
                         <TextFlip text={`${progress}%`} placeholder="00%" />
                       </ListboxButton>
                     </WithTooltip>
                   )}
-                </ListboxItem>
+                </div>
               </ListboxItem>
             </Listbox>
           )}

176-184: Add ariaLabel to progress button for accessibility.

The ListboxButton at line 176 contains only visual elements (ProgressCircle and percentage) without an accessible text label. Per project conventions, ariaLabel is mandatory on all Button usages.

Based on learnings

(This is addressed in the diff above.)

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

130-136: Replace willChange: 'auto' with willChange: 'transform' for better performance.

Setting willChange: 'auto' is redundant (it's the default). Since you're animating the transform property, use willChange: 'transform' to hint to the browser for optimization.

(This is addressed in the diff above.)


208-218: Consider adding ariaLabel={false} to ListboxButton with text content.

The ListboxButton at line 208 has text content ({item.action.label}) that serves as its accessible name. Per project conventions, you can explicitly set ariaLabel={false} to document that the button's children provide the label.

Based on learnings

                     {item.action && (
                       <ListboxButton
+                        ariaLabel={false}
                         onClick={() => {
                           item.action?.onClick({
                             api,
                             accept: () => checklistStore.accept(item.id),
                           });
                         }}
                       >
                         {item.action.label}
                       </ListboxButton>
                     )}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c051458 and 990d21e.

📒 Files selected for processing (2)
  • code/core/src/components/components/Listbox/Listbox.tsx (4 hunks)
  • code/core/src/manager/components/sidebar/ChecklistModule.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
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.
📚 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/components/components/Listbox/Listbox.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.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/components/components/Listbox/Listbox.tsx
  • code/core/src/manager/components/sidebar/ChecklistModule.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/ChecklistModule.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/ChecklistModule.tsx
📚 Learning: 2025-10-03T07:55:42.639Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.

Applied to files:

  • code/core/src/manager/components/sidebar/ChecklistModule.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/ChecklistModule.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/ChecklistModule.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/ChecklistModule.tsx
🧬 Code graph analysis (2)
code/core/src/components/components/Listbox/Listbox.tsx (1)
code/core/src/components/index.ts (6)
  • Listbox (48-48)
  • ListboxItem (52-52)
  • ListboxAction (49-49)
  • ListboxButton (50-50)
  • ListboxText (53-53)
  • ListboxIcon (51-51)
code/core/src/manager/components/sidebar/ChecklistModule.tsx (7)
code/core/src/components/index.ts (11)
  • Collapsible (44-44)
  • Card (45-45)
  • ListboxButton (50-50)
  • ProgressSpinner (129-129)
  • Listbox (48-48)
  • ListboxItem (52-52)
  • ListboxAction (49-49)
  • WithTooltip (91-91)
  • TooltipNote (85-85)
  • ListboxIcon (51-51)
  • ListboxText (53-53)
code/core/src/components/components/Collapsible/Collapsible.tsx (1)
  • Collapsible (12-41)
code/core/src/components/components/Card/Card.tsx (1)
  • Card (35-44)
code/core/src/components/components/Listbox/Listbox.tsx (6)
  • ListboxButton (61-68)
  • Listbox (8-16)
  • ListboxItem (18-59)
  • ListboxAction (70-79)
  • ListboxIcon (96-103)
  • ListboxText (81-94)
code/core/src/manager/components/sidebar/useChecklist.ts (1)
  • useChecklist (17-100)
code/core/src/manager/components/TextFlip.tsx (1)
  • TextFlip (63-100)
code/core/src/manager/manager-stores.ts (1)
  • checklistStore (25-25)
⏰ 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: merged
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (2)
code/core/src/components/components/Listbox/Listbox.tsx (2)

70-94: Clean refactor: state management moved to ListboxItem.

The simplification of ListboxAction and ListboxText to use static styles with inherit values is a good design. This allows ListboxItem to centrally control the active state styling, reducing duplication and improving maintainability.


96-103: Well-designed icon component.

The new ListboxIcon component follows good patterns:

  • Uses the CSS variable --listbox-item-muted-color defined by ListboxItem for consistent theming
  • Fixed 16×16 size is standard for UI icons
  • Centered alignment provides visual consistency

@ghengeveld ghengeveld merged commit e6456e2 into onboarding-checklist Nov 14, 2025
7 of 9 checks passed
@ghengeveld ghengeveld deleted the onboarding-guide branch November 14, 2025 16:17
@coderabbitai coderabbitai bot mentioned this pull request Nov 14, 2025
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:merged Run the CI jobs that normally run when merged. core feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants