Add sendBeacon support to ActionController

pull/12185/head
Sage Abdullah 2024-06-26 11:12:35 +01:00 zatwierdzone przez Thibaud Colas
rodzic 5168ee0c7c
commit 54f12e6354
2 zmienionych plików z 61 dodań i 5 usunięć

Wyświetl plik

@ -44,7 +44,7 @@ describe('ActionController', () => {
</button>`);
});
it('it should allow for a form POST with created data', () => {
it('should allow for a form POST with created data', () => {
const btn = document.querySelector('[data-controller="w-action"]');
const submitMock = jest.fn();
window.HTMLFormElement.prototype.submit = submitMock;
@ -59,6 +59,44 @@ describe('ActionController', () => {
});
});
describe('sendBeacon method', () => {
beforeEach(async () => {
await setup(`
<button
data-controller="w-action"
data-action="blur->w-action#sendBeacon"
data-w-action-url-value="https://analytics.example/not-interested"
>
If you move focus away from this button, a POST request will be sent.
</button>
<button id="other-button">Other button</button>
`);
});
it('should send a POST request using sendBeacon with the CSRF token included', () => {
const sendBeaconMock = jest.fn();
Object.defineProperty(window.navigator, 'sendBeacon', {
value: sendBeaconMock,
});
const btn = document.querySelector('[data-controller="w-action"]');
const otherBtn = document.getElementById('other-button');
btn.focus();
otherBtn.focus();
expect(sendBeaconMock).toHaveBeenCalledTimes(1);
expect(sendBeaconMock).toHaveBeenCalledWith(
'https://analytics.example/not-interested',
expect.any(FormData),
);
const formData = sendBeaconMock.mock.lastCall[1];
expect(
Object.fromEntries(formData.entries()).csrfmiddlewaretoken,
).toEqual('potato');
});
});
describe('click method', () => {
beforeEach(async () => {
await setup(`

Wyświetl plik

@ -26,6 +26,11 @@ import { WAGTAIL_CONFIG } from '../config/wagtailConfig';
* Enable
* </button>
*
* @example - triggering a POST request via sendBeacon
* <button data-controller="w-action" data-action="blur->w-action#sendBeacon">
* If you move focus away from this button, a POST request will be sent.
* </button>
*
* @example - triggering a dynamic redirect
* // note: a link is preferred normally
* <form>
@ -71,10 +76,7 @@ export class ActionController extends Controller<
*/
noop() {}
post(event: Event) {
event.preventDefault();
event.stopPropagation();
private createFormElement() {
const formElement = document.createElement('form');
formElement.action = this.urlValue;
@ -97,10 +99,26 @@ export class ActionController extends Controller<
formElement.appendChild(nextElement);
}
return formElement;
}
post(event: Event) {
event.preventDefault();
event.stopPropagation();
const formElement = this.createFormElement();
document.body.appendChild(formElement);
formElement.submit();
}
/**
* Like post, but uses the Beacon API, which can be used to send data
* to a server without waiting for a response. Useful for sending analytics
* data or a "release" signal before navigating away from a page.
*/
sendBeacon() {
navigator.sendBeacon(this.urlValue, new FormData(this.createFormElement()));
}
/**
* Trigger a redirect based on the custom event's detail, the Stimulus param
* or finally check the controlled element for a value to use.