Issue #2764931 by tedbow, tim.plunkett, nod_, drpal, droplet, dawehner, Wim Leers, phenaproxima: Contextual links don't work with 'use-ajax' links

8.5.x
webchick 2017-10-16 18:15:27 -04:00
parent fd8d98399a
commit 18f322a212
10 changed files with 158 additions and 70 deletions

View File

@ -46,26 +46,7 @@
}
}
// Bind Ajax behaviors to all items showing the class.
$('.use-ajax').once('ajax').each(function () {
const element_settings = {};
// Clicked links look better with the throbber than the progress bar.
element_settings.progress = { type: 'throbber' };
// For anchor tags, these will go to the target of the anchor rather
// than the usual location.
const href = $(this).attr('href');
if (href) {
element_settings.url = href;
element_settings.event = 'click';
}
element_settings.dialogType = $(this).data('dialog-type');
element_settings.dialogRenderer = $(this).data('dialog-renderer');
element_settings.dialog = $(this).data('dialog-options');
element_settings.base = $(this).attr('id');
element_settings.element = this;
Drupal.ajax(element_settings);
});
Drupal.ajax.bindAjaxLinks(document.body);
// This class means to submit the form to the action using Ajax.
$('.use-ajax-submit').once('ajax').each(function () {
@ -268,6 +249,39 @@
return Drupal.ajax.instances.filter(instance => instance && instance.element !== false && !document.body.contains(instance.element));
};
/**
* Bind Ajax functionality to links that use the 'use-ajax' class.
*
* @param {HTMLElement} element
* Element to enable Ajax functionality for.
*/
Drupal.ajax.bindAjaxLinks = (element) => {
// Bind Ajax behaviors to all items showing the class.
$(element).find('.use-ajax').once('ajax').each((i, ajaxLink) => {
const $linkElement = $(ajaxLink);
const elementSettings = {
// Clicked links look better with the throbber than the progress bar.
progress: { type: 'throbber' },
dialogType: $linkElement.data('dialog-type'),
dialog: $linkElement.data('dialog-options'),
dialogRenderer: $linkElement.data('dialog-renderer'),
base: $linkElement.attr('id'),
element: ajaxLink,
};
const href = $linkElement.attr('href');
/**
* For anchor tags, these will go to the target of the anchor rather
* than the usual location.
*/
if (href) {
elementSettings.url = href;
elementSettings.event = 'click';
}
Drupal.ajax(elementSettings);
});
};
/**
* Settings for an Ajax object.
*
@ -1340,4 +1354,5 @@
}
},
};
}(jQuery, window, Drupal, drupalSettings));

View File

@ -27,23 +27,7 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr
}
}
$('.use-ajax').once('ajax').each(function () {
var element_settings = {};
element_settings.progress = { type: 'throbber' };
var href = $(this).attr('href');
if (href) {
element_settings.url = href;
element_settings.event = 'click';
}
element_settings.dialogType = $(this).data('dialog-type');
element_settings.dialogRenderer = $(this).data('dialog-renderer');
element_settings.dialog = $(this).data('dialog-options');
element_settings.base = $(this).attr('id');
element_settings.element = this;
Drupal.ajax(element_settings);
});
Drupal.ajax.bindAjaxLinks(document.body);
$('.use-ajax-submit').once('ajax').each(function () {
var element_settings = {};
@ -137,6 +121,28 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr
});
};
Drupal.ajax.bindAjaxLinks = function (element) {
$(element).find('.use-ajax').once('ajax').each(function (i, ajaxLink) {
var $linkElement = $(ajaxLink);
var elementSettings = {
progress: { type: 'throbber' },
dialogType: $linkElement.data('dialog-type'),
dialog: $linkElement.data('dialog-options'),
dialogRenderer: $linkElement.data('dialog-renderer'),
base: $linkElement.attr('id'),
element: ajaxLink
};
var href = $linkElement.attr('href');
if (href) {
elementSettings.url = href;
elementSettings.event = 'click';
}
Drupal.ajax(elementSettings);
});
};
Drupal.Ajax = function (base, element, element_settings) {
var defaults = {
event: element ? 'mousedown' : null,

View File

@ -20,6 +20,7 @@ drupal.contextual-links:
dependencies:
- core/jquery
- core/drupal
- core/drupal.ajax
- core/drupalSettings
- core/backbone
- core/modernizr

View File

@ -249,4 +249,19 @@
Drupal.theme.contextualTrigger = function () {
return '<button class="trigger visually-hidden focusable" type="button"></button>';
};
/**
* Bind Ajax contextual links when 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) => {
Drupal.ajax.bindAjaxLinks(data.$el[0]);
});
}(jQuery, Drupal, drupalSettings, _, Backbone, window.JSON, window.sessionStorage));

View File

@ -144,4 +144,8 @@
Drupal.theme.contextualTrigger = function () {
return '<button class="trigger visually-hidden focusable" type="button"></button>';
};
$(document).on('drupalContextualLinkAdded', function (event, data) {
Drupal.ajax.bindAjaxLinks(data.$el[0]);
});
})(jQuery, Drupal, drupalSettings, _, Backbone, window.JSON, window.sessionStorage);

View File

@ -2,3 +2,11 @@ contextual_test:
title: 'Test Link'
route_name: 'contextual_test'
group: 'contextual_test'
contextual_test_ajax:
title: 'Test Link with Ajax'
route_name: 'contextual_test'
group: 'contextual_test'
options:
attributes:
class: ['use-ajax']
data-dialog-type: 'modal'

View File

@ -15,3 +15,23 @@ function contextual_test_block_view_alter(array &$build, BlockPluginInterface $b
'route_parameters' => [],
];
}
/**
* Implements hook_contextual_links_view_alter().
*
* @todo Apparently this too late to attach the library?
* It won't work without contextual_test_page_attachments_alter()
* Is that a problem? Should the contextual module itself do the attaching?
*/
function contextual_test_contextual_links_view_alter(&$element, $items) {
if (isset($element['#links']['contextual-test-ajax'])) {
$element['#attached']['library'][] = 'core/drupal.dialog.ajax';
}
}
/**
* Implements hook_page_attachments_alter().
*/
function contextual_test_page_attachments_alter(array &$attachments) {
$attachments['#attached']['library'][] = 'core/drupal.dialog.ajax';
}

View File

@ -69,6 +69,17 @@ class ContextualLinksTest extends JavascriptTestBase {
$this->clickContextualLink('#block-branding', 'Test Link');
$this->assertSession()->pageTextContains('Everything is contextual!');
// Test click a contextual link that uses ajax.
$this->drupalGet('user');
$this->assertSession()->assertWaitOnAjaxRequest();
$current_page_string = 'NOT_RELOADED_IF_ON_PAGE';
$this->getSession()->executeScript('document.body.appendChild(document.createTextNode("' . $current_page_string . '"));');
$this->clickContextualLink('#block-branding', 'Test Link with Ajax');
$this->assertNotEmpty($this->assertSession()->waitForElementVisible('css', '#drupal-modal'));
$this->assertSession()->elementContains('css', '#drupal-modal', 'Everything is contextual!');
// Check to make sure that page was not reloaded.
$this->assertSession()->pageTextContains($current_page_string);
// Test clicking contextual link with toolbar.
$this->container->get('module_installer')->install(['toolbar']);
$this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), ['access toolbar']);

