Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ and this project adheres to

### Fixed

- The breadcrumb project picker now shows the full ancestor path on the project
settings page (previously it collapsed to the sandbox's own name).
[#4769](https://github.com/OpenFn/lightning/issues/4769)

## [2.16.4-pre] - 2026-05-18

### Added
Expand Down
25 changes: 4 additions & 21 deletions lib/lightning_web/components/layout_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ defmodule LightningWeb.LayoutComponents do
## Example

<.breadcrumbs>
<.breadcrumb_project_picker project={@project} current_user={@current_user} access_root={@access_root} />
<.breadcrumb_project_picker project={@project} label={@project_label} />
<.breadcrumb_items items={[{"History", "/projects/\#{@project}/history"}]} />
<.breadcrumb>
<:label>{@page_title}</:label>
Expand Down Expand Up @@ -356,32 +356,15 @@ defmodule LightningWeb.LayoutComponents do
LiveView pages and the collaborative editor.
"""
attr :project, Lightning.Projects.Project, required: true
attr :current_user, Lightning.Accounts.User, default: nil
attr :access_root, Lightning.Projects.Project, default: nil
attr :label, :string, required: true

def breadcrumb_project_picker(assigns) do
alias Lightning.Projects
alias Lightning.Projects.Project

access_root =
case {assigns[:access_root], assigns[:current_user]} do
{%Project{} = ar, _} ->
ar

{nil, %Lightning.Accounts.User{} = user} ->
Projects.access_root_for_user(assigns.project, user)

_ ->
assigns.project
end

assigns =
assigns
|> assign(
:label,
Projects.display_name_within_access_root(assigns.project, access_root)
:is_sandbox,
to_string(Lightning.Projects.Project.sandbox?(assigns.project))
)
|> assign(:is_sandbox, to_string(Project.sandbox?(assigns.project)))
|> assign(:color, assigns.project.color)

~H"""
Expand Down
7 changes: 7 additions & 0 deletions lib/lightning_web/hooks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,19 @@ defmodule LightningWeb.Hooks do
access_root =
Lightning.Projects.access_root_for_user(project, current_user)

project_label =
Lightning.Projects.display_name_within_access_root(
project,
access_root
)

{:cont,
socket
|> assign(:side_menu_theme, "primary-theme")
|> assign(:project_user, project_user)
|> assign(:project, project)
|> assign(:access_root, access_root)
|> assign(:project_label, project_label)
|> assign(:projects, projects)}

true ->
Expand Down
3 changes: 1 addition & 2 deletions lib/lightning_web/live/channel_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ defmodule LightningWeb.ChannelLive.Index do
<LayoutComponents.breadcrumbs>
<LayoutComponents.breadcrumb_project_picker
project={@project}
current_user={@current_user}
access_root={@access_root}
label={@project_label}
/>
<LayoutComponents.breadcrumb>
<:label>{@page_title}</:label>
Expand Down
3 changes: 1 addition & 2 deletions lib/lightning_web/live/channel_request_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ defmodule LightningWeb.ChannelRequestLive.Show do
<LayoutComponents.breadcrumbs>
<LayoutComponents.breadcrumb_project_picker
project={@project}
current_user={@current_user}
access_root={@access_root}
label={@project_label}
/>
<LayoutComponents.breadcrumb_items items={[
{"History", ~p"/projects/#{@project}/history"},
Expand Down
3 changes: 1 addition & 2 deletions lib/lightning_web/live/dataclip_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ defmodule LightningWeb.DataclipLive.Show do
<LayoutComponents.breadcrumbs>
<LayoutComponents.breadcrumb_project_picker
project={@project}
current_user={@current_user}
access_root={@access_root}
label={@project_label}
/>
<LayoutComponents.breadcrumb>
<:label>
Expand Down
5 changes: 4 additions & 1 deletion lib/lightning_web/live/project_live/settings.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
<LayoutComponents.header current_user={@current_user}>
<:breadcrumbs>
<LayoutComponents.breadcrumbs>
<LayoutComponents.breadcrumb_project_picker project={@project} />
<LayoutComponents.breadcrumb_project_picker
project={@project}
label={@project_label}
/>
<LayoutComponents.breadcrumb>
<:label>{@page_title}</:label>
</LayoutComponents.breadcrumb>
Expand Down
3 changes: 1 addition & 2 deletions lib/lightning_web/live/run_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
<LayoutComponents.breadcrumbs>
<LayoutComponents.breadcrumb_project_picker
project={@project}
current_user={@current_user}
access_root={@access_root}
label={@project_label}
/>
<LayoutComponents.breadcrumb>
<:label>{@page_title}</:label>
Expand Down
3 changes: 1 addition & 2 deletions lib/lightning_web/live/run_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ defmodule LightningWeb.RunLive.Show do
<LayoutComponents.breadcrumbs>
<LayoutComponents.breadcrumb_project_picker
project={@project}
current_user={@current_user}
access_root={@access_root}
label={@project_label}
/>
<LayoutComponents.breadcrumb_items items={[
{"History", ~p"/projects/#{@project}/history"}
Expand Down
3 changes: 1 addition & 2 deletions lib/lightning_web/live/sandbox_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,7 @@ defmodule LightningWeb.SandboxLive.Index do
<LayoutComponents.breadcrumbs>
<LayoutComponents.breadcrumb_project_picker
project={@project}
current_user={@current_user}
access_root={@access_root}
label={@project_label}
/>
<LayoutComponents.breadcrumb>
<:label>Sandboxes</:label>
Expand Down
3 changes: 1 addition & 2 deletions lib/lightning_web/live/workflow_live/edit.ex
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ defmodule LightningWeb.WorkflowLive.Edit do
<LayoutComponents.breadcrumbs>
<LayoutComponents.breadcrumb_project_picker
project={@project}
current_user={@current_user}
access_root={@access_root}
label={@project_label}
/>
<LayoutComponents.breadcrumb_items items={[
{"Workflows", "/projects/#{@project.id}/w"}
Expand Down
3 changes: 1 addition & 2 deletions lib/lightning_web/live/workflow_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ defmodule LightningWeb.WorkflowLive.Index do
<LayoutComponents.breadcrumbs>
<LayoutComponents.breadcrumb_project_picker
project={@project}
current_user={@current_user}
access_root={@access_root}
label={@project_label}
/>
<LayoutComponents.breadcrumb>
<:label>{@page_title}</:label>
Expand Down
51 changes: 9 additions & 42 deletions test/lightning_web/components/layout_components_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -62,29 +62,24 @@ defmodule LightningWeb.LayoutComponentsTest do
end

describe "breadcrumb_project_picker/1" do
test "renders ReactComponent mount point for a root project" do
test "renders the ReactComponent mount point for a root project" do
project = %Lightning.Projects.Project{
id: Ecto.UUID.generate(),
name: "my-project",
parent_id: nil,
parent: %Ecto.Association.NotLoaded{
__field__: :parent,
__owner__: Lightning.Projects.Project,
__cardinality__: :one
}
parent_id: nil
}

html =
(&LayoutComponents.breadcrumb_project_picker/1)
|> render_component(%{project: project})
|> render_component(%{project: project, label: "my-project"})

assert html =~ "breadcrumb-project-picker-trigger"
assert html =~ ~s(data-react-name="PickerButton")
assert html =~ ~s(data-label="my-project")
assert html =~ ~s(data-is-sandbox="false")
end

test "renders ReactComponent mount point with sandbox data" do
test "renders the ReactComponent mount point for a sandbox" do
parent = %Lightning.Projects.Project{
id: Ecto.UUID.generate(),
name: "parent-project"
Expand All @@ -100,45 +95,17 @@ defmodule LightningWeb.LayoutComponentsTest do

html =
(&LayoutComponents.breadcrumb_project_picker/1)
|> render_component(%{project: project})
|> render_component(%{
project: project,
label: "parent-project/my-sandbox"
})

assert html =~ "breadcrumb-project-picker-trigger"
assert html =~ ~s(data-react-name="PickerButton")
assert html =~ ~s(data-label="my-sandbox")
assert html =~ ~s(data-label="parent-project/my-sandbox")
assert html =~ ~s(data-is-sandbox="true")
assert html =~ ~s(data-color="#E33D63")
end

test "uses the access_root attr to truncate the displayed ancestor chain" do
root = insert(:project, name: "acme-workspace")
sandbox = insert(:project, name: "acme-staging", parent: root)

html =
(&LayoutComponents.breadcrumb_project_picker/1)
|> render_component(%{project: sandbox, access_root: sandbox})

assert html =~ ~s(data-label="acme-staging")
refute html =~ "acme-workspace/acme-staging"
end

test "derives access_root from current_user when access_root is not passed" do
user = insert(:user)
root = insert(:project, name: "acme-workspace")

sandbox =
insert(:project,
name: "acme-staging",
parent: root,
project_users: [%{user: user, role: :admin}]
)

html =
(&LayoutComponents.breadcrumb_project_picker/1)
|> render_component(%{project: sandbox, current_user: user})

assert html =~ ~s(data-label="acme-staging")
refute html =~ "acme-workspace/acme-staging"
end
end

describe "global_project_picker/1" do
Expand Down
Loading