fix(composer): autosize Create Worktree note on PR prefill (#10580)
* fix(composer): autosize Create Worktree note on PR prefill The Note textarea only grew on onInput, so programmatic PR title prefills left the box at one row with overflow hidden. Resize whenever the note value changes and allow scroll under max-height (fixes #10575). * review: size the composer note with field-sizing instead of a measure pass The prefill bug is the failure mode of imperative sizing: the height is only recomputed at the events someone remembered to hook, so a programmatic setNote (and a pane resize, and a font reflow) leaves it stale. Let the layout engine own the height, matching NativeChatComposerField and LinearIssueTextEditor. Drops the extracted helper and its unit test — that test asserted the two assignments it wrote and stayed green with the bug present. The composer test now pins the class contract and goes red on the pre-fix markup. --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
This commit is contained in:
parent
1d3decdbc4
commit
ac830689c4
|
|
@ -871,3 +871,42 @@ describe('NewWorkspaceComposerCard folder task source mode', () => {
|
|||
expect(recipeChanges).toEqual([null])
|
||||
})
|
||||
})
|
||||
|
||||
describe('NewWorkspaceComposerCard note sizing', () => {
|
||||
// Sizing is layout-driven (field-sizing) rather than a JS measure pass, and happy-dom
|
||||
// has no layout engine, so these assert the class contract that produces the growth.
|
||||
afterEach(() => {
|
||||
act(() => current?.root.unmount())
|
||||
current?.container.remove()
|
||||
current = null
|
||||
})
|
||||
|
||||
function findNoteTextarea(container: HTMLElement): HTMLTextAreaElement {
|
||||
const label = [...container.querySelectorAll('label')].find(
|
||||
(candidate) => candidate.textContent?.trim() === 'Note'
|
||||
)
|
||||
const textarea = label?.parentElement?.querySelector('textarea')
|
||||
expect(textarea).toBeTruthy()
|
||||
return textarea as HTMLTextAreaElement
|
||||
}
|
||||
|
||||
it('sizes from the note value, so a PR prefill written straight to state still shows in full', () => {
|
||||
// #10575: the prefill never fires an input event, so nothing but the value can drive height.
|
||||
current = renderCard({
|
||||
advancedOpen: true,
|
||||
note: `PR #10575 — ${'a note title long enough to wrap over several lines '.repeat(3)}`
|
||||
})
|
||||
|
||||
expect(findNoteTextarea(current.container).className).toContain('[field-sizing:content]')
|
||||
})
|
||||
|
||||
it('keeps a note past the height cap readable instead of clipping it', () => {
|
||||
current = renderCard({ advancedOpen: true, note: 'a'.repeat(4000) })
|
||||
|
||||
const { className } = findNoteTextarea(current.container)
|
||||
expect(className).toContain('max-h-40')
|
||||
expect(className).toContain('overflow-y-auto')
|
||||
expect(className).toContain('scrollbar-sleek')
|
||||
expect(className).not.toContain('overflow-hidden')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1001,18 +1001,15 @@ export default function NewWorkspaceComposerCard({
|
|||
value={note}
|
||||
onChange={(event) => onNoteChange(event.target.value)}
|
||||
onPaste={handleNotePaste}
|
||||
onInput={(event) => {
|
||||
// Why: reset then size to content so short notes stay compact and long ones grow without a scrollbar until max-h clamps.
|
||||
const ta = event.currentTarget
|
||||
ta.style.height = 'auto'
|
||||
ta.style.height = `${ta.scrollHeight}px`
|
||||
}}
|
||||
placeholder={translate(
|
||||
'auto.components.NewWorkspaceComposerCard.090cfedeb4',
|
||||
'Write a note'
|
||||
)}
|
||||
rows={1}
|
||||
className="w-full min-w-0 resize-none overflow-hidden rounded-md border border-input bg-transparent px-3 py-1.5 text-sm shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 max-h-40"
|
||||
// Why (#10575): field-sizing:content grows the note with its value, so a PR/MR
|
||||
// prefill written straight to state sizes like typed text — an onInput measure
|
||||
// pass never saw it. Past the max-h clamp the sleek scrollbar keeps it readable.
|
||||
className="w-full min-w-0 resize-none overflow-y-auto scrollbar-sleek rounded-md border border-input bg-transparent px-3 py-1.5 text-sm shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [field-sizing:content] max-h-40"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue