diff --git a/.babelrc b/.babelrc index 40a133180..9da67dbe3 100644 --- a/.babelrc +++ b/.babelrc @@ -6,19 +6,5 @@ "modules": false } ] - ], - "env": { - "test": { - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "current" - } - } - ] - ] - } - } + ] } diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 29336dbd2..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,39 +0,0 @@ -module.exports = { - parserOptions: { - parser: "babel-eslint", - ecmaVersion: 2017, - sourceType: "module" - }, - env: { - "browser": true, - "node": true, - "cypress/globals": true - }, - extends: [ - "eslint:recommended", - "plugin:vue/recommended", - "plugin:prettier/recommended", - "prettier", - "prettier/vue", - ], - rules: { - "no-console": ["error", {allow: ["error", "warn"]}], - "no-debugger": "error" , - "vue/require-default-prop": "off", - "vue/require-prop-type-constructor": "off", - "prettier/prettier": ["error", { printWidth: 120 }], - }, - plugins: [ - "standard", - "vue", - "prettier", - "cypress" - ], - settings: { - 'import/resolver': { - node: { - "extensions": [".js", ".vue"] - } - } - } -}; diff --git a/.prettierrc b/.prettierrc index c86170262..bca3b96cb 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,8 +1,8 @@ { + "arrowParens": "avoid", "bracketSpacing": true, + "printWidth": 120, "singleQuote": true, - "jsxBracketSameLine": true, - "trailingComma": "all", - "printWidth": 80, - "semi": true -} \ No newline at end of file + "semi": true, + "trailingComma": "all" +} diff --git a/.stylelintrc b/.stylelintrc index f3d909b63..1d56e4af0 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -3,9 +3,43 @@ "stylelint-config-standard", "stylelint-config-recommended-scss" ], - "syntax": "scss", + "customSyntax": "postcss-scss", "plugins": ["stylelint-scss"], + "ignoreFiles": [ + "assets/scss/vendor/**/*.scss" + ], "rules": { - "no-descending-specificity": null + "no-descending-specificity": null, + + "selector-class-pattern": [ + "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?:__[a-z][a-z0-9]*(?:-[a-z0-9]+)*)?(?:--[a-z][a-z0-9]*(?:-[a-z0-9]+)*)*$", + { + "message": "Expected class selector to be kebab-case BEM (block__element--modifier)" + } + ], + + "import-notation": null, + "no-invalid-position-at-import-rule": null, + "scss/load-no-partial-leading-underscore": null, + "scss/load-partial-extension": null, + + "alpha-value-notation": null, + "color-function-alias-notation": null, + "color-function-notation": null, + "custom-property-pattern": null, + "declaration-block-no-redundant-longhand-properties": null, + "number-max-precision": null, + "property-no-deprecated": null, + "property-no-vendor-prefix": null, + "scss/at-extend-no-missing-placeholder": null, + "scss/comment-no-empty": null, + "scss/no-global-function-names": null, + "scss/operator-no-unspaced": null, + "selector-attribute-quotes": null, + "selector-id-pattern": null, + "selector-not-notation": null, + "shorthand-property-no-redundant-values": null, + "value-keyword-case": null, + "function-url-quotes": null } } diff --git a/assets/js/app/ajax-save.js b/assets/js/app/ajax-save.js index d19fa8177..0eb47eacb 100644 --- a/assets/js/app/ajax-save.js +++ b/assets/js/app/ajax-save.js @@ -14,10 +14,10 @@ record_id = JSON.stringify(record_id); /** * Start of Ajaxy solution for saving */ -$(document).ready(function() { +$(document).ready(function () { let unsaved = false; - $(form).change(function() { + $(form).change(function () { //triggers change in all input fields including text type unsaved = true; }); @@ -48,13 +48,13 @@ $(document).ready(function() { $('#toastType').append(toastType); $('#toastBody').append(toastMessage); - $(document).ready(function() { + $(document).ready(function () { let toastElList = [].slice.call(document.querySelectorAll('.toast')); - let toastList = toastElList.map(function(toastEl) { + let toastList = toastElList.map(function (toastEl) { return new Toast(toastEl, []); }); - toastList.forEach(function(toast) { + toastList.forEach(function (toast) { toast.show(); }); }); @@ -67,25 +67,25 @@ $(document).ready(function() { // For classic POST: disable only after native HTML5 validation has passed (submit event) if (elementSubmit.length) { - $(form).on('submit', function() { + $(form).on('submit', function () { elementSubmit.prop('disabled', true); }); } - elementButton.on('click', function() { + elementButton.on('click', function () { elementButton.prop('disabled', true); $.ajax({ type: 'POST', link: url, data: $(form).serialize(), - beforeSend: function() { + beforeSend: function () { patience_virtue(elementButton); }, - complete: function() { + complete: function () { renable(); elementButton.prop('disabled', false); }, - success: function(data, textStatus) { + success: function (data, textStatus) { if (!record_id) { window.location.replace(data.url); } else if (window.location.pathname === '/bolt/duplicate/' + duplicate_id) { @@ -98,7 +98,7 @@ $(document).ready(function() { showToast(); } }, - error: function(jq, status, err) { + error: function (jq, status, err) { // eslint-disable-next-line no-console console.log(status, err); showToast(); diff --git a/assets/js/app/common.js b/assets/js/app/common.js index b94ec7e04..8523ef607 100644 --- a/assets/js/app/common.js +++ b/assets/js/app/common.js @@ -8,7 +8,7 @@ import ClipboardJS from 'clipboard'; import { version } from '../version'; window.assetsVersion = version; -$(document).ready(function() { +$(document).ready(function () { // add a js class to indicate we have JS enabled. Might need a change to either modernizr or somethng comparable $('html').addClass('js'); @@ -20,7 +20,7 @@ $(document).ready(function() { */ $('.admin__sidebar').addClass('admin__sidebar--is-collapsed'); - $('.admin-sidebar-toggler').on('click', function() { + $('.admin-sidebar-toggler').on('click', function () { if ($('.admin__sidebar').hasClass('admin__sidebar--is-collapsed')) { $('.admin__sidebar') .addClass('admin__sidebar--is-expanded') @@ -52,7 +52,7 @@ $(document).ready(function() { }, 50); } - $('a[data-bs-toggle="tab"]').on('click', function() { + $('a[data-bs-toggle="tab"]').on('click', function () { let newUrl; const hash = $(this).attr('href'); newUrl = url.split('#')[0] + hash; @@ -62,7 +62,7 @@ $(document).ready(function() { /* ** Convert all ISO dates with class .datetime-relative to relative time */ - $('.datetime-relative').each(function() { + $('.datetime-relative').each(function () { $(this).text(DateTime.fromISO($(this).text()).toRelative()); }); @@ -70,15 +70,15 @@ $(document).ready(function() { ** Initialise all popover elements */ let popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); - popoverTriggerList.map(function(popoverTriggerEl) { + popoverTriggerList.map(function (popoverTriggerEl) { return new Popover(popoverTriggerEl); }); /* ** When a field from another group is invalid, show it. */ - $('#editor button[type="submit"]').click(function() { - $('#editor input:invalid').each(function() { + $('#editor button[type="submit"]').click(function () { + $('#editor input:invalid').each(function () { // Find the tab-pane that this element is inside, and get the id let $closest = $(this).closest('.tab-pane'); let id = $closest.attr('id'); @@ -97,20 +97,15 @@ $(document).ready(function() { ** Simulates disabled behavior for elements with data-readonly attribute. * This is needed, because a disabled input cannot be required. */ - $('[data-readonly]').on('keydown paste', function(e) { + $('[data-readonly]').on('keydown paste', function (e) { e.preventDefault(); }); /* Part of the code above, however make sure flatpickr is not readonly * and that its validation works. */ - $('.editor--date') - .siblings() - .prop('readonly', false) - .attr('data-readonly', 'readonly'); + $('.editor--date').siblings().prop('readonly', false).attr('data-readonly', 'readonly'); $('.editor--date').on('change', e => { - const target = $(e.target) - .parent() - .find('input[data-readonly="readonly"]'); + const target = $(e.target).parent().find('input[data-readonly="readonly"]'); if (target.val()) { target[0].setCustomValidity(''); } else { @@ -136,15 +131,12 @@ $(document).ready(function() { $('[data-errormessage]').on('input', handleInput); /* Set the errormessage on the correct editor--date field */ - $('.editor--date').each(function() { + $('.editor--date').each(function () { let siblings = $(this).siblings(); const errormessage = $(this).attr('data-errormessage'); - siblings.each(function() { - $(this) - .attr('data-errormessage', errormessage) - .on('invalid', handleInvalid) - .on('input', handleInput); + siblings.each(function () { + $(this).attr('data-errormessage', errormessage).on('invalid', handleInvalid).on('input', handleInput); }); }); /* End of custom error message */ @@ -161,7 +153,7 @@ $(document).ready(function() { // Reset the content of a modal to it's default - window.$(document).on('click', '[data-bs-toggle="modal"]', function(event) { + window.$(document).on('click', '[data-bs-toggle="modal"]', function (event) { let resourcesModal = document.getElementById('resourcesModal'); let saveButton = document.getElementById('modalButtonAccept'); diff --git a/assets/js/app/editor/Components/Collection.vue b/assets/js/app/editor/Components/Collection.vue index a2b994651..9ea850c36 100644 --- a/assets/js/app/editor/Components/Collection.vue +++ b/assets/js/app/editor/Components/Collection.vue @@ -140,7 +140,7 @@ export default { initialSelectValue() { return this.templateSelectOptions[0].key; }, - allowMore: function() { + allowMore: function () { return this.counter < this.limit; }, }, @@ -154,24 +154,24 @@ export default { */ window .$(document) - .on('click', vueThis.selector.collectionContainer + ' .collection-item .summary', function(e) { + .on('click', vueThis.selector.collectionContainer + ' .collection-item .summary', function (e) { e.preventDefault(); let thisCollectionItem = vueThis.getCollectionItemFromPressedButton(this); thisCollectionItem.toggleClass('collapsed'); }); - window.$(document).on('click', vueThis.selector.collectionContainer + vueThis.selector.remove, function(e) { + window.$(document).on('click', vueThis.selector.collectionContainer + vueThis.selector.remove, function (e) { e.preventDefault(); e.stopPropagation(); let collectionContainer = window.$(this).closest(vueThis.selector.collectionContainer); let button = this; - $('#modalButtonAccept').on('click', function() { + $('#modalButtonAccept').on('click', function () { vueThis.getCollectionItemFromPressedButton(button).remove(); }); vueThis.setAllButtonsStates(collectionContainer); vueThis.counter--; }); - window.$(document).on('click', vueThis.selector.collectionContainer + vueThis.selector.moveUp, function(e) { + window.$(document).on('click', vueThis.selector.collectionContainer + vueThis.selector.moveUp, function (e) { e.preventDefault(); e.stopPropagation(); let thisCollectionItem = vueThis.getCollectionItemFromPressedButton(this); @@ -180,7 +180,7 @@ export default { vueThis.setButtonsState(thisCollectionItem); vueThis.setButtonsState(prevCollectionitem); }); - window.$(document).on('click', vueThis.selector.collectionContainer + vueThis.selector.moveDown, function(e) { + window.$(document).on('click', vueThis.selector.collectionContainer + vueThis.selector.moveDown, function (e) { e.preventDefault(); e.stopPropagation(); let thisCollectionItem = vueThis.getCollectionItemFromPressedButton(this); @@ -189,14 +189,14 @@ export default { vueThis.setButtonsState(thisCollectionItem); vueThis.setButtonsState(nextCollectionItem); }); - window.$(document).on('click', vueThis.selector.collectionContainer + vueThis.selector.expandAll, function(e) { + window.$(document).on('click', vueThis.selector.collectionContainer + vueThis.selector.expandAll, function (e) { e.preventDefault(); const collection = $(e.target).closest(vueThis.selector.collectionContainer); collection.find('.collection-item').removeClass('collapsed'); }); window .$(document) - .on('click', vueThis.selector.collectionContainer + vueThis.selector.collapseAll, function(e) { + .on('click', vueThis.selector.collectionContainer + vueThis.selector.collapseAll, function (e) { e.preventDefault(); const collection = $(e.target).closest(vueThis.selector.collectionContainer); collection.find('.collection-item').addClass('collapsed'); @@ -204,11 +204,11 @@ export default { /** * Update the title dynamically. */ - $(document).ready(function() { - $.each(window.$(vueThis.selector.collectionContainer + vueThis.selector.item), function() { + $(document).ready(function () { + $.each(window.$(vueThis.selector.collectionContainer + vueThis.selector.item), function () { updateTitle(this); }); - window.$(vueThis.selector.collectionContainer).on('keyup change', vueThis.selector.item, function() { + window.$(vueThis.selector.collectionContainer).on('keyup change', vueThis.selector.item, function () { updateTitle(this); }); }); @@ -217,12 +217,8 @@ export default { * with the value of the first text-based field. */ function updateTitle(item) { - const label = $(item) - .find('.collection-item-title') - .first(); - const input = $(item) - .find('textarea,input[type="text"]') - .first(); + const label = $(item).find('.collection-item-title').first(); + const input = $(item).find('textarea,input[type="text"]').first(); // We use this 'innerText' trick to ensure the title is plain text. var title = document.createElement('span'); title.innerHTML = $(input).val() ? $(input).val() : label.attr('data-label'); @@ -249,29 +245,21 @@ export default { }, setAllButtonsStates(collectionContainer) { let vueThis = this; - collectionContainer.children(vueThis.selector.item).each(function() { + collectionContainer.children(vueThis.selector.item).each(function () { vueThis.setButtonsState(window.$(this)); }); }, setButtonsState(item) { //by default, enable - item.find(this.selector.moveUp) - .first() - .removeAttr('disabled'); - item.find(this.selector.moveDown) - .first() - .removeAttr('disabled'); + item.find(this.selector.moveUp).first().removeAttr('disabled'); + item.find(this.selector.moveDown).first().removeAttr('disabled'); if (!this.getPreviousCollectionItem(item)) { // first in collection - item.find(this.selector.moveUp) - .first() - .attr('disabled', 'disabled'); + item.find(this.selector.moveUp).first().attr('disabled', 'disabled'); } if (!this.getNextCollectionItem(item)) { // last in collection - item.find(this.selector.moveDown) - .first() - .attr('disabled', 'disabled'); + item.find(this.selector.moveDown).first().attr('disabled', 'disabled'); } }, getPreviousCollectionItem(item) { @@ -281,10 +269,7 @@ export default { return item.next('.collection-item').length === 0 ? false : item.next('.collection-item'); }, getCollectionItemFromPressedButton(button) { - return window - .$(button) - .closest('.collection-item') - .last(); + return window.$(button).closest('.collection-item').last(); }, addCollectionItem(event) { // duplicate template without reference diff --git a/assets/js/app/editor/Components/Embed.vue b/assets/js/app/editor/Components/Embed.vue index 412541fa8..eb9bd8d44 100644 --- a/assets/js/app/editor/Components/Embed.vue +++ b/assets/js/app/editor/Components/Embed.vue @@ -147,14 +147,14 @@ export default { }; }, watch: { - urlData: function() { + urlData: function () { this.updateEmbed(); }, }, mounted() { this.previewImage = this.thumbnail; }, - created: function() { + created: function () { this.debouncedFetchEmbed = _.debounce(this.fetchEmbed, 500); if (this.urlData) { this.updateEmbed(); @@ -172,14 +172,14 @@ export default { }); }, methods: { - updateEmbed: function() { + updateEmbed: function () { this.loading = true; this.debouncedFetchEmbed(); }, - clearEmbed: function() { + clearEmbed: function () { this.urlData = ''; }, - fetchEmbed: function() { + fetchEmbed: function () { const body = new FormData(); body.append('url', this.urlData); body.append('_csrf_token', document.getElementsByName('_csrf_token')[0].value); diff --git a/assets/js/app/editor/Components/File.vue b/assets/js/app/editor/Components/File.vue index 384de5273..7eb734e66 100644 --- a/assets/js/app/editor/Components/File.vue +++ b/assets/js/app/editor/Components/File.vue @@ -271,10 +271,7 @@ export default { } } inputOptions.forEach((element, key) => { - let filenameExtension = element.text - .split('.') - .pop() - .toLowerCase(); + let filenameExtension = element.text.split('.').pop().toLowerCase(); if (element.group == 'directories') { filePath = element.value; let baseAsyncUrl = `${baseAsyncPath}?location=${filePath}&type=files`; @@ -299,9 +296,9 @@ export default { modalContent += `
- +
Toggle Dropdown -