Skip to content

Commit

Permalink
Unsafe Actions: Replace link_to calls with button_to (#2516)
Browse files Browse the repository at this point in the history
The `<a>` element is suitable for [Safe HTTP Methods][] (like `GET`) to
drive page navigations. Unsafe HTTP Methods (like `POST`, `PUT`, and
`DELETE`) are better initiated by `<form>` submissions.

This commit replaces generated calls to `link_to` with calls to
`button_to`.

The rest of the styling changes aim to preserve design decisions made
about preserving the appearance of elements that were once presented as
`<a>` elements that are now presented as `<input type="submit">`
elements nested within `<form>` elements.

While the design changes preserved backwards compatibility, it's worth
re-considering the choice to present them as "navigation" links instead
of "action" buttons.

[Safe HTTP Methods]: https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP
  • Loading branch information
seanpdoyle authored Feb 15, 2024
1 parent 21602c3 commit 074cb1a
Show file tree
Hide file tree
Showing 14 changed files with 133 additions and 60 deletions.
83 changes: 54 additions & 29 deletions app/assets/builds/administrate/application.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/assets/builds/administrate/application.css.map

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions app/assets/stylesheets/administrate/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,3 @@
@import "components/navigation";
@import "components/pagination";
@import "components/search";

@import "utilities/text-color";
5 changes: 5 additions & 0 deletions app/assets/stylesheets/administrate/base/_layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ figure {
margin: 0;
}

/* stylelint-disable selector-no-qualifying-type, selector-class-pattern */
form.button_to { // we don't control this class name
display: contents;
}

img,
picture {
margin: 0;
Expand Down
13 changes: 12 additions & 1 deletion app/assets/stylesheets/administrate/base/_typography.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,22 @@ p {
margin: 0 0 $small-spacing;
}

a {
a,
.link:is(
button,
[type="button"],
[type="reset"],
[type="submit"]
) {
color: $action-color;
text-decoration-skip-ink: auto;
transition: color $base-duration $base-timing;

/* stylelint-disable selector-no-qualifying-type */
&.link--danger {
color: $red;
}

&:hover {
color: mix($black, $action-color, 25%);
}
Expand Down
18 changes: 9 additions & 9 deletions app/assets/stylesheets/administrate/components/_buttons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ button,
vertical-align: middle;
white-space: nowrap;

&:hover {
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}

&:not(.link):hover {
background-color: mix($black, $action-color, 20%);
color: $white;
}

&:focus {
&:not(.link):focus {
outline: $focus-outline;
outline-offset: $focus-outline-offset;
}

&:disabled {
cursor: not-allowed;
opacity: 0.5;

&:hover {
background-color: $action-color;
}
&:not(.link):disabled:hover {
background-color: $action-color;
}
}

Expand Down
8 changes: 7 additions & 1 deletion app/assets/stylesheets/administrate/reset/_normalize.scss
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ pre {
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/

a {
a,
.link:is(
button,
[type="button"],
[type="reset"],
[type="submit"]
) {
background-color: transparent; /* 1 */
-webkit-text-decoration-skip: objects; /* 2 */
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
<% end %>

<% if existing_action?(collection_presenter.resource_name, :destroy) %>
<td><%= link_to(
<td><%= button_to(
t("administrate.actions.destroy"),
[namespace, resource],
class: "text-color-red",
data: { turbo_method: :delete, turbo_confirm: t("administrate.actions.confirm") }
class: "link link--danger",
method: :delete,
data: { turbo_confirm: t("administrate.actions.confirm") }
) if accessible_action?(resource, :destroy) %></td>
<% end %>
5 changes: 3 additions & 2 deletions app/views/administrate/application/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ as well as a link to its edit page.
class: "button",
) if accessible_action?(page.resource, :edit) %>

<%= link_to(
<%= button_to(
t("administrate.actions.destroy"),
[namespace, page.resource],
class: "button button--danger",
data: { turbo_method: :delete, turbo_confirm: t("administrate.actions.confirm") }
method: :delete,
data: { turbo_confirm: t("administrate.actions.confirm") }
) if accessible_action?(page.resource, :destroy) %>
</div>
</header>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
<% end %>

<% if existing_action?(collection_presenter.resource_name, :destroy) %>
<td><%= link_to(
<td><%= button_to(
t("administrate.actions.destroy"),
[namespace, resource],
class: "text-color-red",
class: "link link--danger",
method: :delete,
data: { turbo_confirm: t("administrate.actions.confirm") }
) if authorized_action?(resource, :destroy) %></td>
Expand Down
12 changes: 10 additions & 2 deletions spec/features/index_page_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,17 @@
visit admin_customers_path

within(".main-content") { click_on "Orders" }
expect(page).to have_content(/Cam.*1 order.*Ade.*2 orders.*Ben.*3 orders/)
within :table do
expect(page).to have_css("tbody tr:nth-child(1)", text: "Cam").and(have_text("1 order"))
expect(page).to have_css("tbody tr:nth-child(2)", text: "Ade").and(have_text("2 orders"))
expect(page).to have_css("tbody tr:nth-child(3)", text: "Ben").and(have_text("3 orders"))
end

within(".main-content") { click_on "Orders" }
expect(page).to have_content(/Ben.*3 orders.*Ade.*2 orders.*Cam.*1 order/)
within :table do
expect(page).to have_css("tbody tr:nth-child(1)", text: "Ben").and(have_text("3 orders"))
expect(page).to have_css("tbody tr:nth-child(2)", text: "Ade").and(have_text("2 orders"))
expect(page).to have_css("tbody tr:nth-child(3)", text: "Cam").and(have_text("1 order"))
end
end
end
12 changes: 10 additions & 2 deletions spec/features/orders_index_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,17 @@
visit admin_orders_path

click_on "Customer"
expect(page).to have_content(/Alpha.*Bravo.*Charlie/)
within :table do
expect(page).to have_css("tbody tr:nth-child(1)", text: "Alpha")
expect(page).to have_css("tbody tr:nth-child(2)", text: "Bravo")
expect(page).to have_css("tbody tr:nth-child(3)", text: "Charlie")
end

click_on "Customer"
expect(page).to have_content(/Charlie.*Bravo.*Alpha/)
within :table do
expect(page).to have_css("tbody tr:nth-child(1)", text: "Charlie")
expect(page).to have_css("tbody tr:nth-child(2)", text: "Bravo")
expect(page).to have_css("tbody tr:nth-child(3)", text: "Alpha")
end
end
end
19 changes: 16 additions & 3 deletions spec/features/products_index_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,25 @@
)

visit admin_products_path
expect(page).to have_content(/Gamma.*Alpha.*Beta/)

within :table do
expect(page).to have_css("tbody tr:nth-child(1)", text: "Gamma")
expect(page).to have_css("tbody tr:nth-child(2)", text: "Alpha")
expect(page).to have_css("tbody tr:nth-child(3)", text: "Beta")
end

click_on "Product Meta Tag"
expect(page).to have_content(/Alpha.*Beta.*Gamma/)
within :table do
expect(page).to have_css("tbody tr:nth-child(1)", text: "Alpha")
expect(page).to have_css("tbody tr:nth-child(2)", text: "Beta")
expect(page).to have_css("tbody tr:nth-child(3)", text: "Gamma")
end

click_on "Product Meta Tag"
expect(page).to have_content(/Gamma.*Beta.*Alpha/)
within :table do
expect(page).to have_css("tbody tr:nth-child(1)", text: "Gamma")
expect(page).to have_css("tbody tr:nth-child(2)", text: "Beta")
expect(page).to have_css("tbody tr:nth-child(3)", text: "Alpha")
end
end
end

0 comments on commit 074cb1a

Please sign in to comment.