Skip to content

Commit 58842d4

Browse files
author
Robert Mosolgo
committed
Merge pull request #475 from reactjs/ujs-refactor
Refactor UJS, support Turbolinks 5
2 parents 7c7e009 + 8857958 commit 58842d4

14 files changed

+117
-58
lines changed

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,9 @@ matrix:
2727
rvm: 2.1
2828
allow_failures:
2929
- rvm: jruby-9.0.1.0
30+
31+
before_install:
32+
- mkdir travis-phantomjs
33+
- wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 -O $PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2
34+
- tar -xvf $PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2 -C $PWD/travis-phantomjs
35+
- export PATH=$PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64/bin:$PATH

gemfiles/rails_4.1.gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ source "http://rubygems.org"
44

55
gem "rails", "~> 4.1.10"
66

7+
# Just to make sure we support old Turbolinks:
8+
gem "turbolinks", "~> 2.3.0"
9+
710
gemspec :path => "../"

gemfiles/rails_4.2.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
source "http://rubygems.org"
44

55
gem "rails", "~> 4.2.1"
6+
gem "turbolinks", "~> 2.5.0"
67

78
gemspec :path => "../"

gemfiles/rails_5.gemfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
source "http://rubygems.org"
44

5-
gem "rails", "~> 5.0.0.beta1"
5+
gem "rails", "~> 5.0.0.beta2"
6+
gem "turbolinks", "~> 5.0.0.beta"
67

78
gemspec :path => "../"

