Skip to content

UI: Allow direct kb/mouse actions on zoom tool button#33496

Merged
Sidnioulz merged 6 commits into
nextfrom
zoom-tool-kb-extras
Mar 3, 2026
Merged

UI: Allow direct kb/mouse actions on zoom tool button#33496
Sidnioulz merged 6 commits into
nextfrom
zoom-tool-kb-extras

Conversation

@Sidnioulz
Copy link
Copy Markdown
Member

@Sidnioulz Sidnioulz commented Jan 9, 2026

What I did

Supports #33375.

I allowed for more direct keyboard and scroll operation, directly on the button. This helps keyboard users who expect ArrowDown to do something on other toolbar tools with a popup, and provides a more direct shortcut for mouse users (scroll the zoom button) that is commonly found in graphics apps with a canvas and zoom controls.

Checklist for Contributors

Testing

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

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

Manual testing

  1. Focus Zoom button
  2. Smash your keeb!
  3. Scroll like a hamster!

Documentation

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 PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

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

Summary by CodeRabbit

  • New Features

    • Keyboard support for zoom (Arrow Up/Down, Page Up/Down, Home, End) to change zoom levels.
    • Mouse wheel support for zoom adjustments with appropriate event handling.
    • New relative zoom API (zoomBy) allowing programmatic incremental adjustments with a minimum clamp.
  • Tests

    • Added interactive stories/tests exercising key and wheel interactions, asserting resulting displayed zoom levels.

@Sidnioulz Sidnioulz requested a review from ghengeveld January 9, 2026 13:52
@Sidnioulz Sidnioulz added maintenance User-facing maintenance tasks ci:normal labels Jan 9, 2026
@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jan 9, 2026

View your CI Pipeline Execution ↗ for commit 498b913

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ❌ Failed 13m 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-02 16:27:41 UTC

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Added a programmatic incremental zoom API zoomBy(delta: number), threaded it through ZoomWrapper to Zoom, and added keyboard (ArrowUp/ArrowDown/PageUp/PageDown/Home/End) and mouse-wheel handlers that call zoomIn/zoomOut/zoomTo/zoomBy. Added Storybook stories that simulate those interactions and assert displayed zoom labels.

Changes

Cohort / File(s) Summary
Zoom component & wrapper
code/core/src/manager/components/preview/tools/zoom.tsx
Added zoomBy(delta: number) prop to Zoom and implemented zoomBy in ZoomWrapper (clamped to >= 0.01). Added keyboard onKeyDown handlers (ArrowUp/ArrowDown/PageUp/PageDown/Home/End) and onWheel handlers in the Zoom UI to invoke zoomIn/zoomOut/zoomTo/zoomBy. Updated prop types and render signatures.
Storybook stories / interaction tests
code/core/src/manager/components/preview/tools/zoom.stories.tsx
Imported Storybook test utilities and exposed zoomBy in story args; render passes zoomBy to Zoom. Added public stories (ArrowUpKey, ArrowDownKey, PageUpKey, PageDownKey, HomeKey, EndKey, WheelUp, WheelDown) that simulate keyboard and wheel events and assert zoom label changes.

Sequence Diagram(s)

sequenceDiagram
  participant User as User (keyboard / wheel)
  participant ZoomUI as Zoom component
  participant Wrapper as ZoomWrapper (state + zoomBy)
  participant State as internal setter

  User->>ZoomUI: keydown / wheel event
  ZoomUI->>Wrapper: call zoomIn / zoomOut / zoomTo / zoomBy(delta)
  Wrapper->>State: set(Math.max(0.01, value + delta)) / set(newValue)
  State-->>Wrapper: value updated
  Wrapper-->>ZoomUI: re-render with new value
  ZoomUI-->>User: updated zoom label shown
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs


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

Copy link
Copy Markdown
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

