diff --git a/core/modules/outside_in/css/outside_in.module.css b/core/modules/outside_in/css/outside_in.module.css index 9d90bf227d..f49da91458 100644 --- a/core/modules/outside_in/css/outside_in.module.css +++ b/core/modules/outside_in/css/outside_in.module.css @@ -36,3 +36,7 @@ overflow-y: hidden; } } + +#settings-tray-edit-message:empty { + display: none !important; +} diff --git a/core/modules/outside_in/js/outside_in.es6.js b/core/modules/outside_in/js/outside_in.es6.js index 66708cea0d..dd939afd7b 100644 --- a/core/modules/outside_in/js/outside_in.es6.js +++ b/core/modules/outside_in/js/outside_in.es6.js @@ -1,9 +1,12 @@ /** * @file * Drupal's Settings Tray library. + * + * The Drupal.t() function is used in this file but cannot be destructed here + * aliased because Javascript files are scanned for calls to Drupal.t(). + * @todo Destructure Drupal.t() if in possilbe in https://www.drupal.org/node/2893361 */ - -(function ($, Drupal) { +(function ($, { announce, ajax, attachBehaviors, toolbar, behaviors }) { const blockConfigureSelector = '[data-outside-in-edit]'; const toggleEditSelector = '[data-drupal-outsidein="toggle"]'; const itemsToToggleSelector = '[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-outsidein="editable"] a, [data-drupal-outsidein="editable"] button'; @@ -24,7 +27,7 @@ // Bind Ajax behaviors to all items showing the class. // @todo Fix contextual links to work with use-ajax links in // https://www.drupal.org/node/2764931. - Drupal.attachBehaviors(data.$el[0]); + attachBehaviors(data.$el[0]); // Bind a listener to all 'Quick edit' links for blocks // Click "Edit" button in toolbar to force Contextual Edit which starts @@ -41,9 +44,7 @@ $(document).on('keyup.outsidein', (e) => { if (isInEditMode() && e.keyCode === 27) { - Drupal.announce( - Drupal.t('Exited edit mode.'), - ); + announce(Drupal.t('Exited edit mode.')); toggleEditMode(); } }); @@ -74,7 +75,7 @@ * Helper to toggle Edit mode. */ function toggleEditMode() { - setEditModeState(!isInEditMode()); + setEditModeState(!isInEditMode(), true); } /** @@ -97,7 +98,7 @@ * Close any active toolbar tray before entering edit mode. */ function closeToolbarTrays() { - $(Drupal.toolbar.models.toolbarModel.get('activeTab')).trigger('click'); + $(toolbar.models.toolbarModel.get('activeTab')).trigger('click'); } /** @@ -115,12 +116,47 @@ } /** + * Display a message informing the user that they have entered edit mode. + * + * This message appear at the top of the page below the toolbar. + * The user will be able to click on the message to remove it. + * It will also be removed when exiting edit mode. + * + * @todo Refactor to use Javascript Message API https://www.drupal.org/node/77245 + */ + function displayEditMessage() { + if ($('#settings-tray-edit-message').length) { + return; + } + const editMsg = Drupal.t('You are now in edit mode.'); + const msgDiv = document.createElement('div'); + msgDiv.setAttribute('id', 'settings-tray-edit-message') + Drupal.dialog(msgDiv, { + position: { + my: 'left+5 top+5', + at: 'left+5 top+5', + of: '.dialog-off-canvas__main-canvas', + }, + autoResize: false, + draggable: false, + minHeight: '50px', + title: editMsg, + dialogClass: 'outside-edit-message', + }).show(); + announce(editMsg); + $(msgDiv).on('click.outsidein', e => $(e.target).remove()); + } + + + /** * Helper to switch edit mode state. * * @param {boolean} editMode * True enable edit mode, false disable edit mode. + * @param {boolean} displayMessage + * True if the message about entering edit mode should be shown. */ - function setEditModeState(editMode) { + function setEditModeState(editMode, displayMessage = false) { if (!document.querySelector('[data-off-canvas-main-canvas]')) { throw new Error('data-off-canvas-main-canvas is missing from outside-in-page-wrapper.html.twig'); } @@ -132,6 +168,10 @@ $editButton.text(Drupal.t('Editing')); closeToolbarTrays(); + if (displayMessage) { + displayEditMessage(); + } + $editables = $('[data-drupal-outsidein="editable"]').once('outsidein'); if ($editables.length) { // Use event capture to prevent clicks on links. @@ -176,6 +216,8 @@ $editButton.text(Drupal.t('Edit')); closeOffCanvas(); disableQuickEdit(); + // Remove edit mode message. + $('#settings-tray-edit-message').remove(); } getItemsToToggle().toggleClass('js-outside-in-edit-mode', editMode); $('.edit-mode-inactive').toggleClass('visually-hidden', editMode); @@ -189,7 +231,7 @@ * @prop {Drupal~behaviorAttach} attach * Attaches contextual toolbar behavior on a contextualToolbar-init event. */ - Drupal.behaviors.outsideInEdit = { + behaviors.outsideInEdit = { attach() { const editMode = localStorage.getItem('Drupal.contextualToolbar.isViewing') === 'false'; if (editMode) { @@ -206,15 +248,15 @@ * @prop {Drupal~behaviorAttach} attach * Toggle the js-outside-edit-mode class. */ - Drupal.behaviors.toggleEditMode = { + behaviors.toggleEditMode = { attach() { $(toggleEditSelector).once('outsidein').on('click.outsidein', toggleEditMode); - const search = `${Drupal.ajax.WRAPPER_FORMAT}=drupal_dialog`; - const replace = `${Drupal.ajax.WRAPPER_FORMAT}=drupal_dialog_off_canvas`; + const search = `${ajax.WRAPPER_FORMAT}=drupal_dialog`; + const replace = `${ajax.WRAPPER_FORMAT}=drupal_dialog_off_canvas`; // Loop through all Ajax links and change the format to dialog-off-canvas when // needed. - Drupal.ajax.instances + ajax.instances .filter((instance) => { const hasElement = instance && !!instance.element; let rendererOffCanvas = false; @@ -257,4 +299,4 @@ } }, }); -}(jQuery, Drupal)); +})(jQuery, Drupal); diff --git a/core/modules/outside_in/js/outside_in.js b/core/modules/outside_in/js/outside_in.js index 635196d88f..abae786e20 100644 --- a/core/modules/outside_in/js/outside_in.js +++ b/core/modules/outside_in/js/outside_in.js @@ -5,7 +5,13 @@ * @preserve **/ -(function ($, Drupal) { +(function ($, _ref) { + var announce = _ref.announce, + ajax = _ref.ajax, + attachBehaviors = _ref.attachBehaviors, + toolbar = _ref.toolbar, + behaviors = _ref.behaviors; + var blockConfigureSelector = '[data-outside-in-edit]'; var toggleEditSelector = '[data-drupal-outsidein="toggle"]'; var itemsToToggleSelector = '[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-outsidein="editable"] a, [data-drupal-outsidein="editable"] button'; @@ -13,7 +19,7 @@ var quickEditItemSelector = '[data-quickedit-entity-id]'; $(document).on('drupalContextualLinkAdded', function (event, data) { - Drupal.attachBehaviors(data.$el[0]); + attachBehaviors(data.$el[0]); data.$el.find(blockConfigureSelector).on('click.outsidein', function () { if (!isInEditMode()) { @@ -26,7 +32,7 @@ $(document).on('keyup.outsidein', function (e) { if (isInEditMode() && e.keyCode === 27) { - Drupal.announce(Drupal.t('Exited edit mode.')); + announce(Drupal.t('Exited edit mode.')); toggleEditMode(); } }); @@ -40,7 +46,7 @@ } function toggleEditMode() { - setEditModeState(!isInEditMode()); + setEditModeState(!isInEditMode(), true); } function preventClick(event) { @@ -51,7 +57,7 @@ } function closeToolbarTrays() { - $(Drupal.toolbar.models.toolbarModel.get('activeTab')).trigger('click'); + $(toolbar.models.toolbarModel.get('activeTab')).trigger('click'); } function disableQuickEdit() { @@ -62,7 +68,34 @@ $('.ui-dialog-off-canvas .ui-dialog-titlebar-close').trigger('click'); } + function displayEditMessage() { + if ($('#settings-tray-edit-message').length) { + return; + } + var editMsg = Drupal.t('You are now in edit mode.'); + var msgDiv = document.createElement('div'); + msgDiv.setAttribute('id', 'settings-tray-edit-message'); + Drupal.dialog(msgDiv, { + position: { + my: 'left+5 top+5', + at: 'left+5 top+5', + of: '.dialog-off-canvas__main-canvas' + }, + autoResize: false, + draggable: false, + minHeight: '50px', + title: editMsg, + dialogClass: 'outside-edit-message' + }).show(); + announce(editMsg); + $(msgDiv).on('click.outsidein', function (e) { + return $(e.target).remove(); + }); + } + function setEditModeState(editMode) { + var displayMessage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + if (!document.querySelector('[data-off-canvas-main-canvas]')) { throw new Error('data-off-canvas-main-canvas is missing from outside-in-page-wrapper.html.twig'); } @@ -74,6 +107,10 @@ $editButton.text(Drupal.t('Editing')); closeToolbarTrays(); + if (displayMessage) { + displayEditMessage(); + } + $editables = $('[data-drupal-outsidein="editable"]').once('outsidein'); if ($editables.length) { document.querySelector('[data-off-canvas-main-canvas]').addEventListener('click', preventClick, true); @@ -107,12 +144,14 @@ $editButton.text(Drupal.t('Edit')); closeOffCanvas(); disableQuickEdit(); + + $('#settings-tray-edit-message').remove(); } getItemsToToggle().toggleClass('js-outside-in-edit-mode', editMode); $('.edit-mode-inactive').toggleClass('visually-hidden', editMode); } - Drupal.behaviors.outsideInEdit = { + behaviors.outsideInEdit = { attach: function attach() { var editMode = localStorage.getItem('Drupal.contextualToolbar.isViewing') === 'false'; if (editMode) { @@ -121,14 +160,14 @@ } }; - Drupal.behaviors.toggleEditMode = { + behaviors.toggleEditMode = { attach: function attach() { $(toggleEditSelector).once('outsidein').on('click.outsidein', toggleEditMode); - var search = Drupal.ajax.WRAPPER_FORMAT + '=drupal_dialog'; - var replace = Drupal.ajax.WRAPPER_FORMAT + '=drupal_dialog_off_canvas'; + var search = ajax.WRAPPER_FORMAT + '=drupal_dialog'; + var replace = ajax.WRAPPER_FORMAT + '=drupal_dialog_off_canvas'; - Drupal.ajax.instances.filter(function (instance) { + ajax.instances.filter(function (instance) { var hasElement = instance && !!instance.element; var rendererOffCanvas = false; var wrapperOffCanvas = false; diff --git a/core/modules/outside_in/outside_in.libraries.yml b/core/modules/outside_in/outside_in.libraries.yml index 5c388b82f1..5946d95b75 100644 --- a/core/modules/outside_in/outside_in.libraries.yml +++ b/core/modules/outside_in/outside_in.libraries.yml @@ -20,6 +20,8 @@ drupal.outside_in: - core/drupal - core/jquery.once - core/drupal.ajax + - core/drupal.announce + - toolbar/toolbar drupal.off_canvas: version: VERSION js: diff --git a/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php b/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php index a5eacc5c53..5e7ffaab3f 100644 --- a/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php +++ b/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php @@ -214,6 +214,7 @@ protected function openBlockForm($block_selector) { $this->click($block_selector); $this->waitForOffCanvasToOpen(); $this->assertOffCanvasBlockFormIsValid(); + $this->closeMessageDialog(); } /** @@ -351,6 +352,8 @@ protected function assertEditModeEnabled() { $web_assert->elementContains('css', static::TOOLBAR_EDIT_LINK_SELECTOR, 'Editing'); // The main canvas element should have the "js-outside-in-edit-mode" class. $web_assert->elementExists('css', '.dialog-off-canvas__main-canvas.js-outside-in-edit-mode'); + $web_assert->elementTextContains('css', '.outside-edit-message', 'You are now in edit mode'); + $this->closeMessageDialog(); } /** @@ -367,6 +370,7 @@ protected function assertEditModeDisabled() { // The main canvas element should NOT have the "js-outside-in-edit-mode" // class. $web_assert->elementNotExists('css', '.dialog-off-canvas__main-canvas.js-outside-in-edit-mode'); + $web_assert->elementNotExists('css', '#settings-tray-edit-message'); } /** @@ -469,4 +473,16 @@ public function getBlockSelector(Block $block) { return '#block-' . $block->id(); } + /** + * Closes the message dialog that contains "You are in edit mode." message. + * + * Also waits for the dialog to be removed from the page. + */ + protected function closeMessageDialog() { + if ($message_dialog_close_button = $this->assertSession()->waitForElementVisible('css', '[aria-describedby="settings-tray-edit-message"]')) { + $message_dialog_close_button->click(); + $this->waitForNoElement('css', '[aria-describedby="settings-tray-edit-message"]'); + } + } + }