diff --git a/core/modules/ckeditor5/js/ckeditor5.es6.js b/core/modules/ckeditor5/js/ckeditor5.es6.js index 6716e8d2c02..e5bff4ab57a 100644 --- a/core/modules/ckeditor5/js/ckeditor5.es6.js +++ b/core/modules/ckeditor5/js/ckeditor5.es6.js @@ -322,6 +322,16 @@ element.removeAttribute('required'); } + // Integrate CKEditor 5 viewport offset with Drupal displace. + // @see \Drupal\Tests\ckeditor5\FunctionalJavascript\CKEditor5ToolbarTest + // @see https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editorui-EditorUI.html#member-viewportOffset + $(document).on( + `drupalViewportOffsetChange.ckeditor5.${id}`, + (event, offsets) => { + editor.ui.viewportOffset = offsets; + }, + ); + editor.model.document.on('change:data', () => { const callback = callbacks.get(id); if (callback) { @@ -372,6 +382,9 @@ if (!editor) { return; } + + $(document).off(`drupalViewportOffsetChange.ckeditor5.${id}`); + if (trigger === 'serialize') { editor.updateSourceElement(); } else { diff --git a/core/modules/ckeditor5/js/ckeditor5.js b/core/modules/ckeditor5/js/ckeditor5.js index 6e7a876e7a9..9094a9d499c 100644 --- a/core/modules/ckeditor5/js/ckeditor5.js +++ b/core/modules/ckeditor5/js/ckeditor5.js @@ -198,6 +198,9 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len element.removeAttribute('required'); } + $(document).on("drupalViewportOffsetChange.ckeditor5.".concat(id), function (event, offsets) { + editor.ui.viewportOffset = offsets; + }); editor.model.document.on('change:data', function () { var callback = callbacks.get(id); @@ -229,6 +232,8 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len return; } + $(document).off("drupalViewportOffsetChange.ckeditor5.".concat(id)); + if (trigger === 'serialize') { editor.updateSourceElement(); } else { diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5ToolbarTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5ToolbarTest.php new file mode 100644 index 00000000000..aa2dbd1471a --- /dev/null +++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/CKEditor5ToolbarTest.php @@ -0,0 +1,101 @@ + 'test_format', + 'name' => 'Test format', + 'filters' => [], + ])->save(); + Editor::create([ + 'editor' => 'ckeditor5', + 'format' => 'test_format', + 'settings' => [], + ])->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') + )) + )); + + $this->drupalCreateContentType([ + 'type' => 'article', + 'name' => 'Article', + ]); + + $this->user = $this->drupalCreateUser([ + 'use text format test_format', + 'access toolbar', + 'edit any article content', + 'administer site configuration', + ]); + $this->drupalLogin($this->user); + } + + /** + * Ensures that CKEditor 5 toolbar renders below Drupal Toolbar. + */ + public function test(): void { + $assert_session = $this->assertSession(); + + // Create test content to ensure that CKEditor 5 text editor can be + // scrolled. + $body = ''; + for ($i = 0; $i < 10; $i++) { + $body .= '

' . $this->randomMachineName(32) . '

'; + } + $edit_url = $this->drupalCreateNode(['type' => 'article', 'body' => ['value' => $body, 'format' => 'test_format']])->toUrl('edit-form'); + $this->drupalGet($edit_url); + $this->assertNotEmpty($assert_session->waitForElement('css', '#toolbar-bar')); + $this->assertNotEmpty($assert_session->waitForElement('css', '.ck-editor')); + + // Ensure the body has enough height to enable scrolling. Scroll 110px from + // top of body field to ensure CKEditor 5 toolbar is sticky. + $this->getSession()->evaluateScript('document.body.style.height = "10000px";'); + $this->getSession()->evaluateScript('location.hash = "#edit-body-0-value";'); + $this->getSession()->evaluateScript('scroll(0, document.documentElement.scrollTop + 110);'); + // Focus CKEditor 5 text editor. + $javascript = <<getSession()->evaluateScript($javascript); + + $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-sticky-panel__placeholder')); + $toolbar_height = (int) $this->getSession()->evaluateScript('document.getElementById("toolbar-bar").offsetHeight'); + $ckeditor5_toolbar_position = (int) $this->getSession()->evaluateScript("document.querySelector('.ck-toolbar').getBoundingClientRect().top"); + $this->assertEqualsWithDelta($toolbar_height, $ckeditor5_toolbar_position, 2); + } + +}