Skip to content

Commit

Permalink
Add support to replace the turbo_frame contents
Browse files Browse the repository at this point in the history
  • Loading branch information
hajee committed Jul 25, 2024
1 parent 73cb728 commit 931d5b1
Show file tree
Hide file tree
Showing 23 changed files with 471 additions and 16 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/prettier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- '*'
push:
branches:
- main
- '*'

jobs:
prettier:
Expand All @@ -25,5 +25,5 @@ jobs:
run: 'npm install --legacy-peer-deps'

- name: Run Prettier
run: 'npx prettier --check --fail-on-errors .'
run: 'npx prettier --check .'

2 changes: 1 addition & 1 deletion .github/workflows/standardrb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- '*'
push:
branches:
- main
- '*'

jobs:
standard:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ on:
# change this accordingly.
push:
branches:
- main
- '*'

# Run weekly, so that the baseline AppMap artifact does not expire.
schedule:
Expand Down
2 changes: 1 addition & 1 deletion app/assets/builds/@turbo-boost/commands.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions app/assets/builds/@turbo-boost/commands.js.map

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions app/javascript/invoker.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const parseError = error => {
dispatch(lifecycle.events.clientError, document, { detail: { message, error } }, true)
}

const parseAndRenderResponse = response => {
const parseAndRenderResponse = frameId => response => {
const { strategy } = headers.tokenize(response.headers.get(headers.RESPONSE_HEADER))
response.text().then(content => render(strategy, content))
response.text().then(content => render(strategy, content, frameId))
}

const invoke = (payload = {}) => {
Expand All @@ -21,7 +21,7 @@ const invoke = (payload = {}) => {
headers: headers.prepare({}),
body: JSON.stringify(payload)
})
.then(parseAndRenderResponse)
.then(parseAndRenderResponse(payload.frameId))
.catch(parseError)
} catch (error) {
parseError(error)
Expand Down
18 changes: 17 additions & 1 deletion app/javascript/renderer.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
import elements from './elements'

const getElementByIdFromHTMLString = (htmlString, id) => {
var parser = new DOMParser()
var doc = parser.parseFromString(htmlString, 'text/html')
var element = doc.getElementById(id)
return element.outerHTML
}

const frameReplace = (content, frameId) => {
const newFrame = getElementByIdFromHTMLString(content, frameId)
const frame = document.querySelector(`#${frameId}`)
if (frame && newFrame) TurboBoost?.Streams?.morph?.method(frame, newFrame)
}

const append = content => {
document.body.insertAdjacentHTML('beforeend', content)
}
Expand All @@ -14,10 +29,11 @@ const replace = content => {
}

// TODO: dispatch events after append/replace so we can apply page state
export const render = (strategy, content) => {
export const render = (strategy, content, frameId) => {
if (strategy && content) {
if (strategy.match(/^Append$/i)) return append(content)
if (strategy.match(/^Replace$/i)) return replace(content)
if (strategy.match(/^FrameReplace$/i)) return frameReplace(content, frameId)
}
}

Expand Down
4 changes: 4 additions & 0 deletions lib/turbo_boost/commands/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ def turbo_stream_template_exists?
# Commands support the following redering strategies on the client.
# 1. Replace: The entire page (head, body) is replaced with the new content via morph
# 2. Append: The new content is appended to the body
# 3. FrameReplace: Replace the turbo frame specified in the command with the frame in the content. The content can contain more( e.g. whole content)
def client_render_strategy
# Use the replace strategy if the follow things are true:
#
Expand All @@ -244,6 +245,9 @@ def client_render_strategy
if command_params[:driver] == "window" && controller_action_allowed?
return "Replace" unless turbo_stream_template_exists?
end
if command_params[:driver] == "frame" && controller_action_allowed?
return "FrameReplace" unless turbo_stream_template_exists?
end

"Append"
end
Expand Down
2 changes: 1 addition & 1 deletion test/dummy/app/assets/builds/tailwind.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions test/dummy/app/commands/decrement_count_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
class DecrementCountCommand < ApplicationCommand
def perform
Current.user.decrement! :count
controller.instance_variable_set(:@extra_data, "decremented")
end
end
1 change: 1 addition & 0 deletions test/dummy/app/commands/increment_count_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
class IncrementCountCommand < ApplicationCommand
def perform
Current.user.increment! :count
controller.instance_variable_set(:@extra_data, "incremented")
end
end
8 changes: 8 additions & 0 deletions test/dummy/app/controllers/turbo_frame_replaces_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

class TurboFrameReplacesController < ApplicationController
# GET /turbo_frame_replace
def show
@extra_data ||= "Unchanged"
end
end
25 changes: 25 additions & 0 deletions test/dummy/app/views/basic_commands/@stylesheet.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
@layer components {
#basic_command-no-frame,
#basic_command-turbo-frame-replace,
#basic_command-turbo-frame {
@apply relative mx-auto block max-w-screen-xl rounded-lg border;
}

#basic_command-no-frame {
@apply border-sky-500 p-4 dark:border-sky-800;
}

#basic_command-turbo-frame-replace {
@apply border-blue-500 p-4 dark:border-blue-800;
}

