Improved a11y of 'columnMark' checkboxes
authorMarcel Werk <burntime@woltlab.com>
Sat, 6 Oct 2018 20:56:46 +0000 (22:56 +0200)
committerMarcel Werk <burntime@woltlab.com>
Sat, 6 Oct 2018 20:56:46 +0000 (22:56 +0200)
See #2713

com.woltlab.wcf/templates/headIncludeJavaScript.tpl
wcfsetup/install/files/acp/templates/header.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Clipboard.js
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 0a6b88af46a4059bac9ea70dbb65d75c24b527aa..df510ce369062438bfedc3111bd6e578808f688d 100644 (file)
@@ -50,6 +50,8 @@ requirejs.config({
                        '__months': [ '{lang}wcf.date.month.january{/lang}', '{lang}wcf.date.month.february{/lang}', '{lang}wcf.date.month.march{/lang}', '{lang}wcf.date.month.april{/lang}', '{lang}wcf.date.month.may{/lang}', '{lang}wcf.date.month.june{/lang}', '{lang}wcf.date.month.july{/lang}', '{lang}wcf.date.month.august{/lang}', '{lang}wcf.date.month.september{/lang}', '{lang}wcf.date.month.october{/lang}', '{lang}wcf.date.month.november{/lang}', '{lang}wcf.date.month.december{/lang}' ], 
                        '__monthsShort': [ '{lang}wcf.date.month.short.jan{/lang}', '{lang}wcf.date.month.short.feb{/lang}', '{lang}wcf.date.month.short.mar{/lang}', '{lang}wcf.date.month.short.apr{/lang}', '{lang}wcf.date.month.short.may{/lang}', '{lang}wcf.date.month.short.jun{/lang}', '{lang}wcf.date.month.short.jul{/lang}', '{lang}wcf.date.month.short.aug{/lang}', '{lang}wcf.date.month.short.sep{/lang}', '{lang}wcf.date.month.short.oct{/lang}', '{lang}wcf.date.month.short.nov{/lang}', '{lang}wcf.date.month.short.dec{/lang}' ],
                        'wcf.clipboard.item.unmarkAll': '{lang}wcf.clipboard.item.unmarkAll{/lang}',
+                       'wcf.clipboard.item.markAll': '{lang}wcf.clipboard.item.markAll{/lang}',
+                       'wcf.clipboard.item.mark': '{lang}wcf.clipboard.item.mark{/lang}',
                        'wcf.date.relative.now': '{lang __literal=true}wcf.date.relative.now{/lang}',
                        'wcf.date.relative.minutes': '{capture assign=relativeMinutes}{lang __literal=true}wcf.date.relative.minutes{/lang}{/capture}{@$relativeMinutes|encodeJS}',
                        'wcf.date.relative.hours': '{capture assign=relativeHours}{lang __literal=true}wcf.date.relative.hours{/lang}{/capture}{@$relativeHours|encodeJS}',
index 2372bf6245f41ced1df026c08f1dfac486d339bc..5be17cdc4634a28fcea4270da1db9eed0cf9036b 100644 (file)
@@ -67,6 +67,8 @@
                                '__monthsShort': [ '{lang}wcf.date.month.short.jan{/lang}', '{lang}wcf.date.month.short.feb{/lang}', '{lang}wcf.date.month.short.mar{/lang}', '{lang}wcf.date.month.short.apr{/lang}', '{lang}wcf.date.month.short.may{/lang}', '{lang}wcf.date.month.short.jun{/lang}', '{lang}wcf.date.month.short.jul{/lang}', '{lang}wcf.date.month.short.aug{/lang}', '{lang}wcf.date.month.short.sep{/lang}', '{lang}wcf.date.month.short.oct{/lang}', '{lang}wcf.date.month.short.nov{/lang}', '{lang}wcf.date.month.short.dec{/lang}' ],
                                'wcf.acp.search.noResults': '{lang}wcf.acp.search.noResults{/lang}',
                                'wcf.clipboard.item.unmarkAll': '{lang}wcf.clipboard.item.unmarkAll{/lang}',
+                               'wcf.clipboard.item.markAll': '{lang}wcf.clipboard.item.markAll{/lang}',
+                               'wcf.clipboard.item.mark': '{lang}wcf.clipboard.item.mark{/lang}',
                                'wcf.date.relative.now': '{lang __literal=true}wcf.date.relative.now{/lang}',
                                'wcf.date.relative.minutes': '{capture assign=relativeMinutes}{lang __literal=true}wcf.date.relative.minutes{/lang}{/capture}{@$relativeMinutes|encodeJS}',
                                'wcf.date.relative.hours': '{capture assign=relativeHours}{lang __literal=true}wcf.date.relative.hours{/lang}{/capture}{@$relativeHours|encodeJS}',
index 2be8dcb930f6337b53985da70da2c5bc53370426..1be78855e66fabc6a4e2dbb4184e08e7f728bc4a 100644 (file)
@@ -57,6 +57,7 @@ define(
        var _callbackUnmarkAll = null;
        
        var _addPageOverlayActiveClass = false;
+       var _specialCheckboxSelector = '.messageCheckboxLabel > input[type="checkbox"], .message .messageClipboardCheckbox > input[type="checkbox"], .messageGroupList .columnMark > label > input[type="checkbox"]';
        
        /**
         * Clipboard API
@@ -95,6 +96,10 @@ define(
                                _options.pageClassNames.push(options.pageClassName);
                        }
                        
+                       if (!Element.prototype.matches) {
+                               Element.prototype.matches = Element.prototype.msMatchesSelector;
+                       }
+                       
                        this._initContainers();
                        
                        if (_options.hasMarkedItems && _elements.length) {
@@ -124,6 +129,21 @@ define(
                                
                                if (containerData === undefined) {
                                        var markAll = elBySel('.jsClipboardMarkAll', container);
+                                       
+                                       if (markAll.matches(_specialCheckboxSelector)) {
+                                               var label = markAll.closest('label');
+                                               elAttr(label, 'role', 'checkbox');
+                                               elAttr(label, 'tabindex', '0');
+                                               elAttr(label, 'aria-checked', false);
+                                               elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.markAll'));
+                                               
+                                               label.addEventListener('keyup', function (event) {
+                                                       if (event.keyCode === 13 || event.keyCode === 32) {
+                                                               checkbox.click();
+                                                       }
+                                               });
+                                       }
+                                       
                                        if (markAll !== null) {
                                                elData(markAll, 'container-id', containerId);
                                                markAll.addEventListener(WCF_CLICK_EVENT, this._markAll.bind(this));
@@ -145,6 +165,20 @@ define(
                                                elData(checkbox, 'container-id', containerId);
                                                
                                                (function(checkbox) {
+                                                       if (checkbox.matches(_specialCheckboxSelector)) {
+                                                               var label = checkbox.closest('label');
+                                                               elAttr(label, 'role', 'checkbox');
+                                                               elAttr(label, 'tabindex', '0');
+                                                               elAttr(label, 'aria-checked', false);
+                                                               elAttr(label, 'aria-label', Language.get('wcf.clipboard.item.mark'));
+                                                               
+                                                               label.addEventListener('keyup', function (event) {
+                                                                       if (event.keyCode === 13 || event.keyCode === 32) {
+                                                                               checkbox.click();
+                                                                       }
+                                                               });
+                                                       }
+                                                       
                                                        var link = checkbox.closest('a');
                                                        if (link === null) {
                                                                checkbox.addEventListener(WCF_CLICK_EVENT, _callbackCheckbox);
@@ -191,6 +225,11 @@ define(
                _markAll: function(event) {
                        var checkbox = event.currentTarget;
                        var isMarked = (checkbox.nodeName !== 'INPUT' || checkbox.checked);
+                       
+                       if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
+                               elAttr(checkbox.parentNode, 'aria-checked', isMarked);
+                       }
+                       
                        var objectIds = [];
                        
                        var containerId = elData(checkbox, 'container-id');
@@ -218,6 +257,10 @@ define(
                                        }
                                }
                                
+                               if (elAttr(item.parentNode, 'role') === 'checkbox') {
+                                       elAttr(item.parentNode, 'aria-checked', isMarked);
+                               }
+                               
                                var clipboardObject = DomTraverse.parentByClass(checkbox, 'jsClipboardObject');
                                if (clipboardObject !== null) {
                                        clipboardObject.classList[(isMarked ? 'addClass' : 'removeClass')]('jsMarked');
@@ -256,6 +299,14 @@ define(
                                }
                                
                                data.markAll.checked = markedAll;
+                               
+                               if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
+                                       elAttr(data.markAll.parentNode, 'aria-checked', isMarked);
+                               }
+                       }
+                       
+                       if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
+                               elAttr(checkbox.parentNode, 'aria-checked', checkbox.checked);
                        }
                        
                        this._saveState(type, [ objectId ], isMarked);
@@ -463,9 +514,17 @@ define(
                                                
                                                if (containerData.markAll !== null) {
                                                        containerData.markAll.checked = false;
+                                                       
+                                                       if (elAttr(containerData.markAll.parentNode, 'role') === 'checkbox') {
+                                                               elAttr(containerData.markAll.parentNode, 'aria-checked', false);
+                                                       }
                                                }
                                                for (var i = 0, length = containerData.checkboxes.length; i < length; i++) {
                                                        containerData.checkboxes[i].checked = false;
+                                                       
+                                                       if (elAttr(containerData.checkboxes[i].parentNode, 'role') === 'checkbox') {
+                                                               elAttr(containerData.checkboxes[i].parentNode, 'aria-checked', false);
+                                                       }
                                                }
                                                
                                                UiPageAction.remove('wcfClipboard-' + data.returnValues.objectType);
@@ -614,11 +673,19 @@ define(
                                
                                checkbox.checked = isMarked;
                                clipboardObject.classList[(isMarked ? 'add' : 'remove')]('jsMarked');
+                               
+                               if (elAttr(checkbox.parentNode, 'role') === 'checkbox') {
+                                       elAttr(checkbox.parentNode, 'aria-checked', isMarked);
+                               }
                        }
                        
                        if (data.markAll !== null) {
                                data.markAll.checked = markAll;
                                
+                               if (elAttr(data.markAll.parentNode, 'role') === 'checkbox') {
+                                       elAttr(data.markAll.parentNode, 'aria-checked', markAll);
+                               }
+                               
                                var parent = data.markAll;
                                while (parent = parent.parentNode) {
                                        if (parent instanceof Element && parent.classList.contains('columnMark')) {
index 069d7c683307bc0ccc2b75ce098f5ca1d6ca5710..a1b42b2478469b14db72ac977e25a992e7bb8ccc 100644 (file)
@@ -2527,6 +2527,8 @@ Fehler sind beispielsweise:
        
        <category name="wcf.clipboard">
                <item name="wcf.clipboard.item.unmarkAll"><![CDATA[Demarkieren]]></item>
+               <item name="wcf.clipboard.item.markAll"><![CDATA[Alle Elemente markieren]]></item>
+               <item name="wcf.clipboard.item.mark"><![CDATA[Element markieren]]></item>
                
                <item name="wcf.clipboard.item.com.woltlab.wcf.article.delete"><![CDATA[Endgültig löschen ({#$count})]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.article.delete.confirmMessage"><![CDATA[{if $count == 1}Einen{else}{#$count}{/if} Artikel löschen?]]></item>
index 04f2c51c7633cdfef3d27e83ff44a5e8e038abb4..8c49ecb8cd083f9f11efafbf5807376b5aed857c 100644 (file)
@@ -2463,6 +2463,8 @@ Errors are:
        
        <category name="wcf.clipboard">
                <item name="wcf.clipboard.item.unmarkAll"><![CDATA[Unmark All]]></item>
+               <item name="wcf.clipboard.item.markAll"><![CDATA[Mark All Objects]]></item>
+               <item name="wcf.clipboard.item.mark"><![CDATA[Mark Object]]></item>
                
                <item name="wcf.clipboard.item.com.woltlab.wcf.article.delete"><![CDATA[Delete ({#$count})]]></item>
                <item name="wcf.clipboard.item.com.woltlab.wcf.article.delete.confirmMessage"><![CDATA[Do you really want to delete {#$count} article{if $count != 1}s{/if}?]]></item>