<div class="n7-background-02 p-6 border n7-border-gray-01">
    <div class="pb-6 space-y-4 border-b n7-border-gray-01">
        <p>I campi obbligatori sono contrassegnati da un asterisco
            <abbr class="font-bold no-underline" title="obbligatorio">*</abbr>
        </p>
    </div>

    <form novalidate id="hasValidationForm" class="has-js-validation-form py-6 space-y-4">
        <div class="n7-input-field gap-1 ">
            <label for="fieldName" class="n7-input-field__label">
                Nome

                <abbr class="n7-font-bold n7-no-underline" title="obbligatorio">*</abbr>

            </label>

            <div class="n7-input">
                <input id="fieldName" class="text-sm n7-input-placeholder is-name" type="text" data-label="Nome" aria-required="true" autocomplete="given-name" aria-describedBy="fieldNameError">
            </div>

            <span class="n7-input-field__hint">

                <span class="n7-input-field__error" id="fieldNameError" aria-live="assertive">

                </span>

            </span>

        </div>

        <div class="n7-input-field gap-1 ">
            <label for="fieldSurname" class="n7-input-field__label">
                Cognome

                <abbr class="n7-font-bold n7-no-underline" title="obbligatorio">*</abbr>

            </label>

            <div class="n7-input">
                <input id="fieldSurname" class="text-sm n7-input-placeholder is-familyname" type="text" data-label="Cognome" aria-required="true" autocomplete="family-name" aria-describedBy="fieldSurnameError">
            </div>

            <span class="n7-input-field__hint">

                <span class="n7-input-field__error" id="fieldSurnameError" aria-live="assertive">

                </span>

            </span>

        </div>

        <div class="n7-input-field gap-1 ">
            <label for="fieldEmail" class="n7-input-field__label">
                Email

                <abbr class="n7-font-bold n7-no-underline" title="obbligatorio">*</abbr>

            </label>

            <div class="n7-input">
                <input id="fieldEmail" class="text-sm n7-input-placeholder is-email" type="email" data-label="Email" aria-required="true" autocomplete="email" aria-describedBy="fieldEmailError">
            </div>

            <span class="n7-input-field__hint">

                <span class="n7-input-field__error" id="fieldEmailError" aria-live="assertive">

                </span>

            </span>

        </div>

        <div class="n7-input n7-input-field n7-input-checkbox has-js-validation-required is-privacy">
            <div class="flex gap-2 items-center">
                <input id="fieldPrivacy" name="fieldPrivacy" data-label="Privacy policy" class="    has-js-validation-required is-privacy" type="checkbox" aria-required="true" aria-describedBy="">
                <label class="n7-input-checkbox__label" for="fieldPrivacy">
                    Dichiaro di aver letto la <a href="" target="_blank" class="n7-link-primary underline font-bold ">Privacy Policy<span class="sr-only"> (apre una nuova tab o finestra del browser)</span></a> e di accettarne i contenuti.
                    <abbr class="font-bold no-underline" title="obbligatorio">*</abbr></label>
            </div>

            <span class="n7-input-field__hint">

                <span class="n7-input-field__error" id="fieldPrivacyError" aria-live="assertive">

                </span>

            </span>

        </div>

        <div class="n7-form-feedback">
            <p class="n7-form-feedback-sr sr-only" aria-live="assertive"><!-- js --></p>
            <p class="n7-form-feedback-msg" aria-hidden="true"></p>
        </div>

        <div class="n7-form-actions">
            <button type="submit" class="n7-btn 
    n7-btn--primary
    form-submit">

                Registrati

            </button>

        </div>
    </form>
