Skip to content

Commit

Permalink
improve a11y (close #1206)
Browse files Browse the repository at this point in the history
  • Loading branch information
aristov committed Sep 29, 2015
1 parent f44b5c8 commit db4a138
Show file tree
Hide file tree
Showing 154 changed files with 894 additions and 185 deletions.
7 changes: 6 additions & 1 deletion common.blocks/attach/attach.tmpl-specs/10-simple.html
@@ -1 +1,6 @@
<span class="attach attach_theme_simple attach_size_l i-bem" data-bem="{&quot;attach&quot;:{}}"><span class="button button_theme_islands button_size_m button__control i-bem" data-bem="{&quot;button&quot;:{}}" role="button"><input class="attach__control" type="file" name="attach-name" tabindex="1"/></span><span class="attach__no-file">no file</span></span>
<span class="attach attach_theme_simple attach_size_l i-bem" data-bem="{&quot;attach&quot;:{}}">
<span class="button button_theme_islands button_size_m button__control i-bem" data-bem="{&quot;button&quot;:{}}" role="button">
<input class="attach__control" type="file" name="attach-name" tabindex="1"/>
</span>
<span class="attach__no-file">no file</span>
</span>
8 changes: 7 additions & 1 deletion common.blocks/attach/attach.tmpl-specs/20-disabled.html
@@ -1 +1,7 @@
<span class="attach attach_theme_simple attach_disabled i-bem" data-bem="{&quot;attach&quot;:{}}"><span class="button button_theme_simple button_disabled button__control i-bem" data-bem="{&quot;button&quot;:{}}" role="button" aria-disabled="true"><input class="attach__control" type="file" disabled="disabled"/><span class="button__text">file</span></span><span class="attach__no-file">no file</span></span>
<span class="attach attach_theme_simple attach_disabled i-bem" data-bem="{&quot;attach&quot;:{}}">
<span class="button button_theme_simple button_disabled button__control i-bem" data-bem="{&quot;button&quot;:{}}" role="button" aria-disabled="true">
<input class="attach__control" type="file" disabled="disabled"/>
<span class="button__text">file</span>
</span>
<span class="attach__no-file">no file</span>
</span>
5 changes: 5 additions & 0 deletions common.blocks/button/_togglable/button_togglable.bh.js
@@ -0,0 +1,5 @@
module.exports = function(bh) {
bh.match(['button_togglable_check', 'button_togglable_radio'], function(ctx) {
ctx.attr('aria-pressed', String(!!ctx.mod('checked')));
});
};
21 changes: 21 additions & 0 deletions common.blocks/button/_togglable/button_togglable.js
@@ -0,0 +1,21 @@
/**
* @module button
*/

modules.define('button', function(provide, Button) {

/**
* @exports
* @class button
* @bem
*/
provide(Button.decl({ modName : 'togglable' }, /** @lends button.prototype */{
onSetMod : {
'checked' : function(_, modVal) {
this.__base.apply(this, arguments);
this.domElem.attr('aria-pressed', !!modVal);
}
}
}));

});
@@ -0,0 +1,3 @@
block('button').mod('togglable', 'check').attrs()(function() {
return this.extend(applyNext(), { 'aria-pressed' : !!this.mods.checked });
});
Expand Up @@ -34,7 +34,7 @@ describe('button_togglable_check', function() {
});

describe('check/uncheck', function() {
it('should switch "checked" mod on "pointerpress"/"pointerreleaase" only if "pointerrelease" target is in block', function() {
it('should switch "checked" mod on "pointerpress"/"pointerrelease" only if "pointerrelease" target is in block', function() {
function triggerPointerUpPointerDown(onBlock) {
button.domElem
.trigger('pointerpress')
Expand All @@ -50,6 +50,13 @@ describe('button_togglable_check', function() {
triggerPointerUpPointerDown(false);
button.hasMod('checked').should.be.false;
});

it('should update aria-attributes properly', function() {
button.setMod('checked');
button.domElem.attr('aria-pressed').should.be.equal('true');
button.delMod('checked');
button.domElem.attr('aria-pressed').should.be.equal('false');
});
});
});

Expand Down
@@ -0,0 +1,3 @@
block('button').mod('togglable', 'radio').attrs()(function() {
return this.extend(applyNext(), { 'aria-pressed' : !!this.mods.checked });
});
Expand Up @@ -51,6 +51,13 @@ describe('button_togglable_radio', function() {
triggerPointerUpPointerDown(false);
button.hasMod('checked').should.be.false;
});

it('should update aria-attributes properly', function() {
button.setMod('checked');
button.domElem.attr('aria-pressed').should.be.equal('true');
button.delMod('checked');
button.domElem.attr('aria-pressed').should.be.equal('false');
});
});
});

