diff --git a/src/bundle/Resources/public/js/scripts/admin.contenttype.edit.js b/src/bundle/Resources/public/js/scripts/admin.contenttype.edit.js index 37d1dd1018..4f9e104cce 100644 --- a/src/bundle/Resources/public/js/scripts/admin.contenttype.edit.js +++ b/src/bundle/Resources/public/js/scripts/admin.contenttype.edit.js @@ -1,9 +1,13 @@ (function(global, doc, ibexa, Routing, Translator) { const TIMEOUT_REMOVE_PLACEHOLDERS = 1500; + const SELECTOR_INPUTS_TO_VALIDATE = '.ibexa-input[required]:not([disabled]):not([hidden])'; let targetContainer = null; let sourceContainer = null; let currentDraggedItem = null; let draggedItemPosition = null; + let isEditFormValid = false; + const editForm = doc.querySelector('.ibexa-content-type-edit-form'); + let inputsToValidate = editForm.querySelectorAll(SELECTOR_INPUTS_TO_VALIDATE); const draggableGroups = []; const token = doc.querySelector('meta[name="CSRF-Token"]').content; const siteaccess = doc.querySelector('meta[name="SiteAccess"]').content; @@ -11,6 +15,11 @@ const filterFieldInput = doc.querySelector('.ibexa-available-field-types__sidebar-filter'); const popupMenuElement = sectionsNode.querySelector('.ibexa-popup-menu'); const addGroupTriggerBtn = sectionsNode.querySelector('.ibexa-content-type-edit__add-field-definitions-group-btn'); + const noFieldsAddedError = Translator.trans( + /*@Desc("You have to add at least one field definition")*/ 'content_type.edit.error.no_added_fields_definition', + {}, + 'content_type', + ); const endpoints = { add: { actionName: 'add_field_definition', @@ -80,7 +89,9 @@ const fieldGroupInput = fieldNode.querySelector('.ibexa-input--field-group'); const removeFieldsBtn = fieldNode.querySelectorAll('.ibexa-collapse__extra-action-button--remove-field-definitions'); + const fieldInputsToValidate = fieldNode.querySelectorAll(SELECTOR_INPUTS_TO_VALIDATE); + fieldInputsToValidate.forEach(attachValidateEvents); removeDragPlaceholders(); fieldGroupInput.value = fieldsGroupId; targetContainer.insertBefore(fieldNode, targetPlace); @@ -132,8 +143,6 @@ }); }; const afterChangeGroup = () => { - const submitBtn = doc.querySelector('.ibexa-content-type-edit__publish-content-type'); - const fieldsDefinitionCount = doc.querySelectorAll('.ibexa-collapse--field-definition').length; const groups = doc.querySelectorAll('.ibexa-collapse--field-definitions-group'); const itemsAction = doc.querySelectorAll('.ibexa-content-type-edit__add-field-definitions-group .ibexa-popup-menu__item-content'); @@ -158,8 +167,6 @@ doc.querySelectorAll('.ibexa-collapse--field-definition').forEach((fieldDefinition, index) => { fieldDefinition.querySelector('.ibexa-input--position').value = index; }); - - submitBtn.toggleAttribute('disabled', !fieldsDefinitionCount); }; const addField = () => { if (!sourceContainer.classList.contains('ibexa-available-field-types__list')) { @@ -253,7 +260,58 @@ }) .catch(ibexa.helpers.notification.showErrorNotification); }; + const validateInput = (input) => { + const isInputEmpty = !input.value; + const field = input.closest('.form-group'); + const labelNode = field.querySelector('.ibexa-label'); + const errorNode = field.querySelector('.ibexa-form-error'); + + input.classList.toggle('is-invalid', isInputEmpty); + + if (errorNode) { + const fieldName = labelNode.innerHTML; + const errorMessage = ibexa.errors.emptyField.replace('{fieldName}', fieldName); + + errorNode.innerHTML = isInputEmpty ? errorMessage : ''; + } + + isEditFormValid = isEditFormValid && !isInputEmpty; + }; + const validateForm = () => { + const fieldDefinitionsStatuses = {}; + + isEditFormValid = true; + inputsToValidate = editForm.querySelectorAll(SELECTOR_INPUTS_TO_VALIDATE); + + inputsToValidate.forEach((input) => { + const fieldDefinition = input.closest('.ibexa-collapse--field-definition'); + + if (fieldDefinition) { + const { fieldDefinitionIdentifier } = fieldDefinition.dataset; + const isInputEmpty = !input.value; + + if (!fieldDefinitionsStatuses[fieldDefinitionIdentifier]) { + fieldDefinitionsStatuses[fieldDefinitionIdentifier] = []; + } + + fieldDefinitionsStatuses[fieldDefinitionIdentifier].push(isInputEmpty); + } + validateInput(input); + }); + + Object.entries(fieldDefinitionsStatuses).forEach(([fieldDefinitionIdentifier, inputsStatus]) => { + const isFieldDefinitionValid = inputsStatus.every((hasError) => !hasError); + const fieldDefinitionNode = doc.querySelector(`[data-field-definition-identifier="${fieldDefinitionIdentifier}"]`); + + fieldDefinitionNode.classList.toggle('is-invalid', !isFieldDefinitionValid); + }); + }; + const attachValidateEvents = (input) => { + input.addEventListener('change', validateForm, false); + input.addEventListener('blur', validateForm, false); + input.addEventListener('input', validateForm, false); + }; class FieldDefinitionDraggable extends ibexa.core.Draggable { onDrop(event) { targetContainer = event.currentTarget; @@ -368,20 +426,25 @@ draggableGroups.push(draggable); }); - doc.querySelector('.ibexa-btn--save-content-type').addEventListener( - 'click', - () => { - if (doc.querySelectorAll('.ibexa-collapse--field-definition').length) { - return; + inputsToValidate.forEach(attachValidateEvents); + + editForm.addEventListener( + 'submit', + (event) => { + const fieldDefinitionsCount = doc.querySelectorAll('.ibexa-collapse--field-definition').length; + + validateForm(); + + if (!fieldDefinitionsCount) { + isEditFormValid = false; + ibexa.helpers.notification.showErrorNotification(noFieldsAddedError); } - ibexa.helpers.notification.showErrorNotification( - Translator.trans( - /*@Desc("You have to add at least one field definition")*/ 'content_type.edit.error.no_added_fields_definition', - {}, - 'content_type', - ), - ); + if (!isEditFormValid) { + event.preventDefault(); + + return; + } }, false, ); diff --git a/src/bundle/Resources/views/themes/admin/content_type/create.html.twig b/src/bundle/Resources/views/themes/admin/content_type/create.html.twig index 499aaf87c5..14ebe18751 100644 --- a/src/bundle/Resources/views/themes/admin/content_type/create.html.twig +++ b/src/bundle/Resources/views/themes/admin/content_type/create.html.twig @@ -18,7 +18,12 @@ {% endblock %} {% block form %} - {{ form_start(form, { attr: { class: 'ibexa-form' } } ) }} + {{ form_start(form, { + attr: { + class: 'ibexa-content-type-edit-form ibexa-form', + novalidate: 'novalidate', + } + }) }}