Skip to content

Commit

Permalink
add turbo stream morph action to broadcasts (#583)
Browse files Browse the repository at this point in the history
  • Loading branch information
omarluq authored Mar 15, 2024
1 parent a167573 commit 102a491
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 2 deletions.
8 changes: 8 additions & 0 deletions app/channels/turbo/streams/broadcasts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ def refresh_debouncer_for(*streamables, request_id: nil) # :nodoc:
Turbo::ThreadDebouncer.for("turbo-refresh-debouncer-#{stream_name_from(streamables.including(request_id))}")
end

def broadcast_morph_to(*streamables, **opts)
broadcast_action_to(*streamables, action: :morph, **opts)
end

def broadcast_morph_later_to(*streamables, **opts)
broadcast_action_later_to(*streamables, action: :morph, **opts)
end

private
def render_format(format, **rendering)
ApplicationController.render(formats: [ format ], **rendering)
Expand Down
23 changes: 23 additions & 0 deletions app/models/concerns/turbo/broadcastable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,29 @@ def broadcast_render_later_to(*streamables, **rendering)
Turbo::StreamsChannel.broadcast_render_later_to(*streamables, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
end

# Broadcast a morph action to the stream name identified by the passed <tt>streamables</tt>. Example:
# sends <turbo-stream action="morph" target="clearance_5"><template><div id="clearance_5">My Clearance</div></template></turbo-stream>
# to the stream named "identity:2:clearances"
# clearance.broadcast_morph_to examiner.identity, :clearances
def broadcast_morph_to(*streamables, **rendering)
Turbo::StreamsChannel.broadcast_morph_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
end

# Same as <tt>broadcast_morph_to</tt> but the designated stream is automatically set to the current model.
def broadcast_morph(**rendering)
broadcast_morph_to(self, target: self, **rendering)
end

# Same as <tt>broadcast_morph_to</tt> but run asynchronously via a <tt>Turbo::Streams::BroadcastJob</tt>.
def broadcast_morph_later_to(*streamables, **rendering)
Turbo::StreamsChannel.broadcast_morph_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts?
end

# Same as <tt>broadcast_morph_later_to</tt> but the designated stream is automatically set to the current model.
def broadcast_morph_later(target: broadcast_target_default, **rendering)
broadcast_morph_later_to self, **rendering
end

private
def broadcast_target_default
self.class.broadcast_target_default
Expand Down
26 changes: 26 additions & 0 deletions app/models/turbo/streams/tag_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,32 @@ def prepend_all(targets, content = nil, **rendering, &block)
action_all :prepend, targets, content, **rendering, &block
end

# Morph the <tt>target</tt> in the dom with either the <tt>content</tt> passed in or a rendering result determined
# by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the target as a record. Examples:
#
# <%= turbo_stream.morph "clearance_5", "<div id='clearance_5'>Morph the dom target identified by clearance_5</div>" %>
# <%= turbo_stream.morph clearance %>
# <%= turbo_stream.morph clearance, partial: "clearances/clearance", locals: { title: "Hello" } %>
# <%= turbo_stream.morph "clearance_5" do %>
# <div id='clearance_5'>Morph the dom target identified by clearance_5</div>
# <% end %>
def morph(target, content = nil, **rendering, &block)
action :morph, target, content, **rendering, &block
end

# Morph the <tt>targets</tt> in the dom with either the <tt>content</tt> passed in or a rendering result determined
# by the <tt>rendering</tt> keyword arguments, the content in the block, or the rendering of the targets as a record. Examples:
#
# <%= turbo_stream.morph_all ".clearance_item", "<div class='clearance_item'>Morph the dom target identified by the class clearance_item</div>" %>
# <%= turbo_stream.morph_all clearance %>
# <%= turbo_stream.morph_all clearance, partial: "clearances/clearance", locals: { title: "Hello" } %>
# <%= turbo_stream.morph_all ".clearance_item" do %>
# <div class='clearance_item'>Morph the dom target identified by the class clearance_item</div>
# <% end %>
def morph_all(targets, content = nil, **rendering, &block)
action_all :morph, targets, content, **rendering, &block
end

# Send an action of the type <tt>name</tt> to <tt>target</tt>. Options described in the concrete methods.
def action(name, target, content = nil, allow_inferred_rendering: true, **rendering, &block)
template = render_template(target, content, allow_inferred_rendering: allow_inferred_rendering, **rendering, &block)
Expand Down
50 changes: 48 additions & 2 deletions test/streams/broadcastable_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,30 @@ class Turbo::BroadcastableTest < ActionCable::Channel::TestCase
@message.broadcast_render_to @profile
end
end

test "broadcasting morph to stream now" do
assert_broadcast_on "stream", turbo_stream_action_tag("morph", target: "message_1", template: render(@message)) do
@message.broadcast_morph_to "stream", target: "message_1"
end
end

test "broadcasting morph to stream now targeting children-only children-only" do
assert_broadcast_on "stream", turbo_stream_action_tag("morph", target: "message_1", 'children-only': true, template: render(@message)) do
@message.broadcast_morph_to "stream", target: "message_1", attributes: { 'children-only': true }
end
end

test "broadcasting morph now" do
assert_broadcast_on @message.to_gid_param, turbo_stream_action_tag("morph", target: "message_1", template: render(@message)) do
@message.broadcast_morph target: "message_1"
end
end

test "broadcasting morph now targeting children-only" do
assert_broadcast_on @message.to_gid_param, turbo_stream_action_tag("morph", target: "message_1", 'children-only': true, template: render(@message)) do
@message.broadcast_morph target: "message_1", attributes: { 'children-only': true }
end
end
end

class Turbo::BroadcastableArticleTest < ActionCable::Channel::TestCase
Expand Down Expand Up @@ -518,6 +542,30 @@ class Turbo::SuppressingBroadcastsTest < ActionCable::Channel::TestCase
end
end

test "suppressing broadcasting morph to stream now" do
assert_no_broadcasts_when_suppressing do
@message.broadcast_morph_to "stream"
end
end

test "suppressing broadcasting morph to stream later" do
assert_no_broadcasts_later_when_supressing do
@message.broadcast_morph_later_to "stream"
end
end

test "suppressing broadcasting morph now" do
assert_no_broadcasts_when_suppressing do
@message.broadcast_morph
end
end

test "suppressing broadcasting morph later" do
assert_no_broadcasts_later_when_supressing do
@message.broadcast_morph_later
end
end

private
def assert_no_broadcasts_when_suppressing
assert_no_broadcasts @message.to_gid_param do
Expand All @@ -535,5 +583,3 @@ def assert_no_broadcasts_later_when_supressing
end
end
end


30 changes: 30 additions & 0 deletions test/streams/streams_channel_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,34 @@ class Turbo::StreamsChannelTest < ActionCable::Channel::TestCase
Turbo::StreamsChannel.broadcast_stream_to "stream", content: "direct"
end
end

test "broadcasting morph now" do
options = { partial: "messages/message", locals: { message: "hello!" } }

assert_broadcast_on "stream", turbo_stream_action_tag("morph", target: "message_1", template: render(options)) do
Turbo::StreamsChannel.broadcast_morph_to "stream", target: "message_1", **options
end

assert_broadcast_on "stream", turbo_stream_action_tag("morph", targets: ".message", template: render(options)) do
Turbo::StreamsChannel.broadcast_morph_to "stream", targets: ".message", **options
end
end

test "broadcasting morph later" do
options = { partial: "messages/message", locals: { message: "hello!" } }

assert_broadcast_on "stream", turbo_stream_action_tag("morph", target: "message_1", template: render(options)) do
perform_enqueued_jobs do
Turbo::StreamsChannel.broadcast_morph_later_to \
"stream", target: "message_1", **options
end
end

assert_broadcast_on "stream", turbo_stream_action_tag("morph", targets: ".message", template: render(options)) do
perform_enqueued_jobs do
Turbo::StreamsChannel.broadcast_morph_later_to \
"stream", targets: ".message", **options
end
end
end
end

0 comments on commit 102a491

Please sign in to comment.