feat(member-live): default the overview to comfortable density with folded-in email

This commit is contained in:
Simon 2026-07-16 09:38:57 +02:00
parent 0a23d03d9d
commit 4d100aef22
10 changed files with 58 additions and 44 deletions

View file

@ -8,11 +8,11 @@ defmodule MvWeb.MemberLive.Index.ViewSettingsTest do
alias MvWeb.MemberLive.Index.ViewSettings
test "defaults are all-compact with the email line off" do
test "defaults are comfortable density with the compact cells and email line on" do
assert ViewSettings.defaults() == %{
density: :compact,
density: :comfortable,
compact_member: true,
member_include_email: false,
member_include_email: true,
compact_address: true
}
end
@ -90,7 +90,7 @@ defmodule MvWeb.MemberLive.Index.ViewSettingsTest do
# from cookie
compact_address: false,
# default
member_include_email: false
member_include_email: true
}
end
end

View file

@ -53,7 +53,8 @@ defmodule MvWeb.MemberLive.IndexA11yHardeningTest do
# LiveView test is that the guarded checkbox markup exists in both densities.
assert has_element?(view, "#members-table-guard input[type='checkbox'].checkbox")
# Switch to comfortable via the view-settings dropdown.
# Toggle density via the view-settings dropdown (default is comfortable, so
# this switches to compact) to check the other density too.
view |> element("[data-testid='view-settings-button']") |> render_click()
view |> element("[data-testid='view-setting-density']") |> render_click()
assert has_element?(view, "#members-table-guard input[type='checkbox'].checkbox")

View file

@ -203,7 +203,12 @@ defmodule MvWeb.MemberLive.IndexCustomFieldsSortingTest do
conn: conn,
field_string: field
} do
conn = conn_with_oidc_user(conn)
# Email is folded into the Member cell by default; turn the fold off so email
# is a separate, sortable column for this cross-column-sort test.
conn =
conn
|> conn_with_oidc_user()
|> Plug.Conn.put_session("member_view_settings", ~s({"member_include_email":false}))
{:ok, view, _html} =
live(

View file

@ -35,8 +35,9 @@ defmodule MvWeb.MemberLive.IndexDefaultColumnsTest do
# Composite Name + Address cells.
assert has_element?(view, "[data-testid='member-name']")
assert has_element?(view, "[data-testid='member-address']")
# E-Mail is its own column by default (compact Member field, email line off).
assert has_element?(view, "[data-testid='email']")
# E-Mail is folded into the Member cell by default (include-email on), so it
# shows as the in-cell email line rather than a separate column.
assert has_element?(view, "[data-testid='member-name-email']")
# Fee type, fee status, groups, join date headers.
assert has_element?(view, "[data-testid='membership_fee_type']")
assert has_element?(view, "[data-testid='join_date']")
@ -47,17 +48,18 @@ defmodule MvWeb.MemberLive.IndexDefaultColumnsTest do
test "the individual name/address sub-fields are hidden by default", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/members")
# The composite Name/Address cells replace their constituents by default.
# E-Mail is excluded here: it is a default-visible column of its own.
for field <- ~w(first_name last_name city street house_number postal_code country) do
# The composite Name/Address cells replace their constituents by default, and
# the email is folded into the Member cell (include-email on), so no separate
# email column shows either.
for field <- ~w(first_name last_name email city street house_number postal_code country) do
refute has_element?(view, "[data-testid='#{field}']")
end
end
test "an explicit field selection still overrides the curated default", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/members?fields=email")
{:ok, view, _html} = live(conn, ~p"/members?fields=join_date")
assert has_element?(view, "[data-testid='email']")
assert has_element?(view, "[data-testid='join_date']")
refute has_element?(view, "[data-testid='member-name']")
end
end

View file

@ -26,13 +26,13 @@ defmodule MvWeb.MemberLive.IndexDensityTest do
view
end
test "defaults to compact when nothing is stored", %{conn: conn} do
test "defaults to comfortable when nothing is stored", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/members")
assert has_element?(view, "[data-testid='members-table-scroll'][data-density='compact']")
# The compact-mode toggle reflects the active (compact) state.
assert has_element?(view, "[data-testid='members-table-scroll'][data-density='comfortable']")
# The compact-mode toggle reflects the active (comfortable, i.e. not compact) state.
open_view_settings(view)
assert has_element?(view, "[data-testid='view-setting-density'][aria-checked='true']")
assert has_element?(view, "[data-testid='view-setting-density'][aria-checked='false']")
end
test "toggle switches the density token and the control state", %{conn: conn} do
@ -41,12 +41,12 @@ defmodule MvWeb.MemberLive.IndexDensityTest do
open_view_settings(view)
view |> element("[data-testid='view-setting-density']") |> render_click()
assert has_element?(view, "[data-testid='members-table-scroll'][data-density='comfortable']")
assert has_element?(view, "[data-testid='view-setting-density'][aria-checked='false']")
# Toggling again returns to compact.
view |> element("[data-testid='view-setting-density']") |> render_click()
assert has_element?(view, "[data-testid='members-table-scroll'][data-density='compact']")
assert has_element?(view, "[data-testid='view-setting-density'][aria-checked='true']")
# Toggling again returns to comfortable.
view |> element("[data-testid='view-setting-density']") |> render_click()
assert has_element?(view, "[data-testid='members-table-scroll'][data-density='comfortable']")
end
test "restores a persisted density from the session on reload", %{conn: conn} do

View file

@ -255,7 +255,9 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
describe "URL parameter persistence" do
test "field selection is persisted in URL", %{conn: conn} do
conn = conn_with_oidc_user(conn)
# Email is offered as a togglable column only when it is not folded into the
# Member cell; unfold it so the dropdown lists it.
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} = live(conn, "/members")
# Open dropdown and hide email
@ -374,7 +376,7 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
end
test "handles rapid toggling", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} = live(conn, "/members")
# Open dropdown

View file

@ -38,9 +38,8 @@ defmodule MvWeb.MemberLive.IndexMemberFieldsDisplayTest do
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, "/members")
# The composite Member cell carries first + last name; the in-cell email
# line stays off by default, but the email is surfaced in its own column
# (compact Member field, include-email off).
# The composite Member cell carries first + last name and the in-cell email
# line (include-email on by default), so all three values are rendered.
for m <- [m1, m2], field <- [m.first_name, m.last_name, m.email] do
assert html =~ field
end

View file

@ -174,7 +174,9 @@ defmodule MvWeb.MemberLive.IndexTest do
# those tests additionally opt out via `non_compact/1`. Either way a `fields`
# param rides on every push_patch, hence the relaxed substring assertions.
test "clicking a column header toggles sort order and updates the URL", %{conn: conn} do
conn = conn_with_oidc_user(conn)
# Email is folded into the Member cell by default; unfold it to a separate
# sortable column for this column-sort test.
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} = live(conn, "/members?fields=email")
# The component data test ids are built with the name of the field
@ -247,7 +249,7 @@ defmodule MvWeb.MemberLive.IndexTest do
end
test "sorting works with search query", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} = live(conn, "/members?fields=email&query=test")
view
@ -261,7 +263,7 @@ defmodule MvWeb.MemberLive.IndexTest do
end
test "sorting maintains search query when toggling order", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} =
live(conn, "/members?fields=email&query=test&sort_field=email&sort_order=asc")
@ -280,7 +282,7 @@ defmodule MvWeb.MemberLive.IndexTest do
describe "URL param handling" do
@describetag :ui
test "handle_params reads sort query and applies it", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} =
live(conn, "/members?fields=email&query=&sort_field=email&sort_order=desc")
@ -300,7 +302,7 @@ defmodule MvWeb.MemberLive.IndexTest do
end
test "handle_params preserves search query with sort params", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} =
live(conn, "/members?fields=email&query=test&sort_field=email&sort_order=desc")
@ -313,7 +315,7 @@ defmodule MvWeb.MemberLive.IndexTest do
describe "search and sort integration" do
@describetag :ui
test "search maintains sort state", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} =
live(conn, "/members?fields=email&query=&sort_field=email&sort_order=desc")
@ -328,7 +330,7 @@ defmodule MvWeb.MemberLive.IndexTest do
end
test "sort maintains search state", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
{:ok, view, _html} =
live(conn, "/members?fields=email&query=test&sort_field=email&sort_order=asc")
@ -1607,7 +1609,7 @@ defmodule MvWeb.MemberLive.IndexTest do
end
test "boolean filters are preserved during navigation actions", %{conn: conn} do
conn = conn_with_oidc_user(conn)
conn = conn |> conn_with_oidc_user() |> non_compact()
boolean_field = create_boolean_custom_field()
{:ok, view, _html} =

View file

@ -3,7 +3,7 @@ defmodule MvWeb.MemberLive.IndexViewSettingsTest do
§1.20 the view-settings dropdown toggles: the composite "Member" and address
fields, the "include email" sub-toggle and their effect on the rendered
columns. Turning a composite off surfaces the underlying separate columns.
§1.2 email is not in the Member cell by default.
§1.2 email is folded into the Member cell by default (include-email on).
"""
use MvWeb.ConnCase, async: false
@ -34,20 +34,23 @@ defmodule MvWeb.MemberLive.IndexViewSettingsTest do
view
end
test "member cell hides the email line by default", %{conn: conn} do
test "member cell shows the email line by default", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/members")
assert has_element?(view, "[data-testid='member-name']")
refute has_element?(view, "[data-testid='member-name-email']")
assert has_element?(view, "[data-testid='member-name-email']", "ada@example.com")
end
test "the include-email sub-toggle surfaces the email line in the member cell", %{conn: conn} do
test "the include-email sub-toggle removes the email line from the member cell", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/members")
# Email is folded into the Member cell by default; the sub-toggle turns it off.
assert has_element?(view, "[data-testid='member-name-email']", "ada@example.com")
open_view_settings(view)
view |> element("[data-testid='view-setting-member-include-email']") |> render_click()
assert has_element?(view, "[data-testid='member-name-email']", "ada@example.com")
refute has_element?(view, "[data-testid='member-name-email']")
end
test "turning off the compact member field shows separate name columns", %{conn: conn} do