turbo-rails 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b0552458511ec8ad34c4b0b3978d2a9ffb88485b844f60f6bc283287e0eecdd
4
- data.tar.gz: eccd64955e5db2682efe9b503c24feeb6cbf50c1e542d7bcef0416ded95026fb
3
+ metadata.gz: 3d7e1db921a74088b68618665ff1df3ae7e3216fff065c2236c4bc8fd3c94812
4
+ data.tar.gz: e3b2ab5358a0c5b333c32401650f3c78325342e9e0e3eac47c1da4ab05621b13
5
5
  SHA512:
6
- metadata.gz: 950a8abe637775b61e610d9c99a61f22fa514704a2a3d4453a0e53854c2c4dfe4e4c65f342cd534869d9d1edd83c513590a70744632a6981df52265b20ca142e
7
- data.tar.gz: 5d97247505d6934b2bda932a53b0ca3b19d69fe7000a269001cd50ecc9f38bba090e26f50a3d489073699a93ccc50093f218465f45b054969d10c10026052100
6
+ metadata.gz: c28d47c9a443718f9737b040b30d91964b1a02fb6f68fd3c9913b13ba7467ecbdb8e9a4bef4df711ad7aa99b8eb1d5a1005cc805b535a0fd21c41ff2f6ac279b
7
+ data.tar.gz: a20b70fa8892752160064a79ede46b553a1d04f84e91200d3f62092e973aa49c2627f6cc2e866b43c2452c5f8143ca5bc103c1807c3cf0f80499d2ae12be14b0
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- turbo-rails (0.5.1)
4
+ turbo-rails (0.5.2)
5
5
  rails (>= 6.0.0)
6
6
 
7
7
  GEM
@@ -73,7 +73,7 @@ GEM
73
73
  erubi (1.10.0)
74
74
  globalid (0.4.2)
75
75
  activesupport (>= 4.2.0)
76
- i18n (1.8.5)
76
+ i18n (1.8.6)
77
77
  concurrent-ruby (~> 1.0)
78
78
  loofah (2.8.0)
79
79
  crass (~> 1.0.2)
@@ -144,4 +144,4 @@ DEPENDENCIES
144
144
  turbo-rails!
145
145
 
146
146
  BUNDLED WITH
147
- 2.1.4
147
+ 2.2.3
data/README.md CHANGED
@@ -48,6 +48,15 @@ If you're using Webpack and need to use either the cable consumer or the Turbo i
48
48
  You can watch [the video introduction to Hotwire](https://hotwire.dev/#screencast), which focuses extensively on demonstration Turbo in a Rails demo. Then you should familiarize yourself with [Turbo handbook](https://turbo.hotwire.dev/handbook/introduction) to understand Drive, Frames, and Streams in-depth. Finally, dive into the code documentation by starting with [`Turbo::FramesHelper`](https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/frames_helper.rb), [`Turbo::StreamsHelper`](https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/streams_helper.rb), [`Turbo::Streams::TagBuilder`](https://github.com/hotwired/turbo-rails/blob/main/app/models/turbo/streams/tag_builder.rb), and [`Turbo::Broadcastable`](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb).
49
49
 
50
50
 
51
+ ## Compatibility with Rails UJS
52
+
53
+ Rails UJS includes helpers for sending links and forms over XMLHttpRequest, so you can respond with Ajax. Turbo supersedes this functionality, so you should ensure that you're either running Rails 6.1 with the defaults that turn this off for forms, or that you add `config.action_view.form_with_generates_remote_forms = false` to your `config/application.rb`.
54
+
55
+ Note that the helpers that turn `link_to` into remote invocations will _not_ currently work with Turbo. Links that have been made remote will not stick within frames nor will they allow you to respond with turbo stream actions. The recommendation is to replace these links with styled `button_to`, so you'll flow through a regular form, and you'll be better off with a11y compliance.
56
+
57
+ You can still use the `data-confirm` and `data-disable-with`.
58
+
59
+
51
60
  ## Development
52
61
 
53
62
  * To run the Rails tests: `bundle exec rake`.
@@ -19,7 +19,7 @@ const submittersByForm = new WeakMap;
19
19
  function findSubmitterFromClickTarget(target) {
20
20
  const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
21
21
  const candidate = element ? element.closest("input, button") : null;
22
- return (candidate === null || candidate === void 0 ? void 0 : candidate.getAttribute("type")) == "submit" ? candidate : null;
22
+ return (candidate === null || candidate === void 0 ? void 0 : candidate.type) == "submit" ? candidate : null;
23
23
  }
24
24
 
25
25
  function clickCaptured(event) {
@@ -129,6 +129,12 @@ class FetchResponse {
129
129
  get failed() {
130
130
  return !this.succeeded;
131
131
  }
132
+ get clientError() {
133
+ return this.statusCode >= 400 && this.statusCode <= 499;
134
+ }
135
+ get serverError() {
136
+ return this.statusCode >= 500 && this.statusCode <= 599;
137
+ }
132
138
  get redirected() {
133
139
  return this.response.redirected;
134
140
  }
@@ -377,7 +383,7 @@ class FormSubmission {
377
383
  }
378
384
  get method() {
379
385
  var _a;
380
- const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.method;
386
+ const method = ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formmethod")) || this.formElement.getAttribute("method") || "";
381
387
  return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get;
382
388
  }
383
389
  get action() {
@@ -429,7 +435,9 @@ class FormSubmission {
429
435
  };
430
436
  }
431
437
  requestSucceededWithResponse(request, response) {
432
- if (this.requestMustRedirect(request) && !response.redirected) {
438
+ if (response.clientError || response.serverError) {
439
+ this.delegate.formSubmissionFailedWithResponse(this, response);
440
+ } else if (this.requestMustRedirect(request) && !response.redirected) {
433
441
  const error = new Error("Form responses must redirect to another location");
434
442
  this.delegate.formSubmissionErrored(this, error);
435
443
  } else {
@@ -614,7 +622,9 @@ class FrameController {
614
622
  const frame = this.findFrameElement(formSubmission.formElement);
615
623
  frame.controller.loadResponse(response);
616
624
  }
617
- formSubmissionFailedWithResponse(formSubmission, fetchResponse) {}
625
+ formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
626
+ this.element.controller.loadResponse(fetchResponse);
627
+ }
618
628
  formSubmissionErrored(formSubmission, error) {}
619
629
  formSubmissionFinished(formSubmission) {}
620
630
  navigateFrame(element, url) {
@@ -1570,7 +1580,8 @@ class FormSubmitObserver {
1570
1580
  const form = event.target instanceof HTMLFormElement ? event.target : undefined;
1571
1581
  const submitter = event.submitter || undefined;
1572
1582
  if (form) {
1573
- if (this.delegate.willSubmitForm(form, submitter)) {
1583
+ const method = (submitter === null || submitter === void 0 ? void 0 : submitter.getAttribute("formmethod")) || form.method;
1584
+ if (method != "dialog" && this.delegate.willSubmitForm(form, submitter)) {
1574
1585
  event.preventDefault();
1575
1586
  this.delegate.formSubmitted(form, submitter);
1576
1587
  }
@@ -1803,12 +1814,10 @@ class Navigator {
1803
1814
  }
1804
1815
  formSubmissionStarted(formSubmission) {}
1805
1816
  async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
1806
- console.log("Form submission succeeded", formSubmission);
1807
1817
  if (formSubmission == this.formSubmission) {
1808
1818
  const responseHTML = await fetchResponse.responseHTML;
1809
1819
  if (responseHTML) {
1810
1820
  if (formSubmission.method != FetchMethod.get) {
1811
- console.log("Clearing snapshot cache after successful form submission");
1812
1821
  this.view.clearSnapshotCache();
1813
1822
  }
1814
1823
  const {statusCode: statusCode} = fetchResponse;
@@ -1818,17 +1827,21 @@ class Navigator {
1818
1827
  responseHTML: responseHTML
1819
1828
  }
1820
1829
  };
1821
- console.log("Visiting", fetchResponse.location, visitOptions);
1822
1830
  this.proposeVisit(fetchResponse.location, visitOptions);
1823
1831
  }
1824
1832
  }
1825
1833
  }
1826
- formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
1827
- console.error("Form submission failed", formSubmission, fetchResponse);
1828
- }
1829
- formSubmissionErrored(formSubmission, error) {
1830
- console.error("Form submission failed", formSubmission, error);
1834
+ async formSubmissionFailedWithResponse(formSubmission, fetchResponse) {
1835
+ const responseHTML = await fetchResponse.responseHTML;
1836
+ if (responseHTML) {
1837
+ const snapshot = Snapshot.fromHTMLString(responseHTML);
1838
+ this.view.render({
1839
+ snapshot: snapshot
1840
+ }, (() => {}));
1841
+ this.view.clearSnapshotCache();
1842
+ }
1831
1843
  }
1844
+ formSubmissionErrored(formSubmission, error) {}
1832
1845
  formSubmissionFinished(formSubmission) {}
1833
1846
  visitStarted(visit) {
1834
1847
  this.delegate.visitStarted(visit);
@@ -2475,7 +2488,7 @@ class Session {
2475
2488
  });
2476
2489
  }
2477
2490
  willFollowLinkToLocation(link, location) {
2478
- return this.linkIsVisitable(link) && this.locationIsVisitable(location) && this.applicationAllowsFollowingLinkToLocation(link, location);
2491
+ return this.elementIsNavigable(link) && this.locationIsVisitable(location) && this.applicationAllowsFollowingLinkToLocation(link, location);
2479
2492
  }
2480
2493
  followedLinkToLocation(link, location) {
2481
2494
  const action = this.getActionForLink(link);
@@ -2496,7 +2509,7 @@ class Session {
2496
2509
  this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
2497
2510
  }
2498
2511
  willSubmitForm(form, submitter) {
2499
- return true;
2512
+ return this.elementIsNavigable(form) && this.elementIsNavigable(submitter);
2500
2513
  }
2501
2514
  formSubmitted(form, submitter) {
2502
2515
  this.navigator.submitForm(form, submitter);
@@ -2582,8 +2595,8 @@ class Session {
2582
2595
  const action = link.getAttribute("data-turbo-action");
2583
2596
  return isAction(action) ? action : "advance";
2584
2597
  }
2585
- linkIsVisitable(link) {
2586
- const container = link.closest("[data-turbo]");
2598
+ elementIsNavigable(element) {
2599
+ const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
2587
2600
  if (container) {
2588
2601
  return container.getAttribute("data-turbo") != "false";
2589
2602
  } else {
@@ -8,7 +8,7 @@ module Turbo::Streams::ActionHelper
8
8
  # # => <turbo-stream action="replace" target="message_1"><template><div id="message_1">Hello!</div></template></turbo-stream>
9
9
  def turbo_stream_action_tag(action, target:, template: nil)
10
10
  target = convert_to_turbo_stream_dom_id(target)
11
- template = template ? "<template>#{template}</template>" : ""
11
+ template = action.to_sym == :remove ? "" : "<template>#{template}</template>"
12
12
 
13
13
  %(<turbo-stream action="#{action}" target="#{target}">#{template}</turbo-stream>).html_safe
14
14
  end
@@ -34,11 +34,11 @@ module Turbo::StreamsHelper
34
34
  #
35
35
  # # app/views/entries/index.html.erb
36
36
  # <%= turbo_stream_from Current.account, :entries %>
37
- # <div id="entries">New entries will be appended to this container</div>
37
+ # <div id="entries">New entries will be appended to this target</div>
38
38
  #
39
39
  # The example above will process all turbo streams sent to a stream name like <tt>account:5:entries</tt>
40
40
  # (when Current.account.id = 5). Updates to this stream can be sent like
41
- # <tt>entry.broadcast_append_to entry.account, :entries, container: "entries"</tt>.
41
+ # <tt>entry.broadcast_append_to entry.account, :entries, target: "entries"</tt>.
42
42
  def turbo_stream_from(*streamables)
43
43
  tag.turbo_cable_stream_source channel: "Turbo::StreamsChannel", "signed-stream-name": Turbo::StreamsChannel.signed_stream_name(streamables)
44
44
  end
@@ -1,3 +1,3 @@
1
1
  module Turbo
2
- VERSION = "0.5.2"
2
+ VERSION = "0.5.3"
3
3
  end
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotwired/turbo-rails",
3
- "version": "7.0.0-beta.2",
3
+ "version": "7.0.0-beta.3",
4
4
  "description": "The speed of a single-page web application without having to write any JavaScript",
5
5
  "module": "app/javascript/turbo/index.js",
6
6
  "main": "app/assets/javascripts/turbo.js",
@@ -13,7 +13,7 @@
13
13
  "release": "npm publish && git commit -am \"$npm_package_name v$npm_package_version\" && git push"
14
14
  },
15
15
  "dependencies": {
16
- "@hotwired/turbo": "^7.0.0-beta.1",
16
+ "@hotwired/turbo": "^7.0.0-beta.2",
17
17
  "@rails/actioncable": "^6.1.0"
18
18
  },
19
19
  "devDependencies": {
@@ -33,6 +33,12 @@ class Turbo::StreamsChannelTest < ActionCable::Channel::TestCase
33
33
  end
34
34
  end
35
35
 
36
+ test "broadcasting append now with empty template" do
37
+ assert_broadcast_on "stream", %(<turbo-stream action="append" target="message_1"><template></template></turbo-stream>) do
38
+ Turbo::StreamsChannel.broadcast_append_to "stream", target: "message_1", content: ""
39
+ end
40
+ end
41
+
36
42
  test "broadcasting prepend now" do
37
43
  assert_broadcast_on "stream", turbo_stream_action_tag("prepend", target: "messages", template: "<p>hello!</p>") do
38
44
  Turbo::StreamsChannel.broadcast_prepend_to "stream", target: "messages", partial: "messages/message", locals: { message: "hello!" }
data/yarn.lock CHANGED
@@ -23,10 +23,10 @@
23
23
  chalk "^2.0.0"
24
24
  js-tokens "^4.0.0"
25
25
 
26
- "@hotwired/turbo@^7.0.0-beta.1":
27
- version "7.0.0-beta.1"
28
- resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-7.0.0-beta.1.tgz#0111db8a80a7bb308c4cfdd2720112d06bf25c33"
29
- integrity sha512-V7hhELbDkYUGaHw/Yw4tu9pDUT1WE4t3A7tRs7Zh0Uwtk2vaJnmykNZ2uq20BnLjofufA1+MLJZ6AXJ3B/Il5A==
26
+ "@hotwired/turbo@^7.0.0-beta.2":
27
+ version "7.0.0-beta.2"
28
+ resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-7.0.0-beta.2.tgz#0f20c1f5b67fe07472a5f1eda2e3f79146324512"
29
+ integrity sha512-3YyWDaImtkU8MJ+//qV8nmV8KeFInN/nptqwJo1Bl5UjROmixPr3w9tPHyA499Mo1mBJOur8jiXVowbX6VWLUg==
30
30
 
31
31
  "@rails/actioncable@^6.1.0":
32
32
  version "6.1.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Stephenson
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-12-30 00:00:00.000000000 Z
13
+ date: 2021-01-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails