Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 3.8k
fix(files): let a mouse wheel scroll CSV and XLSX previews horizontally#6563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Uh oh!
There was an error while loading. Please reload this page.
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
b019698
fix(files): let a mouse wheel scroll CSV and XLSX previews horizontally
waleedlatif1 2c11f91
fix(files): keep the vertical component of a diagonal wheel pan
waleedlatif1 f987a15
fix(files): normalize wheel deltas to pixels and import the hook abso…
waleedlatif1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
4 changes: 3 additions & 1 deletion
4 apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/csv-table-preview.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 3 additions & 1 deletion
4 apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/preview-panel.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
135 changes: 135 additions & 0 deletions
135 apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/preview-wheel-zoom.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| /** | ||
| * @vitest-environment jsdom | ||
| * | ||
| * A mouse whose wheel reports only `deltaY` has no native gesture for reaching a preview | ||
| * table's horizontal overflow short of dragging the scrollbar, so the tabular previews bind | ||
| * `bindPreviewHorizontalWheel`. It must move the container on a horizontal gesture, stay out | ||
| * of the way otherwise, and — unlike the zooming variant — leave ctrl/cmd+wheel to the browser | ||
| * so page zoom still works over a table. | ||
| */ | ||
| import { beforeEach, describe, expect, it } from 'vitest' | ||
| import { bindPreviewHorizontalWheel } from '@/app/workspace/[workspaceId]/files/components/file-viewer/preview-wheel-zoom' | ||
| /** jsdom does no layout, so scrollWidth/clientWidth are stubbed to model an overflowing container. */ | ||
| function makeContainer({ scrollWidth = 2000, clientWidth = 1000 } = {}): HTMLElement { | ||
| const el = document.createElement('div') | ||
| Object.defineProperty(el, 'scrollWidth', { value: scrollWidth, configurable: true }) | ||
| Object.defineProperty(el, 'clientWidth', { value: clientWidth, configurable: true }) | ||
| el.scrollLeft = 0 | ||
| document.body.appendChild(el) | ||
| return el | ||
| } | ||
| function wheel(el: HTMLElement, init: WheelEventInit): WheelEvent { | ||
| const event = new WheelEvent('wheel', { bubbles: true, cancelable: true, ...init }) | ||
| el.dispatchEvent(event) | ||
| return event | ||
| } | ||
| describe('bindPreviewHorizontalWheel', () => { | ||
| let container: HTMLElement | ||
| let unbind: () => void | ||
| beforeEach(() => { | ||
| document.body.innerHTML = '' | ||
| container = makeContainer() | ||
| unbind = bindPreviewHorizontalWheel(container) | ||
| }) | ||
| it("scrolls by a trackpad's horizontal delta", () => { | ||
| const event = wheel(container, { deltaX: 120, deltaY: 0 }) | ||
| expect(container.scrollLeft).toBe(120) | ||
| expect(event.defaultPrevented).toBe(true) | ||
| }) | ||
| it('maps shift+wheel to horizontal for a vertical-only mouse', () => { | ||
| const event = wheel(container, { deltaX: 0, deltaY: 120, shiftKey: true }) | ||
| expect(container.scrollLeft).toBe(120) | ||
| expect(event.defaultPrevented).toBe(true) | ||
| }) | ||
| /** | ||
| * Cancelling a wheel event is all-or-nothing, so a diagonal trackpad pan must have its | ||
| * vertical movement re-applied by hand — otherwise `preventDefault` silently eats it. | ||
| */ | ||
| it('keeps the vertical movement of a diagonal pan', () => { | ||
| Object.defineProperty(container, 'scrollHeight', { value: 5000, configurable: true }) | ||
| Object.defineProperty(container, 'clientHeight', { value: 500, configurable: true }) | ||
| wheel(container, { deltaX: 40, deltaY: 90 }) | ||
| expect(container.scrollLeft).toBe(40) | ||
| expect(container.scrollTop).toBe(90) | ||
| }) | ||
| it('does not also spend a shift gesture vertically', () => { | ||
| wheel(container, { deltaX: 0, deltaY: 120, shiftKey: true }) | ||
| expect(container.scrollLeft).toBe(120) | ||
| expect(container.scrollTop).toBe(0) | ||
| }) | ||
| /** | ||
| * `deltaMode` is not always pixels — Firefox reports mouse wheels in lines — while scroll | ||
| * offsets always are, so a three-line notch added raw would move the table three pixels. | ||
| */ | ||
| it('converts a line-mode delta to pixels', () => { | ||
| wheel(container, { deltaX: 3, deltaY: 0, deltaMode: WheelEvent.DOM_DELTA_LINE }) | ||
| expect(container.scrollLeft).toBe(48) | ||
| }) | ||
| it('converts a page-mode delta to the container width', () => { | ||
| wheel(container, { deltaX: 1, deltaY: 0, deltaMode: WheelEvent.DOM_DELTA_PAGE }) | ||
| expect(container.scrollLeft).toBe(1000) | ||
| }) | ||
| it('leaves a plain vertical wheel alone so the container still scrolls down', () => { | ||
| const event = wheel(container, { deltaX: 0, deltaY: 120 }) | ||
| expect(container.scrollLeft).toBe(0) | ||
| expect(event.defaultPrevented).toBe(false) | ||
| }) | ||
| /** Zoom is the browser's here — the tabular previews have no zoom of their own. */ | ||
| it.each([ | ||
| ['ctrl', { ctrlKey: true }], | ||
| ['cmd', { metaKey: true }], | ||
| ])('leaves %s+wheel to the browser', (_label, modifier) => { | ||
| const event = wheel(container, { deltaX: 120, deltaY: 0, ...modifier }) | ||
| expect(container.scrollLeft).toBe(0) | ||
| expect(event.defaultPrevented).toBe(false) | ||
| }) | ||
| it('does nothing when the container has no horizontal overflow', () => { | ||
| const fitted = makeContainer({ scrollWidth: 1000, clientWidth: 1000 }) | ||
| const unbindFitted = bindPreviewHorizontalWheel(fitted) | ||
| const event = wheel(fitted, { deltaX: 120, deltaY: 0 }) | ||
| expect(fitted.scrollLeft).toBe(0) | ||
| expect(event.defaultPrevented).toBe(false) | ||
| unbindFitted() | ||
| }) | ||
| it('stops scrolling once unbound', () => { | ||
| unbind() | ||
| wheel(container, { deltaX: 120, deltaY: 0 }) | ||
| expect(container.scrollLeft).toBe(0) | ||
| }) | ||
| it('scrolls a child gesture, since the listener captures', () => { | ||
| const cell = document.createElement('td') | ||
| container.appendChild(cell) | ||
| wheel(cell, { deltaX: 80, deltaY: 0 }) | ||
| expect(container.scrollLeft).toBe(80) | ||
| }) | ||
| }) |
73 changes: 68 additions & 5 deletions
73 apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/preview-wheel-zoom.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21 ...m/app/workspace/[workspaceId]/files/components/file-viewer/use-horizontal-wheel-scroll.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| 'use client' | ||
| import { useCallback, useRef } from 'react' | ||
| import { bindPreviewHorizontalWheel } from '@/app/workspace/[workspaceId]/files/components/file-viewer/preview-wheel-zoom' | ||
| /** | ||
| * Ref callback that gives a preview scroll container horizontal wheel scrolling. | ||
| * | ||
| * The tabular previews render a table wider than its frame, and a mouse whose wheel | ||
| * reports only `deltaY` has no native way to reach the overflow short of dragging the | ||
| * scrollbar. Binding is done through a ref callback rather than an effect so the | ||
| * listener attaches with the node and detaches when React passes `null`. | ||
| */ | ||
| export function useHorizontalWheelScroll() { | ||
| const unbindRef = useRef<(() => void) | null>(null) | ||
| return useCallback((node: HTMLDivElement | null) => { | ||
| unbindRef.current?.() | ||
| unbindRef.current = node ? bindPreviewHorizontalWheel(node) : null | ||
| }, []) | ||
| } |
4 changes: 3 additions & 1 deletion
4 apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/xlsx-preview.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.