From 67e90fa6059da8f92f76fa7427c1443e5d0fe8b1 Mon Sep 17 00:00:00 2001 From: boo-code Date: Thu, 3 Sep 2026 04:12:30 +0200 Subject: [PATCH] Give the quantity spinner buttons an accessible name TouchSpin builds the increase and decrease buttons itself and puts nothing in them but an icon element, so assistive technology and accessibility checkers see two unnamed buttons - "a button is empty or has no value text". The buttons do not exist until TouchSpin has run, so the labels travel on the quantity input as data attributes, where they can be translated, and are applied to the generated buttons right after initialisation. Done for the three places the spinner is built: the product page, the quick view and the cart. --- _dev/js/cart.js | 3 ++ _dev/js/components/touchspin-labels.js | 42 +++++++++++++++++++ _dev/js/listing.js | 3 ++ _dev/js/product.js | 3 ++ .../catalog/_partials/product-add-to-cart.tpl | 2 + .../_partials/cart-detailed-product-line.tpl | 2 + 6 files changed, 55 insertions(+) create mode 100644 _dev/js/components/touchspin-labels.js diff --git a/_dev/js/cart.js b/_dev/js/cart.js index 7271878a..aea6fcee 100644 --- a/_dev/js/cart.js +++ b/_dev/js/cart.js @@ -1,5 +1,6 @@ import $ from 'jquery'; import prestashop from 'prestashop'; +import applyTouchspinLabels from './components/touchspin-labels'; import debounce from './components/debounce'; prestashop.cart = prestashop.cart || {}; @@ -79,6 +80,8 @@ function createSpin() { min: parseInt($(spinner).attr('min'), 10), max: 1000000, }); + + applyTouchspinLabels($(spinner)); }); $(prestashop.themeSelectors.touchspin).off('touchstart.touchspin'); diff --git a/_dev/js/components/touchspin-labels.js b/_dev/js/components/touchspin-labels.js new file mode 100644 index 00000000..a19c86ff --- /dev/null +++ b/_dev/js/components/touchspin-labels.js @@ -0,0 +1,42 @@ +/** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.md. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA and Contributors + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + */ + +/** + * Gives the buttons TouchSpin generates an accessible name. + * + * The buttons hold nothing but an icon element, so assistive technology and accessibility + * checkers see an unnamed button. TouchSpin builds them itself, after the template has been + * rendered, which is why the labels travel on the input as data attributes and are applied here + * rather than written in the template. + * + * @param {jQuery} $input the quantity input TouchSpin was initialised on + */ +export default function applyTouchspinLabels($input) { + const increase = $input.data('increaseLabel'); + const decrease = $input.data('decreaseLabel'); + const $group = $input.closest('.bootstrap-touchspin'); + + if (increase) { + $group.find('.bootstrap-touchspin-up').attr('aria-label', increase); + } + + if (decrease) { + $group.find('.bootstrap-touchspin-down').attr('aria-label', decrease); + } +} diff --git a/_dev/js/listing.js b/_dev/js/listing.js index e3fe44d9..720f03c1 100644 --- a/_dev/js/listing.js +++ b/_dev/js/listing.js @@ -24,6 +24,7 @@ */ import $ from 'jquery'; import prestashop from 'prestashop'; +import applyTouchspinLabels from './components/touchspin-labels'; // eslint-disable-next-line import "velocity-animate"; import updateSources from './components/update-sources'; @@ -107,6 +108,8 @@ $(document).ready(() => { max: 1000000, }); + applyTouchspinLabels(qv.find(prestashop.selectors.quantityWanted)); + $(prestashop.themeSelectors.touchspin).off('touchstart.touchspin'); }; diff --git a/_dev/js/product.js b/_dev/js/product.js index 67decd8c..d9c244bd 100644 --- a/_dev/js/product.js +++ b/_dev/js/product.js @@ -24,6 +24,7 @@ */ import $ from 'jquery'; import prestashop from 'prestashop'; +import applyTouchspinLabels from './components/touchspin-labels'; import ProductSelect from './components/product-select'; import updateSources from './components/update-sources'; @@ -128,6 +129,8 @@ $(document).ready(() => { max: 1000000, }); + applyTouchspinLabels($quantityInput); + $(prestashop.themeSelectors.touchspin).off('touchstart.touchspin'); $quantityInput.on('focusout', () => { diff --git a/templates/catalog/_partials/product-add-to-cart.tpl b/templates/catalog/_partials/product-add-to-cart.tpl index b059b085..ff7a4a0e 100644 --- a/templates/catalog/_partials/product-add-to-cart.tpl +++ b/templates/catalog/_partials/product-add-to-cart.tpl @@ -39,6 +39,8 @@ min="{if $product.quantity_required}{$product.quantity_required}{else}1{/if}" class="input-group" aria-label="{l s='Quantity' d='Shop.Theme.Actions'}" + data-increase-label="{l s='Increase quantity' d='Shop.Theme.Actions'}" + data-decrease-label="{l s='Decrease quantity' d='Shop.Theme.Actions'}" > diff --git a/templates/checkout/_partials/cart-detailed-product-line.tpl b/templates/checkout/_partials/cart-detailed-product-line.tpl index 01825c20..93a1607c 100644 --- a/templates/checkout/_partials/cart-detailed-product-line.tpl +++ b/templates/checkout/_partials/cart-detailed-product-line.tpl @@ -136,6 +136,8 @@ {else}