Merge pull request #4345 from consul/sdg_list_refactor

Refactor tags and SDG "tags" rendering
This commit is contained in:
Javi Martín
2021-02-04 12:46:35 +01:00
committed by GitHub
32 changed files with 254 additions and 219 deletions

View File

@@ -606,12 +606,6 @@
img { img {
max-width: 12rem; max-width: 12rem;
} }
.budget-investment-content {
ul {
margin-bottom: 0;
}
}
} }
} }

View File

@@ -1,4 +1,4 @@
.sdg-goal-filter-links { .sdg-goal-tag-cloud {
.sdg-goal-tag-list { .sdg-goal-tag-list {
@extend %sdg-goal-list; @extend %sdg-goal-list;

View File

@@ -1,50 +1,31 @@
module SDG::TagList module SDG::TagList
extend ActiveSupport::Concern extend ActiveSupport::Concern
attr_reader :record_or_name, :limit attr_reader :record, :limit
delegate :link_list, to: :helpers
def initialize(record_or_name, limit: nil) def initialize(record, limit: nil)
@record_or_name = record_or_name @record = record
@limit = limit @limit = limit
end end
def render? def render?
process.enabled? SDG::ProcessEnabled.new(record).enabled?
end end
def see_more_link(collection) def tag_records
count = count_out_of_limit(collection) tags = record.send(association_name)
if count > 0 if tags.respond_to?(:limit)
[ tags.order(:code).limit(limit)
"#{count}+", else
polymorphic_path(record), tags.sort[0..(limit.to_i - 1)]
class: "more-#{i18n_namespace}", title: t("sdg.#{i18n_namespace}.filter.more", count: count)
]
end end
end end
def filter_text(goal_or_target) def see_more_link
t("sdg.#{i18n_namespace}.filter.link", render Shared::SeeMoreLinkComponent.new(record, association_name, limit: limit)
resources: model.model_name.human(count: :other),
code: goal_or_target.code)
end end
def index_by(advanced_search) def association_name
polymorphic_path(model, advanced_search: advanced_search) raise NotImplementedError, "method must be implemented in the included class"
end
def count_out_of_limit(collection)
return 0 unless limit
collection.size - limit
end
def process
@process ||= SDG::ProcessEnabled.new(record_or_name)
end
def model
process.name.constantize
end end
end end

View File

@@ -0,0 +1 @@
<%= link_list(*links, class: "sdg-#{parameter_name}-tag-list") %>

View File

@@ -0,0 +1,49 @@
class SDG::FilterLinksComponent < ApplicationComponent
attr_reader :records, :related_model, :see_more_link
delegate :link_list, to: :helpers
def initialize(records, related_model, see_more_link: nil)
@records = records
@related_model = related_model
@see_more_link = see_more_link
end
def links
[*sdg_links, see_more_link]
end
private
def sdg_links
records.map do |goal_or_target|
[
render(SDG::TagComponent.new(goal_or_target)),
index_by(parameter_name => goal_or_target.code),
title: filter_text(goal_or_target),
data: { code: goal_or_target.code }
]
end
end
def filter_text(goal_or_target)
t("sdg.#{i18n_namespace}.filter.link",
resources: related_model.model_name.human(count: :other),
code: goal_or_target.code)
end
def index_by(advanced_search)
polymorphic_path(related_model, advanced_search: advanced_search)
end
def i18n_namespace
parameter_name.pluralize
end
def parameter_name
if records.first.is_a?(SDG::Goal)
"goal"
else
"target"
end
end
end

View File

@@ -1,5 +0,0 @@
<div class="sdg-goal-filter-links">
<div class="sidebar-divider"></div>
<h2 class="sidebar-title"><%= heading %></h2>
<%= render SDG::Goals::TagListComponent.new(class_name) %>
</div>

View File

@@ -3,31 +3,17 @@ class SDG::Goals::PlainTagListComponent < ApplicationComponent
private private
def record
record_or_name
end
def tags def tags
[*goal_tags, see_more_link].compact [*goal_tags, see_more_link].select(&:present?)
end
def see_more_link
options = super(goals)
link_to(*options) if options.present?
end end
def goal_tags def goal_tags
goals.order(:code).limit(limit).map do |goal| tag_records.map do |goal|
render SDG::Goals::IconComponent.new(goal) render SDG::TagComponent.new(goal)
end end
end end
def goals def association_name
record.sdg_goals :sdg_goals
end
def i18n_namespace
"goals"
end end
end end

View File

@@ -0,0 +1,5 @@
<div class="sdg-goal-tag-cloud">
<div class="sidebar-divider"></div>
<h2 class="sidebar-title"><%= heading %></h2>
<%= render SDG::FilterLinksComponent.new(goals, class_name.constantize) %>
</div>

View File

@@ -1,4 +1,4 @@
class SDG::Goals::FilterLinksComponent < ApplicationComponent class SDG::Goals::TagCloudComponent < ApplicationComponent
attr_reader :class_name attr_reader :class_name
def initialize(class_name) def initialize(class_name)
@@ -14,4 +14,8 @@ class SDG::Goals::FilterLinksComponent < ApplicationComponent
def heading def heading
t("sdg.goals.filter.heading") t("sdg.goals.filter.heading")
end end
def goals
SDG::Goal.order(:code)
end
end end

View File

@@ -1 +1 @@
<%= link_list(*links, class: "sdg-goal-tag-list") %> <%= render SDG::FilterLinksComponent.new(tag_records, related_model, see_more_link: see_more_link) %>

View File

@@ -3,33 +3,11 @@ class SDG::Goals::TagListComponent < ApplicationComponent
private private
def record def association_name
record_or_name if record_or_name.respond_to?(:sdg_goals) :sdg_goals
end end
def links def related_model
[*goal_links, see_more_link(goals)] record.class
end
def goal_links
goals.order(:code).limit(limit).map do |goal|
[
render(SDG::Goals::IconComponent.new(goal)),
index_by_goal(goal),
title: filter_text(goal)
]
end
end
def goals
record&.sdg_goals || SDG::Goal.all
end
def index_by_goal(goal)
index_by(goal: goal.code)
end
def i18n_namespace
"goals"
end end
end end

View File

@@ -0,0 +1 @@
<%= text -%>

View File

@@ -0,0 +1,15 @@
class SDG::TagComponent < ApplicationComponent
attr_reader :goal_or_target
def initialize(goal_or_target)
@goal_or_target = goal_or_target
end
def text
if goal_or_target.is_a?(SDG::Goal)
render SDG::Goals::IconComponent.new(goal_or_target)
else
"#{SDG::Target.model_name.human} #{goal_or_target.code}"
end
end
end

View File

@@ -3,35 +3,17 @@ class SDG::Targets::PlainTagListComponent < ApplicationComponent
private private
def record
record_or_name
end
def tags def tags
[*target_tags, see_more_link].compact [*target_tags, see_more_link].select(&:present?)
end
def see_more_link
options = super(targets)
link_to(*options) if options.present?
end end
def target_tags def target_tags
targets.sort[0..(limit.to_i - 1)].map do |target| tag_records.map do |target|
tag.span(text(target), data: { code: target.code }) tag.span(render(SDG::TagComponent.new(target)), data: { code: target.code })
end end
end end
def targets def association_name
record.sdg_targets :sdg_targets
end
def text(target)
"#{SDG::Target.model_name.human} #{target.code}"
end
def i18n_namespace
"targets"
end end
end end

View File

@@ -1 +1 @@
<%= link_list(*links, class: "sdg-target-tag-list") %> <%= render SDG::FilterLinksComponent.new(tag_records, related_model, see_more_link: see_more_link) %>

View File

@@ -3,34 +3,11 @@ class SDG::Targets::TagListComponent < ApplicationComponent
private private
def record def association_name
record_or_name :sdg_targets
end end
def links def related_model
[*target_links, see_more_link(targets)] record.class
end
def target_links
targets.sort[0..(limit.to_i - 1)].map do |target|
[
"#{SDG::Target.model_name.human} #{target.code}",
index_by_target(target),
title: filter_text(target),
data: { code: target.code }
]
end
end
def targets
record.sdg_targets
end
def index_by_target(target)
index_by(target: target.code)
end
def i18n_namespace
"targets"
end end
end end

View File

@@ -0,0 +1,3 @@
<% if count_out_of_limit > 0 %>
<%= link_to text, url, class: html_class, title: title %>
<% end %>

View File

@@ -0,0 +1,37 @@
class Shared::SeeMoreLinkComponent < ApplicationComponent
attr_reader :record, :association_name, :limit
def initialize(record, association_name, limit: nil)
@record = record
@association_name = association_name
@limit = limit
end
private
def text
"#{count_out_of_limit}+"
end
def url
polymorphic_path(record)
end
def title
t("#{i18n_namespace}.filter.more", count: count_out_of_limit)
end
def count_out_of_limit
return 0 unless limit
record.send(association_name).size - limit
end
def i18n_namespace
association_name.to_s.tr("_", ".")
end
def html_class
"more-#{i18n_namespace.split(".").last}"
end
end

View File

@@ -0,0 +1 @@
<%= link_list(*links, class: "tags", id: "tags_#{dom_id(taggable)}") %>

View File

@@ -0,0 +1,37 @@
class Shared::TagListComponent < ApplicationComponent
attr_reader :taggable, :limit
delegate :link_list, to: :helpers
def initialize(taggable, limit:)
@taggable = taggable
@limit = limit
end
private
def links
[*tag_links, see_more_link]
end
def tag_links
taggable.tag_list_with_limit(limit).map do |tag|
[
sanitize(tag.name),
taggables_path(taggable, tag.name)
]
end
end
def see_more_link
render Shared::SeeMoreLinkComponent.new(taggable, :tags, limit: limit)
end
def taggables_path(taggable, tag_name)
case taggable.class.name
when "Legislation::Proposal"
legislation_process_proposals_path(taggable.process, search: tag_name)
else
polymorphic_path(taggable.class, search: tag_name)
end
end
end

View File

@@ -1,11 +1,15 @@
module LinkListHelper module LinkListHelper
def link_list(*links, **options) def link_list(*links, **options)
return "" if links.compact.empty? return "" if links.select(&:present?).empty?
tag.ul(options) do tag.ul(options) do
safe_join(links.compact.map do |text, url, current = false, **link_options| safe_join(links.select(&:present?).map do |text, url, current = false, **link_options|
tag.li(({ "aria-current": true } if current)) do tag.li(({ "aria-current": true } if current)) do
link_to text, url, link_options if url
link_to text, url, link_options
else
text
end
end end
end, "\n") end, "\n")
end end

View File

@@ -1,10 +0,0 @@
module TagsHelper
def taggables_path(taggable, tag_name)
case taggable.class.name
when "Legislation::Proposal"
legislation_process_proposals_path(taggable.process, search: tag_name)
else
polymorphic_path(taggable.class, search: tag_name)
end
end
end

View File

@@ -12,13 +12,6 @@ module Taggable
tags.sort { |a, b| b.taggings_count <=> a.taggings_count }[0, limit] tags.sort { |a, b| b.taggings_count <=> a.taggings_count }[0, limit]
end end
def tags_count_out_of_limit(limit = nil)
return 0 unless limit
count = tags.size - limit
count < 0 ? 0 : count
end
def max_number_of_tags def max_number_of_tags
errors.add(:tag_list, :less_than_or_equal_to, count: 6) if tag_list.count > 6 errors.add(:tag_list, :less_than_or_equal_to, count: 6) if tag_list.count > 6
end end

View File

@@ -13,4 +13,4 @@
</ul> </ul>
</div> </div>
<%= render SDG::Goals::FilterLinksComponent.new(taggable) %> <%= render SDG::Goals::TagCloudComponent.new(taggable) %>

View File

@@ -1,20 +1,4 @@
<%- limit ||= nil %> <%- limit ||= nil %>
<%= render SDG::TagListComponent.new(taggable, limit: limit) %> <%= render SDG::TagListComponent.new(taggable, limit: limit) %>
<%= render Shared::TagListComponent.new(taggable, limit: limit) %>
<% if taggable.tags.any? %>
<ul id="tags_<%= dom_id(taggable) %>" class="tags">
<% taggable.tag_list_with_limit(limit).each do |tag| %>
<li>
<%= link_to sanitize(tag.name),
taggables_path(taggable, tag.name) %></li>
<% end %>
<% if taggable.tags_count_out_of_limit(limit) > 0 %>
<li>
<%= link_to "#{taggable.tags_count_out_of_limit(limit)}+",
polymorphic_path(taggable) %>
</li>
<% end %>
</ul>
<% end %>

View File

@@ -221,6 +221,7 @@ ignore_unused:
- "sdg.goals.goal_*" - "sdg.goals.goal_*"
- "sdg.*.filter.more.*" - "sdg.*.filter.more.*"
- "sdg_management.relations.index.filter*" - "sdg_management.relations.index.filter*"
- "tags.filter.more.*"
#### ####
## Exclude these keys from the `i18n-tasks eq-base" report: ## Exclude these keys from the `i18n-tasks eq-base" report:
# ignore_eq_base: # ignore_eq_base:

View File

@@ -973,3 +973,8 @@ en:
create: create:
enqueue_remote_translation: Translations have been correctly requested. enqueue_remote_translation: Translations have been correctly requested.
button: Translate page button: Translate page
tags:
filter:
more:
one: "One more tag"
other: "%{count} more tags"

View File

@@ -973,3 +973,8 @@ es:
create: create:
enqueue_remote_translation: Se han solicitado correctamente las traducciones. enqueue_remote_translation: Se han solicitado correctamente las traducciones.
button: Traducir página button: Traducir página
tags:
filter:
more:
one: "Una etiqueta más"
other: "%{count} etiquetas más"

View File

@@ -1,25 +0,0 @@
require "rails_helper"
describe SDG::Goals::FilterLinksComponent, type: :component do
before do
Setting["feature.sdg"] = true
Setting["sdg.process.debates"] = true
Setting["sdg.process.proposals"] = true
end
it "renders a title" do
component = SDG::Goals::FilterLinksComponent.new("Debate")
render_inline component
expect(page).to have_content "Filters by SDG"
end
it "renders all goals" do
component = SDG::Goals::FilterLinksComponent.new("Proposal")
render_inline component
expect(page).to have_css ".sdg-goal-icon", count: 17
end
end

View File

@@ -0,0 +1,27 @@
require "rails_helper"
describe SDG::Goals::TagCloudComponent, type: :component do
before do
Setting["feature.sdg"] = true
Setting["sdg.process.debates"] = true
Setting["sdg.process.proposals"] = true
end
it "renders a title" do
component = SDG::Goals::TagCloudComponent.new("Debate")
render_inline component
expect(page).to have_content "Filters by SDG"
end
it "renders all goals ordered by code" do
component = SDG::Goals::TagCloudComponent.new("Proposal")
render_inline component
expect(page).to have_selector ".sdg-goal-icon", count: 17
expect(page.first("a")[:title]).to end_with "goal 1"
expect(page.all("a").last[:title]).to end_with "goal 17"
end
end

View File

@@ -60,16 +60,4 @@ describe SDG::Goals::TagListComponent, type: :component do
title: "One more goal", title: "One more goal",
href: "/debates/#{debate.to_param}" href: "/debates/#{debate.to_param}"
end end
context "given a class name" do
let(:component) { SDG::Goals::TagListComponent.new("Debate") }
it "renders all goals ordered by code" do
render_inline component
expect(page).to have_selector "li", count: 17
expect(page.first("a")[:title]).to end_with "goal 1"
expect(page.all("a").last[:title]).to end_with "goal 17"
end
end
end end

View File

@@ -19,6 +19,15 @@ describe LinkListHelper do
expect(list).to be_html_safe expect(list).to be_html_safe
end end
it "accepts anchor tags" do
list = helper.link_list(link_to("Home", "/"), ["Info", "/info"], class: "menu")
expect(list).to eq '<ul class="menu">' +
'<li><a href="/">Home</a></li>' + "\n" +
'<li><a href="/info">Info</a></li></ul>'
expect(list).to be_html_safe
end
it "accepts options for links" do it "accepts options for links" do
render helper.link_list(["Home", "/", class: "root"], ["Info", "/info", id: "info"]) render helper.link_list(["Home", "/", class: "root"], ["Info", "/info", id: "info"])
@@ -30,7 +39,15 @@ describe LinkListHelper do
it "ignores nil entries" do it "ignores nil entries" do
render helper.link_list(["Home", "/", class: "root"], nil, ["Info", "/info", id: "info"]) render helper.link_list(["Home", "/", class: "root"], nil, ["Info", "/info", id: "info"])
expect(page).to have_css "a", count: 2 expect(page).to have_css "li", count: 2
expect(page).to have_css "a.root", count: 1, exact_text: "Home"
expect(page).to have_css "a#info", count: 1, exact_text: "Info"
end
it "ignores empty entries" do
render helper.link_list(["Home", "/", class: "root"], "", ["Info", "/info", id: "info"])
expect(page).to have_css "li", count: 2
expect(page).to have_css "a.root", count: 1, exact_text: "Home" expect(page).to have_css "a.root", count: 1, exact_text: "Home"
expect(page).to have_css "a#info", count: 1, exact_text: "Info" expect(page).to have_css "a#info", count: 1, exact_text: "Info"
end end