From e73ec097d8518cf634753d5c0bc2e712f8476323 Mon Sep 17 00:00:00 2001 From: T Floyd Wright Date: Mon, 20 Apr 2026 09:35:18 -0700 Subject: [PATCH 1/2] chore: drop nodejs from initial apt step Ubuntu's nodejs/npm pulls in libnode-dev, which now collides with NodeSource's nodejs 18 over /usr/include/node/common.gypi. Letting NodeSource install both avoids the conflict. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d52f117..ca253d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM hexpm/elixir:1.19.5-erlang-26.2.5-ubuntu-jammy-20251013 -RUN apt-get update -y && apt-get install -y build-essential git nodejs npm curl inotify-tools \ +RUN apt-get update -y && apt-get install -y build-essential git curl inotify-tools \ && apt-get clean && rm -f /var/lib/apt/lists/*_* RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ && apt-get install -y nodejs From 6953fd36b4f54d4cff012d9739d0e741bf8f1cd1 Mon Sep 17 00:00:00 2001 From: T Floyd Wright Date: Mon, 20 Apr 2026 09:35:28 -0700 Subject: [PATCH 2/2] fix: render unique ids for nested embeds in show view Nested detail_view ids and tab hrefs were built from the parent's @id only, ignoring the parent's index in its own embed list. With two parent embeds each containing the same child embed, both children rendered with identical DOM ids and identical tab hrefs. --- lib/live_admin/components/resource/show.ex | 25 +++++++++++-------- test/live_admin/components/container_test.exs | 21 ++++++++++++++++ test/support/resources.ex | 11 ++++++++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/lib/live_admin/components/resource/show.ex b/lib/live_admin/components/resource/show.ex index 2222cbd..39a053b 100644 --- a/lib/live_admin/components/resource/show.ex +++ b/lib/live_admin/components/resource/show.ex @@ -90,8 +90,8 @@ defmodule LiveAdmin.Components.Container.Show do """ end - attr(:last, :integer, default: 0) - attr(:current, :integer, default: 0) + attr(:sibling_count, :integer, default: 0) + attr(:current_index, :integer, default: 0) attr(:id, :string, required: true) attr(:record, :map, required: true) attr(:resource, :map, required: true) @@ -116,17 +116,17 @@ defmodule LiveAdmin.Components.Container.Show do ) ~H""" -
- <%= if Enum.any?(@embeds) || @last > 0 do %> +
+ <%= if Enum.any?(@embeds) || @sibling_count > 0 do %>
<%= if @id == "main" do %> <% end %> <%= for {field, _, _} <- @embeds, @record |> Map.fetch!(field) |> List.wrap() |> Enum.any? do %> - {trans(humanize(field))} + {trans(humanize(field))} <% end %> - <%= if @last > 0 do %> - <%= for n <- 0..@last do %> + <%= if @sibling_count > 0 do %> + <%= for n <- 0..@sibling_count do %> {n} <% end %> <% end %> @@ -184,7 +184,7 @@ defmodule LiveAdmin.Components.Container.Show do <% end %> <.expand_modal - id={"#{@id}-#{field}-#{@current}-expand"} + id={"#{indexed_id(@id, @current_index)}-#{field}-expand"} title={@title} field={field} value={Map.fetch!(@record, field)} @@ -197,12 +197,12 @@ defmodule LiveAdmin.Components.Container.Show do <%= for {field, {_, {_, %{related: schema}}}, _} <- @embeds, embed = Map.fetch!(@record, field), list = List.wrap(embed) do %> <%= for {record, index} <- Enum.with_index(list) do %> <.detail_view - id={"#{@id}_#{field}"} + id={"#{indexed_id(@id, @current_index)}_#{field}"} fields={Enum.map(schema.__schema__(:fields), &{&1, schema.__schema__(:type, &1), []})} record={record} title={trans(humanize(field))} - current={index} - last={Enum.count(list) - 1} + current_index={index} + sibling_count={Enum.count(list) - 1} resource={@resource} resources={@resources} base_path={@base_path} @@ -219,6 +219,9 @@ defmodule LiveAdmin.Components.Container.Show do defp renderable?({_, {Ecto.Embedded, _}}), do: false defp renderable?(_), do: true + defp indexed_id("main", _), do: "main" + defp indexed_id(id, index), do: "#{id}_#{index}" + defp assoc_resource_key(resource, field, resources, config) do schema = LiveAdmin.fetch_config(resource, :schema, config) LiveAdmin.associated_resource(schema, field, resources, :key) diff --git a/test/live_admin/components/container_test.exs b/test/live_admin/components/container_test.exs index 0e1d0e2..a4d9792 100644 --- a/test/live_admin/components/container_test.exs +++ b/test/live_admin/components/container_test.exs @@ -266,6 +266,27 @@ defmodule LiveAdmin.Components.ContainerTest do end end + describe "view resource with plural embed containing nested plural embed" do + setup %{conn: conn} do + post = + Repo.insert!(%Post{ + title: "test", + previous_versions: [ + %Version{links: [%Version.Link{url: "https://a.example"}]}, + %Version{links: [%Version.Link{url: "https://b.example"}]} + ] + }) + + {:ok, view, _} = live(conn, "/live_admin_test_post/#{post.post_id}") + + %{view: view} + end + + test "renders unique detail-view id for each parent's nested embed", %{view: view} do + assert has_element?(view, "#main_previous_versions_1_links_0") + end + end + describe "view child resource" do setup %{conn: conn} do user = Repo.insert!(%User{}) diff --git a/test/support/resources.ex b/test/support/resources.ex index 739bfeb..31932fb 100644 --- a/test/support/resources.ex +++ b/test/support/resources.ex @@ -79,6 +79,17 @@ defmodule LiveAdminTest.Post.Version do field(:body, :string) field(:tags, {:array, :string}) + embeds_many(:links, __MODULE__.Link) + timestamps(updated_at: false) end end + +defmodule LiveAdminTest.Post.Version.Link do + use Ecto.Schema + + @primary_key false + embedded_schema do + field(:url, :string) + end +end