</div>
<div class="n7-background-02 p-6 border n7-border-gray-01">
    <div class="pb-6 space-y-4 border-b n7-border-gray-01">
        <p>I campi obbligatori sono contrassegnati da un asterisco
        <abbr class="font-bold no-underline" title="obbligatorio">*</abbr></p>
    </div>
    
    <form novalidate id="hasValidationForm" class="has-js-validation-form py-6 space-y-4">
        {% render '@input-field', {
            label: 'Nome',
            input: {
                id: 'fieldName',
                type: 'text',
                shortLabel: 'Nome',
                classes: 'is-name',
                autocomplete: 'given-name',
                fieldRequired: true
            }
        }, true %}
        
        {% render '@input-field', {
            label: 'Cognome',
            input: {
                id: 'fieldSurname',
                type: 'text',
                shortLabel: 'Cognome',
                classes: 'is-familyname',
                autocomplete: 'family-name',
                fieldRequired: true
            }
        }, true %}
        
        {% render '@input-field', {
            label: 'Email',
            input: {
                id: 'fieldEmail',
                type: 'email',
                shortLabel: 'Email',
                classes: 'is-email',
                autocomplete: 'email',
                fieldRequired: true
            }
        }, true %}
        
        {% render '@input-checkbox', {
            label: 'Dichiaro di aver letto la <a href="" target="_blank" class="n7-link-primary underline font-bold ">Privacy Policy<span class="sr-only"> (apre una nuova tab o finestra del browser)</span></a> e di accettarne i contenuti.',
            id: 'fieldPrivacy',
            shortLabel: 'Privacy policy',
            classes: 'has-js-validation-required is-privacy',
            fieldRequired: true
        }, true %}
        
        <div class="n7-form-feedback">
            <p class="n7-form-feedback-sr sr-only" aria-live="assertive"><!-- js --></p>
            <p class="n7-form-feedback-msg" aria-hidden="true"></p>
        </div>
        
        <div class="n7-form-actions">
            {% render '@button',{ label: 'Registrati', buttonType: 'submit', classes: 'form-submit' },true %}
        </div>
    </form>
