@@ -117,6 +117,7 @@ define([
117
117
$element . hide ( ) ;
118
118
clearTimeout ( timer ) ;
119
119
} ) ;
120
+
120
121
$ ( document ) . on ( 'tap' , function ( ) {
121
122
$element . hide ( ) ;
122
123
clearTimeout ( timer ) ;
@@ -169,6 +170,9 @@ define([
169
170
//selector of product images gallery wrapper
170
171
mediaGallerySelector : '[data-gallery-role=gallery-placeholder]' ,
171
172
173
+ // selector of category product tile wrapper
174
+ selectorProductTile : '.product-item' ,
175
+
172
176
// number of controls to show (false or zero = show all)
173
177
numberToShow : false ,
174
178
@@ -178,6 +182,9 @@ define([
178
182
// enable label for control
179
183
enableControlLabel : true ,
180
184
185
+ // control label id
186
+ controlLabelId : '' ,
187
+
181
188
// text for more button
182
189
moreButtonText : 'More' ,
183
190
@@ -191,7 +198,10 @@ define([
191
198
mediaGalleryInitial : [ { } ] ,
192
199
193
200
//
194
- onlyMainImg : false
201
+ onlyMainImg : false ,
202
+
203
+ // whether swatches are rendered in product list or on product page
204
+ inProductList : false
195
205
} ,
196
206
197
207
/**
@@ -246,7 +256,9 @@ define([
246
256
'img' : $main . find ( '.product-image-photo' ) . attr ( 'src' )
247
257
} ] ;
248
258
}
249
- this . productForm = this . element . parents ( this . options . selectorProduct ) . find ( 'form:first' ) ;
259
+
260
+ this . productForm = this . element . parents ( this . options . selectorProductTile ) . find ( 'form:first' ) ;
261
+ this . inProductList = this . productForm . length > 0 ;
250
262
} ,
251
263
252
264
/**
@@ -264,9 +276,11 @@ define([
264
276
265
277
$ . each ( this . options . jsonConfig . attributes , function ( ) {
266
278
var item = this ,
267
- options = $widget . _RenderSwatchOptions ( item ) ,
279
+ controlLabelId = 'option-label-' + item . code + '-' + item . id ,
280
+ options = $widget . _RenderSwatchOptions ( item , controlLabelId ) ,
268
281
select = $widget . _RenderSwatchSelect ( item , chooseText ) ,
269
282
input = $widget . _RenderFormInput ( item ) ,
283
+ listLabel = '' ,
270
284
label = '' ;
271
285
272
286
// Show only swatch controls
@@ -276,22 +290,28 @@ define([
276
290
277
291
if ( $widget . options . enableControlLabel ) {
278
292
label +=
279
- '<span class="' + classes . attributeLabelClass + '">' + item . label + '</span>' +
293
+ '<span id="' + controlLabelId + '" class="' + classes . attributeLabelClass + '">' + item . label + '</span>' +
280
294
'<span class="' + classes . attributeSelectedOptionLabelClass + '"></span>' ;
281
295
}
282
296
283
- if ( $widget . productForm ) {
297
+ if ( $widget . inProductList ) {
284
298
$widget . productForm . append ( input ) ;
285
299
input = '' ;
300
+ listLabel = 'aria-label="' + item . label + '"' ;
301
+ } else {
302
+ listLabel = 'aria-labelledby="' + controlLabelId + '"' ;
286
303
}
287
304
288
305
// Create new control
289
306
container . append (
290
- '<div class="' + classes . attributeClass + ' ' + item . code +
291
- '" attribute-code="' + item . code +
292
- '" attribute-id="' + item . id + '">' +
293
- label +
294
- '<div class="' + classes . attributeOptionsWrapper + ' clearfix">' +
307
+ '<div class="' + classes . attributeClass + ' ' + item . code + '" ' +
308
+ 'attribute-code="' + item . code + '" ' +
309
+ 'attribute-id="' + item . id + '">' +
310
+ label +
311
+ '<div aria-activedescendant="" ' +
312
+ 'tabindex="0" ' +
313
+ 'role="listbox" ' + listLabel +
314
+ 'class="' + classes . attributeOptionsWrapper + ' clearfix">' +
295
315
options + select +
296
316
'</div>' + input +
297
317
'</div>'
@@ -336,10 +356,11 @@ define([
336
356
* Render swatch options by part of config
337
357
*
338
358
* @param {Object } config
359
+ * @param {String } controlId
339
360
* @returns {String }
340
361
* @private
341
362
*/
342
- _RenderSwatchOptions : function ( config ) {
363
+ _RenderSwatchOptions : function ( config , controlId ) {
343
364
var optionConfig = this . options . jsonSwatchConfig [ config . id ] ,
344
365
optionClass = this . options . classes . optionClass ,
345
366
moreLimit = parseInt ( this . options . numberToShow , 10 ) ,
@@ -375,11 +396,17 @@ define([
375
396
thumb = optionConfig [ id ] . hasOwnProperty ( 'thumb' ) ? optionConfig [ id ] . thumb : '' ;
376
397
label = this . label ? this . label : '' ;
377
398
attr =
399
+ ' id="' + controlId + '-item-' + id + '"' +
400
+ ' aria-checked="false"' +
401
+ ' aria-describedby="' + controlId + '"' +
402
+ ' tabindex="0"' +
378
403
' option-type="' + type + '"' +
379
404
' option-id="' + id + '"' +
380
405
' option-label="' + label + '"' +
406
+ ' aria-label="' + label + '"' +
381
407
' option-tooltip-thumb="' + thumb + '"' +
382
- ' option-tooltip-value="' + value + '"' ;
408
+ ' option-tooltip-value="' + value + '"' +
409
+ ' role="option"' ;
383
410
384
411
if ( ! this . hasOwnProperty ( 'products' ) || this . products . length <= 0 ) {
385
412
attr += ' option-empty="true"' ;
@@ -392,19 +419,19 @@ define([
392
419
} else if ( type === 1 ) {
393
420
// Color
394
421
html += '<div class="' + optionClass + ' color" ' + attr +
395
- '" style="background: ' + value +
422
+ ' style="background: ' + value +
396
423
' no-repeat center; background-size: initial;">' + '' +
397
424
'</div>' ;
398
425
} else if ( type === 2 ) {
399
426
// Image
400
427
html += '<div class="' + optionClass + ' image" ' + attr +
401
- '" style="background: url(' + value + ') no-repeat center; background-size: initial;">' + '' +
428
+ ' style="background: url(' + value + ') no-repeat center; background-size: initial;">' + '' +
402
429
'</div>' ;
403
430
} else if ( type === 3 ) {
404
431
// Clear
405
432
html += '<div class="' + optionClass + '" ' + attr + '></div>' ;
406
433
} else {
407
- // Defaualt
434
+ // Default
408
435
html += '<div class="' + optionClass + '" ' + attr + '>' + label + '</div>' ;
409
436
}
410
437
} ) ;
@@ -460,10 +487,9 @@ define([
460
487
'type="text" ' +
461
488
'value="" ' +
462
489
'data-selector="super_attribute[' + config . id + ']" ' +
463
- 'data-validate="{required:true}" ' +
490
+ 'data-validate="{required: true}" ' +
464
491
'aria-required="true" ' +
465
- 'aria-invalid="true" ' +
466
- 'style="visibility: hidden; position:absolute; left:-1000px">' ;
492
+ 'aria-invalid="false">' ;
467
493
} ,
468
494
469
495
/**
@@ -472,22 +498,38 @@ define([
472
498
* @private
473
499
*/
474
500
_EventListener : function ( ) {
501
+ var $widget = this ,
502
+ options = this . options . classes ;
475
503
476
- var $widget = this ;
477
-
478
- $widget . element . on ( 'click' , '.' + this . options . classes . optionClass , function ( ) {
504
+ $widget . element . on ( 'click' , '.' + options . optionClass , function ( ) {
479
505
return $widget . _OnClick ( $ ( this ) , $widget ) ;
480
506
} ) ;
481
507
482
- $widget . element . on ( 'change' , '.' + this . options . classes . selectClass , function ( ) {
508
+ $widget . element . on ( 'change' , '.' + options . selectClass , function ( ) {
483
509
return $widget . _OnChange ( $ ( this ) , $widget ) ;
484
510
} ) ;
485
511
486
- $widget . element . on ( 'click' , '.' + this . options . classes . moreButton , function ( e ) {
512
+ $widget . element . on ( 'click' , '.' + options . moreButton , function ( e ) {
487
513
e . preventDefault ( ) ;
488
514
489
515
return $widget . _OnMoreClick ( $ ( this ) ) ;
490
516
} ) ;
517
+
518
+ $widget . element . on ( 'keydown' , function ( e ) {
519
+ if ( e . which == 13 ) {
520
+ var target = $ ( e . target ) ;
521
+
522
+ if ( target . is ( '.' + options . optionClass ) ) {
523
+ return $widget . _OnClick ( target , $widget ) ;
524
+ } else if ( target . is ( '.' + options . selectClass ) ) {
525
+ return $widget . _OnChange ( target , $widget ) ;
526
+ } else if ( target . is ( '.' + options . moreButton ) ) {
527
+ e . preventDefault ( ) ;
528
+
529
+ return $widget . _OnMoreClick ( target ) ;
530
+ }
531
+ }
532
+ } ) ;
491
533
} ,
492
534
493
535
/**
@@ -498,13 +540,17 @@ define([
498
540
* @private
499
541
*/
500
542
_OnClick : function ( $this , $widget ) {
501
-
502
543
var $parent = $this . parents ( '.' + $widget . options . classes . attributeClass ) ,
544
+ $wrapper = $this . parents ( '.' + $widget . options . classes . attributeOptionsWrapper ) ,
503
545
$label = $parent . find ( '.' + $widget . options . classes . attributeSelectedOptionLabelClass ) ,
504
546
attributeId = $parent . attr ( 'attribute-id' ) ,
547
+ $input = $parent . find ( '.' + $widget . options . classes . attributeInput ) ;
548
+
549
+ if ( $widget . inProductList ) {
505
550
$input = $widget . productForm . find (
506
551
'.' + $widget . options . classes . attributeInput + '[name="super_attribute[' + attributeId + ']"]'
507
552
) ;
553
+ }
508
554
509
555
if ( $this . hasClass ( 'disabled' ) ) {
510
556
return ;
@@ -514,11 +560,13 @@ define([
514
560
$parent . removeAttr ( 'option-selected' ) . find ( '.selected' ) . removeClass ( 'selected' ) ;
515
561
$input . val ( '' ) ;
516
562
$label . text ( '' ) ;
563
+ $this . attr ( 'aria-checked' , false ) ;
517
564
} else {
518
565
$parent . attr ( 'option-selected' , $this . attr ( 'option-id' ) ) . find ( '.selected' ) . removeClass ( 'selected' ) ;
519
566
$label . text ( $this . attr ( 'option-label' ) ) ;
520
567
$input . val ( $this . attr ( 'option-id' ) ) ;
521
568
$this . addClass ( 'selected' ) ;
569
+ $widget . _toggleCheckedAttributes ( $this , $wrapper ) ;
522
570
}
523
571
524
572
$widget . _Rebuild ( ) ;
@@ -533,6 +581,19 @@ define([
533
581
$input . trigger ( 'change' ) ;
534
582
} ,
535
583
584
+ /**
585
+ * Toggle accessibility attributes
586
+ *
587
+ * @param {Object } $this
588
+ * @param {Object } $wrapper
589
+ * @private
590
+ */
591
+ _toggleCheckedAttributes : function ( $this , $wrapper ) {
592
+ $wrapper . attr ( 'aria-activedescendant' , $this . attr ( 'id' ) )
593
+ . find ( '.' + this . options . classes . optionClass ) . attr ( 'aria-checked' , false ) ;
594
+ $this . attr ( 'aria-checked' , true ) ;
595
+ } ,
596
+
536
597
/**
537
598
* Event for select
538
599
*
@@ -543,9 +604,13 @@ define([
543
604
_OnChange : function ( $this , $widget ) {
544
605
var $parent = $this . parents ( '.' + $widget . options . classes . attributeClass ) ,
545
606
attributeId = $parent . attr ( 'attribute-id' ) ,
607
+ $input = $parent . find ( '.' + $widget . options . classes . attributeInput ) ;
608
+
609
+ if ( $widget . productForm . length > 0 ) {
546
610
$input = $widget . productForm . find (
547
611
'.' + $widget . options . classes . attributeInput + '[name="super_attribute[' + attributeId + ']"]'
548
612
) ;
613
+ }
549
614
550
615
if ( $this . val ( ) > 0 ) {
551
616
$parent . attr ( 'option-selected' , $this . val ( ) ) ;
@@ -687,7 +752,6 @@ define([
687
752
'prices' : $widget . _getPrices ( result , $productPrice . priceBox ( 'option' ) . prices )
688
753
}
689
754
) ;
690
-
691
755
} ,
692
756
693
757
/**
0 commit comments