hyper-react 0.99.6 → 1.0.0.lap21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +27 -0
  3. data/.gitignore +30 -37
  4. data/.rubocop.yml +1159 -0
  5. data/.travis.yml +32 -0
  6. data/Appraisals +31 -0
  7. data/CHANGELOG.md +143 -0
  8. data/DOCS.md +1515 -0
  9. data/Gemfile +2 -5
  10. data/LICENSE +19 -0
  11. data/README.md +5 -33
  12. data/Rakefile +25 -6
  13. data/UPGRADING.md +24 -0
  14. data/component-name-lookup.md +145 -0
  15. data/dciy.toml +3 -0
  16. data/dciy_prepare.sh +8 -0
  17. data/dciy_run.sh +10 -0
  18. data/hyper-react.gemspec +24 -18
  19. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
  20. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/server_rendering.js +5 -0
  21. data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/test_application.rb +2 -0
  22. data/lib/generators/reactive_ruby/test_app/templates/boot.rb.erb +6 -0
  23. data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
  24. data/lib/generators/reactive_ruby/test_app/templates/test_application.rb.erb +13 -0
  25. data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
  26. data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
  27. data/lib/generators/reactive_ruby/test_app/templates/views/layouts/test_layout.html.erb +0 -0
  28. data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +117 -0
  29. data/lib/hyper-react.rb +66 -4
  30. data/lib/rails-helpers/top_level_rails_component.rb +75 -0
  31. data/lib/react/api.rb +203 -0
  32. data/lib/react/callbacks.rb +41 -0
  33. data/lib/react/children.rb +30 -0
  34. data/lib/react/component.rb +177 -0
  35. data/lib/react/component/api.rb +69 -0
  36. data/lib/react/component/base.rb +13 -0
  37. data/lib/react/component/class_methods.rb +181 -0
  38. data/lib/react/component/dsl_instance_methods.rb +23 -0
  39. data/lib/react/component/params.rb +6 -0
  40. data/lib/react/component/props_wrapper.rb +78 -0
  41. data/lib/react/component/should_component_update.rb +99 -0
  42. data/lib/react/component/tags.rb +108 -0
  43. data/lib/react/config.rb +5 -0
  44. data/lib/react/config/client.rb.erb +19 -0
  45. data/lib/react/config/server.rb +23 -0
  46. data/lib/react/element.rb +150 -0
  47. data/lib/react/event.rb +76 -0
  48. data/lib/react/ext/hash.rb +9 -0
  49. data/lib/react/ext/opal-jquery/element.rb +26 -0
  50. data/lib/react/ext/string.rb +8 -0
  51. data/lib/react/hash.rb +13 -0
  52. data/lib/react/native_library.rb +87 -0
  53. data/lib/react/object.rb +15 -0
  54. data/lib/react/react-source-browser.rb +3 -0
  55. data/lib/react/react-source-server.rb +3 -0
  56. data/lib/react/react-source.rb +16 -0
  57. data/lib/react/ref_callback.rb +31 -0
  58. data/lib/react/rendering_context.rb +146 -0
  59. data/lib/react/server.rb +19 -0
  60. data/lib/react/state_wrapper.rb +23 -0
  61. data/lib/react/test.rb +16 -0
  62. data/lib/react/test/dsl.rb +17 -0
  63. data/lib/react/test/matchers/render_html_matcher.rb +56 -0
  64. data/lib/react/test/rspec.rb +15 -0
  65. data/lib/react/test/session.rb +37 -0
  66. data/lib/react/test/utils.rb +71 -0
  67. data/lib/react/top_level.rb +110 -0
  68. data/lib/react/top_level_render.rb +28 -0
  69. data/lib/react/validator.rb +136 -0
  70. data/lib/reactive-ruby/component_loader.rb +43 -0
  71. data/lib/reactive-ruby/isomorphic_helpers.rb +235 -0
  72. data/lib/reactive-ruby/rails.rb +8 -0
  73. data/lib/reactive-ruby/rails/component_mount.rb +48 -0
  74. data/lib/reactive-ruby/rails/controller_helper.rb +14 -0
  75. data/lib/reactive-ruby/rails/railtie.rb +20 -0
  76. data/lib/reactive-ruby/serializers.rb +15 -0
  77. data/lib/reactive-ruby/server_rendering/contextual_renderer.rb +41 -0
  78. data/lib/reactive-ruby/server_rendering/hyper_asset_container.rb +46 -0
  79. data/lib/reactive-ruby/version.rb +3 -0
  80. data/lib/reactrb/auto-import.rb +27 -0
  81. data/logo1.png +0 -0
  82. data/logo2.png +0 -0
  83. data/logo3.png +0 -0
  84. data/path_release_steps.md +9 -0
  85. data/spec/controller_helper_spec.rb +35 -0
  86. data/spec/index.html.erb +11 -0
  87. data/spec/react/callbacks_spec.rb +142 -0
  88. data/spec/react/children_spec.rb +132 -0
  89. data/spec/react/component/base_spec.rb +36 -0
  90. data/spec/react/component_spec.rb +1073 -0
  91. data/spec/react/dsl_spec.rb +323 -0
  92. data/spec/react/element_spec.rb +132 -0
  93. data/spec/react/event_spec.rb +39 -0
  94. data/spec/react/native_library_spec.rb +387 -0
  95. data/spec/react/observable_spec.rb +31 -0
  96. data/spec/react/opal_jquery_extensions_spec.rb +68 -0
  97. data/spec/react/param_declaration_spec.rb +253 -0
  98. data/spec/react/react_spec.rb +278 -0
  99. data/spec/react/refs_callback_spec.rb +65 -0
  100. data/spec/react/server_spec.rb +25 -0
  101. data/spec/react/state_spec.rb +52 -0
  102. data/spec/react/test/dsl_spec.rb +43 -0
  103. data/spec/react/test/matchers/render_html_matcher_spec.rb +83 -0
  104. data/spec/react/test/rspec_spec.rb +62 -0
  105. data/spec/react/test/session_spec.rb +88 -0
  106. data/spec/react/test/utils_spec.rb +28 -0
  107. data/spec/react/top_level_component_spec.rb +103 -0
  108. data/spec/react/tutorial/tutorial_spec.rb +42 -0
  109. data/spec/react/validator_spec.rb +134 -0
  110. data/spec/reactive-ruby/component_loader_spec.rb +74 -0
  111. data/spec/reactive-ruby/isomorphic_helpers_spec.rb +157 -0
  112. data/spec/reactive-ruby/rails/asset_pipeline_spec.rb +17 -0
  113. data/spec/reactive-ruby/rails/component_mount_spec.rb +64 -0
  114. data/spec/reactive-ruby/server_rendering/contextual_renderer_spec.rb +39 -0
  115. data/spec/spec_helper.rb +55 -0
  116. data/spec/test_app/README.md +24 -0
  117. data/spec/test_app/Rakefile +6 -0
  118. data/spec/test_app/app/assets/config/manifest.js +3 -0
  119. data/spec/test_app/app/assets/images/.keep +0 -0
  120. data/spec/test_app/app/assets/javascripts/application.rb +7 -0
  121. data/spec/test_app/app/assets/javascripts/cable.js +13 -0
  122. data/spec/test_app/app/assets/javascripts/channels/.keep +0 -0
  123. data/spec/test_app/app/assets/javascripts/server_rendering.js +5 -0
  124. data/spec/test_app/app/assets/stylesheets/application.css +15 -0
  125. data/spec/test_app/app/channels/application_cable/channel.rb +4 -0
  126. data/spec/test_app/app/channels/application_cable/connection.rb +4 -0
  127. data/spec/test_app/app/controllers/application_controller.rb +3 -0
  128. data/spec/test_app/app/controllers/concerns/.keep +0 -0
  129. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  130. data/spec/test_app/app/jobs/application_job.rb +2 -0
  131. data/spec/test_app/app/mailers/application_mailer.rb +4 -0
  132. data/spec/test_app/app/models/application_record.rb +3 -0
  133. data/spec/test_app/app/models/concerns/.keep +0 -0
  134. data/spec/test_app/app/views/components.rb +11 -0
  135. data/spec/test_app/app/views/components/hello_world.rb +11 -0
  136. data/spec/test_app/app/views/components/todo.rb +14 -0
  137. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  138. data/spec/test_app/app/views/layouts/explicit_layout.html.erb +0 -0
  139. data/spec/test_app/app/views/layouts/mailer.html.erb +13 -0
  140. data/spec/test_app/app/views/layouts/mailer.text.erb +1 -0
  141. data/spec/test_app/app/views/layouts/test_layout.html.erb +0 -0
  142. data/spec/test_app/bin/bundle +3 -0
  143. data/spec/test_app/bin/rails +4 -0
  144. data/spec/test_app/bin/rake +4 -0
  145. data/spec/test_app/bin/setup +38 -0
  146. data/spec/test_app/bin/update +29 -0
  147. data/spec/test_app/bin/yarn +11 -0
  148. data/spec/test_app/config.ru +5 -0
  149. data/spec/test_app/config/application.rb +45 -0
  150. data/spec/test_app/config/boot.rb +6 -0
  151. data/spec/test_app/config/cable.yml +10 -0
  152. data/spec/test_app/config/database.yml +25 -0
  153. data/spec/test_app/config/environment.rb +5 -0
  154. data/spec/test_app/config/environments/development.rb +54 -0
  155. data/spec/test_app/config/environments/production.rb +91 -0
  156. data/spec/test_app/config/environments/test.rb +42 -0
  157. data/spec/test_app/config/initializers/application_controller_renderer.rb +8 -0
  158. data/spec/test_app/config/initializers/assets.rb +14 -0
  159. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  160. data/spec/test_app/config/initializers/cookies_serializer.rb +5 -0
  161. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  162. data/spec/test_app/config/initializers/inflections.rb +16 -0
  163. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  164. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  165. data/spec/test_app/config/locales/en.yml +33 -0
  166. data/spec/test_app/config/puma.rb +56 -0
  167. data/spec/test_app/config/routes.rb +3 -0
  168. data/spec/test_app/config/secrets.yml +32 -0
  169. data/spec/test_app/config/spring.rb +6 -0
  170. data/spec/test_app/db/development.sqlite3 +0 -0
  171. data/spec/test_app/db/schema.rb +15 -0
  172. data/spec/test_app/db/seeds.rb +7 -0
  173. data/spec/test_app/db/test.sqlite3 +0 -0
  174. data/spec/test_app/lib/assets/.keep +0 -0
  175. data/spec/test_app/log/.keep +0 -0
  176. data/spec/test_app/package.json +5 -0
  177. data/spec/test_app/public/404.html +67 -0
  178. data/spec/test_app/public/422.html +67 -0
  179. data/spec/test_app/public/500.html +66 -0
  180. data/spec/test_app/public/apple-touch-icon-precomposed.png +0 -0
  181. data/spec/test_app/public/apple-touch-icon.png +0 -0
  182. data/spec/test_app/public/favicon.ico +0 -0
  183. data/spec/vendor/es5-shim.min.js +7 -0
  184. data/spec/vendor/jquery-2.2.4.min.js +4 -0
  185. metadata +401 -61
  186. data/CODE_OF_CONDUCT.md +0 -49
  187. data/lib/react/version.rb +0 -3
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'opal-jquery extensions', js: true do
4
+ describe 'Element' do
5
+ xit 'will reuse the wrapper componet class for the same Element' do
6
+ # TODO how come a def component_will_unmount will not be received
7
+ stub_const 'Foo', Class.new(React::Component::Base)
8
+ Foo.class_eval do
9
+ param :name
10
+ def render
11
+ "hello #{params.name}"
12
+ end
13
+
14
+ def component_will_unmount
15
+
16
+ end
17
+ end
18
+
19
+ expect_any_instance_of(Foo).to_not receive(:component_will_unmount)
20
+
21
+ test_div = Element.new(:div)
22
+ test_div.render { Foo(name: 'fred') }
23
+ test_div.render { Foo(name: 'freddy') }
24
+ expect(Element[test_div].find('span').html).to eq('hello freddy')
25
+ end
26
+
27
+ it 'renders a top level component using render with a block' do
28
+ expect_evaluate_ruby do
29
+ class Foo < React::Component::Base
30
+ param :name
31
+ def render
32
+ "hello #{params.name}"
33
+ end
34
+ end
35
+ test_div = Element.new(:div)
36
+ test_div.render { Foo(name: 'fred') }
37
+ Element[test_div].find('span').html
38
+ end.to eq('hello fred')
39
+ end
40
+
41
+ it 'renders a top level component using render with a container and params ' do
42
+ expect_evaluate_ruby do
43
+ test_div = Element.new(:div)
44
+ test_div.render(:span, id: :render_test_span) { 'hello' }
45
+ Element[test_div].find('#render_test_span').html
46
+ end.to eq('hello')
47
+ end
48
+
49
+ it 'will find the DOM node given a react element' do
50
+ expect_evaluate_ruby do
51
+ class Foo < React::Component::Base
52
+ def render
53
+ div { 'hello' }
54
+ end
55
+ end
56
+ Element[React::Test::Utils.render_component_into_document(Foo)].html
57
+ end.to eq('hello')
58
+ end
59
+
60
+ it "accepts plain js object as selector" do
61
+ evaluate_ruby do
62
+ Element[JS.call(:eval, "(function () { return window; })();")]
63
+ end
64
+ expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
65
+ .not_to match(/Exception|Error/)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,253 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'the param macro', js: true do
4
+ it 'defines collect_other_params_as method on params proxy' do
5
+ mount 'Foo', bar: 'biz' do
6
+ class Foo < React::Component::Base
7
+ collect_other_params_as :foo
8
+
9
+ def render
10
+ div { params.foo[:bar] }
11
+ end
12
+ end
13
+ end
14
+ expect(page.body[-35..-19]).to include("<div>biz</div>")
15
+ end
16
+
17
+ it "can create and access a required param" do
18
+ mount 'Foo', foo: :bar do
19
+ class Foo < React::Component::Base
20
+ param :foo
21
+
22
+ def render
23
+ div { params.foo }
24
+ end
25
+ end
26
+ end
27
+ expect(page.body[-35..-19]).to include("<div>bar</div>")
28
+ end
29
+
30
+ it "can create and access an optional params" do
31
+ mount 'Foo', foo1: :bar1, foo3: :bar3 do
32
+ class Foo < React::Component::Base
33
+
34
+ param foo1: :no_bar1
35
+ param foo2: :no_bar2
36
+ param :foo3, default: :no_bar3
37
+ param :foo4, default: :no_bar4
38
+
39
+ def render
40
+ div { "#{params.foo1}-#{params.foo2}-#{params.foo3}-#{params.foo4}" }
41
+ end
42
+ end
43
+ end
44
+ expect(page.body[-60..-19]).to include('<div>bar1-no_bar2-bar3-no_bar4</div>')
45
+ end
46
+
47
+ it 'can specify validation rules with the type option' do
48
+ expect_evaluate_ruby do
49
+ class Foo < React::Component::Base
50
+ param :foo, type: String
51
+ end
52
+ Foo.prop_types
53
+ end.to have_key('_componentValidator')
54
+ end
55
+
56
+ it "can type check params" do
57
+ mount 'Foo', foo1: 12, foo2: "string" do
58
+ class Foo < React::Component::Base
59
+
60
+ param :foo1, type: String
61
+ param :foo2, type: String
62
+
63
+ def render
64
+ div { "#{params.foo1}-#{params.foo2}" }
65
+ end
66
+ end
67
+ end
68
+ expect(page.body[-60..-19]).to include('<div>12-string</div>')
69
+ expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
70
+ .to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `foo1` could not be converted to String/)
71
+ end
72
+
73
+ it 'logs error in warning if validation failed' do
74
+ evaluate_ruby do
75
+ class Lorem; end
76
+ class Foo2 < React::Component::Base
77
+ param :foo
78
+ param :lorem, type: Lorem
79
+ param :bar, default: nil, type: String
80
+ def render; div; end
81
+ end
82
+
83
+ React::Test::Utils.render_component_into_document(Foo2, bar: 10, lorem: Lorem.new)
84
+ end
85
+ expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
86
+ .to match(/Warning: Failed prop( type|Type): In component `Foo2`\nRequired prop `foo` was not specified\nProvided prop `bar` could not be converted to String/)
87
+ end
88
+
89
+ it 'should not log anything if validation passes' do
90
+ evaluate_ruby do
91
+ class Lorem; end
92
+ class Foo < React::Component::Base
93
+ param :foo
94
+ param :lorem, type: Lorem
95
+ param :bar, default: nil, type: String
96
+
97
+ def render; div; end
98
+ end
99
+ React::Test::Utils.render_component_into_document(Foo, foo: 10, bar: '10', lorem: Lorem.new)
100
+ end
101
+ expect(page.driver.browser.manage.logs.get(:browser).reject { |m| m.message =~ /(D|d)eprecated/ }.map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
102
+ .not_to match(/Warning|Error/)
103
+ end
104
+
105
+ describe 'advanced type handling' do
106
+ before(:each) do
107
+ on_client do
108
+ class Foo < React::Component::Base
109
+ def render; ""; end
110
+ end
111
+ end
112
+ end
113
+
114
+ it "can use the [] notation for arrays" do
115
+ mount 'Foo', foo: 10, bar: [10] do
116
+ Foo.class_eval do
117
+ param :foo, type: []
118
+ param :bar, type: []
119
+ end
120
+ end
121
+ expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
122
+ .to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `foo` could not be converted to Array/)
123
+ end
124
+
125
+ it "can use the [xxx] notation for arrays of a specific type" do
126
+ mount 'Foo', foo: [10], bar: ["10"] do
127
+ Foo.class_eval do
128
+ param :foo, type: [String]
129
+ param :bar, type: [String]
130
+ end
131
+ end
132
+ expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
133
+ .to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `foo`\[0\] could not be converted to String/)
134
+ end
135
+
136
+ it "can convert a json hash to a type" do
137
+ mount 'Foo', foo: "", bar: { bazwoggle: 1 }, baz: [{ bazwoggle: 2 }] do
138
+ class BazWoggle
139
+ def initialize(kind)
140
+ @kind = kind
141
+ end
142
+ attr_accessor :kind
143
+ def self._react_param_conversion(json, validate_only)
144
+ new(json[:bazwoggle]) if json[:bazwoggle]
145
+ end
146
+ end
147
+ Foo.class_eval do
148
+ param :foo, type: BazWoggle
149
+ param :bar, type: BazWoggle
150
+ param :baz, type: [BazWoggle]
151
+ def render
152
+ "#{params.bar.kind}, #{params.baz[0].kind}"
153
+ end
154
+ end
155
+ end
156
+ expect(page.body[-60..-19]).to include('<span>1, 2</span>')
157
+ expect(page.driver.browser.manage.logs.get(:browser).map { |m| m.message.gsub(/\\n/, "\n") }.to_a.join("\n"))
158
+ .to match(/Warning: Failed prop( type|Type): In component `Foo`\nProvided prop `foo` could not be converted to BazWoggle/)
159
+ end
160
+
161
+ describe "converts params only once" do
162
+ it "not on every access" do
163
+ mount 'Foo', foo: {bazwoggle: 1} do
164
+ class BazWoggle
165
+ def initialize(kind)
166
+ @kind = kind
167
+ end
168
+ attr_accessor :kind
169
+ def self._react_param_conversion(json, validate_only)
170
+ new(json[:bazwoggle]) if json[:bazwoggle]
171
+ end
172
+ end
173
+ Foo.class_eval do
174
+ param :foo, type: BazWoggle
175
+ def render
176
+ params.foo.kind = params.foo.kind+1
177
+ "#{params.foo.kind}"
178
+ end
179
+ end
180
+ end
181
+ expect(page.body[-60..-19]).to include('<span>2</span>')
182
+ end
183
+
184
+ it "even if contains an embedded native object"
185
+ # its not clear what this test was trying to accomplish...
186
+ # do
187
+ # pending 'Fix after merging'
188
+ # stub_const "Bar", Class.new(React::Component::Base)
189
+ # stub_const "BazWoggle", Class.new
190
+ # BazWoggle.class_eval do
191
+ # def initialize(kind)
192
+ # @kind = kind
193
+ # end
194
+ # attr_accessor :kind
195
+ # def self._react_param_conversion(json, validate_only)
196
+ # new(JSON.from_object(json[0])[:bazwoggle]) if JSON.from_object(json[0])[:bazwoggle]
197
+ # end
198
+ # end
199
+ # Bar.class_eval do
200
+ # param :foo, type: BazWoggle
201
+ # def render
202
+ # params.foo.kind.to_s
203
+ # end
204
+ # end
205
+ # Foo.class_eval do
206
+ # export_state :change_me
207
+ # before_mount do
208
+ # Foo.change_me! "initial"
209
+ # end
210
+ # def render
211
+ # Bar(foo: Native([`{bazwoggle: #{Foo.change_me}}`]))
212
+ # end
213
+ # end
214
+ # div = `document.createElement("div")`
215
+ # React.render(React.create_element(Foo, {}), div)
216
+ # Foo.change_me! "updated"
217
+ # expect(`div.children[0].innerHTML`).to eq("updated")
218
+ # end
219
+ end
220
+
221
+ it "will alias a Proc type param" do
222
+ evaluate_ruby do
223
+ Foo.class_eval do
224
+ param :foo, type: Proc
225
+ def render
226
+ params.foo
227
+ end
228
+ end
229
+ React::Test::Utils.render_component_into_document(Foo, foo: lambda { 'works!' })
230
+ end
231
+ expect(page.body[-60..-19]).to include('<span>works!</span>')
232
+ end
233
+
234
+ it "will create a 'bang' (i.e. update) method if the type is React::Observable" do
235
+ expect_evaluate_ruby do
236
+ Foo.class_eval do
237
+ param :foo, type: React::Observable
238
+ before_mount do
239
+ params.foo! "ha!"
240
+ end
241
+ def render
242
+ params.foo
243
+ end
244
+ end
245
+ current_state = ""
246
+ observer = React::Observable.new(current_state) { |new_state| current_state = new_state }
247
+ React::Test::Utils.render_component_into_document(Foo, foo: observer)
248
+ current_state
249
+ end.to eq("ha!")
250
+ expect(page.body[-60..-19]).to include('<span>ha!</span>')
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,278 @@
1
+ require "spec_helper"
2
+
3
+ describe 'React', js: true do
4
+ describe "is_valid_element?" do
5
+ it "should return true if passed a valid element" do
6
+
7
+ expect_evaluate_ruby do
8
+ element = React::Element.new(JS.call(:eval, "React.createElement('div')"))
9
+ React.is_valid_element?(element)
10
+ end.to eq(true)
11
+ end
12
+
13
+ it "should return false is passed a non React element" do
14
+
15
+ expect_evaluate_ruby do
16
+ element = React::Element.new(JS.call(:eval, "{}"))
17
+ React.is_valid_element?(element)
18
+ end.to eq(false)
19
+ end
20
+ end
21
+
22
+ describe "create_element" do
23
+ it "should create a valid element with only tag" do
24
+
25
+ expect_evaluate_ruby do
26
+ element = React.create_element('div')
27
+ React.is_valid_element?(element)
28
+ end.to eq(true)
29
+ end
30
+
31
+ context "with block" do
32
+ it "should create a valid element with text as only child when block yield String" do
33
+
34
+ evaluate_ruby do
35
+ ELEMENT = React.create_element('div') { "lorem ipsum" }
36
+ end
37
+ expect_evaluate_ruby("React.is_valid_element?(ELEMENT)").to eq(true)
38
+ expect_evaluate_ruby("ELEMENT.props.children").to eq("lorem ipsum")
39
+ end
40
+
41
+ it "should create a valid element with children as array when block yield Array of element" do
42
+
43
+ evaluate_ruby do
44
+ ELEMENT = React.create_element('div') do
45
+ [React.create_element('span'), React.create_element('span'), React.create_element('span')]
46
+ end
47
+ end
48
+ expect_evaluate_ruby("React.is_valid_element?(ELEMENT)").to eq(true)
49
+ expect_evaluate_ruby("ELEMENT.props.children.length").to eq(3)
50
+ end
51
+
52
+ it "should render element with children as array when block yield Array of element" do
53
+
54
+ expect_evaluate_ruby do
55
+ element = React.create_element('div') do
56
+ [React.create_element('span'), React.create_element('span'), React.create_element('span')]
57
+ end
58
+ dom_node = React::Test::Utils.render_into_document(element)
59
+ dom_node.JS[:children].JS[:length]
60
+ end.to eq(3)
61
+ end
62
+ end
63
+
64
+ describe "custom element" do
65
+ before :each do
66
+ on_client do
67
+ class Foo < React::Component::Base
68
+ def initialize(native)
69
+ @native = native
70
+ end
71
+
72
+ def render
73
+ React.create_element("div") { "lorem" }
74
+ end
75
+
76
+ def props
77
+ Hash.new(@native.JS[:props])
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ it "should render element with only one children correctly" do
84
+
85
+ evaluate_ruby do
86
+ element = React.create_element(Foo) { React.create_element('span') }
87
+ INSTANCE = React::Test::Utils.render_into_document(element)
88
+ true
89
+ end
90
+ expect_evaluate_ruby("INSTANCE.props[:children].is_a?(Array)").to be_falsy
91
+ expect_evaluate_ruby("INSTANCE.props[:children][:type]").to eq("span")
92
+ end
93
+
94
+ it "should render element with more than one children correctly" do
95
+
96
+ evaluate_ruby do
97
+ element = React.create_element(Foo) { [React.create_element('span'), React.create_element('span')] }
98
+ INSTANCE = React::Test::Utils.render_into_document(element)
99
+ true
100
+ end
101
+ expect_evaluate_ruby("INSTANCE.props[:children].is_a?(Array)").to be_truthy
102
+ expect_evaluate_ruby("INSTANCE.props[:children].length").to eq(2)
103
+ end
104
+
105
+ it "should create a valid element provided class defined `render`" do
106
+
107
+ expect_evaluate_ruby do
108
+ element = React.create_element(Foo)
109
+ React.is_valid_element?(element)
110
+ end.to eq(true)
111
+ end
112
+
113
+ it "should allow creating with properties" do
114
+ expect_evaluate_ruby do
115
+ Foo.class_eval do
116
+ param :foo
117
+ end
118
+ element = React.create_element(Foo, foo: "bar")
119
+ element.props.foo
120
+ end.to eq("bar")
121
+ end
122
+
123
+ it "should raise error if provided class doesn't defined `render`" do
124
+
125
+ expect_evaluate_ruby do
126
+ begin
127
+ React.create_element(Array)
128
+ rescue
129
+ 'failed'
130
+ end
131
+ end.to eq('failed')
132
+ end
133
+
134
+ it "should use the same instance for the same ReactComponent" do
135
+
136
+ mount 'Foo' do
137
+ Foo.class_eval do
138
+ attr_accessor :a
139
+
140
+ def initialize(n)
141
+ self.a = 10
142
+ end
143
+
144
+ def component_will_mount
145
+ self.a = 20
146
+ end
147
+
148
+ def render
149
+ React.create_element("div") { self.a.to_s }
150
+ end
151
+ end
152
+ end
153
+ expect(page.body[-60..-19]).to include("<div>20</div>")
154
+ end
155
+
156
+ it "should match the instance cycle to ReactComponent life cycle" do
157
+ expect_evaluate_ruby do
158
+ Foo.class_eval do
159
+ def initialize(native)
160
+ @@count ||= 0
161
+ @@count += 1
162
+ end
163
+ def render
164
+ React.create_element("div")
165
+ end
166
+ def self.count
167
+ @@count
168
+ end
169
+ end
170
+
171
+ React::Test::Utils.render_component_into_document(Foo)
172
+ React::Test::Utils.render_component_into_document(Foo)
173
+ Foo.count
174
+ end.to eq(2)
175
+ end
176
+ end
177
+
178
+ describe "create element with properties" do
179
+ it "should enforce snake-cased property name" do
180
+
181
+ expect_evaluate_ruby do
182
+ element = React.create_element("div", class_name: "foo")
183
+ element.props.className
184
+ end.to eq("foo")
185
+ end
186
+
187
+ it "should allow custom property" do
188
+
189
+ expect_evaluate_ruby do
190
+ element = React.create_element("div", foo: "bar")
191
+ element.props.foo
192
+ end.to eq("bar")
193
+ end
194
+
195
+ it "should not camel-case custom property" do
196
+
197
+ expect_evaluate_ruby do
198
+ element = React.create_element("div", foo_bar: "foo")
199
+ element.props.foo_bar
200
+ end.to eq("foo")
201
+ end
202
+ end
203
+
204
+ describe "class_name helpers (React.addons.classSet)" do
205
+ it "should not alter behavior when passing a string" do
206
+
207
+ expect_evaluate_ruby do
208
+ element = React.create_element("div", class_name: "foo bar")
209
+ element.props.className
210
+ end.to eq("foo bar")
211
+ end
212
+ end
213
+ end
214
+
215
+ describe "render" do
216
+ it "should render element to DOM" do # was async, don know how to handle
217
+
218
+ evaluate_ruby do
219
+ DIV = JS.call(:eval, 'document.createElement("div")')
220
+ React.render(React.create_element('span') { "lorem" }, DIV)
221
+ '' # make to_json happy
222
+ end
223
+ expect_evaluate_ruby("DIV.JS[:children].JS[0].JS[:tagName]").to eq("SPAN")
224
+ expect_evaluate_ruby("DIV.JS[:textContent]").to eq("lorem")
225
+ end
226
+
227
+ it "should work without providing a block" do
228
+ expect_evaluate_ruby do
229
+ begin
230
+ React::Test::Utils.render_into_document(React.create_element('span') { "lorem" })
231
+ true
232
+ rescue
233
+ false
234
+ end
235
+ end.to be_truthy
236
+ expect(page.body[-80..-10]).to include('<div><span>lorem</span></div>')
237
+ end
238
+
239
+ it "returns the actual ruby instance" do
240
+
241
+ expect_evaluate_ruby do
242
+ class Foo
243
+ def render
244
+ React.create_element("div") { "lorem" }
245
+ end
246
+ end
247
+
248
+ div = JS.call(:eval, 'document.createElement("div")')
249
+ instance = React.render(React.create_element(Foo), div)
250
+ instance.is_a?(Foo)
251
+ end.to be_truthy
252
+ end
253
+
254
+ it "returns the actual DOM node" do
255
+
256
+ expect_evaluate_ruby do
257
+ div = JS.call(:eval, 'document.createElement("div")')
258
+ node = React.render(React.create_element('span') { "lorem" }, div)
259
+ node.JS['nodeType']
260
+ end.to eq(1)
261
+ end
262
+ end
263
+
264
+ describe "unmount_component_at_node" do
265
+ it "should unmount component at node" do
266
+
267
+ # was run_async
268
+ # unmount was passed in a block run_async which passed in a block to React.render
269
+ # trying to emulate that failed, becasue during render, _getOpalInstance was not yet defined.
270
+ # it is defined only after render, when the component was mounted. So we call unmount after render
271
+ expect_evaluate_ruby do
272
+ div = JS.call(:eval, 'document.createElement("div")')
273
+ React.render(React.create_element('span') { "lorem" }, div )
274
+ React.unmount_component_at_node(div)
275
+ end.to eq(true)
276
+ end
277
+ end
278
+ end