You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
8.2 KiB
267 lines
8.2 KiB
/** |
|
* @file |
|
* Message API. |
|
*/ |
|
((Drupal) => { |
|
/** |
|
* @typedef {class} Drupal.Message~messageDefinition |
|
*/ |
|
|
|
/** |
|
* Constructs a new instance of the Drupal.Message class. |
|
* |
|
* This provides a uniform interface for adding and removing messages to a |
|
* specific location on the page. |
|
* |
|
* @param {HTMLElement} messageWrapper |
|
* The zone where to add messages. If no element is provided an attempt is |
|
* made to determine a default location. |
|
* |
|
* @return {Drupal.Message~messageDefinition} |
|
* Class to add and remove messages. |
|
*/ |
|
Drupal.Message = class { |
|
constructor(messageWrapper = null) { |
|
if (!messageWrapper) { |
|
this.messageWrapper = Drupal.Message.defaultWrapper(); |
|
} else { |
|
this.messageWrapper = messageWrapper; |
|
} |
|
} |
|
|
|
/** |
|
* Attempt to determine the default location for |
|
* inserting JavaScript messages or create one if needed. |
|
* |
|
* @return {HTMLElement} |
|
* The default destination for JavaScript messages. |
|
*/ |
|
static defaultWrapper() { |
|
// Search for the element with '[data-drupal-messages]' selector. |
|
// If not found then only try to search for fallback element. |
|
let wrapper = |
|
document.querySelector('[data-drupal-messages]') || |
|
document.querySelector('[data-drupal-messages-fallback]'); |
|
if (!wrapper) { |
|
// If no status messages element is found, a fallback element is created to prevent |
|
// execution-breaking JS errors when attempting to report a problem. |
|
// This scenario can occur on any page that does not include a status_messages |
|
// render element. |
|
wrapper = document.createElement('div'); |
|
document.body.appendChild(wrapper); |
|
} |
|
|
|
if (wrapper.hasAttribute('data-drupal-messages-fallback')) { |
|
// Remove the fallback attribute if it exists. |
|
wrapper.removeAttribute('data-drupal-messages-fallback'); |
|
wrapper.classList.remove('hidden'); |
|
} |
|
wrapper.setAttribute('data-drupal-messages', ''); |
|
|
|
return wrapper.innerHTML === '' |
|
? Drupal.Message.messageInternalWrapper(wrapper) |
|
: wrapper.firstElementChild; |
|
} |
|
|
|
/** |
|
* Provide an object containing the available message types. |
|
* |
|
* @return {Object} |
|
* An object containing message type strings. |
|
*/ |
|
static getMessageTypeLabels() { |
|
return { |
|
status: Drupal.t('Status message'), |
|
error: Drupal.t('Error message'), |
|
warning: Drupal.t('Warning message'), |
|
}; |
|
} |
|
|
|
/** |
|
* Sequentially adds a message to the message area. |
|
* |
|
* @name Drupal.Message~messageDefinition.add |
|
* |
|
* @param {string} message |
|
* The message to display |
|
* @param {object} [options] |
|
* The context of the message. |
|
* @param {string} [options.id] |
|
* The message ID, it can be a simple value: `'file_validation_error'` |
|
* or several values separated by a space: `'my_module form_validation'` |
|
* which can be used as an explicit selector for a message. |
|
* @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 an empty string. |
|
* @param {string} [options.priority] |
|
* Priority of the message for Drupal.announce(). |
|
* |
|
* @return {string} |
|
* ID of message. |
|
*/ |
|
add(message, options = {}) { |
|
if (!options.hasOwnProperty('type')) { |
|
options.type = 'status'; |
|
} |
|
|
|
if (typeof message !== 'string') { |
|
throw new Error('Message must be a string.'); |
|
} |
|
|
|
// Send message to screen reader. |
|
Drupal.Message.announce(message, options); |
|
/** |
|
* Use the provided index for the message or generate a pseudo-random key |
|
* to allow message deletion. |
|
*/ |
|
options.id = options.id |
|
? String(options.id) |
|
: `${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)) { |
|
const { type } = options; |
|
throw new Error( |
|
`The message type, ${type}, is not present in Drupal.Message.getMessageTypeLabels().`, |
|
); |
|
} |
|
|
|
this.messageWrapper.appendChild( |
|
Drupal.theme('message', { text: message }, options), |
|
); |
|
|
|
return options.id; |
|
} |
|
|
|
/** |
|
* Select a message based on id. |
|
* |
|
* @name Drupal.Message~messageDefinition.select |
|
* |
|
* @param {string} id |
|
* The message id to delete from the area. |
|
* |
|
* @return {Element} |
|
* Element found. |
|
*/ |
|
select(id) { |
|
return this.messageWrapper.querySelector( |
|
`[data-drupal-message-id^="${id}"]`, |
|
); |
|
} |
|
|
|
/** |
|
* Removes a message element from the message area. |
|
* |
|
* @name Drupal.Message~messageDefinition.remove |
|
* |
|
* @param {string} id |
|
* The unique identifier of the message to remove, as returned by |
|
* {@link Drupal.Message~messageDefinition.add}. |
|
* |
|
* @return {Element} |
|
* Returns the removed message element. |
|
*/ |
|
remove(id) { |
|
return this.messageWrapper.removeChild(this.select(id)); |
|
} |
|
|
|
/** |
|
* Removes all messages from the message area. |
|
* |
|
* @name Drupal.Message~messageDefinition.clear |
|
*/ |
|
clear() { |
|
this.messageWrapper |
|
.querySelectorAll('[data-drupal-message-id]') |
|
.forEach((message) => { |
|
this.messageWrapper.removeChild(message); |
|
}); |
|
} |
|
|
|
/** |
|
* Helper to call Drupal.announce() with the right parameters. |
|
* |
|
* @param {string} message |
|
* Displayed message. |
|
* @param {object} options |
|
* Additional data. |
|
* @param {string} [options.announce] |
|
* Screen-reader version of the message if necessary. To prevent a message |
|
* 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'. |
|
*/ |
|
static 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 text or fallback to the displayed message. |
|
*/ |
|
if (options.announce !== '') { |
|
Drupal.announce(options.announce || message, options.priority); |
|
} |
|
} |
|
|
|
/** |
|
* Function for creating the internal message wrapper element. |
|
* |
|
* @param {HTMLElement} messageWrapper |
|
* The message wrapper. |
|
* |
|
* @return {HTMLElement} |
|
* The internal wrapper DOM element. |
|
*/ |
|
static messageInternalWrapper(messageWrapper) { |
|
const innerWrapper = document.createElement('div'); |
|
innerWrapper.setAttribute('class', 'messages__wrapper'); |
|
messageWrapper.insertAdjacentElement('afterbegin', innerWrapper); |
|
return innerWrapper; |
|
} |
|
}; |
|
|
|
/** |
|
* Theme function for a message. |
|
* |
|
* @param {object} message |
|
* The message object. |
|
* @param {string} message.text |
|
* The message text. |
|
* @param {object} options |
|
* The message context. |
|
* @param {string} options.type |
|
* The message type. |
|
* @param {string} options.id |
|
* ID of the message, for reference. |
|
* |
|
* @return {HTMLElement} |
|
* A DOM Node. |
|
*/ |
|
Drupal.theme.message = ({ text }, { type, id }) => { |
|
const messagesTypes = Drupal.Message.getMessageTypeLabels(); |
|
const messageWrapper = document.createElement('div'); |
|
|
|
messageWrapper.setAttribute('class', `messages messages--${type}`); |
|
messageWrapper.setAttribute( |
|
'role', |
|
type === 'error' || type === 'warning' ? 'alert' : 'status', |
|
); |
|
messageWrapper.setAttribute('data-drupal-message-id', id); |
|
messageWrapper.setAttribute('data-drupal-message-type', type); |
|
|
|
messageWrapper.setAttribute('aria-label', messagesTypes[type]); |
|
|
|
messageWrapper.innerHTML = `${text}`; |
|
|
|
return messageWrapper; |
|
}; |
|
})(Drupal);
|
|
|