Remove duplication in poll actions tables

We were using the same logic in four different places, so we're creating
a new class to handle that logic.

Note that I didn't find a way to delegate the `content` method to a
`Admin::TableActionsComponent`, so we're delegating the `action` method
instead. That means we need to create a method returning an
`Admin::TableActionsComponent`. We also need to cache this object;
otherwise we were getting an error when calling `actions.action` from
the `Admin::Poll::Questions::TableActionsComponent`.
This commit is contained in:
Javi Martín
2022-09-18 17:51:18 +02:00
parent 4c8f247de7
commit 518af3eb97
11 changed files with 91 additions and 40 deletions

View File

@@ -0,0 +1,3 @@
<%= render table_actions_component do %>
<%= content %>
<% end %>

View File

@@ -0,0 +1,20 @@
class Admin::AllowedTableActionsComponent < ApplicationComponent
attr_reader :record, :options
delegate :can?, to: :helpers
delegate :action, to: :table_actions_component
def initialize(record, **options)
@record = record
@options = options
end
private
def actions
(options[:actions] || [:edit, :destroy]).select { |action| can?(action, record) }
end
def table_actions_component
@table_actions_component ||= Admin::TableActionsComponent.new(record, **options.merge(actions: actions))
end
end

View File

@@ -1,9 +1,8 @@
<%= render Admin::TableActionsComponent.new(document, <%= render Admin::AllowedTableActionsComponent.new(document,
actions: actions, destroy_path: document_path(document)) do |actions| %>
destroy_path: document_path(document)) do |table_actions| %> <%= actions.action(:download,
<%= table_actions.action(:download, text: t("documents.buttons.download_document"),
text: t("documents.buttons.download_document"), path: document.attachment,
path: document.attachment, target: "_blank",
target: "_blank", rel: "nofollow") %>
rel: "nofollow") %>
<% end %> <% end %>

View File

@@ -1,14 +1,7 @@
class Admin::Poll::Questions::Answers::Documents::TableActionsComponent < ApplicationComponent class Admin::Poll::Questions::Answers::Documents::TableActionsComponent < ApplicationComponent
attr_reader :document attr_reader :document
delegate :can?, to: :helpers
def initialize(document) def initialize(document)
@document = document @document = document
end end
private
def actions
[:destroy].select { |action| can?(action, document) }
end
end end

View File

@@ -1 +1 @@
<%= render Admin::TableActionsComponent.new(answer, actions: actions) %> <%= render Admin::AllowedTableActionsComponent.new(answer) %>

View File

@@ -1,14 +1,7 @@
class Admin::Poll::Questions::Answers::TableActionsComponent < ApplicationComponent class Admin::Poll::Questions::Answers::TableActionsComponent < ApplicationComponent
attr_reader :answer attr_reader :answer
delegate :can?, to: :helpers
def initialize(answer) def initialize(answer)
@answer = answer @answer = answer
end end
private
def actions
[:edit, :destroy].select { |action| can?(action, answer) }
end
end end

View File

@@ -1 +1 @@
<%= render Admin::TableActionsComponent.new(video, actions: actions) %> <%= render Admin::AllowedTableActionsComponent.new(video) %>

View File

@@ -1,14 +1,7 @@
class Admin::Poll::Questions::Answers::Videos::TableActionsComponent < ApplicationComponent class Admin::Poll::Questions::Answers::Videos::TableActionsComponent < ApplicationComponent
attr_reader :video attr_reader :video
delegate :can?, to: :helpers
def initialize(video) def initialize(video)
@video = video @video = video
end end
private
def actions
[:edit, :destroy].select { |action| can?(action, video) }
end
end end

View File

@@ -1,3 +1,3 @@
<%= render Admin::TableActionsComponent.new(question, actions: actions) do |table_actions| %> <%= render Admin::AllowedTableActionsComponent.new(question) do |actions| %>
<%= table_actions.action(:answers, text: t("admin.polls.show.edit_answers")) %> <%= actions.action(:answers, text: t("admin.polls.show.edit_answers")) %>
<% end %> <% end %>

View File

@@ -1,14 +1,7 @@
class Admin::Poll::Questions::TableActionsComponent < ApplicationComponent class Admin::Poll::Questions::TableActionsComponent < ApplicationComponent
attr_reader :question attr_reader :question
delegate :can?, to: :helpers
def initialize(question) def initialize(question)
@question = question @question = question
end end
private
def actions
[:edit, :destroy].select { |action| can?(action, question) }
end
end end

View File

@@ -0,0 +1,57 @@
require "rails_helper"
describe Admin::AllowedTableActionsComponent, controller: Admin::BaseController do
before do
sign_in(create(:administrator).user)
allow_any_instance_of(Admin::AllowedTableActionsComponent).to receive(:can?).and_return true
end
let(:record) { create(:banner, title: "Important!") }
it "renders edit and destroy actions by default if they're allowed" do
component = Admin::AllowedTableActionsComponent.new(record)
render_inline component
expect(page).to have_link count: 1
expect(page).to have_link "Edit"
expect(page).to have_button count: 1
expect(page).to have_button "Delete"
end
it "accepts an actions parameter" do
render_inline Admin::AllowedTableActionsComponent.new(record, actions: [:edit])
expect(page).to have_link "Edit"
expect(page).not_to have_button "Delete"
end
it "accepts custom options" do
render_inline Admin::AllowedTableActionsComponent.new(record, edit_text: "change", edit_path: "/myedit")
expect(page).to have_link "change", href: "/myedit"
end
it "accepts custom content" do
render_inline Admin::AllowedTableActionsComponent.new(record) do
"<a href='/'>Main</a>".html_safe
end
expect(page).to have_link count: 2
expect(page).to have_link "Main", href: "/"
expect(page).to have_link "Edit"
expect(page).to have_button count: 1
expect(page).to have_button "Delete"
end
it "only renders the allowed actions" do
component = Admin::AllowedTableActionsComponent.new(record)
allow(component).to receive(:can?).with(:edit, record).and_return true
allow(component).to receive(:can?).with(:destroy, record).and_return false
render_inline component
expect(page).to have_link "Edit"
expect(page).not_to have_button "Delete"
end
end