lib/form/amd/src/configtext_maxlength.js

// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Validation for configtext_maxlength.
 *
 * @module     core_form/configtext-maxlength
 * @copyright  2021 The Open University
 */
import {get_string as getString} from 'core/str';
import Templates from 'core/templates';
import Notification from 'core/notification';
import {prefetchStrings, prefetchTemplates} from 'core/prefetch';

let registered = false;

/**
 * Initialisation function.
 */
export const init = () => {
    if (registered) {
        return;
    }
    prefetchStrings('core', [
        'maximumchars',
    ]);

    prefetchTemplates([
        'core_form/setting_validation_failure',
    ]);

    registered = true;

    document.addEventListener('input', e => {
        const maxLengthField = e.target.closest('[data-validation-max-length]');
        if (!maxLengthField) {
            return;
        }

        if (maxLengthField.value.length > maxLengthField.dataset.validationMaxLength) {
            // Disable the form for this field.
            maxLengthField.form.addEventListener('submit', submissionCheck);
            // Display an error.
            getString('maximumchars', 'core', maxLengthField.dataset.validationMaxLength)
                .then(errorMessage => {
                    return Templates.renderForPromise('core_form/setting_validation_failure', {
                        fieldid: maxLengthField.id,
                        message: errorMessage,
                    });
                })
                .then(errorTemplate => {
                    if (!maxLengthField.dataset.validationFailureId) {
                        const formWrapper = maxLengthField.closest('.form-text');
                        Templates.prependNodeContents(formWrapper, errorTemplate.html, errorTemplate.js);
                        maxLengthField.dataset.validationFailureId = `maxlength_error_${maxLengthField.id}`;
                        // Disable submit button when the message is displayed.
                        updateSubmitButton();
                    }
                    return;
                })
                .then(() => {
                    maxLengthField.setAttribute('aria-invalid', true);
                    const errorField = document.getElementById(maxLengthField.dataset.validationFailureId);
                    if (errorField) {
                        errorField.setAttribute('aria-describedby', maxLengthField.id);
                    }
                    return;
                })
                .catch(Notification.exception);
        } else {
            // Remove the old message.
            const validationMessage = document.getElementById(maxLengthField.dataset.validationFailureId);
            if (validationMessage) {
                validationMessage.parentElement.remove();
                delete maxLengthField.dataset.validationFailureId;
                maxLengthField.removeAttribute('aria-invalid');
                // Enable submit button when the message was removed.
                updateSubmitButton();
            }
        }
    });
};

/**
 * Handle form submission.
 *
 * @param {Event} e The event.
 */
const submissionCheck = e => {
    const maxLengthFields = e.target.querySelectorAll('[data-validation-max-length]');
    const maxLengthFieldsArray = Array.from(maxLengthFields);
    maxLengthFieldsArray.some(maxLengthField => {
        // Focus on the first validation failure.
        if (maxLengthField.value.length > maxLengthField.dataset.validationMaxLength) {
            e.preventDefault();
            maxLengthField.focus();
            return true;
        }
        return false;
    });
};

/**
 * Update submit button.
 */
const updateSubmitButton = () => {
    const shouldDisable = document.querySelector('form#adminsettings .error');
    document.querySelector('form#adminsettings button[type="submit"]').disabled = !!shouldDisable;
};