View File

@ -148,6 +148,27 @@
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 (!('dialogOptions' in instance.options.data)) {
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.
*
@ -159,6 +180,12 @@
* @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';
@ -167,12 +194,6 @@
}
});
/**
* Bind Ajax behaviors to all items showing the class.
* @todo Fix contextual links to work with use-ajax links in
* https://www.drupal.org/node/2764931.
*/
Drupal.attachBehaviors(data.$el[0]);
/**
* Bind a listener to all 'Quick edit' links for blocks. Click "Edit" button
* in toolbar to force Contextual Edit which starts Settings Tray edit
@ -211,21 +232,6 @@
Drupal.behaviors.toggleEditMode = {
attach() {
$(toggleEditSelector).once('settingstray').on('click.settingstray', toggleEditMode);
// 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 (!('dialogOptions' in instance.options.data)) {
instance.options.data.dialogOptions = {};
}
instance.options.data.dialogOptions.settingsTrayActiveEditableId = $(instance.element).parents('.settings-tray-editable').attr('id');
instance.progress = { type: 'fullscreen' };
});
},
};

View File

@ -93,7 +93,21 @@
setEditModeState(!isInEditMode());
}
function prepareAjaxLinks() {
Drupal.ajax.instances.filter(function (instance) {
return instance && $(instance.element).attr('data-dialog-renderer') === 'off_canvas';
}).forEach(function (instance) {
if (!('dialogOptions' in instance.options.data)) {
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) {
@ -101,8 +115,6 @@
}
});
Drupal.attachBehaviors(data.$el[0]);
data.$el.find(blockConfigureSelector).on('click.settingstray', function () {
if (!isInEditMode()) {
$(toggleEditSelector).trigger('click').trigger('click.settings_tray');
@ -122,16 +134,6 @@
Drupal.behaviors.toggleEditMode = {
attach: function attach() {
$(toggleEditSelector).once('settingstray').on('click.settingstray', toggleEditMode);
Drupal.ajax.instances.filter(function (instance) {
return instance && $(instance.element).attr('data-dialog-renderer') === 'off_canvas';
}).forEach(function (instance) {
if (!('dialogOptions' in instance.options.data)) {
instance.options.data.dialogOptions = {};
}
instance.options.data.dialogOptions.settingsTrayActiveEditableId = $(instance.element).parents('.settings-tray-editable').attr('id');
instance.progress = { type: 'fullscreen' };
});
}
};