lib/assets/javascripts/react_ujs.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//= require react_ujs_mount
2+
//= require react_ujs_turbolinks
3+
//= require react_ujs_turbolinks_classic
4+
//= require react_ujs_turbolinks_classic_deprecated
5+
//= require react_ujs_native
6+
//= require react_ujs_event_setup
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
;(function(document, window) {
2+
// jQuery is optional. Use it to support legacy browsers.
3+
var $ = (typeof window.jQuery !== 'undefined') && window.jQuery;
4+
if ($) {
5+
ReactRailsUJS.handleEvent = function(eventName, callback) {
6+
$(document).on(eventName, callback);
7+
};
8+
} else {
9+
ReactRailsUJS.handleEvent = function(eventName, callback) {
10+
document.addEventListener(eventName, callback);
11+
};
12+
}
13+
// Detect which kind of events to set up:
14+
if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
15+
if (typeof Turbolinks.EVENTS !== 'undefined') {
16+
// Turbolinks.EVENTS is in classic version 2.4.0+
17+
ReactRailsUJS.TurbolinksClassic.setup();
18+
} else if (typeof Turbolinks.controller !== "undefined") {
19+
// Turbolinks.controller is in version 5+
20+
ReactRailsUJS.Turbolinks.setup();
21+
} else {
22+
ReactRailsUJS.TurbolinksClassicDeprecated.setup();
23+
}
24+
} else {
25+
ReactRailsUJS.Native.setup();
26+
}
27+
})(document, window);
Lines changed: 10 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
/*globals React, Turbolinks*/
2-
3-
// Unobtrusive scripting adapter for React
41
;(function(document, window) {
52
// jQuery is optional. Use it to support legacy browsers.
63
var $ = (typeof window.jQuery !== 'undefined') && window.jQuery;
74

8-
// create the namespace
95
window.ReactRailsUJS = {
6+
// This attribute holds the name of component which should be mounted
7+
// example: `data-react-class="MyApp.Items.EditForm"`
108
CLASS_NAME_ATTR: 'data-react-class',
9+
10+
// This attribute holds JSON stringified props for initializing the component
11+
// example: `data-react-props="{\"item\": { \"id\": 1, \"name\": \"My Item\"} }"`
1112
PROPS_ATTR: 'data-react-props',
12-
RAILS_ENV_DEVELOPMENT: <%= Rails.env == "development" %>,
13+
1314
// helper method for the mount and unmount methods to find the
1415
// `data-react-class` DOM elements
1516
findDOMNodes: function(searchSelector) {
@@ -40,6 +41,8 @@
4041
}
4142
},
4243

44+
// Within `searchSelector`, find nodes which should have React components
45+
// inside them, and mount them with their props.
4346
mountComponents: function(searchSelector) {
4447
var nodes = window.ReactRailsUJS.findDOMNodes(searchSelector);
4548

@@ -60,6 +63,8 @@
6063
}
6164
},
6265

66+
// Within `searchSelector`, find nodes which have React components
67+
// inside them, and unmount those components.
6368
unmountComponents: function(searchSelector) {
6469
var nodes = window.ReactRailsUJS.findDOMNodes(searchSelector);
6570

@@ -72,51 +77,4 @@
7277
}
7378
}
7479
};
75-
76-
// functions not exposed publicly
77-
function handleTurbolinksEvents () {
78-
var handleEvent;
79-
var unmountEvent;
80-
81-
if ($) {
82-
handleEvent = function(eventName, callback) {
83-
$(document).on(eventName, callback);
84-
};
85-
86-
} else {
87-
handleEvent = function(eventName, callback) {
88-
document.addEventListener(eventName, callback);
89-
};
90-
}
91-
92-
if (Turbolinks.EVENTS) {
93-
unmountEvent = Turbolinks.EVENTS.BEFORE_UNLOAD;
94-
} else {
95-
unmountEvent = 'page:receive';
96-
Turbolinks.pagesCached(0);
97-
98-
if (window.ReactRailsUJS.RAILS_ENV_DEVELOPMENT) {
99-
console.warn('The Turbolinks cache has been disabled (Turbolinks >= 2.4.0 is recommended). See https://github.com/reactjs/react-rails/issues/87 for more information.');
100-
}
101-
}
102-
handleEvent('page:change', function() {window.ReactRailsUJS.mountComponents()});
103-
handleEvent(unmountEvent, function() {window.ReactRailsUJS.unmountComponents()});
104-
}
105-
106-
function handleNativeEvents() {
107-
if ($) {
108-
$(function() {window.ReactRailsUJS.mountComponents()});
109-
} else if ('addEventListener' in window) {
110-
document.addEventListener('DOMContentLoaded', function() {window.ReactRailsUJS.mountComponents()});
111-
} else {
112-
// add support to IE8 without jQuery
113-
window.attachEvent('onload', function() {window.ReactRailsUJS.mountComponents()});
114-
}
115-
}
116-
117-
if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
118-
handleTurbolinksEvents();
119-
} else {
120-
handleNativeEvents();
121-
}
12280
})(document, window);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
;(function(document, window) {
2+
// jQuery is optional. Use it to support legacy browsers.
3+
var $ = (typeof window.jQuery !== 'undefined') && window.jQuery;
4+
5+
window.ReactRailsUJS.Native = {
6+
// Attach handlers to browser events to mount & unmount components
7+
setup: function() {
8+
if ($) {
9+
$(function() {window.ReactRailsUJS.mountComponents()});
10+
} else if ('addEventListener' in window) {
11+
document.addEventListener('DOMContentLoaded', function() {window.ReactRailsUJS.mountComponents()});
12+
} else {
13+
// add support to IE8 without jQuery
14+
window.attachEvent('onload', function() {window.ReactRailsUJS.mountComponents()});
15+
}
16+
}
17+
};
18+
})(document, window);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
;(function(document, window) {
2+
window.ReactRailsUJS.Turbolinks = {
3+
// Turbolinks 5+ got rid of named events (?!)
4+
setup: function() {
5+
ReactRailsUJS.handleEvent('turbolinks:load', function() {window.ReactRailsUJS.mountComponents()});
6+
ReactRailsUJS.handleEvent('turbolinks:before-cache', function() {window.ReactRailsUJS.unmountComponents()});
7+
}
8+
};
9+
})(document, window);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
;(function(document, window) {
2+
window.ReactRailsUJS.TurbolinksClassic = {
3+
// Attach handlers to Turbolinks-Classic events
4+
// for mounting and unmounting components
5+
setup: function() {
6+
ReactRailsUJS.handleEvent(Turbolinks.EVENTS.CHANGE, function() {window.ReactRailsUJS.mountComponents()});
7+
ReactRailsUJS.handleEvent(Turbolinks.EVENTS.BEFORE_UNLOAD, function() {window.ReactRailsUJS.unmountComponents()});
8+
}
9+
};
10+
})(document, window);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
;(function(document, window) {
2+
window.ReactRailsUJS.TurbolinksClassicDeprecated = {
3+
// Before Turbolinks 2.4.0, Turbolinks didn't
4+
// have named events and didn't have a before-unload event.
5+
// Also, it didn't work with the Turbolinks cache, see
6+
// https://github.com/reactjs/react-rails/issues/87
7+
setup: function() {
8+
Turbolinks.pagesCached(0)
9+
ReactRailsUJS.handleEvent('page:change', function() {window.ReactRailsUJS.mountComponents()});
10+
ReactRailsUJS.handleEvent('page:receive', function() {window.ReactRailsUJS.unmountComponents()});
11+
}
12+
};
13+
})(document, window);

