From 82efd0b5c9118d808e3b6f9f8a636b655fa6824d Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 22 Jul 2018 19:11:33 -1000 Subject: [PATCH] Add random dom id option (#1121) * Added configuration option random_dom_id * Added method RenderOptions has_random_dom_id? This new global and react_component helper option allows configuring whether or not React on Rails will automatically add a random id to the DOM node ID. --- docs/api/view-helpers-api.md | 15 + docs/basics/configuration.md | 9 + lib/react_on_rails/configuration.rb | 9 +- lib/react_on_rails/helper.rb | 1 + .../react_component/render_options.rb | 26 +- spec/dummy/Gemfile.lock | 4 +- .../config/initializers/react_on_rails.rb | 1 + .../helpers/react_on_rails_helper_spec.rb | 70 ++- spec/dummy/yarn.lock | 403 ------------------ .../config/initializers/react_on_rails.rb | 1 + spec/react_on_rails/configuration_spec.rb | 9 + .../react_component/render_options_spec.rb | 32 +- 12 files changed, 154 insertions(+), 426 deletions(-) diff --git a/docs/api/view-helpers-api.md b/docs/api/view-helpers-api.md index bc30ed0fa..f4f36e857 100644 --- a/docs/api/view-helpers-api.md +++ b/docs/api/view-helpers-api.md @@ -3,6 +3,8 @@ Once the bundled files have been generated in your `app/assets/webpack` folder and you have registered your components, you will want to render these components on your Rails views using the included helper method, `react_component`. +------------ + ### react_component ```ruby @@ -24,11 +26,13 @@ react_component(component_name, - **id:** Id for the div, will be used to attach the React component. This will get assigned automatically if you do not provide an id. Must be unique. - **html_options:** Any other HTML options get placed on the added div for the component. For example, you can set a class (or inline style) on the outer div so that it behaves like a span, with the styling of `display:inline-block`. - **trace:** set to true to print additional debugging information in the browser. Defaults to true for development, off otherwise. Only on the **client side** will you will see the `railsContext` and your props. + - **random_dom_id:** True to automatically generate random dom ids when using multiple instances of the same React component on one Rails view. - **options if prerender (server rendering) is true:** - **replay_console:** Default is true. False will disable echoing server-rendering logs to the browser. While this can make troubleshooting server rendering difficult, so long as you have the configuration of `logging_on_server` set to true, you'll still see the errors on the server. - **logging_on_server:** Default is true. True will log JS console messages and errors to the server. - **raise_on_prerender_error:** Default is false. True will throw an error on the server side rendering. Your controller will have to handle the error. +------------- ### react_component_hash @@ -68,6 +72,8 @@ export default (props, _railsContext) => { ``` +------------ + ### cached_react_component and cached_react_component_hash Fragment caching is a [React on Rails Pro](https://github.com/shakacode/react_on_rails/wiki) feature. The API is the same as the above, but for 2 differences: @@ -79,10 +85,13 @@ Fragment caching is a [React on Rails Pro](https://github.com/shakacode/react_on some_slow_method_that_returns_props end %> ``` +------------ + ### rails_context You can call `rails_context(server_side: true | false)` from your controller or view to see what values are are in the Rails Context. Pass true or false depending on whether you want to see the server side or the client side rails_context. +------------ ### Renderer Functions (function that will call ReactDOM.render or ReactDOM.hydrate) @@ -92,6 +101,8 @@ Why would you want to call `ReactDOM.hydrate` yourself? One possible use case is Renderer functions are not meant to be used on the server since there's no DOM on the server. Instead, use a generator function. Attempting to server render with a renderer function will throw an error. +------------ + ### React Router [React Router](https://github.com/reactjs/react-router) is supported, including server-side rendering! See: @@ -100,6 +111,8 @@ Renderer functions are not meant to be used on the server since there's no DOM o 2. Examples in [spec/dummy/app/views/react_router](../../spec/dummy/app/views/react_router) and follow to the JavaScript code in the [spec/dummy/client/app/startup/ServerRouterApp.jsx](../../spec/dummy/client/app/startup/ServerRouterApp.jsx). 3. [Code Splitting docs](../misc-pending/code-splitting.md) for information about how to set up code splitting for server rendered routes. +------------ + ## server_render_js `server_render_js(js_expression, options = {})` @@ -109,6 +122,8 @@ Renderer functions are not meant to be used on the server since there's no DOM o This is a helper method that takes any JavaScript expression and returns the output from evaluating it. If you have more than one line that needs to be executed, wrap it in an IIFE. JS exceptions will be caught and console messages handled properly. +------------ + # More details See the [lib/react_on_rails/helper.rb](../../lib/react_on_rails/helper.rb) source. diff --git a/docs/basics/configuration.md b/docs/basics/configuration.md index dc8569ded..f1631799c 100644 --- a/docs/basics/configuration.md +++ b/docs/basics/configuration.md @@ -12,6 +12,15 @@ ReactOnRails.configure do |config| # setInterval, clearTimout when server rendering. config.trace = Rails.env.development? + # Configure if default DOM IDs have a random value or are fixed. + # false ==> Sets the dom id to "#{react_component_name}-react-component" + # true ==> Adds "-#{SecureRandom.uuid}" to that ID + # If you might use multiple instances of the same React component on a Rails page, then + # it is convenient to set this to true or else you have to either manually set the ids to + # avoid collisions. Most newer apps will have only one instance of a component on a page, + # so this should be false in most cases. + # This value can be overrident for a given call to react_component + config.random_dom_id = false # default is true # defaults to "" (top level) # diff --git a/lib/react_on_rails/configuration.rb b/lib/react_on_rails/configuration.rb index 7185355a2..c4ff19ef3 100644 --- a/lib/react_on_rails/configuration.rb +++ b/lib/react_on_rails/configuration.rb @@ -9,6 +9,7 @@ def self.configure DEFAULT_GENERATED_ASSETS_DIR = File.join(%w[public webpack], Rails.env).freeze DEFAULT_SERVER_RENDER_TIMEOUT = 20 DEFAULT_POOL_SIZE = 1 + DEFAULT_RANDOM_DOM_ID = TRUE # for backwards compatability def self.configuration @configuration ||= Configuration.new( @@ -32,7 +33,8 @@ def self.configuration server_render_method: nil, symlink_non_digested_assets_regex: nil, build_test_command: "", - build_production_command: "" + build_production_command: "", + random_dom_id: DEFAULT_RANDOM_DOM_ID ) end @@ -45,7 +47,7 @@ class Configuration :webpack_generated_files, :rendering_extension, :build_test_command, :build_production_command, :i18n_dir, :i18n_yml_dir, - :server_render_method, :symlink_non_digested_assets_regex + :server_render_method, :symlink_non_digested_assets_regex, :random_dom_id def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender: nil, replay_console: nil, @@ -56,7 +58,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender generated_assets_dir: nil, webpack_generated_files: nil, rendering_extension: nil, build_test_command: nil, build_production_command: nil, - i18n_dir: nil, i18n_yml_dir: nil, + i18n_dir: nil, i18n_yml_dir: nil, random_dom_id: nil, server_render_method: nil, symlink_non_digested_assets_regex: nil) self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root self.server_bundle_js_file = server_bundle_js_file @@ -67,6 +69,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender self.i18n_dir = i18n_dir self.i18n_yml_dir = i18n_yml_dir + self.random_dom_id = random_dom_id self.prerender = prerender self.replay_console = replay_console self.logging_on_server = logging_on_server diff --git a/lib/react_on_rails/helper.rb b/lib/react_on_rails/helper.rb index aa76a783a..6d6dba66f 100644 --- a/lib/react_on_rails/helper.rb +++ b/lib/react_on_rails/helper.rb @@ -98,6 +98,7 @@ def env_stylesheet_link_tag(args = {}) # raise_on_prerender_error: Default to false. True will raise exception on server # if the JS code throws # Any other options are passed to the content tag, including the id. + # random_dom_id can be set to override the global default. def react_component(component_name, options = {}) internal_result = internal_react_component(component_name, options) server_rendered_html = internal_result[:result]["html"] diff --git a/lib/react_on_rails/react_component/render_options.rb b/lib/react_on_rails/react_component/render_options.rb index 06d7144d5..a2a1b49ca 100644 --- a/lib/react_on_rails/react_component/render_options.rb +++ b/lib/react_on_rails/react_component/render_options.rb @@ -22,8 +22,26 @@ def props options.fetch(:props) { NO_PROPS } end + def random_dom_id + retrieve_key(:random_dom_id) + end + def dom_id - @dom_id ||= options.fetch(:id) { generate_unique_dom_id } + @dom_id ||= options.fetch(:id) do + if random_dom_id + generate_unique_dom_id + else + base_dom_id + end + end + end + + def has_random_dom_id? + return false if options[:id] + + return false unless random_dom_id + + true end def html_options @@ -58,8 +76,12 @@ def to_s attr_reader :options + def base_dom_id + "#{react_component_name}-react-component" + end + def generate_unique_dom_id - "#{react_component_name}-react-component-#{SecureRandom.uuid}" + "#{base_dom_id}-#{SecureRandom.uuid}" end def retrieve_key(key) diff --git a/spec/dummy/Gemfile.lock b/spec/dummy/Gemfile.lock index cf3d3756f..d6cf98434 100644 --- a/spec/dummy/Gemfile.lock +++ b/spec/dummy/Gemfile.lock @@ -7,7 +7,7 @@ GIT PATH remote: ../.. specs: - react_on_rails (11.0.9) + react_on_rails (11.0.10) addressable connection_pool execjs (~> 2.5) @@ -352,4 +352,4 @@ DEPENDENCIES webpacker BUNDLED WITH - 1.16.2 + 1.16.3 diff --git a/spec/dummy/config/initializers/react_on_rails.rb b/spec/dummy/config/initializers/react_on_rails.rb index 223976d89..106e7f542 100644 --- a/spec/dummy/config/initializers/react_on_rails.rb +++ b/spec/dummy/config/initializers/react_on_rails.rb @@ -16,6 +16,7 @@ def self.custom_context(view_context) end ReactOnRails.configure do |config| + config.random_dom_id = false # default is true config.node_modules_location = "client" # Pre 9.0.0 always used "client" config.build_production_command = "yarn run build:production" config.build_test_command = "yarn run build:test" diff --git a/spec/dummy/spec/helpers/react_on_rails_helper_spec.rb b/spec/dummy/spec/helpers/react_on_rails_helper_spec.rb index 7f3345030..1c9145c4e 100644 --- a/spec/dummy/spec/helpers/react_on_rails_helper_spec.rb +++ b/spec/dummy/spec/helpers/react_on_rails_helper_spec.rb @@ -87,23 +87,34 @@ { name: "My Test Name" } end - let(:react_component_div) do + let(:react_component_random_id_div) do '
' end + let(:react_component_div) do + '
' + end + let(:id) { "App-react-component-0" } - let(:react_definition_script) do + let(:react_definition_script_random) do <<-SCRIPT.strip_heredoc SCRIPT end + let(:react_definition_script) do + <<-SCRIPT.strip_heredoc + + SCRIPT + end + let(:react_definition_script_no_params) do <<-SCRIPT.strip_heredoc + data-component-name="App" data-dom-id="App-react-component">{} SCRIPT end @@ -121,7 +132,7 @@ it { is_expected.to include json_props_sanitized } end - describe "API with component name only" do + describe "API with component name only (no props or other options)" do subject { react_component("App") } it { is_expected.to be_an_instance_of ActiveSupport::SafeBuffer } it { is_expected.to include react_component_div } @@ -140,6 +151,51 @@ expect(is_expected.target).to script_tag_be_included(react_definition_script) } + context "with 'random_dom_id' false option" do + subject { react_component("App", props: props, random_dom_id: false) } + + let(:react_definition_script) do + <<-SCRIPT.strip_heredoc + + SCRIPT + end + + it { is_expected.to include '
' } + it { expect(is_expected.target).to script_tag_be_included(react_definition_script) } + end + + context "with 'random_dom_id' false option" do + subject { react_component("App", props: props, random_dom_id: true) } + + let(:react_definition_script) do + <<-SCRIPT.strip_heredoc + + SCRIPT + end + + it { is_expected.to include '
' } + it { expect(is_expected.target).to script_tag_be_included(react_definition_script) } + end + + context "with 'random_dom_id' global" do + around(:example) do |example| + ReactOnRails.configure { |config| config.random_dom_id = false } + example.run + ReactOnRails.configure { |config| config.random_dom_id = true } + end + + subject { react_component("App", props: props) } + + let(:react_definition_script) do + <<-SCRIPT.strip_heredoc + + SCRIPT + end + + it { is_expected.to include '
' } + it { expect(is_expected.target).to script_tag_be_included(react_definition_script) } + end + context "with 'id' option" do subject { react_component("App", props: props, id: id) } @@ -152,7 +208,7 @@ end it { is_expected.to include id } - it { is_expected.not_to include react_component_div } + it { is_expected.not_to include react_component_random_id_div } it { expect(is_expected.target).to script_tag_be_included(react_definition_script) } @@ -217,6 +273,8 @@ ReactOnRails.configuration.rendering_extension = nil end + after { ReactOnRails.configuration.rendering_extension = @rendering_extension } + it "should not throw an error if not in a view" do class PlainClass include ReactOnRailsHelper @@ -226,7 +284,5 @@ class PlainClass expect { ob.send(:rails_context, server_side: true) }.to_not raise_error expect { ob.send(:rails_context, server_side: false) }.to_not raise_error end - - after { ReactOnRails.configuration.rendering_extension = @rendering_extension } end end diff --git a/spec/dummy/yarn.lock b/spec/dummy/yarn.lock index 0eda22335..fb57ccd13 100644 --- a/spec/dummy/yarn.lock +++ b/spec/dummy/yarn.lock @@ -2,406 +2,3 @@ # yarn lockfile v1 -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -chownr@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -debug@^2.1.2: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - dependencies: - ms "2.0.0" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - -fs-minipass@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" - dependencies: - minipass "^2.2.1" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fsevents@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" - dependencies: - nan "^2.9.2" - node-pre-gyp "^0.10.0" - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -glob@^7.0.5: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - -iconv-lite@^0.4.4: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - dependencies: - minimatch "^3.0.4" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -ini@~1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -minipass@^2.2.1, minipass@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" - dependencies: - minipass "^2.2.1" - -mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - -nan@^2.9.2: - version "2.10.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" - -needle@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" - dependencies: - debug "^2.1.2" - iconv-lite "^0.4.4" - sax "^1.2.4" - -node-pre-gyp@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz#6e4ef5bb5c5203c6552448828c852c40111aac46" - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.0" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.1.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - dependencies: - abbrev "1" - osenv "^0.1.4" - -npm-bundled@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" - -npm-packlist@^1.1.6: - version "1.1.10" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.10.tgz#1039db9e985727e464df066f4cf0ab6ef85c398a" - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - -rc@^1.1.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -readable-stream@^2.0.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -rimraf@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - dependencies: - glob "^7.0.5" - -safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - -semver@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - -set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - -tar@^4: - version "4.4.4" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd" - dependencies: - chownr "^1.0.1" - fs-minipass "^1.2.5" - minipass "^2.3.3" - minizlib "^1.1.0" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.2" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - dependencies: - string-width "^1.0.2 || 2" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -yallist@^3.0.0, yallist@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" diff --git a/spec/dummy_no_webpacker/config/initializers/react_on_rails.rb b/spec/dummy_no_webpacker/config/initializers/react_on_rails.rb index 6829d164c..29db837c9 100644 --- a/spec/dummy_no_webpacker/config/initializers/react_on_rails.rb +++ b/spec/dummy_no_webpacker/config/initializers/react_on_rails.rb @@ -15,6 +15,7 @@ def self.custom_context(view_context) end ReactOnRails.configure do |config| + config.random_dom_id = false # default is true config.node_modules_location = "client" # Pre 9.0.0 always used "client" config.build_production_command = "yarn run build:production" config.build_test_command = "yarn run build:test" diff --git a/spec/react_on_rails/configuration_spec.rb b/spec/react_on_rails/configuration_spec.rb index 7358925e0..b7f267744 100644 --- a/spec/react_on_rails/configuration_spec.rb +++ b/spec/react_on_rails/configuration_spec.rb @@ -143,10 +143,19 @@ module ReactOnRails ReactOnRails.configure do |config| config.server_bundle_js_file = "something.js" config.prerender = true + config.random_dom_id = false end expect(ReactOnRails.configuration.server_bundle_js_file).to eq("something.js") expect(ReactOnRails.configuration.prerender).to eq(true) + expect(ReactOnRails.configuration.random_dom_id).to eq(false) + end + + it "has a default configuration of the gem" do + ReactOnRails.configure do |_config| + end + + expect(ReactOnRails.configuration.random_dom_id).to eq(true) end end end diff --git a/spec/react_on_rails/react_component/render_options_spec.rb b/spec/react_on_rails/react_component/render_options_spec.rb index dc512a564..ff96b31f7 100644 --- a/spec/react_on_rails/react_component/render_options_spec.rb +++ b/spec/react_on_rails/react_component/render_options_spec.rb @@ -8,6 +8,7 @@ trace replay_console raise_on_prerender_error + random_dom_id ].freeze def the_attrs(react_component_name: "App", options: {}) @@ -61,18 +62,30 @@ def the_attrs(react_component_name: "App", options: {}) describe "#dom_id" do context "without id option" do - it "returns a unique identifier" do - attrs = the_attrs(react_component_name: "SomeApp") - opts = described_class.new(attrs) + context "with random_dom_id set to true" do + it "returns a unique identifier" do + attrs = the_attrs(react_component_name: "SomeApp", options: { random_dom_id: true }) + opts = described_class.new(attrs) - expect(SecureRandom).to receive(:uuid).and_return("123456789") - expect(opts.dom_id).to eq "SomeApp-react-component-123456789" - end + expect(SecureRandom).to receive(:uuid).and_return("123456789") + expect(opts.dom_id).to eq "SomeApp-react-component-123456789" + expect(opts.has_random_dom_id?).to eq(true) + end - it "is memoized" do - opts = described_class.new(the_attrs) + it "is memoized" do + opts = described_class.new(the_attrs) - expect(opts.dom_id).to eq opts.dom_id + expect(opts.dom_id).to eq opts.dom_id + end + end + + context "with random_dom_id set to false" do + it "returns a default identifier" do + attrs = the_attrs(react_component_name: "SomeApp", options: { random_dom_id: false}) + opts = described_class.new(attrs) + expect(opts.dom_id).to eq "SomeApp-react-component" + expect(opts.has_random_dom_id?).to eq(false) + end end end @@ -84,6 +97,7 @@ def the_attrs(react_component_name: "App", options: {}) opts = described_class.new(attrs) expect(opts.dom_id).to eq "im-an-id" + expect(opts.has_random_dom_id?).to eq(false) end end end