#basic_command-turbo-frame-replace::before {
content: '<turbo-frame with replace>';
@apply absolute -top-4 left-4 inline-block rounded-t bg-blue-500 px-2 font-mono text-xs text-white dark:bg-blue-800 dark:text-blue-200;
}

#basic_command-no-frame::before {
content: '<partial>';
@apply absolute -top-4 left-4 inline-block rounded-t bg-sky-500 px-2 font-mono text-xs text-white dark:bg-sky-800 dark:text-sky-200;
Expand All @@ -17,39 +29,52 @@
content: '<turbo-frame>';
@apply absolute -top-4 left-4 inline-block rounded-t bg-rose-500 px-2 font-mono text-xs text-white dark:bg-rose-800 dark:text-rose-200;
}

#basic_command-no-frame > h3,
#basic_command-turbo-frame-replace > h3,
#basic_command-turbo-frame > h3 {
@apply text-2xl font-semibold text-gray-500 dark:text-white md:text-2xl;
}

#basic_command-no-frame > p,
#basic_command-turbo-frame-replace > p,
#basic_command-turbo-frame > p {
@apply mb-4 font-light text-gray-500 dark:text-gray-400;
}

#basic_command-no-frame > code,
#replace_this code,
#basic_command-turbo-frame > code {
@apply px-3 py-1 text-3xl font-bold text-red-500 md:text-4xl;
}
#basic_command-no-frame > div,
#basic_command-turbo-frame-replace > div,
#basic_command-turbo-frame > div {
@apply flex rounded-md shadow-sm;
}
#basic_command-no-frame > div:last-child,
#replace_this > div:last-child,
#basic_command-turbo-frame > div:last-child {
@apply mt-5;
}

#basic_command-no-frame > div > button,
#replace_this > div > button,
#basic_command-turbo-frame > div > button {
@apply inline-flex items-center border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:text-blue-700 focus:ring-2 focus:ring-blue-700 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 dark:hover:text-white dark:focus:text-white dark:focus:ring-blue-500;
}
#basic_command-no-frame > div > button:first-child,
#replace_this > div > button:first-child,
#basic_command-turbo-frame > div > button:first-child {
@apply rounded-l-lg;
}
#basic_command-no-frame > div > button:last-child,
#replace_this > div > button:last-child,
#basic_command-turbo-frame > div > button:last-child {
@apply rounded-r-md;
}
#basic_command-no-frame > div > button > svg,
#replace_this > div > button > svg,
#basic_command-turbo-frame > div > button > svg {
@apply mr-1 h-3 w-3;
}
Expand Down
1 change: 1 addition & 0 deletions test/dummy/app/views/sidebars/_sidebar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<%= render "sidebars/button", value: "Demos", heroicon: :play_circle, active: demos_controller? do %>
<%= render "sidebars/link", label: "Basic Command", href: basic_command_path, icon: :command_line, active: controller_action?("basic_commands#show") %>
<%= render "sidebars/link", label: "Turbo Frame Replace", href: turbo_frame_replace_path, icon: :command_line, active: controller_action?("/turbo_frame_replace#show_more") %>
<%= render "sidebars/link", label: "Tables", href: todos_path(demo: "tables"), icon: :command_line, active: params[:demo] == "tables" %>
<%= render "sidebars/link", label: "Simple Form", href: todos_path(demo: "simple-form"), icon: :command_line, active: params[:demo] == "simple-form" %>
<%= render "sidebars/link", label: "Complex Form", href: todos_path(demo: "complex-form"), icon: :command_line, active: params[:demo] == "complex-form" %>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!-- content... -->

<%= turbo_frame_tag "basic_command-turbo-frame-replace" do %>
<h3>Counter</h3>

<p>
In this demo, the counter value or "state" is managed by the User model.
It is persisted to the database and used when rendering the page. But this data:
</p>
<code class="mb-3" role="state"><%= @extra_data %></code>
<p>
is outside of the frame and although it is updated in the returned HTML, it is not updated on the screen.
</p>

<%= turbo_frame_tag :replace_this do %>
<code role="counter"><%= padded_number Current.user.count %></code>

<% cache do %>
<div>
<button data-turbo-command="IncrementCountCommand">
<%= render "icons/heroicons/plus", solid: true %> Increment
</button>

<button data-turbo-command="DecrementCountCommand">
<%= render "icons/heroicons/minus", solid: true %> Decrement
</button>

<button data-turbo-command="ResetCountCommand" data-turbo-confirm="Are you sure?">
<%= render "icons/heroicons/arrow_path", solid: true %> Reset
</button>
</div>
<% end %>
<% end %>
<% end %>

<!-- content... -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
├── app
│ ├── commands
│ │ ├── decrement_count_command.rb
│ │ ├── increment_count_command.rb
│ │ └── reset_count_command.rb
│ │
│ ├── controllers
│ │ └── turbo_framereplaces_controller.rb
│ │
│ ├── models
│ │ └── user.rb
│ │
│ ├── views
│ │ └── basic_commands
│ │ ├── _turbo_frame_replace.html.erb
│ │ └── show.html.erb
Loading

0 comments on commit 931d5b1

Please sign in to comment.