Skip to content

Commit

Permalink
Support custom rendering from turbo:before-render
Browse files Browse the repository at this point in the history
The problem
---

The rendering process during a page-wide navigation is opaque, and
cannot be extended. Proposals have been made to use third-party
rendering tools like [morphdom][], or other animation libraries.

Outside of the HTML manipulation, Turbo is also responsible for loading
script, transposing permanent elements, etc.

How might these tools integrate with Turbo in a way that's compliant
with permanent elements.

The solution
---

When publishing a `turbo:before-render` event, dispatch it with a
`render()` function property in addition to the `resume()`.

This way, consumer applications can override rendering:

```html
import morphdom from "morphdom"

addEventListener("turbo:before-render", ({ detail }) => {
  detail.render = (currentElement, newElement) => {
    morphdom(currentElement, newElement)
  }

  // or, more tersely
  detail.render = morphdom
})
```

Potentially Closes [hotwired#197][]
Potentially Closes [hotwired#378][]
Potentially Closes [hotwired#218][]

[morphdom]: https://github.com/patrick-steele-idem/morphdom
[hotwired#218]: hotwired#218
[hotwired#378]: hotwired#378
[hotwired#197]: hotwired#197
  • Loading branch information
seanpdoyle committed Nov 11, 2021
1 parent aad4011 commit ee43fea
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 3 deletions.
8 changes: 7 additions & 1 deletion src/core/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,13 @@ export class Session implements FormSubmitObserverDelegate, HistoryDelegate, Lin

allowsImmediateRender({ element }: PageSnapshot, options: PageViewRenderOptions) {
const event = this.notifyApplicationBeforeRender(element, options)
return !event.defaultPrevented
const { defaultPrevented, detail: { render } } = event

if (this.view.renderer && render) {
this.view.renderer.renderElement = render
}

return !defaultPrevented
}

viewRenderedSnapshot(snapshot: PageSnapshot, isPreview: boolean) {
Expand Down
5 changes: 3 additions & 2 deletions src/core/view.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Renderer } from "./renderer"
import { Renderer, Render } from "./renderer"
import { Snapshot } from "./snapshot"
import { Position } from "./types"
import { getAnchor } from "./url"

export interface ViewRenderOptions<E> {
resume: (value: any) => void
render: Render<E>
}

export interface ViewDelegate<E extends Element, S extends Snapshot<E>> {
Expand Down Expand Up @@ -82,7 +83,7 @@ export abstract class View<E extends Element, S extends Snapshot<E> = Snapshot<E
this.prepareToRenderSnapshot(renderer)

const renderInterception = new Promise(resolve => this.resolveInterceptionPromise = resolve)
const options = { resume: this.resolveInterceptionPromise }
const options = { resume: this.resolveInterceptionPromise, render: this.renderer.renderElement }
const immediateRender = this.delegate.allowsImmediateRender(snapshot, options)
if (!immediateRender) await renderInterception

Expand Down
16 changes: 16 additions & 0 deletions src/tests/functional/rendering_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@ export class RenderingTests extends TurboDriveTestCase {
this.assert(await newBody.equals(await this.body))
}

async "test before-render event supports custom render function"() {
await this.evaluate(() => addEventListener("turbo:before-render", (event: Event) => {
if (event instanceof CustomEvent) {
const { render } = event.detail
event.detail.render = (c: HTMLBodyElement, n: HTMLBodyElement) => {
n.insertAdjacentHTML("beforeend", `<span id="custom-rendered">Custom Rendered</span>`)
render(c, n)
}
}
}))
await this.clickSelector("#same-origin-link")
await this.nextBody

this.assert.ok(await this.querySelector("#custom-rendered"), "renders with custom function")
}

async "test reloads when tracked elements change"() {
this.clickSelector("#tracked-asset-change-link")
await this.nextBody
Expand Down

0 comments on commit ee43fea

Please sign in to comment.