mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(coach): keep the composer one row tall until someone types
On a phone the composer stacked its controls under the textarea, so an EMPTY input was two rows tall: a text line, a 44 px tap-target row, and padding. That is roughly a tenth of a phone screen spent before anyone has typed anything, and it is the first thing on the screen when the Coach opens. The auto-grow was never the problem. The textarea already starts at one line and grows to six; it was the layout around it that reserved the second row up front. The stacking existed to protect keyboard focus order. That argument does not hold: focus follows DOM order, and the DOM order is textarea, actions, Send in both layouts. Nothing jumps. `items-end` keeps the controls beside the last line as the textarea grows, so the send button stays where the thumb left it. One component serves both surfaces — the conversation view and the hero, which re-parents the same live composer — so this is one fix rather than two. The test that asserted the stacked layout now asserts the row, with the reason written down, and it fails when the stack comes back.
This commit is contained in:
1 parent
8a2bb70dbd
commit
d7badf1512
2 files changed
+27
-9
No files matched your search
@@ -228,7 +228,7 @@ describe("<CoachInput>", () => {
|
||||
expect(html).toContain('data-slot="coach-input-send"');
|
||||
});
|
||||
|
||||
it("puts the textarea above a split control row on phones", () => {
|
||||
it("keeps the textarea and its controls on one row at every width", () => {
|
||||
const html = render(
|
||||
<CoachInput
|
||||
value=""
|
||||
@@ -258,10 +258,20 @@ describe("<CoachInput>", () => {
|
||||
const controls = html.match(
|
||||
/<div[^>]*data-slot="coach-input-controls"[^>]*>/,
|
||||
);
|
||||
expect(controls?.[0]).toMatch(/\bw-full\b/);
|
||||
expect(controls?.[0]).toMatch(/\bjustify-between\b/);
|
||||
expect(controls?.[0]).toMatch(/\bsm:w-auto\b/);
|
||||
expect(controls?.[0]).not.toMatch(/\bsm:contents\b/);
|
||||
// The controls sit BESIDE the textarea, not under it. Stacking them made an
|
||||
// empty composer two rows tall — a text line plus a 44 px tap-target row —
|
||||
// which is roughly a tenth of a phone screen before anyone has typed. The
|
||||
// controls must therefore never claim the full width or push themselves
|
||||
// onto their own line.
|
||||
expect(controls?.[0]).not.toMatch(/\bw-full\b/);
|
||||
expect(controls?.[0]).not.toMatch(/\bjustify-between\b/);
|
||||
expect(controls?.[0]).toMatch(/\bshrink-0\b/);
|
||||
|
||||
// And the row itself is a row at every width, bottom-aligned so the send
|
||||
// button stays beside the last line as the textarea grows.
|
||||
const shell = html.match(/<div[^>]*class="[^"]*\bflex-row\b[^"]*"[^>]*>/);
|
||||
expect(shell?.[0]).toMatch(/\bitems-end\b/);
|
||||
expect(shell?.[0]).not.toMatch(/\bflex-col\b/);
|
||||
});
|
||||
|
||||
it("sizes the hub actions trigger to the 44px tap-target floor on phones", () => {
|
||||
|
||||
@@ -419,9 +419,17 @@ export function CoachInput({
|
||||
"border-border/60 bg-muted/40 group rounded-2xl border",
|
||||
"shadow-sm transition-colors",
|
||||
"focus-within:border-primary/50 focus-within:ring-primary/50 focus-within:bg-background focus-within:ring-2",
|
||||
// Mobile uses two rows. Desktop keeps the same DOM and visual order
|
||||
// (textarea, actions, Send) so keyboard focus never jumps backwards.
|
||||
"flex flex-col gap-1.5 p-1.5 sm:flex-row sm:items-end",
|
||||
// One row at every width. Mobile used to stack the controls under the
|
||||
// textarea, which made an EMPTY composer two rows tall: a 44 px line
|
||||
// plus a 44 px touch-target row plus padding, around a tenth of a
|
||||
// phone screen before anyone had typed anything. The stacking was
|
||||
// there to protect keyboard focus order, but that argument does not
|
||||
// hold: focus follows DOM order, and the DOM order (textarea,
|
||||
// actions, Send) is identical in both layouts. Nothing jumps.
|
||||
//
|
||||
// `items-end` keeps the controls beside the LAST line as the textarea
|
||||
// grows, so the send button stays where the thumb left it.
|
||||
"flex flex-row items-end gap-1.5 p-1.5",
|
||||
)}
|
||||
>
|
||||
<textarea
|
||||
@@ -447,7 +455,7 @@ export function CoachInput({
|
||||
/>
|
||||
<div
|
||||
data-slot="coach-input-controls"
|
||||
className="order-2 flex w-full items-center justify-between gap-1.5 sm:w-auto sm:shrink-0 sm:justify-start"
|
||||
className="order-2 flex shrink-0 items-center gap-1.5"
|
||||
>
|
||||
<div
|
||||
data-slot="coach-input-leading"
|
||||
|
||||
Reference in new issue
Block a user