diff --git a/src/heading.js b/src/heading.js index b7b9e42..0712c09 100644 --- a/src/heading.js +++ b/src/heading.js @@ -41,7 +41,7 @@ export default class Heading extends Plugin { for ( let option of options ) { // Add the option to the collection. dropdownItems.add( new Model( { - modelElement: option.modelElement, + commandName: option.modelElement, label: option.title } ) ); @@ -82,8 +82,8 @@ export default class Heading extends Plugin { const dropdown = createListDropdown( dropdownModel, locale ); // Execute command when an item from the dropdown is selected. - this.listenTo( dropdown, 'execute', ( { source: { modelElement } } ) => { - editor.execute( modelElement ); + this.listenTo( dropdown, 'execute', ( evt ) => { + editor.execute( evt.source.commandName ); editor.editing.view.focus(); } ); @@ -113,13 +113,7 @@ export default class Heading extends Plugin { }; return editor.config.get( 'heading.options' ).map( option => { - let title; - - if ( option.modelElement == 'paragraph' ) { - title = localizedTitles.Paragraph; - } else { - title = localizedTitles[ option.title ]; - } + const title = localizedTitles[ option.title ]; if ( title && title != option.title ) { // Clone the option to avoid altering the original `config.heading.options`. diff --git a/src/headingcommand.js b/src/headingcommand.js index 196df5d..846a01c 100644 --- a/src/headingcommand.js +++ b/src/headingcommand.js @@ -77,9 +77,6 @@ export default class HeadingCommand extends Command { _doExecute( options = {} ) { const editor = this.editor; const document = editor.document; - const selection = document.selection; - const ranges = [ ...selection.getRanges() ]; - const isSelectionBackward = selection.isBackward; // If current option is same as new option - toggle already applied option back to default one. const shouldRemove = this.value; @@ -87,28 +84,24 @@ export default class HeadingCommand extends Command { document.enqueueChanges( () => { const batch = options.batch || document.batch(); - for ( let element of document.selection.getSelectedBlocks() ) { + for ( let block of document.selection.getSelectedBlocks() ) { // When removing applied option. if ( shouldRemove ) { - if ( element.name === this.modelElement ) { - // Apply paragraph to the selection withing that particular element only instead + if ( block.is( this.modelElement ) ) { + // Apply paragraph to the selection withing that particular block only instead // of working on the entire document selection. const selection = new Selection(); - selection.addRange( Range.createIn( element ) ); + selection.addRange( Range.createIn( block ) ); // Share the batch with the paragraph command. editor.execute( 'paragraph', { selection, batch } ); } } // When applying new option. - else { - batch.rename( element, this.modelElement ); + else if ( !block.is( this.modelElement ) ) { + batch.rename( block, this.modelElement ); } } - - // If range's selection start/end is placed directly in renamed block - we need to restore it's position - // after renaming, because renaming puts new element there. - selection.setRanges( ranges, isSelectionBackward ); } ); } @@ -121,7 +114,7 @@ export default class HeadingCommand extends Command { const block = this.editor.document.selection.getSelectedBlocks().next().value; if ( block ) { - this.value = this.modelElement == block.name; + this.value = block.is( this.modelElement ); } } } diff --git a/src/headingengine.js b/src/headingengine.js index 708e4fa..e7dcd7b 100644 --- a/src/headingengine.js +++ b/src/headingengine.js @@ -32,7 +32,7 @@ export default class HeadingEngine extends Plugin { // more properties (https://github.com/ckeditor/ckeditor5/issues/403). editor.config.define( 'heading', { options: [ - { modelElement: 'paragraph' }, + { modelElement: 'paragraph', title: 'Paragraph' }, { modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' }, { modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2' }, { modelElement: 'heading3', viewElement: 'h4', title: 'Heading 3' } @@ -55,7 +55,6 @@ export default class HeadingEngine extends Plugin { const data = editor.data; const editing = editor.editing; const options = editor.config.get( 'heading.options' ); - let command; for ( let option of options ) { // Skip paragraph - it is defined in required Paragraph feature. @@ -74,8 +73,7 @@ export default class HeadingEngine extends Plugin { .toElement( option.modelElement ); // Register the heading command for this option. - command = new HeadingCommand( editor, option ); - editor.commands.set( command.modelElement, command ); + editor.commands.set( option.modelElement, new HeadingCommand( editor, option ) ); } } } @@ -94,9 +92,9 @@ export default class HeadingEngine extends Plugin { this.listenTo( enterCommand, 'afterExecute', ( evt, data ) => { const positionParent = editor.document.selection.getFirstPosition().parent; const batch = data.batch; - const isHeading = options.some( option => option.modelElement == positionParent.name ); + const isHeading = options.some( option => positionParent.is( option.modelElement ) ); - if ( isHeading && positionParent.name != defaultModelElement && positionParent.childCount === 0 ) { + if ( isHeading && !positionParent.is( defaultModelElement ) && positionParent.childCount === 0 ) { batch.rename( positionParent, defaultModelElement ); } } ); diff --git a/tests/heading.js b/tests/heading.js index 802ff73..c0ae1c8 100644 --- a/tests/heading.js +++ b/tests/heading.js @@ -63,7 +63,7 @@ describe( 'Heading', () => { const executeSpy = testUtils.sinon.spy( editor, 'execute' ); const dropdown = editor.ui.componentFactory.create( 'headings' ); - dropdown.modelElement = 'paragraph'; + dropdown.commandName = 'paragraph'; dropdown.fire( 'execute' ); sinon.assert.calledOnce( executeSpy ); @@ -74,7 +74,7 @@ describe( 'Heading', () => { const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' ); const dropdown = editor.ui.componentFactory.create( 'headings' ); - dropdown.modelElement = 'paragraph'; + dropdown.commandName = 'paragraph'; dropdown.fire( 'execute' ); sinon.assert.calledOnce( focusSpy ); @@ -118,35 +118,18 @@ describe( 'Heading', () => { let commands; beforeEach( () => { - const editorElement = document.createElement( 'div' ); - - return ClassicTestEditor.create( editorElement, { - plugins: [ Heading ], - toolbar: [ 'heading' ], - lang: 'pl', - heading: { - options: [ - { modelElement: 'paragraph', viewElement: 'p', title: 'Paragraph' }, - { modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' }, - { modelElement: 'heading2', viewElement: 'h3', title: 'Not automatically localized' } - ] - } - } ) - .then( newEditor => { - editor = newEditor; - dropdown = editor.ui.componentFactory.create( 'headings' ); - commands = {}; - editor.config.get( 'heading.options' ).forEach( ( { modelElement } ) => { - commands[ modelElement ] = editor.commands.get( modelElement ); - } ); - } ); + return localizedEditor( [ + { modelElement: 'paragraph', title: 'Paragraph' }, + { modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' }, + { modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2' } + ] ); } ); it( 'does not alter the original config', () => { expect( editor.config.get( 'heading.options' ) ).to.deep.equal( [ - { modelElement: 'paragraph', viewElement: 'p', title: 'Paragraph' }, + { modelElement: 'paragraph', title: 'Paragraph' }, { modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' }, - { modelElement: 'heading2', viewElement: 'h3', title: 'Not automatically localized' } + { modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2' } ] ); } ); @@ -164,9 +147,56 @@ describe( 'Heading', () => { expect( listView.items.map( item => item.label ) ).to.deep.equal( [ 'Akapit', 'Nagłówek 1', - 'Not automatically localized' + 'Nagłówek 2' ] ); } ); + + it( 'allows custom titles', () => { + return localizedEditor( [ + { modelElement: 'paragraph', title: 'Custom paragraph title' }, + { modelElement: 'heading1', title: 'Custom heading1 title' } + ] ).then( () => { + const listView = dropdown.listView; + + expect( listView.items.map( item => item.label ) ).to.deep.equal( [ + 'Custom paragraph title', + 'Custom heading1 title', + ] ); + } ); + } ); + + it( 'translates default using the the locale', () => { + return localizedEditor( [ + { modelElement: 'paragraph', title: 'Paragraph' } + ] ).then( () => { + const listView = dropdown.listView; + + expect( listView.items.map( item => item.label ) ).to.deep.equal( [ + 'Akapit' + ] ); + } ); + } ); + + function localizedEditor( options ) { + const editorElement = document.createElement( 'div' ); + + return ClassicTestEditor.create( editorElement, { + plugins: [ Heading ], + toolbar: [ 'heading' ], + lang: 'pl', + heading: { + options: options + } + } ) + .then( newEditor => { + editor = newEditor; + dropdown = editor.ui.componentFactory.create( 'headings' ); + commands = {}; + editor.config.get( 'heading.options' ).forEach( ( { modelElement } ) => { + commands[ modelElement ] = editor.commands.get( modelElement ); + } ); + } ); + } } ); } ); } ); diff --git a/tests/headingcommand.js b/tests/headingcommand.js index aea5f17..26dcdb0 100644 --- a/tests/headingcommand.js +++ b/tests/headingcommand.js @@ -158,7 +158,7 @@ describe( 'HeadingCommand', () => { } it( 'converts all elements where selection is applied', () => { - setData( document, 'foo[bar]baz' ); + setData( document, 'foo[bar]baz' ); commands.heading3._doExecute(); expect( getData( document ) ).to.equal( diff --git a/tests/headingengine.js b/tests/headingengine.js index a307e37..fc4647e 100644 --- a/tests/headingengine.js +++ b/tests/headingengine.js @@ -9,16 +9,8 @@ import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import ParagraphCommand from '@ckeditor/ckeditor5-paragraph/src/paragraphcommand'; import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; import Enter from '@ckeditor/ckeditor5-enter/src/enter'; -import { add } from '@ckeditor/ckeditor5-utils/src/translation-service'; import { getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; -add( 'pl', { - 'Paragraph': 'Akapit', - 'Heading 1': 'Nagłówek 1', - 'Heading 2': 'Nagłówek 2', - 'Heading 3': 'Nagłówek 3', -} ); - describe( 'HeadingEngine', () => { let editor, document; @@ -113,7 +105,7 @@ describe( 'HeadingEngine', () => { describe( 'default value', () => { it( 'should be set', () => { expect( editor.config.get( 'heading.options' ) ).to.deep.equal( [ - { modelElement: 'paragraph' }, + { modelElement: 'paragraph', title: 'Paragraph' }, { modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' }, { modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2' }, { modelElement: 'heading3', viewElement: 'h4', title: 'Heading 3' } @@ -123,7 +115,7 @@ describe( 'HeadingEngine', () => { it( 'should customize options', () => { const options = [ - { modelElement: 'paragraph', viewElement: 'p', title: 'Paragraph' }, + { modelElement: 'paragraph', title: 'Paragraph' }, { modelElement: 'h4', viewElement: 'h4', title: 'H4' } ];