🤖 Fix all issues with AI agents
In @code/core/src/manager/components/preview/tools/zoom.tsx:
- Around line 206-211: zoomBy currently clamps only the minimum and can exceed
the maximum zoom; update the zoomBy callback to clamp the new value between 0.01
and the max zoom level (use ZOOM_LEVELS.at(-1) or the equivalent MAX_ZOOM)
before calling set, so it matches the bounds enforced by zoomIn/zoomOut (refer
to the zoomBy function, set, value, and ZOOM_LEVELS symbols).
- Around line 163-168: The Home/End handling is reversed: in the keydown handler
where it checks e.key for 'Home' and 'End' (calling zoomTo with ZOOM_LEVELS
indices), swap the targets so Home calls zoomTo(ZOOM_LEVELS[0]) and End calls
zoomTo(ZOOM_LEVELS[ZOOM_LEVELS.length - 1]); keep e.preventDefault() in both
branches and update the branches around the zoomTo calls in the same keydown
handler to maintain accessibility per WAI-ARIA.
🧹 Nitpick comments (1)
code/core/src/manager/components/preview/tools/zoom.stories.tsx (1)

86-144: Inconsistent use of await on focus() calls.

Some tests use zoom.focus() (lines 90, 110, 120, 130, 140) while one uses await zoom.focus() (line 100). While focus() is synchronous, consistency improves readability.

♻️ Suggested fix for consistency (choose one style)

Either remove the await on line 100:

-    await zoom.focus();
+    zoom.focus();

Or add await to all other focus calls for consistency if there's a linting preference.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80cbd5b and 7ad1e99.

