Введение
Magento 2 использует библиотеку jQuery в качестве основы для интерактивных JS-компонентов во frontend-части магазина. Количество разработанных решений довольно обширное, но все же, часто у разработчиков возникает потребность изменять поведение тех или иных компонентов. Данная статья посвящена основам разработки и расширении jQuery widgets в том контексте, в котором нам преподносит его Magento 2.
Типовая постановка задачи
Давайте рассмотрим пример. Представьте что вам на текущем этапе проекта необходимо стилизовать footer, в котором содержатся основные ссылки магазина. Ниже на скриншоте показан один из примеров, у нас могут содержаться сгруппированные в колонки ссылки, колонок может быть произвольное количество. Под цифрой 1 обозначены условные заголовки группы ссылок, сами ссылки обозначены цифрой 2

В мобильной версии дизайна требуется следующий вид:

В desktop/tablet версиях дизайна никакой интерактивности к ссылкам применять не нужно, а в mobile версии ссылки скрываются/показываются при нажатии на заголовок.
Если обратиться к официальной документации Magento для frontend-разработчиков (Источник №1) то можно увидеть подходящий для нашего случая компонент Accordion (Источник №2). Специфика нашего дизайна в том что компонент должен активироваться только в мобильной версии, но в компоненте Accordion подходящих опций для задания breakpoints нет. Тем самым у нас возникает потребность в расширении функциональности стандартного Accordion. Мы добавим виджету возможность инициализироваться и уничтожаться в зависимости от заданных breakpoints.
Расширение виджета
Создадим файл:
app/design/frontend/{vendor}/{theme}/web/js/destroyableAccordion.js
расширяющий логику стандартного accordion, который располагается по следующему пути:
lib/web/mage/accordion.js
Зарегистрируем наш файл в конфигурации requirejs, для этого создадим файл:
app/design/frontend/{vendor}/{theme}/requirejs-config.js
И запишем следующее содержимое:
var config = {
map: {
"*": {
"destroyableAccordion": "js/destroyableAccordion"
}
}
};
На данном шаге мы добились того что создали алиас “destroyableAccordion” для нашего виджета, по которому можем инициализировать виджет или подключать его в более комплексных компонентах.
Запишем в destroyableAccordion следующее содержимое:
define([
'jquery',
'matchMedia',
'mage/accordion'
], function($ , mediaCheck){
$.widget(namespace.destroyableAccordion, $.mage.accordion, {
options: {
mediaType: null,
breakpoint: null
},
_create : function () {
this._super();
this._destroyOn();
},
_destroyOn : function(){
var breakPoint = this.options.breakpoint;
var mediaType = this.options.mediaType;
mediaCheck({
media: '(' + mediaType + ': ' + breakPoint + 'px)',
entry: $.proxy(function() {
this._destroy();
$.each(this.contents, function() {
$(this).show();
});
}, this),
exit: $.proxy(function() {
this._processPanels();
}, this)
});
}
});
return $.namespace.destroyableAccordion;
});
Рассмотрим вышепредставленный код:
С 1 по 5 строки - с помощью requirejs осуществляется импорт jQuery, библиотеки для работы с медиа-запросами matchMedia, и расширяемый mage/accordion.
6 строка - вызывается функция widget из jQuery, которая в нашем случае принимает три параметра:
- строку с названием нашего компонента с указанным namespace
- объект расширяемого виджета
- объект содержащий опции и расширяющие стандартную функциональность методы
С 7 по 10 строки - список опций, необходимых для передачи в функции destroyOn. Описывать опции в файле виджета с пустыми значениями не обязательно, достаточно передать их при инициализации, здесь опции перечислены исключительно для наглядности.
С 11 по 14 строки - функция _create() является конструктором и вызывается при создании виджета, this в этой функции - непосредственно инстанс виджета. В этой функции мы вызываем _create у расширяемого mage.accordion через вызов функции this._super(). И далее вызываем метод нашей функциональности this._destroyOn();
С 15 по 29 строки - описан метод _destroyOn(), в котором склеиваются переданные опции в строку медиазапроса, которая в дальнейшем используется в качестве аргумента для mediaCheck. При ресайзе браузера если разрешение экрана соответствует медиазапросу то вызывается метод this.destroy() у виджета, иначе this._processPanels(). Последняя функция описана в mage.tabs и содержит в себе логику кликов на табы для раскрытия целевого контента.
Теперь мы готовы инициализировать наш виджет через специальный атрибут Magento 2 data-mage-init (дополнительную информацию по способам инициализации JS-кода вы можете найти в источнике №3), ниже показан пример:
<ul class="footer-links_accordion" data-mage-init='{"destroyableAccordion":{"mediaType": "min-width","breakpoint": "768"}}'>
<li class="footer-links_accordion_item" role="tablist">
<div class="footer-links_accordion_item_title" data-role="collapsible" role="tab">
<div data-role="trigger"><span>Here to help</span></div>
</div>
<div class="footer-links_accordion_item_content" data-role="content" role="tabpanel">
<ul class="footer-list">
<li class="footer-list_item"><a href="#">My account</a></li>
</ul>
</div>
</li>
</ul>
Таким образом мы рассмотрели простейшие и самые лучшие техники по расширению JS-логики стандартных виджетов в Magento 2. Хорошим тоном является организация всех JS-файлов проекта в отдельных файлах, их дальнейший импорт через requirejs и использование декларативного подхода в инициализации JS.
