fix(ui): keep public forms accessible

This commit is contained in:
SimpleTest 2026-07-24 21:33:49 +03:00
parent ba872dda63
commit f69bb9c342
6 changed files with 56 additions and 24 deletions

View File

@ -2,6 +2,17 @@ import AxeBuilder from "@axe-core/playwright";
import { expect, test } from "@playwright/test";
import { newIsolatedContext } from "./helpers";
const publicAccessibilityPaths = [
"/",
"/safety",
"/users/log-in",
"/users/register",
"/support",
"/account/delete",
"/legal/content-removal",
"/legal/take-it-down",
];
async function expectAccessible(page: import("@playwright/test").Page): Promise<void> {
const results = await new AxeBuilder({ page }).analyze();
@ -32,7 +43,7 @@ test("public pages are accessible by keyboard in light and dark themes", async (
await page.getByRole("button", { name: `Use ${theme} theme` }).click();
await expect(page.locator("html")).toHaveAttribute("data-theme", theme);
for (const path of ["/", "/safety", "/users/log-in", "/users/register"]) {
for (const path of publicAccessibilityPaths) {
await page.goto(path);
await expectAccessible(page);
}
@ -51,14 +62,21 @@ test("public navigation does not overflow narrow or wide viewports", async ({ br
const context = await newIsolatedContext(browser);
const page = await context.newPage();
await page.setViewportSize(viewport);
await page.goto("/");
for (const path of ["/", "/support", "/legal/content-removal", "/legal/take-it-down"]) {
await page.goto(path);
const dimensions = await page.evaluate(() => ({
clientWidth: document.documentElement.clientWidth,
scrollWidth: document.documentElement.scrollWidth,
}));
expect(dimensions.scrollWidth).toBeLessThanOrEqual(dimensions.clientWidth);
expect(dimensions.scrollWidth, `${path} overflowed at ${viewport.width}px`).toBeLessThanOrEqual(
dimensions.clientWidth,
);
}
await page.goto("/");
await expect(page.getByRole("navigation", { name: "Primary navigation" })).toBeVisible();
await expect(page.locator("body > header")).toHaveCount(1);

View File

@ -294,9 +294,11 @@ defmodule WhoNeedHelpWeb.CoreComponents do
def input(%{type: "select"} = assigns) do
~H"""
<div class="fieldset mb-2">
<label for={@id}>
<span :if={@label} class="label mb-1">{@label}</span>
<div class="fieldset mb-2 w-full min-w-0">
<label for={@id} class="block w-full min-w-0 max-w-full">
<span :if={@label} class="label mb-1 max-w-full whitespace-normal break-words">
{@label}
</span>
<select
id={@id}
name={@name}
@ -315,9 +317,11 @@ defmodule WhoNeedHelpWeb.CoreComponents do
def input(%{type: "textarea"} = assigns) do
~H"""
<div class="fieldset mb-2">
<label for={@id}>
<span :if={@label} class="label mb-1">{@label}</span>
<div class="fieldset mb-2 w-full min-w-0">
<label for={@id} class="block w-full min-w-0 max-w-full">
<span :if={@label} class="label mb-1 max-w-full whitespace-normal break-words">
{@label}
</span>
<textarea
id={@id}
name={@name}
@ -336,9 +340,11 @@ defmodule WhoNeedHelpWeb.CoreComponents do
# All other inputs text, datetime-local, url, password, etc. are handled here...
def input(assigns) do
~H"""
<div class="fieldset mb-2">
<label for={@id}>
<span :if={@label} class="label mb-1">{@label}</span>
<div class="fieldset mb-2 w-full min-w-0">
<label for={@id} class="block w-full min-w-0 max-w-full">
<span :if={@label} class="label mb-1 max-w-full whitespace-normal break-words">
{@label}
</span>
<input
type={@type}
name={@name}
@ -399,8 +405,8 @@ defmodule WhoNeedHelpWeb.CoreComponents do
|> assign(:errors, Enum.map(errors, &translate_error(&1)))
~H"""
<fieldset class="fieldset mb-2">
<legend class="label mb-1">{@label}</legend>
<fieldset class="fieldset mb-2 w-full min-w-0">
<legend class="label mb-1 max-w-full whitespace-normal break-words">{@label}</legend>
<div
id={"#{@id}-picker"}
phx-hook="DateTimePicker"

View File

@ -2,7 +2,9 @@
<div>
<div class="flex flex-wrap items-center justify-between gap-3">
<div>
<div class="text-sm font-semibold text-error">{gettext("CONTENT REMOVAL")}</div>
<div class="border-l-4 border-error pl-2 text-sm font-semibold text-base-content">
{gettext("CONTENT REMOVAL")}
</div>
<h1 class="mt-1 text-4xl font-black">{gettext("Your notices")}</h1>
</div>
<.link navigate={~p"/legal/content-removal"} class="btn btn-error">

View File

@ -1,6 +1,8 @@
<Layouts.app flash={@flash} current_scope={@current_scope} page_width={:compact}>
<div>
<div class="text-sm font-semibold text-error">{gettext("CONTENT REMOVAL")}</div>
<div class="border-l-4 border-error pl-2 text-sm font-semibold text-base-content">
{gettext("CONTENT REMOVAL")}
</div>
<h1 class="mt-2 text-4xl font-black">{gettext("Report illegal or harmful content")}</h1>
<p class="mt-3 text-base-content/65">
{gettext(
@ -21,7 +23,7 @@
"Use the dedicated urgent process. Never upload or reproduce the image or video."
)}
</p>
<.link navigate={~p"/legal/take-it-down"} class="btn btn-error btn-outline btn-sm mt-3">
<.link navigate={~p"/legal/take-it-down"} class="btn btn-error btn-sm mt-3">
{gettext("Open TAKE IT DOWN request")}
</.link>
</div>

View File

@ -1,6 +1,8 @@
<Layouts.app flash={@flash} current_scope={@current_scope} page_width={:compact}>
<div>
<div class="text-sm font-semibold text-error">{gettext("TAKE IT DOWN")}</div>
<div class="border-l-4 border-error pl-2 text-sm font-semibold text-base-content">
{gettext("TAKE IT DOWN")}
</div>
<h1 class="mt-2 text-4xl font-black">
{gettext("Request removal of intimate visual material")}
</h1>

View File

@ -1,6 +1,8 @@
<Layouts.app flash={@flash} current_scope={@current_scope} page_width={:compact}>
<div>
<div class="text-sm font-semibold text-error">{gettext("ACCOUNT AND DATA")}</div>
<div class="border-l-4 border-error pl-2 text-sm font-semibold text-base-content">
{gettext("ACCOUNT AND DATA")}
</div>
<h1 class="mt-2 text-4xl font-black">{gettext("Request account deletion")}</h1>
<p class="mt-3 text-base-content/65">
{gettext(