📒 Files selected for processing (2)
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
  • code/core/src/manager/components/preview/tools/zoom.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

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

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/core/src/manager/components/preview/tools/zoom.tsx
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
**/*.{js,jsx,json,html,ts,tsx,mjs}

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

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/manager/components/preview/tools/zoom.tsx
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
**/*.{ts,tsx}

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

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/preview/tools/zoom.tsx
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
**/*.{ts,tsx,js,jsx}

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

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
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/preview/tools/zoom.tsx
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

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

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock at the highest level of abstraction needed in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `vi.mocked()` to type and access the mocked functions in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
🧬 Code graph analysis (1)
code/core/src/manager/components/preview/tools/zoom.tsx (1)
code/core/src/preview-api/index.ts (1)
  • useCallback (6-6)
⏰ 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). (4)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: get-parameters
🔇 Additional comments (5)
code/core/src/manager/components/preview/tools/zoom.tsx (2)

150-178: LGTM on keyboard and wheel event handling structure.

The event handler logic is well-structured with appropriate preventDefault() calls to stop default browser behavior. The wheel direction mapping (negative deltaY = zoom in, positive = zoom out) follows the natural scroll metaphor.


54-60: LGTM on the zoomBy prop addition.

The new zoomBy prop is properly typed and threaded through from ZoomWrapper to Zoom. The useCallback dependency array is correct.

Also applies to: 241-241

code/core/src/manager/components/preview/tools/zoom.stories.tsx (3)

126-144: Update HomeKey/EndKey test expectations after accessibility fix.

If the Home/End key mapping is corrected in zoom.tsx per accessibility standards, these test expectations will need to be swapped:

  • HomeKey should expect 25% (minimum)
  • EndKey should expect 400% (maximum)

146-166: Good choice using Simulate.wheel for wheel events.

Using react-dom/test-utils Simulate is appropriate here since userEvent from @testing-library/user-event doesn't fully support wheel events. The waitFor wrapper ensures the async state update is captured.


25-37: LGTM on the render function with zoomBy wiring.

The zoomBy implementation correctly mirrors the lower-bound clamping logic from the main component. Note that the story uses simplified +/- 0.5 for zoomIn/zoomOut which differs from the actual ZOOM_LEVELS-based implementation—this is acceptable for component-level isolation testing.

Comment thread code/core/src/manager/components/preview/tools/zoom.tsx
Comment thread code/core/src/manager/components/preview/tools/zoom.tsx
@Sidnioulz Sidnioulz force-pushed the zoom-tool-kb-extras branch from 7ad1e99 to 0917ebf Compare January 9, 2026 15:34
Copy link
Copy Markdown
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

🤖 Fix all issues with AI agents
In @code/core/src/manager/components/preview/tools/zoom.stories.tsx:
- Around line 5-6: Remove the deprecated Simulate import and replace its usage
with fireEvent: delete the "Simulate" import from the top import statement and
add "fireEvent" to the existing import that currently has "expect, fn, screen,
waitFor, within"; then replace all calls to Simulate.wheel inside the file (the
two occurrences) with fireEvent.wheel so the wheel events use the modern test
utility.

In @code/core/src/manager/components/preview/tools/zoom.tsx:
- Around line 153-173: The Home/End key handling in the onKeyDown handler of the
zoom control is reversed: change the Home branch (currently calling
zoomTo(ZOOM_LEVELS[ZOOM_LEVELS.length - 1])) to call zoomTo(ZOOM_LEVELS[0]) so
Home goes to minimum zoom, and change the End branch (currently calling
zoomTo(ZOOM_LEVELS[0])) to call zoomTo(ZOOM_LEVELS[ZOOM_LEVELS.length - 1]) so
End goes to maximum zoom; update the two else-if branches in the onKeyDown
anonymous function accordingly and keep the e.preventDefault() calls.
🧹 Nitpick comments (1)
code/core/src/manager/components/preview/tools/zoom.tsx (1)

174-181: Consider handling horizontal wheel events.

The current implementation triggers zoom for any wheel event with non-zero deltaY. While this works for vertical scrolling, it doesn't guard against horizontal scrolling when deltaY === 0, which would still call preventDefault() but not perform any zoom action.

♻️ Optional enhancement to skip no-op cases
        onWheel={(e) => {
          if (e.deltaY < 0) {
            zoomIn();
+            e.preventDefault();
          } else if (e.deltaY > 0) {
            zoomOut();
+            e.preventDefault();
          }
-          e.preventDefault();
        }}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ad1e99 and 0917ebf.

📒 Files selected for processing (2)
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
  • code/core/src/manager/components/preview/tools/zoom.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

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

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/core/src/manager/components/preview/tools/zoom.tsx
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
**/*.{js,jsx,json,html,ts,tsx,mjs}

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

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/manager/components/preview/tools/zoom.tsx
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
**/*.{ts,tsx}

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

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/preview/tools/zoom.tsx
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
**/*.{ts,tsx,js,jsx}

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

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
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/preview/tools/zoom.tsx
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

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

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

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

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock at the highest level of abstraction needed in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `vi.mocked()` to type and access the mocked functions in Vitest tests

Applied to files:

  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
🧬 Code graph analysis (1)
code/core/src/manager/components/preview/tools/zoom.tsx (1)
code/core/src/preview-api/index.ts (1)
  • useCallback (6-6)
⏰ 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: nx
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (6)
code/core/src/manager/components/preview/tools/zoom.tsx (3)

54-60: LGTM: zoomBy prop correctly added to Zoom component.

The new zoomBy prop is properly typed and destructured for use in the component.


209-214: LGTM: zoomBy implementation is correct.

The function properly clamps the minimum value to 0.01 while allowing unlimited maximum zoom, which is consistent with the ZoomInput's maxValue={1000} configuration at line 97.


223-244: LGTM: useEffect dependencies are correct.

The zoomBy function is correctly excluded from the effect dependencies since it's not used within the effect. The effect only registers shortcuts for zoomIn, zoomOut, and zoomTo.

code/core/src/manager/components/preview/tools/zoom.stories.tsx (3)

25-38: LGTM: zoomBy implementation in stories matches component behavior.

The zoomBy callback correctly clamps the minimum value to 0.01, consistent with the implementation in zoom.tsx.


126-144: Update test expectations if Home/End semantics are corrected.

The HomeKey and EndKey stories expect 400% and 25% respectively, matching the current implementation. However, if the Home/End key semantics are reversed to match UI conventions (as suggested in zoom.tsx), these test expectations will need to be swapped.


