Skip to content

Commit

Permalink
Build-in integration with rails/ujs
Browse files Browse the repository at this point in the history
As an alternative to building support for Rails' Unobtrusive JavaScript
into `@hotwired/turbo` itself, instead publish a
`@hotwired/turbo-rails/ujs` file to re-use the existing `@rails/ujs`
hooks, and bridge the gaps between new `turbo:`-prefixed and
`ajax:`-prefixed events.

This is a re-imagining of [hotwired/turbo#40][] and
([hotwired/turbo#384][]). If deemed viable, this work would yield some
follow up tasks:

* drop support for `[data-turbo-method]` ([hotwired/turbo#277][])
* drop support for `[data-confirm]` ([hotwired/turbo#379][])

[hotwired/turbo#40]: hotwired/turbo#40
[hotwired/turbo#277]: hotwired/turbo#277
[hotwired/turbo#379]: hotwired/turbo#379
[hotwired/turbo#384]:hotwired/turbo#384
  • Loading branch information
seanpdoyle committed Oct 12, 2021
1 parent 12b0962 commit 4c5899a
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 0 deletions.
1 change: 1 addition & 0 deletions test/application_system_test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase

Capybara.configure do |config|
config.server = :puma, { Silent: true }
config.match = :prefer_exact
end
6 changes: 6 additions & 0 deletions test/dummy/app/controllers/messages_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class MessagesController < ApplicationController
before_action { sleep params.fetch(:sleep, 0).to_f }

def show
@message = Message.new(id: 1, content: "My message")
end
Expand All @@ -17,4 +19,8 @@ def create
def update
@message = Message.new(id: 1, content: "My message")
end

def new
@message = Message.new
end
end
29 changes: 29 additions & 0 deletions test/dummy/app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,35 @@
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= yield :head %>
<%= javascript_importmap_tags %>
<script type="module">
import Rails from "https://jspm.dev/@rails/ujs"

const extendUJS = ({ delegate, disableElement, enableElement, linkDisableSelector, buttonDisableSelector, formSubmitSelector }) => {
const getTurboFrame = (element) => {
if (element) {
const frameId = element.getAttribute("data-turbo-frame")
return frameId ? document.querySelector(`turbo-frame#${frameId}`) : element.closest("turbo-frame")
}
}

delegate(document, linkDisableSelector, "turbo:click", ({ target }) => {
const frame = getTurboFrame(target.closest("a"))

if (frame) {
frame.addEventListener("turbo:frame-load", () => enableElement(target), { once: true })
}
})
delegate(document, buttonDisableSelector, "turbo:before-cache", enableElement)

delegate(document, formSubmitSelector, "turbo:submit-start", ({ detail: { formSubmission: { submitter } } }) => disableElement(submitter))
delegate(document, formSubmitSelector, "turbo:submit-end", ({ detail: { formSubmission: { submitter } } }) => enableElement(submitter))
delegate(document, formSubmitSelector, "turbo:before-cache", enableElement)
}

Rails.start()

extendUJS(Rails)
</script>
</head>

<body>
Expand Down
75 changes: 75 additions & 0 deletions test/dummy/app/views/messages/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<turbo-frame id="frame"></turbo-frame>

<h1>a[data-disable-with]</h1>

<a href="<%= new_message_path(sleep: 0.1) %>" data-disable-with="Page: Visiting">Page: Visit</a>

<a href="<%= new_message_path(sleep: 0.1) %>" data-turbo-frame="frame" data-disable-with="Frame: Visiting">Frame: Visit</a>

<hr>

<h1>form[method="get"]</h1>

<form>
<input type="hidden" name="sleep" value="0.1">
<button data-disable-with="Submitting GET form button">Submit GET form button</button>
</form>

<form data-turbo-frame="frame">
<input type="hidden" name="sleep" value="0.1">
<button data-disable-with="Submitting GET form[data-turbo-frame] button">Submit GET form[data-turbo-frame] button</button>
</form>

<form>
<input type="hidden" name="sleep" value="0.1">
<input type="submit" value="Submit GET form button[data-turbo-frame]" data-turbo-frame="frame" data-disable-with="Submitting GET form button[data-turbo-frame]">
</form>

<form>
<input type="hidden" name="sleep" value="0.1">
<input type="submit" value="Submit GET form input" data-disable-with="Submitting GET form input">
</form>

<form data-turbo-frame="frame"
<input type="hidden" name="sleep" value="0.1">
<input type="submit" value="Submit GET form[data-turbo-frame] input" data-disable-with="Submitting GET form[data-turbo-frame] input">
</form>

<form>
<input type="hidden" name="sleep" value="0.1">
<input type="submit" value="Submit GET form input[data-turbo-frame]" data-turbo-frame="frame" data-disable-with="Submitting GET form input[data-turbo-frame]">
</form>

<hr>

<h1>form[method="post"]</h1>

<%= form_with model: @message do %>
<input type="hidden" name="sleep" value="0.1">
<button data-disable-with="Submitting POST form button">Submit POST form button</button>
<% end %>

<%= form_with model: @message, data: { turbo_frame: "frame" } do |form| %>
<input type="hidden" name="sleep" value="0.1">
<button data-disable-with="Submitting POST form[data-turbo-frame] button">Submit POST form[data-turbo-frame] button</button>
<% end %>

<%= form_with model: @message do |form| %>
<input type="hidden" name="sleep" value="0.1">
<input type="submit" value="Submit POST form button[data-turbo-frame]" data-turbo-frame="frame" data-disable-with="Submitting POST form button[data-turbo-frame]">
<% end %>

<%= form_with model: @message do %>
<input type="hidden" name="sleep" value="0.1">
<input type="submit" value="Submit POST form input" data-disable-with="Submitting POST form input">
<% end %>

<%= form_with model: @message, data: { turbo_frame: "frame" } do |form| %>
<input type="hidden" name="sleep" value="0.1">
<input type="submit" value="Submit POST form[data-turbo-frame] input" data-disable-with="Submitting POST form[data-turbo-frame] input">
<% end %>

<%= form_with model: @message do |form| %>
<input type="hidden" name="sleep" value="0.1">
<input type="submit" value="Submit POST form input[data-turbo-frame]" data-turbo-frame="frame" data-disable-with="Submitting POST form input[data-turbo-frame]">
<% end %>
153 changes: 153 additions & 0 deletions test/system/ujs/disable_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
require "application_system_test_case"

class UjsHTMLAnchorElementTest < ApplicationSystemTestCase
test "supports with a[data-disable-with]" do
visit new_message_path

click_link "Page: Visit"

assert_link "Page: Visiting"
assert_link "Frame: Visit"
assert_no_link "Page: Visiting"
end

test "supports a[data-turbo-frame][data-disable-with]" do
visit new_message_path

click_link "Frame: Visit"

assert_link "Frame: Visiting"
assert_link "Frame: Visit"
assert_no_link "Frame: Visiting"
end
end

class UjsHTMLButtonElementTest < ApplicationSystemTestCase
# test "supports form[method=get] button[data-disable-with]" do
# visit new_message_path
#
# click_button "Page: Submit GET form button"
#
# assert_button "Page: Submitting GET form button", disabled: true
# assert_no_button "Page: Submit GET form button"
# assert_button "Page: Submit GET form button"
# assert_no_button "Page: Submitting GET form button"
# end
#
# test "supports form[method=get][data-turbo-frame] button[data-disable-with]" do
# visit new_message_path
#
# click_button "Frame: Submit GET form[data-turbo-frame] button"
#
# assert_button "Frame: Submitting GET form[data-turbo-frame] button", disabled: true
# assert_no_button "Frame: Submitting GET form[data-turbo-frame] button"
# assert_button "Frame: Submit GET form[data-turbo-frame] button"
# assert_no_button "Frame: Submitting GET form[data-turbo-frame] button"
# end
#
# test "supports form[method=get] button[data-disable-with][data-turbo-frame]" do
# visit new_message_path
#
# click_button "Frame: Submit GET form button[data-turbo-frame]"
#
# assert_button "Frame: Submitting GET form button[data-turbo-frame]", disabled: true
# assert_no_button "Frame: Submitting GET form button[data-turbo-frame]"
# assert_button "Frame: Submitting GET form button[data-turbo-frame]"
# assert_no_button "Frame: Submitting GET form button[data-turbo-frame]"
# end
#
test "supports form[method=post] button[data-disable-with]" do
visit new_message_path

click_button "Submit POST form button"

assert_button "Submitting POST form button", disabled: true
assert_button "Submit POST form button"
assert_no_button "Submitting POST form button"
end

test "supports form[method=post] button[data-turbo-frame][data-disable-with]" do
visit new_message_path

click_button "Submit POST form button[data-turbo-frame]"

assert_button "Submitting POST form button[data-turbo-frame]", disabled: true
assert_button "Submit POST form button[data-turbo-frame]"
assert_no_button "Submitting POST form button[data-turbo-frame]"
end

test "supports form[method=post][data-turbo-frame] button[data-disable-with]" do
visit new_message_path

click_button "Submit POST form[data-turbo-frame] button"

assert_button "Submitting POST form[data-turbo-frame] button", disabled: true
assert_button "Submit POST form[data-turbo-frame] button"
assert_no_button "Submitting POST form[data-turbo-frame] button"
end
end

class UjsHTMLInputElementTest < ApplicationSystemTestCase
# test "supports form[method=get] input[data-disable-with]" do
# visit new_message_path
#
# click_button "Page: Submit GET form input"
#
# assert_button "Page: Submitting GET form input", disabled: true
# assert_no_button "Page: Submit GET form input"
# assert_button "Page: Submit GET form input"
# assert_no_button "Page: Submitting GET form input"
# end
#
# test "supports form[method=get][data-turbo-frame] input[data-disable-with]" do
# visit new_message_path
#
# click_button "Frame: Submit GET form[data-turbo-frame] input"
#
# assert_button "Frame: Submitting GET form input", disabled: true
# assert_no_button "Frame: Submitting GET form[data-turbo-frame] input"
# assert_button "Frame: Submit GET form[data-turbo-frame] input"
# assert_no_button "Frame: Submitting GET form[data-turbo-frame] input"
# end
#
# test "supports form[method=get] input[data-disable-with][data-turbo-frame]" do
# visit new_message_path
#
# click_button "Frame: Submit GET form input[data-turbo-frame]"
#
# assert_button "Frame: Submitting GET form input[data-turbo-frame]", disabled: true
# assert_no_button "Frame: Submitting GET form input[data-turbo-frame]"
# assert_button "Frame: Submitting GET form input[data-turbo-frame]"
# assert_no_button "Frame: Submitting GET form input[data-turbo-frame]"
# end

test "supports form[method=post] input[data-disable-with]" do
visit new_message_path

click_button "Submit POST form input"

assert_button "Submitting POST form input", disabled: true
assert_button "Submit POST form input"
assert_no_button "Submitting POST form input"
end

test "supports form[method=post] input[data-turbo-frame][data-disable-with]" do
visit new_message_path

click_button "Submit POST form input[data-turbo-frame]"

assert_button "Submitting POST form input[data-turbo-frame]", disabled: true
assert_button "Submit POST form input[data-turbo-frame]"
assert_no_button "Submitting POST form input[data-turbo-frame]"
end

test "supports form[method=post][data-turbo-frame] input[data-disable-with]" do
visit new_message_path

click_button "Submit POST form[data-turbo-frame] input"

assert_button "Submitting POST form[data-turbo-frame] input", disabled: true
assert_button "Submit POST form[data-turbo-frame] input"
assert_no_button "Submitting POST form[data-turbo-frame] input"
end
end

0 comments on commit 4c5899a

Please sign in to comment.