Skip to content

Commit

Permalink
Merge pull request #91 from ezsystems/fieldtypes-bug-fixes
Browse files Browse the repository at this point in the history
EZP-28300: Files field types bugfixes
  • Loading branch information
Łukasz Serwatka committed Nov 24, 2017
2 parents 471d873 + 211ee13 commit 75c4938
Show file tree
Hide file tree
Showing 31 changed files with 430 additions and 155 deletions.
2 changes: 1 addition & 1 deletion src/bundle/Resources/public/css/ezplatform.min.css

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions src/bundle/Resources/public/js/scripts/admin.content.edit.js
@@ -0,0 +1,26 @@
(function (global, doc) {
const form = doc.querySelector('form[name="ezrepoforms_content_edit"]');
const submitBtns = form.querySelectorAll('[type="submit"]:not([formnovalidate])');

form.setAttribute('novalidate', true);

submitBtns.forEach(btn => {
const clickHandler = (event) => {
if (!parseInt(btn.dataset.isFormValid, 10)) {
event.preventDefault();

const validators = global.eZ.fieldTypeValidators;
const isFormValid = validators.map(validator => validator.isValid()).every(result => result);

if (isFormValid) {
btn.dataset.isFormValid = 1;
// for some reason trying to fire click event inside the event handler flow is impossible
// the following line breaks the flow so it's possible to fire click event on a button again.
window.setTimeout(() => btn.click(), 0);
}
}
};
btn.dataset.isFormValid = 0;
btn.addEventListener('click', clickHandler, false);
});
})(window, document);
Expand Up @@ -7,6 +7,8 @@
this.eventsMap = config.eventsMap;
this.fieldSelector = config.fieldSelector;
this.fieldContainer = config.fieldContainer;
this.fieldsToValidate = [];
this.isValid = this.isValid.bind(this);
}

/**
Expand All @@ -15,11 +17,22 @@
* @method attachEvent
* @param {String} eventName
* @param {String} selector
* @param {Function} callback
* @param {Function} validateField
* @memberof BaseFieldValidator
*/
attachEvent(eventName, selector, callback) {
[...doc.querySelectorAll(selector)].forEach(item => item.addEventListener(eventName, callback, false));
attachEvent(config) {
[...doc.querySelectorAll(config.selector)].forEach(item => {
const isValueValidator = typeof config.isValueValidator !== 'undefined' ? config.isValueValidator : true;

this.fieldsToValidate.push({
item,
isValueValidator,
callback: config.validateField
});

item.addEventListener(config.eventName, config.validateField, false);
item.addEventListener('checkIsValid', this.isValid, false);
});
}

/**
Expand All @@ -32,7 +45,10 @@
* @memberof BaseFieldValidator
*/
removeEvent(eventName, selector, callback) {
[...doc.querySelectorAll(selector)].forEach(item => item.removeEventListener(eventName, callback, false));
[...doc.querySelectorAll(selector)].forEach(item => {
item.removeEventListener('checkIsValid', this.isValid, false);
item.removeEventListener(eventName, callback, false);
});
}

/**
Expand All @@ -45,7 +61,7 @@
* @returns {Array}
* @memberof BaseFieldValidator
*/
findValidationStateNodes(fieldNode, input, selectors) {
findValidationStateNodes(fieldNode, input, selectors = []) {
return selectors.reduce((total, selector) => total.concat([...fieldNode.querySelectorAll(selector)]), []);
}

Expand Down Expand Up @@ -155,6 +171,8 @@

this.toggleInvalidState(validationResult.isError, config, event.target);
this.toggleErrorMessage(validationResult, config, event.target);

return validationResult;
}

/**
Expand All @@ -164,10 +182,11 @@
* @memberof BaseFieldValidator
*/
init() {
this.fieldsToValidate = [];
this.eventsMap.forEach(eventConfig => {
eventConfig.validateField = this.validateField.bind(this, eventConfig);

this.attachEvent(eventConfig.eventName, eventConfig.selector, eventConfig.validateField);
this.attachEvent(eventConfig);
});
}

Expand All @@ -181,5 +200,30 @@
this.eventsMap.forEach(({eventName, selector, validateField}) => this.removeEvent(eventName, selector, validateField));
this.init();
}

