input-validator.ts 2.9 KB

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