feat(member): let members tailor overview density and visible columns
The View dropdown drives row density and whether the Member and Address fields render as composite cells or split into their underlying columns; the column manager toggles visibility and resets to the curated default. All choices persist per browser through the URL/session/cookie/global chain, so a saved layout survives reloads without a per-account store.
This commit is contained in:
parent
af2cc2e0d4
commit
dfc616257d
21 changed files with 1643 additions and 263 deletions
78
test/mv_web/member_live/index_a11y_hardening_test.exs
Normal file
78
test/mv_web/member_live/index_a11y_hardening_test.exs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
defmodule MvWeb.MemberLive.IndexA11yHardeningTest do
|
||||
@moduledoc """
|
||||
§3.5 — WCAG 2.2 AA posture: ≥24px interactive targets in both densities,
|
||||
scroll-margin on rows so a focused row is not obscured by the sticky header,
|
||||
select-all `indeterminate` for partial selection, and reflow markup keeping the
|
||||
table in its own focusable scroll region.
|
||||
"""
|
||||
use MvWeb.ConnCase, async: false
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
alias Mv.Helpers.SystemActor
|
||||
|
||||
@moduletag :ui
|
||||
|
||||
setup %{conn: conn} do
|
||||
actor = SystemActor.get_system_actor()
|
||||
|
||||
members =
|
||||
for i <- 1..3 do
|
||||
{:ok, m} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Wcag#{i}", last_name: "Row", email: "wcag#{i}@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
m
|
||||
end
|
||||
|
||||
%{conn: conn_with_oidc_user(conn), members: members}
|
||||
end
|
||||
|
||||
test "rows reserve scroll-margin under the sticky header", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/members")
|
||||
assert html =~ "scroll-mt-16"
|
||||
end
|
||||
|
||||
test "the table lives in its own focusable scroll region that reflows", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/members")
|
||||
|
||||
# Focusable region (1.4.10: table scrolls within its own focusable region).
|
||||
assert html =~ ~r/data-testid="members-table-scroll"[^>]*tabindex="0"/
|
||||
# The toolbar reflows to one column on narrow viewports.
|
||||
assert html =~ "flex-wrap"
|
||||
end
|
||||
|
||||
test "interactive row targets meet the >=24px minimum in both densities", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/members")
|
||||
|
||||
# Compact (default) and comfortable both keep the >=24px checkbox target.
|
||||
assert has_element?(view, "input[type='checkbox'].min-h-6.min-w-6")
|
||||
|
||||
# Switch to comfortable via the view-settings dropdown.
|
||||
view |> element("[data-testid='view-settings-button']") |> render_click()
|
||||
view |> element("[data-testid='view-setting-density']") |> render_click()
|
||||
assert has_element?(view, "input[type='checkbox'].min-h-6.min-w-6")
|
||||
end
|
||||
|
||||
test "select-all reflects indeterminate for a partial selection", %{
|
||||
conn: conn,
|
||||
members: members
|
||||
} do
|
||||
{:ok, view, _html} = live(conn, ~p"/members")
|
||||
|
||||
# No selection: not indeterminate.
|
||||
assert has_element?(view, "#select-all-checkbox[data-indeterminate='false']")
|
||||
|
||||
# Select one of several -> partial -> indeterminate.
|
||||
[m | _] = members
|
||||
render_click(view, "select_member", %{"id" => m.id})
|
||||
assert has_element?(view, "#select-all-checkbox[data-indeterminate='true']")
|
||||
|
||||
# Select all -> fully checked, not indeterminate.
|
||||
view |> element("[phx-click='select_all']") |> render_click()
|
||||
assert has_element?(view, "#select-all-checkbox[data-indeterminate='false']")
|
||||
assert has_element?(view, "#select-all-checkbox[checked]")
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue