Skip to content

Commit cda346d

Browse files
committed
feat(ajax): Support Turbo Stream responses
Useful for handling turbo stream responses in JS code
1 parent 8619d63 commit cda346d

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

app/javascript/alchemy_admin/utils/ajax.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const JSON_CONTENT_TYPE = "application/json"
2+
const TURBO_STREAM_CONTENT_TYPE = "text/vnd.turbo-stream.html"
23

34
function isGetRequest(method) {
45
return method.toLowerCase() === "get"
@@ -43,8 +44,8 @@ export function get(url, params) {
4344
return ajax("GET", url, params)
4445
}
4546

46-
export function patch(url, data) {
47-
return ajax("PATCH", url, data)
47+
export function patch(url, data, accept) {
48+
return ajax("PATCH", url, data, accept)
4849
}
4950

5051
export function post(url, data, accept = JSON_CONTENT_TYPE) {
@@ -63,7 +64,18 @@ export default async function ajax(
6364
)
6465
const contentType = response.headers.get("content-type")
6566
const isJson = contentType?.includes(JSON_CONTENT_TYPE)
66-
const responseData = isJson ? await response.json() : null
67+
const isTurboStream = contentType?.includes(TURBO_STREAM_CONTENT_TYPE)
68+
69+
let responseData = null
70+
if (isJson) {
71+
responseData = await response.json()
72+
} else if (isTurboStream) {
73+
responseData = await response.text()
74+
// Automatically render Turbo Stream if Turbo is available
75+
if (typeof Turbo !== "undefined") {
76+
Turbo.renderStreamMessage(responseData)
77+
}
78+
}
6779

6880
if (response.ok) {
6981
return { data: responseData, status: response.status }

spec/javascript/alchemy_admin/utils/ajax.spec.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,28 @@ describe("ajax utilities", () => {
145145
expect(ajaxSpy).toHaveBeenCalled()
146146
expect(response.data).toEqual({ success: true })
147147
})
148+
149+
it("supports Turbo Stream responses", async () => {
150+
global.Turbo = { renderStreamMessage: vi.fn() }
151+
const turboStreamResponse = {
152+
ok: true,
153+
headers: { get: () => "text/vnd.turbo-stream.html" },
154+
text: async () => "<turbo-stream>...</turbo-stream>"
155+
}
156+
157+
global.fetch.mockResolvedValueOnce(turboStreamResponse)
158+
159+
const response = await patch("/test", null, "text/vnd.turbo-stream.html")
160+
161+
expect(global.Turbo.renderStreamMessage).toHaveBeenCalledWith(
162+
"<turbo-stream>...</turbo-stream>"
163+
)
164+
expect(response.data).toBe("<turbo-stream>...</turbo-stream>")
165+
})
148166
})
149167

150168
afterEach(() => {
151169
vi.resetAllMocks()
170+
delete global.Turbo
152171
})
153172
})

0 commit comments

Comments
 (0)