Fix duplicated rich text input inside choosers (#12173)

Fixes #12002
pull/12127/merge
Sage Abdullah 2024-07-26 10:03:13 +01:00 zatwierdzone przez Matt Westcott
rodzic 4f7db41030
commit a5bb99bf67
4 zmienionych plików z 107 dodań i 45 usunięć

Wyświetl plik

@ -58,6 +58,7 @@ Changelog
* Fix: Show not allowed extension in error message (Sahil Jangra)
* Fix: Fix focal point chooser when localization enabled (Sébastien Corbin)
* Fix: Ensure that system checks for `WAGTAIL_DATE_FORMAT`, `WAGTAIL_DATETIME_FORMAT` and `WAGTAIL_TIME_FORMAT` take `FORMAT_MODULE_PATH` into account (Sébastien Corbin)
* Fix: Prevent rich text fields inside choosers from being duplicated when opened repeatedly (Sage Abdullah)
* Docs: Remove duplicate section on frontend caching proxies from performance page (Jake Howard)
* Docs: Document `restriction_type` field on PageViewRestriction (Shlomo Markowitz)
* Docs: Document Wagtail's bug bounty policy (Jake Howard)

Wyświetl plik

@ -11,52 +11,59 @@ import draftail, {
* Entry point loaded when the Draftail editor is in use.
*/
// Expose Draftail package as a global.
window.Draftail = Draftail;
// Expose module as a global.
window.draftail = draftail;
// This file is included and run when there's a DraftailRichTextArea widget in the response.
// Normally this is only included once in the initial page load, but it may be included
// more than once when there's an AJAX response that includes the widget, e.g. in choosers.
// Ensure we only run the initialization code once.
// https://github.com/wagtail/wagtail/issues/12002
if (!window.Draftail || !window.draftail) {
// Expose Draftail package as a global.
window.Draftail = Draftail;
// Expose module as a global.
window.draftail = draftail;
/** @deprecated RemovedInWagtail70 - Ensure that any third party packages that use global.chooserUrls can still append to this global object until support is removed. */
window.chooserUrls = global.chooserUrls || {};
/** @deprecated RemovedInWagtail70 - Ensure that any third party packages that use global.chooserUrls can still append to this global object until support is removed. */
window.chooserUrls = global.chooserUrls || {};
// Plugins for the built-in entities.
const entityTypes = [
{
type: 'DOCUMENT',
source: draftail.DocumentModalWorkflowSource,
decorator: Document,
},
{
type: 'LINK',
source: draftail.LinkModalWorkflowSource,
decorator: Link,
onPaste: onPasteLink,
},
{
type: 'IMAGE',
source: draftail.ImageModalWorkflowSource,
block: ImageBlock,
},
{
type: 'EMBED',
source: draftail.EmbedModalWorkflowSource,
block: EmbedBlock,
},
];
// Plugins for the built-in entities.
const entityTypes = [
{
type: 'DOCUMENT',
source: draftail.DocumentModalWorkflowSource,
decorator: Document,
},
{
type: 'LINK',
source: draftail.LinkModalWorkflowSource,
decorator: Link,
onPaste: onPasteLink,
},
{
type: 'IMAGE',
source: draftail.ImageModalWorkflowSource,
block: ImageBlock,
},
{
type: 'EMBED',
source: draftail.EmbedModalWorkflowSource,
block: EmbedBlock,
},
];
entityTypes.forEach((type) => draftail.registerPlugin(type, 'entityTypes'));
entityTypes.forEach((type) => draftail.registerPlugin(type, 'entityTypes'));
/**
* Initialize a Draftail editor on a given element when the w-draftail:init event is fired.
*/
document.addEventListener('w-draftail:init', ({ detail = {}, target }) => {
const id = target.id;
/**
* Initialize a Draftail editor on a given element when the w-draftail:init event is fired.
*/
document.addEventListener('w-draftail:init', ({ detail = {}, target }) => {
const id = target.id;
if (!id) {
// eslint-disable-next-line no-console
console.error('`w-draftail:init` event must have a target with an id.');
return;
}
if (!id) {
// eslint-disable-next-line no-console
console.error('`w-draftail:init` event must have a target with an id.');
return;
}
window.draftail.initEditor(`#${id}`, detail, document.currentScript);
});
window.draftail.initEditor(`#${id}`, detail, document.currentScript);
});
}

Wyświetl plik

@ -62,7 +62,7 @@ describe('Calling initEditor via event dispatching', () => {
jest.resetAllMocks();
});
it.only('should support creating a new editor with event dispatching', async () => {
it('should support creating a new editor with event dispatching', async () => {
expect(window.draftail.initEditor).not.toHaveBeenCalled();
document.body.innerHTML = '<main><input id="editor"></main>';
@ -106,6 +106,59 @@ describe('Calling initEditor via event dispatching', () => {
afterAll(() => {
console.error.mockRestore();
window.draftail.initEditor.mockRestore();
/* eslint-enable no-console */
});
});
describe('importing the module multiple times', () => {
it('should run the init function once if the script is included multiple times', async () => {
// Imported at the top level (similar to the initial page load)
const firstDraftail = window.draftail;
// Subsequent imports (e.g. in AJAX responses)
jest.isolateModules(() => {
require('./draftail');
});
// Should be the same instance
const secondDraftail = window.draftail;
expect(secondDraftail).toBe(firstDraftail);
jest.isolateModules(() => {
require('./draftail');
});
const thirdDraftail = window.draftail;
expect(thirdDraftail).toBe(firstDraftail);
jest.spyOn(console, 'error').mockImplementation(() => {});
jest.spyOn(window.draftail, 'initEditor').mockImplementation(() => {});
expect(window.draftail.initEditor).not.toHaveBeenCalled();
document.body.innerHTML = '<main><input id="editor"></main>';
document.getElementById('editor').dispatchEvent(
new CustomEvent('w-draftail:init', {
bubbles: true,
cancelable: false,
detail: { some: 'detail' },
}),
);
expect(console.error).toHaveBeenCalledTimes(0);
// Should only be called once. If the script isn't written correctly, then
// the window.draftail object would be a new instance every time, or
// the initEditor function would be called multiple times.
expect(window.draftail.initEditor).toHaveBeenCalledTimes(1);
expect(window.draftail.initEditor).toHaveBeenLastCalledWith(
'#editor',
{ some: 'detail' },
null,
);
/* eslint-enable no-console */
jest.clearAllMocks();
jest.restoreAllMocks();
});
});

Wyświetl plik

@ -85,6 +85,7 @@ StreamField definitions within migrations are now represented in a more compact
* Show not allowed extension in error message (Sahil Jangra)
* Fix focal point chooser when localization enabled (Sébastien Corbin)
* Ensure that system checks for `WAGTAIL_DATE_FORMAT`, `WAGTAIL_DATETIME_FORMAT` and `WAGTAIL_TIME_FORMAT` take `FORMAT_MODULE_PATH` into account (Sébastien Corbin)
* Prevent rich text fields inside choosers from being duplicated when opened repeatedly (Sage Abdullah)
### Documentation