Explorar el Código

Fix Draftail initialising on the wrong elt. Fix #4295 (#4301)

Thibaud Colas hace 7 años
padre
commit
18f9736c38

+ 6 - 3
client/src/components/Draftail/index.js

@@ -43,11 +43,14 @@ export const wrapWagtailIcon = type => {
 
 /**
  * Initialises the DraftailEditor for a given field.
- * @param {string} fieldName
+ * @param {string} selector
  * @param {Object} options
+ * @param {Element} currentScript
  */
-const initEditor = (fieldName, options) => {
-  const field = document.querySelector(`[name="${fieldName}"]`);
+const initEditor = (selector, options, currentScript) => {
+  // document.currentScript is not available in IE11. Use a fallback instead.
+  const context = currentScript ? currentScript.parentNode : document.body;
+  const field = context.querySelector(selector);
 
   const editorWrapper = document.createElement('div');
   editorWrapper.className = 'Draftail-Editor__wrapper';

+ 46 - 15
client/src/components/Draftail/index.test.js

@@ -10,27 +10,24 @@ import draftail, {
 describe('Draftail', () => {
   describe('#initEditor', () => {
     beforeEach(() => {
+      document.head.innerHTML = '';
       document.body.innerHTML = '';
     });
 
     it('works', () => {
-      const field = document.createElement('input');
-      field.name = 'test';
-      field.value = 'null';
-      document.body.appendChild(field);
+      document.body.innerHTML = '<input type="text" id="test" value="null" />';
+      const field = document.querySelector('#test');
 
-      draftail.initEditor('test', {});
+      draftail.initEditor('#test', {});
 
       expect(field.draftailEditor).toBeDefined();
     });
 
     it('onSave', () => {
-      const field = document.createElement('input');
-      field.name = 'test';
-      field.value = 'null';
-      document.body.appendChild(field);
+      document.body.innerHTML = '<input id="test" value="null" />';
+      const field = document.querySelector('#test');
 
-      draftail.initEditor('test', {});
+      draftail.initEditor('#test', {});
 
       field.draftailEditor.saveState();
 
@@ -38,10 +35,8 @@ describe('Draftail', () => {
     });
 
     it('options', () => {
-      const field = document.createElement('input');
-      field.name = 'test';
-      field.value = 'null';
-      document.body.appendChild(field);
+      document.body.innerHTML = '<input id="test" value="null" />';
+      const field = document.querySelector('#test');
 
       draftail.registerPlugin({
         type: 'IMAGE',
@@ -49,7 +44,7 @@ describe('Draftail', () => {
         block: () => {},
       });
 
-      draftail.initEditor('test', {
+      draftail.initEditor('#test', {
         entityTypes: [
           { type: 'IMAGE' },
         ],
@@ -58,6 +53,42 @@ describe('Draftail', () => {
 
       expect(field.draftailEditor.props).toMatchSnapshot();
     });
+
+    describe('selector conflicts', () => {
+      it('fails to instantiate on the right field', () => {
+        document.body.innerHTML = '<meta name="description" content="null" /><input name="description" value="null" />';
+
+        expect(() => {
+          draftail.initEditor('[name="description"]', {}, document.body);
+        }).toThrow(SyntaxError);
+      });
+
+      it('fails to instantiate on the right field when currentScript is not used', () => {
+        window.draftail = draftail;
+        document.body.innerHTML = `
+          <input name="first" id="description" value="null" />
+          <div>
+            <input name="last" id="description" value="null" />
+            <script>window.draftail.initEditor('#description', {});</script>
+          </div>
+        `;
+
+        expect(document.querySelector('[name="last"]').draftailEditor).not.toBeDefined();
+      });
+
+      it('has no conflict when currentScript is used', () => {
+        window.draftail = draftail;
+        document.body.innerHTML = `
+          <input name="first" id="description" value="null" />
+          <div>
+            <input name="last" id="description" value="null" />
+            <script>window.draftail.initEditor('#description', {}, document.currentScript);</script>
+          </div>
+        `;
+
+        expect(document.querySelector('[name="last"]').draftailEditor).toBeDefined();
+      });
+    });
   });
 
   describe('#wrapWagtailIcon', () => {

+ 2 - 2
wagtail/admin/rich_text/editors/draftail/__init__.py

@@ -52,8 +52,8 @@ class DraftailRichTextArea(WidgetWithScript, widgets.HiddenInput):
         return super().render(name, translated_value, attrs)
 
     def render_js_init(self, id_, name, value):
-        return "window.draftail.initEditor('{name}', {opts})".format(
-            name=name, opts=json.dumps(self.options))
+        return "window.draftail.initEditor('#{id}', {opts}, document.currentScript)".format(
+            id=id_, opts=json.dumps(self.options))
 
     def value_from_datadict(self, data, files, name):
         original_value = super().value_from_datadict(data, files, name)

+ 2 - 2
wagtail/admin/tests/test_rich_text.py

@@ -102,7 +102,7 @@ class TestDefaultRichText(BaseRichTextEditHandlerTestCase, WagtailTestUtils):
         self.assertEqual(response.status_code, 200)
 
         # Check that draftail (default editor) initialisation is applied
-        self.assertContains(response, "window.draftail.initEditor('body',")
+        self.assertContains(response, "window.draftail.initEditor('#id_body',")
 
         # check that media for draftail is being imported
         self.assertContains(response, 'wagtailadmin/js/draftail.js')
@@ -121,7 +121,7 @@ class TestDefaultRichText(BaseRichTextEditHandlerTestCase, WagtailTestUtils):
         self.assertEqual(response.status_code, 200)
 
         # Check that draftail (default editor) initialisation is applied
-        self.assertContains(response, "window.draftail.initEditor('__PREFIX__-value',")
+        self.assertContains(response, "window.draftail.initEditor('#__PREFIX__-value',")
 
         # check that media for draftail is being imported
         self.assertContains(response, 'wagtailadmin/js/draftail.js')