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

Update the history before rendering promoted frames #618

Merged
merged 23 commits into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
710c3ad
Add a updateHistory check to visit
manuelpuyol Jun 30, 2022
5d1d47b
make frame change history before rendering
manuelpuyol Jun 30, 2022
c11a9dd
add frame fixture
manuelpuyol Jun 30, 2022
70c21a8
extract history logic to History class
manuelpuyol Jun 30, 2022
5b225a9
maintain the same restorationIdentifier
manuelpuyol Jun 30, 2022
2ccbb21
remove guard that was always true
manuelpuyol Jun 30, 2022
2fa1923
lint
manuelpuyol Jun 30, 2022
3f7d26b
make history receive a method
manuelpuyol Jun 30, 2022
386f4a9
lint
manuelpuyol Jun 30, 2022
c964e7d
reset frame and action at each nav
manuelpuyol Jun 30, 2022
203f219
udpate ids to reflect each link
manuelpuyol Jun 30, 2022
e73df52
extract getVisitAction and fix compiler issues
manuelpuyol Jun 30, 2022
d46717a
lint
manuelpuyol Jun 30, 2022
e99e69e
add before-frame-render event and make the render pausable
manuelpuyol Jun 30, 2022
0deb581
Add test showcasing the URL being updated first
manuelpuyol Jun 30, 2022
16b3bdd
add pause/abort tests for frames
manuelpuyol Jun 30, 2022
486f7ef
update ids to match initial page
manuelpuyol Jul 12, 2022
93395bc
Merge remote-tracking branch 'turbo/main' into frame-history-update
manuelpuyol Jul 15, 2022
eedb214
Merge remote-tracking branch 'turbo/main' into frame-history-update
manuelpuyol Jul 18, 2022
967c725
remove before-frame-render event code since it was introduced in #431
manuelpuyol Jul 18, 2022
9e7a541
yarn.lock
manuelpuyol Jul 18, 2022
944c169
yarn.lock
manuelpuyol Jul 18, 2022
d535b53
Merge branch 'main' into frame-history-update
manuelpuyol Jul 19, 2022
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
20 changes: 13 additions & 7 deletions src/core/drive/visit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getAnchor } from "../url"
import { Snapshot } from "../snapshot"
import { PageSnapshot } from "./page_snapshot"
import { Action } from "../types"
import { uuid } from "../../util"
import { getHistoryMethodForAction, uuid } from "../../util"
import { PageView } from "./page_view"