/**
* Checks whether field values are valid
*
* @method isValid
* @returns {Boolean}
*/
isValid() {
if (!this.fieldsToValidate.length) {
return true;
}

const results = [];

this.fieldsToValidate.forEach(field => {
if (field.isValueValidator) {
results.push(field.callback({
target: field.item,
currentTarget: field.item
}));
}
});

return results.every(result => !result.isError);
}
};
})(window, document);
Expand Up @@ -12,11 +12,26 @@
*/
validateInput(event) {
const input = event.currentTarget;
const dataContainer = this.fieldContainer.querySelector('.ez-field-edit__data');
const label = this.fieldContainer.querySelector(SELECTOR_FIELD_LABEL).innerHTML;
const result = { isError: false };
const isRequired = (input.required || this.fieldContainer.classList.contains('ez-field-edit--required'));
const dataMaxSize = +input.dataset.maxFileSize;
const maxFileSize = parseInt(dataMaxSize, 10);
const isEmpty = input.files &&
!input.files.length &&
dataContainer &&
!dataContainer.hasAttribute('hidden');
let result = { isError: false };

if (input.required && !input.files.length) {
result.errorMessage = global.eZ.errors.emptyField.replace('{fieldName}', label);
if (isRequired && isEmpty) {
result = {
isError: true,
errorMessage: global.eZ.errors.emptyField.replace('{fieldName}', label)
};
}

if (!isEmpty && maxFileSize > 0 && input.files[0].size > maxFileSize) {
result = this.showFileSizeError();
}

return result;
Expand All @@ -25,9 +40,10 @@
/**
* Displays an error message: file size exceeds maximum value
*
* @method showSizeError
* @method showFileSizeError
* @returns {Object}
*/
showSizeError() {
showFileSizeError() {
const label = this.fieldContainer.querySelector(SELECTOR_FIELD_LABEL).innerHTML;
const result = {
isError: true,
Expand Down
Expand Up @@ -16,8 +16,11 @@
this.showPreview = this.showPreview.bind(this);
this.handleRemoveFile = this.handleRemoveFile.bind(this);
this.handleDropFile = this.handleDropFile.bind(this);
this.checkFileSize = this.checkFileSize.bind(this);
this.maxFileSize = parseInt(this.inputField.dataset.maxFileSize, 10);
this.handleInputChange = this.handleInputChange.bind(this);

const dataMaxSize = +this.inputField.dataset.maxFileSize;

this.maxFileSize = parseInt(dataMaxSize, 10);
}

/**
Expand Down Expand Up @@ -72,10 +75,16 @@
* @param {Event} event
*/
handleDropFile(event) {
if (!this.checkCanDrop(event.dataTransfer.files[0])) {
const file = event.dataTransfer.files[0];

if (!this.checkCanDrop(file)) {
return;
}

if (this.maxFileSize > 0 && file.size > this.maxFileSize) {
return this.showFileSizeError();
}

this.inputField.files = event.dataTransfer.files;
}

Expand Down Expand Up @@ -114,16 +123,16 @@
/**
* Checks if file size is an allowed limit
*
* @method checkFileSize
* @method handleInputChange
* @param {Event} event
*/
checkFileSize(event) {
const file = [...event.currentTarget.files][0];

if (this.maxFileSize !== 0 && file.size > this.maxFileSize) {
return this.showFileSizeError();
handleInputChange(event) {
if (this.maxFileSize > 0 && event.currentTarget.files[0].size > this.maxFileSize) {
return this.resetInputField();
}

this.fieldContainer.querySelector('.ez-field-edit__option--remove-media').checked = false;

this.showPreview(event);
}

Expand All @@ -141,8 +150,8 @@
this.loadDroppedFilePreview(event);
}

this.fieldContainer.querySelector(SELECTOR_PREVIEW).classList.remove('ez-visually-hidden');
dropZone.classList.add('ez-visually-hidden', true);
this.fieldContainer.querySelector(SELECTOR_PREVIEW).removeAttribute('hidden');
dropZone.setAttribute('hidden', true);

btnRemove.addEventListener('click', this.handleRemoveFile, false);
dropZone.removeEventListener('drop', this.handleDropFile);
Expand All @@ -167,29 +176,41 @@
hidePreview() {
const btnRemove = this.fieldContainer.querySelector(SELECTOR_BTN_REMOVE);

this.fieldContainer.querySelector(SELECTOR_DATA).classList.remove('ez-visually-hidden');
this.fieldContainer.querySelector(SELECTOR_PREVIEW).classList.add('ez-visually-hidden', true);
this.fieldContainer.querySelector(SELECTOR_DATA).removeAttribute('hidden');
this.fieldContainer.querySelector(SELECTOR_PREVIEW).setAttribute('hidden', true);
this.fieldContainer.classList.remove('is-invalid');

btnRemove.removeEventListener('click', this.handleRemoveFile);

this.initializeDropZone();
}

/**
* Removes a file from input and hides a preview afterwards
* Resets input field state
*
* @method handleRemoveFile
* @method resetInputField
*/
handleRemoveFile(event) {
event.preventDefault();

resetInputField() {
const clonedInput = this.clonedInputField.cloneNode(true);

// required to reset properly the input of file type properly
this.inputField.parentNode.replaceChild(clonedInput, this.inputField);
this.inputField = clonedInput;
this.inputField.addEventListener('change', this.showPreview, false);
this.inputField.addEventListener('change', this.handleInputChange, false);
this.fieldContainer.querySelector('.ez-field-edit__option--remove-media').checked = true;

this.validator.reinit();
}

/**
* Removes a file from input and hides a preview afterwards
*
* @method handleRemoveFile
*/
handleRemoveFile(event) {
event.preventDefault();

this.resetInputField();
this.hidePreview();
}

Expand All @@ -210,7 +231,7 @@
* @method initializeDropZone
*/
initializeDropZone() {
const dropZone = this.fieldContainer.querySelector('.ez-field-edit__preview.ez-visually-hidden + .ez-field-edit__data');
const dropZone = this.fieldContainer.querySelector('.ez-field-edit__preview[hidden] + .ez-field-edit__data');

if (dropZone) {
dropZone.addEventListener('drop', this.handleDropFile, false);
Expand All @@ -225,7 +246,7 @@
initializePreview() {
const preview = this.fieldContainer.querySelector('.ez-field-edit__preview');

if (!preview.classList.contains('ez-visually-hidden')) {
if (!preview.hasAttribute('hidden')) {
this.showPreview();
}
}
Expand All @@ -239,7 +260,7 @@
this.btnAdd = this.fieldContainer.querySelector('.ez-data-source__btn-add');

this.btnAdd.addEventListener('click', this.openFileSelector, false);
this.inputField.addEventListener('change', this.checkFileSize, false);
this.inputField.addEventListener('change', this.handleInputChange, false);
window.addEventListener('drop', this.preventDefaultAction, false);
window.addEventListener('dragover', this.preventDefaultAction, false);

Expand Down
12 changes: 9 additions & 3 deletions src/bundle/Resources/public/js/scripts/fieldType/ezauthor.js
Expand Up @@ -168,7 +168,7 @@
findExistingErrorNodes(fieldNode, input, selectors) {
return selectors.reduce((total, selector) => total.concat([...input.closest(SELECTOR_AUTHOR).querySelectorAll(selector)]), []);
}
};
}

const validator = new EzAuthorValidator({
classInvalid: 'is-invalid',
Expand All @@ -178,22 +178,24 @@
selector: '.ez-data-source__author .ez-data-source__field--name input',
eventName: 'blur',
callback: 'validateName',
invalidStateSelectors: [SELECTOR_FIELD, SELECTOR_AUTHOR, SELECTOR_FIELD_NAME],
invalidStateSelectors: [SELECTOR_AUTHOR, SELECTOR_FIELD_NAME],
errorNodeSelectors: ['.ez-data-source__field--name .ez-data-source__label-wrapper'],
},
{
selector: '.ez-data-source__author .ez-data-source__field--email input',
eventName: 'blur',
callback: 'validateEmail',
invalidStateSelectors: [SELECTOR_FIELD, SELECTOR_AUTHOR, SELECTOR_FIELD_EMAIL],
invalidStateSelectors: [SELECTOR_AUTHOR, SELECTOR_FIELD_EMAIL],
errorNodeSelectors: ['.ez-data-source__field--email .ez-data-source__label-wrapper'],
},
{
isValueValidator: false,
selector: SELECTOR_REMOVE_AUTHOR,
eventName: 'click',
callback: 'removeItem',
},
{
isValueValidator: false,
selector: '.ez-btn--add-author',
eventName: 'click',
callback: 'addItem',
Expand All @@ -202,4 +204,8 @@
});

validator.init();

global.eZ.fieldTypeValidators = global.eZ.fieldTypeValidators ?
[...global.eZ.fieldTypeValidators, validator] :
[validator];
})(window, document);

0 comments on commit 75c4938

Please sign in to comment.