diff --git a/core/misc/message.es6.js b/core/misc/message.es6.js index 51656e0f3d..26581ad853 100644 --- a/core/misc/message.es6.js +++ b/core/misc/message.es6.js @@ -4,11 +4,11 @@ */ ((Drupal) => { /** - * @typedef {object} Drupal.message~messageDefinition + * @typedef {object} Drupal.Message~messageDefinition */ /** - * Constructs a new instance of the Drupal.message object. + * Constructs a new instance of the Drupal.Message object. * * This provides a uniform interface for adding and removing messages to a * specific location on the page. @@ -17,11 +17,11 @@ * The zone where to add messages. If no element is provided an attempt is * made to determine a default location. * - * @return {Drupal.message~messageDefinition} + * @return {Drupal.Message~messageDefinition} * Object to add and remove messages. */ - Drupal.message = class { + Drupal.Message = class { constructor(messageWrapper = null) { this.messageWrapper = messageWrapper; } @@ -41,7 +41,7 @@ wrapper.setAttribute('data-drupal-messages', ''); wrapper.removeAttribute('class'); } - return wrapper.innerHTML === '' ? Drupal.message.messageInternalWrapper(wrapper) : wrapper.firstElementChild; + return wrapper.innerHTML === '' ? Drupal.Message.messageInternalWrapper(wrapper) : wrapper.firstElementChild; } /** @@ -61,7 +61,7 @@ /** * Sequentially adds a message to the message area. * - * @name Drupal.message~messageDefinition.add + * @name Drupal.Message~messageDefinition.add * * @param {string} message * The message to display @@ -84,7 +84,7 @@ */ add(message, options = {}) { if (!this.messageWrapper) { - this.messageWrapper = Drupal.message.defaultWrapper(); + this.messageWrapper = Drupal.Message.defaultWrapper(); } if (!options.hasOwnProperty('type')) { options.type = 'status'; @@ -95,7 +95,7 @@ } // Send message to screen reader. - Drupal.message.announce(message, options); + Drupal.Message.announce(message, options); /** * Use the provided index for the message or generate a unique key to * allow message deletion. @@ -105,8 +105,8 @@ `${options.type}-${Math.random().toFixed(15).replace('0.', '')}`; // Throw an error if an unexpected message type is used. - if (!(Drupal.message.getMessageTypeLabels().hasOwnProperty(options.type))) { - throw new Error(`The message type, ${options.type}, is not present in Drupal.message.getMessageTypeLabels().`); + if (!(Drupal.Message.getMessageTypeLabels().hasOwnProperty(options.type))) { + throw new Error(`The message type, ${options.type}, is not present in Drupal.Message.getMessageTypeLabels().`); } this.messageWrapper.appendChild(Drupal.theme('message', { text: message }, options)); @@ -115,79 +115,45 @@ } /** - * Select a set of messages based on index. + * Select a message based on id. * - * @name Drupal.message~messageDefinition.select + * @name Drupal.Message~messageDefinition.select * - * @param {string|Array.} index - * The message index to delete from the area. + * @param {string} id + * The message id to delete from the area. * - * @return {NodeList|Array} - * Elements found. + * @return {Element} + * Element found. */ - select(index) { - // When there are nothing to select, return an empty list. - if (!index || (Array.isArray(index) && index.length === 0)) { - return []; - } - - // Construct an array of selectors based on the available message index(s). - const selectors = (Array.isArray(index) ? index : [index]) - .map(currentIndex => `[data-drupal-message-id^="${currentIndex}"]`); - - return this.messageWrapper.querySelectorAll(selectors.join(',')); - } - - /** - * Helper to remove elements. - * - * @param {NodeList|Array.} elements - * DOM Nodes to be removed. - * - * @return {number} - * Number of removed nodes. - */ - removeElements(elements) { - if (!elements || !elements.length) { - return 0; - } - - const length = elements.length; - for (let i = 0; i < length; i++) { - this.messageWrapper.removeChild(elements[i]); - } - return length; + select(id) { + return this.messageWrapper.querySelector(`[data-drupal-message-id^="${id}"]`); } /** * Removes messages from the message area. * - * @name Drupal.message~messageDefinition.remove + * @name Drupal.Message~messageDefinition.remove * - * @param {string|Array.} ids + * @param {string} id * Index of the message to remove, as returned by - * {@link Drupal.message~messageDefinition.add}, or an - * array of indexes. + * {@link Drupal.Message~messageDefinition.add}. * * @return {number} * Number of removed messages. */ - remove(ids) { - const messages = this.select(ids); - return this.removeElements(messages); + remove(id) { + return this.messageWrapper.removeChild(this.select(id)); } /** * Removes all messages from the message area. * - * @name Drupal.message~messageDefinition.clear - * - * @return {number} - * Number of removed messages. + * @name Drupal.Message~messageDefinition.clear */ clear() { - const messages = this.messageWrapper.querySelectorAll('[data-drupal-message-id]'); - return this.removeElements(messages); + Array.prototype.forEach.call(this.messageWrapper.querySelectorAll('[data-drupal-message-id]'), (message) => { + this.messageWrapper.removeChild(message); + }); } /** @@ -253,7 +219,7 @@ * A DOM Node. */ Drupal.theme.message = ({ text }, options) => { - const messagesTypes = Drupal.message.getMessageTypeLabels(); + const messagesTypes = Drupal.Message.getMessageTypeLabels(); const messageWraper = document.createElement('div'); const messageText = document.createElement('h2'); messageText.setAttribute('class', 'visually-hidden'); diff --git a/core/misc/message.js b/core/misc/message.js index 6eb6829b9f..34664e3be4 100644 --- a/core/misc/message.js +++ b/core/misc/message.js @@ -10,7 +10,7 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons (function (Drupal) { - Drupal.message = function () { + Drupal.Message = function () { function _class() { var messageWrapper = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; @@ -25,7 +25,7 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!this.messageWrapper) { - this.messageWrapper = Drupal.message.defaultWrapper(); + this.messageWrapper = Drupal.Message.defaultWrapper(); } if (!options.hasOwnProperty('type')) { options.type = 'status'; @@ -35,12 +35,12 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons throw new Error('Message must be a string.'); } - Drupal.message.announce(message, options); + Drupal.Message.announce(message, options); options.id = options.id ? String(options.id) : options.type + '-' + Math.random().toFixed(15).replace('0.', ''); - if (!Drupal.message.getMessageTypeLabels().hasOwnProperty(options.type)) { - throw new Error('The message type, ' + options.type + ', is not present in Drupal.message.getMessageTypeLabels().'); + if (!Drupal.Message.getMessageTypeLabels().hasOwnProperty(options.type)) { + throw new Error('The message type, ' + options.type + ', is not present in Drupal.Message.getMessageTypeLabels().'); } this.messageWrapper.appendChild(Drupal.theme('message', { text: message }, options)); @@ -49,41 +49,22 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons } }, { key: 'select', - value: function select(index) { - if (!index || Array.isArray(index) && index.length === 0) { - return []; - } - - var selectors = (Array.isArray(index) ? index : [index]).map(function (currentIndex) { - return '[data-drupal-message-id^="' + currentIndex + '"]'; - }); - - return this.messageWrapper.querySelectorAll(selectors.join(',')); - } - }, { - key: 'removeElements', - value: function removeElements(elements) { - if (!elements || !elements.length) { - return 0; - } - - var length = elements.length; - for (var i = 0; i < length; i++) { - this.messageWrapper.removeChild(elements[i]); - } - return length; + value: function select(id) { + return this.messageWrapper.querySelector('[data-drupal-message-id^="' + id + '"]'); } }, { key: 'remove', - value: function remove(ids) { - var messages = this.select(ids); - return this.removeElements(messages); + value: function remove(id) { + return this.messageWrapper.removeChild(this.select(id)); } }, { key: 'clear', value: function clear() { - var messages = this.messageWrapper.querySelectorAll('[data-drupal-message-id]'); - return this.removeElements(messages); + var _this = this; + + Array.prototype.forEach.call(this.messageWrapper.querySelectorAll('[data-drupal-message-id]'), function (message) { + _this.messageWrapper.removeChild(message); + }); } }], [{ key: 'defaultWrapper', @@ -95,7 +76,7 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons wrapper.setAttribute('data-drupal-messages', ''); wrapper.removeAttribute('class'); } - return wrapper.innerHTML === '' ? Drupal.message.messageInternalWrapper(wrapper) : wrapper.firstElementChild; + return wrapper.innerHTML === '' ? Drupal.Message.messageInternalWrapper(wrapper) : wrapper.firstElementChild; } }, { key: 'getMessageTypeLabels', @@ -133,7 +114,7 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons Drupal.theme.message = function (_ref, options) { var text = _ref.text; - var messagesTypes = Drupal.message.getMessageTypeLabels(); + var messagesTypes = Drupal.Message.getMessageTypeLabels(); var messageWraper = document.createElement('div'); var messageText = document.createElement('h2'); messageText.setAttribute('class', 'visually-hidden'); diff --git a/core/modules/system/tests/modules/js_message_test/js/js_message_test.es6.js b/core/modules/system/tests/modules/js_message_test/js/js_message_test.es6.js new file mode 100644 index 0000000000..2eb41a21c9 --- /dev/null +++ b/core/modules/system/tests/modules/js_message_test/js/js_message_test.es6.js @@ -0,0 +1,92 @@ +/** + * @file + * Testing behavior for JSMessageTest. + */ + +(($, { behaviors }, { testMessages }) => { + // Message types + const indexes = {}; + testMessages.types.forEach((type) => { + indexes[type] = []; + }); + + // Message storage. + const messageObjects = { + default: { + zone: new Drupal.Message(), + indexes, + }, + multiple: [], + }; + + testMessages.selectors + .filter(Boolean) + .forEach((selector) => { + messageObjects[selector] = { + zone: new Drupal.Message(document.querySelector(selector)), + indexes, + }; + }); + + /** + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Add click listeners that show and remove links with context and type. + */ + behaviors.js_message_test = { + attach() { + $('[data-drupal-messages-area]').once('messages-details').on('click', '[data-action]', (e) => { + const $target = $(e.currentTarget); + const type = $target.attr('data-type'); + const area = $target.closest('[data-drupal-messages-area]').attr('data-drupal-messages-area') || 'default'; + const message = messageObjects[area].zone; + const action = $target.attr('data-action'); + + if (action === 'add') { + messageObjects[area].indexes[type].push( + message.add(`This is a message of the type, ${type}. You be the the judge of its importance.`, { type }), + ); + } + else if (action === 'remove') { + message.remove(messageObjects[area].indexes[type].pop()); + } + }); + $('[data-action="add-multiple"]').once('add-multiple').on('click', () => { + /** + * Add several of different types to make sure message type doesn't + * cause issues in the API. + */ + [0, 1, 2, 3, 4, 5].forEach((i) => { + messageObjects.multiple.push( + messageObjects.default.zone.add( + `This is message number ${i} of the type, ${testMessages.types[i % testMessages.types.length]}. You be the the judge of its importance.`, + { type: testMessages.types[i % testMessages.types.length] }, + ), + ); + }); + }); + $('[data-action="remove-multiple"]').once('remove-multiple').on('click', () => { + messageObjects.multiple + .forEach(messageIndex => messageObjects.default.zone.remove(messageIndex)); + messageObjects.multiple = []; + }); + $('[data-action="add-multiple-error"]').once('add-multiple-error').on('click', () => { + // Use the same number of elements to facilitate things on the PHP side. + [0, 1, 2, 3, 4, 5].forEach(i => messageObjects.default.zone.add(`Msg-${i}`, { type: 'error' })); + messageObjects.default.zone.add(`Msg-${testMessages.types.length * 2}`, { type: 'status' }); + }); + $('[data-action="remove-type"]').once('remove-type').on('click', () => { + Array.prototype.map + .call(document.querySelectorAll('[data-drupal-message-id^="error"]'), element => element.getAttribute('data-drupal-message-id')) + .forEach(id => messageObjects.default.zone.remove(id)); + }); + $('[data-action="clear-all"]').once('clear-all').on('click', () => { + messageObjects.default.zone.clear(); + }); + $('[data-action="id-no-status"]').once('id-no-status').on('click', () => { + messageObjects.default.zone.add('Msg-id-no-status', { id: 'my-special-id' }); + }); + }, + }; +})(jQuery, Drupal, drupalSettings); diff --git a/core/modules/system/tests/modules/js_message_test/js/js_message_test.js b/core/modules/system/tests/modules/js_message_test/js/js_message_test.js index 8f21178ed3..5f48c826ac 100644 --- a/core/modules/system/tests/modules/js_message_test/js/js_message_test.js +++ b/core/modules/system/tests/modules/js_message_test/js/js_message_test.js @@ -1,86 +1,79 @@ /** - * @file - * Testing behavior for JSMessageTest. - */ - -(function ($, Drupal, drupalSettings) { - - 'use strict'; - - var messageObjects = {}; - var messageIndexes = {multiple: []}; - - drupalSettings.testMessages.selectors.forEach(function (selector) { - messageObjects[selector] = new Drupal.message(selector === '' ? undefined : document.querySelector(selector)); - - messageIndexes[selector] = {}; - drupalSettings.testMessages.types.forEach(function (type) { - messageIndexes[selector][type] = []; - }); +* DO NOT EDIT THIS FILE. +* See the following change record for more information, +* https://www.drupal.org/node/2815083 +* @preserve +**/ + +(function ($, _ref, _ref2) { + var behaviors = _ref.behaviors; + var testMessages = _ref2.testMessages; + + var indexes = {}; + testMessages.types.forEach(function (type) { + indexes[type] = []; }); - var defaultMessageArea = messageObjects[drupalSettings.testMessages.selectors[0]]; + var messageObjects = { + default: { + zone: new Drupal.Message(), + indexes: indexes + }, + multiple: [] + }; - /** - * @type {Drupal~behavior} - * - * @prop {Drupal~behaviorAttach} attach - * Add click listeners that show and remove links with context and type. - */ - Drupal.behaviors.js_message_test = { - attach: function (context) { + testMessages.selectors.filter(Boolean).forEach(function (selector) { + messageObjects[selector] = { + zone: new Drupal.Message(document.querySelector(selector)), + indexes: indexes + }; + }); + behaviors.js_message_test = { + attach: function attach() { $('[data-drupal-messages-area]').once('messages-details').on('click', '[data-action]', function (e) { var $target = $(e.currentTarget); var type = $target.attr('data-type'); - var area = $target.closest('[data-drupal-messages-area]').attr('data-drupal-messages-area'); - var message = messageObjects[area]; + var area = $target.closest('[data-drupal-messages-area]').attr('data-drupal-messages-area') || 'default'; + var message = messageObjects[area].zone; var action = $target.attr('data-action'); if (action === 'add') { - messageIndexes[area][type].push(message.add(`This is a message of the type, ${type}. You be the the judge of its importance. 😜`, {type: type})); - } - else if (action === 'remove') { - message.remove(messageIndexes[area][type].pop()); + messageObjects[area].indexes[type].push(message.add('This is a message of the type, ' + type + '. You be the the judge of its importance.', { type: type })); + } else if (action === 'remove') { + message.remove(messageObjects[area].indexes[type].pop()); } }); - - $('[data-action="add-multiple"]').once('add-multiple').on('click', function (e) { - var types = drupalSettings.testMessages.types; - // Add several of different types to make sure message type doesn't - // cause issues in the API. - for (var i = 0; i < types.length * 2; i++) { - messageIndexes.multiple.push(defaultMessageArea.add(`This is message number ${i} of the type, ${types[i % types.length]}. You be the the judge of its importance. 😜`, {type: types[i % types.length]})); - } + $('[data-action="add-multiple"]').once('add-multiple').on('click', function () { + [0, 1, 2, 3, 4, 5].forEach(function (i) { + messageObjects.multiple.push(messageObjects.default.zone.add('This is message number ' + i + ' of the type, ' + testMessages.types[i % testMessages.types.length] + '. You be the the judge of its importance.', { type: testMessages.types[i % testMessages.types.length] })); + }); }); - - $('[data-action="remove-multiple"]').once('remove-multiple').on('click', function (e) { - defaultMessageArea.remove(messageIndexes.multiple); - messageIndexes.multiple = []; + $('[data-action="remove-multiple"]').once('remove-multiple').on('click', function () { + messageObjects.multiple.forEach(function (messageIndex) { + return messageObjects.default.zone.remove(messageIndex); + }); + messageObjects.multiple = []; }); - - $('[data-action="add-multiple-error"]').once('add-multiple-error').on('click', function (e) { - // Use the same number of elements to facilitate things on the PHP side. - var total = drupalSettings.testMessages.types.length * 2; - for (var i = 0; i < total; i++) { - defaultMessageArea.add('Msg-' + i, {type: 'error'}); - } - defaultMessageArea.add('Msg-' + total, {type: 'status'}); + $('[data-action="add-multiple-error"]').once('add-multiple-error').on('click', function () { + [0, 1, 2, 3, 4, 5].forEach(function (i) { + return messageObjects.default.zone.add('Msg-' + i, { type: 'error' }); + }); + messageObjects.default.zone.add('Msg-' + testMessages.types.length * 2, { type: 'status' }); }); - - $('[data-action="remove-type"]').once('remove-type').on('click', function (e) { - defaultMessageArea.remove('error'); + $('[data-action="remove-type"]').once('remove-type').on('click', function () { + Array.prototype.map.call(document.querySelectorAll('[data-drupal-message-id^="error"]'), function (element) { + return element.getAttribute('data-drupal-message-id'); + }).forEach(function (id) { + return messageObjects.default.zone.remove(id); + }); }); - - $('[data-action="clear-all"]').once('clear-all').on('click', function (e) { - defaultMessageArea.clear(); + $('[data-action="clear-all"]').once('clear-all').on('click', function () { + messageObjects.default.zone.clear(); }); - - $('[data-action="id-no-status"]').once('id-no-status').on('click', function (e) { - defaultMessageArea.add('Msg-id-no-status', {id: 'my-special-id'}); + $('[data-action="id-no-status"]').once('id-no-status').on('click', function () { + messageObjects.default.zone.add('Msg-id-no-status', { id: 'my-special-id' }); }); - } }; - -})(jQuery, Drupal, drupalSettings); +})(jQuery, Drupal, drupalSettings); \ No newline at end of file diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Core/JsMessageTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Core/JsMessageTest.php index ca21be0551..14b4e8b625 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/Core/JsMessageTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/JsMessageTest.php @@ -45,8 +45,8 @@ public function testAddRemoveMessages() { $selector = "$messagesSelector .messages.messages--$type"; $msg_element = $web_assert->waitForElementVisible('css', $selector); $this->assertNotEmpty($msg_element, "Message element visible: $selector"); - $web_assert->elementContains('css', $selector, "Msg-$type"); - $current_messages[$selector] = ucfirst($type) . " message Msg-$type"; + $web_assert->elementContains('css', $selector, "This is a message of the type, $type. You be the the judge of its importance."); + $current_messages[$selector] = ucfirst($type) . " message This is a message of the type, $type. You be the the judge of its importance."; $this->assertCurrentMessages($current_messages, $messagesSelector); } // Remove messages 1 by 1 and confirm the messages are expected. @@ -64,7 +64,7 @@ public function testAddRemoveMessages() { $types = JsMessageTestCases::getTypes(); $nb_messages = count($types) * 2; for ($i = 0; $i < $nb_messages; $i++) { - $current_messages[] = ucfirst($types[$i % count($types)]) . " message Msg-$i"; + $current_messages[] = ucfirst($types[$i % count($types)]) . " message This is message number $i of the type, {$types[$i % count($types)]}. You be the the judge of its importance."; } // Test adding multiple messages at once. // @see processMessages()