Skip to content

Commit

Permalink
Implement nested assemblies navigation (decidim#13662)
Browse files Browse the repository at this point in the history
  • Loading branch information
mllocs authored Dec 5, 2024
1 parent 24568e5 commit f749d39
Show file tree
Hide file tree
Showing 17 changed files with 168 additions and 197 deletions.
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,8 @@ GEM
net-smtp (0.3.4)
net-protocol
nio4r (2.7.3)
nokogiri (1.16.7-arm64-darwin)
racc (~> 1.4)
nokogiri (1.16.7-x86_64-linux)
racc (~> 1.4)
oauth (1.1.0)
Expand Down Expand Up @@ -798,6 +800,7 @@ GEM
zeitwerk (2.6.18)

PLATFORMS
arm64-darwin-23
x86_64-linux

DEPENDENCIES
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,7 @@
@apply inline-block;
}
}

.table-list__title-ellipsis {
@apply whitespace-nowrap overflow-hidden text-ellipsis max-w-[200px] sm:max-w-sm md:max-w-md lg:max-w-lg xl:max-w-2xl inline-block align-middle;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ class AssembliesController < Decidim::Assemblies::Admin::ApplicationController
include Decidim::Assemblies::Admin::Filterable
include Decidim::Admin::ParticipatorySpaceAdminContext
include Decidim::Admin::HasTrashableResources
helper_method :current_assembly, :parent_assembly, :current_participatory_space

helper_method :current_assembly, :parent_assembly, :parent_assembly_id, :current_participatory_space

layout "decidim/admin/assemblies"

Expand All @@ -31,7 +32,7 @@ def create
CreateAssembly.call(@form) do
on(:ok) do |assembly|
flash[:notice] = I18n.t("assemblies.create.success", scope: "decidim.admin")
redirect_to assemblies_path(q: { parent_id_eq: assembly.parent_id })
redirect_to components_path(assembly)
end

on(:invalid) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def create
CopyAssembly.call(@form, current_assembly, current_user) do
on(:ok) do
flash[:notice] = I18n.t("assemblies_copies.create.success", scope: "decidim.admin")
redirect_to assemblies_path(parent_id: current_assembly.parent_id)
redirect_to assemblies_path
end

on(:invalid) do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,32 @@ def processes_selected
end
end

# Public: A collection of Assemblies that can be selected as parent
# assemblies for another assembly; to be used in forms.
def parent_assemblies_for_select
@parent_assemblies_for_select ||= ParentAssembliesForSelect.for(current_organization, current_assembly)
# Public: select options representing a collection of Assemblies that
# can be selected as parent assemblies for another assembly; to be used in forms.
def parent_assemblies_options
options = []
root_assemblies = ParentAssembliesForSelect.for(current_organization, current_assembly).where(parent_id: nil).sort_by(&:weight)

root_assemblies.each do |assembly|
build_assembly_options(assembly, options)
end

options
end

private

# Recursively build the options for the assembly tree
def build_assembly_options(assembly, options, level = 0)
name = sanitize("#{"&nbsp;" * 4 * level} #{assembly.translated_title}")
options << [name, assembly.id]

# Skip the current assembly to avoid selecting a child as parent
return if assembly == current_assembly

assembly.children.each do |child|
build_assembly_options(child, options, level + 1)
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "src/decidim/assemblies/admin/assemblies_list"
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint-disable require-jsdoc */

class AdminAssembliesListComponent {
run() {
this.rebindArrows();
}

rebindArrows() {
this.unbindArrows();
this.bindArrows();
}

bindArrows() {
document.querySelectorAll("[data-arrow-up]").forEach((element) => {
element.addEventListener("click", this._onClickUpArrow);
});
document.querySelectorAll("[data-arrow-down]").forEach((element) => {
element.addEventListener("click", this._onClickDownArrow);
});
}

unbindArrows() {
document.querySelectorAll("[data-arrow-up]").forEach((element) => {
element.removeEventListener("click", this._onClickUpArrow);
});
document.querySelectorAll("[data-arrow-down]").forEach((element) => {
element.removeEventListener("click", this._onClickDownArrow);
});
}

_onClickDownArrow(event) {
event.preventDefault();

const target = event.currentTarget;
const assembly = target.closest("[data-assembly-id]");
const upArrow = assembly.querySelector("[data-arrow-up]");

target.classList.toggle("hidden");
upArrow.classList.toggle("hidden");
}

_onClickUpArrow(event) {
event.preventDefault();

const target = event.currentTarget;
const assembly = target.closest("[data-assembly-id]");
const parentLevel = assembly.dataset.level;
const downArrow = assembly.querySelector("[data-arrow-down]");

target.classList.toggle("hidden");
downArrow.classList.toggle("hidden");

// Get all following tr elements
let nextElement = assembly.nextElementSibling;
while (nextElement) {
const currentLevel = nextElement.dataset.level;
const nextSibling = nextElement.nextElementSibling;

if (currentLevel > parentLevel) {
nextElement.remove();
} else {
break;
}
nextElement = nextSibling;
}
}
}

window.Decidim.AdminAssembliesListComponent = AdminAssembliesListComponent;
const component = new AdminAssembliesListComponent();

component.run();
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
<tr>
<td>
<tr data-assembly-id="<%= assembly.id %>" data-parent-id="<%= assembly.parent_id %>" data-level="<%= assembly.ancestors.count %>">
<td class="whitespace-nowrap">
<% if parent_assembly_id %>
<% [assembly.ancestors.count - 1, 0].max.times do |index| %>
<span class="px-3"></span>
<% end %>
<span class="px-3 text-xl text-secondary opacity-10">|</span>
<% end %>

<% if assembly.promoted? %>
<%= icon_with_tooltip "star-s-fill", t("models.assembly.fields.promoted", scope: "decidim.admin") %>
<% end %>

<% if allowed_to? :update, :assembly, assembly: assembly %>
<%= link_to translated_attribute(assembly.title), edit_assembly_path(assembly) %>
<% elsif allowed_to? :read, :component, assembly: assembly %>
<%= link_to translated_attribute(assembly.title), components_path(assembly) %><br>
<% else %>
<%= translated_attribute(assembly.title) %>
<span class="table-list__title-ellipsis">
<% if allowed_to? :update, :assembly, assembly: assembly %>
<%= link_to translated_attribute(assembly.title), edit_assembly_path(assembly) %>
<% elsif allowed_to? :read, :component, assembly: assembly %>
<%= link_to translated_attribute(assembly.title), components_path(assembly) %><br>
<% else %>
<%= translated_attribute(assembly.title) %>
<% end %>
</span>

<% if assembly.children.count.positive? %>
<%= link_to url_for(query_params_with(parent_id_eq: assembly.id)), remote: true, data: { arrow_down: true } do %>
<%= icon "arrow-down-s-line", class: "w-4 h-4 ml-2" %>
<% end %>
<%= link_to "#", class: "hidden", data: { arrow_up: true } do %>
<%= icon "arrow-up-s-line", class: "w-4 h-4 ml-2" %>
<% end %>
<% end %>
</td>
<td class="table-list__date">
Expand Down Expand Up @@ -47,14 +65,6 @@
<% else %>
<span class="action-space icon"></span>
<% end %>
<% if assembly.children.count.positive? || allowed_to?(:read, :assembly, assembly:) %>
<%= icon_link_to "government-line",
url_for(query_params_with(parent_id_eq: assembly.id)),
t("decidim.admin.titles.assemblies"),
class: "action-icon--dial #{"highlighted" if assembly.children.count.positive?}" %>
<% else %>
<span class="action-space icon"></span>
<% end %>
<% if allowed_to? :copy, :assembly, assembly: assembly, assembly: parent_assembly %>
<%= icon_link_to "file-copy-line", new_assembly_copy_path(assembly), t("actions.duplicate", scope: "decidim.admin"), class: "action-icon--copy" %>
<% else %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,7 @@
<% else %>
<div class="row column">
<%= form.select :parent_id,
options_from_collection_for_select(
parent_assemblies_for_select,
:id,
:translated_title,
selected: current_assembly.try(:parent_id)
),
parent_assemblies_options,
include_blank: t(".select_parent_assembly") %>
</div>
<% end %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@

<%= decidim_paginate @assemblies %>
</div>

<%= append_javascript_pack_tag "decidim_assemblies_admin_list" %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
$('[data-assembly-id="<%= parent_assembly_id %>"]').after(
'<%= j(render partial: "decidim/assemblies/admin/assemblies/assembly_row",
collection: @assemblies,
as: :assembly,
locals: { view: :index }).strip.html_safe %>'
);

