Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Renderer instance available to View delegates #1027

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions src/core/frames/frame_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,25 +250,31 @@ export class FrameController {

// View delegate

allowsImmediateRender({ element: newFrame }, options) {
allowsImmediateRender({ element: newFrame }, renderer, options) {
const event = dispatch("turbo:before-frame-render", {
target: this.element,
detail: { newFrame, ...options },
detail: { newFrame, renderMethod: renderer.renderMethod, ...options },
cancelable: true
})
const {
defaultPrevented,
detail: { render }
} = event

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

return !defaultPrevented
}

viewRenderedSnapshot(_snapshot, _isPreview, _renderMethod) {}
viewRenderedSnapshot({ currentSnapshot: { element: frame }, renderMethod }) {
return dispatch("turbo:frame-render", {
detail: { renderMethod },
target: frame,
cancelable: true
})
}

preloadOnLoadLinksForView(element) {
session.preloadOnLoadLinksForView(element)
Expand Down Expand Up @@ -303,9 +309,11 @@ export class FrameController {
if (this.view.renderPromise) await this.view.renderPromise
this.changeHistory()

await this.view.render(renderer)
await mergeIntoNext("turbo:frame-render", { on: this.element, detail: { fetchResponse } }, async () => {
await this.view.render(renderer)
})

this.complete = true
session.frameRendered(fetchResponse, this.element)
session.frameLoaded(this.element)
await this.fetchResponseLoaded(fetchResponse)
} else if (this.#willHandleFrameMissingFromResponse(fetchResponse)) {
Expand Down Expand Up @@ -580,3 +588,15 @@ function activateElement(element, currentURL) {
}
}
}

async function mergeIntoNext(eventName, { on: target, detail }, callback) {
const listener = (event) => Object.assign(event.detail, detail)
const listenerOptions = { once: true, capture: true }
target.addEventListener(eventName, listener, listenerOptions)

try {
await callback()
} finally {
target.removeEventListener(eventName, listener, listenerOptions)
}
}
28 changes: 8 additions & 20 deletions src/core/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,8 @@ export class Session {
}
}

allowsImmediateRender({ element }, options) {
const event = this.notifyApplicationBeforeRender(element, options)
allowsImmediateRender({ element }, renderer, options) {
const event = this.notifyApplicationBeforeRender(element, renderer, options)
const {
defaultPrevented,
detail: { render }
Expand All @@ -326,9 +326,9 @@ export class Session {
return !defaultPrevented
}

viewRenderedSnapshot(_snapshot, _isPreview, renderMethod) {
viewRenderedSnapshot(renderer) {
this.view.lastRenderedLocation = this.history.location
this.notifyApplicationAfterRender(renderMethod)
this.notifyApplicationAfterRender(renderer)
}

preloadOnLoadLinksForView(element) {
Expand All @@ -345,10 +345,6 @@ export class Session {
this.notifyApplicationAfterFrameLoad(frame)
}

frameRendered(fetchResponse, frame) {
this.notifyApplicationAfterFrameRender(fetchResponse, frame)
}

// Application events

applicationAllowsFollowingLinkToLocation(link, location, ev) {
Expand Down Expand Up @@ -384,15 +380,15 @@ export class Session {
return dispatch("turbo:before-cache")
}

notifyApplicationBeforeRender(newBody, options) {
notifyApplicationBeforeRender(newBody, { renderMethod }, options) {
return dispatch("turbo:before-render", {
detail: { newBody, ...options },
detail: { newBody, renderMethod, ...options },
cancelable: true
})
}

notifyApplicationAfterRender(renderMethod) {
return dispatch("turbo:render", { detail: { renderMethod } })
notifyApplicationAfterRender({ isPreview, renderMethod }) {
return dispatch("turbo:render", { detail: { isPreview, renderMethod } })
}

notifyApplicationAfterPageLoad(timing = {}) {
Expand All @@ -414,14 +410,6 @@ export class Session {
return dispatch("turbo:frame-load", { target: frame })
}

notifyApplicationAfterFrameRender(fetchResponse, frame) {
return dispatch("turbo:frame-render", {
detail: { fetchResponse },
target: frame,
cancelable: true
})
}

// Helpers

submissionIsNavigatable(form, submitter) {
Expand Down
8 changes: 4 additions & 4 deletions src/core/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class View {
// Rendering

async render(renderer) {
const { isPreview, shouldRender, willRender, newSnapshot: snapshot } = renderer
const { shouldRender, willRender, newSnapshot: snapshot } = renderer

// A workaround to ignore tracked element mismatch reloads when performing
// a promoted Visit from a frame navigation
Expand All @@ -69,12 +69,12 @@ export class View {
await this.prepareToRenderSnapshot(renderer)

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

await this.renderSnapshot(renderer)
this.delegate.viewRenderedSnapshot(snapshot, isPreview, this.renderer.renderMethod)
this.delegate.viewRenderedSnapshot(renderer)
this.delegate.preloadOnLoadLinksForView(this.element)
this.finishRenderingSnapshot(renderer)
} finally {
Expand Down
2 changes: 1 addition & 1 deletion src/tests/functional/frame_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ test("'turbo:frame-render' is triggered after frame has finished rendering", asy
await page.click("#frame-part")

await nextEventNamed(page, "turbo:frame-render") // recursive
const { fetchResponse } = await nextEventNamed(page, "turbo:frame-render")
const { fetchResponse } = await nextEventNamed(page, "turbo:frame-render", { renderMethod: "replace" })

assert.include(fetchResponse.response.url, "/src/tests/fixtures/frames/part.html")
})
Expand Down
2 changes: 1 addition & 1 deletion src/tests/functional/rendering_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test("triggers before-render and render events", async ({ page }) => {

assert.equal(await page.textContent("h1"), "One")

await nextEventNamed(page, "turbo:render")
await nextEventNamed(page, "turbo:render", { renderMethod: "replace" })
assert.equal(await newBody, await page.evaluate(() => document.body.outerHTML))
})

Expand Down
Loading