Extract method to render an admin table action

This way it will be easier to change the behavior of all table actions,
like adding ARIA attributes. In the past, when we changed the behavior
of the `link_to` method, we had to change all table action classes.
This commit is contained in:
Javi Martín
2021-08-17 22:21:38 +02:00
parent d7015792ea
commit 6a2c01b119
35 changed files with 184 additions and 134 deletions

View File

@@ -0,0 +1 @@
<%= link_to text, path, html_options %>

View File

@@ -0,0 +1,47 @@
class Admin::ActionComponent < ApplicationComponent
include Admin::Namespace
attr_reader :action, :record, :options
def initialize(action, record, **options)
@action = action
@record = record
@options = options
end
private
def text
options[:text] || t("admin.actions.#{action}")
end
def path
options[:path] || default_path
end
def html_options
{
class: html_class,
data: { confirm: confirmation_text }
}.merge(options.except(:confirm, :path, :text))
end
def html_class
"#{action.to_s.gsub("_", "-")}-link"
end
def confirmation_text
if options[:confirm] == true
t("admin.actions.confirm")
else
options[:confirm]
end
end
def default_path
if %i[answers configure destroy preview show].include?(action.to_sym)
namespaced_polymorphic_path(namespace, record)
else
namespaced_polymorphic_path(namespace, record, { action: action }.merge(request.query_parameters))
end
end
end

View File

@@ -17,9 +17,9 @@
<td><%= group.headings.count %></td> <td><%= group.headings.count %></td>
<td> <td>
<%= render Admin::TableActionsComponent.new(group) do |actions| %> <%= render Admin::TableActionsComponent.new(group) do |actions| %>
<%= actions.link_to t("admin.budget_groups.headings_manage"), <%= actions.action(:headings,
headings_path(actions, group), text: t("admin.budget_groups.headings_manage"),
class: "headings-link" %> path: headings_path(group)) %>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -1,4 +1,5 @@
class Admin::BudgetGroups::GroupsComponent < ApplicationComponent class Admin::BudgetGroups::GroupsComponent < ApplicationComponent
include Admin::Namespace
attr_reader :groups attr_reader :groups
def initialize(groups) def initialize(groups)
@@ -11,7 +12,7 @@ class Admin::BudgetGroups::GroupsComponent < ApplicationComponent
@budget ||= groups.first.budget @budget ||= groups.first.budget
end end
def headings_path(table_actions_component, group) def headings_path(group)
send("#{table_actions_component.namespace}_budget_group_headings_path", group.budget, group) send("#{namespace}_budget_group_headings_path", group.budget, group)
end end
end end

View File

@@ -1,22 +1,25 @@
<%= render Admin::TableActionsComponent.new(budget, <%= render Admin::TableActionsComponent.new(budget,
destroy_confirmation: t("admin.actions.confirm_delete", resource_name: t("admin.budgets.shared.resource_name"), destroy_confirmation: t("admin.actions.confirm_delete", resource_name: t("admin.budgets.shared.resource_name"),
name: budget.name) name: budget.name)
) do %> ) do |actions| %>
<%= link_to t("admin.budgets.index.budget_investments"), <%= actions.action(:investments,
admin_budget_budget_investments_path(budget_id: budget.id), text: t("admin.budgets.index.budget_investments"),
class: "investments-link" %> path: admin_budget_budget_investments_path(budget_id: budget.id)) %>
<%= link_to t("admin.budgets.index.edit_groups"), <%= actions.action(:groups,
admin_budget_groups_path(budget), text: t("admin.budgets.index.edit_groups"),
class: "groups-link" %> path: admin_budget_groups_path(budget)) %>
<% if budget.poll.present? %> <% if budget.poll.present? %>
<%= link_to t("admin.budgets.index.admin_ballots"), <%= actions.action(:ballots,
admin_poll_booth_assignments_path(budget.poll), text: t("admin.budgets.index.admin_ballots"),
class: "ballots-link" %> path: admin_poll_booth_assignments_path(budget.poll)) %>
<% else %> <% else %>
<%= link_to_create_budget_poll %> <%= actions.action(:ballots,
text: t("admin.budgets.index.admin_ballots"),
path: create_budget_poll_path,
method: :post) %>
<% end %> <% end %>
<%= link_to t("admin.budgets.actions.preview"), <%= actions.action(:preview,
budget_path(budget), text: t("admin.budgets.actions.preview"),
target: "_blank", path: budget_path(budget),
class: "preview-link" %> target: "_blank") %>
<% end %> <% end %>

View File

@@ -7,17 +7,14 @@ class Admin::Budgets::TableActionsComponent < ApplicationComponent
private private
def link_to_create_budget_poll def create_budget_poll_path
balloting_phase = budget.phases.find_by(kind: "balloting") balloting_phase = budget.phases.find_by(kind: "balloting")
link_to t("admin.budgets.index.admin_ballots"), admin_polls_path(poll: {
admin_polls_path(poll: { name: budget.name,
name: budget.name, budget_id: budget.id,
budget_id: budget.id, starts_at: balloting_phase.starts_at,
starts_at: balloting_phase.starts_at, ends_at: balloting_phase.ends_at
ends_at: balloting_phase.ends_at })
}),
class: "ballots-link",
method: :post
end end
end end

View File

@@ -1,12 +1,14 @@
<%= render Admin::TableActionsComponent.new(actions: []) do %> <%= render Admin::TableActionsComponent.new(record, actions: []) do |actions| %>
<%= link_to restore_text, restore_path, <%= actions.action(:restore,
method: :put, text: restore_text,
data: { confirm: t("admin.actions.confirm") }, path: restore_path,
class: "restore-link" %> method: :put,
confirm: true) %>
<% unless record.confirmed_hide? %> <% unless record.confirmed_hide? %>
<%= link_to confirm_hide_text, confirm_hide_path, <%= actions.action(:confirm_hide,
method: :put, text: confirm_hide_text,
class: "confirm-hide-link" %> path: confirm_hide_path,
method: :put) %>
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -1,13 +1,13 @@
<%= render Admin::TableActionsComponent.new(actions: []) do %> <%= render Admin::TableActionsComponent.new(organization, actions: []) do |actions| %>
<% if can_verify? %> <% if can_verify? %>
<%= link_to t("admin.organizations.index.verify"), <%= actions.action(:verify,
verify_admin_organization_path(organization, request.query_parameters), text: t("admin.organizations.index.verify"),
method: :put, class: "verify-link" %> method: :put) %>
<% end %> <% end %>
<% if can_reject? %> <% if can_reject? %>
<%= link_to t("admin.organizations.index.reject"), <%= actions.action(:reject,
reject_admin_organization_path(organization, request.query_parameters), text: t("admin.organizations.index.reject"),
method: :put, class: "reject-link" %> method: :put) %>
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -23,11 +23,11 @@
destroy_options: { class: "destroy-officer-link" } destroy_options: { class: "destroy-officer-link" }
) %> ) %>
<% else %> <% else %>
<%= render Admin::TableActionsComponent.new(actions: []) do |actions| %> <%= render Admin::TableActionsComponent.new(officer, actions: []) do |actions| %>
<%= actions.link_to t("admin.poll_officers.officer.add"), <%= actions.action(:create_officer,
add_user_path(officer), text: t("admin.poll_officers.officer.add"),
method: :post, path: add_user_path(officer),
class: "create-officer-link" %> method: :post) %>
<% end %> <% end %>
<% end %> <% end %>
</td> </td>

View File

