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>
<%= render Admin::TableActionsComponent.new(group) do |actions| %>
<%= actions.link_to t("admin.budget_groups.headings_manage"),
headings_path(actions, group),
class: "headings-link" %>
<%= actions.action(:headings,
text: t("admin.budget_groups.headings_manage"),
path: headings_path(group)) %>
<% end %>
</td>
</tr>

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,14 @@
<%= render Admin::TableActionsComponent.new(actions: []) do %>
<%= link_to restore_text, restore_path,
<%= render Admin::TableActionsComponent.new(record, actions: []) do |actions| %>
<%= actions.action(:restore,
text: restore_text,
path: restore_path,
method: :put,
data: { confirm: t("admin.actions.confirm") },
class: "restore-link" %>
confirm: true) %>
<% unless record.confirmed_hide? %>
<%= link_to confirm_hide_text, confirm_hide_path,
method: :put,
class: "confirm-hide-link" %>
<%= actions.action(:confirm_hide,
text: confirm_hide_text,
path: confirm_hide_path,
method: :put) %>
<% 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? %>
<%= link_to t("admin.organizations.index.verify"),
verify_admin_organization_path(organization, request.query_parameters),
method: :put, class: "verify-link" %>
<%= actions.action(:verify,
text: t("admin.organizations.index.verify"),
method: :put) %>
<% end %>
<% if can_reject? %>
<%= link_to t("admin.organizations.index.reject"),
reject_admin_organization_path(organization, request.query_parameters),
method: :put, class: "reject-link" %>
<%= actions.action(:reject,
text: t("admin.organizations.index.reject"),
method: :put) %>
<% end %>
<% end %>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,6 +24,7 @@
</td>
<td>
<%= render Admin::TableActionsComponent.new(
user,
actions: [:edit],
edit_text: t("admin.poll_shifts.new.edit_shifts"),
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>
<%= render Admin::TableActionsComponent.new(
content_block,
actions: [:destroy],
destroy_path: admin_site_customization_delete_heading_content_block_path(content_block)
) %>

View File

@@ -27,6 +27,7 @@
<td>
<div class="small-12 medium-6 column">
<%= render Admin::TableActionsComponent.new(
document,
actions: [:destroy],
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>
<%= render Admin::TableActionsComponent.new(page) do |actions| %>
<%= actions.link_to t("admin.site_customization.pages.page.see_cards"),
admin_site_customization_page_widget_cards_path(page),
class: "cards-link" %>
<%= actions.action(:cards,
text: t("admin.site_customization.pages.page.see_cards"),
path: admin_site_customization_page_widget_cards_path(page)) %>
<% if page.status == "published" %>
<%= actions.link_to t("admin.site_customization.pages.index.see_page"),
page.url,
target: "_blank",
class: "show-link" %>
<%= actions.action(:show,
text: t("admin.site_customization.pages.index.see_page"),
path: page.url,
options: { target: "_blank" }) %>
<% end %>
<% end %>
</td>

View File

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

View File

@@ -20,9 +20,7 @@
</td>
<td>
<%= render Admin::TableActionsComponent.new(valuator) do |actions| %>
<%= actions.link_to t("admin.shared.view"),
admin_valuator_path(valuator),
class: "show-link" %>
<%= actions.action(:show, text: t("admin.shared.view")) %>
<% end %>
</td>
</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"
describe Admin::Organizations::TableActionsComponent do
describe Admin::Organizations::TableActionsComponent, controller: Admin::OrganizationsController do
let(:organization) { create(:organization) }
let(:component) { Admin::Organizations::TableActionsComponent.new(organization) }

View File

@@ -37,7 +37,7 @@ describe Admin::TableActionsComponent, controller: Admin::BaseController do
end
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 "Delete", href: "/mydestroy"