diff -u b/core/misc/message.es6.js b/core/misc/message.es6.js --- b/core/misc/message.es6.js +++ b/core/misc/message.es6.js @@ -2,7 +2,7 @@ * @file * Message API. */ -(((Drupal) => { +((Drupal) => { /** * @typedef {object} Drupal.message~messageDefinition */ @@ -44,14 +44,14 @@ * * @param {string} message * The message to display - * @param {string} [type=status] - * Message type, can be either 'status', 'error' or 'warning'. * @param {object} [options] * The context of the message, used for removing messages again. * @param {string} [options.id] * The message ID, it can be a simple value: `'filevalidationerror'` * or several values separated by a space: `'mymodule formvalidation'` * which can be used as a sort of tag for message deletion. + * @param {string} [options.type=status] + * Message type, can be either 'status', 'error' or 'warning'. * @param {string} [options.announce] * Screen-reader version of the message if necessary. To prevent a message * being sent to Drupal.announce() this should be `''`. @@ -61,17 +61,19 @@ * @return {string} * ID of message. */ - add(message, type = 'status', options = {}) { + add(message, options = { type: 'status' }) { if (typeof message !== 'string') { throw new Error('Message must be a string.'); } // Send message to screen reader. - this.announce(message, type, options); + this.announce(message, options); // Use the provided index for the message or generate a unique key to // allow message deletion. - options.id = `${type} ${options.id || Math.random().toFixed(15).replace('0.', '')}`; - this.messageWrapper.appendChild(Drupal.theme('message', { text: message, type }, options)); + options.id = options.id ? + String(options.id) : + `${options.type}-${Math.random().toFixed(15).replace('0.', '')}`; + this.messageWrapper.appendChild(Drupal.theme('message', { text: message }, options)); return options.id; } @@ -100,7 +102,7 @@ // class names work. currentIndex.trim() .split(whitespace) - .map(i => `[data-drupal-message~="${i}"]`) + .map(i => `[data-drupal-message-id^="${i}"]`) .join(''), ); @@ -133,7 +135,7 @@ * * @name Drupal.message~messageDefinition.remove * - * @param {string|Array.} options + * @param {string|Array.} ids * Index of the message to remove, as returned by * {@link Drupal.message~messageDefinition.add}, or an * array of indexes. @@ -141,8 +143,8 @@ * @return {number} * Number of removed messages. */ - remove(options) { - const messages = this.select(options); + remove(ids) { + const messages = this.select(ids); return this.removeElements(messages); } @@ -155,7 +157,7 @@ * Number of removed messages. */ clear() { - const messages = this.messageWrapper.querySelectorAll('[data-drupal-message]'); + const messages = this.messageWrapper.querySelectorAll('[data-drupal-message-id]'); return this.removeElements(messages); } @@ -164,8 +166,6 @@ * * @param {string} message * Displayed message. - * @param {string} type - * Message type, can be either 'status', 'error' or 'warning'. * @param {object} options * Additional data. * @param {string} [options.announce] @@ -173,9 +173,11 @@ * being sent to Drupal.announce() this should be `''`. * @param {string} [options.priority] * Priority of the message for Drupal.announce(). + * @param {string} [options.type] + * Message type, can be either 'status', 'error' or 'warning'. */ - announce(message, type, options) { - if (!options.priority && (type === 'warning' || type === 'error')) { + announce(message, options) { + if (!options.priority && (options.type === 'warning' || options.type === 'error')) { options.priority = 'assertive'; } // If screen reader message is not disabled announce screen reader specific @@ -193,10 +195,10 @@ * The message object. * @param {string} message.text * The message text. - * @param {string} message.type - * The message type. * @param {object} options * The message context. + * @param {string} options.type + * The message type. * @param {string} options.id * ID of the message, for reference. * @@ -207,16 +209,17 @@ const messagesTypes = Drupal.message.getMessageTypes(); const messageWraper = document.createElement('div'); let messageText = message.text; - messageWraper.setAttribute('class', `messages messages--${message.type}`); + messageWraper.setAttribute('class', `messages messages--${options.type}`); messageWraper.setAttribute('role', 'contentinfo'); - messageWraper.setAttribute('data-drupal-message', options.id); - if (message.type in messagesTypes) { - messageWraper.setAttribute('aria-label', messagesTypes[message.type]); - messageText = `

${messagesTypes[message.type]}

\n${message.text}`; + messageWraper.setAttribute('data-drupal-message-id', options.id); + messageWraper.setAttribute('data-drupal-message-type', options.type); + if (options.type in messagesTypes) { + messageWraper.setAttribute('aria-label', messagesTypes[options.type]); + messageText = `

${messagesTypes[options.type]}

\n${message.text}`; } // Alerts have a different HTML structure - if (message.type === 'error') { + if (options.type === 'error') { const ariaAlert = document.createElement('div'); ariaAlert.setAttribute('role', 'alert'); ariaAlert.innerHTML = messageText; @@ -231 +234 @@ -})(Drupal)); +})(Drupal); diff -u b/core/misc/message.js b/core/misc/message.js --- b/core/misc/message.js +++ b/core/misc/message.js @@ -25,17 +25,16 @@ _createClass(Message, [{ key: 'add', value: function add(message) { - var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'status'; - var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { type: 'status' }; if (typeof message !== 'string') { throw new Error('Message must be a string.'); } - this.announce(message, type, options); + this.announce(message, options); - options.id = type + ' ' + (options.id || Math.random().toFixed(15).replace('0.', '')); - this.messageWrapper.appendChild(Drupal.theme('message', { text: message, type: type }, options)); + options.id = options.id ? String(options.id) : options.type + '-' + Math.random().toFixed(15).replace('0.', ''); + this.messageWrapper.appendChild(Drupal.theme('message', { text: message }, options)); return options.id; } @@ -51,7 +50,7 @@ var selectors = indexes.map(function (currentIndex) { return currentIndex.trim().split(whitespace).map(function (i) { - return '[data-drupal-message~="' + i + '"]'; + return '[data-drupal-message-id^="' + i + '"]'; }).join(''); }); @@ -72,20 +71,20 @@ } }, { key: 'remove', - value: function remove(options) { - var messages = this.select(options); + value: function remove(ids) { + var messages = this.select(ids); return this.removeElements(messages); } }, { key: 'clear', value: function clear() { - var messages = this.messageWrapper.querySelectorAll('[data-drupal-message]'); + var messages = this.messageWrapper.querySelectorAll('[data-drupal-message-id]'); return this.removeElements(messages); } }, { key: 'announce', - value: function announce(message, type, options) { - if (!options.priority && (type === 'warning' || type === 'error')) { + value: function announce(message, options) { + if (!options.priority && (options.type === 'warning' || options.type === 'error')) { options.priority = 'assertive'; } @@ -111,15 +110,16 @@ var messagesTypes = Drupal.message.getMessageTypes(); var messageWraper = document.createElement('div'); var messageText = message.text; - messageWraper.setAttribute('class', 'messages messages--' + message.type); + messageWraper.setAttribute('class', 'messages messages--' + options.type); messageWraper.setAttribute('role', 'contentinfo'); - messageWraper.setAttribute('data-drupal-message', options.id); - if (message.type in messagesTypes) { - messageWraper.setAttribute('aria-label', messagesTypes[message.type]); - messageText = '

' + messagesTypes[message.type] + '

\n' + message.text; + messageWraper.setAttribute('data-drupal-message-id', options.id); + messageWraper.setAttribute('data-drupal-message-type', options.type); + if (options.type in messagesTypes) { + messageWraper.setAttribute('aria-label', messagesTypes[options.type]); + messageText = '

' + messagesTypes[options.type] + '

\n' + message.text; } - if (message.type === 'error') { + if (options.type === 'error') { var ariaAlert = document.createElement('div'); ariaAlert.setAttribute('role', 'alert'); ariaAlert.innerHTML = messageText; diff -u b/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 --- b/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 @@ -38,7 +38,7 @@ var action = $target.attr('data-action'); if (action === 'add') { - messageIndexes[area][type].push(message.add('Msg-' + type, type)); + messageIndexes[area][type].push(message.add('Msg-' + type, {type: type})); } else if (action === 'remove') { message.remove(messageIndexes[area][type].pop()); @@ -50,7 +50,7 @@ // 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('Msg-' + i, types[i % types.length])); + messageIndexes.multiple.push(defaultMessageArea.add('Msg-' + i, {type: types[i % types.length]})); } }); @@ -63,9 +63,9 @@ // 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, 'error'); + defaultMessageArea.add('Msg-' + i, {type: 'error'}); } - defaultMessageArea.add('Msg-' + total, 'status'); + defaultMessageArea.add('Msg-' + total, {type: 'status'}); }); $('[data-action="remove-type"]').once('remove-type').on('click', function (e) {