Sfoglia il codice sorgente

Add a higher-level API for chooser modals

Previously, anything invoking the chooser modal needed to make its own call to ModalWorkflow, which meant it needed to import the corresponding 'onloadHandlers' dict, know the appropriate chosen response identifier to listen to, and know how to modify the chooser URL to pass parameters (if applicable). This would mean a lot of duplicated logic if there were multiple places where the modal is invoked.

Here we introduce a ChooserModal base class which encapsulates those details - a caller just needs to instantiate it with the base URL, and call `open` on it to open the modal (passing an options dict and a response callback).
Matt Westcott 2 anni fa
parent
commit
947a7883f9

+ 11 - 20
client/src/components/ChooserWidget/index.js

@@ -1,11 +1,9 @@
-import { chooserModalOnloadHandlers } from '../../includes/chooserModal';
+import { ChooserModal } from '../../includes/chooserModal';
 
 export class Chooser {
-  modalOnloadHandlers = chooserModalOnloadHandlers;
-
+  chooserModalClass = ChooserModal;
   titleStateKey = 'title'; // key used in the 'state' dictionary to hold the human-readable title
   editUrlStateKey = 'edit_url'; // key used in the 'state' dictionary to hold the URL of the edit page
-  chosenResponseName = 'chosen'; // identifier for the ModalWorkflow response that indicates an item was chosen
 
   constructor(id) {
     this.initHTMLElements(id);
@@ -30,7 +28,6 @@ export class Chooser {
     );
     this.input = document.getElementById(id);
     this.editLink = this.chooserElement.querySelector('.edit-link');
-    this.chooserBaseUrl = this.chooserElement.dataset.chooserUrl;
   }
 
   getStateFromHTML() {
@@ -117,25 +114,19 @@ export class Chooser {
     }
   }
 
-  getModalUrl() {
-    return this.chooserBaseUrl;
-  }
-
-  getModalUrlParams() {
+  getModalOptions() {
     return null;
   }
 
   openChooserModal() {
-    // eslint-disable-next-line no-undef
-    ModalWorkflow({
-      url: this.getModalUrl(),
-      urlParams: this.getModalUrlParams(),
-      onload: this.modalOnloadHandlers,
-      responses: {
-        [this.chosenResponseName]: (result) => {
-          this.setState(result);
-        },
-      },
+    if (!this.modal) {
+      // eslint-disable-next-line new-cap
+      this.modal = new this.chooserModalClass(
+        this.chooserElement.dataset.chooserUrl,
+      );
+    }
+    this.modal.open(this.getModalOptions(), (result) => {
+      this.setState(result);
     });
   }
 }

+ 33 - 0
client/src/entrypoints/admin/page-chooser-modal.js

@@ -1,4 +1,5 @@
 import $ from 'jquery';
+import { ChooserModal } from '../../includes/chooserModal';
 import { initTooltips } from '../../includes/initTooltips';
 
 /* global wagtail */
@@ -208,3 +209,35 @@ const PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS = {
   },
 };
 window.PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS = PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS;
+
+class PageChooserModal extends ChooserModal {
+  onloadHandlers = PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS;
+  chosenResponseName = 'pageChosen';
+
+  getURL(opts) {
+    let url = super.getURL();
+    if (opts.parentId) {
+      url += opts.parentId + '/';
+    }
+    return url;
+  }
+
+  getURLParams(opts) {
+    const urlParams = super.getURLParams(opts);
+    urlParams.page_type = opts.model_names.join(',');
+    if (opts.target_pages) {
+      urlParams.target_pages = opts.target_pages;
+    }
+    if (opts.match_subclass) {
+      urlParams.match_subclass = opts.match_subclass;
+    }
+    if (opts.can_choose_root) {
+      urlParams.can_choose_root = 'true';
+    }
+    if (opts.user_perms) {
+      urlParams.user_perms = opts.user_perms;
+    }
+    return urlParams;
+  }
+}
+window.PageChooserModal = PageChooserModal;

+ 13 - 24
client/src/entrypoints/admin/page-chooser.js

@@ -2,12 +2,12 @@ import { Chooser } from '../../components/ChooserWidget';
 
 class PageChooser extends Chooser {
   // eslint-disable-next-line no-undef
-  modalOnloadHandlers = PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS;
+  chooserModalClass = PageChooserModal;
+
   titleStateKey = 'adminTitle';
   editUrlStateKey = 'editUrl';
-  chosenResponseName = 'pageChosen';
 
-  constructor(id, parentId, options) {
+  constructor(id, parentId, options = {}) {
     super(id);
     this.initialParentId = parentId;
     this.options = options;
@@ -21,29 +21,18 @@ class PageChooser extends Chooser {
     return state;
   }
 
-  getModalUrl() {
-    let url = super.getModalUrl();
+  getModalOptions() {
+    const opts = {
+      model_names: this.options.model_names,
+      target_pages: this.options.target_pages,
+      match_subclass: this.options.match_subclass,
+      can_choose_root: this.options.can_choose_root,
+      user_perms: this.options.user_perms,
+    };
     if (this.state && this.state.parentId) {
-      url += this.state.parentId + '/';
-    }
-    return url;
-  }
-
-  getModalUrlParams() {
-    const urlParams = { page_type: this.options.model_names.join(',') };
-    if (this.options.target_pages) {
-      urlParams.target_pages = this.options.target_pages;
-    }
-    if (this.options.match_subclass) {
-      urlParams.match_subclass = this.options.match_subclass;
-    }
-    if (this.options.can_choose_root) {
-      urlParams.can_choose_root = 'true';
-    }
-    if (this.options.user_perms) {
-      urlParams.user_perms = this.options.user_perms;
+      opts.parentId = this.state.parentId;
     }
-    return urlParams;
+    return opts;
   }
 }
 window.PageChooser = PageChooser;

+ 9 - 1
client/src/entrypoints/documents/document-chooser-modal.js

@@ -1,5 +1,8 @@
 import $ from 'jquery';
-import { ChooserModalOnloadHandlerFactory } from '../../includes/chooserModal';
+import {
+  ChooserModalOnloadHandlerFactory,
+  ChooserModal,
+} from '../../includes/chooserModal';
 
 class DocumentChooserModalOnloadHandlerFactory extends ChooserModalOnloadHandlerFactory {
   ajaxifyLinks(modal, context) {
@@ -25,3 +28,8 @@ window.DOCUMENT_CHOOSER_MODAL_ONLOAD_HANDLERS =
     creationFormTabSelector: '#tab-upload',
     creationFormEventName: 'wagtail:documents-upload',
   }).getOnLoadHandlers();
+
+class DocumentChooserModal extends ChooserModal {
+  onloadHandlers = window.DOCUMENT_CHOOSER_MODAL_ONLOAD_HANDLERS;
+}
+window.DocumentChooserModal = DocumentChooserModal;

+ 1 - 1
client/src/entrypoints/documents/document-chooser.js

@@ -2,7 +2,7 @@ import { Chooser } from '../../components/ChooserWidget';
 
 class DocumentChooser extends Chooser {
   // eslint-disable-next-line no-undef
-  modalOnloadHandlers = DOCUMENT_CHOOSER_MODAL_ONLOAD_HANDLERS;
+  chooserModalClass = DocumentChooserModal;
 }
 window.DocumentChooser = DocumentChooser;
 

+ 9 - 1
client/src/entrypoints/images/image-chooser-modal.js

@@ -1,5 +1,8 @@
 import $ from 'jquery';
-import { ChooserModalOnloadHandlerFactory } from '../../includes/chooserModal';
+import {
+  ChooserModalOnloadHandlerFactory,
+  ChooserModal,
+} from '../../includes/chooserModal';
 
 class ImageChooserModalOnloadHandlerFactory extends ChooserModalOnloadHandlerFactory {
   ajaxifyLinks(modal, context) {
@@ -111,3 +114,8 @@ window.IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS =
     creationFormEventName: 'wagtail:images-upload',
     creationFormTabSelector: '#tab-upload',
   }).getOnLoadHandlers();
+
+class ImageChooserModal extends ChooserModal {
+  onloadHandlers = window.IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS;
+}
+window.ImageChooserModal = ImageChooserModal;

+ 1 - 1
client/src/entrypoints/images/image-chooser.js

@@ -2,7 +2,7 @@ import { Chooser } from '../../components/ChooserWidget';
 
 class ImageChooser extends Chooser {
   // eslint-disable-next-line no-undef
-  modalOnloadHandlers = IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS;
+  chooserModalClass = ImageChooserModal;
 
   initHTMLElements(id) {
     super.initHTMLElements(id);

+ 11 - 7
client/src/entrypoints/snippets/snippet-chooser.js

@@ -1,22 +1,26 @@
+import { ChooserModal } from '../../includes/chooserModal';
 import { Chooser } from '../../components/ChooserWidget';
 
 /* global wagtailConfig */
 
-class SnippetChooser extends Chooser {
-  titleStateKey = 'string';
-
-  getModalUrl() {
-    let urlQuery = '';
+class SnippetChooserModal extends ChooserModal {
+  getURLParams(opts) {
+    const params = super.getURLParams(opts);
     if (wagtailConfig.ACTIVE_CONTENT_LOCALE) {
       // The user is editing a piece of translated content.
       // Pass the locale along as a request parameter. If this
       // snippet is also translatable, the results will be
       // pre-filtered by this locale.
-      urlQuery = '?locale=' + wagtailConfig.ACTIVE_CONTENT_LOCALE;
+      params.locale = wagtailConfig.ACTIVE_CONTENT_LOCALE;
     }
-    return this.chooserBaseUrl + urlQuery;
+    return params;
   }
 }
+
+class SnippetChooser extends Chooser {
+  titleStateKey = 'string';
+  chooserModalClass = SnippetChooserModal;
+}
 window.SnippetChooser = SnippetChooser;
 
 function createSnippetChooser(id) {

+ 34 - 0
client/src/includes/chooserModal.js

@@ -313,6 +313,39 @@ class ChooserModalOnloadHandlerFactory {
 const chooserModalOnloadHandlers =
   new ChooserModalOnloadHandlerFactory().getOnLoadHandlers();
 
+class ChooserModal {
+  onloadHandlers = chooserModalOnloadHandlers;
+  chosenResponseName = 'chosen'; // identifier for the ModalWorkflow response that indicates an item was chosen
+
+  constructor(baseUrl) {
+    this.baseUrl = baseUrl;
+  }
+
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  getURL(opts) {
+    return this.baseUrl;
+  }
+
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  getURLParams(opts) {
+    return {};
+  }
+
+  open(opts, callback) {
+    // eslint-disable-next-line no-undef
+    ModalWorkflow({
+      url: this.getURL(opts || {}),
+      urlParams: this.getURLParams(opts || {}),
+      onload: this.onloadHandlers,
+      responses: {
+        [this.chosenResponseName]: (result) => {
+          callback(result);
+        },
+      },
+    });
+  }
+}
+
 export {
   validateCreationForm,
   submitCreationForm,
@@ -320,4 +353,5 @@ export {
   SearchController,
   ChooserModalOnloadHandlerFactory,
   chooserModalOnloadHandlers,
+  ChooserModal,
 };

+ 3 - 0
client/tests/stubs.js

@@ -55,3 +55,6 @@ global.IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS = { type: 'image' };
 global.PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS = { type: 'page' };
 global.EMBED_CHOOSER_MODAL_ONLOAD_HANDLERS = { type: 'embed' };
 global.DOCUMENT_CHOOSER_MODAL_ONLOAD_HANDLERS = { type: 'document' };
+
+class PageChooserModal {}
+global.PageChooserModal = PageChooserModal;