Skip to content

Commit 83d6a6c

Browse files
authored
update breadcrumb navigation to show sandbox hierarchy (#3817)
* Update breadcrumb navigation to show sandbox hierarchy - Display parent project name before sandbox name in breadcrumbs - Pass project attribute to header component in SandboxLive.Index - Add comprehensive tests for breadcrumb rendering with sandboxes * clean up * Refactor breadcrumb logic to remove duplication
1 parent 5043e49 commit 83d6a6c

File tree

4 files changed

+106
-3
lines changed

4 files changed

+106
-3
lines changed

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ and this project adheres to
3737
- Refactored inspector component architecture to use composition pattern with
3838
reusable layout shell and pure form components
3939
[#3768](https://github.com/OpenFn/lightning/issues/3768)
40+
- Updated breadcrumb navigation to display parent project name before sandbox
41+
name [#3474](https://github.com/OpenFn/lightning/issues/3474)
4042

4143
### Fixed
4244

@@ -922,8 +924,9 @@ This bug was introduced in version `v2.12.3-pre` on May 29th. If you're tracking
922924
- Refactor OAuth credentials to reuse existing refresh tokens for same scopes
923925
[#2908](https://github.com/OpenFn/lightning/issues/2908) \
924926
⚠️️ Please note that you will need to migrate your existing OAuth credentials.
925-
To do that run the following command: `mix run priv/repo/migrate_oauth_credentials.exs`
926-
for local setup or `docker exec -it <lightning_container_name> /app/bin/lightning eval "Lightning.Credentials.OauthMigration.run()"`
927+
To do that run the following command:
928+
`mix run priv/repo/migrate_oauth_credentials.exs` for local setup or
929+
`docker exec -it <lightning_container_name> /app/bin/lightning eval "Lightning.Credentials.OauthMigration.run()"`
927930
for production environments.
928931

929932
### Changed

lib/lightning_web/components/layout_components.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ defmodule LightningWeb.LayoutComponents do
133133
<.breadcrumb path="/projects">
134134
<:label>Projects</:label>
135135
</.breadcrumb>
136+
137+
<%= if @project.parent_id && Ecto.assoc_loaded?(@project.parent) do %>
138+
<.breadcrumb path={"/projects/#{@project.parent.id}/w"}>
139+
<:label>{@project.parent.name}</:label>
140+
</.breadcrumb>
141+
<% end %>
142+
136143
<.breadcrumb path={"/projects/#{@project.id}/w"}>
137144
<:label>{@project.name}</:label>
138145
</.breadcrumb>

lib/lightning_web/live/sandbox_live/index.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ defmodule LightningWeb.SandboxLive.Index do
278278
~H"""
279279
<LayoutComponents.page_content>
280280
<:header>
281-
<LayoutComponents.header current_user={@current_user}>
281+
<LayoutComponents.header current_user={@current_user} project={@project}>
282282
<:title>Sandboxes</:title>
283283
</LayoutComponents.header>
284284
</:header>

test/lightning_web/components/layout_components_test.exs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,97 @@ defmodule LightningWeb.LayoutComponentsTest do
3434
assert element |> Floki.attribute("class") |> List.first() =~
3535
"menu-item-active"
3636
end
37+
38+
describe "header/1 breadcrumbs" do
39+
import Lightning.ProjectsFixtures
40+
41+
test "renders regular project breadcrumb" do
42+
user = Lightning.AccountsFixtures.user_fixture()
43+
project = project_fixture()
44+
45+
assigns = %{
46+
current_user: user,
47+
socket: nil,
48+
breadcrumbs: [],
49+
project: project,
50+
title: []
51+
}
52+
53+
html =
54+
(&LayoutComponents.header/1)
55+
|> render_component(assigns)
56+
57+
assert html =~ "Projects"
58+
assert html =~ project.name
59+
refute html =~ "parent"
60+
end
61+
62+
test "renders sandbox breadcrumb with parent project" do
63+
user = Lightning.AccountsFixtures.user_fixture()
64+
parent_project = project_fixture(name: "Parent Project")
65+
66+
sandbox_project =
67+
project_fixture(name: "My Sandbox", parent_id: parent_project.id)
68+
|> Lightning.Repo.preload(:parent)
69+
70+
assigns = %{
71+
current_user: user,
72+
socket: nil,
73+
breadcrumbs: [],
74+
project: sandbox_project,
75+
title: []
76+
}
77+
78+
html =
79+
(&LayoutComponents.header/1)
80+
|> render_component(assigns)
81+
82+
assert html =~ "Projects"
83+
assert html =~ "Parent Project"
84+
assert html =~ "My Sandbox"
85+
86+
parsed = Floki.parse_fragment!(html)
87+
breadcrumb_links = Floki.find(parsed, "nav ol li a")
88+
89+
breadcrumb_texts =
90+
breadcrumb_links
91+
|> Enum.map(&Floki.text/1)
92+
|> Enum.map(&String.trim/1)
93+
94+
assert "Parent Project" in breadcrumb_texts
95+
assert "My Sandbox" in breadcrumb_texts
96+
97+
breadcrumb_index_parent =
98+
Enum.find_index(breadcrumb_texts, &(&1 == "Parent Project"))
99+
100+
breadcrumb_index_sandbox =
101+
Enum.find_index(breadcrumb_texts, &(&1 == "My Sandbox"))
102+
103+
assert breadcrumb_index_parent < breadcrumb_index_sandbox
104+
end
105+
106+
test "renders sandbox breadcrumb without parent when parent not preloaded" do
107+
user = Lightning.AccountsFixtures.user_fixture()
108+
parent_project = project_fixture(name: "Parent Project")
109+
110+
sandbox_project =
111+
project_fixture(name: "My Sandbox", parent_id: parent_project.id)
112+
113+
assigns = %{
114+
current_user: user,
115+
socket: nil,
116+
breadcrumbs: [],
117+
project: sandbox_project,
118+
title: []
119+
}
120+
121+
html =
122+
(&LayoutComponents.header/1)
123+
|> render_component(assigns)
124+
125+
assert html =~ "Projects"
126+
refute html =~ "Parent Project"
127+
assert html =~ "My Sandbox"
128+
end
129+
end
37130
end

0 commit comments

Comments
 (0)