export interface VisitDelegate {
Expand Down Expand Up @@ -45,13 +45,16 @@ export type VisitOptions = {
response?: VisitResponse
visitCachedSnapshot(snapshot: Snapshot): void
willRender: boolean
updateHistory: boolean
restorationIdentifier?: string
}

const defaultOptions: VisitOptions = {
action: "advance",
historyChanged: false,
visitCachedSnapshot: () => {},
willRender: true,
updateHistory: true,
}

export type VisitResponse = {
Expand All @@ -75,6 +78,7 @@ export class Visit implements FetchRequestDelegate {
readonly timingMetrics: TimingMetrics = {}
readonly visitCachedSnapshot: (snapshot: Snapshot) => void
readonly willRender: boolean
readonly updateHistory: boolean

followedRedirect = false
frame?: number
Expand All @@ -99,10 +103,11 @@ export class Visit implements FetchRequestDelegate {
this.location = location
this.restorationIdentifier = restorationIdentifier || uuid()

const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender } = {
...defaultOptions,
...options,
}
const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory } =
{
...defaultOptions,
...options,
}
this.action = action
this.historyChanged = historyChanged
this.referrer = referrer
Expand All @@ -111,6 +116,7 @@ export class Visit implements FetchRequestDelegate {
this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action)
this.visitCachedSnapshot = visitCachedSnapshot
this.willRender = willRender
this.updateHistory = updateHistory
this.scrolled = !willRender
}

Expand Down Expand Up @@ -171,9 +177,9 @@ export class Visit implements FetchRequestDelegate {
}

changeHistory() {
if (!this.historyChanged) {
if (!this.historyChanged && this.updateHistory) {
const actionForHistory = this.location.href === this.referrer?.href ? "replace" : this.action
const method = this.getHistoryMethodForAction(actionForHistory)
const method = getHistoryMethodForAction(actionForHistory)
this.history.update(method, this.location, this.restorationIdentifier)
this.historyChanged = true
}
Expand Down
32 changes: 27 additions & 5 deletions src/core/frames/frame_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ import {
import { FetchMethod, FetchRequest, FetchRequestDelegate, FetchRequestHeaders } from "../../http/fetch_request"
import { FetchResponse } from "../../http/fetch_response"
import { AppearanceObserver, AppearanceObserverDelegate } from "../../observers/appearance_observer"
import { clearBusyState, getAttribute, parseHTMLDocument, markAsBusy } from "../../util"
import {
clearBusyState,
getAttribute,
parseHTMLDocument,
markAsBusy,
uuid,
getHistoryMethodForAction,
} from "../../util"
import { FormSubmission, FormSubmissionDelegate } from "../drive/form_submission"
import { Snapshot } from "../snapshot"
import { ViewDelegate } from "../view"
Expand All @@ -17,7 +24,7 @@ import { FrameView } from "./frame_view"
import { LinkInterceptor, LinkInterceptorDelegate } from "./link_interceptor"
import { FrameRenderer } from "./frame_renderer"
import { session } from "../index"
import { isAction } from "../types"
import { isAction, Action } from "../types"

export class FrameController
implements
Expand All @@ -41,13 +48,17 @@ export class FrameController
private connected = false
private hasBeenLoaded = false
private ignoredAttributes: Set<FrameElementObservedAttribute> = new Set()
private action?: Action
private frame?: FrameElement
readonly restorationIdentifier: string

constructor(element: FrameElement) {
this.element = element
this.view = new FrameView(this, this.element)
this.appearanceObserver = new AppearanceObserver(this, this.element)
this.linkInterceptor = new LinkInterceptor(this, this.element)
this.formInterceptor = new FormInterceptor(this, this.element)
this.restorationIdentifier = uuid()
}

connect() {
Expand Down Expand Up @@ -126,6 +137,7 @@ export class FrameController
const snapshot = new Snapshot(await this.extractForeignFrameElement(body))
const renderer = new FrameRenderer(this.view.snapshot, snapshot, false, false)
if (this.view.renderPromise) await this.view.renderPromise
this.changeHistory()
await this.view.render(renderer)
this.complete = true
session.frameRendered(fetchResponse, this.element)
Expand Down Expand Up @@ -277,9 +289,10 @@ export class FrameController
}

private proposeVisitIfNavigatedWithAction(frame: FrameElement, element: Element, submitter?: HTMLElement) {
const action = getAttribute("data-turbo-action", submitter, element, frame)
this.action = getAttribute("data-turbo-action", submitter, element, frame) as Action
manuelpuyol marked this conversation as resolved.
Show resolved Hide resolved
this.frame = frame

if (isAction(action)) {
if (isAction(this.action)) {
const { visitCachedSnapshot } = new SnapshotSubstitution(frame)
frame.delegate.fetchResponseLoaded = (fetchResponse: FetchResponse) => {
if (frame.src) {
Expand All @@ -288,16 +301,25 @@ export class FrameController
const response = { statusCode, redirected, responseHTML }

session.visit(frame.src, {
action,
action: this.action,
response,
visitCachedSnapshot,
willRender: false,
updateHistory: false,
restorationIdentifier: this.restorationIdentifier,
})
}
}
}
}

changeHistory() {
if (this.action && this.frame) {
const method = getHistoryMethodForAction(this.action)
session.history.update(method, expandURL(this.frame.src as string), this.restorationIdentifier)
manuelpuyol marked this conversation as resolved.
Show resolved Hide resolved
}
}

private findFrameElement(element: Element, submitter?: HTMLElement) {
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target")
return getFrameElementById(id) ?? this.element
Expand Down
2 changes: 1 addition & 1 deletion src/core/native/browser_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class BrowserAdapter implements Adapter {
}

visitProposedToLocation(location: URL, options?: Partial<VisitOptions>) {
this.navigator.startVisit(location, uuid(), options)
this.navigator.startVisit(location, options?.restorationIdentifier || uuid(), options)
}

visitStarted(visit: Visit) {
Expand Down
22 changes: 22 additions & 0 deletions src/tests/fixtures/tabs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Tabs</title>
<script src="/dist/turbo.es2017-umd.js"></script>
<script src="/src/tests/fixtures/test.js"></script>
</head>
<body>
<h1>Tabs</h1>

<turbo-frame id="tab-content" data-turbo-action="advance">
<div>
<a id="drive_enabled" href="/src/tests/fixtures/tabs.html">Tab 1</a>
<a id="drive_enabled" href="/src/tests/fixtures/tabs/two.html">Tab 2</a>
<a id="drive_enabled" href="/src/tests/fixtures/tabs/three.html">Tab 3</a>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to declare these anchors with an [id] attribute? If it is, could we make sure their values don't collide with other elements on the page?

Also, are there plans to add tests to cover the new behavior and to guard against regressions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I wanted to validate the idea and implementation and will follow up with some tests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seanpdoyle I added some tests in e99e69e...16b3bdd, but it's really hard to know the state of tests already in place since there's a lot of flakiness. I'm different seeing test pass and fail each run

</div>

One
</turbo-frame>
</body>
</html>
9 changes: 9 additions & 0 deletions src/tests/fixtures/tabs/three.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<turbo-frame id="tab-content" data-turbo-action="advance">
<div>
<a id="drive_enabled" href="/src/tests/fixtures/tabs.html">Tab 1</a>
<a id="drive_enabled" href="/src/tests/fixtures/tabs/two.html">Tab 2</a>
<a id="drive_enabled" href="/src/tests/fixtures/tabs/three.html">Tab 3</a>
manuelpuyol marked this conversation as resolved.
Show resolved Hide resolved
</div>

Three
</turbo-frame>
9 changes: 9 additions & 0 deletions src/tests/fixtures/tabs/two.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<turbo-frame id="tab-content" data-turbo-action="advance">
<div>
<a id="drive_enabled" href="/src/tests/fixtures/tabs.html">Tab 1</a>
<a id="drive_enabled" href="/src/tests/fixtures/tabs/two.html">Tab 2</a>
<a id="drive_enabled" href="/src/tests/fixtures/tabs/three.html">Tab 3</a>
</div>

Two
</turbo-frame>
12 changes: 12 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Action } from "./core/types"

export type DispatchOptions = {
target: EventTarget
cancelable: boolean
Expand Down Expand Up @@ -92,3 +94,13 @@ export function clearBusyState(...elements: Element[]) {
element.removeAttribute("aria-busy")
}
}

export function getHistoryMethodForAction(action: Action) {
switch (action) {
case "replace":
return history.replaceState
case "advance":
case "restore":
return history.pushState
}
}