Skip to content

Вынос селекторов в общий конфиг (Issue #18)#97

Merged
biz87 merged 3 commits intobetafrom
feature/18-selectors-config
Feb 20, 2026
Merged

Вынос селекторов в общий конфиг (Issue #18)#97
biz87 merged 3 commits intobetafrom
feature/18-selectors-config

Conversation

@Ibochkarev
Copy link
Copy Markdown
Member

Описание

Вынос селекторов в общий конфиг (Issue #18). Все захардкоженные селекторы в JS перенесены в единый модуль Selectors.js с возможностью переопределения через ms3Config.selectors. Сохранена обратная совместимость: дефолтные селекторы включают и data-ms3-* атрибуты, и CSS-классы.

Основные изменения:

  • Новый файл assets/components/minishop3/js/web/core/Selectors.js — объект дефолтных селекторов и функция getSelectors() (слияние с ms3Config.selectors)
  • UI-классы (CartUI, OrderUI, QuantityUI, CustomerUI, ProductCardUI) и ms3.js используют селекторы из config.selectors вместо хардкода
  • Подключение Selectors.js в ms3_frontend_assets (перед ApiClient.js) + миграция для существующих установок
  • В MiniShop3.php в js_setting добавлено поле selectors: [] для переопределения на стороне шаблона

Тип изменений

  • Исправление бага (non-breaking change)
  • Новая функциональность (non-breaking change)
  • Breaking change (изменение, ломающее обратную совместимость)
  • Рефакторинг (без изменения функциональности)
  • Документация
  • Другое (опишите):

Связанные Issues

Closes #18

Как это было протестировано?

  • Ручное тестирование
  • Автоматические тесты (PHPStan, ESLint)
  • Тестирование на разных версиях PHP/MODX

Конфигурация тестирования:

  • MiniShop3: текущая ветка
  • MODX: —
  • PHP: —

Скриншоты (если применимо)

Не применимо — визуальное поведение не меняется.

Чеклист

  • Код соответствует стилю проекта
  • Добавлены/обновлены комментарии в сложных местах
  • Изменения не ломают существующую функциональность
  • Лексиконы добавлены на двух языках (ru/en) — не требуется
  • PHPStan проходит без новых ошибок
  • ESLint проходит без ошибок (для JS/Vue изменений)
  • Обновлён CHANGELOG.md (для значимых изменений)

Дополнительные заметки

  • Fallback: при отсутствии getSelectors используется window.Ms3DefaultSelectors; в UI-классах сохранены строковые fallback-селекторы
  • Для старых браузеров (IE11): в initFormHandler используется form.matches || form.msMatchesSelector || form.webkitMatchesSelector
  • Переопределение в шаблоне: ms3Config.selectors = { formOrder: '.my-order-form' } — частичное слияние с дефолтами

- Добавлен Selectors.js с дефолтными селекторами и getSelectors()
- Конфиг переопределяется через ms3Config.selectors
- UI-классы (CartUI, OrderUI, QuantityUI, CustomerUI, ProductCardUI)
  используют селекторы из конфига
- Fallback на CSS-классы для обратной совместимости
- Миграция для добавления Selectors.js в ms3_frontend_assets

Co-authored-by: Cursor <[email protected]>
@Ibochkarev Ibochkarev requested a review from biz87 February 17, 2026 17:10
@Ibochkarev Ibochkarev self-assigned this Feb 17, 2026
@biz87
Copy link
Copy Markdown
Member

biz87 commented Feb 17, 2026

Ревью PR #97

Подход правильный — централизация селекторов с возможностью переопределения. Есть несколько проблем.

1. Определение направления кнопок +/- не использует селекторы (значимо)

В QuantityUI.handleButtonClick() направление определяется хардкодом:

const isInc = e.target.getAttribute('data-ms3-qty') === 'inc' || e.target.classList.contains('inc-qty')
const isDec = e.target.getAttribute('data-ms3-qty') === 'dec' || e.target.classList.contains('dec-qty')

Если пользователь переопределит qtyInc: '.my-plus-btn', кнопка получит listener (через initButtons), но isInc/isDec её не распознают — направление не определится. Нужно чтобы handleButtonClick использовал e.target.matches(qtyIncSel) / e.target.matches(qtyDecSel) вместо хардкода.

2. IE11 polyfill — мёртвый код

const matches = form.matches || form.msMatchesSelector || form.webkitMatchesSelector

Весь код использует ?. (optional chaining), async/await, ...spread — ничего из этого не работает в IE11. Fallback на msMatchesSelector/webkitMatchesSelector никогда не выполнится. Достаточно просто form.matches(formSel).

3. .eslintrc.json — косметическое переформатирование

Не относится к issue #18. Единственное значимое изменение — добавление getSelectors в globals, остальное — шум в diff.

4. Повторяющийся паттерн (this.config?.selectors || {})

Эта конструкция встречается ~15 раз. Можно вынести в геттер или закешировать в конструкторе каждого UI-класса:

get sel () {
  return this.config?.selectors || {}
}

5. console.warn ссылается на внутренний конфиг

console.warn('ms3_link must be inside a form matching sel.form')

sel.form ничего не скажет разработчику при отладке. Лучше вывести реальный селектор:

console.warn(`ms3_link must be inside a form matching: ${formSel}`)

- QuantityUI: use e.target.matches() with quantityIncreaseSelector/quantityDecreaseSelector
  instead of hardcoded data-ms3-qty for custom selector support
- ms3.js: remove IE11 polyfill; use form.matches(formSelector)
- Add selectors getter to all UI classes and ms3; replace config?.selectors||{} with this.selectors
- Rename shortened vars: sel→selectors, qtyInputSel→quantityInputSelector,
  formSel→formSelector, linkSel→linkSelector, cartCostEl→cartCostElement, etc.
- Improve console.warn to show actual formSelector value

Refs #18

Co-authored-by: Cursor <[email protected]>
@Ibochkarev
Copy link
Copy Markdown
Member Author

@biz87 можно смотреть

@biz87
Copy link
Copy Markdown
Member

biz87 commented Feb 20, 2026

Ревью PR #97 (повторное)

Статус предыдущих замечаний

# Замечание Статус
1 QuantityUI.handleButtonClick() — isInc/isDec хардкод вместо селекторов ✅ Исправлено — e.target.matches(quantityIncreaseSelector)
2 IE11 polyfill — мёртвый код ✅ Исправлено — убран, просто form.matches(formSelector)
3 .eslintrc.json — косметическое переформатирование ❌ Не исправлено
4 Повторяющийся паттерн this.config?.selectors || {} ✅ Исправлено — геттер selectors в каждом классе
5 console.warn ссылается на sel.form вместо реального значения ✅ Исправлено — выводит ${formSelector}

Новые замечания

1. .eslintrc.json — косметика всё ещё в PR (minor)

Из предыдущего ревью. Единственное значимое изменение — "getSelectors": "readonly", остальное — перенос extends/env на одну строку. Лишний шум в diff. Стоит откатить форматирование, оставив только добавление getSelectors.

2. Дублирование inline fallback-ов (средне)

Каждое место использования содержит fallback-строку, идентичную дефолту в Selectors.js:

// Selectors.js
form: '[data-ms3-form], .ms3_form',

// ms3.js:115
const formSelector = selectors.form || '[data-ms3-form], .ms3_form'

// CartUI.js:50
const formSelector = selectors.form || '[data-ms3-form], .ms3_form'

// QuantityUI.js:96
const formSelector = selectors.form || '[data-ms3-form], .ms3_form'
// ...и ещё ~15 мест

Это частично подрывает цель централизации — при изменении дефолта в Selectors.js нужно менять все inline fallback-и.

selectors мержатся в ms3.config при init(), и все UI-классы получают уже готовый конфиг с селекторами (строка 71-75 ms3.js). Если Selectors.js не загрузился, getSelectors fallback на Ms3DefaultSelectors fallback на {} — и тогда inline fallback-и спасают ситуацию. Однако вероятность этого сценария крайне мала (файл в том же списке assets и загружается синхронно).

Предложение: оставить fallback-и только в ms3.js:init() (единственная точка входа), а в UI-классах убрать || '...' — доверять конфигу. Это устранит дублирование без потери надёжности.

3. 'selectors' => [] в MiniShop3.php (minor)

'selectors' => []

PHP [] → JSON [] (массив). Семантически это map (ключ-значение), должен быть объект:

'selectors' => (object)[]  // → JSON {}

Функционально работает (spread пустого массива в JS ничего не делает), но для консистентности лучше (object)[] или new \stdClass().

Что хорошо

  • Архитектура — правильное решение: дефолты + merge с пользовательскими значениями, единая точка конфигурации
  • Обратная совместимость — дефолтные селекторы включают и data-ms3-*, и CSS-классы
  • Миграция — идемпотентна, повторяет проверенный паттерн из add_product_card_ui / add_quantity_ui
  • Дедупликация — убран ручной dedup через Set в QuantityUI/CartUI (querySelectorAll с составным селектором и так не дублирует)
  • Геттеры — чистый паттерн get selectors() вместо повторения this.config?.selectors || {}
  • Переименованияselselectors, cartCostElcartCostElement — читаемее
  • Переход от getElementById к querySelector в OrderUI — необходим для поддержки переопределяемых селекторов

Вердикт

Approve с minor замечаниями. Замечания 1 и 3 — косметические, можно поправить перед мержем или после. Замечание 2 (inline fallback-и) — стоит обсудить, но не блокирует.

- Updated UI classes (CartUI, CustomerUI, OrderUI, ProductCardUI, QuantityUI) to directly use this.selectors for improved readability and maintainability.
- Enhanced ms3.js to merge default selectors with custom ones from ms3Config.
- Reformatted .eslintrc.json for consistency and added a rule to ignore specific unused variables.

Co-authored-by: Cursor <[email protected]>
@biz87 biz87 merged commit 5d82f5b into beta Feb 20, 2026
@Ibochkarev Ibochkarev deleted the feature/18-selectors-config branch February 20, 2026 18:51
@biz87 biz87 mentioned this pull request Feb 20, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Хранение селекторов в общем конфиге

2 participants