input-validator.ts 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  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. if (inputEl.validity.valid) {
  62. messageEl.hidden = true
  63. // Require another blur before displaying errors again.
  64. canDisplayErrorMessages = false
  65. } else {
  66. messageTextNode.data = inputEl.validationMessage
  67. messageEl.hidden = false
  68. }
  69. }
  70. }