Skip to content

Commit cb225b6

Browse files
committed
fix(tables): route reference preview wheel scrolling
1 parent 81b8847 commit cb225b6

2 files changed

Lines changed: 68 additions & 7 deletions

File tree

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/reference-row-preview.test.tsx

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ describe('ReferenceRowPreview', () => {
141141
const subtable = container.querySelector('[role="table"]')
142142
expect(subtable?.className).toContain('w-full')
143143
expect(subtable?.className).toContain('h-full')
144-
expect(subtable?.className).toContain('cursor-default')
145-
expect(subtable?.className).toContain('select-none')
144+
expect(subtable?.className).not.toContain('cursor-default')
145+
expect(subtable?.className).not.toContain('select-none')
146146
expect(subtable?.className).toContain('grid-rows-2')
147147
expect(subtable?.querySelectorAll('[role="row"]')).toHaveLength(2)
148148
expect(subtable?.querySelectorAll('[role="columnheader"]')).toHaveLength(2)
@@ -163,6 +163,41 @@ describe('ReferenceRowPreview', () => {
163163
expect(container.innerHTML).not.toContain('rounded-md')
164164
})
165165

166+
it('scrolls horizontally when wheel input starts on cell text', () => {
167+
renderPreview()
168+
169+
const subtableViewport = container.querySelector<HTMLElement>('.overscroll-x-contain')
170+
const cellText = Array.from(container.querySelectorAll('[role="cell"] span')).find(
171+
(element) => element.textContent === 'Acme'
172+
)
173+
if (!subtableViewport || !cellText) throw new Error('Expected the referenced row preview')
174+
175+
const wheelEvent = new WheelEvent('wheel', {
176+
bubbles: true,
177+
cancelable: true,
178+
deltaX: 80,
179+
})
180+
act(() => {
181+
cellText.dispatchEvent(wheelEvent)
182+
})
183+
184+
expect(subtableViewport.scrollLeft).toBe(80)
185+
expect(wheelEvent.defaultPrevented).toBe(true)
186+
187+
const verticalWheelEvent = new WheelEvent('wheel', {
188+
bubbles: true,
189+
cancelable: true,
190+
deltaX: 10,
191+
deltaY: 80,
192+
})
193+
act(() => {
194+
cellText.dispatchEvent(verticalWheelEvent)
195+
})
196+
197+
expect(subtableViewport.scrollLeft).toBe(80)
198+
expect(verticalWheelEvent.defaultPrevented).toBe(false)
199+
})
200+
166201
it('sizes the inner scroller to the visible portion of the preview cell', () => {
167202
let previewCellRight = 1_500
168203
vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(function () {
@@ -248,12 +283,18 @@ describe('ReferenceRowPreview', () => {
248283
const previewShell = container.querySelector<HTMLElement>('tbody > tr > td > div > div')
249284
const scrollRoot = container.querySelector<HTMLElement>('[data-table-scroll]')
250285
const previewCell = container.querySelector<HTMLElement>('tbody > tr > td')
286+
const previewViewport = container.querySelector<HTMLElement>('.overscroll-x-contain')
251287
if (!scrollRoot) throw new Error('Expected the table scroll root to be rendered')
252288
if (!previewCell) throw new Error('Expected the preview cell to be rendered')
289+
if (!previewViewport) throw new Error('Expected the preview viewport to be rendered')
253290
const scrollListener = registeredListeners.find(
254291
({ target, type }) => target === scrollRoot && type === 'scroll'
255292
)?.listener
293+
const wheelListener = registeredListeners.find(
294+
({ target, type }) => target === previewViewport && type === 'wheel'
295+
)?.listener
256296
if (!scrollListener) throw new Error('Expected the scroll listener to be registered')
297+
if (!wheelListener) throw new Error('Expected the wheel listener to be registered')
257298
expect(observe).toHaveBeenCalledTimes(2)
258299
expect(observe.mock.calls.some(([target]) => target === scrollRoot)).toBe(true)
259300
expect(observe.mock.calls.some(([target]) => target === previewCell)).toBe(true)
@@ -274,6 +315,11 @@ describe('ReferenceRowPreview', () => {
274315
type: 'scroll',
275316
listener: scrollListener,
276317
})
318+
expect(removedListeners).toContainEqual({
319+
target: previewViewport,
320+
type: 'wheel',
321+
listener: wheelListener,
322+
})
277323
})
278324

279325
it('shows no match when the stored row ID does not resolve', () => {

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/reference-row-preview.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const ReferenceRowPreview = memo(function ReferenceRowPreview({
3535
}: ReferenceRowPreviewProps) {
3636
const previewCellRef = useRef<HTMLTableCellElement>(null)
3737
const previewShellRef = useRef<HTMLDivElement>(null)
38+
const previewViewportRef = useRef<HTMLDivElement>(null)
3839
const tableQuery = useTable(workspaceId, referenceTableId)
3940
const rowQuery = useTableRow(workspaceId, referenceTableId, referenceRowId)
4041
const table = tableQuery.data
@@ -86,6 +87,20 @@ export const ReferenceRowPreview = memo(function ReferenceRowPreview({
8687
}
8788
}, [])
8889

90+
useLayoutEffect(() => {
91+
const previewViewport = previewViewportRef.current
92+
if (!previewViewport) return
93+
94+
const handleWheel = (event: WheelEvent) => {
95+
if (Math.abs(event.deltaX) <= Math.abs(event.deltaY)) return
96+
event.preventDefault()
97+
previewViewport.scrollLeft += event.deltaX
98+
}
99+
100+
previewViewport.addEventListener('wheel', handleWheel, { passive: false })
101+
return () => previewViewport.removeEventListener('wheel', handleWheel)
102+
}, [])
103+
89104
let content: ReactNode
90105
if (tableQuery.isLoading || rowQuery.isLoading) {
91106
content = (
@@ -114,10 +129,7 @@ export const ReferenceRowPreview = memo(function ReferenceRowPreview({
114129
)
115130
} else {
116131
content = (
117-
<div
118-
role='table'
119-
className='grid h-full w-full min-w-max cursor-default select-none grid-rows-2 text-small'
120-
>
132+
<div role='table' className='grid h-full w-full min-w-max grid-rows-2 text-small'>
121133
<div role='row' className='flex min-w-max'>
122134
{columns.map((column) => (
123135
<div
@@ -178,7 +190,10 @@ export const ReferenceRowPreview = memo(function ReferenceRowPreview({
178190
<span className='font-medium'>{table?.name ?? 'Referenced table'}</span>
179191
</div>
180192

181-
<div className='h-[72px] shrink-0 overflow-x-auto overflow-y-hidden overscroll-x-contain border-[var(--border)] border-y bg-[var(--bg)]'>
193+
<div
194+
ref={previewViewportRef}
195+
className='h-[72px] shrink-0 overflow-x-auto overflow-y-hidden overscroll-x-contain border-[var(--border)] border-y bg-[var(--bg)]'
196+
>
182197
{content}
183198
</div>
184199

0 commit comments

Comments
 (0)