@@ -4,7 +4,7 @@
destroy_options: { class: "destroy-role-link" } destroy_options: { class: "destroy-role-link" }
) %> ) %>
<% else %> <% else %>
<%= render Admin::TableActionsComponent.new(actions: []) do %> <%= render Admin::TableActionsComponent.new(record, actions: []) do |actions| %>
<%= link_to add_user_text, add_user_path, method: :post, class: "create-role-link" %> <%= actions.action(:create_role, text: add_user_text, path: add_user_path, method: :post) %>
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -2,10 +2,10 @@
<%= content %> <%= content %>
<% if actions.include?(:edit) %> <% if actions.include?(:edit) %>
<%= link_to edit_text, edit_path, edit_options %> <%= action(:edit, edit_options.merge(text: edit_text, path: edit_path)) %>
<% end %> <% end %>
<% if actions.include?(:destroy) %> <% if actions.include?(:destroy) %>
<%= link_to destroy_text, destroy_path, destroy_options %> <%= action(:destroy, destroy_options.merge(text: destroy_text, path: destroy_path)) %>
<% end %> <% end %>
</div> </div>

View File

@@ -1,12 +1,15 @@
class Admin::TableActionsComponent < ApplicationComponent class Admin::TableActionsComponent < ApplicationComponent
include Admin::Namespace
attr_reader :record, :options attr_reader :record, :options
def initialize(record = nil, **options) def initialize(record, **options)
@record = record @record = record
@options = options @options = options
end end
def action(action_name, **args)
render Admin::ActionComponent.new(action_name, record, **args)
end
private private
def actions def actions
@@ -14,15 +17,15 @@ class Admin::TableActionsComponent < ApplicationComponent
end end
def edit_text def edit_text
options[:edit_text] || t("admin.actions.edit") options[:edit_text]
end end
def edit_path def edit_path
options[:edit_path] || namespaced_polymorphic_path(namespace, record, action: :edit) options[:edit_path]
end end
def edit_options def edit_options
{ class: "edit-link" }.merge(options[:edit_options] || {}) options[:edit_options] || {}
end end
def destroy_text def destroy_text
@@ -30,18 +33,13 @@ class Admin::TableActionsComponent < ApplicationComponent
end end
def destroy_path def destroy_path
options[:destroy_path] || namespaced_polymorphic_path(namespace, record) options[:destroy_path]
end end
def destroy_options def destroy_options
{ {
method: :delete, method: :delete,
class: "destroy-link", confirm: options[:destroy_confirmation] || true
data: { confirm: destroy_confirmation }
}.merge(options[:destroy_options] || {}) }.merge(options[:destroy_options] || {})
end end
def destroy_confirmation
options[:destroy_confirmation] || t("admin.actions.confirm")
end
end end

View File

@@ -31,15 +31,11 @@
<td> <td>
<% if admin_notification.draft? %> <% if admin_notification.draft? %>
<%= render Admin::TableActionsComponent.new(admin_notification) do |actions| %> <%= render Admin::TableActionsComponent.new(admin_notification) do |actions| %>
<%= actions.link_to t("admin.admin_notifications.index.preview"), <%= actions.action(:preview, text: t("admin.admin_notifications.index.preview")) %>
admin_admin_notification_path(admin_notification),
class: "preview-link" %>
<% end %> <% end %>
<% else %> <% else %>
<%= render Admin::TableActionsComponent.new(actions: []) do |actions| %> <%= render Admin::TableActionsComponent.new(admin_notification, actions: []) do |actions| %>
<%= actions.link_to t("admin.admin_notifications.index.view"), <%= actions.action(:show, text: t("admin.admin_notifications.index.view")) %>
admin_admin_notification_path(admin_notification),
class: "show-link" %>
<% end %> <% end %>
<% end %> <% end %>
</td> </td>

View File

@@ -34,10 +34,8 @@
<%= audit.user&.name %> <%= audit.user&.name %>
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new(actions: []) do |actions| %> <%= render Admin::TableActionsComponent.new(audit, actions: []) do |actions| %>
<%= actions.link_to t("shared.show"), <%= actions.action(:show, text: t("shared.show")) %>
admin_polymorphic_path(audit),
class: "show-link" %>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -6,6 +6,7 @@
<td colspan="4">&nbsp;</td> <td colspan="4">&nbsp;</td>
<td> <td>
<%= render Admin::TableActionsComponent.new( <%= render Admin::TableActionsComponent.new(
action,
actions: [:edit], actions: [:edit],
edit_path: admin_settings_path(anchor: "tab-proposals"), edit_path: admin_settings_path(anchor: "tab-proposals"),
) %> ) %>

View File

@@ -30,9 +30,7 @@
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new(newsletter) do |actions| %> <%= render Admin::TableActionsComponent.new(newsletter) do |actions| %>
<%= actions.link_to t("admin.newsletters.index.preview"), <%= actions.action :preview, text: t("admin.newsletters.index.preview") %>
admin_newsletter_path(newsletter),
class: "preview-link" %>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -27,6 +27,7 @@
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new( <%= render Admin::TableActionsComponent.new(
official,
actions: [:edit], actions: [:edit],
edit_path: edit_admin_official_path(official) edit_path: edit_admin_official_path(official)
) %> ) %>

View File

@@ -33,6 +33,7 @@
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new( <%= render Admin::TableActionsComponent.new(
user,
actions: [:edit], actions: [:edit],
edit_path: edit_admin_official_path(user), edit_path: edit_admin_official_path(user),
edit_text: (t("admin.officials.search.make_official") unless user.official?) edit_text: (t("admin.officials.search.make_official") unless user.official?)

View File

@@ -26,12 +26,12 @@
</td> </td>
<td> <td>
<% unless @poll.expired? %> <% unless @poll.expired? %>
<%= render Admin::TableActionsComponent.new(actions: []) do |actions| %> <%= render Admin::TableActionsComponent.new(booth, actions: []) do |actions| %>
<%= actions.link_to t("admin.booth_assignments.manage.actions.assign"), <%= actions.action(:assign_booth,
admin_poll_booth_assignments_path(@poll, booth_id: booth.id), text: t("admin.booth_assignments.manage.actions.assign"),
method: :post, path: admin_poll_booth_assignments_path(@poll, booth_id: booth.id),
remote: true, method: :post,
class: "assign-booth-link" %> remote: true) %>
<% end %> <% end %>
<% end %> <% end %>
</td> </td>

View File

@@ -7,10 +7,10 @@
</td> </td>
<td> <td>
<% if controller_name == "shifts" || controller_name == "booths" && action_name == "available" %> <% if controller_name == "shifts" || controller_name == "booths" && action_name == "available" %>
<%= render Admin::TableActionsComponent.new(actions: []) do |actions| %> <%= render Admin::TableActionsComponent.new(booth, actions: []) do |actions| %>
<%= actions.link_to t("admin.booths.booth.shifts"), <%= actions.action(:shifts,
new_admin_booth_shift_path(booth), text: t("admin.booths.booth.shifts"),
class: "shifts-link" %> path: new_admin_booth_shift_path(booth)) %>
<% end %> <% end %>
<% else %> <% else %>
<%= render Admin::TableActionsComponent.new(booth, actions: [:edit]) %> <%= render Admin::TableActionsComponent.new(booth, actions: [:edit]) %>

View File

@@ -10,9 +10,7 @@
<%= render Admin::TableActionsComponent.new(poll, <%= render Admin::TableActionsComponent.new(poll,
destroy_confirmation: t("admin.polls.destroy.alert") destroy_confirmation: t("admin.polls.destroy.alert")
) do |actions| %> ) do |actions| %>
<%= actions.link_to t("admin.actions.configure"), <%= actions.action(:configure) %>
admin_poll_path(poll),
class: "configure-link" %>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -29,8 +29,7 @@
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new(question) do |actions| %> <%= render Admin::TableActionsComponent.new(question) do |actions| %>
<%= actions.link_to t("admin.polls.show.edit_answers"), admin_question_path(question), <%= actions.action(:answers, text: t("admin.polls.show.edit_answers")) %>
class: "answers-link" %>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -15,10 +15,10 @@
<%= l poll.starts_at.to_date %> - <%= l poll.ends_at.to_date %> <%= l poll.starts_at.to_date %> - <%= l poll.ends_at.to_date %>
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new(actions: []) do |actions| %> <%= render Admin::TableActionsComponent.new(poll, actions: []) do |actions| %>
<%= actions.link_to t("admin.booth_assignments.manage_assignments"), <%= actions.action(:manage,
manage_admin_poll_booth_assignments_path(poll), text: t("admin.booth_assignments.manage_assignments"),
class: "manage-link" %> path: manage_admin_poll_booth_assignments_path(poll)) %>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -26,8 +26,7 @@
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new(question) do |actions| %> <%= render Admin::TableActionsComponent.new(question) do |actions| %>
<%= actions.link_to t("admin.polls.show.edit_answers"), admin_question_path(question), <%= actions.action(:answers, text: t("admin.polls.show.edit_answers")) %>
class: "answers-link" %>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -15,11 +15,11 @@
</p> </p>
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new(actions: []) do |actions| %> <%= render Admin::TableActionsComponent.new(proposal, actions: []) do |actions| %>
<%= actions.link_to t("admin.shared.view"), proposal_path(proposal), class: "show-link" %> <%= actions.action(:show, text: t("admin.shared.view"), path: proposal_path(proposal)) %>
<%= actions.link_to t("admin.questions.index.create_question"), <%= actions.action(:new,
new_admin_question_path(proposal_id: proposal.id), text: t("admin.questions.index.create_question"),
class: "new-link" %> path: new_admin_question_path(proposal_id: proposal.id)) %>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -40,11 +40,11 @@
actions: [:destroy], actions: [:destroy],
destroy_path: document_path(document) destroy_path: document_path(document)
) do |actions| %> ) do |actions| %>
<%= actions.link_to t("documents.buttons.download_document"), <%= actions.action(:download,
document.attachment.url, text: t("documents.buttons.download_document"),
target: "_blank", path: document.attachment.url,
rel: "nofollow", target: "_blank",
class: "download-link" %> rel: "nofollow") %>
<% end %> <% end %>
</td> </td>

