perf(editor): skip unchanged review-note reflows (#17212)

This commit is contained in:
Neil
2026-08-29 16:01:27 -07:00
committed by GitHub
parent 80a0595449
commit eb6cd67219
2 changed files with 62 additions and 7 deletions
@@ -1,5 +1,5 @@
// @vitest-environment happy-dom
import { act, cleanup, render } from '@testing-library/react'
import { act, cleanup, fireEvent, render } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { DiffCommentCard } from './DiffCommentCard'
@@ -44,6 +44,7 @@ describe('DiffCommentCard content resize', () => {
afterEach(() => {
cleanup()
vi.restoreAllMocks()
vi.unstubAllGlobals()
})
@@ -101,4 +102,52 @@ describe('DiffCommentCard content resize', () => {
expect(constructObserver).not.toHaveBeenCalled()
expect(onContentResize).not.toHaveBeenCalled()
})
it('requests rail reflow only when the editing textarea height changes', () => {
const onContentResize = vi.fn()
let scrollHeight = 60
vi.spyOn(HTMLTextAreaElement.prototype, 'scrollHeight', 'get').mockImplementation(
() => scrollHeight
)
const view = render(
<DiffCommentCard
lineNumber={26}
body="A saved note"
onContentResize={onContentResize}
onSubmitEdit={async () => true}
/>
)
fireEvent.click(view.getByRole('button', { name: 'Edit note' }))
expect(onContentResize).toHaveBeenCalledOnce()
const textarea = view.getByRole('textbox')
expect(textarea.style.height).toBe('60px')
fireEvent.change(textarea, { target: { value: 'A saved note!' } })
expect(onContentResize).toHaveBeenCalledOnce()
for (let index = 0; index < 50; index++) {
fireEvent.change(textarea, { target: { value: `A saved note! ${index}` } })
}
expect(textarea.style.height).toBe('60px')
expect(onContentResize).toHaveBeenCalledOnce()
scrollHeight = 400
fireEvent.change(textarea, { target: { value: 'A much taller saved note' } })
expect(textarea.style.height).toBe('240px')
expect(onContentResize).toHaveBeenCalledTimes(2)
scrollHeight = 480
fireEvent.change(textarea, { target: { value: 'A still taller saved note' } })
expect(textarea.style.height).toBe('240px')
expect(onContentResize).toHaveBeenCalledTimes(2)
scrollHeight = 80
fireEvent.change(textarea, { target: { value: 'A shorter saved note' } })
expect(textarea.style.height).toBe('80px')
expect(onContentResize).toHaveBeenCalledTimes(3)
fireEvent.click(view.getByRole('button', { name: 'Cancel' }))
expect(onContentResize).toHaveBeenCalledTimes(4)
})
})
@@ -35,6 +35,13 @@ type Props = {
headerActions?: ReactNode
}
function resizeDiffCommentTextarea(textarea: HTMLTextAreaElement): boolean {
const previousHeight = textarea.style.height
textarea.style.height = 'auto'
textarea.style.height = `${Math.min(textarea.scrollHeight, 240)}px`
return textarea.style.height !== previousHeight
}
export function DiffCommentCard({
lineNumber,
startLine,
@@ -116,8 +123,7 @@ export function DiffCommentCard({
if (!el) {
return
}
el.style.height = 'auto'
el.style.height = `${Math.min(el.scrollHeight, 240)}px`
resizeDiffCommentTextarea(el)
el.focus()
el.setSelectionRange(el.value.length, el.value.length)
onContentResizeRef.current?.()
@@ -281,10 +287,10 @@ export function DiffCommentCard({
value={draft}
onChange={(e) => {
setDraft(e.target.value)
const el = e.currentTarget
el.style.height = 'auto'
el.style.height = `${Math.min(el.scrollHeight, 240)}px`
onContentResizeRef.current?.()
// Why: rich-review layout measures every note; skip it when this card stayed put.
if (resizeDiffCommentTextarea(e.currentTarget)) {
onContentResizeRef.current?.()
}
}}
onKeyDown={(e) => {
if (e.key === 'Escape') {