Skip to content

Commit 7147d5d

Browse files
committed
feature #587 [Live] Adding a response:error hook (weaverryan)
This PR was merged into the 2.x branch. Discussion ---------- [Live] Adding a response:error hook | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Tickets | Fix #488 | License | MIT Pretty simple - as requested in #488. Someone using this hook could set `controls.displayError = false` to prevent live components from rendering the error in a modal. Cheers! Commits ------- 96c698a [Live] Adding a response:error hook
2 parents e4a7bab + 96c698a commit 7147d5d

File tree

3 files changed

+44
-3
lines changed

3 files changed

+44
-3
lines changed

src/LiveComponent/assets/src/Component/index.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export default class Component {
106106
* * disconnect (component: Component) => {}
107107
* * render:started (html: string, response: BackendResponse, controls: { shouldRender: boolean }) => {}
108108
* * render:finished (component: Component) => {}
109+
* * response:error (backendResponse: BackendResponse, controls: { displayError: boolean }) => {}
109110
* * loading.state:started (element: HTMLElement, request: BackendRequest) => {}
110111
* * loading.state:finished (element: HTMLElement) => {}
111112
* * model:set (model: string, value: any, component: Component) => {}
@@ -145,7 +146,7 @@ export default class Component {
145146
return this.valueStore.get(modelName);
146147
}
147148

148-
action(name: string, args: any, debounce: number|boolean = false): Promise<BackendResponse> {
149+
action(name: string, args: any = {}, debounce: number|boolean = false): Promise<BackendResponse> {
149150
const promise = this.nextRequestPromise;
150151
this.pendingActions.push({
151152
name,
@@ -301,10 +302,16 @@ export default class Component {
301302
const backendResponse = new BackendResponse(response);
302303
thisPromiseResolve(backendResponse);
303304
const html = await backendResponse.getBody();
305+
304306
// if the response does not contain a component, render as an error
305307
const headers = backendResponse.response.headers;
306308
if (headers.get('Content-Type') !== 'application/vnd.live-component+html' && !headers.get('X-Live-Redirect')) {
307-
this.renderError(html);
309+
const controls = { displayError: true };
310+
this.hooks.triggerHook('response:error', backendResponse, controls);
311+
312+
if (controls.displayError) {
313+
this.renderError(html);
314+
}
308315

309316
return response;
310317
}

src/LiveComponent/assets/test/controller/error.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111

1212
import { createTest, initComponent, shutdownTests } from '../tools';
1313
import { getByText, waitFor } from '@testing-library/dom';
14+
import BackendResponse from '../../src/BackendResponse';
15+
16+
const getErrorElement = (): Element|null => {
17+
return document.getElementById('live-component-error');
18+
};
1419

1520
describe('LiveController Error Handling', () => {
1621
afterEach(() => {
@@ -39,7 +44,7 @@ describe('LiveController Error Handling', () => {
3944
await waitFor(() => expect(document.getElementById('live-component-error')).not.toBeNull());
4045
// the component did not change or re-render
4146
expect(test.element).toHaveTextContent('Original component text');
42-
const errorContainer = document.getElementById('live-component-error');
47+
const errorContainer = getErrorElement();
4348
if (!errorContainer) {
4449
throw new Error('containing missing');
4550
}
@@ -69,4 +74,32 @@ describe('LiveController Error Handling', () => {
6974
// the component did not change or re-render
7075
expect(test.element).toHaveTextContent('Original component text');
7176
});
77+
78+
it('triggers response:error hook', async () => {
79+
const test = await createTest({ }, (data: any) => `
80+
<div ${initComponent(data)}>
81+
component text
82+
</div>
83+
`);
84+
85+
test.expectsAjaxCall('post')
86+
.expectSentData(test.initialData)
87+
.serverWillReturnCustomResponse(200, `
88+
<html><head><title>Hi!</title></head><body><h1>I'm a whole page, not a component!</h1></body></html>
89+
`)
90+
.expectActionCalled('save')
91+
.init();
92+
93+
let isHookCalled = false;
94+
test.component.on('response:error', (backendResponse: BackendResponse, controls) => {
95+
isHookCalled = true;
96+
controls.displayError = false;
97+
});
98+
99+
await test.component.action('save');
100+
101+
await waitFor(() => expect(isHookCalled).toBe(true));
102+
const errorContainer = getErrorElement();
103+
expect(errorContainer).toBeNull();
104+
});
72105
});

src/LiveComponent/doc/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ The following hooks are available (along with the arguments that are passed):
471471
* ``disconnect`` args ``(component: Component)``
472472
* ``render:started`` args ``(html: string, response: BackendResponse, controls: { shouldRender: boolean })``
473473
* ``render:finished`` args ``(component: Component)``
474+
* ``response:error`` args ``(backendResponse: BackendResponse, controls: { displayError: boolean })``
474475
* ``loading.state:started`` args ``(element: HTMLElement, request: BackendRequest)``
475476
* ``loading.state:finished`` args ``(element: HTMLElement)``
476477
* ``model:set`` args ``(model: string, value: any, component: Component)``

0 commit comments

Comments
 (0)