View File

@@ -24,6 +24,7 @@
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new( <%= render Admin::TableActionsComponent.new(
user,
actions: [:edit], actions: [:edit],
edit_text: t("admin.poll_shifts.new.edit_shifts"), edit_text: t("admin.poll_shifts.new.edit_shifts"),
edit_path: new_admin_booth_shift_path(officer_id: user.poll_officer.id) edit_path: new_admin_booth_shift_path(officer_id: user.poll_officer.id)

View File

@@ -44,6 +44,7 @@
<td><%= raw content_block.body %></td> <td><%= raw content_block.body %></td>
<td> <td>
<%= render Admin::TableActionsComponent.new( <%= render Admin::TableActionsComponent.new(
content_block,
actions: [:destroy], actions: [:destroy],
destroy_path: admin_site_customization_delete_heading_content_block_path(content_block) destroy_path: admin_site_customization_delete_heading_content_block_path(content_block)
) %> ) %>

View File

@@ -27,6 +27,7 @@
<td> <td>
<div class="small-12 medium-6 column"> <div class="small-12 medium-6 column">
<%= render Admin::TableActionsComponent.new( <%= render Admin::TableActionsComponent.new(
document,
actions: [:destroy], actions: [:destroy],
destroy_path: admin_site_customization_document_path(document) destroy_path: admin_site_customization_document_path(document)
) %> ) %>

View File

@@ -29,15 +29,15 @@
<td><%= t("admin.site_customization.pages.page.status_#{page.status}") %></td> <td><%= t("admin.site_customization.pages.page.status_#{page.status}") %></td>
<td> <td>
<%= render Admin::TableActionsComponent.new(page) do |actions| %> <%= render Admin::TableActionsComponent.new(page) do |actions| %>
<%= actions.link_to t("admin.site_customization.pages.page.see_cards"), <%= actions.action(:cards,
admin_site_customization_page_widget_cards_path(page), text: t("admin.site_customization.pages.page.see_cards"),
class: "cards-link" %> path: admin_site_customization_page_widget_cards_path(page)) %>
<% if page.status == "published" %> <% if page.status == "published" %>
<%= actions.link_to t("admin.site_customization.pages.index.see_page"), <%= actions.action(:show,
page.url, text: t("admin.site_customization.pages.index.see_page"),
target: "_blank", path: page.url,
class: "show-link" %> options: { target: "_blank" }) %>
<% end %> <% end %>
<% end %> <% end %>
</td> </td>

View File

@@ -18,21 +18,21 @@
<%= t("admin.system_emails.#{system_email_title}.description") %> <%= t("admin.system_emails.#{system_email_title}.description") %>
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new(actions: []) do |actions| %> <%= render Admin::TableActionsComponent.new(system_email_title, actions: []) do |actions| %>
<% if system_email_actions.include?("view") %> <% if system_email_actions.include?("view") %>
<%= actions.link_to t("admin.shared.view"), <%= actions.action(:show,
admin_system_email_view_path(system_email_title), text: t("admin.shared.view"),
class: "show-link" %> path: admin_system_email_view_path(system_email_title)) %>
<% end %> <% end %>
<% if system_email_actions.include?("preview_pending") %> <% if system_email_actions.include?("preview_pending") %>
<%= actions.link_to t("admin.system_emails.preview_pending.action"), <%= actions.action(:preview_pending,
admin_system_email_preview_pending_path(system_email_title), text: t("admin.system_emails.preview_pending.action"),
class: "preview-pending-link" %> path: admin_system_email_preview_pending_path(system_email_title)) %>
<%= actions.link_to t("admin.system_emails.preview_pending.send_pending"), <%= actions.action(:send_pending,
admin_system_email_send_pending_path(system_email_title), text: t("admin.system_emails.preview_pending.send_pending"),
class: "send-pending-link", path: admin_system_email_send_pending_path(system_email_title),
method: :put %> method: :put) %>
<% end %> <% end %>
<% if system_email_actions.include?("edit_info") %> <% if system_email_actions.include?("edit_info") %>

View File

@@ -20,9 +20,7 @@
</td> </td>
<td> <td>
<%= render Admin::TableActionsComponent.new(valuator) do |actions| %> <%= render Admin::TableActionsComponent.new(valuator) do |actions| %>
<%= actions.link_to t("admin.shared.view"), <%= actions.action(:show, text: t("admin.shared.view")) %>
admin_valuator_path(valuator),
class: "show-link" %>
<% end %> <% end %>
</td> </td>
</tr> </tr>

View File

@@ -0,0 +1,9 @@
require "rails_helper"
describe Admin::ActionComponent do
it "includes an HTML class for the action" do
render_inline Admin::ActionComponent.new(:edit, double, path: "/")
expect(page).to have_css "a.edit-link"
end
end

View File

@@ -1,6 +1,6 @@
require "rails_helper" require "rails_helper"
describe Admin::Organizations::TableActionsComponent do describe Admin::Organizations::TableActionsComponent, controller: Admin::OrganizationsController do
let(:organization) { create(:organization) } let(:organization) { create(:organization) }
let(:component) { Admin::Organizations::TableActionsComponent.new(organization) } let(:component) { Admin::Organizations::TableActionsComponent.new(organization) }

View File

@@ -37,7 +37,7 @@ describe Admin::TableActionsComponent, controller: Admin::BaseController do
end end
it "allows custom URLs" do it "allows custom URLs" do
render_inline Admin::TableActionsComponent.new(edit_path: "/myedit", destroy_path: "/mydestroy") render_inline Admin::TableActionsComponent.new(nil, edit_path: "/myedit", destroy_path: "/mydestroy")
expect(page).to have_link "Edit", href: "/myedit" expect(page).to have_link "Edit", href: "/myedit"
expect(page).to have_link "Delete", href: "/mydestroy" expect(page).to have_link "Delete", href: "/mydestroy"