• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ckeditor / ckeditor5 / 25927

pending completion
25927

Pull #14763

CKEditor5 code coverage

web-flow
Merge pull request #14753 from ckeditor/ck/14743-enablePlaceholder-API-should-remain-backward-compatible-for-some-time

Fix (engine): Made the `enablePlaceholder()` API to remain backward compatible for the deprecation period. It will be removed in the future. Closes #14743.
Pull Request #14763: Support for image height attribute

12436 of 12436 branches covered (100.0%)

Branch coverage included in aggregate %.

147 of 147 new or added lines in 10 files covered. (100.0%)

32669 of 32669 relevant lines covered (100.0%)

12002.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/packages/ckeditor5-alignment/src/alignmentui.ts
1
/**
2
 * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
3
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
4
 */
5

6
/**
7
 * @module alignment/alignmentui
8
 */
9

10
import { Plugin } from 'ckeditor5/src/core.js';
11
import {
12
        type Button,
13
        ButtonView,
14
        createDropdown,
15
        addToolbarToDropdown,
16
        MenuBarMenuListItemView,
17
        MenuBarMenuListItemButtonView,
1✔
18
        MenuBarMenuView,
19
        MenuBarMenuListView
20
} from 'ckeditor5/src/ui.js';
21
import { IconAlignCenter, IconAlignJustify, IconAlignLeft, IconAlignRight } from 'ckeditor5/src/icons.js';
22
import type { Locale } from 'ckeditor5/src/utils.js';
23

24
import { isSupported, normalizeAlignmentOptions } from './utils.js';
25
import type { AlignmentFormat, SupportedOption } from './alignmentconfig.js';
26
import type AlignmentCommand from './alignmentcommand.js';
27

28
const iconsMap = /* #__PURE__ */ ( () => new Map( [
29
        [ 'left', IconAlignLeft ],
30
        [ 'right', IconAlignRight ],
31
        [ 'center', IconAlignCenter ],
32
        [ 'justify', IconAlignJustify ]
33
] ) )();
34

35
/**
36
 * The default alignment UI plugin.
37
 *
38
 * It introduces the `'alignment:left'`, `'alignment:right'`, `'alignment:center'` and `'alignment:justify'` buttons
39
 * and the `'alignment'` dropdown.
40
 */
