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