Skip to content
Merged
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
28 changes: 27 additions & 1 deletion types/hotwired__turbo/hotwired__turbo-tests.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FrameElement, StreamElement, visit } from "@hotwired/turbo";
import { FrameElement, StreamActions, StreamElement, visit } from "@hotwired/turbo";

const turboFrame = document.querySelector<FrameElement>("turbo-frame")!;

Expand Down Expand Up @@ -55,6 +55,8 @@ Turbo.visit("my-location");
Turbo.visit("my-location", {});
Turbo.visit("my-location", { action: "advance" });
Turbo.visit("my-location", { action: "replace" });
// @ts-expect-error
Turbo.visit("my-location", { action: "magic" });

Turbo.visit("my-location", {
// @ts-expect-error
Expand All @@ -65,3 +67,27 @@ Turbo.visit("my-location", { frame: "mine" });

// $ExpectType TurboSession
Turbo.session;

StreamActions.log = function() {
// $ExpectType StreamElement
this;
console.log(this.getAttribute("message"));
};

document.addEventListener("turbo:before-fetch-response", function(e) {
let { fetchResponse } = e.detail;
fetchResponse.header("foo");
});

document.addEventListener("turbo:before-render", function(e) {
e.detail.render = (currentElement, newElement) => {
// $ExpectType HTMLBodyElement
currentElement;
// $ExpectType HTMLBodyElement
newElement;
};
});

document.addEventListener("turbo:frame-missing", function(event) {
event.detail.visit(event.detail.response, {});
});
133 changes: 124 additions & 9 deletions types/hotwired__turbo/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ export class StreamElement extends HTMLElement {
}

export class FetchRequest {}
export class FetchResponse {}
export class FetchResponse {
header(key: string): string | undefined;
statusCode: number;
responseText: Promise<string>;
location: URL;
response: Response;
}

/**
* Connects a stream source to the main session.
Expand Down Expand Up @@ -71,10 +77,16 @@ export interface TurboSession {
renderStreamMessage(message: unknown): void;
}

export function visit(
location: string,
options?: { action?: "advance" | "replace"; frame?: string },
): void;
export const StreamActions: {
[action: string]: (this: StreamElement) => void;
};

export type Action = "advance" | "replace" | "restore";
export interface VisitOptions {
action?: Action;
frame?: string;
}
export function visit(location: string, options?: VisitOptions): void;

export interface TurboGlobal {
/**
Expand All @@ -98,14 +110,117 @@ export interface TurboGlobal {
*/
setConfirmMethod(confirmMethod: () => boolean): void;

visit(
location: string,
options?: { action?: "advance" | "replace"; frame?: string },
): void;
visit(location: string, options?: { action?: Action; frame?: string }): void;

session: TurboSession;
}

declare global {
const Turbo: TurboGlobal;
}

export type Render = (currentElement: StreamElement) => Promise<void>;
export type TimingData = unknown;
export type VisitFallback = (location: string | Response, options: VisitOptions) => Promise<void>;

export type TurboBeforeCacheEvent = CustomEvent;
export type TurboBeforePrefetchEvent = CustomEvent;
export type TurboBeforeRenderEvent = CustomEvent<{
newBody: HTMLBodyElement;
renderMethod: "replace" | "morph";
isPreview: boolean;
resume: (value?: any) => void;
render: (currentBody: HTMLBodyElement, newBody: HTMLBodyElement) => void;
}>;
export type TurboBeforeVisitEvent = CustomEvent<{ url: string }>;
export type TurboClickEvent = CustomEvent<{
url: string;
originalEvent: MouseEvent;
}>;
export type TurboFrameLoadEvent = CustomEvent;
export type TurboBeforeFrameRenderEvent = CustomEvent<{
newFrame: FrameElement;
resume: (value?: any) => void;
render: (currentFrame: FrameElement, newFrame: FrameElement) => void;
}>;
export type TurboFrameRenderEvent = CustomEvent<{
fetchResponse: FetchResponse;
}>;
export type TurboLoadEvent = CustomEvent<{ url: string; timing: TimingData }>;
export type TurboRenderEvent = CustomEvent;
export type TurboReloadEvent = CustomEvent;
export type TurboVisitEvent = CustomEvent<{ url: string; action: Action }>;

export type TurboBeforeStreamRenderEvent = CustomEvent<{
newStream: StreamElement;
render: Render;
}>;

export interface FormSubmission {
stop(): void;
}
export type FormSubmissionResult =
| { success: boolean; fetchResponse: FetchResponse }
| { success: false; error: Error };

export type TurboSubmitStartEvent = CustomEvent<{
formSubmission: FormSubmission;
}>;
export type TurboSubmitEndEvent = CustomEvent<
& { formSubmission: FormSubmission }
& {
[K in keyof FormSubmissionResult]?: FormSubmissionResult[K];
}
>;

export type TurboFrameMissingEvent = CustomEvent<{
response: Response;
visit: VisitFallback;
}>;

export type TurboBeforeFetchRequestEvent = CustomEvent<{
fetchOptions: RequestInit;
url: URL;
resume: (value: any) => void;
}>;

export type TurboBeforeFetchResponseEvent = CustomEvent<{
fetchResponse: FetchResponse;
}>;

export type TurboFetchRequestErrorEvent = CustomEvent<{
request: FetchRequest;
error: Error;
}>;

export interface TurboElementEventMap {
"turbo:before-frame-render": TurboBeforeFrameRenderEvent;
"turbo:before-fetch-request": TurboBeforeFetchRequestEvent;
"turbo:before-fetch-response": TurboBeforeFetchResponseEvent;
"turbo:fetch-request-error": TurboFetchRequestErrorEvent;
"turbo:frame-load": TurboFrameLoadEvent;
"turbo:frame-render": TurboFrameRenderEvent;
"turbo:frame-missing": TurboFrameMissingEvent;
"turbo:submit-start": TurboSubmitStartEvent;
"turbo:submit-end": TurboSubmitEndEvent;
}

export interface TurboGlobalEventHandlersEventMap extends TurboElementEventMap {
"turbo:before-cache": TurboBeforeCacheEvent;
"turbo:before-prefetch": TurboBeforePrefetchEvent;
"turbo:before-stream-render": TurboBeforeStreamRenderEvent;
"turbo:before-render": TurboBeforeRenderEvent;
"turbo:before-visit": TurboBeforeVisitEvent;
"turbo:click": TurboClickEvent;
"turbo:load": TurboLoadEvent;
"turbo:render": TurboRenderEvent;
"turbo:reload": TurboReloadEvent;
"turbo:visit": TurboVisitEvent;
}

declare global {
/* eslint-disable @typescript-eslint/no-empty-interface */
interface ElementEventMap extends TurboElementEventMap {}
interface GlobalEventHandlersEventMap extends TurboGlobalEventHandlersEventMap {}
/* eslint-enable @typescript-eslint/no-empty-interface */
}