Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
23
web/core/modules/settings_tray/css/settings_tray.module.css
Normal file
23
web/core/modules/settings_tray/css/settings_tray.module.css
Normal file
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* @file
|
||||
* Styling for Settings Tray module.
|
||||
*/
|
||||
/*
|
||||
* Position the edit toolbar tab.
|
||||
* @todo Move changes into contextual module when Settings Tray is not
|
||||
* experimental: https://www.drupal.org/node/2784591.
|
||||
*/
|
||||
.toolbar .toolbar-bar .contextual-toolbar-tab.toolbar-tab {
|
||||
float: left;
|
||||
}
|
||||
[dir="rtl"] .toolbar .toolbar-bar .contextual-toolbar-tab.toolbar-tab {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode a,
|
||||
.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode input {
|
||||
pointer-events: none;
|
||||
}
|
||||
.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode .contextual-links a {
|
||||
pointer-events: inherit;
|
||||
}
|
19
web/core/modules/settings_tray/css/settings_tray.motion.css
Normal file
19
web/core/modules/settings_tray/css/settings_tray.motion.css
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* @file
|
||||
* Motion effects for Settings Tray module.
|
||||
*
|
||||
* Motion effects are in a separate file so that they can be easily turned off
|
||||
* to improve performance if desired.
|
||||
*/
|
||||
|
||||
/* Transition the edit icon in the toolbar. */
|
||||
#toolbar-bar.button.toolbar-icon.toolbar-icon.toolbar-icon-edit:before {
|
||||
transition: all 0.7s ease;
|
||||
}
|
||||
|
||||
/* Transition the editables on the page, their contextual links and their hover states. */
|
||||
.dialog-off-canvas-main-canvas .contextual,
|
||||
.dialog-off-canvas-main-canvas .js-settings-tray-edit-mode .settings-tray-editable,
|
||||
.dialog-off-canvas-main-canvas.js-off-canvas-dialog-open .js-settings-tray-edit-mode .settings-tray-editable {
|
||||
transition: all 0.7s ease;
|
||||
}
|
70
web/core/modules/settings_tray/css/settings_tray.theme.css
Normal file
70
web/core/modules/settings_tray/css/settings_tray.theme.css
Normal file
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styling for Settings Tray module.
|
||||
*/
|
||||
|
||||
/* @todo remove the @imports when we find a better way to load these styles last.
|
||||
* https://www.drupal.org/node/1945262.
|
||||
*/
|
||||
|
||||
/* Style the edit mode toolbar and tabs. */
|
||||
#toolbar-bar.js-settings-tray-edit-mode {
|
||||
background-image: linear-gradient(to bottom, #0a7bc1, #0a6eb4);
|
||||
}
|
||||
.js-settings-tray-edit-mode .toolbar-item:not(.toolbar-icon-edit) {
|
||||
color: #999;
|
||||
}
|
||||
.js-settings-tray-edit-mode .toolbar-item:not(.toolbar-icon-edit) .is-active {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Style both the edit and editing states of the contextual links toggle tab. */
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item,
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item.is-active,
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item:focus {
|
||||
background-color: #0066a1;
|
||||
background-image: linear-gradient(to bottom, #0066a1, #005b98);
|
||||
color: #eee;
|
||||
text-shadow: none;
|
||||
font-weight: bold;
|
||||
outline: none;
|
||||
}
|
||||
/* Make the hover of the inactive state the same as the active state. */
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item:hover,
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item.is-active {
|
||||
background-image: linear-gradient(to bottom, #0a7bc1, #0a6eb4);
|
||||
color: #fff;
|
||||
}
|
||||
/* Make the hover of the active state the same as the inactive state. */
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item.is-active:hover {
|
||||
background-color: #0066a1;
|
||||
background-image: linear-gradient(to bottom, #0066a1, #005b98);
|
||||
color: #fff;
|
||||
}
|
||||
/* Make the inactive icon grey. */
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item:before {
|
||||
background-image: url(../../../misc/icons/bebebe/pencil.svg);
|
||||
}
|
||||
/* Make the active icon white. */
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item.is-active:before {
|
||||
background-image: url(../../../misc/icons/ffffff/pencil.svg);
|
||||
}
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item:hover:before {
|
||||
background-image: url(../../../misc/icons/ffffff/pencil.svg);
|
||||
}
|
||||
.toolbar-tab > .toolbar-icon.toolbar-icon-edit.toolbar-item:hover > .toolbar-icon-edit:before {
|
||||
background-image: url(../../../misc/icons/ffffff/pencil.svg);
|
||||
}
|
||||
.toolbar-tab > .button.toolbar-icon.toolbar-icon.toolbar-icon-edit:before {
|
||||
background-image: url(../../../misc/icons/ffffff/pencil.svg);
|
||||
}
|
||||
|
||||
/* Style the editables while in edit mode. */
|
||||
.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode .settings-tray-editable {
|
||||
outline: 1px dashed rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode .settings-tray-editable:hover,
|
||||
.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode .settings-tray-editable.settings-tray-active-editable {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
66
web/core/modules/settings_tray/css/settings_tray.toolbar.css
Normal file
66
web/core/modules/settings_tray/css/settings_tray.toolbar.css
Normal file
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @file
|
||||
* Visual styling for the toolbar when Settings Tray module is enabled.
|
||||
*/
|
||||
|
||||
/* @todo Move this into toolbar when module is not experimental:
|
||||
* https://www.drupal.org/node/2784593.
|
||||
*/
|
||||
|
||||
/* Style the edit mode toolbar and tabs. */
|
||||
#toolbar-bar.js-settings-tray-edit-mode {
|
||||
background-color: #fff;
|
||||
}
|
||||
#toolbar-bar.js-settings-tray-edit-mode .toolbar-item {
|
||||
color: #999;
|
||||
}
|
||||
#toolbar-bar.js-settings-tray-edit-mode .toolbar-item .is-active {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Style both the edit and editing states of the contextual links toggle tab. */
|
||||
.toolbar-icon-edit.toolbar-item {
|
||||
background-color: #0066a1;
|
||||
background-image: linear-gradient(to bottom, #0066a1, #005b98);
|
||||
color: #eee;
|
||||
text-shadow: 0 1px hsla(0, 0%, 0%, 0.5);
|
||||
font-weight: 700;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.toolbar-icon-edit.toolbar-item.is-active {
|
||||
background-color: #0a7bc1;
|
||||
background-image: linear-gradient(to bottom, #0a7bc1, #0a6eb4);
|
||||
color: #fff;
|
||||
text-shadow: 0 1px hsla(0, 0%, 0%, 0.5);
|
||||
font-weight: 700;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.toolbar-tab:hover > .toolbar-icon-edit,
|
||||
.toolbar-icon-edit:focus .toolbar-item {
|
||||
background-color: #0a7bc1;
|
||||
background-image: linear-gradient(to bottom, #0a7bc1, #0a6eb4);
|
||||
border-color: #1e5c90;
|
||||
color: #fff;
|
||||
outline: none;
|
||||
}
|
||||
.toolbar-icon.toolbar-icon-edit.toolbar-item:before,
|
||||
button.toolbar-icon.toolbar-icon-edit.toolbar-item:before {
|
||||
background-image: url(../../../misc/icons/bebebe/pencil.svg);
|
||||
}
|
||||
.toolbar-icon.toolbar-icon-edit.toolbar-item:before:hover,
|
||||
button.toolbar-icon.toolbar-icon-edit.toolbar-item:before:focus {
|
||||
background-image: url(../../../misc/icons/ffffff/pencil.svg);
|
||||
}
|
||||
.toolbar-icon.toolbar-icon-edit.toolbar-item:hover > .toolbar-icon-edit:before {
|
||||
background-image: url(../../../misc/icons/ffffff/pencil.svg);
|
||||
}
|
||||
#toolbar-bar.button.toolbar-icon.toolbar-icon.toolbar-icon-edit:before {
|
||||
background-image: url(../../../misc/icons/ffffff/pencil.svg);
|
||||
}
|
||||
|
||||
#toolbar-bar.js-settings-tray-edit-mode button.toolbar-icon.toolbar-icon-edit.toolbar-item.is-active {
|
||||
color: #fff;
|
||||
}
|
||||
#toolbar-bar.js-settings-tray-edit-mode button.toolbar-icon.toolbar-icon-edit.toolbar-item.is-active:hover {
|
||||
background-image: linear-gradient(to bottom, #0a6fb4, #0a65aa);
|
||||
}
|
309
web/core/modules/settings_tray/js/settings_tray.es6.js
Normal file
309
web/core/modules/settings_tray/js/settings_tray.es6.js
Normal file
|
@ -0,0 +1,309 @@
|
|||
/**
|
||||
* @file
|
||||
* Drupal's Settings Tray library.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
(($, Drupal) => {
|
||||
const blockConfigureSelector = '[data-settings-tray-edit]';
|
||||
const toggleEditSelector = '[data-drupal-settingstray="toggle"]';
|
||||
const itemsToToggleSelector =
|
||||
'[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-settingstray="editable"] a, [data-drupal-settingstray="editable"] button';
|
||||
const contextualItemsSelector =
|
||||
'[data-contextual-id] a, [data-contextual-id] button';
|
||||
const quickEditItemSelector = '[data-quickedit-entity-id]';
|
||||
|
||||
/**
|
||||
* Prevent default click events except contextual links.
|
||||
*
|
||||
* In edit mode the default action of click events is suppressed.
|
||||
*
|
||||
* @param {jQuery.Event} event
|
||||
* The click event.
|
||||
*/
|
||||
function preventClick(event) {
|
||||
// Do not prevent contextual links.
|
||||
if ($(event.target).closest('.contextual-links').length) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close any active toolbar tray before entering edit mode.
|
||||
*/
|
||||
function closeToolbarTrays() {
|
||||
$(Drupal.toolbar.models.toolbarModel.get('activeTab')).trigger('click');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the QuickEdit module editor if open.
|
||||
*/
|
||||
function disableQuickEdit() {
|
||||
$('.quickedit-toolbar button.action-cancel').trigger('click');
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes/removes off-canvas.
|
||||
*/
|
||||
function closeOffCanvas() {
|
||||
$('.ui-dialog-off-canvas .ui-dialog-titlebar-close').trigger('click');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all items that should be toggled with class during edit mode.
|
||||
*
|
||||
* @return {jQuery}
|
||||
* Items that should be toggled.
|
||||
*/
|
||||
function getItemsToToggle() {
|
||||
return $(itemsToToggleSelector).not(contextualItemsSelector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to switch edit mode state.
|
||||
*
|
||||
* @param {boolean} editMode
|
||||
* True enable edit mode, false disable edit mode.
|
||||
*/
|
||||
function setEditModeState(editMode) {
|
||||
if (!document.querySelector('[data-off-canvas-main-canvas]')) {
|
||||
throw new Error(
|
||||
'data-off-canvas-main-canvas is missing from settings-tray-page-wrapper.html.twig',
|
||||
);
|
||||
}
|
||||
editMode = !!editMode;
|
||||
const $editButton = $(toggleEditSelector);
|
||||
let $editables;
|
||||
// Turn on edit mode.
|
||||
if (editMode) {
|
||||
$editButton.text(Drupal.t('Editing'));
|
||||
closeToolbarTrays();
|
||||
|
||||
$editables = $('[data-drupal-settingstray="editable"]').once(
|
||||
'settingstray',
|
||||
);
|
||||
if ($editables.length) {
|
||||
// Use event capture to prevent clicks on links.
|
||||
document
|
||||
.querySelector('[data-off-canvas-main-canvas]')
|
||||
.addEventListener('click', preventClick, true);
|
||||
/**
|
||||
* When a click occurs try and find the settings-tray edit link
|
||||
* and click it.
|
||||
*/
|
||||
$editables.not(contextualItemsSelector).on('click.settingstray', e => {
|
||||
// Contextual links are allowed to function in Edit mode.
|
||||
if (
|
||||
$(e.target).closest('.contextual').length ||
|
||||
!localStorage.getItem('Drupal.contextualToolbar.isViewing')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$(e.currentTarget)
|
||||
.find(blockConfigureSelector)
|
||||
.trigger('click');
|
||||
disableQuickEdit();
|
||||
});
|
||||
$(quickEditItemSelector)
|
||||
.not(contextualItemsSelector)
|
||||
.on('click.settingstray', e => {
|
||||
/**
|
||||
* For all non-contextual links or the contextual QuickEdit link
|
||||
* close the off-canvas dialog.
|
||||
*/
|
||||
if (
|
||||
!$(e.target)
|
||||
.parent()
|
||||
.hasClass('contextual') ||
|
||||
$(e.target)
|
||||
.parent()
|
||||
.hasClass('quickedit')
|
||||
) {
|
||||
closeOffCanvas();
|
||||
}
|
||||
// Do not trigger if target is quick edit link to avoid loop.
|
||||
if (
|
||||
$(e.target)
|
||||
.parent()
|
||||
.hasClass('contextual') ||
|
||||
$(e.target)
|
||||
.parent()
|
||||
.hasClass('quickedit')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$(e.currentTarget)
|
||||
.find('li.quickedit a')
|
||||
.trigger('click');
|
||||
});
|
||||
}
|
||||
}
|
||||
// Disable edit mode.
|
||||
else {
|
||||
$editables = $('[data-drupal-settingstray="editable"]').removeOnce(
|
||||
'settingstray',
|
||||
);
|
||||
if ($editables.length) {
|
||||
document
|
||||
.querySelector('[data-off-canvas-main-canvas]')
|
||||
.removeEventListener('click', preventClick, true);
|
||||
$editables.off('.settingstray');
|
||||
$(quickEditItemSelector).off('.settingstray');
|
||||
}
|
||||
|
||||
$editButton.text(Drupal.t('Edit'));
|
||||
closeOffCanvas();
|
||||
disableQuickEdit();
|
||||
}
|
||||
getItemsToToggle().toggleClass('js-settings-tray-edit-mode', editMode);
|
||||
$('.edit-mode-inactive').toggleClass('visually-hidden', editMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to check the state of the settings-tray mode.
|
||||
*
|
||||
* @todo don't use a class for this.
|
||||
*
|
||||
* @return {boolean}
|
||||
* State of the settings-tray edit mode.
|
||||
*/
|
||||
function isInEditMode() {
|
||||
return $('#toolbar-bar').hasClass('js-settings-tray-edit-mode');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to toggle Edit mode.
|
||||
*/
|
||||
function toggleEditMode() {
|
||||
setEditModeState(!isInEditMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares Ajax links to work with off-canvas and Settings Tray module.
|
||||
*/
|
||||
function prepareAjaxLinks() {
|
||||
// Find all Ajax instances that use the 'off_canvas' renderer.
|
||||
Drupal.ajax.instances
|
||||
/**
|
||||
* If there is an element and the renderer is 'off_canvas' then we want
|
||||
* to add our changes.
|
||||
*/
|
||||
.filter(
|
||||
instance =>
|
||||
instance &&
|
||||
$(instance.element).attr('data-dialog-renderer') === 'off_canvas',
|
||||
)
|
||||
/**
|
||||
* Loop through all Ajax instances that use the 'off_canvas' renderer to
|
||||
* set active editable ID.
|
||||
*/
|
||||
.forEach(instance => {
|
||||
// Check to make sure existing dialogOptions aren't overridden.
|
||||
if (!instance.options.data.hasOwnProperty('dialogOptions')) {
|
||||
instance.options.data.dialogOptions = {};
|
||||
}
|
||||
instance.options.data.dialogOptions.settingsTrayActiveEditableId = $(
|
||||
instance.element,
|
||||
)
|
||||
.parents('.settings-tray-editable')
|
||||
.attr('id');
|
||||
instance.progress = { type: 'fullscreen' };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to contextual links being added.
|
||||
*
|
||||
* @param {jQuery.Event} event
|
||||
* The `drupalContextualLinkAdded` event.
|
||||
* @param {object} data
|
||||
* An object containing the data relevant to the event.
|
||||
*
|
||||
* @listens event:drupalContextualLinkAdded
|
||||
*/
|
||||
$(document).on('drupalContextualLinkAdded', (event, data) => {
|
||||
/**
|
||||
* When contextual links are add we need to set extra properties on the
|
||||
* instances in Drupal.ajax.instances for them to work with Edit Mode.
|
||||
*/
|
||||
prepareAjaxLinks();
|
||||
|
||||
// When the first contextual link is added to the page set Edit Mode.
|
||||
$('body')
|
||||
.once('settings_tray.edit_mode_init')
|
||||
.each(() => {
|
||||
const editMode =
|
||||
localStorage.getItem('Drupal.contextualToolbar.isViewing') ===
|
||||
'false';
|
||||
if (editMode) {
|
||||
setEditModeState(true);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Bind a listener to all 'Quick edit' links for blocks. Click "Edit"
|
||||
* button in toolbar to force Contextual Edit which starts Settings Tray
|
||||
* edit mode also.
|
||||
*/
|
||||
data.$el.find(blockConfigureSelector).on('click.settingstray', () => {
|
||||
if (!isInEditMode()) {
|
||||
$(toggleEditSelector)
|
||||
.trigger('click')
|
||||
.trigger('click.settings_tray');
|
||||
}
|
||||
/**
|
||||
* Always disable QuickEdit regardless of whether "EditMode" was just
|
||||
* enabled.
|
||||
*/
|
||||
disableQuickEdit();
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('keyup.settingstray', e => {
|
||||
if (isInEditMode() && e.keyCode === 27) {
|
||||
Drupal.announce(Drupal.t('Exited edit mode.'));
|
||||
toggleEditMode();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Toggle the js-settings-tray-edit-mode class on items that we want to
|
||||
* disable while in edit mode.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Toggle the js-settings-tray-edit-mode class.
|
||||
*/
|
||||
Drupal.behaviors.toggleEditMode = {
|
||||
attach() {
|
||||
$(toggleEditSelector)
|
||||
.once('settingstray')
|
||||
.on('click.settingstray', toggleEditMode);
|
||||
},
|
||||
};
|
||||
|
||||
// Manage Active editable class on opening and closing of the dialog.
|
||||
$(window).on({
|
||||
'dialog:beforecreate': (event, dialog, $element, settings) => {
|
||||
if ($element.is('#drupal-off-canvas')) {
|
||||
$('body .settings-tray-active-editable').removeClass(
|
||||
'settings-tray-active-editable',
|
||||
);
|
||||
const $activeElement = $(`#${settings.settingsTrayActiveEditableId}`);
|
||||
if ($activeElement.length) {
|
||||
$activeElement.addClass('settings-tray-active-editable');
|
||||
}
|
||||
}
|
||||
},
|
||||
'dialog:beforeclose': (event, dialog, $element) => {
|
||||
if ($element.is('#drupal-off-canvas')) {
|
||||
$('body .settings-tray-active-editable').removeClass(
|
||||
'settings-tray-active-editable',
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
})(jQuery, Drupal);
|
156
web/core/modules/settings_tray/js/settings_tray.js
Normal file
156
web/core/modules/settings_tray/js/settings_tray.js
Normal file
|
@ -0,0 +1,156 @@
|
|||
/**
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal) {
|
||||
var blockConfigureSelector = '[data-settings-tray-edit]';
|
||||
var toggleEditSelector = '[data-drupal-settingstray="toggle"]';
|
||||
var itemsToToggleSelector = '[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-settingstray="editable"] a, [data-drupal-settingstray="editable"] button';
|
||||
var contextualItemsSelector = '[data-contextual-id] a, [data-contextual-id] button';
|
||||
var quickEditItemSelector = '[data-quickedit-entity-id]';
|
||||
|
||||
function preventClick(event) {
|
||||
if ($(event.target).closest('.contextual-links').length) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function closeToolbarTrays() {
|
||||
$(Drupal.toolbar.models.toolbarModel.get('activeTab')).trigger('click');
|
||||
}
|
||||
|
||||
function disableQuickEdit() {
|
||||
$('.quickedit-toolbar button.action-cancel').trigger('click');
|
||||
}
|
||||
|
||||
function closeOffCanvas() {
|
||||
$('.ui-dialog-off-canvas .ui-dialog-titlebar-close').trigger('click');
|
||||
}
|
||||
|
||||
function getItemsToToggle() {
|
||||
return $(itemsToToggleSelector).not(contextualItemsSelector);
|
||||
}
|
||||
|
||||
function setEditModeState(editMode) {
|
||||
if (!document.querySelector('[data-off-canvas-main-canvas]')) {
|
||||
throw new Error('data-off-canvas-main-canvas is missing from settings-tray-page-wrapper.html.twig');
|
||||
}
|
||||
editMode = !!editMode;
|
||||
var $editButton = $(toggleEditSelector);
|
||||
var $editables = void 0;
|
||||
|
||||
if (editMode) {
|
||||
$editButton.text(Drupal.t('Editing'));
|
||||
closeToolbarTrays();
|
||||
|
||||
$editables = $('[data-drupal-settingstray="editable"]').once('settingstray');
|
||||
if ($editables.length) {
|
||||
document.querySelector('[data-off-canvas-main-canvas]').addEventListener('click', preventClick, true);
|
||||
|
||||
$editables.not(contextualItemsSelector).on('click.settingstray', function (e) {
|
||||
if ($(e.target).closest('.contextual').length || !localStorage.getItem('Drupal.contextualToolbar.isViewing')) {
|
||||
return;
|
||||
}
|
||||
$(e.currentTarget).find(blockConfigureSelector).trigger('click');
|
||||
disableQuickEdit();
|
||||
});
|
||||
$(quickEditItemSelector).not(contextualItemsSelector).on('click.settingstray', function (e) {
|
||||
if (!$(e.target).parent().hasClass('contextual') || $(e.target).parent().hasClass('quickedit')) {
|
||||
closeOffCanvas();
|
||||
}
|
||||
|
||||
if ($(e.target).parent().hasClass('contextual') || $(e.target).parent().hasClass('quickedit')) {
|
||||
return;
|
||||
}
|
||||
$(e.currentTarget).find('li.quickedit a').trigger('click');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$editables = $('[data-drupal-settingstray="editable"]').removeOnce('settingstray');
|
||||
if ($editables.length) {
|
||||
document.querySelector('[data-off-canvas-main-canvas]').removeEventListener('click', preventClick, true);
|
||||
$editables.off('.settingstray');
|
||||
$(quickEditItemSelector).off('.settingstray');
|
||||
}
|
||||
|
||||
$editButton.text(Drupal.t('Edit'));
|
||||
closeOffCanvas();
|
||||
disableQuickEdit();
|
||||
}
|
||||
getItemsToToggle().toggleClass('js-settings-tray-edit-mode', editMode);
|
||||
$('.edit-mode-inactive').toggleClass('visually-hidden', editMode);
|
||||
}
|
||||
|
||||
function isInEditMode() {
|
||||
return $('#toolbar-bar').hasClass('js-settings-tray-edit-mode');
|
||||
}
|
||||
|
||||
function toggleEditMode() {
|
||||
setEditModeState(!isInEditMode());
|
||||
}
|
||||
|
||||
function prepareAjaxLinks() {
|
||||
Drupal.ajax.instances.filter(function (instance) {
|
||||
return instance && $(instance.element).attr('data-dialog-renderer') === 'off_canvas';
|
||||
}).forEach(function (instance) {
|
||||
if (!instance.options.data.hasOwnProperty('dialogOptions')) {
|
||||
instance.options.data.dialogOptions = {};
|
||||
}
|
||||
instance.options.data.dialogOptions.settingsTrayActiveEditableId = $(instance.element).parents('.settings-tray-editable').attr('id');
|
||||
instance.progress = { type: 'fullscreen' };
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('drupalContextualLinkAdded', function (event, data) {
|
||||
prepareAjaxLinks();
|
||||
|
||||
$('body').once('settings_tray.edit_mode_init').each(function () {
|
||||
var editMode = localStorage.getItem('Drupal.contextualToolbar.isViewing') === 'false';
|
||||
if (editMode) {
|
||||
setEditModeState(true);
|
||||
}
|
||||
});
|
||||
|
||||
data.$el.find(blockConfigureSelector).on('click.settingstray', function () {
|
||||
if (!isInEditMode()) {
|
||||
$(toggleEditSelector).trigger('click').trigger('click.settings_tray');
|
||||
}
|
||||
|
||||
disableQuickEdit();
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('keyup.settingstray', function (e) {
|
||||
if (isInEditMode() && e.keyCode === 27) {
|
||||
Drupal.announce(Drupal.t('Exited edit mode.'));
|
||||
toggleEditMode();
|
||||
}
|
||||
});
|
||||
|
||||
Drupal.behaviors.toggleEditMode = {
|
||||
attach: function attach() {
|
||||
$(toggleEditSelector).once('settingstray').on('click.settingstray', toggleEditMode);
|
||||
}
|
||||
};
|
||||
|
||||
$(window).on({
|
||||
'dialog:beforecreate': function dialogBeforecreate(event, dialog, $element, settings) {
|
||||
if ($element.is('#drupal-off-canvas')) {
|
||||
$('body .settings-tray-active-editable').removeClass('settings-tray-active-editable');
|
||||
var $activeElement = $('#' + settings.settingsTrayActiveEditableId);
|
||||
if ($activeElement.length) {
|
||||
$activeElement.addClass('settings-tray-active-editable');
|
||||
}
|
||||
}
|
||||
},
|
||||
'dialog:beforeclose': function dialogBeforeclose(event, dialog, $element) {
|
||||
if ($element.is('#drupal-off-canvas')) {
|
||||
$('body .settings-tray-active-editable').removeClass('settings-tray-active-editable');
|
||||
}
|
||||
}
|
||||
});
|
||||
})(jQuery, Drupal);
|
80
web/core/modules/settings_tray/settings_tray.api.php
Normal file
80
web/core/modules/settings_tray/settings_tray.api.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Documentation for Settings Tray API.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup settings_tray Settings Tray API
|
||||
* @{
|
||||
* Settings Tray API
|
||||
*
|
||||
* @section sec_overview Overview and terminology
|
||||
*
|
||||
* The Settings Tray module allows blocks to be configured in a sidebar form
|
||||
* without leaving the page. For example:
|
||||
*
|
||||
* - For every block, one can configure whether to display the block title or
|
||||
* not, and optionally override it (block configuration).
|
||||
* - For menu blocks, one can configure which menu levels to display (block
|
||||
* configuration) but also the menu itself (menu configuration).
|
||||
* - For the site branding block, one can change which branding elements to
|
||||
* display (block configuration), but also the site name and slogan (simple
|
||||
* configuration).
|
||||
*
|
||||
* Block visibility conditions are not included the sidebar form.
|
||||
*
|
||||
* @section sec_api The API: the form in the Settings Tray
|
||||
*
|
||||
* By default, the Settings Tray shows any block's built-in form in the
|
||||
* off-canvas dialog.
|
||||
*
|
||||
* @see core/misc/dialog/off-canvas.es6.js
|
||||
*
|
||||
* However, many blocks would benefit from a tailored form which either:
|
||||
* - limits the form items displayed in the Settings Tray to only items that
|
||||
* affect the content of the rendered block
|
||||
* - adds additional form items to edit configuration that is rendered by the
|
||||
* block. See \Drupal\settings_tray\Form\SystemBrandingOffCanvasForm which
|
||||
* adds site name and slogan configuration.
|
||||
*
|
||||
* These can be used to provide a better experience, so that the Settings Tray
|
||||
* only displays what the user will expect to change when editing the block.
|
||||
*
|
||||
* Each block plugin can specify which form to use in the Settings Tray dialog
|
||||
* in its plugin annotation:
|
||||
* @code
|
||||
* forms = {
|
||||
* "settings_tray" = "\Drupal\some_module\Form\MyBlockOffCanvasForm",
|
||||
* },
|
||||
* @endcode
|
||||
*
|
||||
* In some cases, a block's content is not configurable (for example, the title,
|
||||
* main content, and help blocks). Such blocks can opt out of providing a
|
||||
* settings_tray form:
|
||||
* @code
|
||||
* forms = {
|
||||
* "settings_tray" = FALSE,
|
||||
* },
|
||||
* @endcode
|
||||
*
|
||||
* Finally, blocks that do not specify a settings_tray form using the annotation
|
||||
* above will automatically have it set to their plugin class. For example, the
|
||||
* "Powered by Drupal" block plugin
|
||||
* (\Drupal\system\Plugin\Block\SystemPoweredByBlock) automatically gets this
|
||||
* added to its annotation:
|
||||
* @code
|
||||
* forms = {
|
||||
* "settings_tray" = "\Drupal\system\Plugin\Block\SystemPoweredByBlock",
|
||||
* },
|
||||
* @endcode
|
||||
*
|
||||
* Therefore, the entire Settings Tray API is just this annotation: it controls
|
||||
* what the Settings Tray does for a given block.
|
||||
*
|
||||
* @see settings_tray_block_alter()
|
||||
* @see \Drupal\Tests\settings_tray\Functional\SettingsTrayBlockTest::testPossibleAnnotations()
|
||||
*
|
||||
* @}
|
||||
*/
|
10
web/core/modules/settings_tray/settings_tray.info.yml
Normal file
10
web/core/modules/settings_tray/settings_tray.info.yml
Normal file
|
@ -0,0 +1,10 @@
|
|||
name: 'Settings Tray'
|
||||
type: module
|
||||
description: 'Provides a sidebar to configure blocks on the page.'
|
||||
package: Core
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:block
|
||||
- drupal:toolbar
|
||||
- drupal:contextual
|
24
web/core/modules/settings_tray/settings_tray.install
Normal file
24
web/core/modules/settings_tray/settings_tray.install
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the Settings Tray module.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function settings_tray_install() {
|
||||
// This module affects the rendering of blocks and of the page.
|
||||
// @todo Remove in https://www.drupal.org/node/2783791.
|
||||
Cache::invalidateTags(['rendered']);
|
||||
|
||||
// \Drupal\Core\Menu\ContextualLinkManager caches per-group definitions
|
||||
// without associating the cache tag that would allow them to be cleared
|
||||
// by its clearCachedDefinitions() implementation that is automatically
|
||||
// invoked when modules are installed.
|
||||
// @todo Remove when that is fixed in https://www.drupal.org/node/2773591.
|
||||
\Drupal::service('cache.discovery')->deleteAll();
|
||||
}
|
19
web/core/modules/settings_tray/settings_tray.libraries.yml
Normal file
19
web/core/modules/settings_tray/settings_tray.libraries.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
drupal.settings_tray:
|
||||
version: VERSION
|
||||
js:
|
||||
js/settings_tray.js: {}
|
||||
css:
|
||||
component:
|
||||
css/settings_tray.module.css: {}
|
||||
css/settings_tray.motion.css: {}
|
||||
css/settings_tray.toolbar.css: {}
|
||||
theme:
|
||||
# @todo Set the group higher than CSS_AGGREGATE_THEME so that it overrides
|
||||
# both jQuery UI and Classy's dialog.css, remove in
|
||||
# https://www.drupal.org/node/1945262.
|
||||
css/settings_tray.theme.css: { group: 200 }
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/jquery.once
|
||||
- core/drupal.ajax
|
|
@ -0,0 +1,10 @@
|
|||
settings_tray.block_configure:
|
||||
title: 'Quick edit'
|
||||
route_name: 'entity.block.settings_tray_form'
|
||||
group: 'block'
|
||||
options:
|
||||
attributes:
|
||||
class: ['use-ajax']
|
||||
data-dialog-type: dialog
|
||||
data-dialog-renderer: off_canvas
|
||||
data-settings-tray-edit: true
|
187
web/core/modules/settings_tray/settings_tray.module
Normal file
187
web/core/modules/settings_tray/settings_tray.module
Normal file
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Allows configuring blocks and other configuration from the site front-end.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Asset\AttachedAssetsInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\block\entity\Block;
|
||||
use Drupal\block\BlockInterface;
|
||||
use Drupal\settings_tray\Block\BlockEntitySettingTrayForm;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function settings_tray_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.settings_tray':
|
||||
$output = '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Settings Tray module allows users with the <a href=":administer_block_permission">Administer blocks</a> and <a href=":contextual_permission">Use contextual links</a> permissions to edit blocks without visiting a separate page. For more information, see the <a href=":handbook_url">online documentation for the Settings Tray module</a>.', [':handbook_url' => 'https://www.drupal.org/documentation/modules/settings_tray', ':administer_block_permission' => \Drupal::url('user.admin_permissions', [], ['fragment' => 'module-block']), ':contextual_permission' => \Drupal::url('user.admin_permissions', [], ['fragment' => 'module-contextual'])]) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Editing blocks in place') . '</dt>';
|
||||
$output .= '<dd>';
|
||||
$output .= '<p>' . t('To edit blocks in place, either click the <strong>Edit</strong> button in the toolbar and then click on the block, or choose "Quick edit" from the block\'s contextual link. (See the <a href=":contextual">Contextual Links module help</a> for more information about how to use contextual links.)', [':contextual' => \Drupal::url('help.page', ['name' => 'contextual'])]) . '</p>';
|
||||
$output .= '<p>' . t('The Settings Tray for the block will open in a sidebar, with a compact form for configuring what the block shows.') . '</p>';
|
||||
$output .= '<p>' . t('Save the form and the changes will be immediately visible on the page.') . '</p>';
|
||||
$output .= '</dd>';
|
||||
$output .= '</dl>';
|
||||
return ['#markup' => $output];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_contextual_links_view_alter().
|
||||
*
|
||||
* Change Configure Blocks into off_canvas links.
|
||||
*/
|
||||
function settings_tray_contextual_links_view_alter(&$element, $items) {
|
||||
if (isset($element['#links']['settings-trayblock-configure'])) {
|
||||
// Place settings_tray link first.
|
||||
$settings_tray_link = $element['#links']['settings-trayblock-configure'];
|
||||
unset($element['#links']['settings-trayblock-configure']);
|
||||
$element['#links'] = ['settings-trayblock-configure' => $settings_tray_link] + $element['#links'];
|
||||
|
||||
// If this is content block change title to avoid duplicate "Quick Edit".
|
||||
if (isset($element['#links']['block-contentblock-edit'])) {
|
||||
$element['#links']['settings-trayblock-configure']['title'] = t('Quick edit settings');
|
||||
}
|
||||
|
||||
$element['#attached']['library'][] = 'core/drupal.dialog.off_canvas';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block has overrides.
|
||||
*
|
||||
* @param \Drupal\block\BlockInterface $block
|
||||
* The block to check for overrides.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the block has overrides otherwise FALSE.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function _settings_tray_has_block_overrides(BlockInterface $block) {
|
||||
// @todo Replace the following with $block->hasOverrides() in https://www.drupal.org/project/drupal/issues/2910353
|
||||
// and remove this function.
|
||||
return \Drupal::config($block->getEntityType()->getConfigPrefix() . '.' . $block->id())->hasOverrides();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_view_alter().
|
||||
*/
|
||||
function settings_tray_block_view_alter(array &$build) {
|
||||
if (isset($build['#contextual_links']['block'])) {
|
||||
// Ensure that contextual links vary by whether the block has config overrides
|
||||
// or not.
|
||||
// @see _contextual_links_to_id()
|
||||
$build['#contextual_links']['block']['metadata']['has_overrides'] = _settings_tray_has_block_overrides($build['#block']) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Force a new 'data-contextual-id' attribute on blocks when this module is
|
||||
// enabled so as not to reuse stale data cached client-side.
|
||||
// @todo Remove when https://www.drupal.org/node/2773591 is fixed.
|
||||
$build['#contextual_links']['settings_tray'] = [
|
||||
'route_parameters' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_build().
|
||||
*/
|
||||
function settings_tray_entity_type_build(array &$entity_types) {
|
||||
/* @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
|
||||
$entity_types['block']
|
||||
->setFormClass('settings_tray', BlockEntitySettingTrayForm::class)
|
||||
->setLinkTemplate('settings_tray-form', '/admin/structure/block/manage/{block}/settings-tray');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK() for block templates.
|
||||
*/
|
||||
function settings_tray_preprocess_block(&$variables) {
|
||||
// Only blocks that have a settings_tray form and have no configuration
|
||||
// overrides will have a "Quick Edit" link. We could wait for the contextual
|
||||
// links to be initialized on the client side, and then add the class and
|
||||
// data- attribute below there (via JavaScript). But that would mean that it
|
||||
// would be impossible to show Settings Tray's clickable regions immediately
|
||||
// when the page loads. When latency is high, this will cause flicker.
|
||||
// @see \Drupal\settings_tray\Access\BlockPluginHasSettingsTrayFormAccessCheck
|
||||
/** @var \Drupal\settings_tray\Access\BlockPluginHasSettingsTrayFormAccessCheck $access_checker */
|
||||
$access_checker = \Drupal::service('access_check.settings_tray.block.settings_tray_form');
|
||||
/** @var \Drupal\Core\Block\BlockManagerInterface $block_plugin_manager */
|
||||
$block_plugin_manager = \Drupal::service('plugin.manager.block');
|
||||
/** @var \Drupal\Core\Block\BlockPluginInterface $block_plugin */
|
||||
$block_plugin = $block_plugin_manager->createInstance($variables['plugin_id']);
|
||||
if (isset($variables['elements']['#contextual_links']['block']['route_parameters']['block'])) {
|
||||
$block = Block::load($variables['elements']['#contextual_links']['block']['route_parameters']['block']);
|
||||
if ($access_checker->accessBlockPlugin($block_plugin)->isAllowed() && !_settings_tray_has_block_overrides($block)) {
|
||||
// Add class and attributes to all blocks to allow Javascript to target.
|
||||
$variables['attributes']['class'][] = 'settings-tray-editable';
|
||||
$variables['attributes']['data-drupal-settingstray'] = 'editable';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_toolbar_alter().
|
||||
*
|
||||
* Alters the 'contextual' toolbar tab if it exists (meaning the user is allowed
|
||||
* to use contextual links) and if they can administer blocks.
|
||||
*
|
||||
* @todo Remove the "administer blocks" requirement in
|
||||
* https://www.drupal.org/node/2822965.
|
||||
*
|
||||
* @see contextual_toolbar()
|
||||
*/
|
||||
function settings_tray_toolbar_alter(&$items) {
|
||||
$items['contextual']['#cache']['contexts'][] = 'user.permissions';
|
||||
if (isset($items['contextual']['tab']) && \Drupal::currentUser()->hasPermission('administer blocks')) {
|
||||
$items['contextual']['#weight'] = -1000;
|
||||
$items['contextual']['#attached']['library'][] = 'settings_tray/drupal.settings_tray';
|
||||
$items['contextual']['tab']['#attributes']['data-drupal-settingstray'] = 'toggle';
|
||||
|
||||
// Set a class on items to mark whether they should be active in edit mode.
|
||||
// @todo Create a dynamic method for modules to set their own items.
|
||||
// https://www.drupal.org/node/2784589.
|
||||
$edit_mode_items = ['contextual', 'block_place'];
|
||||
foreach ($items as $key => $item) {
|
||||
if (!in_array($key, $edit_mode_items) && (!isset($items[$key]['#wrapper_attributes']['class']) || !in_array('hidden', $items[$key]['#wrapper_attributes']['class']))) {
|
||||
$items[$key]['#wrapper_attributes']['class'][] = 'edit-mode-inactive';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_block_alter().
|
||||
*
|
||||
* Ensures every block plugin definition has an 'settings_tray' form specified.
|
||||
*
|
||||
* @see \Drupal\settings_tray\Access\BlockPluginHasSettingsTrayFormAccessCheck
|
||||
*/
|
||||
function settings_tray_block_alter(&$definitions) {
|
||||
foreach ($definitions as &$definition) {
|
||||
// If a block plugin does not define its own 'settings_tray' form, use the
|
||||
// plugin class itself.
|
||||
if (!isset($definition['forms']['settings_tray'])) {
|
||||
$definition['forms']['settings_tray'] = $definition['class'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_css_alter().
|
||||
*/
|
||||
function settings_tray_css_alter(&$css, AttachedAssetsInterface $assets) {
|
||||
// @todo Remove once conditional ordering is introduced in
|
||||
// https://www.drupal.org/node/1945262.
|
||||
$path = drupal_get_path('module', 'settings_tray') . '/css/settings_tray.theme.css';
|
||||
if (isset($css[$path])) {
|
||||
// Use 200 to come after CSS_AGGREGATE_THEME.
|
||||
$css[$path]['group'] = 200;
|
||||
}
|
||||
}
|
16
web/core/modules/settings_tray/settings_tray.routing.yml
Normal file
16
web/core/modules/settings_tray/settings_tray.routing.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
entity.block.settings_tray_form:
|
||||
path: '/admin/structure/block/manage/{block}/settings-tray'
|
||||
defaults:
|
||||
_entity_form: 'block.settings_tray'
|
||||
_title_callback: '\Drupal\settings_tray\Block\BlockEntitySettingTrayForm::title'
|
||||
requirements:
|
||||
_permission: 'administer blocks'
|
||||
_access_block_plugin_has_settings_tray_form: 'TRUE'
|
||||
_access_block_has_overrides_settings_tray_form: 'TRUE'
|
||||
|
||||
# Deprecated.
|
||||
# @see entity.block.settings_tray_form
|
||||
# @see \Drupal\settings_tray\RouteProcessor\BlockEntityOffCanvasFormRouteProcessorBC
|
||||
# @todo Remove in Drupal 9.0.0.
|
||||
entity.block.off_canvas_form:
|
||||
path: ''
|
18
web/core/modules/settings_tray/settings_tray.services.yml
Normal file
18
web/core/modules/settings_tray/settings_tray.services.yml
Normal file
|
@ -0,0 +1,18 @@
|
|||
services:
|
||||
access_check.settings_tray.block.has_overrides:
|
||||
class: Drupal\settings_tray\Access\BlockHasOverridesAccessCheck
|
||||
tags:
|
||||
- { name: access_check, applies_to: _access_block_has_overrides_settings_tray_form }
|
||||
access_check.settings_tray.block.settings_tray_form:
|
||||
class: Drupal\settings_tray\Access\BlockPluginHasSettingsTrayFormAccessCheck
|
||||
tags:
|
||||
- { name: access_check, applies_to: _access_block_plugin_has_settings_tray_form }
|
||||
|
||||
# BC layers.
|
||||
# @todo Remove in Drupal 9.0.0.
|
||||
settings_tray.route_processor_off_canvas_form_bc:
|
||||
class: \Drupal\settings_tray\RouteProcessor\BlockEntityOffCanvasFormRouteProcessorBC
|
||||
arguments: ['@router.route_provider']
|
||||
public: false
|
||||
tags:
|
||||
- { name: route_processor_outbound }
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray\Access;
|
||||
|
||||
use Drupal\block\BlockInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
|
||||
/**
|
||||
* Determines whether the requested block has a 'settings_tray' form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BlockHasOverridesAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* Checks access for accessing a block's 'settings_tray' form.
|
||||
*
|
||||
* @param \Drupal\block\BlockInterface $block
|
||||
* The block whose 'settings_tray' form is being accessed.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access(BlockInterface $block) {
|
||||
return AccessResult::allowedIf(!_settings_tray_has_block_overrides($block));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray\Access;
|
||||
|
||||
use Drupal\block\BlockInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Plugin\PluginWithFormsInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
|
||||
/**
|
||||
* Determines whether the requested block has a 'settings_tray' form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BlockPluginHasSettingsTrayFormAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* Checks access for accessing a block's 'settings_tray' form.
|
||||
*
|
||||
* @param \Drupal\block\BlockInterface $block
|
||||
* The block whose 'settings_tray' form is being accessed.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access(BlockInterface $block) {
|
||||
/** @var \Drupal\Core\Block\BlockPluginInterface $block_plugin */
|
||||
$block_plugin = $block->getPlugin();
|
||||
return $this->accessBlockPlugin($block_plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks access for accessing a block plugin's 'settings_tray' form.
|
||||
*
|
||||
* @param \Drupal\Core\Block\BlockPluginInterface $block_plugin
|
||||
* The block plugin whose 'settings_tray' form is being accessed.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*
|
||||
* @see settings_tray_preprocess_block()
|
||||
*/
|
||||
public function accessBlockPlugin(BlockPluginInterface $block_plugin) {
|
||||
return AccessResult::allowedIf($block_plugin instanceof PluginWithFormsInterface && $block_plugin->hasFormClass('settings_tray'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray\Block;
|
||||
|
||||
use Drupal\block\BlockForm;
|
||||
use Drupal\block\BlockInterface;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Ajax\AjaxFormHelperTrait;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\RedirectCommand;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\PluginWithFormsInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides form for block instance forms when used in the off-canvas dialog.
|
||||
*
|
||||
* This form removes advanced sections of regular block form such as the
|
||||
* visibility settings, machine ID and region.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BlockEntitySettingTrayForm extends BlockForm {
|
||||
|
||||
use AjaxFormHelperTrait;
|
||||
|
||||
/**
|
||||
* Provides a title callback to get the block's admin label.
|
||||
*
|
||||
* @param \Drupal\block\BlockInterface $block
|
||||
* The block entity.
|
||||
*
|
||||
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* The title.
|
||||
*/
|
||||
public function title(BlockInterface $block) {
|
||||
// @todo Wrap "Configure " in <span class="visually-hidden"></span> once
|
||||
// https://www.drupal.org/node/2359901 is fixed.
|
||||
return $this->t('Configure @block', ['@block' => $block->getPlugin()->getPluginDefinition()['admin_label']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function form(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::form($form, $form_state);
|
||||
|
||||
// Create link to full block form.
|
||||
$query = [];
|
||||
if ($destination = $this->getRequest()->query->get('destination')) {
|
||||
$query['destination'] = $destination;
|
||||
}
|
||||
$form['advanced_link'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Advanced block options'),
|
||||
'#url' => $this->entity->toUrl('edit-form', ['query' => $query]),
|
||||
'#weight' => 1000,
|
||||
];
|
||||
|
||||
// Remove the ID and region elements.
|
||||
unset($form['id'], $form['region'], $form['settings']['admin_label']);
|
||||
|
||||
if (isset($form['settings']['label_display']) && isset($form['settings']['label'])) {
|
||||
// Only show the label input if the label will be shown on the page.
|
||||
$form['settings']['label_display']['#weight'] = -100;
|
||||
$form['settings']['label']['#states']['visible'] = [
|
||||
':input[name="settings[label_display]"]' => ['checked' => TRUE],
|
||||
];
|
||||
|
||||
// Relabel to "Block title" because on the front-end this may be confused
|
||||
// with page title.
|
||||
$form['settings']['label']['#title'] = $this->t("Block title");
|
||||
$form['settings']['label_display']['#title'] = $this->t("Display block title");
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Save @block', ['@block' => $this->entity->getPlugin()->getPluginDefinition()['admin_label']]);
|
||||
$actions['delete']['#access'] = FALSE;
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildVisibilityInterface(array $form, FormStateInterface $form_state) {
|
||||
// Do not display the visibility.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function validateVisibility(array $form, FormStateInterface $form_state) {
|
||||
// Intentionally empty.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function submitVisibility(array $form, FormStateInterface $form_state) {
|
||||
// Intentionally empty.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getPluginForm(BlockPluginInterface $block) {
|
||||
if ($block instanceof PluginWithFormsInterface) {
|
||||
return $this->pluginFormFactory->createInstance($block, 'settings_tray', 'configure');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
$form['actions']['submit']['#ajax'] = [
|
||||
'callback' => '::ajaxSubmit',
|
||||
];
|
||||
$form['#attached']['library'][] = 'core/drupal.dialog.ajax';
|
||||
|
||||
// static::ajaxSubmit() requires data-drupal-selector to be the same between
|
||||
// the various Ajax requests. A bug in \Drupal\Core\Form\FormBuilder
|
||||
// prevents that from happening unless $form['#id'] is also the same.
|
||||
// Normally, #id is set to a unique HTML ID via Html::getUniqueId(), but
|
||||
// here we bypass that in order to work around the data-drupal-selector bug.
|
||||
// This is okay so long as we assume that this form only ever occurs once on
|
||||
// a page.
|
||||
// @todo Remove this workaround once https://www.drupal.org/node/2897377 is
|
||||
// fixed.
|
||||
$form['#id'] = Html::getId($form_state->getBuildInfo()['form_id']);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function successfulAjaxSubmit(array $form, FormStateInterface $form_state) {
|
||||
if ($redirect_url = $this->getRedirectUrl()) {
|
||||
$command = new RedirectCommand($redirect_url->setAbsolute()->toString());
|
||||
}
|
||||
else {
|
||||
// Settings Tray always provides a destination.
|
||||
throw new \Exception("No destination provided by Settings Tray form");
|
||||
}
|
||||
$response = new AjaxResponse();
|
||||
return $response->addCommand($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form's redirect URL from 'destination' provide in the request.
|
||||
*
|
||||
* @return \Drupal\Core\Url|null
|
||||
* The redirect URL or NULL if dialog should just be closed.
|
||||
*/
|
||||
protected function getRedirectUrl() {
|
||||
// \Drupal\Core\Routing\RedirectDestination::get() cannot be used directly
|
||||
// because it will use <current> if 'destination' is not in the query
|
||||
// string.
|
||||
if ($this->getRequest()->query->has('destination') && $destination = $this->getRedirectDestination()->get()) {
|
||||
return Url::fromUserInput('/' . $destination);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray\RouteProcessor;
|
||||
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Processes the Block entity off-canvas form BC route.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BlockEntityOffCanvasFormRouteProcessorBC implements OutboundRouteProcessorInterface {
|
||||
|
||||
/**
|
||||
* The route provider.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteProviderInterface
|
||||
*/
|
||||
protected $routeProvider;
|
||||
|
||||
/**
|
||||
* Constructs a BlockEntityOffCanvasFormRouteProcessorBC object.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
|
||||
* The route provider.
|
||||
*/
|
||||
public function __construct(RouteProviderInterface $route_provider) {
|
||||
$this->routeProvider = $route_provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function processOutbound($route_name, Route $route, array &$parameters, BubbleableMetadata $bubbleable_metadata = NULL) {
|
||||
if ($route_name === 'entity.block.off_canvas_form') {
|
||||
$redirected_route_name = 'entity.block.settings_tray_form';
|
||||
@trigger_error(sprintf("The '%s' route is deprecated since version 8.5.x and will be removed in 9.0.0. Use the '%s' route instead.", $route_name, $redirected_route_name), E_USER_DEPRECATED);
|
||||
static::overwriteRoute($route, $this->routeProvider->getRouteByName($redirected_route_name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites one route's metadata with the other's.
|
||||
*
|
||||
* @param \Symfony\Component\Routing\Route $target_route
|
||||
* The route whose metadata to overwrite.
|
||||
* @param \Symfony\Component\Routing\Route $source_route
|
||||
* The route whose metadata to read from.
|
||||
*
|
||||
* @see \Symfony\Component\Routing\Route
|
||||
*/
|
||||
protected static function overwriteRoute(Route $target_route, Route $source_route) {
|
||||
$target_route->setPath($source_route->getPath());
|
||||
$target_route->setDefaults($source_route->getDefaults());
|
||||
$target_route->setRequirements($source_route->getRequirements());
|
||||
$target_route->setOptions($source_route->getOptions());
|
||||
$target_route->setHost($source_route->getHost());
|
||||
$target_route->setSchemes($source_route->getSchemes());
|
||||
$target_route->setMethods($source_route->getMethods());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
name: 'Configuration override test for Settings Tray'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:settings_tray
|
|
@ -0,0 +1,5 @@
|
|||
services:
|
||||
settings_tray_override_test.overrider:
|
||||
class: Drupal\settings_tray_override_test\ConfigOverrider
|
||||
tags:
|
||||
- { name: config.factory.override }
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray_override_test;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
|
||||
/**
|
||||
* Provides an overridden block for Settings Tray testing.
|
||||
*
|
||||
* @see \Drupal\Tests\settings_tray\FunctionalJavascript\SettingsTrayBlockFormTest::testOverriddenDisabled()
|
||||
*/
|
||||
class ConfigOverrider implements ConfigFactoryOverrideInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadOverrides($names) {
|
||||
$overrides = [];
|
||||
if (in_array('block.block.overridden_block', $names)) {
|
||||
if (\Drupal::state()->get('settings_tray_override_test.block')) {
|
||||
$overrides = $overrides + ['block.block.overridden_block' => ['settings' => ['label' => 'Now this will be the label.']]];
|
||||
}
|
||||
}
|
||||
if (in_array('system.site', $names)) {
|
||||
if (\Drupal::state()->get('settings_tray_override_test.site_name')) {
|
||||
$overrides = $overrides + ['system.site' => ['name' => 'Llama Fan Club']];
|
||||
}
|
||||
}
|
||||
if (in_array('system.menu.main', $names)) {
|
||||
if (\Drupal::state()->get('settings_tray_override_test.menu')) {
|
||||
$overrides = $overrides + ['system.menu.main' => ['label' => 'Labely label']];
|
||||
}
|
||||
}
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheSuffix() {
|
||||
return 'ConfigOverrider';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($name) {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
name: 'Settings Tray Test'
|
||||
type: module
|
||||
description: 'Provides Settings Tray test functionality.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:block
|
||||
- drupal:settings_tray
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray_test\Form;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\PluginFormBase;
|
||||
|
||||
/**
|
||||
* @see \Drupal\settings_tray_test\Plugin\Block\SettingsTrayFormAnnotationIsClassBlock
|
||||
*/
|
||||
class SettingsTrayFormAnnotationIsClassBlockForm extends PluginFormBase {
|
||||
|
||||
/**
|
||||
* The block plugin.
|
||||
*
|
||||
* @var \Drupal\Core\Block\BlockPluginInterface
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = $this->plugin->buildConfigurationForm($form, $form_state);
|
||||
|
||||
$form['some_setting'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => t('Some setting'),
|
||||
'#options' => [
|
||||
'a' => 'A',
|
||||
'b' => 'B',
|
||||
],
|
||||
'#required' => TRUE,
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
|
||||
/**
|
||||
* Block that explicitly provides a "settings_tray" form class.
|
||||
*
|
||||
* @Block(
|
||||
* id = "settings_tray_test_class",
|
||||
* admin_label = "Settings Tray test block: forms[settings_tray]=class",
|
||||
* forms = {
|
||||
* "settings_tray" = "\Drupal\settings_tray_test\Form\SettingsTrayFormAnnotationIsClassBlockForm",
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class SettingsTrayFormAnnotationIsClassBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return ['#markup' => '<span>class</span>'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
|
||||
/**
|
||||
* Block that explicitly provides no "settings_tray" form, thus opting out.
|
||||
*
|
||||
* @Block(
|
||||
* id = "settings_tray_test_false",
|
||||
* admin_label = "Settings Tray test block: forms[settings_tray]=FALSE",
|
||||
* forms = {
|
||||
* "settings_tray" = FALSE,
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class SettingsTrayFormAnnotationIsFalseBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return ['#markup' => '<span>FALSE</span>'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
|
||||
/**
|
||||
* Block that does nothing explicit for Settings Tray.
|
||||
*
|
||||
* @Block(
|
||||
* id = "settings_tray_test_none",
|
||||
* admin_label = "Settings Tray test block: forms[settings_tray] is not specified",
|
||||
* )
|
||||
*/
|
||||
class SettingsTrayFormAnnotationNoneBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return ['#markup' => '<span>none</span>'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\settings_tray_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a 'Block with validation error' test block.
|
||||
*
|
||||
* @Block(
|
||||
* id = "settings_tray_test_validation",
|
||||
* admin_label = @Translation("Block with validation error")
|
||||
* )
|
||||
*/
|
||||
class ValidationErrorBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return ['#markup' => '<span>If I had more time this would be very witty :(.</span>'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateConfigurationForm($form, $form_state);
|
||||
$form_state->setError($form['label'], 'Sorry system error. Please save again.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode a,
|
||||
.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode input {
|
||||
pointer-events: inherit !important;
|
||||
}
|
||||
/**
|
||||
* Remove all transitions for testing.
|
||||
*/
|
||||
* {
|
||||
/* CSS transitions. */
|
||||
-o-transition-property: none !important;
|
||||
-moz-transition-property: none !important;
|
||||
-ms-transition-property: none !important;
|
||||
-webkit-transition-property: none !important;
|
||||
transition-property: none !important;
|
||||
/* CSS transforms. */
|
||||
-o-transform: none !important;
|
||||
-moz-transform: none !important;
|
||||
-ms-transform: none !important;
|
||||
-webkit-transform: none !important;
|
||||
transform: none !important;
|
||||
/* CSS animations. */
|
||||
-webkit-animation: none !important;
|
||||
-moz-animation: none !important;
|
||||
-o-animation: none !important;
|
||||
-ms-animation: none !important;
|
||||
animation: none !important;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
name: 'CSS Test fix'
|
||||
type: module
|
||||
description: 'Provides CSS fixes for tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:settings_tray
|
|
@ -0,0 +1,5 @@
|
|||
drupal.css_fix:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/css_fix.theme.css: {}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Module for attaching CSS during tests.
|
||||
*
|
||||
* CSS pointer-events properties cause testing errors.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_page_attachments().
|
||||
*/
|
||||
function settings_tray_test_css_page_attachments(array &$attachments) {
|
||||
// Unconditionally attach an asset to the page.
|
||||
$attachments['#attached']['library'][] = 'settings_tray_test_css/drupal.css_fix';
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\settings_tray\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests Settings Tray BC routes.
|
||||
*
|
||||
* @group settings_tray
|
||||
* @group legacy
|
||||
*/
|
||||
class BcRoutesTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'settings_tray',
|
||||
];
|
||||
|
||||
/**
|
||||
* @expectedDeprecation The 'entity.block.off_canvas_form' route is deprecated since version 8.5.x and will be removed in 9.0.0. Use the 'entity.block.settings_tray_form' route instead.
|
||||
*/
|
||||
public function testOffCanvasFormRouteBc() {
|
||||
$block = $this->placeBlock('system_powered_by_block');
|
||||
$url_for_current_route = Url::fromRoute('entity.block.settings_tray_form', ['block' => $block->id()])->toString(TRUE)->getGeneratedUrl();
|
||||
$url_for_bc_route = Url::fromRoute('entity.block.off_canvas_form', ['block' => $block->id()])->toString(TRUE)->getGeneratedUrl();
|
||||
$this->assertSame($url_for_current_route, $url_for_bc_route);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\settings_tray\Functional;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests opening and saving block forms in the off-canvas dialog.
|
||||
*
|
||||
* @group settings_tray
|
||||
*/
|
||||
class SettingsTrayTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'settings_tray',
|
||||
'settings_tray_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* Gets the block CSS selector.
|
||||
*
|
||||
* @param \Drupal\block\Entity\Block $block
|
||||
* The block.
|
||||
*
|
||||
* @return string
|
||||
* The CSS selector.
|
||||
*/
|
||||
protected function getBlockSelector(Block $block) {
|
||||
return '#block-' . $block->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the 3 possible forms[settings_tray] annotations: class, FALSE, none.
|
||||
*
|
||||
* There is also functional JS test coverage to ensure that the two blocks
|
||||
* that support Settings Tray (the "class" and "none" cases) do work
|
||||
* correctly.
|
||||
*
|
||||
* @see SettingsTrayBlockFormTest::testBlocks()
|
||||
*/
|
||||
public function testPossibleAnnotations() {
|
||||
$test_block_plugin_ids = [
|
||||
// Block that explicitly provides an "settings_tray" form class.
|
||||
'settings_tray_test_class',
|
||||
// Block that explicitly provides no "settings_tray" form, thus opting out.
|
||||
'settings_tray_test_false',
|
||||
// Block that does nothing explicit for Settings Tray.
|
||||
'settings_tray_test_none',
|
||||
];
|
||||
|
||||
$placed_blocks = [];
|
||||
foreach ($test_block_plugin_ids as $plugin_id) {
|
||||
$placed_blocks[$plugin_id] = $this->placeBlock($plugin_id);
|
||||
}
|
||||
|
||||
$this->drupalGet('');
|
||||
$web_assert = $this->assertSession();
|
||||
foreach ($placed_blocks as $plugin_id => $placed_block) {
|
||||
$block_selector = $this->getBlockSelector($placed_block);
|
||||
|
||||
// All blocks are rendered.
|
||||
$web_assert->elementExists('css', $block_selector);
|
||||
|
||||
// All blocks except 'settings_tray_test_false' are editable. For more
|
||||
// detailed test coverage, which requires JS execution, see
|
||||
// \Drupal\Tests\settings_tray\FunctionalJavascript\SettingsTrayBlockFormTest::testBlocks().
|
||||
if ($plugin_id === 'settings_tray_test_false') {
|
||||
$web_assert->elementNotExists('css', "{$block_selector}[data-drupal-settingstray=\"editable\"]");
|
||||
}
|
||||
else {
|
||||
$web_assert->elementExists('css', "{$block_selector}[data-drupal-settingstray=\"editable\"]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that certain blocks opt out from Settings Tray.
|
||||
*/
|
||||
public function testOptOut() {
|
||||
$web_assert = $this->assertSession();
|
||||
|
||||
$non_excluded_block = $this->placeBlock('system_powered_by_block');
|
||||
$excluded_block_plugin_ids = ['page_title_block', 'system_main_block', 'settings_tray_test_false'];
|
||||
$block_selectors = [];
|
||||
// Place blocks that should be excluded.
|
||||
foreach ($excluded_block_plugin_ids as $excluded_block_plugin_id) {
|
||||
// The block HTML 'id' attribute will be "block-[block_id]".
|
||||
$block_selectors[] = $this->getBlockSelector($this->placeBlock($excluded_block_plugin_id));
|
||||
}
|
||||
$this->drupalGet('');
|
||||
// Assert that block has been marked as "editable" and contextual that
|
||||
// should exist does.
|
||||
$web_assert->elementExists('css', $this->getBlockSelector($non_excluded_block) . "[data-drupal-settingstray=\"editable\"]");
|
||||
// Assert that each block that has a "forms[settings_tray] = FALSE" annotation:
|
||||
// - is still rendered on the page
|
||||
// - but is not marked as "editable" by settings_tray_preprocess_block()
|
||||
// - and does not have the Settings Tray contextual link.
|
||||
foreach ($block_selectors as $block_selector) {
|
||||
$web_assert->elementExists('css', $block_selector);
|
||||
$web_assert->elementNotExists('css', "{$block_selector}[data-drupal-settingstray=\"editable\"]");
|
||||
$web_assert->elementNotExists('css', "$block_selector [data-settings-tray-edit]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\settings_tray\FunctionalJavascript;
|
||||
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Tests handling of configuration overrides.
|
||||
*
|
||||
* @group settings_tray
|
||||
*/
|
||||
class ConfigAccessTest extends SettingsTrayTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'menu_link_content',
|
||||
'menu_ui',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$user = $this->createUser([
|
||||
'administer blocks',
|
||||
'access contextual links',
|
||||
'access toolbar',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests access to block forms with related configuration is correct.
|
||||
*/
|
||||
public function testBlockConfigAccess() {
|
||||
$page = $this->getSession()->getPage();
|
||||
$web_assert = $this->assertSession();
|
||||
|
||||
// Confirm that System Branding block does not expose Site Name field
|
||||
// without permission.
|
||||
$block = $this->placeBlock('system_branding_block');
|
||||
$this->drupalGet('user');
|
||||
$this->enableEditMode();
|
||||
$this->openBlockForm($this->getBlockSelector($block));
|
||||
// The site name field should not appear because the user doesn't have
|
||||
// permission.
|
||||
$web_assert->fieldNotExists('settings[site_information][site_name]');
|
||||
$page->pressButton('Save Site branding');
|
||||
$this->waitForOffCanvasToClose();
|
||||
$this->assertElementVisibleAfterWait('css', 'div:contains(The block configuration has been saved)');
|
||||
$web_assert->assertWaitOnAjaxRequest();
|
||||
// Confirm we did not save changes to the configuration.
|
||||
$this->assertEquals('Drupal', \Drupal::configFactory()->getEditable('system.site')->get('name'));
|
||||
|
||||
$this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), ['administer site configuration']);
|
||||
$this->drupalGet('user');
|
||||
$this->openBlockForm($this->getBlockSelector($block));
|
||||
// The site name field should appear because the user does have permission.
|
||||
$web_assert->fieldExists('settings[site_information][site_name]');
|
||||
|
||||
// Confirm that the Menu block does not expose menu configuration without
|
||||
// permission.
|
||||
// Add a link or the menu will not render.
|
||||
$menu_link_content = MenuLinkContent::create([
|
||||
'title' => 'This is on the menu',
|
||||
'menu_name' => 'main',
|
||||
'link' => ['uri' => 'route:<front>'],
|
||||
]);
|
||||
$menu_link_content->save();
|
||||
$this->assertNotEmpty($menu_link_content->isEnabled());
|
||||
$menu_without_overrides = \Drupal::configFactory()->getEditable('system.menu.main')->get();
|
||||
$block = $this->placeBlock('system_menu_block:main');
|
||||
$this->drupalGet('user');
|
||||
$web_assert->pageTextContains('This is on the menu');
|
||||
$this->openBlockForm($this->getBlockSelector($block));
|
||||
// Edit menu form should not appear because the user doesn't have
|
||||
// permission.
|
||||
$web_assert->pageTextNotContains('Edit menu');
|
||||
$page->pressButton('Save Main navigation');
|
||||
$this->assertElementVisibleAfterWait('css', 'div:contains(The block configuration has been saved)');
|
||||
$web_assert->assertWaitOnAjaxRequest();
|
||||
// Confirm we did not save changes to the menu or the menu link.
|
||||
$this->assertEquals($menu_without_overrides, \Drupal::configFactory()->getEditable('system.menu.main')->get());
|
||||
$menu_link_content = MenuLinkContent::load($menu_link_content->id());
|
||||
$this->assertNotEmpty($menu_link_content->isEnabled());
|
||||
// Confirm menu is still on the page.
|
||||
$this->drupalGet('user');
|
||||
$web_assert->pageTextContains('This is on the menu');
|
||||
|
||||
$this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), ['administer menu']);
|
||||
$this->drupalGet('user');
|
||||
$web_assert->pageTextContains('This is on the menu');
|
||||
$this->openBlockForm($this->getBlockSelector($block));
|
||||
// Edit menu form should appear because the user does have permission.
|
||||
$web_assert->pageTextContains('Edit menu');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\settings_tray\FunctionalJavascript;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\menu_link_content\Entity\MenuLinkContent;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Tests handling of configuration overrides.
|
||||
*
|
||||
* @group settings_tray
|
||||
*/
|
||||
class OverriddenConfigurationTest extends SettingsTrayTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'settings_tray_override_test',
|
||||
'menu_ui',
|
||||
'menu_link_content',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$user = $this->createUser([
|
||||
'administer blocks',
|
||||
'access contextual links',
|
||||
'access toolbar',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test blocks with overridden related configuration removed when overridden.
|
||||
*/
|
||||
public function testOverriddenConfigurationRemoved() {
|
||||
$web_assert = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), ['administer site configuration', 'administer menu']);
|
||||
|
||||
// Confirm the branding block does include 'site_information' section when
|
||||
// the site name is not overridden.
|
||||
$branding_block = $this->placeBlock('system_branding_block');
|
||||
$this->drupalGet('user');
|
||||
$this->enableEditMode();
|
||||
$this->openBlockForm($this->getBlockSelector($branding_block));
|
||||
$web_assert->fieldExists('settings[site_information][site_name]');
|
||||
// Confirm the branding block does not include 'site_information' section
|
||||
// when the site name is overridden.
|
||||
$this->container->get('state')->set('settings_tray_override_test.site_name', TRUE);
|
||||
$this->drupalGet('user');
|
||||
$this->openBlockForm($this->getBlockSelector($branding_block));
|
||||
$web_assert->fieldNotExists('settings[site_information][site_name]');
|
||||
$page->pressButton('Save Site branding');
|
||||
$this->assertElementVisibleAfterWait('css', 'div:contains(The block configuration has been saved)');
|
||||
$web_assert->assertWaitOnAjaxRequest();
|
||||
// Confirm we did not save changes to the configuration.
|
||||
$this->assertEquals('Llama Fan Club', \Drupal::configFactory()->get('system.site')->get('name'));
|
||||
$this->assertEquals('Drupal', \Drupal::configFactory()->getEditable('system.site')->get('name'));
|
||||
|
||||
// Add a link or the menu will not render.
|
||||
$menu_link_content = MenuLinkContent::create([
|
||||
'title' => 'This is on the menu',
|
||||
'menu_name' => 'main',
|
||||
'link' => ['uri' => 'route:<front>'],
|
||||
]);
|
||||
$menu_link_content->save();
|
||||
// Confirm the menu block does include menu section when the menu is not
|
||||
// overridden.
|
||||
$menu_block = $this->placeBlock('system_menu_block:main');
|
||||
$web_assert->assertWaitOnAjaxRequest();
|
||||
$this->drupalGet('user');
|
||||
$web_assert->pageTextContains('This is on the menu');
|
||||
$this->openBlockForm($this->getBlockSelector($menu_block));
|
||||
$web_assert->elementExists('css', '#menu-overview');
|
||||
|
||||
// Confirm the menu block does not include menu section when the menu is
|
||||
// overridden.
|
||||
$this->container->get('state')->set('settings_tray_override_test.menu', TRUE);
|
||||
$this->drupalGet('user');
|
||||
$web_assert->pageTextContains('This is on the menu');
|
||||
$menu_with_overrides = \Drupal::configFactory()->get('system.menu.main')->get();
|
||||
$menu_without_overrides = \Drupal::configFactory()->getEditable('system.menu.main')->get();
|
||||
$this->openBlockForm($this->getBlockSelector($menu_block));
|
||||
$web_assert->elementNotExists('css', '#menu-overview');
|
||||
$page->pressButton('Save Main navigation');
|
||||
$this->assertElementVisibleAfterWait('css', 'div:contains(The block configuration has been saved)');
|
||||
$web_assert->assertWaitOnAjaxRequest();
|
||||
// Confirm we did not save changes to the configuration.
|
||||
$this->assertEquals('Labely label', \Drupal::configFactory()->get('system.menu.main')->get('label'));
|
||||
$this->assertEquals('Main navigation', \Drupal::configFactory()->getEditable('system.menu.main')->get('label'));
|
||||
$this->assertEquals($menu_with_overrides, \Drupal::configFactory()->get('system.menu.main')->get());
|
||||
$this->assertEquals($menu_without_overrides, \Drupal::configFactory()->getEditable('system.menu.main')->get());
|
||||
$web_assert->pageTextContains('This is on the menu');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that blocks with configuration overrides are disabled.
|
||||
*/
|
||||
public function testOverriddenBlock() {
|
||||
$web_assert = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$overridden_block = $this->placeBlock('system_powered_by_block', [
|
||||
'id' => 'overridden_block',
|
||||
'label_display' => 1,
|
||||
'label' => 'This will be overridden.',
|
||||
]);
|
||||
$this->drupalGet('user');
|
||||
$block_selector = $this->getBlockSelector($overridden_block);
|
||||
// Confirm the block is marked as Settings Tray editable.
|
||||
$this->assertEquals('editable', $page->find('css', $block_selector)->getAttribute('data-drupal-settingstray'));
|
||||
// Confirm the label is not overridden.
|
||||
$web_assert->elementContains('css', $block_selector, 'This will be overridden.');
|
||||
$this->enableEditMode();
|
||||
$this->openBlockForm($block_selector);
|
||||
|
||||
// Confirm the block Settings Tray functionality is disabled when block is
|
||||
// overridden.
|
||||
$this->container->get('state')->set('settings_tray_override_test.block', TRUE);
|
||||
$overridden_block->save();
|
||||
$block_config = \Drupal::configFactory()->getEditable('block.block.overridden_block');
|
||||
$block_config->set('settings', $block_config->get('settings'))->save();
|
||||
|
||||
$this->drupalGet('user');
|
||||
$this->assertOverriddenBlockDisabled($overridden_block, 'Now this will be the label.');
|
||||
|
||||
// Test a non-overridden block does show the form in the off-canvas dialog.
|
||||
$block = $this->placeBlock('system_powered_by_block', [
|
||||
'label_display' => 1,
|
||||
'label' => 'Labely label',
|
||||
]);
|
||||
$this->drupalGet('user');
|
||||
$block_selector = $this->getBlockSelector($block);
|
||||
// Confirm the block is marked as Settings Tray editable.
|
||||
$this->assertEquals('editable', $page->find('css', $block_selector)->getAttribute('data-drupal-settingstray'));
|
||||
// Confirm the label is not overridden.
|
||||
$web_assert->elementContains('css', $block_selector, 'Labely label');
|
||||
$this->openBlockForm($block_selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that an overridden block has Settings Tray disabled.
|
||||
*
|
||||
* @param \Drupal\block\Entity\Block $overridden_block
|
||||
* The overridden block.
|
||||
* @param string $override_text
|
||||
* The override text that should appear in the block.
|
||||
*/
|
||||
protected function assertOverriddenBlockDisabled(Block $overridden_block, $override_text) {
|
||||
$web_assert = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$block_selector = $this->getBlockSelector($overridden_block);
|
||||
$block_id = $overridden_block->id();
|
||||
// Confirm the block does not have a quick edit link.
|
||||
$contextual_links = $page->findAll('css', "$block_selector .contextual-links li a");
|
||||
$this->assertNotEmpty($contextual_links);
|
||||
foreach ($contextual_links as $link) {
|
||||
$this->assertNotContains("/admin/structure/block/manage/$block_id/off-canvas", $link->getAttribute('href'));
|
||||
}
|
||||
// Confirm the block is not marked as Settings Tray editable.
|
||||
$this->assertFalse($page->find('css', $block_selector)
|
||||
->hasAttribute('data-drupal-settingstray'));
|
||||
|
||||
// Confirm the text is actually overridden.
|
||||
$web_assert->elementContains('css', $this->getBlockSelector($overridden_block), $override_text);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\settings_tray\FunctionalJavascript;
|
||||
|
||||
use Drupal\block_content\Entity\BlockContent;
|
||||
use Drupal\block_content\Entity\BlockContentType;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Test Settings Tray and Quick Edit modules integration.
|
||||
*
|
||||
* @group settings_tray
|
||||
*/
|
||||
class QuickEditIntegrationTest extends SettingsTrayTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'block_content',
|
||||
'quickedit',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$user = $this->createUser([
|
||||
'administer blocks',
|
||||
'access contextual links',
|
||||
'access toolbar',
|
||||
'administer nodes',
|
||||
'access in-place editing',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests QuickEdit links behavior.
|
||||
*/
|
||||
public function testQuickEditLinks() {
|
||||
$quick_edit_selector = '#quickedit-entity-toolbar';
|
||||
$node_selector = '[data-quickedit-entity-id="node/1"]';
|
||||
$body_selector = '[data-quickedit-field-id="node/1/body/en/full"]';
|
||||
$web_assert = $this->assertSession();
|
||||
// Create a Content type and two test nodes.
|
||||
$this->createContentType(['type' => 'page']);
|
||||
$auth_role = Role::load(Role::AUTHENTICATED_ID);
|
||||
$this->grantPermissions($auth_role, [
|
||||
'edit any page content',
|
||||
'access content',
|
||||
]);
|
||||
$node = $this->createNode(
|
||||
[
|
||||
'title' => 'Page One',
|
||||
'type' => 'page',
|
||||
'body' => [
|
||||
[
|
||||
'value' => 'Regular NODE body for the test.',
|
||||
'format' => 'plain_text',
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
$page = $this->getSession()->getPage();
|
||||
$block_plugin = 'system_powered_by_block';
|
||||
|
||||
foreach ($this->getTestThemes() as $theme) {
|
||||
|
||||
$this->enableTheme($theme);
|
||||
|
||||
$block = $this->placeBlock($block_plugin);
|
||||
$block_selector = $this->getBlockSelector($block);
|
||||
// Load the same page twice.
|
||||
foreach ([1, 2] as $page_load_times) {
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
// The 2nd page load we should already be in edit mode.
|
||||
if ($page_load_times == 1) {
|
||||
$this->enableEditMode();
|
||||
}
|
||||
// In Edit mode clicking field should open QuickEdit toolbar.
|
||||
$page->find('css', $body_selector)->click();
|
||||
$this->assertElementVisibleAfterWait('css', $quick_edit_selector);
|
||||
|
||||
$this->disableEditMode();
|
||||
// Exiting Edit mode should close QuickEdit toolbar.
|
||||
$web_assert->elementNotExists('css', $quick_edit_selector);
|
||||
// When not in Edit mode QuickEdit toolbar should not open.
|
||||
$page->find('css', $body_selector)->click();
|
||||
$web_assert->elementNotExists('css', $quick_edit_selector);
|
||||
$this->enableEditMode();
|
||||
$this->openBlockForm($block_selector);
|
||||
$page->find('css', $body_selector)->click();
|
||||
$this->assertElementVisibleAfterWait('css', $quick_edit_selector);
|
||||
// Off-canvas dialog should be closed when opening QuickEdit toolbar.
|
||||
$this->waitForOffCanvasToClose();
|
||||
|
||||
$this->openBlockForm($block_selector);
|
||||
// QuickEdit toolbar should be closed when opening Off-canvas dialog.
|
||||
$web_assert->elementNotExists('css', $quick_edit_selector);
|
||||
}
|
||||
// Check using contextual links to invoke QuickEdit and open the tray.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$web_assert->assertWaitOnAjaxRequest();
|
||||
$this->disableEditMode();
|
||||
// Open QuickEdit toolbar before going into Edit mode.
|
||||
$this->clickContextualLink($node_selector, "Quick edit");
|
||||
$this->assertElementVisibleAfterWait('css', $quick_edit_selector);
|
||||
// Open off-canvas and enter Edit mode via contextual link.
|
||||
$this->clickContextualLink($block_selector, "Quick edit");
|
||||
$this->waitForOffCanvasToOpen();
|
||||
// QuickEdit toolbar should be closed when opening off-canvas dialog.
|
||||
$web_assert->elementNotExists('css', $quick_edit_selector);
|
||||
// Open QuickEdit toolbar via contextual link while in Edit mode.
|
||||
$this->clickContextualLink($node_selector, "Quick edit", FALSE);
|
||||
$this->waitForOffCanvasToClose();
|
||||
$this->assertElementVisibleAfterWait('css', $quick_edit_selector);
|
||||
$this->disableEditMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that contextual links in custom blocks are changed.
|
||||
*
|
||||
* "Quick edit" is quickedit.module link.
|
||||
* "Quick edit settings" is settings_tray.module link.
|
||||
*/
|
||||
public function testCustomBlockLinks() {
|
||||
$this->createBlockContentType('basic', TRUE);
|
||||
$block_content = $this->createBlockContent('Custom Block', 'basic', TRUE);
|
||||
$this->placeBlock('block_content:' . $block_content->uuid(), ['id' => 'custom']);
|
||||
$this->drupalGet('user');
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->toggleContextualTriggerVisibility('#block-custom');
|
||||
$page->find('css', '#block-custom .contextual button')->press();
|
||||
$links = $page->findAll('css', "#block-custom .contextual-links li a");
|
||||
$link_labels = [];
|
||||
/** @var \Behat\Mink\Element\NodeElement $link */
|
||||
foreach ($links as $link) {
|
||||
$link_labels[$link->getAttribute('href')] = $link->getText();
|
||||
}
|
||||
$href = array_search('Quick edit', $link_labels);
|
||||
$this->assertEquals('', $href);
|
||||
$href = array_search('Quick edit settings', $link_labels);
|
||||
$destination = (string) $this->loggedInUser->toUrl()->toString();
|
||||
$this->assertTrue(strstr($href, "/admin/structure/block/manage/custom/settings-tray?destination=$destination") !== FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom block.
|
||||
*
|
||||
* @param bool|string $title
|
||||
* (optional) Title of block. When no value is given uses a random name.
|
||||
* Defaults to FALSE.
|
||||
* @param string $bundle
|
||||
* (optional) Bundle name. Defaults to 'basic'.
|
||||
* @param bool $save
|
||||
* (optional) Whether to save the block. Defaults to TRUE.
|
||||
*
|
||||
* @return \Drupal\block_content\Entity\BlockContent
|
||||
* Created custom block.
|
||||
*/
|
||||
protected function createBlockContent($title = FALSE, $bundle = 'basic', $save = TRUE) {
|
||||
$title = $title ?: $this->randomName();
|
||||
$block_content = BlockContent::create([
|
||||
'info' => $title,
|
||||
'type' => $bundle,
|
||||
'langcode' => 'en',
|
||||
'body' => [
|
||||
'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
|
||||
'format' => 'plain_text',
|
||||
],
|
||||
]);
|
||||
if ($block_content && $save === TRUE) {
|
||||
$block_content->save();
|
||||
}
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom block type (bundle).
|
||||
*
|
||||
* @param string $label
|
||||
* The block type label.
|
||||
* @param bool $create_body
|
||||
* Whether or not to create the body field.
|
||||
*
|
||||
* @return \Drupal\block_content\Entity\BlockContentType
|
||||
* Created custom block type.
|
||||
*/
|
||||
protected function createBlockContentType($label, $create_body = FALSE) {
|
||||
$bundle = BlockContentType::create([
|
||||
'id' => $label,
|
||||
'label' => $label,
|
||||
'revision' => FALSE,
|
||||
]);
|
||||
$bundle->save();
|
||||
if ($create_body) {
|
||||
block_content_add_body_field($bundle->id());
|
||||
}
|
||||
return $bundle;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\settings_tray\FunctionalJavascript;
|
||||
|
||||
use Drupal\settings_tray_test\Plugin\Block\SettingsTrayFormAnnotationIsClassBlock;
|
||||
use Drupal\settings_tray_test\Plugin\Block\SettingsTrayFormAnnotationNoneBlock;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Testing opening and saving block forms in the off-canvas dialog.
|
||||
*
|
||||
* @group settings_tray
|
||||
*/
|
||||
class SettingsTrayBlockFormTest extends SettingsTrayTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'search',
|
||||
'settings_tray_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$user = $this->createUser([
|
||||
'administer blocks',
|
||||
'access contextual links',
|
||||
'access toolbar',
|
||||
'administer nodes',
|
||||
'search content',
|
||||
]);
|
||||
$this->drupalLogin($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests opening off-canvas dialog by click blocks and elements in the blocks.
|
||||
*/
|
||||
public function testBlocks() {
|
||||
foreach ($this->getBlockTests() as $test) {
|
||||
call_user_func_array([$this, 'doTestBlocks'], $test);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests opening off-canvas dialog by click blocks and elements in the blocks.
|
||||
*/
|
||||
protected function doTestBlocks($theme, $block_plugin, $new_page_text, $element_selector, $label_selector, $button_text, $toolbar_item, $permissions) {
|
||||
if ($permissions) {
|
||||
$this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), $permissions);
|
||||
}
|
||||
if ($new_page_text) {
|
||||
// Some asserts can be based on this value, so it should not be the same
|
||||
// for different blocks, because it can be saved in the site config.
|
||||
$new_page_text = $new_page_text . ' ' . $theme . ' ' . $block_plugin;
|
||||
}
|
||||
$web_assert = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->enableTheme($theme);
|
||||
$block = $this->placeBlock($block_plugin);
|
||||
$block_selector = $this->getBlockSelector($block);
|
||||
$block_id = $block->id();
|
||||
$this->drupalGet('user');
|
||||
|
||||
$link = $web_assert->waitForElement('css', "$block_selector .contextual-links li a");
|
||||
$this->assertEquals('Quick edit', $link->getHtml(), "'Quick edit' is the first contextual link for the block.");
|
||||
$destination = (string) $this->loggedInUser->toUrl()->toString();
|
||||
$this->assertContains("/admin/structure/block/manage/$block_id/settings-tray?destination=$destination", $link->getAttribute('href'));
|
||||
|
||||
if (isset($toolbar_item)) {
|
||||
// Check that you can open a toolbar tray and it will be closed after
|
||||
// entering edit mode.
|
||||
if ($element = $page->find('css', "#toolbar-administration a.is-active")) {
|
||||
// If a tray was open from page load close it.
|
||||
$element->click();
|
||||
$this->waitForNoElement("#toolbar-administration a.is-active");
|
||||
}
|
||||
$page->find('css', $toolbar_item)->click();
|
||||
$this->assertElementVisibleAfterWait('css', "{$toolbar_item}.is-active");
|
||||
}
|
||||
$this->enableEditMode();
|
||||
if (isset($toolbar_item)) {
|
||||
$this->waitForNoElement("{$toolbar_item}.is-active");
|
||||
}
|
||||
$this->openBlockForm($block_selector);
|
||||
switch ($block_plugin) {
|
||||
case 'system_powered_by_block':
|
||||
// Confirm "Display Title" is not checked.
|
||||
$web_assert->checkboxNotChecked('settings[label_display]');
|
||||
// Confirm Title is not visible.
|
||||
$this->assertEquals($this->isLabelInputVisible(), FALSE, 'Label is not visible');
|
||||
$page->checkField('settings[label_display]');
|
||||
$this->assertEquals($this->isLabelInputVisible(), TRUE, 'Label is visible');
|
||||
// Fill out form, save the form.
|
||||
$page->fillField('settings[label]', $new_page_text);
|
||||
|
||||
break;
|
||||
|
||||
case 'system_branding_block':
|
||||
// Fill out form, save the form.
|
||||
$page->fillField('settings[site_information][site_name]', $new_page_text);
|
||||
break;
|
||||
|
||||
case 'settings_tray_test_class':
|
||||
$web_assert->elementExists('css', '[data-drupal-selector="edit-settings-some-setting"]');
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($new_page_text)) {
|
||||
$page->pressButton($button_text);
|
||||
// Make sure the changes are present.
|
||||
$new_page_text_locator = "$block_selector $label_selector:contains($new_page_text)";
|
||||
$this->assertElementVisibleAfterWait('css', $new_page_text_locator);
|
||||
// The page is loaded with the new change but make sure page is
|
||||
// completely loaded.
|
||||
$this->assertPageLoadComplete();
|
||||
}
|
||||
|
||||
$this->openBlockForm($block_selector);
|
||||
|
||||
$this->disableEditMode();
|
||||
// Canvas should close when editing module is closed.
|
||||
$this->waitForOffCanvasToClose();
|
||||
|
||||
$this->enableEditMode();
|
||||
|
||||
// Open block form by clicking a element inside the block.
|
||||
// This confirms that default action for links and form elements is
|
||||
// suppressed.
|
||||
$this->openBlockForm("$block_selector {$element_selector}", $block_selector);
|
||||
$web_assert->elementTextContains('css', '.contextual-toolbar-tab button', 'Editing');
|
||||
$web_assert->elementAttributeContains('css', '.dialog-off-canvas-main-canvas', 'class', 'js-settings-tray-edit-mode');
|
||||
// Simulate press the Escape key.
|
||||
$this->getSession()->executeScript('jQuery("body").trigger(jQuery.Event("keyup", { keyCode: 27 }));');
|
||||
$this->waitForOffCanvasToClose();
|
||||
$this->getSession()->wait(100);
|
||||
$this->getSession()->executeScript("jQuery('[data-quickedit-entity-id]').trigger('mouseleave')");
|
||||
$this->getSession()->getPage()->find('css', static::TOOLBAR_EDIT_LINK_SELECTOR)->mouseOver();
|
||||
$this->assertEditModeDisabled();
|
||||
$this->assertNotEmpty($web_assert->waitForElement('css', '#drupal-live-announce:contains(Exited edit mode)'));
|
||||
$this->waitForNoElement('.contextual-toolbar-tab button:contains(Editing)');
|
||||
$web_assert->elementAttributeNotContains('css', '.dialog-off-canvas-main-canvas', 'class', 'js-settings-tray-edit-mode');
|
||||
|
||||
// Clean up test data so each test does not impact the next.
|
||||
$block->delete();
|
||||
if ($permissions) {
|
||||
user_role_revoke_permissions(Role::AUTHENTICATED_ID, $permissions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates tests for ::testBlocks().
|
||||
*/
|
||||
public function getBlockTests() {
|
||||
$blocks = [];
|
||||
foreach ($this->getTestThemes() as $theme) {
|
||||
$blocks += [
|
||||
"$theme: block-powered" => [
|
||||
'theme' => $theme,
|
||||
'block_plugin' => 'system_powered_by_block',
|
||||
'new_page_text' => 'Can you imagine anyone showing the label on this block',
|
||||
'element_selector' => 'span a',
|
||||
'label_selector' => 'h2',
|
||||
'button_text' => 'Save Powered by Drupal',
|
||||
'toolbar_item' => '#toolbar-item-user',
|
||||
NULL,
|
||||
],
|
||||
"$theme: block-branding" => [
|
||||
'theme' => $theme,
|
||||
'block_plugin' => 'system_branding_block',
|
||||
'new_page_text' => 'The site that will live a very short life',
|
||||
'element_selector' => "a[rel='home']:last-child",
|
||||
'label_selector' => "a[rel='home']:last-child",
|
||||
'button_text' => 'Save Site branding',
|
||||
'toolbar_item' => '#toolbar-item-administration',
|
||||
['administer site configuration'],
|
||||
],
|
||||
"$theme: block-search" => [
|
||||
'theme' => $theme,
|
||||
'block_plugin' => 'search_form_block',
|
||||
'new_page_text' => NULL,
|
||||
'element_selector' => '#edit-submit',
|
||||
'label_selector' => 'h2',
|
||||
'button_text' => 'Save Search form',
|
||||
'toolbar_item' => NULL,
|
||||
NULL,
|
||||
],
|
||||
// This is the functional JS test coverage accompanying
|
||||
// \Drupal\Tests\settings_tray\Functional\SettingsTrayTest::testPossibleAnnotations().
|
||||
"$theme: " . SettingsTrayFormAnnotationIsClassBlock::class => [
|
||||
'theme' => $theme,
|
||||
'block_plugin' => 'settings_tray_test_class',
|
||||
'new_page_text' => NULL,
|
||||
'element_selector' => 'span',
|
||||
'label_selector' => NULL,
|
||||
'button_text' => NULL,
|
||||
'toolbar_item' => NULL,
|
||||
NULL,
|
||||
],
|
||||
// This is the functional JS test coverage accompanying
|
||||
// \Drupal\Tests\settings_tray\Functional\SettingsTrayTest::testPossibleAnnotations().
|
||||
"$theme: " . SettingsTrayFormAnnotationNoneBlock::class => [
|
||||
'theme' => $theme,
|
||||
'block_plugin' => 'settings_tray_test_none',
|
||||
'new_page_text' => NULL,
|
||||
'element_selector' => 'span',
|
||||
'label_selector' => NULL,
|
||||
'button_text' => NULL,
|
||||
'toolbar_item' => NULL,
|
||||
NULL,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests enabling and disabling Edit Mode.
|
||||
*/
|
||||
public function testEditModeEnableDisable() {
|
||||
foreach ($this->getTestThemes() as $theme) {
|
||||
$this->enableTheme($theme);
|
||||
$block = $this->placeBlock('system_powered_by_block');
|
||||
foreach (['contextual_link', 'toolbar_link'] as $enable_option) {
|
||||
$this->drupalGet('user');
|
||||
$this->assertEditModeDisabled();
|
||||
switch ($enable_option) {
|
||||
// Enable Edit mode.
|
||||
case 'contextual_link':
|
||||
$this->clickContextualLink($this->getBlockSelector($block), "Quick edit");
|
||||
$this->waitForOffCanvasToOpen();
|
||||
$this->assertEditModeEnabled();
|
||||
break;
|
||||
|
||||
case 'toolbar_link':
|
||||
$this->enableEditMode();
|
||||
break;
|
||||
}
|
||||
$this->disableEditMode();
|
||||
|
||||
// Make another page request to ensure Edit mode is still disabled.
|
||||
$this->drupalGet('user');
|
||||
$this->assertEditModeDisabled();
|
||||
// Make sure on this page request it also re-enables and disables
|
||||
// correctly.
|
||||
$this->enableEditMode();
|
||||
$this->disableEditMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that validation errors appear in the off-canvas dialog.
|
||||
*/
|
||||
public function testValidationMessages() {
|
||||
$page = $this->getSession()->getPage();
|
||||
$web_assert = $this->assertSession();
|
||||
foreach ($this->getTestThemes() as $theme) {
|
||||
$this->enableTheme($theme);
|
||||
$block = $this->placeBlock('settings_tray_test_validation');
|
||||
$this->drupalGet('user');
|
||||
$this->enableEditMode();
|
||||
$this->openBlockForm($this->getBlockSelector($block));
|
||||
$page->pressButton('Save Block with validation error');
|
||||
$web_assert->assertWaitOnAjaxRequest();
|
||||
// The settings_tray_test_validation test plugin form always has a
|
||||
// validation error.
|
||||
$web_assert->elementContains('css', '#drupal-off-canvas', 'Sorry system error. Please save again');
|
||||
$this->disableEditMode();
|
||||
$block->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTestThemes() {
|
||||
// Remove 'seven' theme. Setting Tray "Edit Mode" will not work with 'seven'
|
||||
// because it removes all contextual links the off-canvas dialog should.
|
||||
return array_filter(parent::getTestThemes(), function ($theme) {
|
||||
return $theme !== 'seven';
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\settings_tray\FunctionalJavascript;
|
||||
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\Tests\contextual\FunctionalJavascript\ContextualLinkClickTrait;
|
||||
use Drupal\Tests\system\FunctionalJavascript\OffCanvasTestBase;
|
||||
|
||||
/**
|
||||
* Base class for Settings Tray tests.
|
||||
*/
|
||||
class SettingsTrayTestBase extends OffCanvasTestBase {
|
||||
|
||||
use ContextualLinkClickTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'settings_tray',
|
||||
// Add test module to override CSS pointer-events properties because they
|
||||
// cause test failures.
|
||||
'settings_tray_test_css',
|
||||
];
|
||||
|
||||
const TOOLBAR_EDIT_LINK_SELECTOR = '#toolbar-bar div.contextual-toolbar-tab button';
|
||||
|
||||
const LABEL_INPUT_SELECTOR = 'input[data-drupal-selector="edit-settings-label"]';
|
||||
|
||||
/**
|
||||
* Open block form by clicking the element found with a css selector.
|
||||
*
|
||||
* @param string $block_selector
|
||||
* A css selector selects the block or an element within it.
|
||||
* @param string $contextual_link_container
|
||||
* The element that contains the contextual links. If none provide the
|
||||
* $block_selector will be used.
|
||||
*/
|
||||
protected function openBlockForm($block_selector, $contextual_link_container = '') {
|
||||
if (!$contextual_link_container) {
|
||||
$contextual_link_container = $block_selector;
|
||||
}
|
||||
// Ensure that contextual link element is present because this is required
|
||||
// to open the off-canvas dialog in edit mode.
|
||||
$contextual_link = $this->assertSession()->waitForElement('css', "$contextual_link_container .contextual-links a");
|
||||
$this->assertNotEmpty($contextual_link);
|
||||
// When page first loads Edit Mode is not triggered until first contextual
|
||||
// link is added.
|
||||
$this->assertElementVisibleAfterWait('css', '.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode');
|
||||
// Ensure that all other Ajax activity is completed.
|
||||
$this->assertSession()->assertWaitOnAjaxRequest();
|
||||
$block = $this->getSession()->getPage()->find('css', $block_selector);
|
||||
$block->mouseOver();
|
||||
$block->click();
|
||||
$this->waitForOffCanvasToOpen();
|
||||
$this->assertOffCanvasBlockFormIsValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables edit mode by pressing edit button in the toolbar.
|
||||
*/
|
||||
protected function enableEditMode() {
|
||||
$this->pressToolbarEditButton();
|
||||
$this->assertEditModeEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables edit mode by pressing edit button in the toolbar.
|
||||
*/
|
||||
protected function disableEditMode() {
|
||||
$this->assertSession()->assertWaitOnAjaxRequest();
|
||||
$this->pressToolbarEditButton();
|
||||
$this->assertEditModeDisabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Press the toolbar Edit button provided by the contextual module.
|
||||
*/
|
||||
protected function pressToolbarEditButton() {
|
||||
$this->assertSession()->waitForElement('css', '[data-contextual-id] .contextual-links a');
|
||||
$edit_button = $this->getSession()
|
||||
->getPage()
|
||||
->find('css', static::TOOLBAR_EDIT_LINK_SELECTOR);
|
||||
$this->getSession()->executeScript("jQuery('[data-quickedit-entity-id]').trigger('mouseleave')");
|
||||
$edit_button->mouseOver();
|
||||
$edit_button->press();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that edit mode has been properly disabled.
|
||||
*/
|
||||
protected function assertEditModeDisabled() {
|
||||
$web_assert = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$this->getSession()->executeScript("jQuery('[data-quickedit-entity-id]').trigger('mouseleave')");
|
||||
$page->find('css', static::TOOLBAR_EDIT_LINK_SELECTOR)->mouseOver();
|
||||
$this->assertTrue($page->waitFor(10, function ($page) {
|
||||
return !$page->find('css', '.contextual .trigger:not(.visually-hidden)');
|
||||
}));
|
||||
// Contextual triggers should be hidden.
|
||||
$web_assert->elementExists('css', '.contextual .trigger.visually-hidden');
|
||||
// No contextual triggers should be not hidden.
|
||||
$web_assert->elementNotExists('css', '.contextual .trigger:not(.visually-hidden)');
|
||||
// The toolbar edit button should read "Edit".
|
||||
$web_assert->elementContains('css', static::TOOLBAR_EDIT_LINK_SELECTOR, 'Edit');
|
||||
// The main canvas element should NOT have the "js-settings-tray-edit-mode"
|
||||
// class.
|
||||
$web_assert->elementNotExists('css', '.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that edit mode has been properly enabled.
|
||||
*/
|
||||
protected function assertEditModeEnabled() {
|
||||
$web_assert = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
// Move the mouse over the toolbar button so that isn't over a contextual
|
||||
// links area which cause the contextual link to be shown.
|
||||
$page->find('css', static::TOOLBAR_EDIT_LINK_SELECTOR)->mouseOver();
|
||||
$this->assertTrue($page->waitFor(10, function ($page) {
|
||||
return !$page->find('css', '.contextual .trigger.visually-hidden');
|
||||
}));
|
||||
// No contextual triggers should be hidden.
|
||||
$web_assert->elementNotExists('css', '.contextual .trigger.visually-hidden');
|
||||
// The toolbar edit button should read "Editing".
|
||||
$web_assert->elementContains('css', static::TOOLBAR_EDIT_LINK_SELECTOR, 'Editing');
|
||||
// The main canvas element should have the "js-settings-tray-edit-mode" class.
|
||||
$web_assert->elementExists('css', '.dialog-off-canvas-main-canvas.js-settings-tray-edit-mode');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that Off-Canvas block form is valid.
|
||||
*/
|
||||
protected function assertOffCanvasBlockFormIsValid() {
|
||||
$web_assert = $this->assertSession();
|
||||
// Confirm that Block title display label has been changed.
|
||||
$web_assert->elementTextContains('css', '.form-item-settings-label-display label', 'Display block title');
|
||||
// Confirm Block title label is shown if checkbox is checked.
|
||||
if ($this->getSession()->getPage()->find('css', 'input[name="settings[label_display]"]')->isChecked()) {
|
||||
$this->assertEquals($this->isLabelInputVisible(), TRUE, 'Label is visible');
|
||||
$web_assert->elementTextContains('css', '.form-item-settings-label label', 'Block title');
|
||||
}
|
||||
else {
|
||||
$this->assertEquals($this->isLabelInputVisible(), FALSE, 'Label is not visible');
|
||||
}
|
||||
|
||||
// Check that common block form elements exist.
|
||||
$web_assert->elementExists('css', static::LABEL_INPUT_SELECTOR);
|
||||
$web_assert->elementExists('css', 'input[data-drupal-selector="edit-settings-label-display"]');
|
||||
// Check that advanced block form elements do not exist.
|
||||
$web_assert->elementNotExists('css', 'input[data-drupal-selector="edit-visibility-request-path-pages"]');
|
||||
$web_assert->elementNotExists('css', 'select[data-drupal-selector="edit-region"]');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTestThemes() {
|
||||
// Remove 'seven' theme. Settings Tray "Edit Mode" will not work with
|
||||
// 'seven' because it removes all contextual links.
|
||||
return array_filter(parent::getTestThemes(), function ($theme) {
|
||||
return $theme !== 'seven';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block CSS selector.
|
||||
*
|
||||
* @param \Drupal\block\Entity\Block $block
|
||||
* The block.
|
||||
*
|
||||
* @return string
|
||||
* The CSS selector.
|
||||
*/
|
||||
public function getBlockSelector(Block $block) {
|
||||
return '#block-' . str_replace('_', '-', $block->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the label input is visible.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the label is visible, FALSE if it is not.
|
||||
*/
|
||||
protected function isLabelInputVisible() {
|
||||
return $this->getSession()->getPage()->find('css', static::LABEL_INPUT_SELECTOR)->isVisible();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\settings_tray\Unit\Access;
|
||||
|
||||
use Drupal\block\BlockInterface;
|
||||
use Drupal\Core\Access\AccessResultAllowed;
|
||||
use Drupal\Core\Access\AccessResultInterface;
|
||||
use Drupal\Core\Access\AccessResultNeutral;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Plugin\PluginWithFormsInterface;
|
||||
use Drupal\settings_tray\Access\BlockPluginHasSettingsTrayFormAccessCheck;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\settings_tray\Access\BlockPluginHasSettingsTrayFormAccessCheck
|
||||
* @group settings_tray
|
||||
*/
|
||||
class BlockPluginHasSettingsTrayFormAccessCheckTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::access
|
||||
* @covers ::accessBlockPlugin
|
||||
* @dataProvider providerTestAccess
|
||||
*/
|
||||
public function testAccess($with_forms, array $plugin_definition, AccessResultInterface $expected_access_result) {
|
||||
$block_plugin = $this->prophesize()->willImplement(BlockPluginInterface::class);
|
||||
|
||||
if ($with_forms) {
|
||||
$block_plugin->willImplement(PluginWithFormsInterface::class);
|
||||
$block_plugin->hasFormClass(Argument::type('string'))->will(function ($arguments) use ($plugin_definition) {
|
||||
return !empty($plugin_definition['forms'][$arguments[0]]);
|
||||
});
|
||||
}
|
||||
|
||||
$block = $this->prophesize(BlockInterface::class);
|
||||
$block->getPlugin()->willReturn($block_plugin->reveal());
|
||||
|
||||
$access_check = new BlockPluginHasSettingsTrayFormAccessCheck();
|
||||
$this->assertEquals($expected_access_result, $access_check->access($block->reveal()));
|
||||
$this->assertEquals($expected_access_result, $access_check->accessBlockPlugin($block_plugin->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for ::testAccess().
|
||||
*/
|
||||
public function providerTestAccess() {
|
||||
$annotation_forms_settings_tray_class = [
|
||||
'forms' => [
|
||||
'settings_tray' => $this->randomMachineName(),
|
||||
],
|
||||
];
|
||||
$annotation_forms_settings_tray_not_set = [];
|
||||
$annotation_forms_settings_tray_false = [
|
||||
'forms' => [
|
||||
'settings_tray' => FALSE,
|
||||
],
|
||||
];
|
||||
return [
|
||||
'block plugin with forms, forms[settings_tray] set to class' => [
|
||||
TRUE,
|
||||
$annotation_forms_settings_tray_class,
|
||||
new AccessResultAllowed(),
|
||||
],
|
||||
'block plugin with forms, forms[settings_tray] not set' => [
|
||||
TRUE,
|
||||
$annotation_forms_settings_tray_not_set,
|
||||
new AccessResultNeutral(),
|
||||
],
|
||||
'block plugin with forms, forms[settings_tray] set to FALSE' => [
|
||||
TRUE,
|
||||
$annotation_forms_settings_tray_false,
|
||||
new AccessResultNeutral(),
|
||||
],
|
||||
// In practice, all block plugins extend BlockBase, which means they all
|
||||
// implement PluginWithFormsInterface, but this may change in the future.
|
||||
// This ensures Settings Tray will continue to work correctly.
|
||||
'block plugin without forms, forms[settings_tray] set to class' => [
|
||||
FALSE,
|
||||
$annotation_forms_settings_tray_class,
|
||||
new AccessResultNeutral(),
|
||||
],
|
||||
'block plugin without forms, forms[settings_tray] not set' => [
|
||||
FALSE,
|
||||
$annotation_forms_settings_tray_not_set,
|
||||
new AccessResultNeutral(),
|
||||
],
|
||||
'block plugin without forms, forms[settings_tray] set to FALSE' => [
|
||||
FALSE,
|
||||
$annotation_forms_settings_tray_false,
|
||||
new AccessResultNeutral(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue