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
|
|
@ -4,11 +4,25 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
|
||||
# The curated default columns hide the individual name/address sort headers,
|
||||
# so these component tests make every sortable field visible via ?fields=.
|
||||
# The individual name/address constituents are only *offered* when the compact
|
||||
# Member / Address view settings are off, so the session opts them out of the
|
||||
# composite columns before selecting the fields via ?fields=.
|
||||
@cols "first_name,email,street,house_number,postal_code,city,country,join_date"
|
||||
|
||||
# Disables the compact Member/Address composites so first_name/last_name/email
|
||||
# and street/house_number/postal_code/city are offered as individual sortable
|
||||
# columns (persisted per browser via the view-settings session key).
|
||||
defp non_compact(conn) do
|
||||
Plug.Conn.put_session(
|
||||
conn,
|
||||
"member_view_settings",
|
||||
~s({"compact_member":false,"compact_address":false})
|
||||
)
|
||||
end
|
||||
|
||||
describe "rendering" do
|
||||
test "renders with correct attributes", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, _html} = live(conn, "/members?fields=#{@cols}")
|
||||
|
||||
# Test that the component renders with correct attributes
|
||||
|
|
@ -18,7 +32,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "renders all sortable headers", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, _html} = live(conn, "/members?fields=#{@cols}")
|
||||
|
||||
sortable_fields = [
|
||||
|
|
@ -38,7 +52,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "renders correct labels", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, _html} = live(conn, "/members?fields=#{@cols}")
|
||||
|
||||
# Test specific labels
|
||||
|
|
@ -50,7 +64,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
|
||||
describe "sort icons" do
|
||||
test "shows neutral icon for specific field when not sorted", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, _html} = live(conn, "/members?fields=#{@cols}")
|
||||
|
||||
# The neutral icon has the opcity class we can test for
|
||||
|
|
@ -62,7 +76,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "shows ascending icon for specific field when sorted ascending", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
{:ok, view, html} =
|
||||
live(conn, "/members?fields=#{@cols}&query=&sort_field=city&sort_order=asc")
|
||||
|
|
@ -85,7 +99,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "shows descending icon for specific field when sorted descending", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
{:ok, _view, html} =
|
||||
live(conn, "/members?fields=#{@cols}&query=&sort_field=email&sort_order=desc")
|
||||
|
|
@ -99,7 +113,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "multiple fields can have different icon states", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
{:ok, view, _html} =
|
||||
live(conn, "/members?fields=#{@cols}&query=&sort_field=city&sort_order=asc")
|
||||
|
|
@ -118,7 +132,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "icon state changes correctly when clicking different fields", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, _html} = live(conn, "/members?fields=#{@cols}")
|
||||
|
||||
# Start: all fields neutral except first name as default
|
||||
|
|
@ -146,7 +160,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "specific field shows correct icon for each sort state", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
# Test EMAIL field specifically
|
||||
{:ok, view, html_asc} =
|
||||
|
|
@ -167,7 +181,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "icon distribution shows exactly one active sort icon", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
# Test neutral state - only one field should have active sort icon
|
||||
{:ok, _view, html_neutral} = live(conn, "/members?fields=#{@cols}")
|
||||
|
|
@ -204,7 +218,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
|
||||
describe "accessibility" do
|
||||
test "sets aria-label correctly for unsorted 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=#{@cols}")
|
||||
|
||||
# Check aria-label for unsorted state
|
||||
|
|
@ -212,7 +226,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "sets aria-label correctly for ascending sort", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
{:ok, view, _html} =
|
||||
live(conn, "/members?fields=#{@cols}&sort_field=first_name&sort_order=asc")
|
||||
|
|
@ -222,7 +236,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "sets aria-label correctly for descending sort", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
{:ok, view, _html} =
|
||||
live(conn, "/members?fields=#{@cols}&sort_field=first_name&sort_order=desc")
|
||||
|
|
@ -232,7 +246,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "includes tooltip with correct aria-label", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
{:ok, view, _html} =
|
||||
live(conn, "/members?fields=#{@cols}&sort_field=first_name&sort_order=asc")
|
||||
|
|
@ -243,7 +257,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "aria-labels work for all sortable fields", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, _html} = live(conn, "/members?fields=#{@cols}&sort_field=email&sort_order=desc")
|
||||
|
||||
# Test aria-labels for different fields
|
||||
|
|
@ -260,7 +274,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
|
||||
describe "component behavior" do
|
||||
test "clicking triggers sort event on parent LiveView", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, _html} = live(conn, "/members?fields=#{@cols}")
|
||||
|
||||
# Click on the first name sort header
|
||||
|
|
@ -273,7 +287,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "component handles different field types correctly", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, _html} = live(conn, "/members?fields=#{@cols}")
|
||||
|
||||
# Test that different field types render correctly
|
||||
|
|
@ -285,7 +299,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
|
||||
describe "edge cases" do
|
||||
test "handles invalid sort field gracefully", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
{:ok, view, html} =
|
||||
live(conn, "/members?fields=#{@cols}&sort_field=invalid_field&sort_order=asc")
|
||||
|
|
@ -296,7 +310,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "handles invalid sort order gracefully", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
|
||||
{:ok, view, html} =
|
||||
live(conn, "/members?fields=#{@cols}&sort_field=first_name&sort_order=invalid")
|
||||
|
|
@ -307,7 +321,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "handles empty sort parameters", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, html} = live(conn, "/members?fields=#{@cols}&sort_field=&sort_order=")
|
||||
|
||||
# Should show neutral icons
|
||||
|
|
@ -318,7 +332,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
|
||||
describe "icon state transitions" do
|
||||
test "icon changes when sorting state changes", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, _html} = live(conn, "/members?fields=#{@cols}")
|
||||
|
||||
# Start with neutral state
|
||||
|
|
@ -334,7 +348,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
test "multiple fields can be tested for icon states", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
conn = conn |> conn_with_oidc_user() |> non_compact()
|
||||
{:ok, view, html} = live(conn, "/members?fields=#{@cols}&sort_field=email&sort_order=desc")
|
||||
|
||||
# Email should be active (descending)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue