Skip to content

Commit

Permalink
should force load react-components which send over turbo-stream (#1620)
Browse files Browse the repository at this point in the history
* should force load react-components which send over turbo-stream
* fix navigation error in spec
  • Loading branch information
theforestvn88 authored Aug 20, 2024
1 parent 4cdc686 commit 56ee2bd
Show file tree
Hide file tree
Showing 20 changed files with 158 additions and 6 deletions.
2 changes: 2 additions & 0 deletions Gemfile.development_dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ gem "sprockets", "~> 4.0"

gem "amazing_print"

gem "turbo-rails"

group :development, :test do
gem "package_json"
gem "listen"
Expand Down
7 changes: 6 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
react_on_rails (14.0.3)
react_on_rails (14.0.4)
addressable
connection_pool
execjs (~> 2.5)
Expand Down Expand Up @@ -369,6 +369,10 @@ GEM
tins (1.33.0)
bigdecimal
sync
turbo-rails (2.0.6)
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
Expand Down Expand Up @@ -431,6 +435,7 @@ DEPENDENCIES
spring (~> 4.0)
sprockets (~> 4.0)
sqlite3 (~> 1.6)
turbo-rails
turbolinks
uglifier
webdrivers (= 5.3.0)
Expand Down
10 changes: 7 additions & 3 deletions lib/react_on_rails/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ def self.configuration
i18n_output_format: nil,
components_subdirectory: nil,
make_generated_server_bundle_the_entrypoint: false,
defer_generated_component_packs: true
defer_generated_component_packs: true,
# forces the loading of React components
force_load: false
)
end

Expand All @@ -53,7 +55,8 @@ class Configuration
:server_render_method, :random_dom_id, :auto_load_bundle,
:same_bundle_for_client_and_server, :rendering_props_extension,
:make_generated_server_bundle_the_entrypoint,
:defer_generated_component_packs
:defer_generated_component_packs,
:force_load

# rubocop:disable Metrics/AbcSize
def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender: nil,
Expand All @@ -68,7 +71,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
same_bundle_for_client_and_server: nil,
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil,
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
components_subdirectory: nil, auto_load_bundle: nil)
components_subdirectory: nil, auto_load_bundle: nil, force_load: nil)
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
self.generated_assets_dirs = generated_assets_dirs
self.generated_assets_dir = generated_assets_dir
Expand Down Expand Up @@ -106,6 +109,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
self.auto_load_bundle = auto_load_bundle
self.make_generated_server_bundle_the_entrypoint = make_generated_server_bundle_the_entrypoint
self.defer_generated_component_packs = defer_generated_component_packs
self.force_load = force_load
end
# rubocop:enable Metrics/AbcSize

Expand Down
8 changes: 8 additions & 0 deletions lib/react_on_rails/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,14 @@ def internal_react_component(react_component_name, options = {})
"data-trace" => (render_options.trace ? true : nil),
"data-dom-id" => render_options.dom_id)

if render_options.force_load
component_specification_tag.concat(
content_tag(:script, %(
ReactOnRails.reactOnRailsComponentLoaded('#{render_options.dom_id}');
).html_safe)
)
end

load_pack_for_generated_component(react_component_name, render_options)
# Create the HTML rendering part
result = server_rendered_react_component(render_options)
Expand Down
4 changes: 4 additions & 0 deletions lib/react_on_rails/react_component/render_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ def logging_on_server
retrieve_configuration_value_for(:logging_on_server)
end

def force_load
retrieve_configuration_value_for(:force_load)
end

def to_s
"{ react_component_name = #{react_component_name}, options = #{options}, request_digest = #{request_digest}"
end
Expand Down
4 changes: 4 additions & 0 deletions node_package/src/ReactOnRails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ ctx.ReactOnRails = {
ClientStartup.reactOnRailsPageLoaded();
},

reactOnRailsComponentLoaded(domId: string): void {
ClientStartup.reactOnRailsComponentLoaded(domId);
},

