Issue #3274937 by nod_, woldtwerk, Dom., Wim Leers: Get CKEditor 5 to work in (modal) dialogs
parent
f90d892297
commit
6d6ab58776
|
@ -440,6 +440,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;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.ui-dialog ~ .ck-body-wrapper {
|
||||
--ck-z-modal: 1261;
|
||||
}
|
|
@ -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);
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
($ => {
|
||||
const oldFocusTabbable = $.ui.dialog._proto._focusTabbable;
|
||||
$.widget('ui.dialog', $.ui.dialog, {
|
||||
_focusTabbable() {
|
||||
const hasFocus = this._focusedElement ? this._focusedElement.get(0) : null;
|
||||
|
||||
if (!(hasFocus && hasFocus.ckeditorInstance)) {
|
||||
oldFocusTabbable.call(this);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
})(jQuery);
|
|
@ -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'
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Drupal\ckeditor5_test\Controller;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides controller for testing CKEditor in off-canvas dialogs.
|
||||
*/
|
||||
class CKEditor5DialogTestController {
|
||||
|
||||
/**
|
||||
* Returns a link that can open a node add form in an modal dialog.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
public function testDialog() {
|
||||
$build['link'] = [
|
||||
'#type' => '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';
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\ckeditor5\FunctionalJavascript;
|
||||
|
||||
use Drupal\ckeditor5\Plugin\Editor\CKEditor5;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
|
||||
/**
|
||||
* Tests for CKEditor 5 to ensure correct focus management in dialogs.
|
||||
*
|
||||
* @group ckeditor5
|
||||
* @internal
|
||||
*/
|
||||
class CKEditor5DialogTest extends CKEditor5TestBase {
|
||||
|
||||
use CKEditor5TestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'node',
|
||||
'ckeditor5',
|
||||
'ckeditor5_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests if CKEditor 5 tooltips can be interacted with in dialogs.
|
||||
*/
|
||||
public function testCKEditor5FocusInTooltipsInDialog() {
|
||||
FilterFormat::create([
|
||||
'format' => '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());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue