fix: hide paid column and add tests
Some checks reported errors
continuous-integration/drone/push Build was killed
Some checks reported errors
continuous-integration/drone/push Build was killed
This commit is contained in:
parent
c3b33b55a5
commit
c9678231f9
4 changed files with 26 additions and 366 deletions
|
|
@ -2,13 +2,6 @@
|
|||
<.header>
|
||||
{gettext("Members")}
|
||||
<:actions>
|
||||
<.live_component
|
||||
module={MvWeb.Components.FieldVisibilityDropdownComponent}
|
||||
id="field-visibility-dropdown"
|
||||
all_fields={@all_available_fields}
|
||||
custom_fields={@all_custom_fields}
|
||||
selected_fields={@user_field_selection}
|
||||
/>
|
||||
<.button
|
||||
:if={Enum.any?(@members, &MapSet.member?(@selected_members, &1.id))}
|
||||
id="copy-emails-btn"
|
||||
|
|
@ -46,6 +39,13 @@
|
|||
paid_filter={@paid_filter}
|
||||
member_count={length(@members)}
|
||||
/>
|
||||
<.live_component
|
||||
module={MvWeb.Components.FieldVisibilityDropdownComponent}
|
||||
id="field-visibility-dropdown"
|
||||
all_fields={@all_available_fields}
|
||||
custom_fields={@all_custom_fields}
|
||||
selected_fields={@user_field_selection}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<.table
|
||||
|
|
@ -247,7 +247,7 @@
|
|||
>
|
||||
{member.join_date}
|
||||
</:col>
|
||||
<:col :let={member} label={gettext("Paid")}>
|
||||
<:col :let={member} :if={:paid in @member_fields_visible} label={gettext("Paid")}>
|
||||
<span class={[
|
||||
"badge",
|
||||
if(member.paid == true, do: "badge-success", else: "badge-error")
|
||||
|
|
|
|||
|
|
@ -210,8 +210,8 @@ defmodule MvWeb.MemberLive.Index.FieldSelection do
|
|||
{:ok, decoded} when is_map(decoded) ->
|
||||
# Ensure all values are booleans
|
||||
Enum.reduce(decoded, %{}, fn
|
||||
{key, value} when is_boolean(value) -> {key, value}
|
||||
{key, _value} -> {key, true}
|
||||
{key, value}, acc when is_boolean(value) -> Map.put(acc, key, value)
|
||||
{key, _value}, acc -> Map.put(acc, key, true)
|
||||
end)
|
||||
|
||||
_ ->
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ defmodule Mv.Membership.MemberFieldVisibilityTest do
|
|||
field_to_hide = List.first(member_fields)
|
||||
field_to_show = List.last(member_fields)
|
||||
|
||||
# Update settings to hide a field
|
||||
# Update settings to hide a field (use string keys for JSONB)
|
||||
{:ok, _updated_settings} =
|
||||
Mv.Membership.update_settings(settings, %{
|
||||
member_field_visibility: %{field_to_hide => false}
|
||||
member_field_visibility: %{Atom.to_string(field_to_hide) => false}
|
||||
})
|
||||
|
||||
# JSONB may convert atom keys to string keys, so we check via show_in_overview? instead
|
||||
|
|
@ -53,10 +53,10 @@ defmodule Mv.Membership.MemberFieldVisibilityTest do
|
|||
fields_to_hide = Enum.take(member_fields, 2)
|
||||
fields_to_show = Enum.take(member_fields, -2)
|
||||
|
||||
# Update settings to hide some fields
|
||||
# Update settings to hide some fields (use string keys for JSONB)
|
||||
visibility_config =
|
||||
Enum.reduce(fields_to_hide, %{}, fn field, acc ->
|
||||
Map.put(acc, field, false)
|
||||
Map.put(acc, Atom.to_string(field), false)
|
||||
end)
|
||||
|
||||
{:ok, _updated_settings} =
|
||||
|
|
|
|||
|
|
@ -1,363 +1,23 @@
|
|||
defmodule MvWeb.Components.FieldVisibilityDropdownComponentTest do
|
||||
@moduledoc """
|
||||
Tests for FieldVisibilityDropdownComponent LiveComponent.
|
||||
"""
|
||||
use MvWeb.ConnCase, async: true
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
alias MvWeb.Components.FieldVisibilityDropdownComponent
|
||||
describe "field visibility dropdown in member view" do
|
||||
test "renders and toggles visibility", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, ~p"/members")
|
||||
|
||||
# Helper to create test assigns
|
||||
defp create_assigns(overrides \\ %{}) do
|
||||
default_assigns = %{
|
||||
id: "test-dropdown",
|
||||
all_fields: [:first_name, :email, :street, "custom_field_123"],
|
||||
custom_fields: [
|
||||
%{id: "123", name: "Custom Field 1"}
|
||||
],
|
||||
selected_fields: %{
|
||||
"first_name" => true,
|
||||
"email" => true,
|
||||
"street" => false,
|
||||
"custom_field_123" => true
|
||||
}
|
||||
}
|
||||
# Renders Dropdown
|
||||
assert has_element?(view, "[data-testid='dropdown-menu']")
|
||||
|
||||
Map.merge(default_assigns, overrides)
|
||||
end
|
||||
# Opens Dropdown
|
||||
view |> element("[data-testid='dropdown-button']") |> render_click()
|
||||
assert has_element?(view, "#field-visibility-menu")
|
||||
assert has_element?(view, "button[phx-click='select_item'][phx-value-item='email']")
|
||||
assert has_element?(view, "button[phx-click='select_all']")
|
||||
assert has_element?(view, "button[phx-click='select_none']")
|
||||
|
||||
describe "update/2" do
|
||||
test "initializes with default values" do
|
||||
assigns = create_assigns()
|
||||
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
assert socket.assigns.id == "test-dropdown"
|
||||
assert socket.assigns.open == false
|
||||
assert socket.assigns.all_fields == assigns.all_fields
|
||||
assert socket.assigns.selected_fields == assigns.selected_fields
|
||||
end
|
||||
|
||||
test "preserves existing open state" do
|
||||
assigns = create_assigns()
|
||||
existing_socket = %{assigns: %{open: true}}
|
||||
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, existing_socket)
|
||||
|
||||
assert socket.assigns.open == true
|
||||
end
|
||||
|
||||
test "handles missing optional assigns" do
|
||||
minimal_assigns = %{id: "test"}
|
||||
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(minimal_assigns, %{})
|
||||
|
||||
assert socket.assigns.all_fields == []
|
||||
assert socket.assigns.custom_fields == []
|
||||
assert socket.assigns.selected_fields == %{}
|
||||
end
|
||||
end
|
||||
|
||||
describe "render/1" do
|
||||
test "renders dropdown button" do
|
||||
assigns = create_assigns()
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
assert html =~ "Columns"
|
||||
assert html =~ "hero-adjustments-horizontal"
|
||||
assert has_element?(html, "button[aria-controls='field-visibility-menu']")
|
||||
end
|
||||
|
||||
test "renders dropdown menu when open" do
|
||||
assigns = create_assigns() |> Map.put(:open, true)
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
assert has_element?(html, "ul#field-visibility-menu")
|
||||
assert html =~ "All"
|
||||
assert html =~ "None"
|
||||
end
|
||||
|
||||
test "does not render menu when closed" do
|
||||
assigns = create_assigns() |> Map.put(:open, false)
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
refute has_element?(html, "ul#field-visibility-menu")
|
||||
end
|
||||
|
||||
test "renders member fields" do
|
||||
assigns = create_assigns() |> Map.put(:open, true)
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
# Field names should be formatted (first_name -> First Name)
|
||||
assert html =~ "First Name" or html =~ "first_name"
|
||||
assert html =~ "Email" or html =~ "email"
|
||||
assert html =~ "Street" or html =~ "street"
|
||||
end
|
||||
|
||||
test "renders custom fields when custom fields exist" do
|
||||
assigns = create_assigns() |> Map.put(:open, true)
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
# Custom field name
|
||||
assert html =~ "Custom Field 1"
|
||||
end
|
||||
|
||||
test "renders checkboxes with correct checked state" do
|
||||
assigns = create_assigns() |> Map.put(:open, true)
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
# first_name should be checked (aria-checked="true")
|
||||
assert html =~ ~s(aria-checked="true")
|
||||
assert html =~ ~s(phx-value-item="first_name")
|
||||
|
||||
# street should not be checked (aria-checked="false")
|
||||
assert html =~ ~s(phx-value-item="street")
|
||||
# Note: The visual checkbox state is handled by CSS classes and aria-checked attribute
|
||||
end
|
||||
|
||||
test "includes accessibility attributes" do
|
||||
assigns = create_assigns() |> Map.put(:open, true)
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
assert html =~ ~s(aria-controls="field-visibility-menu")
|
||||
assert html =~ ~s(aria-haspopup="menu")
|
||||
assert html =~ ~s(role="button")
|
||||
assert html =~ ~s(role="menu")
|
||||
assert html =~ ~s(role="menuitemcheckbox")
|
||||
end
|
||||
|
||||
test "formats member field labels correctly" do
|
||||
assigns = create_assigns() |> Map.put(:open, true)
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
# Field names should be formatted (first_name -> First Name)
|
||||
assert html =~ "First Name" or html =~ "first_name"
|
||||
end
|
||||
|
||||
test "uses custom field names from custom_fields prop" do
|
||||
assigns =
|
||||
create_assigns()
|
||||
|> Map.put(:open, true)
|
||||
|> Map.put(:custom_fields, [
|
||||
%{id: "123", name: "Membership Number"}
|
||||
])
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
assert html =~ "Membership Number"
|
||||
end
|
||||
|
||||
test "falls back to ID when custom field not found" do
|
||||
assigns =
|
||||
create_assigns()
|
||||
|> Map.put(:open, true)
|
||||
# Empty custom fields list
|
||||
|> Map.put(:custom_fields, [])
|
||||
|
||||
html = render_component(FieldVisibilityDropdownComponent, assigns)
|
||||
|
||||
# Should show something like "Custom Field 123"
|
||||
assert html =~ "custom_field_123" or html =~ "Custom Field"
|
||||
end
|
||||
end
|
||||
|
||||
describe "handle_event/2" do
|
||||
test "toggle_dropdown toggles open state" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
assert socket.assigns.open == false
|
||||
|
||||
{:noreply, socket} =
|
||||
FieldVisibilityDropdownComponent.handle_event("toggle_dropdown", %{}, socket)
|
||||
|
||||
assert socket.assigns.open == true
|
||||
|
||||
{:noreply, socket} =
|
||||
FieldVisibilityDropdownComponent.handle_event("toggle_dropdown", %{}, socket)
|
||||
|
||||
assert socket.assigns.open == false
|
||||
end
|
||||
|
||||
test "close_dropdown sets open to false" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
socket = assign(socket, :open, true)
|
||||
|
||||
{:noreply, socket} =
|
||||
FieldVisibilityDropdownComponent.handle_event("close_dropdown", %{}, socket)
|
||||
|
||||
assert socket.assigns.open == false
|
||||
end
|
||||
|
||||
test "select_item toggles field visibility" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
assert socket.assigns.selected_fields["first_name"] == true
|
||||
|
||||
{:noreply, socket} =
|
||||
FieldVisibilityDropdownComponent.handle_event(
|
||||
"select_item",
|
||||
%{"item" => "first_name"},
|
||||
socket
|
||||
)
|
||||
|
||||
assert socket.assigns.selected_fields["first_name"] == false
|
||||
|
||||
{:noreply, socket} =
|
||||
FieldVisibilityDropdownComponent.handle_event(
|
||||
"select_item",
|
||||
%{"item" => "first_name"},
|
||||
socket
|
||||
)
|
||||
|
||||
assert socket.assigns.selected_fields["first_name"] == true
|
||||
end
|
||||
|
||||
test "select_item defaults to true for missing fields" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
{:noreply, socket} =
|
||||
FieldVisibilityDropdownComponent.handle_event(
|
||||
"select_item",
|
||||
%{"item" => "new_field"},
|
||||
socket
|
||||
)
|
||||
|
||||
# Toggled from default true
|
||||
assert socket.assigns.selected_fields["new_field"] == false
|
||||
end
|
||||
|
||||
test "select_item sends message to parent" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
FieldVisibilityDropdownComponent.handle_event(
|
||||
"select_item",
|
||||
%{"item" => "first_name"},
|
||||
socket
|
||||
)
|
||||
|
||||
# Check that message was sent (would be verified in integration test)
|
||||
# For unit test, we just verify the state change
|
||||
assert_receive {:field_toggled, "first_name", false}
|
||||
end
|
||||
|
||||
test "select_all sets all fields to true" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
{:noreply, socket} =
|
||||
FieldVisibilityDropdownComponent.handle_event("select_all", %{}, socket)
|
||||
|
||||
assert socket.assigns.selected_fields["first_name"] == true
|
||||
assert socket.assigns.selected_fields["email"] == true
|
||||
assert socket.assigns.selected_fields["street"] == true
|
||||
assert socket.assigns.selected_fields["custom_field_123"] == true
|
||||
end
|
||||
|
||||
test "select_all sends message to parent" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
FieldVisibilityDropdownComponent.handle_event("select_all", %{}, socket)
|
||||
|
||||
assert_receive {:fields_selected, selection}
|
||||
assert selection["first_name"] == true
|
||||
assert selection["email"] == true
|
||||
end
|
||||
|
||||
test "select_none sets all fields to false" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
{:noreply, socket} =
|
||||
FieldVisibilityDropdownComponent.handle_event("select_none", %{}, socket)
|
||||
|
||||
assert socket.assigns.selected_fields["first_name"] == false
|
||||
assert socket.assigns.selected_fields["email"] == false
|
||||
assert socket.assigns.selected_fields["street"] == false
|
||||
assert socket.assigns.selected_fields["custom_field_123"] == false
|
||||
end
|
||||
|
||||
test "select_none sends message to parent" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
FieldVisibilityDropdownComponent.handle_event("select_none", %{}, socket)
|
||||
|
||||
assert_receive {:fields_selected, selection}
|
||||
assert selection["first_name"] == false
|
||||
assert selection["email"] == false
|
||||
end
|
||||
|
||||
test "handles custom field toggle" do
|
||||
assigns = create_assigns()
|
||||
{:ok, socket} = FieldVisibilityDropdownComponent.update(assigns, %{})
|
||||
|
||||
{:noreply, socket} =
|
||||
FieldVisibilityDropdownComponent.handle_event(
|
||||
"select_item",
|
||||
%{"item" => "custom_field_123"},
|
||||
socket
|
||||
)
|
||||
|
||||
assert socket.assigns.selected_fields["custom_field_123"] == false
|
||||
end
|
||||
end
|
||||
|
||||
describe "integration with LiveView" do
|
||||
test "component can be rendered in LiveView" do
|
||||
conn = conn_with_oidc_user(build_conn())
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# Check that component is rendered
|
||||
assert has_element?(view, "button[aria-controls='field-visibility-menu']")
|
||||
end
|
||||
|
||||
test "clicking button opens dropdown" do
|
||||
conn = conn_with_oidc_user(build_conn())
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# Initially closed
|
||||
refute has_element?(view, "ul#field-visibility-menu")
|
||||
|
||||
# Click button
|
||||
view
|
||||
|> element("button[aria-controls='field-visibility-menu']")
|
||||
|> render_click()
|
||||
|
||||
# Should be open now
|
||||
assert has_element?(view, "ul#field-visibility-menu")
|
||||
end
|
||||
|
||||
test "toggling field updates selection" do
|
||||
conn = conn_with_oidc_user(build_conn())
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# Open dropdown
|
||||
view
|
||||
|> element("button[aria-controls='field-visibility-menu']")
|
||||
|> render_click()
|
||||
|
||||
# Toggle a field
|
||||
view
|
||||
|> element("button[phx-click='select_item'][phx-value-item='first_name']")
|
||||
|> render_click()
|
||||
|
||||
# Component should update (verified by state change)
|
||||
# In a real scenario, this would trigger a reload of members
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue