Skip to content

Commit 2a5418e

Browse files
authored
fix chat ordering and mobile composer (#79)
* fix chat ordering and mobile composer * nit
1 parent b2426e4 commit 2a5418e

9 files changed

Lines changed: 366 additions & 231 deletions

File tree

e2e/chat.spec.ts

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { expect, test } from "@playwright/test";
1+
import { expect, test, type Page } from "@playwright/test";
22
import {
33
DONOR_EMAIL,
44
HOST_EMAIL,
@@ -10,6 +10,19 @@ import {
1010
signIn,
1111
} from "./helpers";
1212

13+
async function expectMessageListAtBottom(page: Page) {
14+
await expect
15+
.poll(async () =>
16+
page.getByTestId("chat-message-list").evaluate((element) => {
17+
return (
18+
Math.ceil(element.scrollTop + element.clientHeight) >=
19+
element.scrollHeight - 1
20+
);
21+
})
22+
)
23+
.toBe(true);
24+
}
25+
1326
test("chat loads the seeded thread and composer for a signed-in donor", async ({
1427
page,
1528
}) => {
@@ -127,7 +140,11 @@ test("chat send disables the composer while pending and appends the new message"
127140
await expect(sendButton).toHaveAttribute("aria-busy", "true");
128141
await expect(composerInput).toBeDisabled();
129142
await expect(messageVisible).toBeVisible();
143+
await expectMessageListAtBottom(page);
130144
await expect(composerInput).toHaveValue("");
145+
await expect(
146+
page.getByTestId("thread-list").locator("a").first()
147+
).toHaveAttribute("data-testid", `thread-preview-${SEEDED_THREAD_ID}`);
131148
await expect
132149
.poll(async () =>
133150
page.evaluate((threadId) => {
@@ -271,3 +288,71 @@ test("invalid chat thread ids redirect back to the chat index", async ({
271288
await expect(page).toHaveURL(/\/chats$/);
272289
await expect(page.getByTestId("thread-list")).toBeVisible();
273290
});
291+
292+
test("mobile listing chat drawer keeps the composer visible as the draft grows", async ({
293+
page,
294+
}) => {
295+
await page.setViewportSize({ width: 390, height: 844 });
296+
await signIn(page, {
297+
email: DONOR_EMAIL,
298+
redirectTo: "/listings/demo-marrickville-compost",
299+
});
300+
301+
await page.getByRole("button", { name: "Contact Avery" }).click();
302+
await expect(page.getByTestId("chat-window")).toBeVisible();
303+
await expect
304+
.poll(async () =>
305+
page.locator("[data-vaul-drawer]").evaluate((drawer) => {
306+
const drawerRect = drawer.getBoundingClientRect();
307+
return (
308+
getComputedStyle(drawer).transform === "none" &&
309+
Math.ceil(drawerRect.bottom) <= window.innerHeight + 1
310+
);
311+
})
312+
)
313+
.toBe(true);
314+
315+
const composerInput = page.getByTestId("chat-composer-input");
316+
const sendButton = page.getByTestId("chat-composer-send");
317+
await composerInput.fill(
318+
[
319+
"Hi Avery, would it be possible to drop some scraps off tomorrow morning?",
320+
"About a lunchbox worth.",
321+
"Sorry for the late notice, just leaving town tomorrow.",
322+
"Please let me know what works best.",
323+
].join("\n")
324+
);
325+
326+
await expect(composerInput).toBeInViewport();
327+
await expect(sendButton).toBeInViewport();
328+
const layoutMetrics = await page
329+
.getByTestId("chat-composer")
330+
.evaluate((composer) => {
331+
const drawer = document.querySelector("[data-vaul-drawer]");
332+
const input = composer.querySelector("textarea");
333+
const composerRect = composer.getBoundingClientRect();
334+
const drawerRect = drawer?.getBoundingClientRect();
335+
const inputRect = input?.getBoundingClientRect();
336+
const drawerStyles = drawer ? getComputedStyle(drawer) : null;
337+
338+
return {
339+
bottomLeftRadius: parseFloat(
340+
drawerStyles?.borderBottomLeftRadius ?? "0"
341+
),
342+
composerBottom: Math.ceil(composerRect.bottom),
343+
drawerTop: Math.floor(drawerRect?.top ?? 0),
344+
inputHeight: Math.ceil(inputRect?.height ?? 0),
345+
topLeftRadius: parseFloat(drawerStyles?.borderTopLeftRadius ?? "0"),
346+
viewportHeight: window.innerHeight,
347+
};
348+
});
349+
350+
expect(layoutMetrics.topLeftRadius).toBeGreaterThan(0);
351+
expect(layoutMetrics.bottomLeftRadius).toBe(0);
352+
expect(layoutMetrics.drawerTop).toBeGreaterThan(0);
353+
expect(layoutMetrics.drawerTop).toBeLessThanOrEqual(24);
354+
expect(layoutMetrics.composerBottom).toBeLessThanOrEqual(
355+
layoutMetrics.viewportHeight + 1
356+
);
357+
expect(layoutMetrics.inputHeight).toBeLessThanOrEqual(112);
358+
});

0 commit comments

Comments
 (0)