hyper-react 0.99.6 → 1.0.0.lap21

Sign up to get free protection for your applications and to get access to all the features.
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,19 @@
1
+ module React
2
+ module Server
3
+ def self.render_to_string(element)
4
+ if !(`typeof ReactDOMServer === 'undefined'`)
5
+ React::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
6
+ else
7
+ raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
8
+ end
9
+ end
10
+
11
+ def self.render_to_static_markup(element)
12
+ if !(`typeof ReactDOMServer === 'undefined'`)
13
+ React::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
14
+ else
15
+ raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module HyperStore
2
+ class StateWrapper < BaseStoreClass # < BasicObject
3
+
4
+ def [](state)
5
+ `#{__from__.instance_variable_get('@native')}.state[#{state}] || #{nil}`
6
+ end
7
+
8
+ def []=(state, new_value)
9
+ `#{__from__.instance_variable_get('@native')}.state[#{state}] = new_value`
10
+ end
11
+
12
+ alias pre_component_method_missing method_missing
13
+
14
+ def method_missing(method, *args)
15
+ if method.end_with?('!') && __from__.respond_to?(:deprecation_warning)
16
+ __from__.deprecation_warning("The mutator 'state.#{method}' has been deprecated. Use 'mutate.#{method.sub(/\!$/,'')}' instead.")
17
+ __from__.mutate.__send__(method.chop, *args)
18
+ else
19
+ pre_component_method_missing(method, *args)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ require 'react/test/session'
2
+ require 'react/test/dsl'
3
+
4
+ module React
5
+ module Test
6
+ class << self
7
+ def current_session
8
+ @current_session ||= Session.new
9
+ end
10
+
11
+ def reset_session!
12
+ @current_session = nil
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'react/test'
2
+
3
+ module React
4
+ module Test
5
+ module DSL
6
+ def component
7
+ React::Test.current_session
8
+ end
9
+
10
+ Session::DSL_METHODS.each do |method|
11
+ define_method method do |*args, &block|
12
+ component.public_send(method, *args, &block)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,56 @@
1
+ module React
2
+ module Test
3
+ module Matchers
4
+ class RenderHTMLMatcher
5
+ def initialize(expected)
6
+ @expected = expected
7
+ @params = {}
8
+ end
9
+
10
+ def with_params(params)
11
+ @params = params
12
+ self
13
+ end
14
+
15
+ def matches?(component)
16
+ @component = component
17
+ @actual = render_to_html
18
+ @expected == @actual
19
+ end
20
+
21
+ def failure_message
22
+ failure_string
23
+ end
24
+
25
+ def failure_message_when_negated
26
+ failure_string(:negative)
27
+ end
28
+
29
+ alias negative_failure_message failure_message_when_negated
30
+
31
+ private
32
+
33
+ def render_to_html
34
+ element = React.create_element(@component, @params)
35
+ React::Server.render_to_static_markup(element)
36
+ end
37
+
38
+ def failure_string(negative = false)
39
+ str = "expected '#{@component.name}' with params '#{@params}' to "
40
+ str = str + "not " if negative
41
+ str = str + "render '#{@expected}', but '#{@actual}' was rendered."
42
+ str
43
+ end
44
+ end
45
+
46
+ def render_static_html(*args)
47
+ RenderHTMLMatcher.new(*args)
48
+ end
49
+
50
+ def render(*args)
51
+ %x{ console.error("Warning: `render` matcher is deprecated in favor of `render_static_html`."); }
52
+ RenderHTMLMatcher.new(*args)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,15 @@
1
+ require 'react/test/dsl'
2
+ require 'react/test/matchers/render_html_matcher'
3
+
4
+ RSpec.configure do |config|
5
+ config.include React::Test::DSL, type: :component
6
+ config.include React::Test::Matchers, type: :component
7
+
8
+ config.after do
9
+ React::Test.reset_session!
10
+ end
11
+
12
+ config.before do
13
+ # nothing yet
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ module React
2
+ module Test
3
+ class Session
4
+ DSL_METHODS = %i[mount instance update_params html].freeze
5
+
6
+ def mount(component_klass, params = {})
7
+ @element = React.create_element(component_klass, params)
8
+ instance
9
+ end
10
+
11
+ def instance
12
+ unless @instance
13
+ @container = `document.createElement('div')`
14
+ @instance = React.render(@element, @container)
15
+ end
16
+ @instance
17
+ end
18
+
19
+ def update_params(params, &block)
20
+ cloned_element = React::Element.new(`React.cloneElement(#{@element.to_n}, #{params.to_n})`)
21
+ React.render(cloned_element, @container, &block)
22
+ nil
23
+ end
24
+
25
+ def html
26
+ html = `#@container.innerHTML`
27
+ %x{
28
+ var REGEX_REMOVE_ROOT_IDS = /\s?data-reactroot="[^"]*"/g;
29
+ var REGEX_REMOVE_IDS = /\s?data-reactid="[^"]+"/g;
30
+ html = html.replace(REGEX_REMOVE_ROOT_IDS, '');
31
+ html = html.replace(REGEX_REMOVE_IDS, '');
32
+ }
33
+ return html
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,71 @@
1
+ module React
2
+ module Test
3
+ class Utils
4
+ def self.render_component_into_document(component, args = {})
5
+ element = React.create_element(component, args)
6
+ render_into_document(element)
7
+ end
8
+
9
+ def self.render_into_document(element)
10
+ raise "You should pass a valid React::Element" unless React.is_valid_element?(element)
11
+ dom_el = `document.body.querySelector('div[data-react-class="React.TopLevelRailsComponent"]').appendChild(document.createElement('div'))`
12
+ React.render(element, dom_el)
13
+ end
14
+
15
+ def self.simulate_click(element)
16
+ # element must be a component or a dom node or a element
17
+ el = if `typeof element.nodeType !== "undefined"`
18
+ element
19
+ elsif element.is_a? React::Component
20
+ element.dom_node
21
+ elsif element.is_a? React::Element
22
+ `ReactDOM.findDOMNode(#{element.to_n}.native)`
23
+ else
24
+ element
25
+ end
26
+ %x{
27
+ var evob = new MouseEvent('click', {
28
+ view: window,
29
+ bubbles: true,
30
+ cancelable: true
31
+ });
32
+ el.dispatchEvent(evob);
33
+ }
34
+ end
35
+
36
+ def self.simulate_keydown(element, key_name = "Enter")
37
+ # element must be a component or a dom node or a element
38
+ el = if `typeof element.nodeType !== "undefined"`
39
+ element
40
+ elsif element.is_a? React::Component
41
+ element.dom_node
42
+ elsif element.is_a? React::Element
43
+ `ReactDOM.findDOMNode(#{element.to_n}.native)`
44
+ else
45
+ element
46
+ end
47
+ %x{
48
+ var evob = new KeyboardEvent('keydown', { key: key_name, bubbles: true, cancelable: true });
49
+ el.dispatchEvent(evob);
50
+ }
51
+ end
52
+
53
+ def self.simulate_submit(element)
54
+ # element must be a component or a dom node or a element
55
+ el = if `typeof element.nodeType !== "undefined"`
56
+ element
57
+ elsif element.is_a? React::Component
58
+ element.dom_node
59
+ elsif element.is_a? React::Element
60
+ `ReactDOM.findDOMNode(#{element.to_n}.native)`
61
+ else
62
+ element
63
+ end
64
+ %x{
65
+ var evob = new Event('submit', { bubbles: true, cancelable: true });
66
+ el.dispatchEvent(evob);
67
+ }
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,110 @@
1
+ require "native"
2
+ require 'active_support/core_ext/object/try'
3
+ require 'react/component/tags'
4
+ require 'react/component/base'
5
+
6
+ module React
7
+
8
+ ATTRIBUTES = %w(accept acceptCharset accessKey action allowFullScreen allowTransparency alt
9
+ async autoComplete autoPlay cellPadding cellSpacing charSet checked classID
10
+ className cols colSpan content contentEditable contextMenu controls coords
11
+ crossOrigin data dateTime defer dir disabled download draggable encType form
12
+ formAction formEncType formMethod formNoValidate formTarget frameBorder height
13
+ hidden href hrefLang htmlFor httpEquiv icon id label lang list loop manifest
14
+ marginHeight marginWidth max maxLength media mediaGroup method min multiple
15
+ muted name noValidate open pattern placeholder poster preload radioGroup
16
+ readOnly rel required role rows rowSpan sandbox scope scrolling seamless
17
+ selected shape size sizes span spellCheck src srcDoc srcSet start step style
18
+ tabIndex target title type useMap value width wmode dangerouslySetInnerHTML) +
19
+ #SVG ATTRIBUTES
20
+ %w(clipPath cx cy d dx dy fill fillOpacity fontFamily
21
+ fontSize fx fy gradientTransform gradientUnits markerEnd
22
+ markerMid markerStart offset opacity patternContentUnits
23
+ patternUnits points preserveAspectRatio r rx ry spreadMethod
24
+ stopColor stopOpacity stroke strokeDasharray strokeLinecap
25
+ strokeOpacity strokeWidth textAnchor transform version
26
+ viewBox x1 x2 x xlinkActuate xlinkArcrole xlinkHref xlinkRole
27
+ xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace y1 y2 y)
28
+ HASH_ATTRIBUTES = %w(data aria)
29
+ HTML_TAGS = React::Component::Tags::HTML_TAGS
30
+
31
+ def self.html_tag?(name)
32
+ tags = HTML_TAGS
33
+ %x{
34
+ for(var i = 0; i < tags.length; i++) {
35
+ if(tags[i] === name)
36
+ return true;
37
+ }
38
+ return false;
39
+ }
40
+ end
41
+
42
+ def self.html_attr?(name)
43
+ attrs = ATTRIBUTES
44
+ %x{
45
+ for(var i = 0; i < attrs.length; i++) {
46
+ if(attrs[i] === name)
47
+ return true;
48
+ }
49
+ return false;
50
+ }
51
+ end
52
+
53
+ def self.create_element(type, properties = {}, &block)
54
+ React::API.create_element(type, properties, &block)
55
+ end
56
+
57
+ def self.render(element, container)
58
+ %x{
59
+ console.error(
60
+ "Warning: Using deprecated behavior of `React.render`,",
61
+ "require \"react/top_level_render\" to get the correct behavior."
62
+ );
63
+ }
64
+ container = `container.$$class ? container[0] : container`
65
+ if !(`typeof ReactDOM === 'undefined'`)
66
+ component = Native(`ReactDOM.render(#{element.to_n}, container, function(){#{yield if block_given?}})`) # v0.15+
67
+ else
68
+ raise "render is not defined. In React >= v15 you must import it with ReactDOM"
69
+ end
70
+
71
+ component.class.include(React::Component::API)
72
+ component
73
+ end
74
+
75
+ def self.is_valid_element(element)
76
+ %x{ console.error("Warning: `is_valid_element` is deprecated in favor of `is_valid_element?`."); }
77
+ element.kind_of?(React::Element) && `React.isValidElement(#{element.to_n})`
78
+ end
79
+
80
+ def self.is_valid_element?(element)
81
+ element.kind_of?(React::Element) && `React.isValidElement(#{element.to_n})`
82
+ end
83
+
84
+ def self.render_to_string(element)
85
+ %x{ console.error("Warning: `React.render_to_string` is deprecated in favor of `React::Server.render_to_string`."); }
86
+ if !(`typeof ReactDOMServer === 'undefined'`)
87
+ React::RenderingContext.build { `ReactDOMServer.renderToString(#{element.to_n})` } # v0.15+
88
+ else
89
+ raise "renderToString is not defined. In React >= v15 you must import it with ReactDOMServer"
90
+ end
91
+ end
92
+
93
+ def self.render_to_static_markup(element)
94
+ %x{ console.error("Warning: `React.render_to_static_markup` is deprecated in favor of `React::Server.render_to_static_markup`."); }
95
+ if !(`typeof ReactDOMServer === 'undefined'`)
96
+ React::RenderingContext.build { `ReactDOMServer.renderToStaticMarkup(#{element.to_n})` } # v0.15+
97
+ else
98
+ raise "renderToStaticMarkup is not defined. In React >= v15 you must import it with ReactDOMServer"
99
+ end
100
+ end
101
+
102
+ def self.unmount_component_at_node(node)
103
+ if !(`typeof ReactDOM === 'undefined'`)
104
+ `ReactDOM.unmountComponentAtNode(node.$$class ? node[0] : node)` # v0.15+
105
+ else
106
+ raise "unmountComponentAtNode is not defined. In React >= v15 you must import it with ReactDOM"
107
+ end
108
+ end
109
+
110
+ end
@@ -0,0 +1,28 @@
1
+ module React
2
+ def self.render(element, container)
3
+ raise "ReactDOM.render is not defined. In React >= v15 you must import it with ReactDOM" if (`typeof ReactDOM === 'undefined'`)
4
+
5
+ container = `container.$$class ? container[0] : container`
6
+
7
+ if block_given?
8
+ cb = %x{
9
+ function(){
10
+ setTimeout(function(){
11
+ #{yield}
12
+ }, 0)
13
+ }
14
+ }
15
+ native = `ReactDOM.render(#{element.to_n}, container, cb)`
16
+ else
17
+ native = `ReactDOM.render(#{element.to_n}, container)`
18
+ end
19
+
20
+ if `#{native}.__opalInstance !== undefined && #{native}.__opalInstance !== null`
21
+ `#{native}.__opalInstance`
22
+ elsif `ReactDOM.findDOMNode !== undefined && #{native}.nodeType === undefined`
23
+ `ReactDOM.findDOMNode(#{native})`
24
+ else
25
+ native
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,136 @@
1
+ module React
2
+ class Validator
3
+ attr_accessor :errors
4
+ attr_reader :props_wrapper
5
+ private :errors, :props_wrapper
6
+
7
+ def initialize(props_wrapper = Class.new(Component::PropsWrapper))
8
+ @props_wrapper = props_wrapper
9
+ end
10
+
11
+ def self.build(&block)
12
+ self.new.build(&block)
13
+ end
14
+
15
+ def build(&block)
16
+ instance_eval(&block)
17
+ self
18
+ end
19
+
20
+ def requires(name, options = {})
21
+ options[:required] = true
22
+ define_rule(name, options)
23
+ end
24
+
25
+ def optional(name, options = {})
26
+ options[:required] = false
27
+ define_rule(name, options)
28
+ end
29
+
30
+ def allow_undefined_props=(allow)
31
+ @allow_undefined_props = allow
32
+ end
33
+
34
+ def undefined_props(props)
35
+ self.allow_undefined_props = true
36
+ props.reject { |name, value| rules[name] }
37
+ end
38
+
39
+ def validate(props)
40
+ self.errors = []
41
+ validate_undefined(props) unless allow_undefined_props?
42
+ props = coerce_native_hash_values(defined_props(props))
43
+ validate_required(props)
44
+ props.each do |name, value|
45
+ validate_types(name, value)
46
+ validate_allowed(name, value)
47
+ end
48
+ errors
49
+ end
50
+
51
+ def default_props
52
+ rules
53
+ .select {|key, value| value.keys.include?("default") }
54
+ .inject({}) {|memo, (k,v)| memo[k] = v[:default]; memo}
55
+ end
56
+
57
+ private
58
+
59
+ def defined_props(props)
60
+ props.select { |name| rules.keys.include?(name) }
61
+ end
62
+
63
+ def allow_undefined_props?
64
+ !!@allow_undefined_props
65
+ end
66
+
67
+ def rules
68
+ @rules ||= { children: { required: false } }
69
+ end
70
+
71
+ def define_rule(name, options = {})
72
+ rules[name] = coerce_native_hash_values(options)
73
+ props_wrapper.define_param(name, options[:type])
74
+ end
75
+
76
+ def errors
77
+ @errors ||= []
78
+ end
79
+
80
+ def validate_types(prop_name, value)
81
+ return unless klass = rules[prop_name][:type]
82
+ if !klass.is_a?(Array)
83
+ allow_nil = !!rules[prop_name][:allow_nil]
84
+ type_check("`#{prop_name}`", value, klass, allow_nil)
85
+ elsif klass.length > 0
86
+ validate_value_array(prop_name, value)
87
+ else
88
+ allow_nil = !!rules[prop_name][:allow_nil]
89
+ type_check("`#{prop_name}`", value, Array, allow_nil)
90
+ end
91
+ end
92
+
93
+ def type_check(prop_name, value, klass, allow_nil)
94
+ return if allow_nil && value.nil?
95
+ return if value.is_a?(klass)
96
+ return if klass.respond_to?(:_react_param_conversion) &&
97
+ klass._react_param_conversion(value, :validate_only)
98
+ errors << "Provided prop #{prop_name} could not be converted to #{klass}"
99
+ end
100
+
101
+ def validate_allowed(prop_name, value)
102
+ return unless values = rules[prop_name][:values]
103
+ return if values.include?(value)
104
+ errors << "Value `#{value}` for prop `#{prop_name}` is not an allowed value"
105
+ end
106
+
107
+ def validate_required(props)
108
+ (rules.keys - props.keys).each do |name|
109
+ next unless rules[name][:required]
110
+ errors << "Required prop `#{name}` was not specified"
111
+ end
112
+ end
113
+
114
+ def validate_undefined(props)
115
+ (props.keys - rules.keys).each do |prop_name|
116
+ errors << "Provided prop `#{prop_name}` not specified in spec"
117
+ end
118
+ end
119
+
120
+ def validate_value_array(name, value)
121
+ klass = rules[name][:type]
122
+ allow_nil = !!rules[name][:allow_nil]
123
+ value.each_with_index do |item, index|
124
+ type_check("`#{name}`[#{index}]", Native(item), klass[0], allow_nil)
125
+ end
126
+ rescue NoMethodError
127
+ errors << "Provided prop `#{name}` was not an Array"
128
+ end
129
+
130
+ def coerce_native_hash_values(hash)
131
+ hash.each do |key, value|
132
+ hash[key] = Native(value)
133
+ end
134
+ end
135
+ end
136
+ end