Disable buttons in table actions when pressed

By default, Rails disables submit inputs (<input type="submit">) when
they're pressed so we avoid a double-submission when users click the
button twice.

However, Rails does not disable submit buttons (<button type="submit">)
when they're pressed. This means there's a chance users might press the
button several times. Even if most our table actions are idempotent, it
might cause certain issues. For instance, pressing the "Delete" button
twice means the second request might raise an
`ActiveRecord::RecordNotFound` exception.

Disabling the button also gives feedback to users, letting them know
they've correctly clicked the button.
This commit is contained in:
Javi Martín
2021-08-20 03:21:54 +02:00
parent 5311daadfe
commit aaa5f6c285
4 changed files with 43 additions and 7 deletions

View File

@@ -0,0 +1,6 @@
.admin .admin-action {
&[disabled] {
@include button-disabled;
}
}

View File

@@ -1,4 +1,4 @@
<% if options[:method] && options[:method] != :get %>
<% if button? %>
<%= button_to(path, html_options) { text } %>
<% else %>
<%= link_to text, path, html_options %>

View File

@@ -10,6 +10,10 @@ class Admin::ActionComponent < ApplicationComponent
private
def button?
options[:method] && options[:method] != :get
end
def text
options[:text] || t("admin.actions.#{action}")
end
@@ -22,12 +26,15 @@ class Admin::ActionComponent < ApplicationComponent
{
class: html_class,
"aria-label": label,
data: { confirm: confirmation_text }
}.merge(options.except(:"aria-label", :confirm, :path, :text))
data: {
confirm: confirmation_text,
disable_with: (text if button?)
}
}.merge(options.except(:"aria-label", :class, :confirm, :path, :text))
end
def html_class
"#{action.to_s.gsub("_", "-")}-link"
"admin-action #{options[:class] || "#{action.to_s.gsub("_", "-")}-link"}".strip
end
def label

View File

@@ -1,10 +1,19 @@
require "rails_helper"
describe Admin::ActionComponent do
it "includes an HTML class for the action" do
render_inline Admin::ActionComponent.new(:edit, double, path: "/")
describe "HTML class" do
it "includes an HTML class for the action by default" do
render_inline Admin::ActionComponent.new(:edit, double, path: "/")
expect(page).to have_css "a.edit-link"
expect(page).to have_css "a.edit-link.admin-action"
end
it "keeps the admin-action class when the class is overwritten" do
render_inline Admin::ActionComponent.new(:edit, double, path: "/", class: "modify-link")
expect(page).to have_css "a.modify-link.admin-action"
expect(page).not_to have_css ".edit-link"
end
end
describe "aria-label attribute" do
@@ -105,4 +114,18 @@ describe Admin::ActionComponent do
end
end
end
describe "data-disable-with attribute" do
it "is not rendered for links" do
render_inline Admin::ActionComponent.new(:edit, double, path: "/")
expect(page).not_to have_css "[data-disable-with]"
end
it "is rendered for buttons" do
render_inline Admin::ActionComponent.new(:hide, double, path: "/", method: :delete)
expect(page).to have_css "button[data-disable-with='Hide']"
end
end
end