diff --git a/lib/mv_web/components/core_components.ex b/lib/mv_web/components/core_components.ex
index b5bd763..2eb3051 100644
--- a/lib/mv_web/components/core_components.ex
+++ b/lib/mv_web/components/core_components.ex
@@ -938,6 +938,11 @@ defmodule MvWeb.CoreComponents do
doc:
"when true, thead th get lg:sticky lg:top-0 bg-base-100 z-10 for use inside a scroll container on desktop"
+ attr :wrapper_overflow_class, :string,
+ default: "overflow-x-auto",
+ doc:
+ "overflow class for the table wrapper; set to overflow-visible when outer container owns scrolling"
+
slot :col, required: true do
attr :label, :string
attr :class, :string
@@ -974,7 +979,7 @@ defmodule MvWeb.CoreComponents do
~H"""
diff --git a/lib/mv_web/live/member_live/index.html.heex b/lib/mv_web/live/member_live/index.html.heex
index 4d86a62..92f19b8 100644
--- a/lib/mv_web/live/member_live/index.html.heex
+++ b/lib/mv_web/live/member_live/index.html.heex
@@ -105,6 +105,7 @@
<.table
id="members"
rows={@members}
+ wrapper_overflow_class="overflow-visible"
sticky_header={true}
row_id={fn member -> "row-#{member.id}" end}
row_click={fn member -> JS.push("select_row_and_navigate", value: %{id: member.id}) end}
diff --git a/test/mv_web/components/core_components_table_test.exs b/test/mv_web/components/core_components_table_test.exs
index 931b42a..45e8c6d 100644
--- a/test/mv_web/components/core_components_table_test.exs
+++ b/test/mv_web/components/core_components_table_test.exs
@@ -151,4 +151,86 @@ defmodule MvWeb.Components.CoreComponentsTableTest do
assert html =~ "ring-primary"
end
end
+
+ describe "table scroll wrapper contract" do
+ test "sticky header table uses horizontal-only overflow wrapper" do
+ rows = [%{id: "1", name: "Alice"}]
+
+ assigns = %{
+ id: "test-table",
+ rows: rows,
+ row_id: fn r -> "row-#{r.id}" end,
+ row_click: nil,
+ sticky_header: true,
+ row_item: &Function.identity/1,
+ col: [
+ %{
+ __slot__: :col,
+ label: "Name",
+ inner_block: fn _socket, item -> [item[:name] || ""] end
+ }
+ ],
+ dynamic_cols: [],
+ action: []
+ }
+
+ html = render_component(&CoreComponents.table/1, assigns)
+
+ assert html =~ ~s(class="overflow-x-auto")
+ refute html =~ ~s(class="overflow-auto")
+ end
+
+ test "table wrapper does not enable vertical overflow by default" do
+ rows = [%{id: "1", name: "Alice"}]
+
+ assigns = %{
+ id: "test-table",
+ rows: rows,
+ row_id: fn r -> "row-#{r.id}" end,
+ row_click: nil,
+ row_item: &Function.identity/1,
+ col: [
+ %{
+ __slot__: :col,
+ label: "Name",
+ inner_block: fn _socket, item -> [item[:name] || ""] end
+ }
+ ],
+ dynamic_cols: [],
+ action: []
+ }
+
+ html = render_component(&CoreComponents.table/1, assigns)
+
+ assert html =~ ~s(class="overflow-x-auto")
+ refute html =~ ~s(class="overflow-auto")
+ end
+
+ test "table wrapper overflow class can be overridden by caller" do
+ rows = [%{id: "1", name: "Alice"}]
+
+ assigns = %{
+ id: "test-table",
+ rows: rows,
+ row_id: fn r -> "row-#{r.id}" end,
+ row_click: nil,
+ wrapper_overflow_class: "overflow-visible",
+ row_item: &Function.identity/1,
+ col: [
+ %{
+ __slot__: :col,
+ label: "Name",
+ inner_block: fn _socket, item -> [item[:name] || ""] end
+ }
+ ],
+ dynamic_cols: [],
+ action: []
+ }
+
+ html = render_component(&CoreComponents.table/1, assigns)
+
+ assert html =~ ~s(class="overflow-visible")
+ refute html =~ ~s(class="overflow-x-auto")
+ end
+ end
end
diff --git a/test/mv_web/live/global_settings_live_test.exs b/test/mv_web/live/global_settings_live_test.exs
index f90802a..4283ec0 100644
--- a/test/mv_web/live/global_settings_live_test.exs
+++ b/test/mv_web/live/global_settings_live_test.exs
@@ -3,6 +3,20 @@ defmodule MvWeb.GlobalSettingsLiveTest do
import Phoenix.LiveViewTest
alias Mv.Membership
+ defp clear_smtp_env do
+ [
+ "SMTP_HOST",
+ "SMTP_PORT",
+ "SMTP_SSL",
+ "SMTP_USERNAME",
+ "SMTP_PASSWORD",
+ "SMTP_PASSWORD_FILE",
+ "MAIL_FROM_EMAIL",
+ "MAIL_FROM_NAME"
+ ]
+ |> Enum.each(&System.delete_env/1)
+ end
+
describe "Global Settings LiveView" do
setup %{conn: conn} do
user = create_test_user(%{email: "admin@example.com"})
diff --git a/test/mv_web/member_live/index_test.exs b/test/mv_web/member_live/index_test.exs
index 686a8e8..95c71ed 100644
--- a/test/mv_web/member_live/index_test.exs
+++ b/test/mv_web/member_live/index_test.exs
@@ -78,6 +78,18 @@ defmodule MvWeb.MemberLive.IndexTest do
assert html =~ "lg:top-0"
assert html =~ "bg-base-100"
end
+
+ test "members page does not nest a second overflow wrapper inside members-table-scroll", %{
+ conn: conn
+ } do
+ conn = conn_with_oidc_user(conn)
+ {:ok, _view, html} = live(conn, ~p"/members")
+
+ assert html =~ ~s(id="members-keyboard")
+ assert html =~ ~s(class="overflow-visible")
+ refute html =~ ~s(id="members-keyboard" class="overflow-x-auto")
+ refute html =~ ~s(id="members-keyboard" class="overflow-auto")
+ end
end
describe "translations" do