</div>
/* No context defined. */
  • Content:
    /**
     * FORM WITH VALIDATION
     * FEEDBACK MESSAGE
     *
    */
    @layer components {
        .n7-form-feedback-msg.is-invalid {
            @apply p-3 bg-error-light n7-content-error font-bold border border-error-dark rounded-md;
        }
    
        .n7-form-feedback-msg.is-success {
            @apply p-3 bg-success-light n7-content-success font-bold border border-success-dark rounded-md;
        }
    }
  • URL: /components/raw/form-with-validation/form-with-validation.css
  • Filesystem Path: components/04-organisms/form-with-validation/form-with-validation.css
  • Size: 368 Bytes
  • Content:
    document.addEventListener("DOMContentLoaded", function () {
      ///* Regex per verifica email */
      const emailRegex = /\S+@\S+\.\S+/; // has @ and .
    
      ///* Form */
      const elForm = document.getElementById("hasValidationForm");
      if (elForm) {
        ///* Campi form */
        const elRequiredName = document.getElementsByClassName("is-name")[0];
        const elRequiredFamilyname = document.getElementsByClassName("is-familyname")[0];
        const elPrivacy = document.getElementsByClassName("is-privacy")[0];
        const elEmail = document.getElementsByClassName("is-email")[0];
    
        ///* Errori form da verificare */
        const formErrors = {
          name: false,
          familyname: false,
          email: false,
          privacy: false
        };
    
        let hasSubmitted = false;
    
        validateField({
          elField: elEmail,
          validateFn: validateFieldEmail
        });
    
        validateField({
          elField: elRequiredName,
          validateFn: validateFieldRequired,
          errorKey: 'name'
        });
    
        validateField({
          elField: elRequiredFamilyname,
          validateFn: validateFieldRequired,
          errorKey: 'familyname'
        });
    
        validateField({
          elField: elPrivacy,
          validateFn: validateFieldPrivacy
        });
    
        // Gestione validazione su 3 eventi: change, blur, keyup
        function validateField({ elField, validateFn, errorKey }) {
          let touched = false;
    
          elField.addEventListener("change", (e) => {
            touched = true; // mark it as touched so that on blur it shows the error.
            validateFn(e.target, { live: true, errorKey });
            if (hasSubmitted) {
              updateSubmitSummary();
            }
          });
    
          elField.addEventListener("keyup", (e) => {
            // remove any error on keyup if existent
            validateFn(e.target, { removeOnly: true, errorKey });
    
            if (hasSubmitted) {
              updateSubmitSummary();
            }
          });
    
          elField.addEventListener("blur", (e) => {
            if (!touched) return;
            // show error if touched
            validateFn(e.target, { live: true, errorKey });
          });
        }
    
        // Controllo email
        function validateFieldEmail(el, opts) {
          const isEmpty = el.value === "";
          updateFieldDOM(el, !isEmpty, "Email obbligatoria.", opts);
    
          if (isEmpty) {
            formErrors.email = true;
          } else {
            const isEmailValid = el.value.match(emailRegex);
            updateFieldDOM(el, isEmailValid, "Email non valida.", opts);
            formErrors.email = !isEmailValid;
          }
        }
    
        // Validazione campi obbligatori
        function validateFieldRequired(el, opts) {
          const isEmpty = el.value === "";
          const errorKey = opts?.errorKey;
          const elField = el.closest(".n7-input-field");
          const elLabel = elField.querySelector(".n7-input-field__label-text");
          const fieldLabel = elLabel ? elLabel.innerText : "Field";
    
          updateFieldDOM(el, !isEmpty, `${fieldLabel} obbligatorio.`, opts);
    
          if (errorKey) {
            formErrors[errorKey] = isEmpty;
          }
        }
    
        // Validazione campo privacy (checkbox)
        function validateFieldPrivacy(el, opts) {
          const isNotChecked = el.checked === false;
          updateFieldDOM(el, !isNotChecked, "Devi dichiarare di aver letto la Privacy Policy.", opts);
    
          formErrors.privacy = isNotChecked;
        }
    
        // Aggiornamento errore nel campo e gestione attributi accessibilità
        function updateFieldDOM(el, isValid, errorMessage, opts) {
          const removeOnly = opts?.removeOnly;
          const isLive = opts?.live;
          const elField = el.closest(".n7-input-field");
          const elError = elField.querySelector(".n7-input-field__error");
    
          if (isValid) {
            elField.classList.remove("is-invalid");
            elError.innerText = ""; // It's valid
            el.removeAttribute("aria-invalid");
          } else if (!removeOnly) {
            elField.classList.add("is-invalid");
            el.setAttribute("aria-invalid", "true");
            elError.setAttribute("aria-live", isLive ? "assertive" : "off");
            elError.innerText = errorMessage;
          }
        }
    
        // Aggiornamento feedback form a invio
        function updateSubmitSummary({ isSubmit } = {}) {
          const elSummary = elForm.querySelector(".n7-form-feedback");
          const elSummaryMsg = elSummary.querySelector(".n7-form-feedback-msg");
    
          // Clear form feedback
          elSummaryMsg.classList.remove("is-invalid");
          elSummaryMsg.classList.remove("is-success");
          elSummaryMsg.innerText = "";
          const errorsState = Object.entries(formErrors);
    
          const invalidFields = errorsState.filter(([key, value]) => value === true).map(([key]) => {
            const elField = elForm.querySelector(`.is-${key}`);
            return elField ? elField.getAttribute('data-label') : key;
          });
    
          if (invalidFields.length > 0) {
            // Show error msg
            const errorCount = invalidFields.length;
            const errorMsg =
              errorCount === 1
                ? `È presente ${errorCount} campo non valido: ${invalidFields.join(', ')}.`
                : `Sono presenti ${errorCount} campi non validi: ${invalidFields.join(', ')}.`;
    
            elSummaryMsg.classList.add("is-invalid");
            elSummaryMsg.innerText = errorMsg;
    
            elSummary.querySelector(".n7-form-feedback-sr").innerText = isSubmit
              ? // Set SR error message only on submit to avoid being re-announced
              // every time the error summary visually changes.
              errorMsg
              : "";
          } else if (isSubmit) {
            const successMsg = "Form inviata con successo.";
            elSummary.querySelector(".n7-form-feedback-sr").innerText = successMsg;
            elSummaryMsg.innerText = successMsg;
            elSummaryMsg.classList.add("is-success");
          }
        }
    
        elForm.addEventListener("submit", (e) => {
          e.preventDefault();
          hasSubmitted = true;
    
          // Validate again
          validateFieldEmail(elEmail);
          validateFieldRequired(elRequiredName, { errorKey: 'name' });
          validateFieldRequired(elRequiredFamilyname, { errorKey: 'familyname' });
          validateFieldPrivacy(elPrivacy);
    
          updateSubmitSummary({ isSubmit: true });
        });
      }
    });
    
  • URL: /components/raw/form-with-validation/form-with-validation.js
  • Filesystem Path: components/04-organisms/form-with-validation/form-with-validation.js
  • Size: 6.1 KB

No notes defined.