hyper-react 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +27 -0
  3. data/.gitignore +36 -0
  4. data/.rubocop.yml +1159 -0
  5. data/.travis.yml +29 -0
  6. data/Appraisals +20 -0
  7. data/CHANGELOG.md +93 -0
  8. data/Gemfile +6 -0
  9. data/LICENSE +19 -0
  10. data/README.md +121 -0
  11. data/Rakefile +33 -0
  12. data/UPGRADING.md +24 -0
  13. data/component-name-lookup.md +145 -0
  14. data/config.ru +25 -0
  15. data/gemfiles/opal_0.8_react_13.gemfile +13 -0
  16. data/gemfiles/opal_0.8_react_14.gemfile +13 -0
  17. data/gemfiles/opal_0.8_react_15.gemfile +13 -0
  18. data/gemfiles/opal_0.9_react_13.gemfile +13 -0
  19. data/gemfiles/opal_0.9_react_14.gemfile +13 -0
  20. data/gemfiles/opal_0.9_react_15.gemfile +13 -0
  21. data/hyper-react.gemspec +43 -0
  22. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +4 -0
  23. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
  24. data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
  25. data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
  26. data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
  27. data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
  28. data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
  29. data/lib/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
  30. data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +109 -0
  31. data/lib/hyper-react.rb +52 -0
  32. data/lib/rails-helpers/top_level_rails_component.rb +54 -0
  33. data/lib/react-sources/react-server.js +2 -0
  34. data/lib/react/api.rb +162 -0
  35. data/lib/react/callbacks.rb +42 -0
  36. data/lib/react/children.rb +30 -0
  37. data/lib/react/component.rb +139 -0
  38. data/lib/react/component/api.rb +50 -0
  39. data/lib/react/component/base.rb +9 -0
  40. data/lib/react/component/class_methods.rb +214 -0
  41. data/lib/react/component/dsl_instance_methods.rb +27 -0
  42. data/lib/react/component/params.rb +6 -0
  43. data/lib/react/component/props_wrapper.rb +83 -0
  44. data/lib/react/component/should_component_update.rb +98 -0
  45. data/lib/react/component/tags.rb +144 -0
  46. data/lib/react/element.rb +168 -0
  47. data/lib/react/event.rb +76 -0
  48. data/lib/react/ext/hash.rb +9 -0
  49. data/lib/react/ext/string.rb +8 -0
  50. data/lib/react/hash.rb +13 -0
  51. data/lib/react/native_library.rb +92 -0
  52. data/lib/react/object.rb +15 -0
  53. data/lib/react/observable.rb +29 -0
  54. data/lib/react/react-source.rb +9 -0
  55. data/lib/react/rendering_context.rb +142 -0
  56. data/lib/react/state.rb +190 -0
  57. data/lib/react/test.rb +16 -0
  58. data/lib/react/test/dsl.rb +17 -0
  59. data/lib/react/test/matchers/render_html_matcher.rb +49 -0
  60. data/lib/react/test/rspec.rb +15 -0
  61. data/lib/react/test/session.rb +46 -0
  62. data/lib/react/top_level.rb +132 -0
  63. data/lib/react/validator.rb +136 -0
  64. data/lib/reactive-ruby/component_loader.rb +49 -0
  65. data/lib/reactive-ruby/isomorphic_helpers.rb +197 -0
  66. data/lib/reactive-ruby/rails.rb +7 -0
  67. data/lib/reactive-ruby/rails/component_mount.rb +46 -0
  68. data/lib/reactive-ruby/rails/controller_helper.rb +15 -0
  69. data/lib/reactive-ruby/rails/railtie.rb +14 -0
  70. data/lib/reactive-ruby/serializers.rb +15 -0
  71. data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +42 -0
  72. data/lib/reactive-ruby/version.rb +3 -0
  73. data/lib/reactrb/auto-import.rb +32 -0
  74. data/lib/reactrb/deep-compare.rb +24 -0
  75. data/lib/reactrb/new-event-name-convention.rb +11 -0
  76. data/lib/sources/react-latest.js +21169 -0
  77. data/lib/sources/react-v13.js +21645 -0
  78. data/lib/sources/react-v14.js +20821 -0
  79. data/lib/sources/react-v15.js +21170 -0
  80. data/logo1.png +0 -0
  81. data/logo2.png +0 -0
  82. data/logo3.png +0 -0
  83. data/path_release_steps.md +9 -0
  84. data/spec/controller_helper_spec.rb +34 -0
  85. data/spec/index.html.erb +10 -0
  86. data/spec/react/callbacks_spec.rb +106 -0
  87. data/spec/react/children_spec.rb +76 -0
  88. data/spec/react/component/base_spec.rb +32 -0
  89. data/spec/react/component_spec.rb +872 -0
  90. data/spec/react/dsl_spec.rb +296 -0
  91. data/spec/react/element_spec.rb +136 -0
  92. data/spec/react/event_spec.rb +24 -0
  93. data/spec/react/native_library_spec.rb +344 -0
  94. data/spec/react/observable_spec.rb +7 -0
  95. data/spec/react/opal_jquery_extensions_spec.rb +66 -0
  96. data/spec/react/param_declaration_spec.rb +258 -0
  97. data/spec/react/react_spec.rb +209 -0
  98. data/spec/react/state_spec.rb +55 -0
  99. data/spec/react/test/dsl_spec.rb +43 -0
  100. data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
  101. data/spec/react/test/rspec_spec.rb +62 -0
  102. data/spec/react/test/session_spec.rb +100 -0
  103. data/spec/react/test/utils_spec.rb +45 -0
  104. data/spec/react/top_level_component_spec.rb +96 -0
  105. data/spec/react/tutorial/tutorial_spec.rb +36 -0
  106. data/spec/react/validator_spec.rb +124 -0
  107. data/spec/reactive-ruby/component_loader_spec.rb +71 -0
  108. data/spec/reactive-ruby/isomorphic_helpers_spec.rb +155 -0
  109. data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +10 -0
  110. data/spec/reactive-ruby/rails/component_mount_spec.rb +66 -0
  111. data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +35 -0
  112. data/spec/spec_helper.rb +115 -0
  113. data/spec/support/react/spec_helpers.rb +64 -0
  114. data/spec/vendor/es5-shim.min.js +6 -0
  115. data/spec/vendor/jquery-2.2.4.min.js +4 -0
  116. metadata +387 -0
