Przeglądaj źródła

Merge pull request #103 from ckeditor/t/ckeditor5/544

Feature: The `StandardEditor` should automatically update the contents of its source textarea upon submission of the form. Closes https://github.com/ckeditor/ckeditor5/issues/544.
Aleksander Nowodzinski 8 lat temu
rodzic
commit
27e5307756

+ 44 - 0
packages/ckeditor5-core/src/editor/standardeditor.js

@@ -10,6 +10,7 @@
 import Editor from './editor';
 import EditingKeystrokeHandler from '../editingkeystrokehandler';
 import EditingController from '@ckeditor/ckeditor5-engine/src/controller/editingcontroller';
+import isFunction from '@ckeditor/ckeditor5-utils/src/lib/lodash/isFunction';
 
 import getDataFromElement from '@ckeditor/ckeditor5-utils/src/dom/getdatafromelement';
 import setDataInElement from '@ckeditor/ckeditor5-utils/src/dom/setdatainelement';
@@ -61,6 +62,8 @@ export default class StandardEditor extends Editor {
 		 */
 
 		this.keystrokes.listenTo( this.editing.view );
+
+		this._attachToForm();
 	}
 
 	/**
@@ -103,6 +106,47 @@ export default class StandardEditor extends Editor {
 		this.setData( getDataFromElement( this.element ) );
 	}
 
+	/**
+	 * Checks if editor is initialized on textarea element that belongs to a form. If yes - updates editor's element
+	 * contents before submitting the form.
+	 *
+	 * @private
+	 */
+	_attachToForm() {
+		const element = this.element;
+
+		// Only when replacing a textarea which is inside of a form element.
+		if ( element && element.tagName.toLowerCase() === 'textarea' && element.form ) {
+			let originalSubmit;
+			const form = element.form;
+			const onSubmit = () => this.updateEditorElement();
+
+			// Replace the original form#submit() to call a custom submit function first.
+			// Check if #submit is a function because the form might have an input named "submit".
+			if ( isFunction( form.submit ) ) {
+				originalSubmit = form.submit;
+
+				form.submit = () => {
+					onSubmit();
+					originalSubmit.apply( form );
+				};
+			}
+
+			// Update the replaced textarea with data before each form#submit event.
+			form.addEventListener( 'submit', onSubmit );
+
+			// Remove the submit listener and revert the original submit method on
+			// editor#destroy.
+			this.on( 'destroy', () => {
+				form.removeEventListener( 'submit', onSubmit );
+
+				if ( originalSubmit ) {
+					form.submit = originalSubmit;
+				}
+			} );
+		}
+	}
+
 	/**
 	 * Creates a standard editor instance.
 	 *

+ 150 - 1
packages/ckeditor5-core/tests/editor/standardeditor.js

@@ -3,7 +3,7 @@
  * For licensing, see LICENSE.md.
  */
 
-/* globals document */
+/* globals document, Event */
 
 import Editor from '../../src/editor/editor';
 import StandardEditor from '../../src/editor/standardeditor';
@@ -216,4 +216,153 @@ describe( 'StandardEditor', () => {
 			expect( editor.data.set.calledWithExactly( '<p>foo</p>' ) ).to.be.true;
 		} );
 	} );