146-166: LGTM: Wheel event tests correctly use waitFor for async state updates.

The wheel tests appropriately use waitFor to account for asynchronous state updates, ensuring stable assertions.

Comment thread code/core/src/manager/components/preview/tools/zoom.stories.tsx Outdated
Comment thread code/core/src/manager/components/preview/tools/zoom.tsx
@Sidnioulz Sidnioulz changed the title Zoom: Allow more direct keyboard and scroll operation Zoom: Optional refinements Jan 9, 2026
Base automatically changed from zoom-tool to next January 9, 2026 17:44
@Sidnioulz Sidnioulz marked this pull request as draft January 19, 2026 21:32
@Sidnioulz Sidnioulz changed the title Zoom: Optional refinements Zoom optional refinements - Direct kb/mouse control on zoom tool button Jan 21, 2026
@Sidnioulz Sidnioulz marked this pull request as ready for review January 21, 2026 14:37
@Sidnioulz Sidnioulz force-pushed the zoom-tool-kb-extras branch from 0917ebf to 27c6350 Compare January 21, 2026 14:38
@Sidnioulz Sidnioulz mentioned this pull request Jan 21, 2026
22 tasks
@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Jan 21, 2026

Package Benchmarks

Commit: 498b913, ran on 2 March 2026 at 16:24:32 UTC

No significant changes detected, all good. 👏

@github-actions github-actions Bot added the Stale label Feb 2, 2026
@Sidnioulz Sidnioulz force-pushed the zoom-tool-kb-extras branch from 27c6350 to 3bec845 Compare March 2, 2026 08:43
@Sidnioulz Sidnioulz changed the title Zoom optional refinements - Direct kb/mouse control on zoom tool button UI: Allow direct kb/mouse actions on zoom tool button Mar 2, 2026
@Sidnioulz Sidnioulz removed the Stale label Mar 2, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
code/core/src/manager/components/preview/tools/zoom.tsx (2)

166-171: ⚠️ Potential issue | 🟠 Major

Swap Home/End targets to match expected keyboard semantics.

Home should jump to minimum zoom and End to maximum zoom; the current branches are reversed. This also impacts the Home/End story assertions downstream.

