Skip to content

Commit d5873f1

Browse files
Show warning badge when force_load feature is enabled and RORP is not used (#1780)
* add a warning badge when force_load feature is enabled but rorp is not used * Add Pro license warning tests for react_component and redux_store helpers * Refactor Pro license validation logic and update related tests
1 parent 20e4455 commit d5873f1

File tree

3 files changed

+225
-6
lines changed

3 files changed

+225
-6
lines changed

lib/react_on_rails/helper.rb

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,17 @@ def react_component(component_name, options = {})
5858
server_rendered_html = internal_result[:result]["html"]
5959
console_script = internal_result[:result]["consoleReplayScript"]
6060
render_options = internal_result[:render_options]
61+
badge = pro_warning_badge_if_needed(render_options.force_load)
6162

6263
case server_rendered_html
6364
when String
64-
build_react_component_result_for_server_rendered_string(
65+
html = build_react_component_result_for_server_rendered_string(
6566
server_rendered_html: server_rendered_html,
6667
component_specification_tag: internal_result[:tag],
6768
console_script: console_script,
6869
render_options: render_options
6970
)
71+
(badge + html).html_safe
7072
when Hash
7173
msg = <<~MSG
7274
Use react_component_hash (not react_component) to return a Hash to your ruby view code. See
@@ -212,18 +214,21 @@ def react_component_hash(component_name, options = {})
212214
server_rendered_html = internal_result[:result]["html"]
213215
console_script = internal_result[:result]["consoleReplayScript"]
214216
render_options = internal_result[:render_options]
217+
badge = pro_warning_badge_if_needed(render_options.force_load)
215218

216219
if server_rendered_html.is_a?(String) && internal_result[:result]["hasErrors"]
217220
server_rendered_html = { COMPONENT_HTML_KEY => internal_result[:result]["html"] }
218221
end
219222

220223
if server_rendered_html.is_a?(Hash)
221-
build_react_component_result_for_server_rendered_hash(
224+
result = build_react_component_result_for_server_rendered_hash(
222225
server_rendered_html: server_rendered_html,
223226
component_specification_tag: internal_result[:tag],
224227
console_script: console_script,
225228
render_options: render_options
226229
)
230+
result[COMPONENT_HTML_KEY] = badge + result[COMPONENT_HTML_KEY]
231+
result
227232
else
228233
msg = <<~MSG
229234
Render-Function used by react_component_hash for #{component_name} is expected to return
@@ -251,6 +256,8 @@ def react_component_hash(component_name, options = {})
251256
# waiting for the page to load.
252257
def redux_store(store_name, props: {}, defer: false, force_load: nil)
253258
force_load = ReactOnRails.configuration.force_load if force_load.nil?
259+
badge = pro_warning_badge_if_needed(force_load)
260+
254261
redux_store_data = { store_name: store_name,
255262
props: props,
256263
force_load: force_load }
@@ -261,7 +268,7 @@ def redux_store(store_name, props: {}, defer: false, force_load: nil)
261268
else
262269
registered_stores << redux_store_data
263270
result = render_redux_store_data(redux_store_data)
264-
prepend_render_rails_context(result)
271+
(badge + prepend_render_rails_context(result)).html_safe
265272
end
266273
end
267274

@@ -440,7 +447,28 @@ def load_pack_for_generated_component(react_component_name, render_options)
440447

441448
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
442449

443-
private
450+
def pro_warning_badge_if_needed(force_load)
451+
return "".html_safe unless force_load
452+
return "".html_safe if ReactOnRails::Utils.react_on_rails_pro_licence_valid?
453+
454+
warning_message = "[REACT ON RAILS] The 'force_load' feature requires a React on Rails Pro license. " \
455+
"Please visit https://shakacode.com/react-on-rails-pro to learn more."
456+
puts warning_message
457+
Rails.logger.warn warning_message
458+
459+
tooltip_text = "The 'force_load' feature requires a React on Rails Pro license. Click to learn more."
460+
461+
badge_html = <<~HTML
462+
<a href="https://shakacode.com/react-on-rails-pro" target="_blank" rel="noopener noreferrer" title="#{tooltip_text}">
463+
<div style="position: fixed; top: 0; right: 0; width: 180px; height: 180px; overflow: hidden; z-index: 9999; pointer-events: none;">
464+
<div style="position: absolute; top: 50px; right: -40px; transform: rotate(45deg); background-color: rgba(220, 53, 69, 0.85); color: white; padding: 7px 40px; text-align: center; font-weight: bold; font-family: sans-serif; font-size: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); pointer-events: auto;">
465+
React On Rails Pro Required
466+
</div>
467+
</div>
468+
</a>
469+
HTML
470+
badge_html.strip.html_safe
471+
end
444472

445473
def run_stream_inside_fiber
446474
unless ReactOnRails::Utils.react_on_rails_pro?

lib/react_on_rails/utils.rb

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
require "active_support/core_ext/string"
88

99
module ReactOnRails
10-
module Utils
10+
module Utils # rubocop:disable Metrics/ModuleLength
1111
TRUNCATION_FILLER = "\n... TRUNCATED #{
1212
Rainbow('To see the full output, set FULL_TEXT_ERRORS=true.').red
1313
} ...\n".freeze
@@ -213,6 +213,21 @@ def self.react_on_rails_pro_version
213213
end
214214
end
215215

216+
def self.react_on_rails_pro_licence_valid?
217+
return @react_on_rails_pro_licence_valid if defined?(@react_on_rails_pro_licence_valid)
218+
219+
@react_on_rails_pro_licence_valid = begin
220+
return false unless react_on_rails_pro?
221+
222+
# Maintain compatibility with legacy versions of React on Rails Pro:
223+
# Earlier releases did not require license validation, as they were distributed as private gems.
224+
# This check ensures that the method works correctly regardless of the installed version.
225+
return true unless ReactOnRailsPro::Utils.respond_to?(:licence_valid?)
226+
227+
ReactOnRailsPro::Utils.licence_valid?
228+
end
229+
end
230+
216231
def self.rsc_support_enabled?
217232
return false unless react_on_rails_pro?
218233

spec/dummy/spec/helpers/react_on_rails_helper_spec.rb

Lines changed: 177 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ class PlainReactOnRailsHelper
2121
{ "HTTP_ACCEPT_LANGUAGE" => "en" }
2222
)
2323
}
24+
25+
allow(ReactOnRails::Utils).to receive_messages(
26+
react_on_rails_pro_licence_valid?: true
27+
)
2428
end
2529

