Hoo man has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/93490


Change subject: Make collapsible sections accessible to screen reader users
......................................................................

Make collapsible sections accessible to screen reader users

This required making it possible to expand/ collapse them using
the keyboard. Furthermore I've addedd the relevant aria attributes
so that screen reader users can know about the collapsible sections.

Change-Id: Ie176f7fda685c320e7bc2fc740be59194adba945
---
M javascripts/modules/mf-toggle.js
M tests/javascripts/modules/test_mf-toggle.js
2 files changed, 126 insertions(+), 35 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend 
refs/changes/90/93490/1

diff --git a/javascripts/modules/mf-toggle.js b/javascripts/modules/mf-toggle.js
index 6f28e60..8eba54f 100644
--- a/javascripts/modules/mf-toggle.js
+++ b/javascripts/modules/mf-toggle.js
@@ -19,8 +19,16 @@
         * @param {jQuery object} $heading A heading belonging to a section
         */
        function toggle( $heading ) {
+               var isCollapsed = $heading.is( '.openSection' );
+
                $heading.toggleClass( 'openSection' );
-               $heading.next().toggleClass( 'openSection' );
+               $heading.next()
+                       .toggleClass( 'openSection' )
+                       .attr( {
+                               'aria-pressed': isCollapsed ? 'false' : 'true',
+                               'aria-expanded': isCollapsed ? 'false' : 'true'
+                       } );
+
                M.emit( 'section-toggle', $heading );
        }
 
@@ -56,19 +64,47 @@
                $headings = $page.find( '.section_heading' );
                $headings.next( 'div' ).addClass( 'content_block' );
 