🔧 Proposed fix
-          } else if (e.key === 'Home') {
-            zoomTo(ZOOM_LEVELS[ZOOM_LEVELS.length - 1]);
+          } else if (e.key === 'Home') {
+            zoomTo(ZOOM_LEVELS[0]);
             e.preventDefault();
           } else if (e.key === 'End') {
-            zoomTo(ZOOM_LEVELS[0]);
+            zoomTo(ZOOM_LEVELS[ZOOM_LEVELS.length - 1]);
             e.preventDefault();
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/manager/components/preview/tools/zoom.tsx` around lines 166 -
171, The Home/End handling is reversed: when e.key === 'Home' call zoomTo with
the minimum zoom (ZOOM_LEVELS[0]) and when e.key === 'End' call zoomTo with the
maximum zoom (ZOOM_LEVELS[ZOOM_LEVELS.length - 1]); update the branches in the
keyboard handler where zoomTo is used (referencing ZOOM_LEVELS and zoomTo) so
Home jumps to min and End to max, and adjust any impacted Home/End story
assertions if present.

209-214: ⚠️ Potential issue | 🟠 Major

Clamp zoomBy to the same upper bound as other zoom controls.

zoomBy currently enforces only a minimum. Repeated ArrowUp can push zoom beyond the supported max level.

🔧 Proposed fix
   const zoomBy = useCallback(
     (delta: number) => {
-      set(Math.max(0.01, value + delta));
+      const min = 0.01;
+      const max = ZOOM_LEVELS[ZOOM_LEVELS.length - 1];
+      set(Math.min(max, Math.max(min, value + delta)));
     },
     [set, value]
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/manager/components/preview/tools/zoom.tsx` around lines 209 -
214, zoomBy currently only enforces a minimum and can exceed the same max used
elsewhere; modify zoomBy to clamp the computed new zoom to the same upper bound
as the other zoom controls (e.g., use the same MAX_ZOOM constant or helper used
by zoomIn/zoomOut/zoomTo). In the zoomBy callback (function zoomBy using set and
value), compute newValue = clamp(value + delta, MIN_ZOOM, MAX_ZOOM) (or
Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, value + delta))) and pass newValue to set
so zoomBy respects the same max limit as the other controls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/core/src/manager/components/preview/tools/zoom.stories.tsx`:
- Around line 89-93: The tests use findByRole('button', { name: 'Change zoom
level' }) but the component's helper openDialog and the actual control use role
'switch', causing lookup failures; update the play functions that query the zoom
control (e.g., where you assign const zoom = await canvas.findByRole(...)) to
use findByRole('switch', { name: 'Change zoom level' }) consistently (same
change for all occurrences noted around the zoom keyboard/wheel stories), keep
the subsequent zoom.focus(), userEvent.keyboard/wheel calls and assertions
unchanged other than the role string so the interactions target the real
control.

---

Duplicate comments:
In `@code/core/src/manager/components/preview/tools/zoom.tsx`:
- Around line 166-171: The Home/End handling is reversed: when e.key === 'Home'
call zoomTo with the minimum zoom (ZOOM_LEVELS[0]) and when e.key === 'End' call
zoomTo with the maximum zoom (ZOOM_LEVELS[ZOOM_LEVELS.length - 1]); update the
branches in the keyboard handler where zoomTo is used (referencing ZOOM_LEVELS
and zoomTo) so Home jumps to min and End to max, and adjust any impacted
Home/End story assertions if present.
- Around line 209-214: zoomBy currently only enforces a minimum and can exceed
the same max used elsewhere; modify zoomBy to clamp the computed new zoom to the
same upper bound as the other zoom controls (e.g., use the same MAX_ZOOM
constant or helper used by zoomIn/zoomOut/zoomTo). In the zoomBy callback
(function zoomBy using set and value), compute newValue = clamp(value + delta,
MIN_ZOOM, MAX_ZOOM) (or Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, value + delta)))
and pass newValue to set so zoomBy respects the same max limit as the other
controls.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27c6350 and 3bec845.

📒 Files selected for processing (2)
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx
  • code/core/src/manager/components/preview/tools/zoom.tsx

Comment thread code/core/src/manager/components/preview/tools/zoom.stories.tsx Outdated
Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
code/core/src/manager/components/preview/tools/zoom.stories.tsx (1)

90-90: Inconsistent await before focus() calls.

Line 100 uses await zoom.focus() while all other keyboard stories omit await. Since focus() is synchronous, the await is unnecessary—consider removing it from line 100 for consistency, or adding it everywhere if you prefer uniformity.

Also applies to: 100-100, 110-110, 120-120, 130-130, 140-140

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/manager/components/preview/tools/zoom.stories.tsx` at line 90,
The focus() calls on the zoom element are synchronously invoked (zoom.focus())
but one or more stories use an unnecessary await; make them consistent by
removing the await operator from all zoom.focus() calls in the keyboard stories
(e.g., the zoom variable's focus() invocations used in the keyboard story
functions), so every story uses plain synchronous zoom.focus(), and run
tests/Storybook to verify no behavior changed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@code/core/src/manager/components/preview/tools/zoom.stories.tsx`:
- Line 90: The focus() calls on the zoom element are synchronously invoked
(zoom.focus()) but one or more stories use an unnecessary await; make them
consistent by removing the await operator from all zoom.focus() calls in the
keyboard stories (e.g., the zoom variable's focus() invocations used in the
keyboard story functions), so every story uses plain synchronous zoom.focus(),
and run tests/Storybook to verify no behavior changed.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3bec845 and a0cf4dc.

📒 Files selected for processing (1)
  • code/core/src/manager/components/preview/tools/zoom.stories.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:normal maintenance User-facing maintenance tasks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants