Show a failed attachment's reason on hover, not as a red line

The composer showed each failed file's reason as a red line of text under
the chips. A failed file no longer blocks the send, since it is dropped
then, so that line was louder than it needed to be. DESIGN.md also says
status text of a sentence or more must not be a coloured paragraph.

The chip's warning icon already marks the failure. The reason is now a
Tooltip on the failed chip. It falls back to "Failed" when no reason is
known. It is also kept as sr-only text so screen readers still read it.
The native title moves from the chip to the truncating file name, where
DESIGN.md keeps title=, and is left off failed chips so the tooltip
doesn't double up.
This commit is contained in:
arc53-machine committed 2026-09-28 10:27:12 +01:00
1 parent dc5ebf5bb9
commit b8a62cedf9
3 files changed
+133 -31

No files matched your search

@@ -276,10 +276,12 @@ test.describe("tier-a · composer resilience", () => {
.first()
.setInputFiles(SMALL_FIXTURE_PATH);
// The failed chip explains itself before Send.
await expect(page.getByText(/upload failed/i)).toBeVisible({
timeout: 5_000,
});
// The failed chip is marked by its warning icon; the reason is a
// hover tooltip, not a line of text in the composer.
const failedIcon = page.getByLabel("Failed", { exact: true });
await expect(failedIcon).toBeVisible({ timeout: 5_000 });
await failedIcon.hover();
await expect(page.getByRole("tooltip")).toContainText(/upload failed/i);
const question = "does this still send? e2e-failed-attachment";
const textarea = page.locator("#message-input");
@@ -298,7 +300,8 @@ test.describe("tier-a · composer resilience", () => {
// The question is in the thread; the chip and its reason are gone.
await expect(page.getByText(question)).toBeVisible();
await expect(page.getByText(/upload failed/i)).toBeHidden();
await expect(failedIcon).toHaveCount(0);
await expect(page.getByText(/upload failed/i)).toHaveCount(0);
await expect(page.getByText("notes.txt")).toBeHidden();
await expect(textarea).toHaveValue("");