diff --git a/Gruntfile.js b/Gruntfile.js index 995a9f2f79..bbb6d27962 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -63,6 +63,9 @@ const bedrockHeadless = (tests, browser, auto) => { name: 'headless-tests', browser, testfiles: testFolders(tests, auto), + + // we have a few tests that don't play nicely when combined together in the monorepo + retries: 3 } } } diff --git a/modules/tinymce/CHANGELOG.md b/modules/tinymce/CHANGELOG.md index dea2d67983..7590e52b5b 100644 --- a/modules/tinymce/CHANGELOG.md +++ b/modules/tinymce/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed +- The `end_container_on_empty_block` option can now take a string of blocks to split when pressing Enter twice #TINY-6559 +- The default value for `end_container_on_empty_block` option has been changed to `'blockquote'` #TINY-6559 + ### Fixed - Dialogs will not exceed the window height on smaller screens #TINY-8146 diff --git a/modules/tinymce/src/core/main/ts/api/OptionTypes.ts b/modules/tinymce/src/core/main/ts/api/OptionTypes.ts index b4cacba7ef..32509f31d5 100644 --- a/modules/tinymce/src/core/main/ts/api/OptionTypes.ts +++ b/modules/tinymce/src/core/main/ts/api/OptionTypes.ts @@ -89,7 +89,7 @@ interface BaseEditorOptions { element_format?: 'xhtml' | 'html'; elementpath?: boolean; encoding?: string; - end_container_on_empty_block?: boolean; + end_container_on_empty_block?: boolean | string; entities?: string; entity_encoding?: EntityEncoding; extended_valid_elements?: string; diff --git a/modules/tinymce/src/core/main/ts/api/Options.ts b/modules/tinymce/src/core/main/ts/api/Options.ts index bace4a73dd..9746f628a0 100644 --- a/modules/tinymce/src/core/main/ts/api/Options.ts +++ b/modules/tinymce/src/core/main/ts/api/Options.ts @@ -155,8 +155,16 @@ const register = (editor: Editor) => { }); registerOption('end_container_on_empty_block', { - processor: 'boolean', - default: false + processor: (value) => { + if (Type.isBoolean(value)) { + return { valid: true, value }; + } else if (Type.isString(value)) { + return { valid: true, value }; + } else { + return { valid: false, message: 'Must be boolean or a string' }; + } + }, + default: 'blockquote' }); registerOption('font_size_style_values', { diff --git a/modules/tinymce/src/core/main/ts/newline/InsertBlock.ts b/modules/tinymce/src/core/main/ts/newline/InsertBlock.ts index 645aa0d47c..74f3ab4df3 100644 --- a/modules/tinymce/src/core/main/ts/newline/InsertBlock.ts +++ b/modules/tinymce/src/core/main/ts/newline/InsertBlock.ts @@ -1,4 +1,4 @@ -import { Arr, Obj, Optional, Optionals } from '@ephox/katamari'; +import { Arr, Obj, Optional, Optionals, Type } from '@ephox/katamari'; import { Css, PredicateFilter, SugarElement, SugarNode } from '@ephox/sugar'; import DOMUtils from '../api/dom/DOMUtils'; @@ -6,6 +6,7 @@ import DomTreeWalker from '../api/dom/TreeWalker'; import Editor from '../api/Editor'; import * as Options from '../api/Options'; import { EditorEvent } from '../api/util/EventDispatcher'; +import Tools from '../api/util/Tools'; import * as Bookmarks from '../bookmark/Bookmarks'; import * as CaretContainer from '../caret/CaretContainer'; import * as NodeType from '../dom/NodeType'; @@ -235,6 +236,17 @@ const addBrToBlockIfNeeded = (dom, block) => { } }; +const shouldEndContainer = (editor: Editor, container: Node | undefined) => { + const optionValue = Options.shouldEndContainerOnEmptyBlock(editor); + if (Type.isNullable(container)) { + return false; + } else if (Type.isString(optionValue)) { + return Arr.contains(Tools.explode(optionValue), container.nodeName.toLowerCase()); + } else { + return optionValue; + } +}; + const insert = (editor: Editor, evt?: EditorEvent) => { let tmpRng, container, offset, parentBlock; let newBlock, fragment, containerBlock, parentBlockName, isAfterLastNodeInContainer; @@ -361,7 +373,7 @@ const insert = (editor: Editor, evt?: EditorEvent) => { } // Split the current container block element if enter is pressed inside an empty inner block element - if (Options.shouldEndContainerOnEmptyBlock(editor) && canSplitBlock(dom, containerBlock) && dom.isEmpty(parentBlock)) { + if (shouldEndContainer(editor, containerBlock) && canSplitBlock(dom, containerBlock) && dom.isEmpty(parentBlock)) { // Split container block for example a BLOCKQUOTE at the current blockParent location for example a P newBlock = dom.split(containerBlock, parentBlock); } else { diff --git a/modules/tinymce/src/core/test/ts/browser/newline/InsertNewLineTest.ts b/modules/tinymce/src/core/test/ts/browser/newline/InsertNewLineTest.ts index 1d33fc5f52..15c661e990 100644 --- a/modules/tinymce/src/core/test/ts/browser/newline/InsertNewLineTest.ts +++ b/modules/tinymce/src/core/test/ts/browser/newline/InsertNewLineTest.ts @@ -162,4 +162,326 @@ describe('browser.tinymce.core.newline.InsertNewLineTest', () => { TinyAssertions.assertContent(editor, '

a

'); TinyAssertions.assertSelection(editor, [ 1, 0 ], 0, [ 1, 0 ], 0); }); + + context('end_container_on_empty_block', () => { + context('With the default value', () => { + it('TINY-6559: Press Enter in blockquote', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 0, 2 ], 0, [ 0, 2 ], 0); + }); + + it('TINY-6559: Press Shift+Enter in blockquote', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { shiftKey: true }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

'); + TinyAssertions.assertSelection(editor, [ 0, 1 ], 2, [ 0, 1 ], 2); + }); + + it('TINY-6559: Press Enter twice in blockquote', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + + it('TINY-6559: Press Enter twice in blockquote while between two lines', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 0 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

 

Line 2

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + + it('TINY-6559: Press Enter twice in a div', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

 

'); + TinyAssertions.assertSelection(editor, [ 0, 3 ], 0, [ 0, 3 ], 0); + }); + + it('TINY-6559: Press Enter twice in a section', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

 

'); + TinyAssertions.assertSelection(editor, [ 0, 3 ], 0, [ 0, 3 ], 0); + }); + }); + + context('Is set to "div"', () => { + it('TINY-6559: Press Enter in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 0, 2 ], 0, [ 0, 2 ], 0); + }); + + it('TINY-6559: Press Shift+Enter in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { shiftKey: true }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

'); + TinyAssertions.assertSelection(editor, [ 0, 1 ], 2, [ 0, 1 ], 2); + }); + + it('TINY-6559: Press Enter twice in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

 

'); + TinyAssertions.assertSelection(editor, [ 0, 3 ], 0, [ 0, 3 ], 0); + }); + + it('TINY-6559: Press Enter twice in blockquote while between two lines', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 0 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

 

 

Line 2

'); + TinyAssertions.assertSelection(editor, [ 0, 2 ], 0, [ 0, 2 ], 0); + }); + + it('TINY-6559: Press Enter twice in a div', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + + it('TINY-6559: Press Enter twice in a section', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

 

'); + TinyAssertions.assertSelection(editor, [ 0, 3 ], 0, [ 0, 3 ], 0); + }); + }); + + context('Is set to "div,blockquote"', () => { + it('TINY-6559: Press Enter in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div,blockquote'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 0, 2 ], 0, [ 0, 2 ], 0); + }); + + it('TINY-6559: Press Shift+Enter in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div,blockquote'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { shiftKey: true }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

'); + TinyAssertions.assertSelection(editor, [ 0, 1 ], 2, [ 0, 1 ], 2); + }); + + it('TINY-6559: Press Enter twice in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div,blockquote'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + + it('TINY-6559: Press Enter twice in blockquote while between two lines', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div,blockquote'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 0 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

 

Line 2

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + + it('TINY-6559: Press Enter twice in a div', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', 'div,blockquote'); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + + it('TINY-6559: Press Enter twice in a section', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

 

'); + TinyAssertions.assertSelection(editor, [ 0, 3 ], 0, [ 0, 3 ], 0); + }); + }); + + context('Is set to true', () => { + it('TINY-6559: Press Enter in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', true); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 0, 2 ], 0, [ 0, 2 ], 0); + }); + + it('TINY-6559: Press Shift+Enter in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', true); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { shiftKey: true }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

'); + TinyAssertions.assertSelection(editor, [ 0, 1 ], 2, [ 0, 1 ], 2); + }); + + it('TINY-6559: Press Enter twice in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', true); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + + it('TINY-6559: Press Enter twice in blockquote while between two lines', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', true); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 0 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

 

Line 2

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + + it('TINY-6559: Press Enter twice in a div', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', true); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + + it('TINY-6559: Press Enter twice in a section', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 1 ], 0, [ 1 ], 0); + }); + }); + + context('Is set to false', () => { + it('TINY-6559: Press Enter in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', false); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

'); + TinyAssertions.assertSelection(editor, [ 0, 2 ], 0, [ 0, 2 ], 0); + }); + + it('TINY-6559: Press Shift+Enter in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', false); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { shiftKey: true }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

'); + TinyAssertions.assertSelection(editor, [ 0, 1 ], 2, [ 0, 1 ], 2); + }); + + it('TINY-6559: Press Enter twice in blockquote', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', false); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

 

'); + TinyAssertions.assertSelection(editor, [ 0, 3 ], 0, [ 0, 3 ], 0); + }); + + it('TINY-6559: Press Enter twice in blockquote while between two lines', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', false); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 0 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

 

 

Line 2

'); + TinyAssertions.assertSelection(editor, [ 0, 2 ], 0, [ 0, 2 ], 0); + }); + + it('TINY-6559: Press Enter twice in a div', () => { + const editor = hook.editor(); + editor.options.set('end_container_on_empty_block', false); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

 

'); + TinyAssertions.assertSelection(editor, [ 0, 3 ], 0, [ 0, 3 ], 0); + }); + + it('TINY-6559: Press Enter twice in a section', () => { + const editor = hook.editor(); + editor.setContent('

Line 1

Line 2

'); + TinySelections.setCursor(editor, [ 0, 1 ], 1); + insertNewline(editor, { }); + insertNewline(editor, { }); + TinyAssertions.assertContent(editor, '

Line 1

Line 2

 

 

'); + TinyAssertions.assertSelection(editor, [ 0, 3 ], 0, [ 0, 3 ], 0); + }); + }); + }); });