Skip to content

Commit

Permalink
Merge pull request #740 from alphagov/toggle-module
Browse files Browse the repository at this point in the history
Add a JS toggle module
  • Loading branch information
benlovell committed Feb 24, 2016
2 parents 63907bf + 9421183 commit 7ee7e18
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ gem 'govuk_frontend_toolkit', '~> 4.7.0'
if ENV['GOVUK_TEMPLATE_DEV']
gem 'govuk_template', :path => "../govuk_template"
else
gem 'govuk_template', '0.16.1'
gem 'govuk_template', '0.17.0'
end
gem 'gds-api-adapters', '26.7.0'
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ GEM
govuk_frontend_toolkit (4.7.0)
rails (>= 3.1.0)
sass (>= 3.2.0)
govuk_template (0.16.1)
govuk_template (0.17.0)
rails (>= 3.1)
http-cookie (1.0.2)
domain_name (~> 0.5)
Expand Down Expand Up @@ -257,7 +257,7 @@ DEPENDENCIES
govuk-content-schema-test-helpers (~> 1.4)
govuk-lint (~> 0.6.0)
govuk_frontend_toolkit (~> 4.7.0)
govuk_template (= 0.16.1)
govuk_template (= 0.17.0)
image_optim (= 0.17.1)
jasmine-rails (~> 0.10.6)
logstasher (= 0.4.8)
Expand Down
59 changes: 59 additions & 0 deletions app/assets/javascripts/modules/toggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
Toggle the display of elements
Use `data-controls` and `data-expanded` to indicate the
starting state and the IDs of the elements that the toggle
controls. This is synonymous with ARIA attributes, which
get added when starting.
*/
(function(Modules) {
"use strict";

Modules.Toggle = function() {
this.start = function($el) {
var toggleSelector = '[data-controls][data-expanded]';

$el.on('click', toggleSelector, toggle);
$el.find(toggleSelector).each(addAriaAttrs);

// Add the ARIA attributes with JavaScript
// If the JS fails and there's no interactive elements, having
// no aria attributes is an accurate representation.
function addAriaAttrs() {
var $toggle = $(this);
$toggle.attr('role', 'button');
$toggle.attr('aria-controls', $toggle.data('controls'));
$toggle.attr('aria-expanded', $toggle.data('expanded'));

var $targets = getTargetElements($toggle);
$targets.attr('aria-live', 'polite');
$targets.attr('role', 'region');
$toggle.data('$targets', $targets);
}

function toggle(event) {
var $toggle = $(event.target),
expanded = $toggle.attr('aria-expanded') === "true",
$targets = $toggle.data('$targets');

if (expanded) {
$toggle.attr('aria-expanded', false);
$targets.addClass('js-hidden');
} else {
$toggle.attr('aria-expanded', true);
$targets.removeClass('js-hidden');
}

event.preventDefault();
}

function getTargetElements($toggle) {
var ids = $toggle.attr('aria-controls').split(' '),
selector = '#' + ids.join(', #');

return $el.find(selector);
}
};
};

})(window.GOVUK.Modules);
1 change: 1 addition & 0 deletions app/assets/javascripts/start-modules.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//= require govuk/modules
//= require modules/toggle

$(document).ready(function () {
GOVUK.modules.start();
Expand Down
78 changes: 78 additions & 0 deletions spec/javascripts/modules/toggle.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
describe('A toggle module', function() {
"use strict";

var toggle,
element;

beforeEach(function() {
toggle = new GOVUK.Modules.Toggle();
});

describe('when starting', function() {
var element = $('\
<div>\
<a href="#" class="my-toggle" data-expanded="false" data-controls="target">Toggle</a>\
<div id="target">Target</div>\
</div>');

it('adds aria attributes to toggles', function() {
toggle.start(element);

var $toggle = element.find('.my-toggle');
expect($toggle.attr('role')).toBe('button');
expect($toggle.attr('aria-expanded')).toBe('false');
expect($toggle.attr('aria-controls')).toBe('target');
});
});

describe('when clicking a toggle', function() {
var element;

beforeEach(function() {
element = $('\
<div>\
<a href="#" class="my-toggle" data-expanded="false" data-controls="target">Toggle</a>\
<div id="target" class="js-hidden">Target</div>\
</div>');

toggle.start(element);
element.find('.my-toggle').trigger('click');
});

it('toggles the display of a target', function() {
expect(element.find('#target').is('.js-hidden')).toBe(false);
element.find('.my-toggle').trigger('click');
expect(element.find('#target').is('.js-hidden')).toBe(true);
});

it('updates the aria-expanded attribute on the toggle', function() {
expect(element.find('.my-toggle').attr('aria-expanded')).toBe('true');

element.find('.my-toggle').trigger('click');
expect(element.find('.my-toggle').attr('aria-expanded')).toBe('false');
});
});

describe('when clicking a toggle that controls multiple targets', function() {
it('toggles the display of each target', function() {
var element = $('\
<div>\
<a href="#" class="my-toggle" data-expanded="false" data-controls="target another-target">Toggle</a>\
<div id="target" class="js-hidden">Target</div>\
<div id="another-target" class="js-hidden">Another target</div>\
</div>');

toggle.start(element);
expect(element.find('#target').is('.js-hidden')).toBe(true);
expect(element.find('#another-target').is('.js-hidden')).toBe(true);

element.find('.my-toggle').trigger('click');
expect(element.find('#target').is('.js-hidden')).toBe(false);
expect(element.find('#another-target').is('.js-hidden')).toBe(false);

element.find('.my-toggle').trigger('click');
expect(element.find('#target').is('.js-hidden')).toBe(true);
expect(element.find('#another-target').is('.js-hidden')).toBe(true);
});
});
});

0 comments on commit 7ee7e18

Please sign in to comment.