Expand Down
2 changes: 1 addition & 1 deletion common.blocks/button/_type/button_type_link.bemhtml
Expand Up @@ -3,7 +3,7 @@ block('button').mod('type', 'link')(

attrs()(function() {
var ctx = this.ctx,
attrs = {};
attrs = { role : 'link' };

ctx.target && (attrs.target = ctx.target);
this.mods.disabled?
Expand Down
4 changes: 3 additions & 1 deletion common.blocks/button/_type/button_type_link.bh.js
@@ -1,7 +1,9 @@
module.exports = function(bh) {

bh.match('button_type_link', function(ctx, json) {
ctx.tag('a');
ctx
.tag('a')
.attr('role', 'link');

json.target && ctx.attr('target', json.target);
ctx.mod('disabled')?
Expand Down
8 changes: 6 additions & 2 deletions common.blocks/button/_type/button_type_link.js
Expand Up @@ -23,12 +23,16 @@ provide(Button.decl({ modName : 'type', modVal : 'link' }, /** @lends button.pro
'disabled' : {
'true' : function() {
this.__base.apply(this, arguments);
this.domElem.removeAttr('href');
this.domElem
.removeAttr('href')
.attr('aria-disabled', true);
},

'' : function() {
this.__base.apply(this, arguments);
this.domElem.attr('href', this._url);
this.domElem
.attr('href', this._url)
.removeAttr('aria-disabled');
}
}
},
Expand Down
4 changes: 3 additions & 1 deletion common.blocks/button/_type/button_type_link.spec.js
Expand Up @@ -45,12 +45,14 @@ describe('button_type_link', function() {
expect(button.domElem.attr('href')).to.be.undefined;
});

it('should remove "href" attribute on disable', function() {
it('should update attributes properly', function() {
button.setMod('disabled');
button.domElem.attr('aria-disabled').should.be.equal('true');
expect(button.domElem.attr('href')).to.be.undefined;

button.delMod('disabled');
button.domElem.attr('href').should.be.equal('/');
expect(button.domElem.attr('aria-disabled')).to.be.undefined;
});
});

Expand Down
5 changes: 5 additions & 0 deletions common.blocks/button/button.js
Expand Up @@ -41,6 +41,11 @@ provide(BEMDOM.decl({ block : this.name, baseBlock : Control }, /** @lends butto
'true' : function() {
this.__base.apply(this, arguments);
this.hasMod('togglable') || this.delMod('pressed');
this.domElem.attr('aria-disabled', true);
},
'' : function() {
this.__base.apply(this, arguments);
this.domElem.removeAttr('aria-disabled');
}
},

Expand Down
15 changes: 13 additions & 2 deletions common.blocks/button/button.spec.js
@@ -1,7 +1,9 @@
modules.define(
'spec',
['button', 'i-bem__dom', 'jquery', 'BEMHTML', 'sinon', 'keyboard__codes'],
function(provide, Button, BEMDOM, $, BEMHTML, sinon, keyCodes) {
['button', 'i-bem__dom', 'jquery', 'BEMHTML', 'sinon', 'keyboard__codes', 'chai'],
function(provide, Button, BEMDOM, $, BEMHTML, sinon, keyCodes, chai) {

var expect = chai.expect;

describe('button', function() {
var button;
Expand Down Expand Up @@ -83,6 +85,15 @@ describe('button', function() {

button.hasMod('pressed').should.be.false;
});

it('should set/remove "aria-disabled" attribute properly', function () {
button
.setMod('disabled')
.domElem.attr('aria-disabled').should.be.equal('true');

button.delMod('disabled');
expect(button.domElem.attr('aria-disabled')).to.be.undefined;
});
});

describe('click', function() {
Expand Down
5 changes: 4 additions & 1 deletion common.blocks/button/button.tmpl-specs/10-params.html
@@ -1 +1,4 @@
<button class="button button_focused button__control i-bem" data-bem="{&quot;button&quot;:{&quot;live&quot;:false}}" role="button" tabindex="0" type="button" id="btn" name="name" value="value"><span aria-hidden="true" class="icon icon_social_twitter"></span><span class="button__text">button</span></button>
<button class="button button_focused button__control i-bem" data-bem="{&quot;button&quot;:{&quot;live&quot;:false}}" role="button" tabindex="0" type="button" id="btn" name="name" value="value">
<span class="icon icon_social_twitter"></span>
<span class="button__text">button</span>
</button>
@@ -1 +1 @@
<a class="button button_type_link button__control i-bem" data-bem="{&quot;button&quot;:{}}" role="button" target="_blank" href="//ya.ru/yandsearch?text=&lt;script type=&quot;javascript&quot;&gt;&amp;amp;=test">content</a>
<a class="button button_type_link button__control i-bem" data-bem="{&quot;button&quot;:{}}" role="link" target="_blank" href="//ya.ru/yandsearch?text=&lt;script type=&quot;javascript&quot;&gt;&amp;amp;=test">content</a>
@@ -1 +1 @@
<a class="button button_type_link button_disabled button__control i-bem" data-bem="{&quot;button&quot;:{&quot;url&quot;:&quot;#&quot;}}" role="button" aria-disabled="true"></a>
<a class="button button_type_link button_disabled button__control i-bem" data-bem="{&quot;button&quot;:{&quot;url&quot;:&quot;#&quot;}}" role="link" aria-disabled="true"></a>
2 changes: 2 additions & 0 deletions common.blocks/checkbox-group/checkbox-group.bemhtml
@@ -1,6 +1,8 @@
block('checkbox-group')(
tag()('span'),

attrs()({ role : 'group' }),

js()(true),

mix()([{ block : 'control-group' }]),
Expand Down
1 change: 1 addition & 0 deletions common.blocks/checkbox-group/checkbox-group.bh.js
Expand Up @@ -3,6 +3,7 @@ module.exports = function(bh) {
bh.match('checkbox-group', function(ctx, json) {
ctx
.tag('span')
.attrs({ role : 'group' })
.js(true)
.mix({ block : 'control-group' });

Expand Down
@@ -1 +1,10 @@
<span class="checkbox-group checkbox-group_theme_islands checkbox-group_size_m control-group i-bem" data-bem="{&quot;checkbox-group&quot;:{}}"><label class="checkbox checkbox_theme_islands checkbox_size_m i-bem" data-bem="{&quot;checkbox&quot;:{}}"><span class="checkbox__box"><input class="checkbox__control" type="checkbox" autocomplete="off" name="default" value="1"/></span>first</label><br/><label class="checkbox checkbox_theme_islands checkbox_size_m checkbox_checked checkbox_disabled i-bem" data-bem="{&quot;checkbox&quot;:{}}"><span class="checkbox__box"><input class="checkbox__control" type="checkbox" autocomplete="off" name="default" value="3" checked="checked" disabled="disabled"/></span>third</label></span>
<span class="checkbox-group checkbox-group_theme_islands checkbox-group_size_m control-group i-bem" role="group" data-bem="{&quot;checkbox-group&quot;:{}}">
<label class="checkbox checkbox_theme_islands checkbox_size_m i-bem" data-bem="{&quot;checkbox&quot;:{}}">
<span class="checkbox__box"><input class="checkbox__control" type="checkbox" autocomplete="off" name="default" value="1"/></span>
<span class="checkbox__text" role="presentation">first</span>
</label><br/>
<label class="checkbox checkbox_theme_islands checkbox_size_m checkbox_checked checkbox_disabled i-bem" data-bem="{&quot;checkbox&quot;:{}}">
<span class="checkbox__box"><input class="checkbox__control" type="checkbox" autocomplete="off" name="default" value="3" checked="checked" disabled="disabled"/></span>
<span class="checkbox__text" role="presentation">third</span>
</label>
</span>
@@ -1 +1,10 @@
<span class="checkbox-group checkbox-group_type_button checkbox-group_disabled control-group i-bem" data-bem="{&quot;checkbox-group&quot;:{}}"><label class="checkbox checkbox_type_button checkbox_disabled i-bem" data-bem="{&quot;checkbox&quot;:{}}"><button class="button button_togglable_check button_disabled button__control i-bem" data-bem="{&quot;button&quot;:{}}" role="button" type="button" disabled="disabled"><span class="button__text">first</span></button><input class="checkbox__control" type="checkbox" autocomplete="off" disabled="disabled"/></label><label class="checkbox checkbox_type_button checkbox_disabled i-bem" data-bem="{&quot;checkbox&quot;:{}}"><button class="button button_togglable_check button_disabled button__control i-bem" data-bem="{&quot;button&quot;:{}}" role="button" type="button" disabled="disabled"><span aria-hidden="true" class="icon icon_social_vk"></span><span class="button__text">VK</span></button><input class="checkbox__control" type="checkbox" autocomplete="off" disabled="disabled"/></label></span>
<span class="checkbox-group checkbox-group_type_button checkbox-group_disabled control-group i-bem" role="group" data-bem="{&quot;checkbox-group&quot;:{}}">
<label class="checkbox checkbox_type_button checkbox_disabled i-bem" data-bem="{&quot;checkbox&quot;:{}}">
<button class="button button_togglable_check button_disabled button__control i-bem" role="checkbox" type="button" disabled="disabled" aria-checked="false" data-bem="{&quot;button&quot;:{}}"><span class="button__text">first</span></button>
<input class="checkbox__control" type="checkbox" autocomplete="off" disabled="disabled"/>
</label>
<label class="checkbox checkbox_type_button checkbox_disabled i-bem" data-bem="{&quot;checkbox&quot;:{}}">
<button class="button button_togglable_check button_disabled button__control i-bem" role="checkbox" type="button" disabled="disabled" aria-checked="false" data-bem="{&quot;button&quot;:{}}"><span class="icon icon_social_vk"></span><span class="button__text">VK</span></button>
<input class="checkbox__control" type="checkbox" autocomplete="off" disabled="disabled"/>
</label>
</span>
@@ -1 +1,3 @@
<span class="checkbox-group control-group i-bem" data-bem="{&quot;checkbox-group&quot;:{}}"><label class="checkbox checkbox_checked i-bem" data-bem="{&quot;checkbox&quot;:{}}"><span class="checkbox__box"><input class="checkbox__control" type="checkbox" autocomplete="off" checked="checked" value="1"/></span></label></span>
<span class="checkbox-group control-group i-bem" role="group" data-bem="{&quot;checkbox-group&quot;:{}}">
<label class="checkbox checkbox_checked i-bem" data-bem="{&quot;checkbox&quot;:{}}"><span class="checkbox__box"><input class="checkbox__control" type="checkbox" autocomplete="off" value="1" checked="checked"/></span></label>
</span>
4 changes: 4 additions & 0 deletions common.blocks/checkbox/__text/checkbox__text.bemhtml
@@ -0,0 +1,4 @@
block('checkbox').elem('text')(
tag()('span'),
attrs()({ role : 'presentation' })
);
7 changes: 7 additions & 0 deletions common.blocks/checkbox/__text/checkbox__text.bh.js
@@ -0,0 +1,7 @@
module.exports = function(bh) {
bh.match('checkbox__text', function(ctx) {
ctx
.tag('span')
.attrs({ role : 'presentation' });
});
};
5 changes: 5 additions & 0 deletions common.blocks/checkbox/_type/checkbox_type_button.bemhtml
Expand Up @@ -12,6 +12,11 @@ block('checkbox').mod('type', 'button')(
theme : mods.theme,
size : mods.size
},
attrs : {
role : 'checkbox',
'aria-pressed' : undefined,
'aria-checked' : !!mods.checked
},
title : ctx.title,
content : [
ctx.icon,
Expand Down
5 changes: 5 additions & 0 deletions common.blocks/checkbox/_type/checkbox_type_button.bh.js
Expand Up @@ -12,6 +12,11 @@ module.exports = function(bh) {
theme : mods.theme,
size : mods.size
},
attrs : {
role : 'checkbox',
'aria-pressed' : null,
'aria-checked' : String(!!mods.checked)
},
title : json.title,
content : [
json.icon,
Expand Down
2 changes: 1 addition & 1 deletion common.blocks/checkbox/_type/checkbox_type_button.deps.js
@@ -1,5 +1,5 @@
[{
mustDeps : 'button'
shouldDeps : { block : 'button', mods : { togglable : 'check' } }
},
{
tech : 'spec.js',
Expand Down
7 changes: 6 additions & 1 deletion common.blocks/checkbox/_type/checkbox_type_button.js
Expand Up @@ -26,7 +26,12 @@ provide(Checkbox.decl({ modName : 'type', modVal : 'button' }, /** @lends checkb
}
},

'checked' : proxyModToButton,
'checked' : function(_, checked) {
proxyModToButton.apply(this, arguments);
this._button.domElem
.removeAttr('aria-pressed') // checkbox accepts aria-checked instead of aria-pressed
.attr('aria-checked', !!checked);
},
'disabled' : proxyModToButton,
'focused' : function(modName, modVal) {
proxyModToButton.call(this, modName, modVal, false);
Expand Down
30 changes: 21 additions & 9 deletions common.blocks/checkbox/_type/checkbox_type_button.spec.js
@@ -1,10 +1,11 @@
modules.define(
'spec',
['checkbox', 'i-bem__dom', 'jquery', 'dom', 'BEMHTML'],
function(provide, Checkbox, BEMDOM, $, dom, BEMHTML) {
['checkbox', 'i-bem__dom', 'jquery', 'dom', 'BEMHTML', 'chai'],
function(provide, Checkbox, BEMDOM, $, dom, BEMHTML, chai) {

describe('checkbox_type_button', function() {
var checkbox;
var expect = chai.expect,
checkbox, button;

function buildCheckbox() {
return BEMDOM.init($(BEMHTML.apply({
Expand All @@ -20,6 +21,7 @@ describe('checkbox_type_button', function() {

beforeEach(function() {
checkbox = buildCheckbox();
button = checkbox.findBlockInside('button');
});

afterEach(function() {
Expand All @@ -29,30 +31,40 @@ describe('checkbox_type_button', function() {
describe('checked', function() {
it('should set/unset "checked" mod for button according to self', function() {
checkbox.setMod('checked');
checkbox.findBlockInside('button').hasMod('checked').should.be.true;
button.hasMod('checked').should.be.true;

checkbox.delMod('checked');
checkbox.findBlockInside('button').hasMod('checked').should.be.false;
button.hasMod('checked').should.be.false;
});

it('should set/unset aria-attributes properly', function() {
checkbox.setMod('checked');
expect(button.domElem.attr('aria-pressed')).to.be.undefined;
button.domElem.attr('aria-checked').should.be.equal('true');

checkbox.delMod('checked');
expect(button.domElem.attr('aria-pressed')).to.be.undefined;
button.domElem.attr('aria-checked').should.be.equal('false');
});
});

describe('disabled', function() {
it('should set/unset "disabled" mod for button according to self', function() {
checkbox.setMod('disabled');
checkbox.findBlockInside('button').hasMod('disabled').should.be.true;
button.hasMod('disabled').should.be.true;

checkbox.delMod('disabled');
checkbox.findBlockInside('button').hasMod('disabled').should.be.false;
button.hasMod('disabled').should.be.false;
});
});

describe('focused', function() {
it('should set/unset "focused" mod for button according to self', function() {
checkbox.setMod('focused');
checkbox.findBlockInside('button').hasMod('focused').should.be.true;
button.hasMod('focused').should.be.true;

checkbox.delMod('focused');
checkbox.findBlockInside('button').hasMod('focused').should.be.false;
button.hasMod('focused').should.be.false;
});
});
});
Expand Down
5 changes: 4 additions & 1 deletion common.blocks/checkbox/checkbox.bemhtml
Expand Up @@ -18,7 +18,10 @@ block('checkbox')(
val : ctx.val
}
},
ctx.text
ctx.text && {
elem : 'text',
content : ctx.text
}
];
})
);
5 changes: 4 additions & 1 deletion common.blocks/checkbox/checkbox.bh.js
Expand Up @@ -14,7 +14,10 @@ module.exports = function(bh) {
val : json.val
}
},
json.text
json.text && {
elem : 'text',
content : json.text
}
]);
});

Expand Down

0 comments on commit db4a138

Please sign in to comment.