41
export default class AlignmentUI extends Plugin {
42
        /**
43
         * Returns the localized option titles provided by the plugin.
44
         *
45
         * The following localized titles corresponding with
47✔
46
         * {@link module:alignment/alignmentconfig~AlignmentConfig#options} are available:
47
         *
47✔
48
         * * `'left'`,
49
         * * `'right'`,
50
         * * `'center'`,
51
         * * `'justify'`.
52
         *
53
         * @readonly
54
         */
55
        public get localizedOptionTitles(): Record<SupportedOption, string> {
56
                const t = this.editor.t;
57

58
                return {
59
                        'left': t( 'Align left' ),
160✔
60
                        'right': t( 'Align right' ),
61
                        'center': t( 'Align center' ),
62
                        'justify': t( 'Justify' )
63
                };
64
        }
65

66
        /**
40✔
67
         * @inheritDoc
40✔
68
         */
40✔
69
        public static get pluginName() {
40✔
70
                return 'AlignmentUI' as const;
71
        }
40✔
72

150✔
73
        /**
74
         * @inheritDoc
150✔
75
         */
76
        public static override get isOfficialPlugin(): true {
40✔
77
                return true;
17✔
78
        }
79

80
        /**
17✔
81
         * @inheritDoc
82
         */
26✔
83
        public init(): void {
84
                const editor = this.editor;
85
                const options = normalizeAlignmentOptions( editor.config.get( 'alignment.options' )! );
86

87
                options
88
                        .map( option => option.name )
89
                        .filter( isSupported )
90
                        .forEach( option => this._addButton( option ) );
91

17✔
92
                this._addToolbarDropdown( options );
93
                this._addMenuBarMenu( options );
94
        }
95

96
        /**
17✔
97
         * Helper method for initializing the button and linking it with an appropriate command.
98
         *
99
         * @param option The name of the alignment option for which the button is added.
100
         */
101
        private _addButton( option: SupportedOption ): void {
102
                const editor = this.editor;
103

17✔
104
                editor.ui.componentFactory.add( `alignment:${ option }`, locale => this._createButton( locale, option ) );
17✔
105
        }
106

107
        /**
21✔
108
         * Helper method for creating the button view element.
109
         *
110
         * @param locale Editor locale.
17✔
111
         * @param option The name of the alignment option for which the button is added.
112
         * @param buttonAttrs Optional parameters passed to button view instance.
113
         */
114
        private _createButton(
17✔
115
                locale: Locale,
1✔
116
                option: SupportedOption,
117
                buttonAttrs: Partial<Button> = {}
118
        ): ButtonView {
17✔
119
                const editor = this.editor;
120
                const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
121
                const buttonView = new ButtonView( locale );
122

123
                buttonView.set( {
124
                        label: this.localizedOptionTitles[ option ],
125
                        icon: iconsMap.get( option ),
126
                        tooltip: true,
127
                        isToggleable: true,
128
                        ...buttonAttrs
150✔
129
                } );
130

150✔
131
                // Bind button model to command.
46✔
132
                buttonView.bind( 'isEnabled' ).to( command );
46✔
133
                buttonView.bind( 'isOn' ).to( command, 'value', value => value === option );
134

46✔
135
                // Execute command.
136
                this.listenTo( buttonView, 'execute', () => {
137
                        editor.execute( 'alignment', { value: option } );
138
                        editor.editing.view.focus();
139
                } );
140

141
                return buttonView;
142
        }
46✔
143

65✔
144
        /**
145
         * Helper method for initializing the toolnar dropdown and linking it with an appropriate command.
146
         *
46✔
147
         * @param options The name of the alignment option for which the button is added.
5✔
148
         */
5✔
149
        private _addToolbarDropdown( options: Array<AlignmentFormat> ): void {
150
                const editor = this.editor;
151
                const factory = editor.ui.componentFactory;
46✔
152

153
                factory.add( 'alignment', locale => {
154
                        const dropdownView = createDropdown( locale );
155
                        const tooltipPosition = locale.uiLanguageDirection === 'rtl' ? 'w' : 'e';
156
                        const t = locale.t;
157

158
                        // Add existing alignment buttons to dropdown's toolbar.
159
                        addToolbarToDropdown(
160
                                dropdownView,
161
                                () => options.map( option => this._createButton( locale, option.name, { tooltipPosition } ) ) as Array<ButtonView>,
162
                                {
163
                                        enableActiveItemFocusOnDropdownOpen: true,
164
                                        isVertical: true,
165
                                        ariaLabel: t( 'Text alignment toolbar' )
166
                                }
167
                        );
168

169
                        // Configure dropdown properties an behavior.
170
                        dropdownView.buttonView.set( {
171
                                label: t( 'Text alignment' ),
172
                                tooltip: true
173
                        } );
174

175
                        dropdownView.extendTemplate( {
176
                                attributes: {
177
                                        class: 'ck-alignment-dropdown'
178
                                }
179
                        } );
180

181
                        // The default icon depends on the direction of the content.
182
                        const defaultIcon = locale.contentLanguageDirection === 'rtl' ? iconsMap.get( 'right' ) : iconsMap.get( 'left' );
183
                        const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
184

185
                        // Change icon to reflect current selection's alignment.
186
                        dropdownView.buttonView.bind( 'icon' ).to( command, 'value', value => iconsMap.get( value ) || defaultIcon );
187

188
                        // Enable button if any of the buttons is enabled.
189
                        dropdownView.bind( 'isEnabled' ).to( command, 'isEnabled' );
190

191
                        // Focus the editable after executing the command.
192
                        // Overrides a default behaviour where the focus is moved to the dropdown button (#12125).
193
                        this.listenTo( dropdownView, 'execute', () => {
194
                                editor.editing.view.focus();
195
                        } );
196

197
                        return dropdownView;
198
                } );
199
        }
200

201
        /**
202
         * Creates a menu for all alignment options to use either in menu bar.
203
         *
204
         * @param options Normalized alignment options from config.
205
         */
206
        private _addMenuBarMenu( options: Array<AlignmentFormat> ): void {
207
                const editor = this.editor;
208

209
                editor.ui.componentFactory.add( 'menuBar:alignment', locale => {
210
                        const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
211
                        const t = locale.t;
212
                        const menuView = new MenuBarMenuView( locale );
213
                        const listView = new MenuBarMenuListView( locale );
214

215
                        menuView.bind( 'isEnabled' ).to( command );
216

217
                        listView.set( {
218
                                ariaLabel: t( 'Text alignment' ),
219
                                role: 'menu'
220
                        } );
221

222
                        menuView.buttonView.set( {
223
                                label: t( 'Text alignment' )
224
                        } );
225

226
                        for ( const option of options ) {
227
                                const listItemView = new MenuBarMenuListItemView( locale, menuView );
228
                                const buttonView = new MenuBarMenuListItemButtonView( locale );
229

230
                                buttonView.delegate( 'execute' ).to( menuView );
231
                                buttonView.set( {
232
                                        label: this.localizedOptionTitles[ option.name ],
233
                                        icon: iconsMap.get( option.name ),
234
                                        role: 'menuitemcheckbox',
235
                                        isToggleable: true
236
                                } );
237

238
                                buttonView.on( 'execute', () => {
239
                                        editor.execute( 'alignment', { value: option.name } );
240

241
                                        editor.editing.view.focus();
242
                                } );
243

244
                                buttonView.bind( 'isOn' ).to( command, 'value', value => value === option.name );
245
                                buttonView.bind( 'isEnabled' ).to( command, 'isEnabled' );
246

247
                                listItemView.children.add( buttonView );
248
                                listView.items.add( listItemView );
249
                        }
250

251
                        menuView.panelView.children.add( listView );
252

253
                        return menuView;
254
                } );
255
        }
256
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc