From 627f842b7997fde21973afa5b196293b685c9b90 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20Remiszewski?=
<118178939+mremiszewski@users.noreply.github.com>
Date: Tue, 7 May 2024 18:48:15 +0200
Subject: [PATCH] Add menu bar integration to multi root editor. (#16280)
Feature (editor-multi-root): Added menu bar support for multi-root editor.
---
.../tests/_utils/classictesteditor.js | 2 +-
.../src/classiceditorui.ts | 60 ++-----
.../tests/classiceditorui.js | 113 -------------
.../src/decouplededitorui.ts | 36 +---
.../tests/decouplededitorui.js | 113 -------------
.../src/multirooteditorui.ts | 2 +
.../src/multirooteditoruiview.ts | 20 ++-
.../tests/manual/multirooteditor.html | 12 +-
.../tests/manual/multirooteditor.js | 1 +
.../tests/multirooteditoruiview.js | 40 ++++-
packages/ckeditor5-ui/src/index.ts | 2 +-
packages/ckeditor5-ui/src/menubar/utils.ts | 40 ++++-
.../ckeditor5-ui/tests/editorui/editorui.js | 4 +-
packages/ckeditor5-ui/tests/menubar/utils.js | 154 +++++++++++++++++-
14 files changed, 275 insertions(+), 324 deletions(-)
diff --git a/packages/ckeditor5-core/tests/_utils/classictesteditor.js b/packages/ckeditor5-core/tests/_utils/classictesteditor.js
index 2adbdc67eb4..42f8b7bcf85 100644
--- a/packages/ckeditor5-core/tests/_utils/classictesteditor.js
+++ b/packages/ckeditor5-core/tests/_utils/classictesteditor.js
@@ -105,7 +105,7 @@ export default class ClassicTestEditor extends ElementApiMixin( Editor ) {
* @memberOf tests.core._utils
* @extends core.editor.EditorUI
*/
-class ClassicTestEditorUI extends EditorUI {
+export class ClassicTestEditorUI extends EditorUI {
/**
* @inheritDoc
*/
diff --git a/packages/ckeditor5-editor-classic/src/classiceditorui.ts b/packages/ckeditor5-editor-classic/src/classiceditorui.ts
index bbc000dda16..f243b44c3ee 100644
--- a/packages/ckeditor5-editor-classic/src/classiceditorui.ts
+++ b/packages/ckeditor5-editor-classic/src/classiceditorui.ts
@@ -10,12 +10,14 @@
import type { Editor, ElementApi } from 'ckeditor5/src/core.js';
import {
EditorUI,
- normalizeToolbarConfig,
- normalizeMenuBarConfig,
DialogView,
+ normalizeToolbarConfig,
+ _initMenuBar,
type DialogViewMoveToEvent,
type Dialog,
- type EditorUIReadyEvent
+ type EditorUIReadyEvent,
+ type EditorUIView,
+ type MenuBarView
} from 'ckeditor5/src/ui.js';
import {
enablePlaceholder,
@@ -38,11 +40,6 @@ export default class ClassicEditorUI extends EditorUI {
*/
private readonly _toolbarConfig: ReturnType;
- /**
- * A normalized `config.menuBar` object.
- */
- private readonly _menuBarConfig: ReturnType;
-
/**
* The element replacer instance used to hide the editor's source element.
*/
@@ -60,9 +57,6 @@ export default class ClassicEditorUI extends EditorUI {
this.view = view;
this._toolbarConfig = normalizeToolbarConfig( editor.config.get( 'toolbar' ) );
- // We use config.define in ClassicEditor, there will always be some configuration.
- this._menuBarConfig = normalizeMenuBarConfig( editor.config.get( 'menuBar' ) || {} );
-
this._elementReplacer = new ElementReplacer();
this.listenTo(
@@ -124,7 +118,11 @@ export default class ClassicEditorUI extends EditorUI {
this._initPlaceholder();
this._initToolbar();
- this._initMenuBar();
+
+ if ( view.menuBarView ) {
+ _initMenuBar( editor, view.menuBarView );
+ }
+
this._initDialogPluginIntegration();
this.fire( 'ready' );
}
@@ -160,21 +158,6 @@ export default class ClassicEditorUI extends EditorUI {
this.addToolbar( view.toolbar );
}
- /**
- * Initializes the editor menu bar.
- */
- private _initMenuBar(): void {
- const view = this.view;
-
- if ( !view.menuBarView ) {
- return;
- }
-
- this._setupMenuBarBehaviors( view.menuBarView.element! );
-
- view.menuBarView.fillFromConfig( this._menuBarConfig, this.componentFactory );
- }
-
/**
* Enable the placeholder text on the editing root.
*/
@@ -275,28 +258,5 @@ export default class ClassicEditorUI extends EditorUI {
}, { priority: 'high' } );
}, { priority: 'low' } );
}
-
- /**
- * Handles focus and keystrokes for menu bar element.
- */
- private _setupMenuBarBehaviors( menuBarViewElement: HTMLElement ) {
- const editor = this.editor;
- this.focusTracker.add( menuBarViewElement );
- editor.keystrokes.listenTo( menuBarViewElement );
-
- editor.keystrokes.set( 'Esc', ( data, cancel ) => {
- if ( menuBarViewElement.contains( this.focusTracker.focusedElement ) ) {
- editor.editing.view.focus();
- cancel();
- }
- } );
-
- editor.keystrokes.set( 'Alt+F9', ( data, cancel ) => {
- if ( !menuBarViewElement.contains( this.focusTracker.focusedElement ) ) {
- this.view.menuBarView!.focus();
- cancel();
- }
- } );
- }
}
diff --git a/packages/ckeditor5-editor-classic/tests/classiceditorui.js b/packages/ckeditor5-editor-classic/tests/classiceditorui.js
index dac99eda49f..81e5ec05c62 100644
--- a/packages/ckeditor5-editor-classic/tests/classiceditorui.js
+++ b/packages/ckeditor5-editor-classic/tests/classiceditorui.js
@@ -765,119 +765,6 @@ describe( 'Focus handling and navigation between editing root and editor toolbar
} );
} );
-describe( 'Focus handling and navigation between editing root and editor menu bar', () => {
- let editorElement, editor, ui, menuBarView, domRoot;
-
- testUtils.createSinonSandbox();
-
- beforeEach( async () => {
- editorElement = document.body.appendChild( document.createElement( 'div' ) );
-
- editor = await ClassicEditor.create( editorElement, {
- plugins: [ Paragraph, Image, ImageToolbar, ImageCaption ],
- toolbar: [ 'imageTextAlternative' ],
- image: {
- toolbar: [ 'toggleImageCaption' ]
- },
- menuBar: {
- isVisible: true
- }
- } );
-
- domRoot = editor.editing.view.domRoots.get( 'main' );
-
- ui = editor.ui;
- menuBarView = ui.view.menuBarView;
- } );
-
- afterEach( () => {
- editorElement.remove();
-
- return editor.destroy();
- } );
-
- describe( 'Focusing menu bar on Alt+F9 key press', () => {
- beforeEach( () => {
- ui.focusTracker.isFocused = true;
- ui.focusTracker.focusedElement = domRoot;
- } );
-
- it( 'should focus the menu bar when the focus is in the editing root', () => {
- const spy = testUtils.sinon.spy( menuBarView, 'focus' );
-
- setModelData( editor.model, 'foo[]' );
-
- ui.focusTracker.isFocused = true;
- ui.focusTracker.focusedElement = domRoot;
-
- // Focus the menu bar.
- pressAltF9( editor );
-
- sinon.assert.calledOnce( spy );
- } );
-
- it( 'should do nothing if the menu bar is already focused', () => {
- const domRootFocusSpy = testUtils.sinon.spy( domRoot, 'focus' );
- const menuBarFocusSpy = testUtils.sinon.spy( menuBarView, 'focus' );
-
- setModelData( editor.model, 'foo[]' );
-
- // Focus the menu bar.
- pressAltF9( editor );
- ui.focusTracker.focusedElement = menuBarView.element;
-
- // Try Alt+F9 again.
- pressAltF9( editor );
-
- sinon.assert.calledOnce( menuBarFocusSpy );
- sinon.assert.notCalled( domRootFocusSpy );
- } );
- } );
-
- describe( 'Restoring focus on Esc key press', () => {
- beforeEach( () => {
- ui.focusTracker.isFocused = true;
- ui.focusTracker.focusedElement = domRoot;
- } );
-
- it( 'should move the focus back from the menu bar to the editing root', () => {
- const domRootFocusSpy = testUtils.sinon.spy( domRoot, 'focus' );
- const menuBarFocusSpy = testUtils.sinon.spy( menuBarView, 'focus' );
-
- setModelData( editor.model, 'foo[]' );
-
- // Focus the menu bar.
- pressAltF9( editor );
- ui.focusTracker.focusedElement = menuBarView.element;
-
- pressEsc( editor );
-
- sinon.assert.callOrder( menuBarFocusSpy, domRootFocusSpy );
- } );
-
- it( 'should do nothing if it was pressed when menu bar was not focused', () => {
- const domRootFocusSpy = testUtils.sinon.spy( domRoot, 'focus' );
- const menuBarFocusSpy = testUtils.sinon.spy( menuBarView, 'focus' );
-
- setModelData( editor.model, 'foo[]' );
-
- pressEsc( editor );
-
- sinon.assert.notCalled( domRootFocusSpy );
- sinon.assert.notCalled( menuBarFocusSpy );
- } );
- } );
-} );
-
-function pressAltF9( editor ) {
- editor.keystrokes.press( {
- keyCode: keyCodes.f9,
- altKey: true,
- preventDefault: sinon.spy(),
- stopPropagation: sinon.spy()
- } );
-}
-
function pressAltF10( editor ) {
editor.keystrokes.press( {
keyCode: keyCodes.f10,
diff --git a/packages/ckeditor5-editor-decoupled/src/decouplededitorui.ts b/packages/ckeditor5-editor-decoupled/src/decouplededitorui.ts
index 6c7dff52cbc..5f635aa6162 100644
--- a/packages/ckeditor5-editor-decoupled/src/decouplededitorui.ts
+++ b/packages/ckeditor5-editor-decoupled/src/decouplededitorui.ts
@@ -13,8 +13,8 @@ import {
import {
EditorUI,
- normalizeMenuBarConfig,
- type EditorUIReadyEvent
+ type EditorUIReadyEvent,
+ _initMenuBar
} from 'ckeditor5/src/ui.js';
import { enablePlaceholder } from 'ckeditor5/src/engine.js';
@@ -81,7 +81,7 @@ export default class DecoupledEditorUI extends EditorUI {
this._initPlaceholder();
this._initToolbar();
- this._initMenuBar();
+ _initMenuBar( editor, this.view.menuBarView );
this.fire( 'ready' );
}
@@ -112,36 +112,6 @@ export default class DecoupledEditorUI extends EditorUI {
this.addToolbar( view.toolbar );
}
- /**
- * Initializes the editor menu bar.
- */
- private _initMenuBar(): void {
- const editor = this.editor;
- const menuBarViewElement = this.view.menuBarView.element!;
- const view = this.view;
-
- this.focusTracker.add( menuBarViewElement );
- editor.keystrokes.listenTo( menuBarViewElement );
-
- const normalizedMenuBarConfig = normalizeMenuBarConfig( editor.config.get( 'menuBar' ) || {} );
-
- view.menuBarView.fillFromConfig( normalizedMenuBarConfig, this.componentFactory );
-
- editor.keystrokes.set( 'Esc', ( data, cancel ) => {
- if ( menuBarViewElement.contains( this.focusTracker.focusedElement ) ) {
- editor.editing.view.focus();
- cancel();
- }
- } );
-
- editor.keystrokes.set( 'Alt+F9', ( data, cancel ) => {
- if ( !menuBarViewElement.contains( this.focusTracker.focusedElement ) ) {
- this.view.menuBarView.focus();
- cancel();
- }
- } );
- }
-
/**
* Enable the placeholder text on the editing root.
*/
diff --git a/packages/ckeditor5-editor-decoupled/tests/decouplededitorui.js b/packages/ckeditor5-editor-decoupled/tests/decouplededitorui.js
index 299d93ba99c..dd19a6e791a 100644
--- a/packages/ckeditor5-editor-decoupled/tests/decouplededitorui.js
+++ b/packages/ckeditor5-editor-decoupled/tests/decouplededitorui.js
@@ -11,7 +11,6 @@ import DecoupledEditor from '../src/decouplededitor.js';
import DecoupledEditorUI from '../src/decouplededitorui.js';
import DecoupledEditorUIView from '../src/decouplededitoruiview.js';
import EditorUI from '@ckeditor/ckeditor5-ui/src/editorui/editorui.js';
-import Heading from '@ckeditor/ckeditor5-heading/src/heading.js';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph.js';
import { Image, ImageCaption, ImageToolbar } from '@ckeditor/ckeditor5-image';
@@ -391,109 +390,6 @@ describe( 'Focus handling and navigation between editing root and editor toolbar
} );
} );
-describe( 'Focus handling and navigation between editing root and menu bar', () => {
- let editorElement, editor, ui, menuBarView, domRoot;
-
- testUtils.createSinonSandbox();
-
- beforeEach( async () => {
- editorElement = document.body.appendChild( document.createElement( 'div' ) );
-
- editor = await DecoupledEditor.create( editorElement, {
- plugins: [ Paragraph, Heading, Image, ImageToolbar, ImageCaption ],
- toolbar: [ 'imageTextAlternative' ],
- image: {
- toolbar: [ 'toggleImageCaption' ]
- }
- } );
-
- domRoot = editor.editing.view.domRoots.get( 'main' );
-
- ui = editor.ui;
- menuBarView = ui.view.menuBarView;
-
- document.body.appendChild( menuBarView.element );
- } );
-
- afterEach( () => {
- editorElement.remove();
- menuBarView.element.remove();
-
- return editor.destroy();
- } );
-
- describe( 'Focusing menu bar on Alt+F9 key press', () => {
- beforeEach( () => {
- ui.focusTracker.isFocused = true;
- ui.focusTracker.focusedElement = domRoot;
- } );
-
- it( 'should focus the menu bar when the focus is in the editing root', () => {
- const spy = testUtils.sinon.spy( menuBarView, 'focus' );
-
- setModelData( editor.model, 'foo[]' );
-
- ui.focusTracker.isFocused = true;
- ui.focusTracker.focusedElement = domRoot;
-
- pressAltF9( editor );
-
- sinon.assert.calledOnce( spy );
- } );
-
- it( 'should do nothing if the menu bar is already focused', () => {
- const domRootFocusSpy = testUtils.sinon.spy( domRoot, 'focus' );
- const menuBarFocusSpy = testUtils.sinon.spy( menuBarView, 'focus' );
-
- setModelData( editor.model, 'foo[]' );
-
- // Focus the toolbar.
- pressAltF9( editor );
- ui.focusTracker.focusedElement = menuBarView.element;
-
- // Try Alt+F9 again.
- pressAltF9( editor );
-
- sinon.assert.calledOnce( menuBarFocusSpy );
- sinon.assert.notCalled( domRootFocusSpy );
- } );
- } );
-
- describe( 'Restoring focus on Esc key press', () => {
- beforeEach( () => {
- ui.focusTracker.isFocused = true;
- ui.focusTracker.focusedElement = domRoot;
- } );
-
- it( 'should move the focus back from the main toolbar to the editing root', () => {
- const domRootFocusSpy = testUtils.sinon.spy( domRoot, 'focus' );
- const menuBarFocusSpy = testUtils.sinon.spy( menuBarView, 'focus' );
-
- setModelData( editor.model, 'foo[]' );
-
- // Focus the menu bar.
- pressAltF9( editor );
- ui.focusTracker.focusedElement = menuBarView.element;
-
- pressEsc( editor );
-
- sinon.assert.callOrder( menuBarFocusSpy, domRootFocusSpy );
- } );
-
- it( 'should do nothing if it was pressed when menu bar was not focused', () => {
- const domRootFocusSpy = testUtils.sinon.spy( domRoot, 'focus' );
- const menuBarFocusSpy = testUtils.sinon.spy( menuBarView, 'focus' );
-
- setModelData( editor.model, 'foo[]' );
-
- pressEsc( editor );
-
- sinon.assert.notCalled( domRootFocusSpy );
- sinon.assert.notCalled( menuBarFocusSpy );
- } );
- } );
-} );
-
function viewCreator( name ) {
return locale => {
const view = new View( locale );
@@ -505,15 +401,6 @@ function viewCreator( name ) {
};
}
-function pressAltF9( editor ) {
- editor.keystrokes.press( {
- keyCode: keyCodes.f9,
- altKey: true,
- preventDefault: sinon.spy(),
- stopPropagation: sinon.spy()
- } );
-}
-
function pressAltF10( editor ) {
editor.keystrokes.press( {
keyCode: keyCodes.f10,
diff --git a/packages/ckeditor5-editor-multi-root/src/multirooteditorui.ts b/packages/ckeditor5-editor-multi-root/src/multirooteditorui.ts
index ad8fc20591b..5f8e87caf60 100644
--- a/packages/ckeditor5-editor-multi-root/src/multirooteditorui.ts
+++ b/packages/ckeditor5-editor-multi-root/src/multirooteditorui.ts
@@ -13,6 +13,7 @@ import {
import {
EditorUI,
+ _initMenuBar,
type EditorUIReadyEvent,
type InlineEditableUIView
} from 'ckeditor5/src/ui.js';
@@ -84,6 +85,7 @@ export default class MultiRootEditorUI extends EditorUI {
}
this._initToolbar();
+ _initMenuBar( this.editor, this.view.menuBarView );
this.fire( 'ready' );
}
diff --git a/packages/ckeditor5-editor-multi-root/src/multirooteditoruiview.ts b/packages/ckeditor5-editor-multi-root/src/multirooteditoruiview.ts
index 654c133e601..aca42e2b05a 100644
--- a/packages/ckeditor5-editor-multi-root/src/multirooteditoruiview.ts
+++ b/packages/ckeditor5-editor-multi-root/src/multirooteditoruiview.ts
@@ -7,7 +7,7 @@
* @module editor-multi-root/multirooteditoruiview
*/
-import { EditorUIView, InlineEditableUIView, ToolbarView } from 'ckeditor5/src/ui.js';
+import { EditorUIView, InlineEditableUIView, MenuBarView, ToolbarView } from 'ckeditor5/src/ui.js';
import type { Locale } from 'ckeditor5/src/utils.js';
import type { EditingView } from 'ckeditor5/src/engine.js';
@@ -26,6 +26,11 @@ export default class MultiRootEditorUIView extends EditorUIView {
*/
public readonly toolbar: ToolbarView;
+ /**
+ * Menu bar view instance.
+ */
+ public readonly menuBarView: MenuBarView;
+
/**
* Editable elements used by the multi-root editor UI.
*/
@@ -70,6 +75,8 @@ export default class MultiRootEditorUIView extends EditorUIView {
shouldGroupWhenFull: options.shouldToolbarGroupWhenFull
} );
+ this.menuBarView = new MenuBarView( locale );
+
this.editables = {};
// Create `InlineEditableUIView` instance for each editable.
@@ -94,6 +101,16 @@ export default class MultiRootEditorUIView extends EditorUIView {
dir: locale.uiLanguageDirection
}
} );
+
+ this.menuBarView.extendTemplate( {
+ attributes: {
+ class: [
+ 'ck-reset_all',
+ 'ck-rounded-corners'
+ ],
+ dir: locale.uiLanguageDirection
+ }
+ } );
}
/**
@@ -150,5 +167,6 @@ export default class MultiRootEditorUIView extends EditorUIView {
this.registerChild( Object.values( this.editables ) );
this.registerChild( this.toolbar );
+ this.registerChild( this.menuBarView );
}
}
diff --git a/packages/ckeditor5-editor-multi-root/tests/manual/multirooteditor.html b/packages/ckeditor5-editor-multi-root/tests/manual/multirooteditor.html
index a3fda27a54b..5c76b005872 100644
--- a/packages/ckeditor5-editor-multi-root/tests/manual/multirooteditor.html
+++ b/packages/ckeditor5-editor-multi-root/tests/manual/multirooteditor.html
@@ -3,6 +3,9 @@
+The menubar
+
+
The toolbar
@@ -15,13 +18,15 @@ The editable