mirror of
https://github.com/block/buzz.git
synced 2026-11-02 07:22:55 +00:00
fix(desktop): align jump-to-latest pill with composer height (#6606)
**Category:** fix **User Impact:** Jump to Latest now stays above the composer as a draft grows to multiple lines. **Problem:** On WebKit, the pill's transform could retain a stale inherited composer-height value after the composer expanded, leaving the control stranded inside the composer. **Solution:** Position and animate the pill with its absolute bottom offset, which consumes the live composer height through layout rather than a promoted transform layer. A smoke test now verifies that the pill rises by the full composer growth and remains clear of the composer. <details> <summary>File changes</summary> **desktop/src/features/messages/ui/MessageTimeline.tsx** Anchor Jump to Latest with a live bottom offset instead of a translated compositor layer so composer resizing reliably moves it. **desktop/tests/e2e/smoke.spec.ts** Add coverage that expands a detached timeline's composer and checks the pill tracks the full height increase without overlapping it. </details> ## Reproduction steps 1. Open a channel with enough messages to scroll. 2. Scroll away from the newest message until Jump to Latest appears. 3. Add several lines to the composer without sending. 4. Confirm Jump to Latest rises with the composer and remains directly above it. ## Validation - `pnpm --dir desktop test` — 5,397 passed - `pnpm --dir desktop check` — passed with four existing informational warnings outside this diff - `pnpm --dir desktop typecheck` — passed - `pnpm --dir desktop exec playwright test tests/e2e/smoke.spec.ts --project=smoke` — 26 passed - Push hooks — desktop check, typecheck, and tests passed ## Screenshots / Demos Both captures use the same long, mid-history timeline and the same four-line composer. | Before | After | | --- | --- | | The stale pill position overflows into the expanded composer. | The pill tracks the live composer height and stays clear above it. | |  |  | Signed-off-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -863,8 +863,12 @@ const MessageTimelineBase = React.forwardRef<
|
||||
<div
|
||||
className={cn(
|
||||
"pointer-events-none absolute inset-x-0 bottom-4 z-50 flex justify-center px-4",
|
||||
// Position the pill with layout rather than a transform. WebKit
|
||||
// can keep an inherited custom property stale on a promoted
|
||||
// transform layer when the composer grows, leaving the pill
|
||||
// stranded inside the dock.
|
||||
hasComposerOverlay &&
|
||||
"translate-y-[calc(-1*var(--composer-overlay-height,8rem))] transform-gpu transition-transform duration-200 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none",
|
||||
"bottom-[calc(1rem+var(--composer-overlay-height,8rem))] transition-[bottom] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none",
|
||||
)}
|
||||
>
|
||||
<UnreadPill
|
||||
|
||||
@@ -892,3 +892,55 @@ test("does not shift the timeline when the composer grows", async ({
|
||||
expect(Math.abs(after.scrollTop - before.scrollTop)).toBeLessThanOrEqual(2);
|
||||
expect(after.distanceFromBottom).toBeGreaterThan(160);
|
||||
});
|
||||
|
||||
test("lifts Jump to latest when the composer grows", async ({ page }) => {
|
||||
const input = page.getByTestId("message-input");
|
||||
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("general");
|
||||
|
||||
await ensureTimelineScrollable(page, `Jump pill growth ${Date.now()}`);
|
||||
await page.waitForTimeout(400);
|
||||
const timeline = page.getByTestId("message-timeline");
|
||||
await timeline.evaluate((element) => {
|
||||
element.dispatchEvent(new WheelEvent("wheel", { deltaY: -100 }));
|
||||
element.scrollTop = 0;
|
||||
element.dispatchEvent(new Event("scroll", { bubbles: true }));
|
||||
});
|
||||
|
||||
const jumpToLatest = page.getByTestId("message-scroll-to-latest");
|
||||
const composer = page.getByTestId("message-composer");
|
||||
await expect(jumpToLatest).toBeVisible();
|
||||
const initialPillBox = await jumpToLatest.boundingBox();
|
||||
const initialComposerBox = await composer.boundingBox();
|
||||
|
||||
await input.fill(
|
||||
[
|
||||
"Composer growth line one",
|
||||
"Composer growth line two",
|
||||
"Composer growth line three",
|
||||
"Composer growth line four",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
await expect
|
||||
.poll(async () => (await composer.boundingBox())?.height ?? 0)
|
||||
.toBeGreaterThan((initialComposerBox?.height ?? 0) + 40);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
const expandedPillBox = await jumpToLatest.boundingBox();
|
||||
const expandedComposerBox = await composer.boundingBox();
|
||||
expect(initialPillBox).not.toBeNull();
|
||||
expect(initialComposerBox).not.toBeNull();
|
||||
expect(expandedPillBox).not.toBeNull();
|
||||
expect(expandedComposerBox).not.toBeNull();
|
||||
|
||||
const composerGrowth =
|
||||
(expandedComposerBox?.height ?? 0) - (initialComposerBox?.height ?? 0);
|
||||
const pillLift = (initialPillBox?.y ?? 0) - (expandedPillBox?.y ?? 0);
|
||||
expect(pillLift).toBeGreaterThanOrEqual(composerGrowth - 2);
|
||||
expect(
|
||||
(expandedPillBox?.y ?? 0) + (expandedPillBox?.height ?? 0),
|
||||
).toBeLessThanOrEqual(expandedComposerBox?.y ?? 0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user