diff --git a/tinymce-angular-component/src/main/ts/editor/editor.component.ts b/tinymce-angular-component/src/main/ts/editor/editor.component.ts index 6e9c976a..621da044 100644 --- a/tinymce-angular-component/src/main/ts/editor/editor.component.ts +++ b/tinymce-angular-component/src/main/ts/editor/editor.component.ts @@ -110,7 +110,13 @@ export class EditorComponent extends Events implements AfterViewInit, ControlVal public writeValue(value: string | null): void { if (this._editor && this._editor.initialized) { + const cursor = this._editor.selection.getBookmark(3); this._editor.setContent(isNullOrUndefined(value) ? '' : value); + try { + this._editor.selection.moveToBookmark(cursor); + } catch (e) { + /* ignore */ + } } else { this.initialValue = value === null ? undefined : value; } diff --git a/tinymce-angular-component/src/test/ts/browser/FormControlTest.ts b/tinymce-angular-component/src/test/ts/browser/FormControlTest.ts index 56e29a3a..8029bb6b 100644 --- a/tinymce-angular-component/src/test/ts/browser/FormControlTest.ts +++ b/tinymce-angular-component/src/test/ts/browser/FormControlTest.ts @@ -8,12 +8,13 @@ import { Assertions, Chain, Log, Pipeline } from '@ephox/agar'; import { UnitTest } from '@ephox/bedrock-client'; import { VersionLoader } from '@tinymce/miniature'; +import { Editor } from 'tinymce'; import { EditorComponent, EditorModule } from '../../../main/ts/public_api'; import { Version } from '../../../main/ts/editor/editor.component'; UnitTest.asynctest('FormControlTest', (success, failure) => { @Component({ - template: `` + template: ``, }) class EditorWithFormControl { public control = new FormControl(); @@ -22,7 +23,7 @@ UnitTest.asynctest('FormControlTest', (success, failure) => { interface TestContext { testComponent: EditorWithFormControl; fixture: ComponentFixture; - editor: any; + editor: Editor; } const cSetupEditorWithFormControl = Chain.async((_, next) => { @@ -54,43 +55,78 @@ UnitTest.asynctest('FormControlTest', (success, failure) => { TestBed.resetTestingModule(); }); - const sTestVersion = (version: Version) => VersionLoader.sWithVersion( - version, - Log.chainsAsStep('', 'FormControl interaction ', [ - cSetupEditorWithFormControl, - Chain.op((context: TestContext) => { - Assertions.assertEq( - 'Expect editor to have no initial value', - '', - context.editor.getContent() - ); - - context.testComponent.control.setValue('

Some Value

'); - context.fixture.detectChanges(); - - Assertions.assertEq( - 'Expect editor to have a value', - '

Some Value

', - context.editor.getContent() - ); - - context.testComponent.control.reset(); - context.fixture.detectChanges(); - - Assertions.assertEq( - 'Expect editor to be empty after reset', - '', - context.editor.getContent() - ); - }), - cTeardown - ]) - ); + const setFormValueAndReset = Chain.op((context: TestContext) => { + Assertions.assertEq('Expect editor to have no initial value', '', context.editor.getContent()); + + context.testComponent.control.setValue('

Some Value

'); + context.fixture.detectChanges(); + + Assertions.assertEq( + 'Expect editor to have a value', + '

Some Value

', + context.editor.getContent() + ); + + context.testComponent.control.reset(); + context.fixture.detectChanges(); + + Assertions.assertEq('Expect editor to be empty after reset', '', context.editor.getContent()); + }); + + /** Sets content and set cursor at the end, just like when a real user types */ + const doFakeType = (str: string) => + Chain.op((context: TestContext) => { + context.testComponent.control.patchValue(`${str}`); + context.fixture.detectChanges(); + + const rng = context.editor.dom.createRng(); + const firstChild = context.editor.getBody().firstChild?.firstChild as Text; + rng.setStart(firstChild, str.length); + rng.setEnd(firstChild, str.length); - Pipeline.async({}, [ - sTestVersion('4'), - sTestVersion('5'), - sTestVersion('6'), - sTestVersion('7') - ], success, failure); -}); \ No newline at end of file + context.editor.selection.setRng(rng); + context.fixture.detectChanges(); + }); + + const checkCursor = Chain.op((context: TestContext) => { + const cursorStartPosition = context.editor.selection.getRng().startOffset; + const cursorEndPosition = context.editor.selection.getRng().endOffset; + const content = context.editor.getContent(); + const strippedContent = content.replace(/<\/?[^>]+(>|$)/g, ''); + Assertions.assertEq( + 'Expect cursor start position to be at the end of the content', + cursorStartPosition, + strippedContent.length + ); + + Assertions.assertEq( + 'Expect cursor end position to be at the end of the content', + cursorEndPosition, + strippedContent.length + ); + }); + + const sTestVersion = (version: Version) => + VersionLoader.sWithVersion( + version, + Log.chainsAsStep('', 'FormControl interaction ', [ + cSetupEditorWithFormControl, + setFormValueAndReset, + doFakeType('test'), + checkCursor, + cTeardown, + ]) + ); + + Pipeline.async( + {}, + [ + sTestVersion('4'), + sTestVersion('5'), + sTestVersion('6'), + sTestVersion('7'), + ], + success, + failure + ); +});