mirror of
https://github.com/overleaf/overleaf.git
synced 2025-02-28 22:42:13 +00:00
Merge pull request #5086 from overleaf/jpa-rework-de-ng-validation
[web] input-validator: rework of content and behavior GitOrigin-RevId: 276c23c651d3954d7e82415b5315907600c8e0e1
This commit is contained in:
parent
6cece12369
commit
895c93d8f2
2 changed files with 59 additions and 60 deletions
|
@ -1,6 +1,7 @@
|
|||
import classNames from 'classnames'
|
||||
import { FetchError, postJSON } from '../../infrastructure/fetch-json'
|
||||
import { validateCaptchaV2 } from './captcha'
|
||||
import inputValidator from './input-validator'
|
||||
|
||||
// Form helper(s) to handle:
|
||||
// - Attaching to the relevant form elements
|
||||
|
@ -145,6 +146,15 @@ export function hydrateForm(el) {
|
|||
formSubmitHelper(el)
|
||||
formInflightHelper(el)
|
||||
formSentHelper(el)
|
||||
|
||||
el.querySelectorAll('input').forEach(inputEl => {
|
||||
if (
|
||||
inputEl.willValidate &&
|
||||
!inputEl.hasAttribute('data-ol-no-custom-form-validation-messages')
|
||||
) {
|
||||
inputValidator(inputEl)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
document.querySelectorAll(`[data-ol-form]`).forEach(form => hydrateForm(form))
|
||||
|
|
|
@ -1,72 +1,61 @@
|
|||
export default function inputValidator(options) {
|
||||
const { selector } = options
|
||||
export default function inputValidator(inputEl) {
|
||||
const messageEl = document.createElement('span')
|
||||
messageEl.className =
|
||||
inputEl.getAttribute('data-ol-validation-message-classes') ||
|
||||
'small text-danger'
|
||||
messageEl.hidden = true
|
||||
inputEl.insertAdjacentElement('afterend', messageEl)
|
||||
|
||||
const inputEl = document.querySelector(selector)
|
||||
// Hide messages until the user leaves the input field or submits the form.
|
||||
let canDisplayErrorMessages = false
|
||||
|
||||
inputEl.addEventListener('input', markDirty)
|
||||
inputEl.addEventListener('change', markDirty)
|
||||
inputEl.addEventListener('blur', insertInvalidMessage)
|
||||
// Handle all kinds of inputs.
|
||||
inputEl.addEventListener('input', handleUpdate)
|
||||
inputEl.addEventListener('change', handleUpdate)
|
||||
|
||||
// Mark an input as "dirty": the user has typed something in at some point
|
||||
function markDirty() {
|
||||
// Note: this is used for the input styling as well as checks when inserting invalid
|
||||
// message below
|
||||
inputEl.dataset.olDirty = true
|
||||
// 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 => {
|
||||
// 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 insertInvalidMessage() {
|
||||
if (!inputEl.validity.valid) {
|
||||
// Already have a invalid message, don't insert another
|
||||
if (inputEl._invalid_message_el) return
|
||||
function displayValidationMessages() {
|
||||
// Display all the error messages and highlight fields with red border.
|
||||
canDisplayErrorMessages = true
|
||||
|
||||
// Only show the message if the input is "dirty"
|
||||
if (!inputEl.dataset.olDirty) return
|
||||
updateValidationMessageContent()
|
||||
}
|
||||
|
||||
const messageEl = createMessageEl({
|
||||
message: getMessage(inputEl),
|
||||
...options,
|
||||
})
|
||||
inputEl.insertAdjacentElement('afterend', messageEl)
|
||||
function updateValidationMessageContent() {
|
||||
if (!canDisplayErrorMessages) return
|
||||
if (!inputEl.hasAttribute('data-ol-dirty')) return
|
||||
|
||||
// Add a reference so we can remove the element when the input becomes valid
|
||||
inputEl._invalid_message_el = messageEl
|
||||
if (inputEl.validity.valid) {
|
||||
messageEl.hidden = true
|
||||
|
||||
// Require another blur before displaying errors again.
|
||||
canDisplayErrorMessages = false
|
||||
} else {
|
||||
if (!inputEl._invalid_message_el) return
|
||||
|
||||
// Remove the message element
|
||||
inputEl._invalid_message_el.remove()
|
||||
// Clean up the reference
|
||||
delete inputEl._invalid_message_el
|
||||
messageEl.textContent = inputEl.validationMessage
|
||||
messageEl.hidden = false
|
||||
}
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
inputEl.removeEventListener('input change', markDirty)
|
||||
inputEl.removeEventListener('blue', insertInvalidMessage)
|
||||
delete inputEl._invalid_message_el
|
||||
delete inputEl.dataset.olDirty
|
||||
}
|
||||
|
||||
return cleanUp
|
||||
}
|
||||
|
||||
function createMessageEl({ message, messageClasses = [] }) {
|
||||
const el = document.createElement('span')
|
||||
// From what I understand, using textContent means that we're safe from XSS
|
||||
el.textContent = message
|
||||
el.classList.add(...messageClasses)
|
||||
|
||||
return el
|
||||
}
|
||||
|
||||
function getMessage(el) {
|
||||
// Could be extended to all ValidityState properties: https://developer.mozilla.org/en-US/docs/Web/API/ValidityState
|
||||
const { valueMissing, typeMismatch } = el.validity
|
||||
if (valueMissing) {
|
||||
return el.dataset.olInvalidValueMissing || 'Missing required value'
|
||||
} else if (typeMismatch) {
|
||||
return el.dataset.olInvalidTypeMismatch || 'Invalid type' // FIXME: Bad default
|
||||
} else {
|
||||
return 'Invalid'
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue