fix(deliverables): scroll the split review columns sideways together
This commit is contained in:
1 parent
142ca8b956
commit
26e2196a86
3 files changed
+23
-7
No files matched your search
@@ -1,4 +1,4 @@
|
||||
/** Review tab: a file selector and icon tools in the document preview's header row, then numbered diff lines in the code font. Unwrapped split columns scroll sideways on their own; wrapped ones share rows so the sides stay aligned. */
|
||||
/** Review tab: a file selector and icon tools in the document preview's header row, then numbered diff lines in the code font. Unwrapped split columns clip their lines and scroll sideways together; wrapped ones share rows so the sides stay aligned. */
|
||||
.root { display: flex; flex-direction: column; box-sizing: border-box; width: 100%; height: 100%; min-height: 0; color: var(--dsw-alias-label-primary); }
|
||||
.header { display: flex; flex: 0 0 auto; gap: 6px; align-items: center; box-sizing: border-box; height: 38px; padding: 0 6px 0 8px; border-bottom: 0.5px solid var(--dsw-alias-border-l3); }
|
||||
.selector { flex: 0 1 auto; min-width: 0; }
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* selected file's turn-start and turn-end comparison drawn unified or side by
|
||||
* side, wrapped or scrolling, and controls to open the file itself.
|
||||
*/
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { ReactNode, UIEvent } from 'react'
|
||||
import {
|
||||
Button, IconChevronDownOutline14, IconCodeOutline16, IconPanelLeftOutline16, IconRightUpOutline16, IconWrapLinesOutline16, Menu, Tooltip,
|
||||
} from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
@@ -271,16 +271,24 @@ function hunkHeader(hunk: WorkspaceDiffHunk): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* The side-by-side view without wrapping: two columns that scroll sideways on
|
||||
* their own, so a long line on one side never runs under the other. Every
|
||||
* line is one fixed-height row, which keeps the sides aligned.
|
||||
* The side-by-side view without wrapping: two columns that clip their long
|
||||
* lines and scroll sideways together, so a long line on one side never runs
|
||||
* under the other and both sides show the same columns of text. Every line is
|
||||
* one fixed-height row, which keeps the sides aligned.
|
||||
*/
|
||||
function SplitColumns({ hunks }: { hunks: readonly WorkspaceDiffHunk[] }): ReactNode {
|
||||
const paired = useMemo(() => hunks.map(hunk => ({ header: hunkHeader(hunk), rows: splitRows(hunk) })), [hunks])
|
||||
const columns = useRef<Record<'left' | 'right', HTMLDivElement | null>>({ left: null, right: null })
|
||||
// Mirror one side's horizontal offset onto the other; the mirrored side's own scroll event then finds nothing to change.
|
||||
const follow = (side: 'left' | 'right') => (event: UIEvent<HTMLDivElement>): void => {
|
||||
const other = columns.current[side === 'left' ? 'right' : 'left']
|
||||
if (other !== null && other.scrollLeft !== event.currentTarget.scrollLeft) other.scrollLeft = event.currentTarget.scrollLeft
|
||||
}
|
||||
return (
|
||||
<div className={css.columns}>
|
||||
{(['left', 'right'] as const).map(side => (
|
||||
<div key={side} className={css.column} data-diff-side={side}>
|
||||
<div key={side} className={css.column} data-diff-side={side}
|
||||
ref={(element) => { columns.current[side] = element }} onScroll={follow(side)}>
|
||||
{paired.map((hunk, position) => (
|
||||
<section key={position} className={css.hunk}>
|
||||
<div className={css.hunkHeader}>{hunk.header}</div>
|
||||
|
||||
@@ -238,6 +238,14 @@ describe('ReviewTab', () => {
|
||||
expect(side('left').map(line => line.getAttribute('data-diff-line'))).toEqual(['context', 'del', 'add', 'context', 'del', 'del'])
|
||||
expect(side('left').map(line => line.textContent)).toEqual(['1a', '2b', '', '3d', '10x', '20z'])
|
||||
expect(side('right').map(line => line.textContent)).toEqual(['1a', '2B', '3c', '4d', '11y', ''])
|
||||
// The two sides scroll sideways together, whichever side the reader drags.
|
||||
const [left, right] = ['left', 'right'].map(name => view.container.querySelector(`[data-diff-side="${name}"]`) as HTMLDivElement)
|
||||
fireEvent.scroll(left!, { target: { scrollLeft: 40 } })
|
||||
expect(right!.scrollLeft).toBe(40)
|
||||
fireEvent.scroll(right!, { target: { scrollLeft: 15 } })
|
||||
expect(left!.scrollLeft).toBe(15)
|
||||
fireEvent.scroll(right!, { target: { scrollLeft: 15 } })
|
||||
expect(left!.scrollLeft).toBe(15)
|
||||
expect(view.getByRole('button', { name: en['review.splitAria'] }).getAttribute('aria-pressed')).toBe('true')
|
||||
// Wrapped lines vary in height, so both sides share one row per pair.
|
||||
fireEvent.click(view.getByRole('button', { name: en['review.wrapAria'] }))
|
||||
|
||||
Reference in new issue
Block a user