input-validator.ts 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import { materialIcon } from '@/features/utils/material-icon'
  2. import classNames from 'classnames'
  3. import '@phosphor-icons/webcomponents/PhWarningCircle'
  4. import { ciamIcon } from '@/features/utils/ciam-icon'
  5. export default function inputValidator(
  6. inputEl: HTMLInputElement | HTMLTextAreaElement
  7. ) {
  8. const isDsBranded = inputEl.classList.contains('form-control-ds')
  9. const messageEl = document.createElement('div')
  10. messageEl.className =
  11. inputEl.getAttribute('data-ol-validation-message-classes') ||
  12. classNames(
  13. 'small text-danger mt-2 form-text',
  14. { 'form-text-ds': isDsBranded }
  15. )
  16. messageEl.hidden = true
  17. const messageInnerEl = messageEl.appendChild(document.createElement('span'))
  18. messageInnerEl.className = classNames('form-text-inner', {
  19. 'form-text-inner-ds': isDsBranded,
  20. })
  21. const messageTextNode = document.createTextNode('')
  22. const iconEl = isDsBranded
  23. ? ciamIcon('error', 'ciam-form-text-icon')
  24. : materialIcon('error')
  25. messageInnerEl.append(iconEl)
  26. messageInnerEl.append(messageTextNode)
  27. const inputContainerEl =
  28. inputEl.closest('.form-complex-input-container') || inputEl
  29. inputContainerEl.insertAdjacentElement('afterend', messageEl)
  30. // Hide messages until the user leaves the input field or submits the form.
  31. let canDisplayErrorMessages = false
  32. // Handle all kinds of inputs.
  33. inputEl.addEventListener('input', handleUpdate)
  34. inputEl.addEventListener('change', handleUpdate)
  35. // The user has left the input field.
  36. inputEl.addEventListener('blur', displayValidationMessages)
  37. // The user has submitted the form and the current field has errors.
  38. inputEl.addEventListener('invalid', (e: Event) => {
  39. // Block the display of browser error messages.
  40. e.preventDefault()
  41. // Force the display of messages.
  42. inputEl.setAttribute('data-ol-dirty', '')
  43. displayValidationMessages()
  44. })
  45. function handleUpdate() {
  46. // Mark an input as "dirty": the user has typed something in at some point
  47. inputEl.setAttribute('data-ol-dirty', '')
  48. // Provide live updates to content sensitive error message like this:
  49. // Please include an '@' in the email address. 'foo' is missing an '@'.
  50. // We should not leave a stale message as the user types.
  51. updateValidationMessageContent()
  52. }
  53. function displayValidationMessages() {
  54. // Display all the error messages and highlight fields with red border.
  55. canDisplayErrorMessages = true
  56. updateValidationMessageContent()
  57. }
  58. function updateValidationMessageContent() {
  59. if (!canDisplayErrorMessages) return
  60. if (!inputEl.hasAttribute('data-ol-dirty')) return
  61. inputEl.classList.toggle('is-invalid', !inputEl.validity.valid)
  62. if (inputEl.validity.valid) {
  63. messageEl.hidden = true
  64. // Require another blur before displaying errors again.
  65. canDisplayErrorMessages = false
  66. } else {
  67. messageTextNode.data = inputEl.validationMessage
  68. messageEl.hidden = false
  69. }
  70. }
  71. }