Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions packages/live_ui/lib/live_ui/renderer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,38 @@ defmodule LiveUi.Renderer do
<%= for child <- child_elements(@element, :default) do %>
<.render element={child} event_target={@event_target} />
<% end %>
<div class="live-ui-top-strip-trailing">
<%= if get_in(@element.attributes, [:shell, :search_event]) do %>
<button
type="button"
class="live-ui-top-strip-search"
aria-label="Open search"
phx-click={get_in(@element.attributes, [:shell, :search_event])}
phx-target={@event_target}
>
<span class="live-ui-top-strip-search-icon" aria-hidden="true"></span>
</button>
<% end %>
<%= if get_in(@element.attributes, [:shell, :settings_event]) do %>
<button
type="button"
class="live-ui-top-strip-settings"
aria-label="Open settings"
phx-click={get_in(@element.attributes, [:shell, :settings_event])}
phx-target={@event_target}
>
<span class="live-ui-top-strip-settings-icon" aria-hidden="true"></span>
</button>
<% end %>
<%= if get_in(@element.attributes, [:shell, :user_avatar_url]) do %>
<img
class="live-ui-top-strip-avatar"
src={get_in(@element.attributes, [:shell, :user_avatar_url])}
alt="User profile"
aria-label="User profile"
/>
<% end %>
</div>
</header>
"""
end
Expand Down
74 changes: 73 additions & 1 deletion packages/live_ui/test/live_ui/renderer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule LiveUi.RendererTest do
import Phoenix.LiveViewTest

alias UnifiedIUR.{Container, Element, Forms, Interaction, Layout}
alias UnifiedIUR.Widgets.{Foundational, Input, Navigation}
alias UnifiedIUR.Widgets.{Components, Foundational, Input, Navigation}

test "renderer maps foundational canonical widgets and layouts into native components" do
element =
Expand Down Expand Up @@ -251,4 +251,76 @@ defmodule LiveUi.RendererTest do
assert html =~ ~s(name="element_id" value="profile-name")
assert html =~ ~s(name="widget" value="text_input")
end

describe "top_strip trailing-region affordances" do
test "renders basic top_strip without trailing affordances" do
element = Components.top_strip([], brand: "Ariston", context: "Dashboard")
html = render_component(&LiveUi.Renderer.render/1, %{element: element})

assert html =~ ~s(data-live-ui-shell-position="top")
assert html =~ "live-ui-top-strip-brand"
assert html =~ "Ariston"
assert html =~ "live-ui-top-strip-trailing"
refute html =~ "live-ui-top-strip-search"
refute html =~ "live-ui-top-strip-settings"
refute html =~ "live-ui-top-strip-avatar"
end

test "renders search button when search_event is set" do
element = Components.top_strip([], brand: "Ariston", search_event: "open_search")
html = render_component(&LiveUi.Renderer.render/1, %{element: element})

assert html =~ "live-ui-top-strip-search"
assert html =~ ~s(aria-label="Open search")
assert html =~ ~s(phx-click="open_search")
refute html =~ "live-ui-top-strip-settings"
refute html =~ "live-ui-top-strip-avatar"
end

test "renders settings button when settings_event is set" do
element = Components.top_strip([], brand: "Ariston", settings_event: "open_settings")
html = render_component(&LiveUi.Renderer.render/1, %{element: element})

assert html =~ "live-ui-top-strip-settings"
assert html =~ ~s(aria-label="Open settings")
assert html =~ ~s(phx-click="open_settings")
refute html =~ "live-ui-top-strip-search"
refute html =~ "live-ui-top-strip-avatar"
end

test "renders avatar img when user_avatar_url is set" do
element =
Components.top_strip([],
brand: "Ariston",
user_avatar_url: "https://example.com/avatar.png"
)

html = render_component(&LiveUi.Renderer.render/1, %{element: element})

assert html =~ "live-ui-top-strip-avatar"
assert html =~ ~s(src="https://example.com/avatar.png")
assert html =~ ~s(aria-label="User profile")
refute html =~ "live-ui-top-strip-search"
refute html =~ "live-ui-top-strip-settings"
end

test "renders all three trailing affordances together" do
element =
Components.top_strip([],
brand: "Ariston",
search_event: "open_search",
settings_event: "open_settings",
user_avatar_url: "https://example.com/avatar.png"
)

html = render_component(&LiveUi.Renderer.render/1, %{element: element})

assert html =~ "live-ui-top-strip-search"
assert html =~ ~s(phx-click="open_search")
assert html =~ "live-ui-top-strip-settings"
assert html =~ ~s(phx-click="open_settings")
assert html =~ "live-ui-top-strip-avatar"
assert html =~ ~s(src="https://example.com/avatar.png")
end
end
end
18 changes: 11 additions & 7 deletions packages/unified_iur/lib/unified_iur/widgets/components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,17 @@ defmodule UnifiedIUR.Widgets.Components do
:top_strip,
:layer_shell_and_callout,
%{
shell: %{
position: :top,
brand: option(opts, :brand, ""),
context: option(opts, :context, ""),
theme: option(opts, :theme, :light),
pane_open?: option(opts, :pane_open?, false)
}
shell:
%{
position: :top,
brand: option(opts, :brand, ""),
context: option(opts, :context, ""),
theme: option(opts, :theme, :light),
pane_open?: option(opts, :pane_open?, false)
}
|> maybe_put(:search_event, option(opts, :search_event))
|> maybe_put(:settings_event, option(opts, :settings_event))
|> maybe_put(:user_avatar_url, option(opts, :user_avatar_url))
},
Map.put(opts, :children, children)
)
Expand Down
50 changes: 50 additions & 0 deletions packages/unified_iur/test/unified_iur/widgets/components_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,56 @@ defmodule UnifiedIUR.Widgets.ComponentsTest do
assert code.attributes.text_safety == %{content: :plain_text}
end

test "top_strip stores trailing-region affordance attrs in shell map" do
strip_no_trailing = Components.top_strip([], brand: "Ariston", context: "Dashboard")

assert strip_no_trailing.kind == :top_strip
assert strip_no_trailing.attributes.shell.brand == "Ariston"
assert strip_no_trailing.attributes.shell.context == "Dashboard"
refute Map.has_key?(strip_no_trailing.attributes.shell, :search_event)
refute Map.has_key?(strip_no_trailing.attributes.shell, :settings_event)
refute Map.has_key?(strip_no_trailing.attributes.shell, :user_avatar_url)

strip_with_search =
Components.top_strip([], brand: "Ariston", search_event: "open_search")

assert strip_with_search.attributes.shell.search_event == "open_search"
refute Map.has_key?(strip_with_search.attributes.shell, :settings_event)
refute Map.has_key?(strip_with_search.attributes.shell, :user_avatar_url)

strip_with_settings =
Components.top_strip([], brand: "Ariston", settings_event: "open_settings")

assert strip_with_settings.attributes.shell.settings_event == "open_settings"

strip_with_avatar =
Components.top_strip([],
brand: "Ariston",
user_avatar_url: "https://example.com/avatar.png"
)

assert strip_with_avatar.attributes.shell.user_avatar_url == "https://example.com/avatar.png"

strip_all_trailing =
Components.top_strip([],
brand: "Ariston",
search_event: "open_search",
settings_event: "open_settings",
user_avatar_url: "https://example.com/avatar.png"
)

assert strip_all_trailing.attributes.shell == %{
position: :top,
brand: "Ariston",
context: "",
theme: :light,
pane_open?: false,
search_event: "open_search",
settings_event: "open_settings",
user_avatar_url: "https://example.com/avatar.png"
}
end

test "represents list repeat metadata and hydrated row children" do
template = Components.artifact_row("Template", [], row_identity: :id)

Expand Down
2 changes: 1 addition & 1 deletion packages/unified_ui/lib/unified_ui/widget_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ defmodule UnifiedUi.WidgetComponents do
kind: :top_strip,
family: :layer_shell_and_callout,
summary:
"Top shell strip with brand, context, theme controls, and mode navigation children.",
"Top shell strip with brand, context, theme controls, mode navigation children, and optional trailing-region affordances (search_event, settings_event, user_avatar_url).",
aliases: []
},
%{
Expand Down
Loading