test/react/rails/react_rails_ujs_test.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,34 +56,38 @@ class ReactRailsUJSTest < ActionDispatch::IntegrationTest
5656
test 'react_ujs works with Turbolinks' do
5757
visit '/pages/1'
5858
assert page.has_content?('Hello Bob')
59+
assert page.evaluate_script("Turbolinks.supported")
5960

6061
# Try clicking links.
6162
page.click_link('Alice')
63+
wait_for_turbolinks_to_be_available
6264
assert page.has_content?('Hello Alice')
6365

6466
page.click_link('Bob')
67+
wait_for_turbolinks_to_be_available
6568
assert page.has_content?('Hello Bob')
6669

6770
# Try going back.
6871
page.execute_script('history.back();')
72+
wait_for_turbolinks_to_be_available
6973
assert page.has_content?('Hello Alice')
7074

71-
wait_for_turbolinks_to_be_available()
72-
7375
# Try Turbolinks javascript API.
7476
page.execute_script('Turbolinks.visit("/pages/2");')
77+
wait_for_turbolinks_to_be_available
7578
assert page.has_content?('Hello Alice')
7679

77-
wait_for_turbolinks_to_be_available()
7880

7981
page.execute_script('Turbolinks.visit("/pages/1");')
82+
wait_for_turbolinks_to_be_available
8083
assert page.has_content?('Hello Bob')
8184

8285
# Component state is not persistent after clicking current page link.
8386
page.click_button 'Goodbye'
8487
assert page.has_content?('Goodbye Bob')
8588

8689
page.click_link('Bob')
90+
wait_for_turbolinks_to_be_available
8791
assert page.has_content?('Hello Bob')
8892
end
8993

test/react/server_rendering/manifest_container_test.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
if Rails::VERSION::MAJOR > 3
66
class ManifestContainerTest < ActiveSupport::TestCase
77
def setup
8-
precompile_assets
8+
capture_io do
9+
precompile_assets
10+
end
911

1012
# Make a new manifest since assets weren't compiled before
1113
config = Rails.application.config

test/test_helper.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ def precompile_assets
4747
end
4848

4949
def clear_precompiled_assets
50-
FileUtils.rm_r(File.expand_path("../dummy/public/assets", __FILE__))
50+
assets_directory = File.expand_path("../dummy/public/assets", __FILE__)
51+
FileUtils.rm_r(assets_directory)
5152
ENV.delete('RAILS_GROUPS')
5253
end
5354

0 commit comments

Comments
 (0)