+
+	describe( 'attaching to a form', () => {
+		let editor, form, textarea, submitStub;
+
+		beforeEach( () => {
+			form = document.createElement( 'form' );
+			textarea = document.createElement( 'textarea' );
+			form.appendChild( textarea );
+			document.body.appendChild( form );
+			submitStub = sinon.stub( form, 'submit' );
+		} );
+
+		afterEach( () => {
+			submitStub.restore();
+			form.remove();
+		} );
+
+		it( 'should update editor#element after calling the submit() method', () => {
+			return createEditor( textarea )
+				.then( editor => {
+					expect( textarea.value ).to.equal( '' );
+
+					// Submit method is replaced by our implementation.
+					expect( form.submit ).to.not.equal( submitStub );
+					form.submit();
+
+					expect( textarea.value ).to.equal( '<p>foo</p>' );
+					sinon.assert.calledOnce( submitStub );
+
+					// Check if original function was called in correct context.
+					sinon.assert.calledOn( submitStub, form );
+
+					return editor.destroy();
+				} );
+		} );
+
+		it( 'should update editor#element after the "submit" event', () => {
+			return createEditor( textarea )
+				.then( editor => {
+					expect( textarea.value ).to.equal( '' );
+
+					form.dispatchEvent( new Event( 'submit' ) );
+
+					expect( textarea.value ).to.equal( '<p>foo</p>' );
+
+					return editor.destroy();
+				} );
+		} );
+
+		it( 'should not update editor#element if it is not a textarea in a form', () => {
+			const element = document.createElement( 'div' );
+			form.appendChild( element );
+
+			return createEditor( element )
+				.then( editor => {
+					expect( textarea.value ).to.equal( '' );
+
+					// Submit method is not replaced by our implementation.
+					expect( form.submit ).to.equal( submitStub );
+					form.submit();
+
+					expect( textarea.value ).to.equal( '' );
+
+					return editor.destroy();
+				} )
+				.then( () => {
+					element.remove();
+				} );
+		} );
+
+		it( 'should not update editor#element not belonging to a form', () => {
+			const textarea = document.createElement( 'textarea' );
+			document.body.appendChild( textarea );
+
+			return createEditor( textarea )
+				.then( editor => {
+					expect( textarea.value ).to.equal( '' );
+
+					// Submit method is not replaced by our implementation.
+					expect( form.submit ).to.equal( submitStub );
+					form.submit();
+
+					expect( textarea.value ).to.equal( '' );
+
+					return editor.destroy();
+				} )
+				.then( () => {
+					textarea.remove();
+				} );
+		} );
+
+		it( 'should not update editor#element after destruction of the editor - form.submit()', () => {
+			return createEditor( textarea )
+				.then( editor => editor.destroy() )
+				.then( () => {
+					expect( textarea.value ).to.equal( '' );
+
+					// Submit method is no longer replaced by our implementation.
+					expect( form.submit ).to.equal( submitStub );
+					form.submit();
+
+					expect( textarea.value ).to.equal( '' );
+				} );
+		} );
+
+		it( 'should not update the editor#element after destruction of the editor - "submit" event', () => {
+			return createEditor( textarea )
+				.then( editor => editor.destroy() )
+				.then( () => {
+					expect( textarea.value ).to.equal( '' );
+
+					form.dispatchEvent( new Event( 'submit' ) );
+
+					expect( textarea.value ).to.equal( '' );
+				} );
+		} );
+
+		it( 'should not replace submit() method when one of the elements in a form is named "submit"', () => {
+			const input = document.createElement( 'input' );
+			input.setAttribute( 'name', 'submit' );
+			form.appendChild( input );
+
+			return createEditor( textarea )
+				.then( () => {
+					expect( form.submit ).to.equal( input );
+					expect( textarea.value ).to.equal( '' );
+
+					form.dispatchEvent( new Event( 'submit' ) );
+
+					expect( textarea.value ).to.equal( '<p>foo</p>' );
+
+					return editor.destroy();
+				} )
+				.then( () => {
+					expect( form.submit ).to.equal( input );
+					input.remove();
+				} );
+		} );
+
+		function createEditor( element ) {
+			return StandardEditor.create( element )
+				.then( newEditor => {
+					editor = newEditor;
+					editor.data.get = () => '<p>foo</p>';
+
+					return editor;
+				} );
+		}
+	} );
 } );

+ 16 - 0
packages/ckeditor5-core/tests/manual/formsubmit.html

@@ -0,0 +1,16 @@
+<style>
+	textarea#editor {
+		display: block !important;
+		width: 100%;
+		height: 100px;
+		margin-bottom: 20px;
+	}
+</style>
+
+<form id="form" action="http://localhost/" method="post">
+	<p>Editor textarea element (normally hidden): </p>
+	<textarea name="editor" id="editor"><p>foo bar baz</p></textarea>
+	<button type="submit">Submit form</button>
+</form>
+<br />
+<button id="submit-with-js">Submit by calling form.submit()</button>

+ 33 - 0
packages/ckeditor5-core/tests/manual/formsubmit.js

@@ -0,0 +1,33 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals console, window, document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import ArticlePreset from '@ckeditor/ckeditor5-presets/src/article';
+
+// Replace original submit method to prevent page reload.
+document.getElementById( 'form' ).submit = () => {};
+
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		plugins: [ ArticlePreset ],
+		toolbar: [ 'bold', 'italic', 'undo', 'redo' ],
+	} )
+	.then( editor => {
+		window.editor = editor;
+		const form = document.getElementById( 'form' );
+
+		document.getElementById( 'submit-with-js' ).addEventListener( 'click', () => {
+			form.submit();
+		} );
+
+		form.addEventListener( 'submit', evt => {
+			evt.preventDefault();
+		} );
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );

+ 4 - 0
packages/ckeditor5-core/tests/manual/formsubmit.md

@@ -0,0 +1,4 @@
+## Updating editor element when submitting a form
+
+1. Change the content of the editor and press the "Submit form" button. Check if the content of the editor element has been updated.
+1. Change the content of the editor again and press "Submit by calling form.submit()" button. Check if the content of the editor element has been updated.