diff --git a/lib/ash_ui/rendering/live_ui_adapter.ex b/lib/ash_ui/rendering/live_ui_adapter.ex index 6b9fb9e5..6efe31b6 100644 --- a/lib/ash_ui/rendering/live_ui_adapter.ex +++ b/lib/ash_ui/rendering/live_ui_adapter.ex @@ -874,6 +874,104 @@ defmodule AshUI.Rendering.LiveUIAdapter do """ end + defp generate_heex(%{"type" => "top_strip"} = iur, opts) do + props = iur["props"] || %{} + title = escaped_text_prop(props, ["title", "label"], "") + brand = escaped_text_prop(props, "brand", "") + context = escaped_text_prop(props, "context", "") + + """ +
+ #{if brand != "", do: "#{brand}", else: ""} + #{if title != "", do: "

#{title}

", else: ""} + #{if context != "", do: "#{context}", else: ""} + #{generate_children(iur["children"], opts)} +
+ """ + end + + defp generate_heex(%{"type" => "sidebar_section"} = iur, opts) do + props = iur["props"] || %{} + label = escaped_text_prop(props, "label", "") + action_label = escaped_text_prop(props, ["action_label", "action_glyph"], "+") + action_intent = truthy_prop(props, "action_intent", false) + + action_html = + if action_intent do + ~s() + else + "" + end + + """ +
+
+ + #{action_html} +
+ #{generate_children(iur["children"], opts)} +
+ """ + end + + defp generate_heex(%{"type" => "sidebar_item"} = iur, opts) do + props = iur["props"] || %{} + label = escaped_text_prop(props, "label", "") + selected? = truthy_prop(props, "selected?", truthy_prop(props, "selected", false)) + aria_current = if selected?, do: ~s( aria-current="page"), else: "" + + """ +
  • + +
  • + """ + end + + defp generate_heex(%{"type" => "tabs"} = iur, _opts) do + props = iur["props"] || %{} + items = prop(props, "items", []) |> List.wrap() + active_item_id = text_prop(props, "active_item_id") + + tabs_html = + Enum.map_join(items, fn item -> + item = normalize_item(item) + item_id = text_prop(item, ["id", "item_id"], "") + item_label = escaped_text_prop(item, "label", "") + selected? = active_item_id && to_string(item_id) == to_string(active_item_id) + aria_selected = if selected?, do: "true", else: "false" + tab_index = if selected?, do: "0", else: "-1" + + ~s() + end) + + """ +
    +
    + #{tabs_html} +
    +
    + """ + end + + defp generate_heex(%{"type" => "tree_view"} = iur, _opts) do + props = iur["props"] || %{} + nodes = prop(props, "nodes", []) |> List.wrap() + selection_mode = escaped_text_prop(props, "selection_mode", "single") + + nodes_html = render_tree_nodes(nodes) + + """ +
    + +
    + """ + end + defp generate_heex(%{"type" => "slide_over_panel"} = iur, opts) do props = iur["props"] || %{} label = escaped_text_prop(props, ["label", "title"], "Panel") diff --git a/test/ash_ui/rendering/live_ui_adapter_test.exs b/test/ash_ui/rendering/live_ui_adapter_test.exs index 95d7adc8..ea3c1679 100644 --- a/test/ash_ui/rendering/live_ui_adapter_test.exs +++ b/test/ash_ui/rendering/live_ui_adapter_test.exs @@ -1257,4 +1257,171 @@ defmodule AshUI.Rendering.LiveUIAdapterTest do assert heex =~ ~s(data-selected="true") end end + + describe "shell-primitive generate_heex clauses (gap-1)" do + test "top_strip renders header with ash-top-strip class and brand/context spans" do + iur = %{ + "type" => "top_strip", + "id" => "top-strip-1", + "props" => %{"title" => "Dashboard", "brand" => "Ariston", "context" => "dev"}, + "children" => [], + "metadata" => %{} + } + + {:ok, heex} = LiveUIAdapter.render(iur, force_fallback: true) + + assert heex =~ " "top_strip", + "id" => "top-strip-2", + "props" => %{"brand" => "Ariston"}, + "children" => [ + %{ + "type" => "text", + "id" => "action-1", + "props" => %{"content" => "Actions"}, + "children" => [], + "metadata" => %{} + } + ], + "metadata" => %{} + } + + {:ok, heex} = LiveUIAdapter.render(iur, force_fallback: true) + + assert heex =~ "ash-top-strip" + assert heex =~ "Actions" + end + + test "sidebar_section renders section with heading label and children" do + iur = %{ + "type" => "sidebar_section", + "id" => "sidebar-section-1", + "props" => %{"label" => "Navigation"}, + "children" => [ + %{ + "type" => "text", + "id" => "nav-item-1", + "props" => %{"content" => "Home"}, + "children" => [], + "metadata" => %{} + } + ], + "metadata" => %{} + } + + {:ok, heex} = LiveUIAdapter.render(iur, force_fallback: true) + + assert heex =~ " "sidebar_section", + "id" => "sidebar-section-2", + "props" => %{"label" => "Workspaces", "action_intent" => true, "action_label" => "Add"}, + "children" => [], + "metadata" => %{} + } + + {:ok, heex} = LiveUIAdapter.render(iur, force_fallback: true) + + assert heex =~ "ash-sidebar-section-action" + assert heex =~ "Add" + end + + test "sidebar_item renders li with button and label" do + iur = %{ + "type" => "sidebar_item", + "id" => "sidebar-item-1", + "props" => %{"label" => "Proposals"}, + "children" => [], + "metadata" => %{} + } + + {:ok, heex} = LiveUIAdapter.render(iur, force_fallback: true) + + assert heex =~ " "sidebar_item", + "id" => "sidebar-item-2", + "props" => %{"label" => "Specs", "selected?" => true}, + "children" => [], + "metadata" => %{} + } + + {:ok, heex} = LiveUIAdapter.render(iur, force_fallback: true) + + assert heex =~ "ash-sidebar-item--selected" + assert heex =~ ~s(aria-current="page") + end + + test "tabs renders tablist with tab buttons for each item" do + iur = %{ + "type" => "tabs", + "id" => "tabs-1", + "props" => %{ + "items" => [ + %{"id" => "tab-a", "label" => "Overview"}, + %{"id" => "tab-b", "label" => "Details"} + ], + "active_item_id" => "tab-a" + }, + "children" => [], + "metadata" => %{} + } + + {:ok, heex} = LiveUIAdapter.render(iur, force_fallback: true) + + assert heex =~ "ash-tabs" + assert heex =~ ~s(role="tablist") + assert heex =~ ~s(role="tab") + assert heex =~ "Overview" + assert heex =~ "Details" + assert heex =~ ~s(aria-selected="true") + end + + test "tree_view renders section with ul and li nodes" do + iur = %{ + "type" => "tree_view", + "id" => "tree-1", + "props" => %{ + "nodes" => [ + %{"id" => "node-1", "label" => "Root Node"}, + %{"id" => "node-2", "label" => "Other Node"} + ], + "selection_mode" => "single" + }, + "children" => [], + "metadata" => %{} + } + + {:ok, heex} = LiveUIAdapter.render(iur, force_fallback: true) + + assert heex =~ "