2630
let(:hash) do
@@ -370,10 +374,137 @@ def helper.append_javascript_pack_tag(name, **options)
370374
it { is_expected.to include force_load_script }
371375
end
372376
end
377+
378+
describe "with Pro license warning" do
379+
let(:badge_html_string) { "React On Rails Pro Required" }
380+
381+
before do
382+
allow(Rails.logger).to receive(:warn)
383+
end
384+
385+
context "when Pro license is NOT installed and force_load is true" do
386+
subject(:react_app) { react_component("App", props: props, force_load: true) }
387+
388+
before do
389+
allow(ReactOnRails::Utils).to receive(:react_on_rails_pro_licence_valid?).and_return(false)
390+
end
391+
392+
it { is_expected.to include(badge_html_string) }
393+
394+
it "logs a warning" do
395+
react_app
396+
expect(Rails.logger).to have_received(:warn).with(a_string_matching(/The 'force_load' feature requires/))
397+
end
398+
end
399+
400+
context "when Pro license is NOT installed and global force_load is true" do
401+
subject(:react_app) { react_component("App", props: props) }
402+
403+
before do
404+
allow(ReactOnRails::Utils).to receive(:react_on_rails_pro_licence_valid?).and_return(false)
405+
end
406+
407+
around do |example|
408+
ReactOnRails.configure { |config| config.force_load = true }
409+
example.run
410+
ReactOnRails.configure { |config| config.force_load = false }
411+
end
412+
413+
it { is_expected.to include(badge_html_string) }
414+
end
415+
416+
context "when Pro license is NOT installed and force_load is false" do
417+
subject(:react_app) { react_component("App", props: props, force_load: false) }
418+
419+
before do
420+
allow(ReactOnRails::Utils).to receive(:react_on_rails_pro_licence_valid?).and_return(false)
421+
end
422+
423+
it { is_expected.not_to include(badge_html_string) }
424+
425+
it "does not log a warning" do
426+
react_app
427+
expect(Rails.logger).not_to have_received(:warn)
428+
end
429+
end
430+
431+
context "when Pro license IS installed and force_load is true" do
432+
subject(:react_app) { react_component("App", props: props, force_load: true) }
433+
434+
before do
435+
allow(ReactOnRails::Utils).to receive_messages(
436+
react_on_rails_pro_licence_valid?: true
437+
)
438+
end
439+
440+
it { is_expected.not_to include(badge_html_string) }
441+
442+
it "does not log a warning" do
443+
react_app
444+
expect(Rails.logger).not_to have_received(:warn)
445+
end
446+
end
447+
end
448+
end
449+
450+
describe "#react_component_hash" do
451+
subject(:react_app) { react_component_hash("App", props: props) }
452+
453+
let(:props) { { name: "My Test Name" } }
454+
455+
before do
456+
allow(SecureRandom).to receive(:uuid).and_return(0)
457+
allow(ReactOnRails::ServerRenderingPool).to receive(:server_render_js_with_console_logging).and_return(
458+
"html" => { "componentHtml" => "<div>Test</div>", "title" => "Test Title" },
459+
"consoleReplayScript" => ""
460+
)
461+
allow(ReactOnRails::ServerRenderingJsCode).to receive(:js_code_renderer)
462+
.and_return(ReactOnRails::ServerRenderingJsCode)
463+
end
464+
465+
it "returns a hash with component and other keys" do
466+
expect(react_app).to be_a(Hash)
467+
expect(react_app).to have_key("componentHtml")
468+
expect(react_app).to have_key("title")
469+
end
470+
471+
context "with Pro license warning" do
472+
let(:badge_html_string) { "React On Rails Pro Required" }
473+
474+
before do
475+
allow(Rails.logger).to receive(:warn)
476+
end
477+
478+
context "when Pro license is NOT installed and force_load is true" do
479+
subject(:react_app) { react_component_hash("App", props: props, force_load: true) }
480+
481+
before do
482+
allow(ReactOnRails::Utils).to receive(:react_on_rails_pro_licence_valid?).and_return(false)
483+
end
484+
485+
it "adds badge to componentHtml" do
486+
expect(react_app["componentHtml"]).to include(badge_html_string)
487+
end
488+
end
489+
490+
context "when Pro license IS installed and force_load is true" do
491+
subject(:react_app) { react_component_hash("App", props: props, force_load: true) }
492+
493+
before do
494+
allow(ReactOnRails::Utils).to receive_messages(
495+
react_on_rails_pro_licence_valid?: true
496+
)
497+
end
498+
499+
it "does not add badge to componentHtml" do
500+
expect(react_app["componentHtml"]).not_to include(badge_html_string)
501+
end
502+
end
503+
end
373504
end
374505

