From f0856c862f0939ceb3635eefa8bd557a9d213d3a Mon Sep 17 00:00:00 2001 From: Antoine Clausse Date: Tue, 6 May 2025 16:18:02 +0200 Subject: [PATCH] [web] Migrate `two-factor-authentication` module to BS5 (#25181) * Delete unused file error.pug * Revert-me: Enable 2FA locally * Migrate 2FA pages to BS5 * Add BS5 notification classes to hydrate-form.js * Revert "Revert-me: Enable 2FA locally" This reverts commit 2874bedb05e579623e5beb6cf518aa8608808802. * Fix: Re-add .text-capitalize on button * Use `notification` mixin for success state * Append complex notifications with icons in `showMessages` * Keep the BS3 version of the notification in `showMessages`, move the BS5 implementation to `createNotificationFromMessageBS5` Check the Boostrap version with `!window?.Frontend?.['bootstrap-3']`, which is a bit hacky * Update breakpoings in 2FA form to leave more room for error notification * Address PR comments: * Remove useless `createTextNode` * Use `isBootstrap5` * `Setup` -> `Set Up` GitOrigin-RevId: d7285853ea1191da7711b7bada8d65ff064bc27d --- .../js/features/form-helpers/hydrate-form.js | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) 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')