diff --git a/services/web/frontend/js/features/form-helpers/hydrate-form.js b/services/web/frontend/js/features/form-helpers/hydrate-form.js index 9c00dd43ae..3febf861b7 100644 --- a/services/web/frontend/js/features/form-helpers/hydrate-form.js +++ b/services/web/frontend/js/features/form-helpers/hydrate-form.js @@ -3,6 +3,7 @@ import { FetchError, postJSON } from '../../infrastructure/fetch-json' import { canSkipCaptcha, validateCaptchaV2 } from './captcha' import inputValidator from './input-validator' import { disableElement, enableElement } from '../utils/disableElement' +import { isBootstrap5 } from '@/features/utils/bootstrap-5' // Form helper(s) to handle: // - Attaching to the relevant form elements @@ -133,6 +134,66 @@ function hideFormElements(formEl) { } } +/** + * Creates a notification element from a message object, with BS5 classes. + * + * @param {Object} message + * @param {'error' | 'success' | 'warning' | 'info'} message.type + * @param {string} message.key + * @param {string} message.text + * @param {string[]} message.hints + * @returns {HTMLDivElement} + */ +function createNotificationFromMessageBS5(message) { + const messageEl = document.createElement('div') + messageEl.className = classNames('mb-3 notification', { + 'notification-type-error': message.type === 'error', + 'notification-type-success': message.type === 'success', + 'notification-type-warning': message.type === 'warning', + 'notification-type-info': message.type === 'info', + }) + messageEl.setAttribute('aria-live', 'assertive') + messageEl.setAttribute('role', message.type === 'error' ? 'alert' : 'status') + + const materialIcon = { + info: 'info', + success: 'check_circle', + error: 'error', + warning: 'warning', + }[message.type] + if (materialIcon) { + const iconEl = document.createElement('div') + iconEl.className = 'notification-icon' + const iconSpan = document.createElement('span') + iconSpan.className = 'material-symbols' + iconSpan.setAttribute('aria-hidden', 'true') + iconSpan.textContent = materialIcon + iconEl.append(iconSpan) + messageEl.append(iconEl) + } + + const contentAndCtaEl = document.createElement('div') + contentAndCtaEl.className = 'notification-content-and-cta' + + const contentEl = document.createElement('div') + contentEl.className = 'notification-content' + contentEl.append(message.text || `Error: ${message.key}`) + + if (message.hints && message.hints.length) { + const listEl = document.createElement('ul') + message.hints.forEach(hint => { + const listItemEl = document.createElement('li') + listItemEl.textContent = hint + listEl.append(listItemEl) + }) + contentEl.append(listEl) + } + contentAndCtaEl.append(contentEl) + messageEl.append(contentAndCtaEl) + + return messageEl +} + // TODO: remove the showMessages function after every form alerts are updated to use the new style // TODO: rename showMessagesNewStyle to showMessages after the above is done function showMessages(formEl, messageBag) { @@ -157,6 +218,9 @@ function showMessages(formEl, messageBag) { customErrorElements.forEach(el => { el.hidden = false }) + } else if (isBootstrap5()) { + const notification = createNotificationFromMessageBS5(message) + messagesEl.append(notification) } else { // No custom error element for key on page, append a new error message const messageEl = document.createElement('div')