diff --git a/core/modules/ckeditor5/ckeditor5.module b/core/modules/ckeditor5/ckeditor5.module index f39dda6ec0..23b2a910d6 100644 --- a/core/modules/ckeditor5/ckeditor5.module +++ b/core/modules/ckeditor5/ckeditor5.module @@ -437,6 +437,15 @@ function ckeditor5_library_info_alter(&$libraries, $extension) { ]; } + if ($extension === 'core') { + // CSS rule to resolve the conflict with z-index between CKEditor 5 and jQuery UI. + $libraries['drupal.dialog']['css']['component']['modules/ckeditor5/css/ckeditor5.dialog.fix.css'] = []; + // Fix the CKEditor 5 focus management in dialogs. Modify the library + // declaration to ensure this file is always loaded after + // drupal.dialog.jquery-ui.js. + $libraries['drupal.dialog']['js']['modules/ckeditor5/js/ckeditor5.dialog.fix.js'] = []; + } + // Only add translation processing if the locale module is enabled. if (!$moduleHandler->moduleExists('locale')) { return; diff --git a/core/modules/ckeditor5/css/ckeditor5.dialog.fix.css b/core/modules/ckeditor5/css/ckeditor5.dialog.fix.css new file mode 100644 index 0000000000..b9e0a2e754 --- /dev/null +++ b/core/modules/ckeditor5/css/ckeditor5.dialog.fix.css @@ -0,0 +1,3 @@ +.ui-dialog ~ .ck-body-wrapper { + --ck-z-modal: 1261; +} diff --git a/core/modules/ckeditor5/js/ckeditor5.dialog.fix.es6.js b/core/modules/ckeditor5/js/ckeditor5.dialog.fix.es6.js new file mode 100644 index 0000000000..14e388ca6a --- /dev/null +++ b/core/modules/ckeditor5/js/ckeditor5.dialog.fix.es6.js @@ -0,0 +1,29 @@ +/** + * @file + * This file overrides the way jQuery UI focus trap works. + * + * When a focus event is fired while a CKEditor 5 instance is focused, do not + * trap the focus and let CKEditor 5 manage that focus. + */ + +(($) => { + // Get core version of the _focusTabbable method. + const oldFocusTabbable = $.ui.dialog._proto._focusTabbable; + + $.widget('ui.dialog', $.ui.dialog, { + // Override core override of jQuery UI's `_focusTabbable()` so that + // CKEditor 5 in modals can work as expected. + _focusTabbable() { + // When the focused element is a CKEditor 5 instance, disable jQuery UI + // focus trap and delegate focus trap to CKEditor 5. + const hasFocus = this._focusedElement + ? this._focusedElement.get(0) + : null; + // In case the element is a CKEditor 5 instance, do not change focus + // management. + if (!(hasFocus && hasFocus.ckeditorInstance)) { + oldFocusTabbable.call(this); + } + }, + }); +})(jQuery); diff --git a/core/modules/ckeditor5/js/ckeditor5.dialog.fix.js b/core/modules/ckeditor5/js/ckeditor5.dialog.fix.js new file mode 100644 index 0000000000..94333b6dcc --- /dev/null +++ b/core/modules/ckeditor5/js/ckeditor5.dialog.fix.js @@ -0,0 +1,19 @@ +/** +* DO NOT EDIT THIS FILE. +* See the following change record for more information, +* https://www.drupal.org/node/2815083 +* @preserve +**/ + +(function ($) { + var oldFocusTabbable = $.ui.dialog._proto._focusTabbable; + $.widget('ui.dialog', $.ui.dialog, { + _focusTabbable: function _focusTabbable() { + var hasFocus = this._focusedElement ? this._focusedElement.get(0) : null; + + if (!(hasFocus && hasFocus.ckeditorInstance)) { + oldFocusTabbable.call(this); + } + } + }); +})(jQuery); \ No newline at end of file diff --git a/core/modules/ckeditor5/tests/modules/ckeditor5_test/ckeditor5_test.routing.yml b/core/modules/ckeditor5/tests/modules/ckeditor5_test/ckeditor5_test.routing.yml index 50d7848984..a7730c0999 100644 --- a/core/modules/ckeditor5/tests/modules/ckeditor5_test/ckeditor5_test.routing.yml +++ b/core/modules/ckeditor5/tests/modules/ckeditor5_test/ckeditor5_test.routing.yml @@ -4,3 +4,10 @@ ckeditor5_test.off_canvas: _controller: '\Drupal\ckeditor5_test\Controller\CKEditor5OffCanvasTestController::testOffCanvas' requirements: _access: 'TRUE' + +ckeditor5_test.dialog: + path: '/ckeditor5_test/dialog' + defaults: + _controller: '\Drupal\ckeditor5_test\Controller\CKEditor5DialogTestController::testDialog' + requirements: + _access: 'TRUE' diff --git a/core/modules/ckeditor5/tests/modules/ckeditor5_test/src/Controller/CKEditor5DialogTestController.php b/core/modules/ckeditor5/tests/modules/ckeditor5_test/src/Controller/CKEditor5DialogTestController.php new file mode 100644 index 0000000000..2eed8d56b9 --- /dev/null +++ b/core/modules/ckeditor5/tests/modules/ckeditor5_test/src/Controller/CKEditor5DialogTestController.php @@ -0,0 +1,44 @@ + 'link', + '#title' => 'Add Node', + '#url' => Url::fromRoute('node.add', ['node_type' => 'page']), + '#attributes' => [ + 'class' => ['use-ajax'], + 'data-dialog-type' => 'dialog', + 'data-dialog-options' => Json::encode([ + 'width' => 700, + 'modal' => TRUE, + 'autoResize' => TRUE, + ]), + ], + ]; + $build['#attached']['library'][] = 'core/drupal.dialog.ajax'; + // Add this library to prevent Modernizr from triggering a deprecation + // notice during testing. + // @todo remove in https://www.drupal.org/project/drupal/issues/3269082. + $build['#attached']['library'][] = 'core/drupal.touchevents-test'; + return $build; + } + +} diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5DialogTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5DialogTest.php new file mode 100644 index 0000000000..9f2f97536a --- /dev/null +++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5DialogTest.php @@ -0,0 +1,84 @@ + 'test_format', + 'name' => 'CKEditor 5 with link', + 'roles' => [RoleInterface::AUTHENTICATED_ID], + ])->save(); + Editor::create([ + 'format' => 'test_format', + 'editor' => 'ckeditor5', + 'settings' => [ + 'toolbar' => [ + 'items' => ['link'], + ], + ], + ])->save(); + + $this->assertSame([], array_map( + function (ConstraintViolation $v) { + return (string) $v->getMessage(); + }, + iterator_to_array(CKEditor5::validatePair( + Editor::load('test_format'), + FilterFormat::load('test_format') + )) + )); + + $page = $this->getSession()->getPage(); + $assert_session = $this->assertSession(); + + $this->drupalGet('/ckeditor5_test/dialog'); + $page->clickLink('Add Node'); + $assert_session->waitForElementVisible('css', '[role="dialog"]'); + $assert_session->assertWaitOnAjaxRequest(); + + $content_area = $assert_session->waitForElementVisible('css', '.ck-editor__editable'); + // Focus the editable area first. + $content_area->click(); + // Then press the button to add a link. + $this->pressEditorButton('Link'); + + $link_url = '/ckeditor5_test/dialog'; + $input = $assert_session->waitForElementVisible('css', '.ck-balloon-panel input.ck-input-text'); + // Make sure the input field can have focus and we can type into it. + $input->setValue($link_url); + // Save the new link. + $page->find('css', '.ck-balloon-panel .ck-button-save')->click(); + // Make sure something was added to the text. + $this->assertNotEmpty($content_area->getText()); + } + +}