/**
* Returns CSRF authenticity token inserted by Rails csrf_meta_tags
* @returns String or null
Expand Down
19 changes: 19 additions & 0 deletions node_package/src/clientStartup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,25 @@ export function reactOnRailsPageLoaded(): void {
forEachReactOnRailsComponentRender(context, railsContext);
}

export function reactOnRailsComponentLoaded(domId: string): void {
debugTurbolinks(`reactOnRailsComponentLoaded ${domId}`);

const railsContext = parseRailsContext();

// If no react on rails components
if (!railsContext) return;

const context = findContext();
if (supportsRootApi) {
context.roots = [];
}

const el = document.querySelector(`[data-dom-id=${domId}]`);
if (!el) return;

render(el, context, railsContext);
}

function unmount(el: Element): void {
const domNodeId = domNodeIdForEl(el);
const domNode = document.getElementById(domNodeId);
Expand Down
1 change: 1 addition & 0 deletions node_package/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export interface ReactOnRails {
setOptions(newOptions: {traceTurbolinks: boolean}): void;
reactHydrateOrRender(domNode: Element, reactElement: ReactElement, hydrate: boolean): RenderReturnType;
reactOnRailsPageLoaded(): void;
reactOnRailsComponentLoaded(domId: string): void;
authenticityToken(): string | null;
authenticityHeaders(otherHeaders: { [id: string]: string }): AuthenticityHeaders;
option(key: string): string | number | boolean | undefined;
Expand Down
5 changes: 5 additions & 0 deletions spec/dummy/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ GEM
timeout (0.4.1)
tins (1.32.1)
sync
turbo-rails (2.0.6)
actionpack (>= 6.0.0)
activejob (>= 6.0.0)
railties (>= 6.0.0)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
Expand Down Expand Up @@ -423,6 +427,7 @@ DEPENDENCIES
spring (~> 4.0)
sprockets (~> 4.0)
sqlite3 (~> 1.6)
turbo-rails
turbolinks
uglifier
webdrivers (= 5.3.0)
Expand Down
6 changes: 6 additions & 0 deletions spec/dummy/app/controllers/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ def data
}.merge(xss_payload)
}

@app_props_hello_from_turbo_stream = {
helloTurboStreamData: {
name: "Mrs. Client Side Rendering From Turbo Stream"
}.merge(xss_payload)
}

@app_props_hello_again = {
helloWorldData: {
name: "Mrs. Client Side Hello Again"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= turbo_frame_tag 'hello-turbo-stream' do %>
<%= button_to "send me hello-turbo-stream component", turbo_stream_send_hello_world_path %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= turbo_stream.update 'hello-turbo-stream' do %>
<%= react_component("HelloTurboStream", props: @app_props_hello_from_turbo_stream, force_load: true) %>
<% end %>
7 changes: 7 additions & 0 deletions spec/dummy/client/app/packs/client-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ import 'core-js/stable';
import 'regenerator-runtime/runtime';
import 'jquery';
import 'jquery-ujs';
import '@hotwired/turbo-rails';

import ReactOnRails from 'react-on-rails';

import HelloTurboStream from '../startup/HelloTurboStream';
import SharedReduxStore from '../stores/SharedReduxStore';

ReactOnRails.setOptions({
traceTurbolinks: true,
turbo: true,
});

ReactOnRails.register({
HelloTurboStream,
});

ReactOnRails.registerStore({
Expand Down
30 changes: 30 additions & 0 deletions spec/dummy/client/app/startup/HelloTurboStream.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import PropTypes from 'prop-types';
import React, { useState, useRef } from 'react';
import RailsContext from '../components/RailsContext';

import css from '../components/HelloWorld.module.scss';

const HelloTurboStream = ({ helloTurboStreamData, railsContext }) => {
const [name, setName] = useState(helloTurboStreamData.name);
const nameDomRef = useRef(null);

const handleChange = () => {
setName(nameDomRef.current.value);
};

return (
<div>
<h3 className={css.brightColor}>Hello, {name}!</h3>
{railsContext && <RailsContext {...{ railsContext }} />}
</div>
);
};

HelloTurboStream.propTypes = {
helloTurboStreamData: PropTypes.shape({
name: PropTypes.string,
}).isRequired,
railsContext: PropTypes.object,
};

export default HelloTurboStream;
2 changes: 2 additions & 0 deletions spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@
get "image_example" => "pages#image_example"
get "context_function_return_jsx" => "pages#context_function_return_jsx"
get "pure_component_wrapped_in_function" => "pages#pure_component_wrapped_in_function"
get "turbo_frame_tag_hello_world" => "pages#turbo_frame_tag_hello_world"
post "turbo_stream_send_hello_world" => "pages#turbo_stream_send_hello_world"
end
1 change: 1 addition & 0 deletions spec/dummy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@babel/preset-env": "7",
"@babel/preset-react": "^7.10.4",
"@babel/runtime": "7.17.9",
"@hotwired/turbo-rails": "^8.0.4",
"@rescript/react": "^0.10.3",
"babel-loader": "8.2.4",
"babel-plugin-macros": "^3.1.0",
Expand Down
20 changes: 20 additions & 0 deletions spec/dummy/spec/helpers/react_on_rails_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,26 @@ class PlainReactOnRailsHelper
it { is_expected.not_to include '<span id="App-react-component-0"></span>' }
it { is_expected.to include '<div id="App-react-component-0"></div>' }
end

describe "'force_load' tag option" do
let(:force_load_script) do
%(
ReactOnRails.reactOnRailsComponentLoaded('App-react-component-0');
).html_safe
end

context "with 'force_load' == true" do
subject { react_component("App", force_load: true) }

it { is_expected.to include force_load_script }
end

context "without 'force_load' tag option" do
subject { react_component("App") }

it { is_expected.not_to include force_load_script }
end
end
end

describe "#redux_store" do
Expand Down
13 changes: 11 additions & 2 deletions spec/dummy/spec/system/integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ def finished_all_ajax_requests?
end
end

describe "TurboStream send react component", :js do
subject { page }

it "force load hello-world component immediately" do
visit "/turbo_frame_tag_hello_world"
click_on "send me hello-turbo-stream component"
expect(page).to have_text "Hello, Mrs. Client Side Rendering From Turbo Stream!"
end
end

describe "Pages/client_side_log_throw", :ignore_js_errors, :js do
subject { page }

Expand Down Expand Up @@ -163,8 +173,7 @@ def finished_all_ajax_requests?
subject { page }

before do
visit "/"
click_on "React Router"
visit "/react_router"
end

context "when rendering /react_router" do
Expand Down
18 changes: 18 additions & 0 deletions spec/dummy/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2056,6 +2056,19 @@
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
integrity sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==

"@hotwired/turbo-rails@^8.0.4":
version "8.0.5"
resolved "https://registry.yarnpkg.com/@hotwired/turbo-rails/-/turbo-rails-8.0.5.tgz#18c2f0e4f7f952307650308590edf5eb9544b0d3"
integrity sha512-1A9G9u28IRAl0C57z8Ka3AhNPyJdwfOrbjr+ABZk2ZEUw2QO7cJ0pgs77asUj2E/tzn1PgrxrSVu24W+1Q5uBA==
dependencies:
"@hotwired/turbo" "^8.0.5"
"@rails/actioncable" "^7.0"

"@hotwired/turbo@^8.0.5":
version "8.0.5"
resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-8.0.5.tgz#abae6dad018a891e4286e87fa0959217e3866d5a"
integrity sha512-TdZDA7fxVQ2ZycygvpnzjGPmFq4sO/E2QVg+2em/sJ3YTSsIWVEis8HmWlumz+c9DjWcUkcCuB+muF08TInpAQ==

"@jridgewell/gen-mapping@^0.1.0":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
Expand Down Expand Up @@ -2116,6 +2129,11 @@
schema-utils "^3.0.0"
source-map "^0.7.3"

"@rails/actioncable@^7.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-7.2.0.tgz#dee66d21bc125a9819dc8080ce896eac78d8c63f"
integrity sha512-crcsPF3skrqJkFZLxesZoyUEt8ol25XtTuOAUMdLa5qQKWTZpL8eLVW71bDCwKDQLbV2z5sBZ/XGEC0i+ZZa+A==

"@rescript/react@^0.10.3":
version "0.10.3"
resolved "https://registry.yarnpkg.com/@rescript/react/-/react-0.10.3.tgz#a2a8bed6b017940ec26c2154764b350f50348889"
Expand Down
1 change: 1 addition & 0 deletions spec/react_on_rails/react_component/render_options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
replay_console
raise_on_prerender_error
random_dom_id
force_load
].freeze

def the_attrs(react_component_name: "App", options: {})
Expand Down

0 comments on commit 56ee2bd

Please sign in to comment.