-               // use mouseup because mousedown blocks the click event and 
links
-               // in headings won't work
-               // FIXME change when micro.tap.js in stable
-               $headings.on( M.tapEvent( 'mouseup' ), function() {
-                       toggle( $( this ) );
-               } );
+               $headings.each( function ( i ) {
+                       var $elem = $( this );
 
-               // in beta expand all sections by default on wide screen 
devices (in beta and alpha)
-               if ( M.isWideScreen() && mw.config.get( 'wgMFMode' ) !== 
'stable' ) {
-                       $headings.each( function() {
+                       $elem.next( '.content_block' ).eq(0)
+                               .attr( {
+                                       // We need to give each content block a 
unique id as that's
+                                       // the only way we can tell screen 
readers what element we're
+                                       // referring to (aria-controls)
+                                       'id': 'content_block_' + i,
+                                       'aria-pressed': 'false',
+                                       'aria-expanded': 'false'
+                               } );
+
+                       $elem.attr( {
+                               role: 'button',
+                               tabindex: 0,
+                               'aria-haspopup': 'true',
+                               'aria-controls': 'content_block_' + i
+                       } )
+                       // FIXME change when micro.tap.js in stable
+                       .on( M.tapEvent( 'mouseup' ), function() {
                                toggle( $( this ) );
+                       } )
+                       .on( 'keypress', function( e ) {
+                               if ( e.which === 13 || e.which === 32 ) {
+                                       // Only handle keypresses on the 
"Enter" or "Space" keys
+                                       toggle( $( this ) );
+                               }
                        } );
-               }
+
+                       // Make sure certain actions from child links don't 
bubble up, so
+                       // that users can still use them without toggling a 
section.
+                       $elem.find( 'a' ).on( 'keypress mouseup', function( e ) 
{
+                               e.stopPropagation();
+                       } );
+
+                       // In beta expand sections by default on wide screen 
devices (in beta and alpha)
+                       if ( M.isWideScreen() && mw.config.get( 'wgMFMode' ) 
!== 'stable' ) {
+                               toggle( $elem );
+                       }
+               } );
 
                // FIXME: remove when this class is no longer in cached pages
                $( '.section_anchors' ).remove();
diff --git a/tests/javascripts/modules/test_mf-toggle.js 
b/tests/javascripts/modules/test_mf-toggle.js
index cf3ea41..8a510d4 100644
--- a/tests/javascripts/modules/test_mf-toggle.js
+++ b/tests/javascripts/modules/test_mf-toggle.js
@@ -5,11 +5,11 @@
 
 function makeSections() {
        $container = $( '<div>' ).appendTo( '#content' );
-       $( '<h2 class="section_heading" id="section_1"><span 
id="First_Section">First Section</span></h2>' ).appendTo( $container );
-       $( '<div class="content_block" id="content_1"><p>Text</p></div>' 
).appendTo( $container );
+       $( '<h2 id="section_0"><span id="First_Section">First 
Section</span></h2>' ).appendTo( $container );
+       $( '<div><p>Text</p></div>' ).appendTo( $container );
 
-       $( '<h2 class="section_heading" id="section_2">' ).appendTo( $container 
);
-       $( '<div class="content_block" id="content_2">' ).appendTo( $container 
);
+       $( '<h2 id="section_1"><a href="#foo">Dummy Link</a></h2>' ).appendTo( 
$container );
+       $( '<div>' ).appendTo( $container );
        return $container;
 }
 QUnit.module( 'MobileFrontend toggle.js: wm_toggle_section', {
@@ -17,7 +17,7 @@
                $container = makeSections();
                sinon.stub( M, 'isWideScreen' ).returns( false );
                toggle.enable();
-               $("#section_1,#content_1,#anchor_1").addClass("openSection");
+               toggle.toggle( $( '#section_0' ) );
        },
        teardown: function() {
                window.location.hash = "#";
@@ -27,47 +27,102 @@
 });
 
 QUnit.test( 'wm_toggle_section', 5, function() {
-       strictEqual($("#section_1").hasClass("openSection"), true, "openSection 
class present");
-       toggle.toggle( $( '#section_1' ) );
-       strictEqual($("#content_1").hasClass("openSection"), false, "check 
content is closed on a toggle");
-       strictEqual($("#section_1").hasClass("openSection"), false, "check 
section is closed");
+       strictEqual($("#section_0").hasClass("openSection"), true, "openSection 
class present");
+       toggle.toggle( $( '#section_0' ) );
+       strictEqual($("#content_block_0").hasClass("openSection"), false, 
"check content is closed on a toggle");
+       strictEqual($("#section_0").hasClass("openSection"), false, "check 
section is closed");
 
        // perform second toggle
-       toggle.toggle( $( '#section_1' ) );
-       strictEqual($("#content_1").hasClass("openSection"), true, "check 
content reopened");
-       strictEqual($("#section_1").hasClass("openSection"), true, "check 
section has reopened");
+       toggle.toggle( $( '#section_0' ) );
+       strictEqual($("#content_block_0").hasClass("openSection"), true, "check 
content reopened");
+       strictEqual($("#section_0").hasClass("openSection"), true, "check 
section has reopened");
 });
 
 QUnit.test( 'clicking a hash link to reveal an already open section', 2, 
function() {
-       strictEqual($("#section_1").hasClass("openSection"), true, "check 
section is open");
+       strictEqual($("#section_0").hasClass("openSection"), true, "check 
section is open");
        toggle.reveal( 'First_Section' );
-       strictEqual($("#section_1").hasClass("openSection"), true, "check 
section is still open");
+       strictEqual($("#section_0").hasClass("openSection"), true, "check 
section is still open");
 });
 
 QUnit.test( 'reveal', 2, function() {
-       toggle.reveal( '#First_Section_2' );
-       strictEqual($("#content_1").hasClass("openSection"), true, "check 
content is visible on a toggle");
-       strictEqual($("#section_1").hasClass("openSection"), true, "check 
section is marked as open");
+       toggle.reveal( 'First_Section' );
+       strictEqual($("#content_block_0").hasClass("openSection"), true, "check 
content is visible on a toggle");
+       strictEqual($("#section_0").hasClass("openSection"), true, "check 
section is marked as open");
 });
 
 QUnit.test( 'clicking hash links', 2, function() {
-       $( '[href=#First_Section_2]' ).trigger( 'click' );
-       strictEqual($("#content_1").hasClass("openSection"), true, "check 
content is visible on a toggle");
-       strictEqual($("#section_1").hasClass("openSection"), true, "check 
section marked as open");
+       $( '[href=#First_Section]' ).trigger( 'click' );
+       strictEqual($("#content_block_0").hasClass("openSection"), true, "check 
content is visible on a toggle");
+       strictEqual($("#section_0").hasClass("openSection"), true, "check 
section marked as open");
 });
 
 QUnit.test( 'clicking a heading toggles it', 2, function() {
-       var visibilityStart = $("#content_2").hasClass("openSection");
-       $( '#section_2' ).trigger( M.tapEvent( 'mouseup' ) );
+       var visibilityStart = $("#content_block_1").hasClass("openSection");
+       $( '#section_1' ).trigger( M.tapEvent( 'mouseup' ) );
        strictEqual(visibilityStart, false, "check content is hidden at start");
-       strictEqual($("#content_2").hasClass("openSection"), true, "check 
content is shown on a toggle");
+       strictEqual($("#content_block_1").hasClass("openSection"), true, "check 
content is shown on a toggle");
 });
+
+QUnit.test( 'Pressing space/ enter toggles a heading', 3, function () {
+       var $section = $( '#section_1' ),
+               $content = $( '#content_block_1' );
+
+       strictEqual( $content.hasClass( 'openSection' ), false, 'check content 
is hidden at start' );
+
+       // Open the section with pressing space
+       var e = jQuery.Event( 'keypress' );
+       e.which = 32;
+       $section.trigger( e );
+       strictEqual( $content.hasClass( 'openSection' ), true, 'check content 
is shown after pressing space' );
+
+       // Enter should close the section again
+       e.which = 13;
+       $section.trigger( e );
+       strictEqual( $content.hasClass( 'openSection' ), false, 'check content 
is hidden after pressing enter' );
+} );
+
+QUnit.test( "Clicking a link within a heading isn't triggering a toggle", 2, 
function () {
+       var $section = $( '#section_1' ),
+               $content = $( '#content_block_1' );
+
+       strictEqual( $content.hasClass( 'openSection' ), false, 'check content 
is hidden at start' );
+
+       $section.find( '> a' ).eq(0).trigger( 'mouseup' );
+       strictEqual( $content.hasClass( 'openSection' ), false, 'check content 
is still being hidden after clicking on the link' );
+} );
+
+QUnit.test( "Verify aria attributes", 9, function () {
+       var $section = $( '#section_1' ),
+               $content = $( '#content_block_1' );
+
+       // Test the initial state produced by the init function
+       strictEqual( $content.hasClass( 'openSection' ), false, 'check content 
is hidden at start' );
+       strictEqual( $content.attr( 'aria-pressed' ), 'false', 'check 
aria-pressed is false at start' );
+       strictEqual( $content.attr( 'aria-expanded' ), 'false', 'check 
aria-expanded is false at start' );
+
+       // Test what the toggle() function gives us when hiding the section
+       $section.trigger( 'mouseup' );
+       strictEqual( $content.hasClass( 'openSection' ), true, 'check content 
is visible after toggling' );
+       strictEqual( $content.attr( 'aria-pressed' ), 'true', 'check 
aria-pressed is true after toggling' );
+       strictEqual( $content.attr( 'aria-expanded' ), 'true', 'check 
aria-expanded is true after toggling' );
+
+       // Test what the toggle() function gives us when showing the section
+       $section.trigger( 'mouseup' );
+       strictEqual( $content.hasClass( 'openSection' ), false, 'check content 
is hidden after toggling' );
+       strictEqual( $content.attr( 'aria-pressed' ), 'false', 'check 
aria-pressed is false after toggling' );
+       strictEqual( $content.attr( 'aria-expanded' ), 'false', 'check 
aria-expanded is false after toggling' );
+} );
 
 QUnit.module( 'MobileFrontend toggle.js: tablet mode', {
        setup: function() {
+               // As this test only works in non-stable mode we have to change
+               // the mode here. This sucks...
+               var oldMode = mw.config.get( 'wgMFMode' );
+               mw.config.set( 'wgMFMode', 'beta' );
                $container = makeSections();
                sinon.stub( M, 'isWideScreen' ).returns( true );
                toggle.enable();
+               mw.config.set( 'wgMFMode', oldMode );
        },
        teardown: function() {
                window.location.hash = "#";
@@ -77,7 +132,7 @@
 } );
 
 QUnit.test( 'open by default', 1, function() {
-       strictEqual( $( '#content_1' ).hasClass( 'openSection' ), true, 'check 
section is visible at start' );
+       strictEqual( $( '#content_block_1' ).hasClass( 'openSection' ), true, 
'check section is visible at start' );
 } );
 
 }( mw.mobileFrontend, jQuery ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/93490
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie176f7fda685c320e7bc2fc740be59194adba945
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Hoo man <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to