Files
overleaf-cep/services/web/frontend/js/features/form-helpers/input-validator.ts
Tim Down 00f6a1e0f9 CIAM registration form buttons, inputs and fixes (#29740)
* Many fixes to CIAM registration form, including Phosphor icons

* Unify layout between Pug and React, fixes for spacing and mobile screen sizes

* Pug lint fix

* Make CIAM footer links underlined

* Add CIAM error notification styling

* Merge duplicate style rules

* Remove outdated comment

* Fix ordering of en.json

* Move aria-label to buttons

* Move full stop into translation string

* Remove dummy password strength indicator

* CIAM spacing and label fixes

* Header logo fixes from review

* Add aria-hidden to error icon

GitOrigin-RevId: 87c8181566f0878256b8010f95f115ec25c7ceb9
2025-11-24 09:06:40 +00:00

94 lines
2.9 KiB
TypeScript

import { materialIcon } from '@/features/utils/material-icon'
import classNames from 'classnames'
import '@phosphor-icons/webcomponents/PhWarningCircle'
function dsErrorIcon() {
const icon = document.createElement('ph-warning-circle')
icon.className = 'ciam-form-text-icon'
icon.ariaHidden = 'true'
return icon
}
export default function inputValidator(
inputEl: HTMLInputElement | HTMLTextAreaElement
) {
const isDsBranded = inputEl.classList.contains('form-control-ds')
const messageEl = document.createElement('div')
messageEl.className =
inputEl.getAttribute('data-ol-validation-message-classes') ||
classNames(
'small text-danger mt-2 form-text',
{ 'form-text-ds': isDsBranded }
)
messageEl.hidden = true
const messageInnerEl = messageEl.appendChild(document.createElement('span'))
messageInnerEl.className = classNames('form-text-inner', {
'form-text-inner-ds': isDsBranded,
})
const messageTextNode = document.createTextNode('')
const iconEl = isDsBranded ? dsErrorIcon() : materialIcon('error')
messageInnerEl.append(iconEl)
messageInnerEl.append(messageTextNode)
const inputContainerEl =
inputEl.closest('.form-complex-input-container') || inputEl
inputContainerEl.insertAdjacentElement('afterend', messageEl)
// Hide messages until the user leaves the input field or submits the form.
let canDisplayErrorMessages = false
// Handle all kinds of inputs.
inputEl.addEventListener('input', handleUpdate)
inputEl.addEventListener('change', handleUpdate)
// The user has left the input field.
inputEl.addEventListener('blur', displayValidationMessages)
// The user has submitted the form and the current field has errors.
inputEl.addEventListener('invalid', (e: Event) => {
// Block the display of browser error messages.
e.preventDefault()
// Force the display of messages.
inputEl.setAttribute('data-ol-dirty', '')
displayValidationMessages()
})
function handleUpdate() {
// Mark an input as "dirty": the user has typed something in at some point
inputEl.setAttribute('data-ol-dirty', '')
// Provide live updates to content sensitive error message like this:
// Please include an '@' in the email address. 'foo' is missing an '@'.
// We should not leave a stale message as the user types.
updateValidationMessageContent()
}
function displayValidationMessages() {
// Display all the error messages and highlight fields with red border.
canDisplayErrorMessages = true
updateValidationMessageContent()
}
function updateValidationMessageContent() {
if (!canDisplayErrorMessages) return
if (!inputEl.hasAttribute('data-ol-dirty')) return
if (inputEl.validity.valid) {
messageEl.hidden = true
// Require another blur before displaying errors again.
canDisplayErrorMessages = false
} else {
messageTextNode.data = inputEl.validationMessage
messageEl.hidden = false
}
}
}