Skip to content

Fix production zoom scroll behavior#1295

Open
VanshajPoonia wants to merge 1 commit into
AOSSIE-Org:mainfrom
VanshajPoonia:fix/1293-production-zoom-scroll
Open

Fix production zoom scroll behavior#1295
VanshajPoonia wants to merge 1 commit into
AOSSIE-Org:mainfrom
VanshajPoonia:fix/1293-production-zoom-scroll

Conversation

@VanshajPoonia
Copy link
Copy Markdown
Contributor

@VanshajPoonia VanshajPoonia commented May 28, 2026

Fixes #1293

Screenshots/Recordings:

This PR addresses the zoom-on-scroll behavior described in issue #1293 and the linked reference issue #656.

Before:

  • In production builds, scroll zoom could fall back to cursor-anchored behavior even while the image still fit inside the viewport.
  • Zooming out could leave the image off-center because pan offsets were preserved.

After:

  • Images that fit inside the viewport zoom from the center.
  • Mouse-anchored zoom is only used on axes where the image overflows.
  • Non-overflowing axes stay centered.
  • Zooming back to minimum scale recenters the image instead of leaving it stuck off-center.

Additional Notes:

The regression was caused by the custom wheel listener depending on react-zoom-pan-pinch's internal wrapperComponent being available during the first effect run. In production builds, that timing could fail, causing the viewer to use the library's default wheel behavior.

This PR attaches the custom wheel listener to a stable container owned by ZoomableImage and disables the library's default wheel handling. A focused regression test was added to verify the custom wheel behavior still works when the internal transform wrapper is not ready.

Local verification completed:

cd frontend
npm test -- ZoomableImage.test.tsx
npm run lint:check
npm run build

AI Usage Disclosure:

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: Codex

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without review otherwise.

Summary by CodeRabbit

  • Refactor

    • Enhanced zoom and pan interaction handling with refined viewport detection and overflow management during image manipulation.
  • Tests

    • Added test coverage for wheel-zoom behavior.

Review Change Stack

@github-actions github-actions Bot added bug Something isn't working build frontend labels May 28, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Walkthrough

ZoomableImage refactors wheel-zoom handling by introducing a wheelAreaRef container and getViewportElement() abstraction to consistently measure viewport dimensions. The wheel interceptor algorithm is rewritten with rotation-aware dimensions, mouse-anchored zoom positioning, and axis-dependent overflow detection. Component rendering and event handlers are updated to use the new viewport abstraction. Unit tests cover wheel-zoom behavior.

Changes

Dynamic Zoom Wheel Interceptor Refactoring

Layer / File(s) Summary
Viewport element abstraction and reset handler refactoring
frontend/src/components/Media/ZoomableImage.tsx
wheelAreaRef DOM container is introduced. getViewportElement() function derives the active viewport element (zoom-pan wrapper when ready, otherwise wheelAreaRef). handleReset callback is refactored to measure wrapper rectangles from the derived viewport element with updated hook dependencies.
Overflow detection and observer wiring refactoring
frontend/src/components/Media/ZoomableImage.tsx
Rotation-triggered overflow effect measures viewport size via getViewportElement(). Wheel and resize observer target is switched from the zoom-pan wrapper component to wheelAreaRef.current. Wrapper geometry snapshot caching is refined for overflow computations.
Wheel zoom algorithm rewrite with mouse-anchored positioning
frontend/src/components/Media/ZoomableImage.tsx
Wheel interceptor computes effective dimensions from clientWidth/clientHeight with rotation awareness, determines overflow for next scale, derives centered versus mouse-anchored target positions conditioned on mouse-over-image and overflow axes, prevents default wheel behavior, updates overflow state, and applies instant transform.
Component rendering and event handler integration
frontend/src/components/Media/ZoomableImage.tsx
JSX wraps TransformWrapper in a div holding wheelAreaRef. Built-in wheel handling is disabled. Event handlers onTransformed, onZoom, and onPanning measure overflow and clamp movement using getViewportElement(). Panning clamping applies PAN_PADDING with wrapper dimensions from the derived viewport element.
Unit tests for wheel zoom behavior
frontend/src/components/Media/__tests__/ZoomableImage.test.tsx
Jest test suite added for wheel-zoom functionality with mocks for convertFileSrc and react-zoom-pan-pinch imperative API. Test verifies wheel event triggers correct setTransform calls with computed translation and scale values.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • AOSSIE-Org/PictoPy#835: Both PRs modify frontend/src/components/Media/ZoomableImage.tsx's wheel-zoom and panning implementation, including custom wheel interceptor logic, overflow measurement, and transform state handling.

Suggested labels

TypeScript/JavaScript

Suggested reviewers

  • rahulharpal1603

Poem

🐰 Wheels spin and scales align,
Mouse-anchored zoom does now divine,
Viewport abstraction holds the ground,
Overflow measured all around,
Production builds now zoom just right!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: fixing zoom scroll behavior in production builds, which is the core focus of this changeset.
Linked Issues check ✅ Passed The code changes implement all requirements from issue #1293: dynamic minimum zoom with viewport-aware fitScale, axis-dependent zoom anchoring (centered for non-overflowing axes, mouse-anchored for overflowing), automatic re-centering at minimum zoom, and stable wheel listener attachment to prevent production-only regressions.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the zoom-on-scroll regression: refactoring ZoomableImage's wheel handling, overflow detection, and viewport measurements, plus adding focused regression tests. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/components/Media/__tests__/ZoomableImage.test.tsx`:
- Around line 97-127: Add two new tests in ZoomableImage.test.tsx alongside the
existing "stable container intercept" case: (1) an axis-dependent anchoring test
that sets up mockElementRect so only one axis overflows (e.g., width larger than
container but height fits), fires a wheel event and asserts mockSetTransform was
called with the expected anchor coordinates and scale change (use the same
pattern as the existing test referencing wheelArea, image, fireEvent.wheel, and
mockSetTransform), and (2) a recenter-on-min-scale test that starts at >min
scale, simulates a zoom-out to the minimum scale via wheel events, and asserts
that the transform was reset to the centered coordinates (relying on
ZoomableImage, mockElementRect, and mockSetTransform to verify recentering).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cf69a53e-2307-4afe-88f9-84a1a37efe16

📥 Commits

Reviewing files that changed from the base of the PR and between b88b61d and e84a26b.

📒 Files selected for processing (2)
  • frontend/src/components/Media/ZoomableImage.tsx
  • frontend/src/components/Media/__tests__/ZoomableImage.test.tsx

Comment thread frontend/src/components/Media/__tests__/ZoomableImage.test.tsx
@rohan-pandeyy
Copy link
Copy Markdown
Contributor

@VanshajPoonia can you create a test release in your fork for me to test the implementation?

@rohan-pandeyy rohan-pandeyy added the question Further information is requested label May 29, 2026
@VanshajPoonia
Copy link
Copy Markdown
Contributor Author

Sure, I created a test release in my fork:

https://github.com/VanshajPoonia/PictoPy/releases/tag/v1.1.0-pr1295-test.1

The release is for testing the zoom on scroll implementation from this PR

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

Labels

bug Something isn't working build frontend question Further information is requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: Dynamic Zoom on Scroll Not Working in Production

2 participants