fix: tag editor hopefully working and fixes to test runner
This commit is contained in:
@@ -13,6 +13,8 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
|||||||
|
|
||||||
embed_templates("tags_editor_html/*")
|
embed_templates("tags_editor_html/*")
|
||||||
|
|
||||||
|
@tags_sections ~w(cloud manage merge)
|
||||||
|
|
||||||
@spec assign_socket(term()) :: term()
|
@spec assign_socket(term()) :: term()
|
||||||
def assign_socket(socket) do
|
def assign_socket(socket) do
|
||||||
assign(socket, :tags_editor, build(socket.assigns))
|
assign(socket, :tags_editor, build(socket.assigns))
|
||||||
@@ -198,9 +200,13 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
|||||||
|
|
||||||
@spec sync(term(), term(), term()) :: term()
|
@spec sync(term(), term(), term()) :: term()
|
||||||
def sync(socket, reload, append_output) do
|
def sync(socket, reload, append_output) do
|
||||||
_ = append_output
|
case Tags.sync_tags_from_posts(socket.assigns.projects.active_project_id) do
|
||||||
:ok = Tags.sync_tags_json(socket.assigns.projects.active_project_id)
|
{:ok, _tags} -> reload.(socket, socket.assigns.workbench)
|
||||||
reload.(socket, socket.assigns.workbench)
|
{:error, reason} ->
|
||||||
|
socket
|
||||||
|
|> append_output.(translated("Tags"), inspect(reason), nil, "error")
|
||||||
|
|> reload.(socket.assigns.workbench)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec build(term()) :: term()
|
@spec build(term()) :: term()
|
||||||
@@ -226,6 +232,8 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
|||||||
select: %{slug: template.slug, title: template.title}
|
select: %{slug: template.slug, title: template.title}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
selected_section = current_tags_section(assigns)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
tags:
|
tags:
|
||||||
Enum.map(tags, fn tag ->
|
Enum.map(tags, fn tag ->
|
||||||
@@ -235,7 +243,8 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
|||||||
new_tag: Map.get(assigns, :tags_editor_new_tag, %{"name" => "", "color" => ""}),
|
new_tag: Map.get(assigns, :tags_editor_new_tag, %{"name" => "", "color" => ""}),
|
||||||
edit_draft: edit_draft,
|
edit_draft: edit_draft,
|
||||||
templates: templates,
|
templates: templates,
|
||||||
merge_target: Map.get(assigns, :tags_editor_merge_target, List.first(selected) || "")
|
merge_target: Map.get(assigns, :tags_editor_merge_target, List.first(selected) || ""),
|
||||||
|
selected_section: selected_section
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -293,6 +302,25 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp current_tags_section(assigns) do
|
||||||
|
assigns
|
||||||
|
|> current_tab_meta()
|
||||||
|
|> Map.get(:sidebar_item_id, "tags-cloud")
|
||||||
|
|> to_string()
|
||||||
|
|> String.replace_prefix("tags-", "")
|
||||||
|
|> case do
|
||||||
|
section when section in @tags_sections -> section
|
||||||
|
_other -> "cloud"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp current_tab_meta(assigns) do
|
||||||
|
case Map.get(assigns, :current_tab) do
|
||||||
|
%{type: type, id: id} -> Map.get(assigns[:tab_meta] || %{}, {type, id}, %{})
|
||||||
|
_other -> %{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp tag_counts(project_id) do
|
defp tag_counts(project_id) do
|
||||||
Repo.all(from post in Post, where: post.project_id == ^project_id, select: post.tags)
|
Repo.all(from post in Post, where: post.project_id == ^project_id, select: post.tags)
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|
|||||||
@@ -1,13 +1,26 @@
|
|||||||
<div class="tags-view-shell" data-testid="tags-editor">
|
<div
|
||||||
|
id="tags-editor-shell"
|
||||||
|
class="tags-view-shell"
|
||||||
|
data-testid="tags-editor"
|
||||||
|
phx-hook="TagsSectionScroll"
|
||||||
|
data-selected-tags-section={@tags_editor.selected_section}
|
||||||
|
data-tags-scroll-target={"tags-section-#{@tags_editor.selected_section}"}
|
||||||
|
>
|
||||||
<div class="tags-view">
|
<div class="tags-view">
|
||||||
<div class="tags-view-header">
|
<div class="tags-view-header">
|
||||||
<h2><%= translated("Tags") %></h2>
|
<h2><%= translated("Tags") %></h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tags-view-content">
|
<div class="tags-view-content">
|
||||||
<div class="tags-section">
|
<div class="tags-section" id="tags-section-cloud">
|
||||||
<div class="tags-section-header"><h3><%= translated("Tag Cloud") %></h3></div>
|
<div class="tags-section-header"><h3><%= translated("Tag Cloud") %></h3></div>
|
||||||
<div class="tags-section-content">
|
<div class="tags-section-content">
|
||||||
|
<%= if Enum.empty?(@tags_editor.tags) do %>
|
||||||
|
<div class="tags-empty-state">
|
||||||
|
<p><%= translated("No tags found") %></p>
|
||||||
|
<button class="secondary" type="button" phx-click="sync_tags_editor"><%= translated("Discover") %></button>
|
||||||
|
</div>
|
||||||
|
<% else %>
|
||||||
<div class="tag-cloud">
|
<div class="tag-cloud">
|
||||||
<%= for tag <- @tags_editor.tags do %>
|
<%= for tag <- @tags_editor.tags do %>
|
||||||
<button class={["tag-cloud-item", if(tag.name in @tags_editor.selected, do: "selected"), if(tag.color, do: "has-color")]} style={tag_style(tag, @tags_editor.tags)} type="button" phx-click="toggle_tag_selection" phx-value-name={tag.name}>
|
<button class={["tag-cloud-item", if(tag.name in @tags_editor.selected, do: "selected"), if(tag.color, do: "has-color")]} style={tag_style(tag, @tags_editor.tags)} type="button" phx-click="toggle_tag_selection" phx-value-name={tag.name}>
|
||||||
@@ -15,10 +28,11 @@
|
|||||||
</button>
|
</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tags-section">
|
<div class="tags-section" id="tags-section-manage">
|
||||||
<div class="tags-section-header"><h3><%= translated("Create / Edit") %></h3></div>
|
<div class="tags-section-header"><h3><%= translated("Create / Edit") %></h3></div>
|
||||||
<div class="tags-section-content">
|
<div class="tags-section-content">
|
||||||
<form class="tag-create-form" phx-change="change_new_tag_editor">
|
<form class="tag-create-form" phx-change="change_new_tag_editor">
|
||||||
@@ -48,7 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tags-section">
|
<div class="tags-section" id="tags-section-merge">
|
||||||
<div class="tags-section-header"><h3><%= translated("Merge Tags") %></h3></div>
|
<div class="tags-section-header"><h3><%= translated("Merge Tags") %></h3></div>
|
||||||
<div class="tags-section-content">
|
<div class="tags-section-content">
|
||||||
<div class="merge-form">
|
<div class="merge-form">
|
||||||
@@ -64,7 +78,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tags-section">
|
<div class="tags-section" id="tags-section-sync">
|
||||||
<div class="tags-section-header"><h3><%= translated("Sync") %></h3></div>
|
<div class="tags-section-header"><h3><%= translated("Sync") %></h3></div>
|
||||||
<div class="tags-section-content">
|
<div class="tags-section-content">
|
||||||
<button class="secondary" type="button" phx-click="sync_tags_editor"><%= translated("Discover") %></button>
|
<button class="secondary" type="button" phx-click="sync_tags_editor"><%= translated("Discover") %></button>
|
||||||
|
|||||||
@@ -830,6 +830,35 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
TagsSectionScroll: {
|
||||||
|
mounted() {
|
||||||
|
this.lastTargetId = null;
|
||||||
|
this.scrollToSelectedSection();
|
||||||
|
},
|
||||||
|
|
||||||
|
updated() {
|
||||||
|
this.scrollToSelectedSection();
|
||||||
|
},
|
||||||
|
|
||||||
|
scrollToSelectedSection() {
|
||||||
|
const targetId = this.el.dataset.tagsScrollTarget;
|
||||||
|
|
||||||
|
if (!targetId || targetId === this.lastTargetId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastTargetId = targetId;
|
||||||
|
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
const target = document.getElementById(targetId);
|
||||||
|
|
||||||
|
if (target && this.el.contains(target)) {
|
||||||
|
target.scrollIntoView({ block: "start", behavior: "smooth" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
ChatSurface: {
|
ChatSurface: {
|
||||||
mounted() {
|
mounted() {
|
||||||
this.stickToBottom = true;
|
this.stickToBottom = true;
|
||||||
|
|||||||
@@ -330,6 +330,61 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert html =~ ~s(data-settings-scroll-target="settings-section-ai")
|
assert html =~ ~s(data-settings-scroll-target="settings-section-ai")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "tags sidebar selections expose a scroll target for the tags editor" do
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
_html = render_click(view, "select_view", %{"view" => "tags"})
|
||||||
|
|
||||||
|
html =
|
||||||
|
view
|
||||||
|
|> element("[data-testid='sidebar-open-item'][data-item-id='tags-merge']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
assert html =~ ~s(data-testid="tags-editor")
|
||||||
|
assert html =~ ~s(phx-hook="TagsSectionScroll")
|
||||||
|
assert html =~ ~s(data-selected-tags-section="merge")
|
||||||
|
assert html =~ ~s(data-tags-scroll-target="tags-section-merge")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "tags discover materializes post tags and enables merge from the tags editor", %{
|
||||||
|
project: project
|
||||||
|
} do
|
||||||
|
assert {:ok, post} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Tagged Post",
|
||||||
|
content: "Body",
|
||||||
|
tags: ["Alpha", "Beta"]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert Tags.list_tags(project.id) == []
|
||||||
|
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
_html = render_click(view, "select_view", %{"view" => "tags"})
|
||||||
|
|
||||||
|
_html =
|
||||||
|
view
|
||||||
|
|> element("[data-testid='sidebar-open-item'][data-item-id='tags-cloud']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
html = render_click(view, "sync_tags_editor", %{})
|
||||||
|
|
||||||
|
assert Enum.map(Tags.list_tags(project.id), & &1.name) == ["Alpha", "Beta"]
|
||||||
|
assert html =~ "Alpha"
|
||||||
|
assert html =~ "Beta"
|
||||||
|
|
||||||
|
_html = render_click(view, "toggle_tag_selection", %{"name" => "Alpha"})
|
||||||
|
_html = render_click(view, "toggle_tag_selection", %{"name" => "Beta"})
|
||||||
|
_html = render_change(view, "change_merge_target", %{"target" => "Alpha"})
|
||||||
|
|
||||||
|
html = render_click(view, "merge_tags_editor", %{})
|
||||||
|
|
||||||
|
assert Enum.map(Tags.list_tags(project.id), & &1.name) == ["Alpha"]
|
||||||
|
assert Repo.get!(Post, post.id).tags == ["Alpha"]
|
||||||
|
assert html =~ "Alpha"
|
||||||
|
end
|
||||||
|
|
||||||
test "database-backed sidebar entries require confirmation before deletion", %{
|
test "database-backed sidebar entries require confirmation before deletion", %{
|
||||||
project: project,
|
project: project,
|
||||||
temp_dir: temp_dir
|
temp_dir: temp_dir
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ cache_root = Path.join(System.tmp_dir!(), "bds-test-cache-#{System.unique_intege
|
|||||||
File.mkdir_p!(cache_root)
|
File.mkdir_p!(cache_root)
|
||||||
Application.put_env(:bds, :project_cache_root, cache_root)
|
Application.put_env(:bds, :project_cache_root, cache_root)
|
||||||
|
|
||||||
|
Enum.each(["LC_ALL", "LC_MESSAGES", "LANG"], fn variable ->
|
||||||
|
System.put_env(variable, "en_US.UTF-8")
|
||||||
|
end)
|
||||||
|
|
||||||
ExUnit.start()
|
ExUnit.start()
|
||||||
|
|
||||||
ExUnit.after_suite(fn _results ->
|
ExUnit.after_suite(fn _results ->
|
||||||
|
|||||||
Reference in New Issue
Block a user