var component = new window.Decidim.AdminAssembliesListComponent();

component.run();
3 changes: 2 additions & 1 deletion decidim-assemblies/config/assets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
Decidim::Webpacker.register_path("#{base_path}/app/packs")
Decidim::Webpacker.register_entrypoints(
decidim_assemblies: "#{base_path}/app/packs/entrypoints/decidim_assemblies.js",
decidim_assemblies_admin: "#{base_path}/app/packs/entrypoints/decidim_assemblies_admin.js"
decidim_assemblies_admin: "#{base_path}/app/packs/entrypoints/decidim_assemblies_admin.js",
decidim_assemblies_admin_list: "#{base_path}/app/packs/entrypoints/decidim_assemblies_admin_list.js"
)
2 changes: 1 addition & 1 deletion decidim-assemblies/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ en:
assemblies:
create:
error: There was a problem creating a new assembly.
success: Assembly created successfully.
success: Assembly created successfully. You can now add components and configure it.
edit:
update: Update
index:
Expand Down
4 changes: 0 additions & 4 deletions decidim-assemblies/spec/shared/manage_assemblies_examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,4 @@
expect(page).to have_admin_callout("successfully")
end
end

it "shows the Assemblies link to manage nested assemblies" do
expect(page).to have_link("Assemblies", href: decidim_admin_assemblies.assemblies_path(q: { parent_id_eq: assembly.id }))
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@
expect(last_assembly.taxonomies).to contain_exactly(taxonomy)