@@ -0,0 +1,209 @@
1
+ require "spec_helper"
2
+
3
+ if opal?
4
+ RSpec.describe React, type: :component do
5
+ after(:each) do
6
+ React::API.clear_component_class_cache
7
+ end
8
+
9
+ describe "is_valid_element" do
10
+ it "should return true if passed a valid element" do
11
+ element = React::Element.new(`React.createElement('div')`)
12
+ expect(React.is_valid_element(element)).to eq(true)
13
+ end
14
+
15
+ it "should return false is passed a non React element" do
16
+ element = React::Element.new(`{}`)
17
+ expect(React.is_valid_element(element)).to eq(false)
18
+ end
19
+ end
20
+
21
+ describe "create_element" do
22
+ it "should create a valid element with only tag" do
23
+ element = React.create_element('div')
24
+ expect(React.is_valid_element(element)).to eq(true)
25
+ end
26
+
27
+ context "with block" do
28
+ it "should create a valid element with text as only child when block yield String" do
29
+ element = React.create_element('div') { "lorem ipsum" }
30
+ expect(React.is_valid_element(element)).to eq(true)
31
+ expect(element.props.children).to eq("lorem ipsum")
32
+ end
33
+
34
+ it "should create a valid element with children as array when block yield Array of element" do
35
+ element = React.create_element('div') do
36
+ [React.create_element('span'), React.create_element('span'), React.create_element('span')]
37
+ end
38
+ expect(React.is_valid_element(element)).to eq(true)
39
+ expect(element.props.children.length).to eq(3)
40
+ end
41
+
42
+ it "should render element with children as array when block yield Array of element" do
43
+ element = React.create_element('div') do
44
+ [React.create_element('span'), React.create_element('span'), React.create_element('span')]
45
+ end
46
+ instance = renderElementToDocument(element)
47
+ expect(Element[instance].children.length).to eq(3)
48
+ end
49
+ end
50
+
51
+ describe "custom element" do
52
+ before do
53
+ stub_const 'Foo', Class.new
54
+ Foo.class_eval do
55
+ def render
56
+ React.create_element("div") { "lorem" }
57
+ end
58
+ end
59
+ end
60
+
61
+ it "should render element with only one children correctly" do
62
+ element = React.create_element(Foo) { React.create_element('span') }
63
+ instance = renderElementToDocument(element)
64
+ expect(instance.props.children).not_to be_a(Array)
65
+ expect(instance.props.children.type).to eq("span")
66
+ end
67
+
68
+ it "should render element with more than one children correctly" do
69
+ element = React.create_element(Foo) { [React.create_element('span'), React.create_element('span')] }
70
+ instance = renderElementToDocument(element)
71
+ expect(instance.props.children).to be_a(Array)
72
+ expect(instance.props.children.length).to eq(2)
73
+ end
74
+
75
+ it "should create a valid element provided class defined `render`" do
76
+ element = React.create_element(Foo)
77
+ expect(React.is_valid_element(element)).to eq(true)
78
+ end
79
+
80
+ it "should allow creating with properties" do
81
+ element = React.create_element(Foo, foo: "bar")
82
+ expect(element.props.foo).to eq("bar")
83
+ end
84
+
85
+ it "should raise error if provided class doesn't defined `render`" do
86
+ expect { React.create_element(Array) }.to raise_error
87
+ end
88
+
89
+ it "should use the same instance for the same ReactComponent" do
90
+ Foo.class_eval do
91
+ attr_accessor :a
92
+ def initialize(n)
93
+ self.a = 10
94
+ end
95
+
96
+ def component_will_mount
97
+ self.a = 20
98
+ end
99
+
100
+ def render
101
+ React.create_element("div") { self.a.to_s }
102
+ end
103
+ end
104
+
105
+ expect(Foo).to render("<div>20</div>")
106
+ end
107
+
108
+ it "should match the instance cycle to ReactComponent life cycle" do
109
+ `var count = 0;`
110
+
111
+ Foo.class_eval do
112
+ def initialize
113
+ `count = count + 1;`
114
+ end
115
+ def render
116
+ React.create_element("div")
117
+ end
118
+ end
119
+
120
+ renderToDocument(Foo)
121
+ renderToDocument(Foo)
122
+
123
+ expect(`count`).to eq(2)
124
+ end
125
+ end
126
+
127
+ describe "create element with properties" do
128
+ it "should enforce snake-cased property name" do
129
+ element = React.create_element("div", class_name: "foo")
130
+ expect(element.props.className).to eq("foo")
131
+ end
132
+
133
+ it "should allow custom property" do
134
+ element = React.create_element("div", foo: "bar")
135
+ expect(element.props.foo).to eq("bar")
136
+ end
137
+
138
+ it "should not camel-case custom property" do
139
+ element = React.create_element("div", foo_bar: "foo")
140
+ expect(element.props.foo_bar).to eq("foo")
141
+ end
142
+ end
143
+
144
+ describe "class_name helpers (React.addons.classSet)" do
145
+ it "should transform Hash provided to `class_name` props as string", v13_only: true do
146
+ classes = {foo: true, bar: false, lorem: true}
147
+ element = React.create_element("div", class_name: classes)
148
+ expect(element.props.className).to eq("foo lorem")
149
+ end
150
+
151
+ it "should not alter behavior when passing a string" do
152
+ element = React.create_element("div", class_name: "foo bar")
153
+
154
+ expect(element.props.className).to eq("foo bar")
155
+ end
156
+ end
157
+ end
158
+
159
+ describe "render" do
160
+ async "should render element to DOM" do
161
+ div = `document.createElement("div")`
162
+ React.render(React.create_element('span') { "lorem" }, div) do
163
+ run_async {
164
+ expect(`div.children[0].tagName`).to eq("SPAN")
165
+ expect(`div.textContent`).to eq("lorem")
166
+ }
167
+ end
168
+ end
169
+
170
+ it "should work without providing a block" do
171
+ div = `document.createElement("div")`
172
+ React.render(React.create_element('span') { "lorem" }, div)
173
+ end
174
+
175
+ it "should return a React::Component::API compatible object" do
176
+ div = `document.createElement("div")`
177
+ component = React.render(React.create_element('span') { "lorem" }, div)
178
+ React::Component::API.public_instance_methods(true).each do |method_name|
179
+ expect(component).to respond_to(method_name)
180
+ end
181
+ end
182
+
183
+ pending "should return nil to prevent abstraction leakage" do
184
+ div = `document.createElement("div")`
185
+ expect {
186
+ React.render(React.create_element('span') { "lorem" }, div)
187
+ }.to be_nil
188
+ end
189
+ end
190
+
191
+ describe "render_to_string" do
192
+ it "should render a React.Element to string" do
193
+ ele = React.create_element('span') { "lorem" }
194
+ expect(React.render_to_string(ele)).to be_kind_of(String)
195
+ end
196
+ end
197
+
198
+ describe "unmount_component_at_node" do
199
+ async "should unmount component at node" do
200
+ div = `document.createElement("div")`
201
+ React.render(React.create_element('span') { "lorem" }, div ) do
202
+ run_async {
203
+ expect(React.unmount_component_at_node(div)).to eq(true)
204
+ }
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe 'React::State' do
5
+ it "can created static exported states" do
6
+ stub_const 'Foo', Class.new
7
+ Foo.class_eval do
8
+ include React::Component
9
+ export_state(:foo) { 'bar' }
10
+ end
11
+
12
+ expect(Foo.foo).to eq('bar')
13
+ end
14
+
15
+ # these will all require async operations and testing to see if things get
16
+ # re-rendered see spec_helper the "render" test method
17
+
18
+ # if Foo.foo is used during rendering then when Foo.foo changes we will
19
+ # rerender
20
+ it "sets up observers when exported states are read"
21
+
22
+ # React::State.set_state(object, attribute, value) +
23
+ # React::State.get_state(object, attribute)
24
+ it "can be accessed outside of react using get/set_state"
25
+
26
+ it 'ignores state updates during rendering' do
27
+ stub_const 'StateTest', Class.new(React::Component::Base)
28
+ StateTest.class_eval do
29
+ export_state :boom
30
+ before_mount do
31
+ # force boom to be on the observing list during the current rendering cycle
32
+ StateTest.boom! !StateTest.boom
33
+ # this is automatically called by after_mount / after_update, but we don't want
34
+ # to have to setup a complicated async test, so we just force it now.
35
+ # if we don't do this, then updating boom will have no effect on the first render
36
+ React::State.update_states_to_observe
37
+ end
38
+ def render
39
+ (StateTest.boom ? "Boom" : "No Boom").tap { StateTest.boom! !StateTest.boom }
40
+ end
41
+ end
42
+ %x{
43
+ var log = [];
44
+ var org_warn_console = window.console.warn;
45
+ var org_error_console = window.console.error;
46
+ window.console.warn = window.console.error = function(str){log.push(str)}
47
+ }
48
+ markup = React.render_to_static_markup(React.create_element(StateTest))
49
+ `window.console.warn = org_warn_console; window.console.error = org_error_console;`
50
+ expect(markup).to eq('<span>Boom</span>')
51
+ expect(StateTest.boom).to be_falsy
52
+ expect(`log`).to eq([])
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ RSpec.describe React::Test::DSL do
5
+ describe 'the DSL' do
6
+ let(:session) { Class.new { include React::Test::DSL }.new }
7
+
8
+ before do
9
+ React::Test.reset_session!
10
+
11
+ stub_const 'Greeter', Class.new
12
+ Greeter.class_eval do
13
+ include React::Component
14
+
15
+ params do
16
+ optional :message
17
+ optional :from
18
+ end
19
+
20
+ def render
21
+ span { "Hello #{params.message}" }
22
+ end
23
+ end
24
+ end
25
+
26
+ it 'is possible to include it in another class' do
27
+ session.mount(Greeter)
28
+ expect(session.instance).to be_a(Greeter)
29
+ end
30
+
31
+ it "should provide a 'component' shortcut for more expressive tests" do
32
+ session.component.mount(Greeter)
33
+ expect(session.component.instance).to be_a(Greeter)
34
+ end
35
+
36
+ React::Test::Session::DSL_METHODS.each do |method|
37
+ it "responds to all DSL method: #{method}" do
38
+ expect(session).to respond_to(method)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ describe React::Test::Matchers::RenderHTMLMatcher do
5
+ let(:component) {
6
+ Class.new do
7
+ include React::Component
8
+ params do
9
+ optional :string
10
+ end
11
+ def render
12
+ div do
13
+ span { params.string } if params.string
14
+ 'lorem'
15
+ end
16
+ end
17
+ end
18
+ }
19
+ let(:expected) { '<div>lorem</div>' }
20
+ let(:matcher) { described_class.new(expected) }
21
+
22
+ describe '#matches?' do
23
+ it 'is truthy when rendered component html equals expected html' do
24
+ expect(matcher.matches?(component)).to be_truthy
25
+ end
26
+
27
+ it 'is falsey when rendered component html does not equal expected html' do
28
+ matcher = described_class.new('foo')
29
+ expect(matcher.matches?(component)).to be_falsey
30
+ end
31
+ end
32
+
33
+ describe '#with_params' do
34
+ let(:expected) { '<div><span>str</span>lorem</div>' }
35
+
36
+ it 'renders the component with the given params' do
37
+ matcher.with_params(string: 'str')
38
+ expect(matcher.matches?(component)).to be_truthy
39
+ end
40
+ end
41
+
42
+ describe '#failure_message' do
43
+ let(:expected) { '<div><span>str</span>lorem</div>' }
44
+
45
+ it 'includes the name of the component' do
46
+ stub_const 'Foo', component
47
+ matcher.matches?(Foo)
48
+ expect(matcher.failure_message).to match(/expected 'Foo'/)
49
+ end
50
+
51
+ it 'includes the params hash' do
52
+ matcher.with_params(string: 'bar')
53
+ matcher.matches?(component)
54
+ expect(matcher.failure_message).to match(/with params '{"string"=>"bar"}'/)
55
+ end
56
+
57
+ it 'includes the expected html value' do
58
+ matcher.matches?(component)
59
+ expect(matcher.failure_message).to match(/to render '#{expected}'/)
60
+ end
61
+
62
+ it 'includes the actual html value' do
63
+ actual = '<div>lorem<\/div>'
64
+ matcher.matches?(component)
65
+ expect(matcher.failure_message).to match(/, but '#{actual}' was rendered/)
66
+ end
67
+
68
+ it 'does not include "to not render"' do
69
+ matcher.matches?(component)
70
+ expect(matcher.failure_message).to_not match(/to not render/)
71
+ end
72
+ end
73
+
74
+ describe '#negative_failure_message' do
75
+ let(:expected) { '<div><span>str</span>lorem</div>' }
76
+
77
+ it 'includes "to not render"' do
78
+ matcher.matches?(component)
79
+ expect(matcher.negative_failure_message).to match(/to not render/)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ if opal?
4
+ RSpec.describe 'react/test/rspec', type: :component do
5
+ before do
6
+ stub_const 'Greeter', Class.new
7
+ Greeter.class_eval do
8
+ include React::Component
9
+ params do
10
+ optional :message
11
+ optional :from
12
+ end
13
+
14
+ def render
15
+ span { "Hello #{params.message}" }
16
+ end
17
+ end
18
+ end
19
+
20
+ it 'should include react/test in rspec' do
21
+ comp = mount(Greeter)
22
+ expect(component.instance).to eq(comp)
23
+ end
24
+
25
+ it 'includes rspec matchers' do
26
+ expect(Greeter).to render(
27
+ '<span>Hello world</span>'
28
+ ).with_params(message: 'world')
29
+ end
30
+
31
+ describe 'resetting the session' do
32
+ it 'creates an instance of the mounted component in one example' do
33
+ mount(Greeter)
34
+ end
35
+
36
+ it '...then is not availalbe in the next' do
37
+ expect { component.instance }.to raise_error
38
+ end
39
+ end
40
+ end
41
+
42
+ RSpec.describe 'react/test/rspec', type: :other do
43
+ before do
44
+ stub_const 'Greeter', Class.new
45
+ Greeter.class_eval do
46
+ include React::Component
47
+ params do
48
+ optional :message
49
+ optional :from
50
+ end
51
+
52
+ def render
53
+ span { "Hello #{params.message}" }
54
+ end
55
+ end
56
+ end
57
+
58
+ it 'should not include react/test in rspec' do
59
+ expect { mount(Greeter) }.to raise_error
60
+ end
61
+ end
62
+ end