diff --git a/app/code/Magento/Bundle/view/frontend/web/js/slide.js b/app/code/Magento/Bundle/view/frontend/web/js/slide.js index 5afe4ad8a9ea7..86b341e4adcb8 100644 --- a/app/code/Magento/Bundle/view/frontend/web/js/slide.js +++ b/app/code/Magento/Bundle/view/frontend/web/js/slide.js @@ -85,7 +85,7 @@ define([ $('html, body').animate({ scrollTop: $(this.options.bundleOptionsContainer).offset().top }, 600); - $('#product-options-wrapper > fieldset').focus(); + $('#product-options-wrapper > fieldset').trigger('focus'); }, /** diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/form.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/form.js index 76aaddf55ac99..9f85c0f03217b 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/form.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/form.js @@ -45,10 +45,10 @@ define([ content: data.message }); } else { - $(this.options.categoryIdSelector).val(data.id).change(); - $(this.options.categoryPathSelector).val(data.path).change(); - $(this.options.categoryParentSelector).val(data.parentId).change(); - $(this.options.categoryLevelSelector).val(data.level).change(); + $(this.options.categoryIdSelector).val(data.id).trigger('change'); + $(this.options.categoryPathSelector).val(data.path).trigger('change'); + $(this.options.categoryParentSelector).val(data.parentId).trigger('change'); + $(this.options.categoryLevelSelector).val(data.level).trigger('change'); } } }; diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js b/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js index 4d0448f8f2a1e..56713c0c6f300 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/custom-options.js @@ -437,7 +437,7 @@ define([ this.refreshSortableElements(); this.options.selectionItemCount[data.id] = parseInt(this.options.selectionItemCount[data.id], 10) + 1; - $('#' + this.options.fieldId + '_' + data.id + '_select_' + data['select_id'] + '_title').focus(); + $('#' + this.options.fieldId + '_' + data.id + '_select_' + data['select_id'] + '_title').trigger('focus'); }, /** diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/new-category-dialog.js b/app/code/Magento/Catalog/view/adminhtml/web/js/new-category-dialog.js index 2661026ad05ea..8c3b576b38b01 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/new-category-dialog.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/new-category-dialog.js @@ -75,7 +75,7 @@ define([ $('#new_category_name').val(enteredName); if (enteredName === '') { - $('#new_category_name').focus(); + $('#new_category_name').trigger('focus'); } $('#new_category_messages').html(''); }, @@ -88,7 +88,7 @@ define([ validationOptions.unhighlight($('#new_category_parent-suggest').get(0), validationOptions.errorClass, validationOptions.validClass || ''); newCategoryForm.validation('clearError'); - $('#category_ids-suggest').focus(); + $('#category_ids-suggest').trigger('focus'); }, buttons: [{ text: $.mage.__('Create Category'), diff --git a/app/code/Magento/Catalog/view/frontend/web/js/zoom.js b/app/code/Magento/Catalog/view/frontend/web/js/zoom.js index 1c68818ed9c4e..1305a9acac167 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/zoom.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/zoom.js @@ -92,7 +92,7 @@ define([ }, this)); // Window resize will change offset for draggable - $(window).resize(this._draggableImage()); + $(window).on('resize', this._draggableImage); }, /** diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js b/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js index 0a10ff32bcb22..fa52b336a90f7 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/full-screen-loader.js @@ -27,7 +27,7 @@ define([ */ stopLoader: function (forceStop) { var $elem = $(containerId), - stop = $elem.trigger.bind($elem, 'processStop'); + stop = $elem.trigger.bind($elem, 'processStop'); //eslint-disable-line jquery-no-bind-unbind forceStop ? stop() : resolver(stop); } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js index 2a52b64647749..8f6c5ad8118e0 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js @@ -354,7 +354,7 @@ define([ } if (!emailValidationResult) { - $(loginFormSelector + ' input[name=username]').focus(); + $(loginFormSelector + ' input[name=username]').trigger('focus'); return false; } diff --git a/app/code/Magento/GiftMessage/view/frontend/web/js/gift-options.js b/app/code/Magento/GiftMessage/view/frontend/web/js/gift-options.js index cff3324fceaf8..e3d185db0cc5c 100644 --- a/app/code/Magento/GiftMessage/view/frontend/web/js/gift-options.js +++ b/app/code/Magento/GiftMessage/view/frontend/web/js/gift-options.js @@ -48,7 +48,7 @@ define([ container.show() .find('.giftmessage-area:not(:visible)').each(function (x, element) { if ($(element).val().length > 0) { - $(element).change(); + $(element).trigger('change'); container.find('a').click(); } }); diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/insertImageAction.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/insertImageAction.js index 322b29c92ca5b..34e3e924d848b 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/insertImageAction.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/insertImageAction.js @@ -49,7 +49,7 @@ define([ if (targetElement.is('textarea')) { this.insertAtCursor(targetElement.get(0), data.content); targetElement.focus(); - $(targetElement).change(); + $(targetElement).trigger('change'); } else { targetElement.val(data.content) .data('size', data.size) diff --git a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js index 151c5196db25f..798a02f681f36 100644 --- a/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js +++ b/app/code/Magento/ProductVideo/view/frontend/web/js/fotorama-add-video-events.js @@ -812,7 +812,7 @@ define([ if (self.isFullscreen && !self.fotoramaItem.data('fotorama').options.fullscreen.arrows) { if ($('.' + self.FTAR + '--prev').is(':focus') || $('.' + self.FTAR + '--next').is(':focus')) { - $(self.FTCF).focus(); + $(self.FTCF).trigger('focus'); } } }); diff --git a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js index 39ef5960eae2e..6782a87b02a9f 100644 --- a/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js @@ -55,7 +55,7 @@ define([ }); if (firstSwatch.length) { - $(firstSwatch).focus(); + $(firstSwatch).trigger('focus'); } } }); diff --git a/app/code/Magento/Ui/view/base/web/js/grid/resize.js b/app/code/Magento/Ui/view/base/web/js/grid/resize.js index a74fc7624c2d7..3286d4d2906e4 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/resize.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/resize.js @@ -92,7 +92,7 @@ define([ if ($(table).is(this.mainTableSelector)) { this.table = table; this.tableWidth = $(table).outerWidth(); - $(window).resize(this.checkAfterResize); + $(window).on('resize', this.checkAfterResize); } //TODO - Must be deleted when Firefox fixed problem with table-layout: fixed @@ -244,7 +244,7 @@ define([ setTimeout(function () { if (self.layoutFixedPolyfillIterator < 20) { - $(window).resize(); + $(window).trigger('resize'); self.layoutFixedPolyfillIterator++; self._layoutFixedPolyfill(); } else { diff --git a/app/code/Magento/Ui/view/base/web/js/grid/sticky/sticky.js b/app/code/Magento/Ui/view/base/web/js/grid/sticky/sticky.js index 85f47f19db3c5..8d06945f8e383 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/sticky/sticky.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/sticky/sticky.js @@ -220,7 +220,7 @@ define([ * @returns {Object} Chainable. */ initOnListingScroll: function () { - $(this.listingNode).scroll(function (e) { + $(this.listingNode).on('scroll', function (e) { this.flags.listingScrolled = true; this.flags.listingScrolledValue = $(e.target).scrollLeft(); }.bind(this)); @@ -234,7 +234,7 @@ define([ * @returns {Object} Chainable. */ initOnResize: function () { - $(window).resize(function () { + $(window).on('resize', function () { this.flags.resized = true; }.bind(this)); diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal.js b/app/code/Magento/Ui/view/base/web/js/modal/modal.js index eb3e1b07e3979..2fea698d1ef89 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/modal.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/modal.js @@ -260,15 +260,15 @@ define([ infelicity; if (type === 'opened' && this.options.focus) { - this.modal.find($(this.options.focus)).focus(); + this.modal.find($(this.options.focus)).trigger('focus'); } else if (type === 'opened' && !this.options.focus) { - this.modal.find(this.options.focusableScope).focus(); + this.modal.find(this.options.focusableScope).trigger('focus'); } else if (position === 'end') { - this.modal.find(this.options.modalCloseBtn).focus(); + this.modal.find(this.options.modalCloseBtn).trigger('focus'); } else if (position === 'start') { infelicity = 2; //Constant for find last focusable element focusableElements = this.modal.find(':focusable'); - focusableElements.eq(focusableElements.length - infelicity).focus(); + focusableElements.eq(focusableElements.length - infelicity).trigger('focus'); } }, @@ -331,7 +331,7 @@ define([ _close: function () { var trigger = _.bind(this._trigger, this, 'closed', this.modal); - $(this.focussedElement).focus(); + $(this.focussedElement).trigger('focus'); this._destroyOverlay(); this._unsetActive(); _.defer(trigger, this); diff --git a/app/design/adminhtml/Magento/backend/web/js/theme.js b/app/design/adminhtml/Magento/backend/web/js/theme.js index 069970deae681..e3fca76a7ad1a 100644 --- a/app/design/adminhtml/Magento/backend/web/js/theme.js +++ b/app/design/adminhtml/Magento/backend/web/js/theme.js @@ -224,7 +224,7 @@ define('globalNavigation', [ if (e.which === 13) { this._close(e); - $(selectors.topLevelHref, menuItem).focus(); + $(selectors.topLevelHref, menuItem).trigger('focus'); } }, diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/region-updater.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/region-updater.test.js index 0fdf36f0cfecc..be419dacbe500 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/region-updater.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/region-updater.test.js @@ -159,7 +159,7 @@ define([ }); it('Check that region list is not displayed when selected country has no predefined regions', function () { init(); - $(countryEl).val('GB').change(); + $(countryEl).val('GB').trigger('change'); expect($(regionInputEl).is(':visible')).toBe(true); expect($(regionInputEl).is(':disabled')).toBe(false); expect($(regionSelectEl).is(':visible')).toBe(false); @@ -167,7 +167,7 @@ define([ }); it('Check country that has predefined and optional regions', function () { init(); - $(countryEl).val('DE').change(); + $(countryEl).val('DE').trigger('change'); expect($(regionSelectEl).is(':visible')).toBe(true); expect($(regionSelectEl).is(':disabled')).toBe(false); expect($(regionSelectEl).hasClass('required-entry')).toBe(false); @@ -182,7 +182,7 @@ define([ }); it('Check country that has predefined and required regions', function () { init(); - $(countryEl).val('US').change(); + $(countryEl).val('US').trigger('change'); expect($(regionSelectEl).is(':visible')).toBe(true); expect($(regionSelectEl).is(':disabled')).toBe(false); expect($(regionSelectEl).hasClass('required-entry')).toBe(true); @@ -199,7 +199,7 @@ define([ init({ optionalRegionAllowed: false }); - $(countryEl).val('DE').change(); + $(countryEl).val('DE').trigger('change'); expect($(regionSelectEl).is(':visible')).toBe(false); expect($(regionInputEl).is(':visible')).toBe(false); }); @@ -226,7 +226,7 @@ define([ $(countryEl).val('GB'); $(regionInputEl).val('Liverpool'); init(); - $(countryEl).val('IT').change(); + $(countryEl).val('IT').trigger('change'); expect($(countryEl).val()).toBe('IT'); expect($(regionInputEl).val()).toBe(''); }); @@ -235,7 +235,7 @@ define([ init({ defaultRegion: '2' }); - $(countryEl).val('DE').change(); + $(countryEl).val('DE').trigger('change'); expect($(countryEl).val()).toBe('DE'); expect($(regionSelectEl).val()).toBe(''); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js index f12d36e888f22..2fc3a737ba38c 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/PageCache/frontend/js/page-cache.test.js @@ -185,7 +185,9 @@ define([ patternPlaceholderClose: /^ \/BLOCK (.+) $/ } }; + // eslint-disable-next-line jquery-no-bind-unbind replacer = $.mage.pageCache.prototype._replacePlaceholder.bind(context); + // eslint-disable-next-line jquery-no-bind-unbind searcher = $.mage.pageCache.prototype._searchPlaceholders.bind(context); wdContainer @@ -208,7 +210,9 @@ define([ patternPlaceholderClose: /^ \/BLOCK (.+) $/ } }; + // eslint-disable-next-line jquery-no-bind-unbind replacer = $.mage.pageCache.prototype._replacePlaceholder.bind(context); + // eslint-disable-next-line jquery-no-bind-unbind searcher = $.mage.pageCache.prototype._searchPlaceholders.bind(context); wdContainer diff --git a/dev/tests/js/jasmine/tests/lib/mage/backend/bootstrap.test.js b/dev/tests/js/jasmine/tests/lib/mage/backend/bootstrap.test.js index 60115c4ac2c64..2e89ea208762a 100644 --- a/dev/tests/js/jasmine/tests/lib/mage/backend/bootstrap.test.js +++ b/dev/tests/js/jasmine/tests/lib/mage/backend/bootstrap.test.js @@ -25,6 +25,7 @@ define([ $pageMainActions.appendTo('body'); $('body').notification(); + // eslint-disable-next-line jquery-no-event-shorthand $.ajaxSettings.error(); expect($('.message-error').length).toBe(1); diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc index 611105806dd4a..1f3581bd3734d 100644 --- a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc @@ -1,6 +1,7 @@ { "extends": [ "./.eslintrc-reset", - "./.eslintrc-magento" + "./.eslintrc-magento", + "./.eslintrc-jquery" ] -} \ No newline at end of file +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc-jquery b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc-jquery new file mode 100644 index 0000000000000..0051d6f0f302e --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc-jquery @@ -0,0 +1,11 @@ +{ + "rules": { + "jquery-no-andSelf": 2, + "jquery-no-bind-unbind": 2, + "jquery-no-click-event-shorthand": 2, + "jquery-no-delegate-undelegate": 2, + "jquery-no-event-shorthand": 2, + "jquery-no-size": 2, + "jquery-no-trim": 2 + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc-reset b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc-reset index ceb9a8c421c93..631a5a98e15a1 100644 --- a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc-reset +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc-reset @@ -1,22 +1,24 @@ { // http://eslint.org/docs/rules/ - "ecmaFeatures": { - "binaryLiterals": false, // enable binary literals - "blockBindings": false, // enable let and const (aka block bindings) - "defaultParams": false, // enable default function parameters - "forOf": false, // enable for-of loops - "generators": false, // enable generators - "objectLiteralComputedProperties": false, // enable computed object literal property names - "objectLiteralDuplicateProperties": false, // enable duplicate object literal properties in strict mode - "objectLiteralShorthandMethods": false, // enable object literal shorthand methods - "objectLiteralShorthandProperties": false, // enable object literal shorthand properties - "octalLiterals": false, // enable octal literals - "regexUFlag": false, // enable the regular expression u flag - "regexYFlag": false, // enable the regular expression y flag - "templateStrings": false, // enable template strings - "unicodeCodePointEscapes": false, // enable code point escapes - "jsx": false // enable JSX + "parserOptions": { + "ecmaFeatures": { + "binaryLiterals": false, // enable binary literals + "blockBindings": false, // enable let and const (aka block bindings) + "defaultParams": false, // enable default function parameters + "forOf": false, // enable for-of loops + "generators": false, // enable generators + "objectLiteralComputedProperties": false, // enable computed object literal property names + "objectLiteralDuplicateProperties": false, // enable duplicate object literal properties in strict mode + "objectLiteralShorthandMethods": false, // enable object literal shorthand methods + "objectLiteralShorthandProperties": false, // enable object literal shorthand properties + "octalLiterals": false, // enable octal literals + "regexUFlag": false, // enable the regular expression u flag + "regexYFlag": false, // enable the regular expression y flag + "templateStrings": false, // enable template strings + "unicodeCodePointEscapes": false, // enable code point escapes + "jsx": false // enable JSX + }, }, "env": { @@ -214,4 +216,4 @@ "no-bitwise": 0, // disallow use of bitwise operators (off by default) "no-plusplus": 0 // disallow use of unary operators, ++ and -- (off by default) } -} \ No newline at end of file +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-andSelf.js b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-andSelf.js new file mode 100644 index 0000000000000..f19e8611e828f --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-andSelf.js @@ -0,0 +1,26 @@ +'use strict' + +const utils = require('./utils.js') + +module.exports = { + meta: { + docs: {}, + schema: [] + }, + + create: function(context) { + return { + CallExpression: function(node) { + if (node.callee.type !== 'MemberExpression') return + if (node.callee.property.name !== 'andSelf') return + + if (utils.isjQuery(node)) { + context.report({ + node: node, + message: 'jQuery.andSelf() removed, use jQuery.addBack()' + }) + } + } + } + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-bind-unbind.js b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-bind-unbind.js new file mode 100644 index 0000000000000..cfa0caaec5719 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-bind-unbind.js @@ -0,0 +1,26 @@ +'use strict' + +const utils = require('./utils.js') + +module.exports = { + meta: { + docs: {}, + schema: [] + }, + + create: function(context) { + return { + CallExpression: function(node) { + if (node.callee.type !== 'MemberExpression') return + if (!['bind', 'unbind'].includes(node.callee.property.name)) return + + if (utils.isjQuery(node)) { + context.report({ + node: node, + message: 'jQuery $.bind and $.unbind are deprecated, use $.on and $.off instead' + }) + } + } + } + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-click-event-shorthand.js b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-click-event-shorthand.js new file mode 100644 index 0000000000000..4f1c6f1a07e8d --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-click-event-shorthand.js @@ -0,0 +1,28 @@ +'use strict' + +const utils = require('./utils.js') + +module.exports = { + meta: { + docs: {}, + schema: [] + }, + + create: function(context) { + return { + CallExpression: function(node) { + let names = ['blur', 'focus', 'focusin', 'focusout', 'resize', 'scroll', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'change', 'select', 'submit', 'keydown', 'keypress', 'keyup', 'contextmenu'], + name + if (node.callee.type !== 'MemberExpression') return + if (!names.includes(node.callee.property.name)) return + if (utils.isjQuery(node)) { + name = node.callee.property.name; + context.report({ + node: node, + message: 'Instead of .' + name + '(fn) use .on("' + name + '", fn). Instead of .' + name + '() use .trigger("' + name + '")' + }) + } + } + } + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-delegate-undelegate.js b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-delegate-undelegate.js new file mode 100644 index 0000000000000..e051ce4c21fb9 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-delegate-undelegate.js @@ -0,0 +1,26 @@ +'use strict' + +const utils = require('./utils.js') + +module.exports = { + meta: { + docs: {}, + schema: [] + }, + + create: function(context) { + return { + CallExpression: function(node) { + if (node.callee.type !== 'MemberExpression') return + if (!['delegate', 'undelegate'].includes(node.callee.property.name)) return + + if (utils.isjQuery(node)) { + context.report({ + node: node, + message: 'jQuery $.delegate and $.undelegate are deprecated, use $.on and $.off instead' + }) + } + } + } + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-event-shorthand.js b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-event-shorthand.js new file mode 100644 index 0000000000000..4966345965999 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-event-shorthand.js @@ -0,0 +1,28 @@ +'use strict' + +const utils = require('./utils.js') + +module.exports = { + meta: { + docs: {}, + schema: [] + }, + + create: function(context) { + return { + CallExpression: function(node) { + let names = ['load', 'unload', 'error'], + name + if (node.callee.type !== 'MemberExpression') return + if (!names.includes(node.callee.property.name)) return + if (utils.isjQuery(node)) { + name = node.callee.property.name; + context.report({ + node: node, + message: 'jQuery.' + name + '() was removed, use .on("' + name + '", fn) instead.' + }) + } + } + } + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-size.js b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-size.js new file mode 100644 index 0000000000000..9599d678a2d2e --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-size.js @@ -0,0 +1,26 @@ +'use strict' + +const utils = require('./utils.js') + +module.exports = { + meta: { + docs: {}, + schema: [] + }, + + create: function(context) { + return { + CallExpression: function(node) { + if (node.callee.type !== 'MemberExpression') return + if (node.callee.property.name !== 'size') return + + if (utils.isjQuery(node)) { + context.report({ + node: node, + message: 'jQuery.size() removed, use jQuery.length()' + }) + } + } + } + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-trim.js b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-trim.js new file mode 100644 index 0000000000000..ef3f5303e1182 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/jquery-no-trim.js @@ -0,0 +1,23 @@ +'use strict' + +module.exports = { + meta: { + docs: {}, + schema: [] + }, + + create: function(context) { + return { + CallExpression: function(node) { + if (node.callee.type !== 'MemberExpression') return + if (node.callee.object.name !== '$') return + if (node.callee.property.name !== 'trim') return + + context.report({ + node: node, + message: 'jQuery.trim is deprecated; use String.prototype.trim' + }) + } + } + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/utils.js b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/utils.js new file mode 100644 index 0000000000000..50f2840c78074 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery/utils.js @@ -0,0 +1,39 @@ +'use strict' + +function traverse(node) { + while (node) { + switch (node.type) { + case 'CallExpression': + node = node.callee + break + case 'MemberExpression': + node = node.object + break + case 'Identifier': + return node + default: + return null + } + } +} + +// Traverses from a node up to its root parent to determine if it +// originated from a jQuery `$()` function. +// +// node - The CallExpression node to start the traversal. +// +// Examples +// +// // $('div').find('p').first() +// isjQuery(firstNode) // => true +// +// Returns true if the function call node is attached to a jQuery element set. +function isjQuery(node) { + const id = traverse(node) + return id && id.name.startsWith('$') +} + +module.exports = { + traverse: traverse, + isjQuery: isjQuery +} diff --git a/dev/tools/grunt/configs/eslint.json b/dev/tools/grunt/configs/eslint.json index ec0d821a1c0d8..aa52c745a1dbc 100644 --- a/dev/tools/grunt/configs/eslint.json +++ b/dev/tools/grunt/configs/eslint.json @@ -2,6 +2,7 @@ "file": { "options": { "configFile": "dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc", + "rulePaths": ["dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery"], "reset": true, "useEslintrc": false } @@ -9,6 +10,7 @@ "test": { "options": { "configFile": "dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/.eslintrc", + "rulePaths": ["dev/tests/static/testsuite/Magento/Test/Js/_files/eslint/rules/jquery"], "reset": true, "outputFile": "dev/tests/static/eslint-error-report.xml", "format": "junit", diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index a74218466bcf5..a108baa142b81 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -481,6 +481,7 @@ define([ return; } //var dataElements = $(this.containerId+this.tableSufix).down('.data tbody').select('input', 'select'); + // eslint-disable-next-line jquery-no-click-event-shorthand dataElements = $(this.containerId + this.tableSufix).down('tbody').select('input', 'select'); for (i = 0; i < dataElements.length; i++) { @@ -1506,6 +1507,7 @@ define([ checkbox, checked; if (trElement) { + // eslint-disable-next-line jquery-no-click-event-shorthand checkbox = Element.select(trElement, 'input'); if (checkbox[0] && !checkbox[0].disabled) { @@ -1536,6 +1538,7 @@ define([ var checkbox, selectors, inputs, i; if (this.multidimensionalMode) { + // eslint-disable-next-line jquery-no-click-event-shorthand checkbox = $(row).select('.checkbox')[0]; selectors = this.inputsToManage.map(function (name) { return ['input[name="' + name + '"]', 'select[name="' + name + '"]']; diff --git a/lib/web/mage/apply/main.js b/lib/web/mage/apply/main.js index 827283da3dbd9..9442bffdb959b 100644 --- a/lib/web/mage/apply/main.js +++ b/lib/web/mage/apply/main.js @@ -34,6 +34,7 @@ define([ $el = $(el); if ($el[component]) { + // eslint-disable-next-line jquery-no-bind-unbind fn = $el[component].bind($el, config); } } diff --git a/lib/web/mage/backend/validation.js b/lib/web/mage/backend/validation.js index e19a0c8f32dd4..2162bf8f928b5 100644 --- a/lib/web/mage/backend/validation.js +++ b/lib/web/mage/backend/validation.js @@ -23,7 +23,7 @@ define([ if (this.settings.focusInvalid) { try { $(this.errorList.length && this.errorList[0].element || []) - .focus() + .trigger('focus') .trigger('focusin'); } catch (e) { // ignore IE throwing errors when focusing hidden elements diff --git a/lib/web/mage/decorate.js b/lib/web/mage/decorate.js index 437dee1be2888..07b591271b023 100644 --- a/lib/web/mage/decorate.js +++ b/lib/web/mage/decorate.js @@ -121,6 +121,7 @@ define([ } message = $.mage.__('Method %s does not exist on jQuery.decorate'); + // eslint-disable-next-line jquery-no-event-shorthand $.error(message.replace('%s', method)); }; }); diff --git a/lib/web/mage/gallery/gallery.js b/lib/web/mage/gallery/gallery.js index b38aa8dfd08ee..2ea5b0cbe621f 100644 --- a/lib/web/mage/gallery/gallery.js +++ b/lib/web/mage/gallery/gallery.js @@ -251,11 +251,11 @@ define([ infelicity; if (position === 'end') { - settings.$gallery.find(settings.closeIcon).focus(); + settings.$gallery.find(settings.closeIcon).trigger('focus'); } else if (position === 'start') { infelicity = 3; //Constant for find last focusable element focusableElements = settings.$gallery.find(':focusable'); - focusableElements.eq(focusableElements.length - infelicity).focus(); + focusableElements.eq(focusableElements.length - infelicity).trigger('focus'); } }, @@ -496,7 +496,7 @@ define([ settings.fotoramaApi.setOptions(settings.currentConfig.options); if (_.isNumber(index)) { - $selectable.eq(index).focus(); + $selectable.eq(index).trigger('focus'); } } },