within "[data-content]" do
expect(page).to have_current_path decidim_admin_assemblies.assemblies_path(q: { parent_id_eq: parent_assembly&.id })
expect(page).to have_content(translated(attributes[:title]))
expect(page).to have_current_path decidim_admin_assemblies.components_path(last_assembly)
end

visit decidim_admin.root_path
Expand Down Expand Up @@ -192,7 +191,7 @@ def assembly_without_type(type)
end
end

context "when managing child assemblies" do
context "when navigating child assemblies" do
let!(:parent_assembly) { create(:assembly, organization:) }
let!(:child_assembly) { create(:assembly, :with_content_blocks, organization:, parent: parent_assembly, blocks_manifests: [:announcement]) }
let(:assembly) { child_assembly }
Expand All @@ -201,24 +200,23 @@ def assembly_without_type(type)
switch_to_host(organization.host)
login_as user, scope: :user
visit decidim_admin_assemblies.assemblies_path
within "tr", text: translated(parent_assembly.title) do
click_on "Assemblies"
end
end

it_behaves_like "manage assemblies"
it_behaves_like "creating an assembly"
it_behaves_like "manage assemblies announcements"

describe "listing child assemblies" do
it_behaves_like "filtering collection by published/unpublished" do
let!(:published_space) { child_assembly }
let!(:unpublished_space) { create(:assembly, :unpublished, parent: parent_assembly, organization:) }
end
it "expands the parent assembly" do
expect(page).to have_no_content(translated(child_assembly.title))

within "tr", text: translated(parent_assembly.title) do
find("a[data-arrow-down]").click
end

expect(page).to have_content(translated(child_assembly.title))

within "tr", text: translated(parent_assembly.title) do
find("a[data-arrow-up]").click
end

it_behaves_like "filtering collection by private/public" do
let!(:public_space) { child_assembly }
let!(:private_space) { create(:assembly, :private, parent: parent_assembly, organization:) }
expect(page).to have_no_content(translated(child_assembly.title))
end
end
end
Expand Down
Loading

0 comments on commit f749d39

Please sign in to comment.