375506
describe "#redux_store" do
376-
subject(:store) { redux_store("reduxStore", props: props) }
507+
subject(:store) { redux_store("reduxStore", props: props, force_load: true) }
377508

378509
let(:props) do
379510
{ name: "My Test Name" }
@@ -394,6 +525,51 @@ def helper.append_javascript_pack_tag(name, **options)
394525
it {
395526
expect(expect(store).target).to script_tag_be_included(react_store_script)
396527
}
528+
529+
context "with Pro license warning" do
530+
let(:badge_html_string) { "React On Rails Pro Required" }
531+
532+
before do
533+
allow(Rails.logger).to receive(:warn)
534+
end
535+
536+
context "when Pro license is NOT installed and force_load is true" do
537+
subject(:store) { redux_store("reduxStore", props: props, force_load: true) }
538+
539+
before do
540+
allow(ReactOnRails::Utils).to receive(:react_on_rails_pro_licence_valid?).and_return(false)
541+
end
542+
543+
it { is_expected.to include(badge_html_string) }
544+
545+
it "logs a warning" do
546+
store
547+
expect(Rails.logger).to have_received(:warn).with(a_string_matching(/The 'force_load' feature requires/))
548+
end
549+
end
550+
551+
context "when Pro license is NOT installed and force_load is false" do
552+
subject(:store) { redux_store("reduxStore", props: props, force_load: false) }
553+
554+
before do
555+
allow(ReactOnRails::Utils).to receive(:react_on_rails_pro_licence_valid?).and_return(false)
556+
end
557+
558+
it { is_expected.not_to include(badge_html_string) }
559+
end
560+
561+
context "when Pro license IS installed and force_load is true" do
562+
subject(:store) { redux_store("reduxStore", props: props, force_load: true) }
563+
564+
before do
565+
allow(ReactOnRails::Utils).to receive_messages(
566+
react_on_rails_pro_licence_valid?: true
567+
)
568+
end
569+
570+
it { is_expected.not_to include(badge_html_string) }
571+
end
572+
end
397573
end
398574

399575
describe "#server_render_js", :js, type: :system do

0 commit comments

Comments
 (0)