diff --git a/packages/ckeditor5-special-characters/docs/_snippets/features/special-characters-new-category.js b/packages/ckeditor5-special-characters/docs/_snippets/features/special-characters-new-category.js index 51e007a5100..df73f900341 100644 --- a/packages/ckeditor5-special-characters/docs/_snippets/features/special-characters-new-category.js +++ b/packages/ckeditor5-special-characters/docs/_snippets/features/special-characters-new-category.js @@ -14,7 +14,7 @@ function SpecialCharactersEmoji( editor ) { { title: 'wind blowing face', character: '🌬️' }, { title: 'floppy disk', character: '💾' }, { title: 'heart', character: '❤️' } - ] ); + ], { label: 'Emoticons' } ); } ClassicEditor diff --git a/packages/ckeditor5-special-characters/docs/features/special-characters.md b/packages/ckeditor5-special-characters/docs/features/special-characters.md index de88cfa0fd6..e7f66250241 100644 --- a/packages/ckeditor5-special-characters/docs/features/special-characters.md +++ b/packages/ckeditor5-special-characters/docs/features/special-characters.md @@ -44,7 +44,7 @@ function SpecialCharactersEmoji( editor ) { { title: 'wind blowing face', character: '🌬️' }, { title: 'floppy disk', character: '💾' }, { title: 'heart', character: '❤️' } - ] ); + ], { label: 'Emoticons' } ); } ClassicEditor @@ -66,6 +66,10 @@ After adding the above plugin to the editor configuration, the new category will The title of a special character must be unique across the entire special characters set. + + The third argument of the {@link module:special-characters/specialcharacters~SpecialCharacters#addItems `SpecialCharacters#addItems()`} method is optional. You can use it to specify a label displayed as a category name. It is useful when your editor uses a language other than English. Check out the {@link features/ui-language UI language guide} to learn more. + + Below you can see a demo based on the example shown above. Use the special characters icon in the editor toolbar and then select "Emoji" in the select dropdown in order to insert an emoji into the WYSIWYG editor. {@snippet features/special-characters-new-category} diff --git a/packages/ckeditor5-special-characters/lang/contexts.json b/packages/ckeditor5-special-characters/lang/contexts.json index e5d9fe520b9..5ffd05aa777 100644 --- a/packages/ckeditor5-special-characters/lang/contexts.json +++ b/packages/ckeditor5-special-characters/lang/contexts.json @@ -1,5 +1,11 @@ { "Special characters": "Name of the special characters plugins, visible in a dropdown and as a button tooltip.", + "All": "A category name is displayed in a special characters dropdown representing all available symbols.", + "Arrows": "A category name is displayed in a special characters dropdown representing arrow symbols.", + "Currency": "A category name is displayed in a special characters dropdown representing currency symbols.", + "Latin": "A category name is displayed in a special characters dropdown representing latin symbols.", + "Mathematical": "A category name is displayed in a special characters dropdown representing mathematical symbols.", + "Text": "A category name is displayed in a special characters dropdown representing text symbols.", "leftwards simple arrow": "A label for the \"leftwards simple arrow\" symbol.", "rightwards simple arrow": "A label for the \"rightwards simple arrow\" symbol.", "upwards simple arrow": "A label for the \"upwards simple arrow\" symbol.", diff --git a/packages/ckeditor5-special-characters/src/specialcharacters.js b/packages/ckeditor5-special-characters/src/specialcharacters.js index 4c01810c2d9..61dae38ec65 100644 --- a/packages/ckeditor5-special-characters/src/specialcharacters.js +++ b/packages/ckeditor5-special-characters/src/specialcharacters.js @@ -49,6 +49,8 @@ export default class SpecialCharacters extends Plugin { constructor( editor ) { super( editor ); + const t = editor.t; + /** * Registered characters. A pair of a character name and its symbol. * @@ -58,12 +60,20 @@ export default class SpecialCharacters extends Plugin { this._characters = new Map(); /** - * Registered groups. Each group contains a collection with symbol names. + * Registered groups. Each group contains a displayed label and a collection with symbol names. * * @private - * @member {Map.>} #_groups + * @member {Map., label: String}>} #_groups */ this._groups = new Map(); + + /** + * A label describing the "All" special characters category. + * + * @private + * @member {String} #_allSpecialCharactersGroupLabel + */ + this._allSpecialCharactersGroupLabel = t( 'All' ); } /** @@ -126,8 +136,10 @@ export default class SpecialCharacters extends Plugin { * * @param {String} groupName * @param {Array.} items + * @param {Object} options + * @param {String} [options.label=groupName] */ - addItems( groupName, items ) { + addItems( groupName, items, options = { label: groupName } ) { if ( groupName === ALL_SPECIAL_CHARACTERS_GROUP ) { /** * The name "All" for a special category group cannot be used because it is a special category that displays all @@ -135,13 +147,13 @@ export default class SpecialCharacters extends Plugin { * * @error special-character-invalid-group-name */ - throw new CKEditorError( 'special-character-invalid-group-name', null, { ALL_SPECIAL_CHARACTERS_GROUP } ); + throw new CKEditorError( 'special-character-invalid-group-name', null ); } - const group = this._getGroup( groupName ); + const group = this._getGroup( groupName, options.label ); for ( const item of items ) { - group.add( item.title ); + group.items.add( item.title ); this._characters.set( item.title, item.character ); } } @@ -183,7 +195,11 @@ export default class SpecialCharacters extends Plugin { return new Set( this._characters.keys() ); } - return this._groups.get( groupName ); + const group = this._groups.get( groupName ); + + if ( group ) { + return group.items; + } } /** @@ -202,10 +218,14 @@ export default class SpecialCharacters extends Plugin { * * @private * @param {String} groupName The name of the group to create. + * @param {String} label The label describing the new group. */ - _getGroup( groupName ) { + _getGroup( groupName, label ) { if ( !this._groups.has( groupName ) ) { - this._groups.set( groupName, new Set() ); + this._groups.set( groupName, { + items: new Set(), + label + } ); } return this._groups.get( groupName ); @@ -240,10 +260,14 @@ export default class SpecialCharacters extends Plugin { * @returns {Object} Returns an object with `navigationView`, `gridView` and `infoView` properties, containing UI parts. */ _createDropdownPanelContent( locale, dropdownView ) { - const specialCharsGroups = Array.from( this.getGroups() ); + // The map contains a name of category (an identifier) and its label (a translational string). + const specialCharsGroups = new Map( [ + // Add a special group that shows all available special characters. + [ ALL_SPECIAL_CHARACTERS_GROUP, this._allSpecialCharactersGroupLabel ], - // Add a special group that shows all available special characters. - specialCharsGroups.unshift( ALL_SPECIAL_CHARACTERS_GROUP ); + ...Array.from( this.getGroups() ) + .map( name => ( [ name, this._groups.get( name ).label ] ) ) + ] ); const navigationView = new SpecialCharactersNavigationView( locale, specialCharsGroups ); const gridView = new CharacterGridView( locale ); diff --git a/packages/ckeditor5-special-characters/src/specialcharactersarrows.js b/packages/ckeditor5-special-characters/src/specialcharactersarrows.js index 00213a975c0..e44d5fb960d 100644 --- a/packages/ckeditor5-special-characters/src/specialcharactersarrows.js +++ b/packages/ckeditor5-special-characters/src/specialcharactersarrows.js @@ -59,6 +59,6 @@ export default class SpecialCharactersArrows extends Plugin { { title: t( 'on with exclamation mark with left right arrow above' ), character: '🔛' }, { title: t( 'soon with rightwards arrow above' ), character: '🔜' }, { title: t( 'top with upwards arrow above' ), character: '🔝' } - ] ); + ], { label: t( 'Arrows' ) } ); } } diff --git a/packages/ckeditor5-special-characters/src/specialcharacterscurrency.js b/packages/ckeditor5-special-characters/src/specialcharacterscurrency.js index e39ae65d0fe..a656efd27a5 100644 --- a/packages/ckeditor5-special-characters/src/specialcharacterscurrency.js +++ b/packages/ckeditor5-special-characters/src/specialcharacterscurrency.js @@ -73,6 +73,6 @@ export default class SpecialCharactersCurrency extends Plugin { { character: '₻', title: t( 'Nordic mark sign' ) }, { character: '₼', title: t( 'Manat sign' ) }, { character: '₽', title: t( 'Ruble sign' ) } - ] ); + ], { label: t( 'Currency' ) } ); } } diff --git a/packages/ckeditor5-special-characters/src/specialcharacterslatin.js b/packages/ckeditor5-special-characters/src/specialcharacterslatin.js index 331dba00d4d..9eaad1cd4fb 100644 --- a/packages/ckeditor5-special-characters/src/specialcharacterslatin.js +++ b/packages/ckeditor5-special-characters/src/specialcharacterslatin.js @@ -165,6 +165,6 @@ export default class SpecialCharactersLatin extends Plugin { { character: 'Ž', title: t( 'Latin capital letter z with caron' ) }, { character: 'ž', title: t( 'Latin small letter z with caron' ) }, { character: 'ſ', title: t( 'Latin small letter long s' ) } - ] ); + ], { label: t( 'Latin' ) } ); } } diff --git a/packages/ckeditor5-special-characters/src/specialcharactersmathematical.js b/packages/ckeditor5-special-characters/src/specialcharactersmathematical.js index 29054b5f0c8..9bcde2e09b5 100644 --- a/packages/ckeditor5-special-characters/src/specialcharactersmathematical.js +++ b/packages/ckeditor5-special-characters/src/specialcharactersmathematical.js @@ -81,6 +81,6 @@ export default class SpecialCharactersMathematical extends Plugin { { character: '¼', title: t( 'Vulgar fraction one quarter' ) }, { character: '½', title: t( 'Vulgar fraction one half' ) }, { character: '¾', title: t( 'Vulgar fraction three quarters' ) } - ] ); + ], { label: t( 'Mathematical' ) } ); } } diff --git a/packages/ckeditor5-special-characters/src/specialcharacterstext.js b/packages/ckeditor5-special-characters/src/specialcharacterstext.js index c11661c8c44..f821beab045 100644 --- a/packages/ckeditor5-special-characters/src/specialcharacterstext.js +++ b/packages/ckeditor5-special-characters/src/specialcharacterstext.js @@ -64,6 +64,6 @@ export default class SpecialCharactersText extends Plugin { { character: '§', title: t( 'Section sign' ) }, { character: '¶', title: t( 'Paragraph sign' ) }, { character: '⁋', title: t( 'Reversed paragraph sign' ) } - ] ); + ], { label: t( 'Text' ) } ); } } diff --git a/packages/ckeditor5-special-characters/src/ui/specialcharactersnavigationview.js b/packages/ckeditor5-special-characters/src/ui/specialcharactersnavigationview.js index 109a80c2bba..76b2c0115aa 100644 --- a/packages/ckeditor5-special-characters/src/ui/specialcharactersnavigationview.js +++ b/packages/ckeditor5-special-characters/src/ui/specialcharactersnavigationview.js @@ -22,7 +22,7 @@ export default class SpecialCharactersNavigationView extends FormHeaderView { * class. * * @param {module:utils/locale~Locale} locale The localization services instance. - * @param {Iterable.} groupNames The names of the character groups. + * @param {Map.} groupNames The names of the character groups and their displayed labels. */ constructor( locale, groupNames ) { super( locale ); @@ -70,7 +70,7 @@ export default class SpecialCharactersNavigationView extends FormHeaderView { * Returns a dropdown that allows selecting character groups. * * @private - * @param {Iterable.} groupNames The names of the character groups. + * @param {Map.} groupNames The names of the character groups and their displayed labels. * @returns {module:ui/dropdown/dropdownview~DropdownView} */ _createGroupDropdown( groupNames ) { @@ -79,9 +79,9 @@ export default class SpecialCharactersNavigationView extends FormHeaderView { const dropdown = createDropdown( locale ); const groupDefinitions = this._getCharacterGroupListItemDefinitions( dropdown, groupNames ); - dropdown.set( 'value', groupDefinitions.first.model.label ); + dropdown.set( 'value', groupDefinitions.first.model.name ); - dropdown.buttonView.bind( 'label' ).to( dropdown, 'value' ); + dropdown.buttonView.bind( 'label' ).to( dropdown, 'value', value => groupNames.get( value ) ); dropdown.buttonView.set( { isOn: false, @@ -91,7 +91,7 @@ export default class SpecialCharactersNavigationView extends FormHeaderView { } ); dropdown.on( 'execute', evt => { - dropdown.value = evt.source.label; + dropdown.value = evt.source.name; } ); dropdown.delegate( 'execute' ).to( this ); @@ -107,23 +107,24 @@ export default class SpecialCharactersNavigationView extends FormHeaderView { * * @private * @param {module:ui/dropdown/dropdownview~DropdownView} dropdown - * @param {Iterable.} groupNames The names of the character groups. + * @param {Map.} groupNames The names of the character groups and their displayed labels. * @returns {Iterable.} */ _getCharacterGroupListItemDefinitions( dropdown, groupNames ) { const groupDefs = new Collection(); - for ( const name of groupNames ) { + for ( const [ name, label ] of groupNames ) { const definition = { type: 'button', model: new Model( { - label: name, + name, + label, withText: true } ) }; definition.model.bind( 'isOn' ).to( dropdown, 'value', value => { - return value === definition.model.label; + return value === definition.model.name; } ); groupDefs.add( definition ); diff --git a/packages/ckeditor5-special-characters/tests/specialcharacters.js b/packages/ckeditor5-special-characters/tests/specialcharacters.js index 96dc62da2f7..f55f110217b 100644 --- a/packages/ckeditor5-special-characters/tests/specialcharacters.js +++ b/packages/ckeditor5-special-characters/tests/specialcharacters.js @@ -148,7 +148,7 @@ describe( 'SpecialCharacters', () => { const navigation = dropdown.panelView.children.first.navigationView; expect( grid.tiles.get( 0 ).label ).to.equal( '<' ); - navigation.groupDropdownView.fire( new EventInfo( { label: 'Arrows' }, 'execute' ) ); + navigation.groupDropdownView.fire( new EventInfo( { name: 'Arrows' }, 'execute' ) ); expect( grid.tiles.get( 0 ).label ).to.equal( '←' ); } ); @@ -244,6 +244,23 @@ describe( 'SpecialCharacters', () => { expect( plugin._characters.size ).to.equal( startingCharacterSize + 2 ); } ); + it( 'allows defining a displayed label different from a category name', () => { + plugin.addItems( 'Symbols', [ + { title: 'arrow left', character: '←' }, + { title: 'arrow right', character: '→' } + ], { label: 'Custom arrows plugin' } ); + + expect( plugin._groups.has( 'Symbols' ) ).to.equal( true ); + + const arrowGroup = plugin._groups.get( 'Symbols' ); + + expect( arrowGroup ).to.have.property( 'label', 'Custom arrows plugin' ); + expect( arrowGroup ).to.have.property( 'items' ); + expect( arrowGroup.items.size ).to.equal( 2 ); + expect( arrowGroup.items.has( 'arrow left' ) ).to.equal( true ); + expect( arrowGroup.items.has( 'arrow right' ) ).to.equal( true ); + } ); + it( 'does not accept "All" as a group name', () => { expectToThrowCKEditorError( () => { plugin.addItems( 'All', [] ); diff --git a/packages/ckeditor5-special-characters/tests/specialcharactersarrows.js b/packages/ckeditor5-special-characters/tests/specialcharactersarrows.js index 02bd00e3ff5..519be03933e 100644 --- a/packages/ckeditor5-special-characters/tests/specialcharactersarrows.js +++ b/packages/ckeditor5-special-characters/tests/specialcharactersarrows.js @@ -46,10 +46,16 @@ describe( 'SpecialCharactersArrows', () => { expect( addItemsFirstCallArgs[ 0 ] ).to.equal( 'Arrows' ); } ); + it( 'defines a label displayed in the toolbar', () => { + expect( addItemsFirstCallArgs[ 2 ] ).to.deep.equal( { + label: 'Arrows' + } ); + } ); + it( 'adds proper characters', () => { expect( addItemsFirstCallArgs[ 1 ] ).to.deep.include( { - title: 'rightwards double arrow', - character: '⇒' + title: 'leftwards arrow to bar', + character: '⇤' } ); expect( addItemsFirstCallArgs[ 1 ] ).to.deep.include( { diff --git a/packages/ckeditor5-special-characters/tests/specialcharacterscurrency.js b/packages/ckeditor5-special-characters/tests/specialcharacterscurrency.js index 9d92348b5b4..3ccf69fd9e3 100644 --- a/packages/ckeditor5-special-characters/tests/specialcharacterscurrency.js +++ b/packages/ckeditor5-special-characters/tests/specialcharacterscurrency.js @@ -46,6 +46,12 @@ describe( 'SpecialCharactersCurrency', () => { expect( addItemsFirstCallArgs[ 0 ] ).to.equal( 'Currency' ); } ); + it( 'defines a label displayed in the toolbar', () => { + expect( addItemsFirstCallArgs[ 2 ] ).to.deep.equal( { + label: 'Currency' + } ); + } ); + it( 'adds proper characters', () => { expect( addItemsFirstCallArgs[ 1 ] ).to.deep.include( { character: '¢', diff --git a/packages/ckeditor5-special-characters/tests/specialcharacterslatin.js b/packages/ckeditor5-special-characters/tests/specialcharacterslatin.js index 6d085166d68..df375961040 100644 --- a/packages/ckeditor5-special-characters/tests/specialcharacterslatin.js +++ b/packages/ckeditor5-special-characters/tests/specialcharacterslatin.js @@ -46,6 +46,12 @@ describe( 'SpecialCharactersLatin', () => { expect( addItemsFirstCallArgs[ 0 ] ).to.equal( 'Latin' ); } ); + it( 'defines a label displayed in the toolbar', () => { + expect( addItemsFirstCallArgs[ 2 ] ).to.deep.equal( { + label: 'Latin' + } ); + } ); + it( 'adds proper characters', () => { expect( addItemsFirstCallArgs[ 1 ] ).to.deep.include( { character: 'Ō', diff --git a/packages/ckeditor5-special-characters/tests/specialcharactersmathematical.js b/packages/ckeditor5-special-characters/tests/specialcharactersmathematical.js new file mode 100644 index 00000000000..dfc0a05e6a3 --- /dev/null +++ b/packages/ckeditor5-special-characters/tests/specialcharactersmathematical.js @@ -0,0 +1,66 @@ +/** + * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +/* globals document */ + +import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; +import SpecialCharacters from '../src/specialcharacters'; +import SpecialCharactersMathematical from '../src/specialcharactersmathematical'; +import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; + +describe( 'SpecialCharactersMathematical', () => { + testUtils.createSinonSandbox(); + + let editor, editorElement, addItemsSpy, addItemsFirstCallArgs; + + beforeEach( () => { + editorElement = document.createElement( 'div' ); + + addItemsSpy = sinon.spy( SpecialCharacters.prototype, 'addItems' ); + + document.body.appendChild( editorElement ); + return ClassicTestEditor + .create( editorElement, { + plugins: [ SpecialCharacters, SpecialCharactersMathematical ] + } ) + .then( newEditor => { + editor = newEditor; + addItemsFirstCallArgs = addItemsSpy.args[ 0 ]; + } ); + } ); + + afterEach( () => { + addItemsSpy.restore(); + + editorElement.remove(); + return editor.destroy(); + } ); + + it( 'adds new items', () => { + expect( addItemsSpy.callCount ).to.equal( 1 ); + } ); + + it( 'properly names the category', () => { + expect( addItemsFirstCallArgs[ 0 ] ).to.equal( 'Mathematical' ); + } ); + + it( 'defines a label displayed in the toolbar', () => { + expect( addItemsFirstCallArgs[ 2 ] ).to.deep.equal( { + label: 'Mathematical' + } ); + } ); + + it( 'adds proper characters', () => { + expect( addItemsFirstCallArgs[ 1 ] ).to.deep.include( { + title: 'Less-than sign', + character: '<' + } ); + + expect( addItemsFirstCallArgs[ 1 ] ).to.deep.include( { + title: 'Greater-than sign', + character: '>' + } ); + } ); +} ); diff --git a/packages/ckeditor5-special-characters/tests/specialcharacterstext.js b/packages/ckeditor5-special-characters/tests/specialcharacterstext.js index 3ae34f64686..f3b879d3c9f 100644 --- a/packages/ckeditor5-special-characters/tests/specialcharacterstext.js +++ b/packages/ckeditor5-special-characters/tests/specialcharacterstext.js @@ -46,6 +46,12 @@ describe( 'SpecialCharactersText', () => { expect( addItemsFirstCallArgs[ 0 ] ).to.equal( 'Text' ); } ); + it( 'defines a label displayed in the toolbar', () => { + expect( addItemsFirstCallArgs[ 2 ] ).to.deep.equal( { + label: 'Text' + } ); + } ); + it( 'adds proper characters', () => { expect( addItemsFirstCallArgs[ 1 ] ).to.deep.include( { character: '…', diff --git a/packages/ckeditor5-special-characters/tests/ui/specialcharactersnavigationview.js b/packages/ckeditor5-special-characters/tests/ui/specialcharactersnavigationview.js index ddc68f3d7ae..37fab6d6e6b 100644 --- a/packages/ckeditor5-special-characters/tests/ui/specialcharactersnavigationview.js +++ b/packages/ckeditor5-special-characters/tests/ui/specialcharactersnavigationview.js @@ -19,7 +19,10 @@ describe( 'SpecialCharactersNavigationView', () => { t: val => val }; - view = new SpecialCharactersNavigationView( locale, [ 'groupA', 'groupB' ] ); + view = new SpecialCharactersNavigationView( locale, new Map( [ + [ 'groupA', 'labelA' ], + [ 'groupB', 'labelB' ] + ] ) ); view.render(); } ); @@ -87,7 +90,10 @@ describe( 'SpecialCharactersNavigationView', () => { t: val => val }; - view = new SpecialCharactersNavigationView( locale, [ 'groupA', 'groupB' ] ); + view = new SpecialCharactersNavigationView( locale, new Map( [ + [ 'groupA', 'labelA' ], + [ 'groupB', 'labelB' ] + ] ) ); view.render(); expect( view.groupDropdownView.panelPosition ).to.equal( 'se' ); @@ -105,11 +111,11 @@ describe( 'SpecialCharactersNavigationView', () => { } ); describe( 'buttonView', () => { - it( 'binds #label to #value', () => { - expect( groupDropdownView.buttonView.label ).to.equal( 'groupA' ); + it( 'binds #label to translation #value', () => { + expect( groupDropdownView.buttonView.label ).to.equal( 'labelA' ); groupDropdownView.listView.items.last.children.first.fire( 'execute' ); - expect( groupDropdownView.buttonView.label ).to.equal( 'groupB' ); + expect( groupDropdownView.buttonView.label ).to.equal( 'labelB' ); } ); it( 'should be configured by the #groupDropdownView', () => { @@ -129,13 +135,13 @@ describe( 'SpecialCharactersNavigationView', () => { it( 'have basic properties', () => { expect( groupDropdownView.listView.items .map( item => { - const { label, withText } = item.children.first; + const { name, label, withText } = item.children.first; - return { label, withText }; + return { name, label, withText }; } ) ) .to.deep.equal( [ - { label: 'groupA', withText: true }, - { label: 'groupB', withText: true } + { name: 'groupA', label: 'labelA', withText: true }, + { name: 'groupB', label: 'labelB', withText: true } ] ); } ); diff --git a/packages/ckeditor5-special-characters/tests/ui/specialcharactersview.js b/packages/ckeditor5-special-characters/tests/ui/specialcharactersview.js index f2d36dc5805..0a4af8becf5 100644 --- a/packages/ckeditor5-special-characters/tests/ui/specialcharactersview.js +++ b/packages/ckeditor5-special-characters/tests/ui/specialcharactersview.js @@ -19,7 +19,9 @@ describe( 'SpecialCahractersView', () => { t: val => val }; - navigationView = new SpecialCharactersNavigationView( locale, [ 'groupA' ] ); + navigationView = new SpecialCharactersNavigationView( locale, new Map( [ + [ 'groupA', 'labelA' ] + ] ) ); gridView = new CharacterGridView( locale ); infoView = new CharacterInfoView( locale ); view = new SpecialCahractersView( locale, navigationView, gridView, infoView );