Creates accessible tabs - a single content area with multiple panels.
21 |
22 |
Example A
23 |
An example with the default options.
24 |
25 |
Tabs panel one
26 |
27 |
Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.
28 |
29 |
Tabs panel two
30 |
31 |
Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis.
32 |
Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.
33 |
34 |
Tabs panel three
35 |
36 |
Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.
37 |
38 |
39 |
40 |
Initialisation
41 |
42 |
43 | $(".example-a").accTabs();
44 |
45 |
46 |
47 |
Example B
48 |
Open a different tab by default.
49 |
50 |
Tabs panel one
51 |
52 |
Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.
53 |
54 |
Tabs panel two
55 |
56 |
Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis.
57 |
Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.
58 |
59 |
Tabs panel three
60 |
61 |
Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.
Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.
80 |
81 |
Tabs panel two 2
82 |
83 |
84 |
Tabs panel one
85 |
86 |
Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.
87 |
88 |
Tabs panel two
89 |
90 |
Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis.
91 |
Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.
92 |
93 |
94 |
95 |
Tabs panel three
96 |
97 |
Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.
98 |
99 |
100 |
101 |
102 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
157 |
158 |
--------------------------------------------------------------------------------
/jquery.accTabs.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery Accessible Tabs
3 | *
4 | * @description: Creates accessible tabs - a single content area with multiple panels
5 | * @source: https://github.com/nomensa/jquery.accessible-tabs.git
6 | * @version: '0.1.2'
7 | *
8 | * @author: Nomensa
9 | * @license: licenced under MIT - http://opensource.org/licenses/mit-license.php
10 | */
11 |
12 | (function($, window, document, undefined) {
13 | 'use strict';
14 |
15 | var pluginName,
16 | defaults,
17 | counter = 0;
18 |
19 | pluginName = 'accTabs';
20 | defaults = {
21 | // Specify which tab to open by default using 0-based position
22 | defaultTab: 0,
23 | // Callback when the plugin is created
24 | callbackCreate: function() {},
25 | // Callback when the plugin is destroyed
26 | callbackDestroy: function() {},
27 | // A class applied to the target element
28 | containerClass: 'js-tabs',
29 | // A class applied to the active tab control
30 | controlActiveClass: 'js-tabs_control-item--active',
31 | // An explanation of how the tabs operate, which is prepended to the tabs content
32 | controlsText: 'Use the tab and enter or arrow keys to move between tabs',
33 | // Class to apply to the controls text element
34 | controlsTextClass: 'js-tabs_control-text',
35 | // A class applied to the active panel
36 | panelActiveClass: 'js-tabs_panel--active',
37 | // Class to apply the controls list
38 | tabControlsClass: 'js-tabs_control',
39 | // Ids for tab controls should start with the following string
40 | tabControlId: 'js-tabs_control-item--',
41 | // Class to be applied to the tab panel
42 | tabPanelClass: 'js-tabs_panel',
43 | // Ids for tab panels should start with the following string
44 | tabPanelId: 'js-tabs_panel--',
45 | // Callback when the tab is activated
46 | callbackTabActivated: function() {}
47 | };
48 |
49 | function AccTabs(element, options) {
50 | /*
51 | Constructor function for the tabs plugin
52 | */
53 | var self = this;
54 |
55 | self.element = $(element);
56 | // Combine user options with default options
57 | self.options = $.extend({}, defaults, options);
58 |
59 | function init() {
60 | /*
61 | Our init function to create an instance of the plugin
62 | */
63 | self.controlsWrapper = $('
"),i.element.prepend(i.controlsTextWrapper),i.activateTab(a("button",i.controlsWrapper).eq(i.options.defaultTab)),i.element.addClass(i.options.containerClass),h++,i.options.callbackCreate()}function e(){return i.handleClick=function(a){a.preventDefault(),i.activateTab(this),i.options.callbackTabActivated()},i.handleClick}function f(){return i.handleKeyDown=function(b){switch(b.which){case 37:case 38:b.preventDefault(),0!==a(this).parent().prev().length?a(this).parent().prev().find("> button").focus().click():a(i.controlsWrapper).find("li:last > button").focus().click();break;case 39:case 40:b.preventDefault(),0!==a(this).parent().next().length?a(this).parent().next().find("> button").focus().click():a(i.controlsWrapper).find("li:first > button").focus().click()}},i.handleKeyDown}var i=this;i.element=a(b),i.options=a.extend({},g,c),d()}var f,g,h=0;f="accTabs",g={defaultTab:0,callbackCreate:function(){},callbackDestroy:function(){},containerClass:"js-tabs",controlActiveClass:"js-tabs_control-item--active",controlsText:"Use the tab and enter or arrow keys to move between tabs",controlsTextClass:"js-tabs_control-text",panelActiveClass:"js-tabs_panel--active",tabControlsClass:"js-tabs_control",tabControlId:"js-tabs_control-item--",tabPanelClass:"js-tabs_panel",tabPanelId:"js-tabs_panel--",callbackTabActivated:function(){}},e.prototype.activateTab=function(b){if("false"===a(b).attr("aria-selected")){var c=this.options.controlActiveClass,d="#"+a(b).attr("data-controls");0!==a('[aria-selected="true"]',this.controlsWrapper).length&&(a('[aria-selected="true"]',this.controlsWrapper).attr("aria-selected","false").parent("li").removeClass(c),a('> [aria-hidden="false"]',this.element).attr("aria-hidden","true").hide().removeClass(this.options.panelActiveClass)),a(b,this.element).attr("aria-selected","true"),a(b,this.element).parent("li").addClass(c),a(d,this.element).attr("aria-hidden","false").show().addClass(this.options.panelActiveClass)}},e.prototype.rebuild=function(){return new e(this.element,this.options)},e.prototype.destroy=function(){var b=this;this.element.removeClass(this.options.containerClass),a("> ."+this.options.controlsTextClass,this.element).remove(),a("> ."+this.options.tabControlsClass,this.element).remove(),a("> div",this.element).prev().removeAttr("style"),a("> div",this.element).each(function(c,d){a(d).removeAttr("aria-hidden aria-labelledby id role style").removeClass(b.options.tabPanelClass)}),this.options.callbackDestroy()},a.fn[f]=function(b){return this.each(function(){a.data(this,"plugin_"+f)||a.data(this,"plugin_"+f,new e(this,b))})}}(jQuery,window,document);
--------------------------------------------------------------------------------
/jquery.accTabs.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('accessible-tabs', function() {
4 |
5 | var markUp =
6 | '
' +
7 | '
Tabs panel one
' +
8 | '
' +
9 | '
Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.
' +
10 | '
' +
11 | '
Tabs panel two
' +
12 | '
' +
13 | '
Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis.
' +
14 | '
Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.
' +
15 | '
' +
16 | '
Tabs panel three
' +
17 | '
' +
18 | '
Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.
' +
19 | '
' +
20 | '
',
21 | testElement,
22 | testElement2;
23 |
24 | beforeEach(function() {
25 | testElement = $(markUp);
26 | });
27 |
28 | it('depends on jQuery', function() {
29 | expect($).toBeDefined();
30 | });
31 |
32 | it('should be protected against multiple instantiations', function() {
33 | var plugin = testElement.accTabs();
34 | expect(plugin === testElement.accTabs()).toBe(true);
35 | });
36 |
37 | describe('- plugin init', function() {
38 |
39 | beforeEach(function() {
40 | testElement.accTabs();
41 | });
42 |
43 | it('should creating an unordered list for the tab controls', function() {
44 | expect(testElement.find('> ul').hasClass('js-tabs_control')).toBe(true);
45 | expect(testElement.find('> ul').attr('role')).toBe('tablist');
46 | });
47 |
48 | it('should create a control for each tab panel', function() {
49 | var numberOfPanels = testElement.find('> div').length;
50 |
51 | expect(testElement.find('> ul > li').length).toBe(numberOfPanels);
52 |
53 | $('> ul > li', testElement).each(function(index, value) {
54 | expect($(value).attr('role')).toBe('presentation');
55 |
56 | expect($(value).find('> button').attr('aria-selected')).toBeDefined();
57 | expect($(value).find('> button').attr('role')).toBe('tab');
58 | });
59 | });
60 |
61 | it('should hide the heading for each tab panel', function() {
62 |
63 | $('> div', testElement).each(function(index, value) {
64 | expect($(value).prev().is(':hidden')).toBe(true);
65 | });
66 | });
67 |
68 | it('should add a class and attributes to each tab panel', function() {
69 |
70 | $('> div', testElement).each(function(index, value) {
71 | expect($(value).hasClass('js-tabs_panel')).toBe(true);
72 | expect($(value).attr('aria-labelledby')).toBeDefined();
73 | expect($(value).attr('aria-hidden')).toBeDefined();
74 | expect($(value).attr('id')).toBeDefined();
75 | expect($(value).attr('role')).toBe('tabpanel');
76 | });
77 | });
78 |
79 | it('should add the explanatory control text', function() {
80 | expect(testElement.find('.js-tabs_control-text').length).toBe(1);
81 | });
82 |
83 | it('should add the active class to the tabs', function() {
84 | expect(testElement.hasClass('js-tabs')).toBe(true);
85 | });
86 |
87 | it('should trigger "callbackCreate" once the plugin has been created', function() {
88 | var mocks,
89 | el,
90 | created = false;
91 |
92 | mocks = {
93 | callbackCreate: function(testElement) {
94 | created = true;
95 | }
96 | },
97 | el = testElement.accTabs({
98 | callbackCreate: mocks.callbackCreate
99 | });
100 |
101 | expect(created).toBe(true);
102 | });
103 |
104 | describe('- createHandleKeyDown function', function() {
105 | var triggerKeyDown = function (element, keyCode) {
106 | /*
107 | Keyboard trigger function
108 | */
109 | var inputEl = element.find('> button'),
110 | e = $.Event('keydown');
111 |
112 | e.which = keyCode;
113 | inputEl.trigger(e);
114 | };
115 |
116 | it('should handle arrow left keyboard events', function() {
117 | $('> ul > li', testElement).each(function() {
118 | triggerKeyDown($(this), 37);
119 |
120 | expect($(this).find('> button').parent('li').hasClass('js-tabs_control-item--active')).toBe(false);
121 | expect($(this).find('> button').attr('aria-selected')).toBe('false');
122 |
123 | // Check if there is a previous control tab or move the last
124 | if ($(this).prev().length !== 0) {
125 | expect($(this).prev().find('> button').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
126 | expect($(this).prev().find('> button').attr('aria-selected')).toBe('true');
127 | } else {
128 | expect($(this).parent().find('li:last > button').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
129 | expect($(this).parent().find('li:last > button').attr('aria-selected')).toBe('true');
130 | }
131 | });
132 | });
133 |
134 | it('should handle arrow up keyboard events', function() {
135 | $('> ul > li', testElement).each(function() {
136 | triggerKeyDown($(this), 38);
137 |
138 | expect($(this).find('> button').parent('li').hasClass('js-tabs_control-item--active')).toBe(false);
139 | expect($(this).find('> button').attr('aria-selected')).toBe('false');
140 |
141 | // Check if there is a previous control tab or move the last
142 | if ($(this).prev().length !== 0) {
143 | expect($(this).prev().find('> button').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
144 | expect($(this).prev().find('> button').attr('aria-selected')).toBe('true');
145 | } else {
146 | expect($(this).parent().find('li:last > button').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
147 | expect($(this).parent().find('li:last > button').attr('aria-selected')).toBe('true');
148 | }
149 | });
150 | });
151 |
152 | it('should handle arrow right keyboard events', function() {
153 | $('> ul > li', testElement).each(function() {
154 | triggerKeyDown($(this), 39);
155 |
156 | expect($(this).find('> button').parent('li').hasClass('js-tabs_control-item--active')).toBe(false);
157 | expect($(this).find('> button').attr('aria-selected')).toBe('false');
158 |
159 | // Check if there is a next control tab or move the first
160 | if ($(this).next().length !== 0) {
161 | expect($(this).next().find('> button').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
162 | expect($(this).next().find('> button').attr('aria-selected')).toBe('true');
163 | } else {
164 | expect($(this).parent().find('li:first > button').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
165 | expect($(this).parent().find('li:first > button').attr('aria-selected')).toBe('true');
166 | }
167 | });
168 | });
169 |
170 | it('should handle arrow down keyboard events', function() {
171 | $('> ul > li', testElement).each(function() {
172 | triggerKeyDown($(this), 40);
173 |
174 | expect($(this).find('> button').parent('li').hasClass('js-tabs_control-item--active')).toBe(false);
175 | expect($(this).find('> button').attr('aria-selected')).toBe('false');
176 |
177 | // Check if there is a next control tab or move the first
178 | if ($(this).next().length !== 0) {
179 | expect($(this).next().find('> button').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
180 | expect($(this).next().find('> button').attr('aria-selected')).toBe('true');
181 | } else {
182 | expect($(this).parent().find('li:first > button').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
183 | expect($(this).parent().find('li:first > button').attr('aria-selected')).toBe('true');
184 | }
185 | });
186 | });
187 | });
188 | });
189 |
190 | describe('- activateTab method', function() {
191 |
192 | beforeEach(function() {
193 | testElement = $(markUp);
194 | testElement.accTabs();
195 | });
196 |
197 | it('should add a class and attributes to the first tab control', function() {
198 | expect(testElement.find('button:first').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
199 | expect(testElement.find('button:first').attr('aria-selected')).toBe('true');
200 | });
201 |
202 | it('should show the first tab panel', function() {
203 | expect(testElement.find('div:first').css('display')).toBe('block');
204 | expect(testElement.find('div:first').attr('aria-hidden')).toBe('false');
205 | });
206 | });
207 |
208 | describe('- createHandleClick method', function() {
209 | it('should trigger "callbackTabActivated" once a tab has been clicked', function() {
210 | var activated = false;
211 |
212 | testElement.accTabs({
213 | callbackTabActivated: function() {
214 | activated = true;
215 | }
216 | });
217 |
218 | var button = testElement.find('.js-tabs_control li:nth-child(2) button');
219 | button.trigger('click');
220 | expect(activated).toBe(true);
221 | });
222 | });
223 |
224 | describe('- rebuild method', function() {
225 |
226 | beforeEach(function() {
227 | testElement2 = $(markUp);
228 | });
229 |
230 | it('should reinitiate the plugin', function() {
231 | var plugin = testElement.accTabs();
232 |
233 | plugin.data('plugin_accTabs').destroy();
234 | plugin.data('plugin_accTabs').rebuild();
235 |
236 | expect(plugin === testElement.accTabs()).toBe(true);
237 | });
238 | });
239 |
240 | describe('- destroy method', function() {
241 |
242 | beforeEach(function() {
243 | testElement2 = $(markUp);
244 | });
245 |
246 | it('should remove the container class', function() {
247 | testElement2.accTabs();
248 |
249 | testElement2.data('plugin_accTabs').destroy();
250 |
251 | expect(testElement2.hasClass('js-tabs')).toBe(false);
252 | });
253 |
254 | it('should remove the tab controls help text', function() {
255 | testElement2.accTabs();
256 |
257 | testElement2.data('plugin_accTabs').destroy();
258 |
259 | expect(testElement2.find('.js-tabs_control').length).toBe(0);
260 | });
261 |
262 | it('should remove the tab controls', function() {
263 | testElement2.accTabs();
264 |
265 | testElement2.data('plugin_accTabs').destroy();
266 |
267 | expect(testElement2.find('.js-tabs_control-text').length).toBe(0);
268 | });
269 |
270 | it('should reset the headings back to their original state', function() {
271 | testElement2.accTabs();
272 |
273 | testElement2.data('plugin_accTabs').destroy();
274 |
275 | expect(testElement2.find('> div').prev().attr('style')).toBe(undefined);
276 | });
277 |
278 | it('should reset the tab panels back to their original state', function() {
279 | testElement2.accTabs();
280 |
281 | testElement2.data('plugin_accTabs').destroy();
282 |
283 | expect(testElement2.find('> div').attr('aria-hidden')).toBe(undefined);
284 | expect(testElement2.find('> div').attr('aria-labelledby')).toBe(undefined);
285 | expect(testElement2.find('> div').attr('id')).toBe(undefined);
286 | expect(testElement2.find('> div').attr('role')).toBe(undefined);
287 | expect(testElement2.find('> div').attr('style')).toBe(undefined);
288 | expect(testElement2.find('> div').hasClass('js-tabs_panel')).toBe(false);
289 | expect(testElement2.find('> div:first').hasClass('js-tabs_panel--custom')).toBe(false);
290 | });
291 |
292 | it('should trigger "callbackDestroy" once the plugin has been destroyed', function() {
293 | var mocks,
294 | el,
295 | destroyed = false;
296 |
297 | mocks = {
298 | callbackDestroy: function(testElement) {
299 | destroyed = true;
300 | }
301 | },
302 | el = testElement.accTabs({
303 | callbackDestroy: mocks.callbackDestroy
304 | });
305 |
306 | el.data('plugin_accTabs').destroy();
307 |
308 | expect(destroyed).toBe(true);
309 | });
310 | });
311 |
312 | describe('- plugin options', function() {
313 |
314 | beforeEach(function() {
315 | testElement2 = $(markUp);
316 | });
317 |
318 | it('should activate a given tab if "defaultTab is set"', function() {
319 | testElement.accTabs({
320 | defaultTab: 2
321 | });
322 |
323 | expect(testElement.find('button:eq(2)').parent('li').hasClass('js-tabs_control-item--active')).toBe(true);
324 | expect(testElement.find('button:eq(2)').attr('aria-selected')).toBe('true');
325 | expect(testElement.find('div:eq(2)').css('display')).toBe('block');
326 | expect(testElement.find('div:eq(2)').attr('aria-hidden')).toBe('false');
327 | });
328 |
329 | it('should set the custom class on the tabs', function() {
330 | testElement.accTabs({
331 | containerClass: 'js-tabs--custom'
332 | });
333 |
334 | expect(testElement.hasClass('js-tabs--custom')).toBe(true);
335 | });
336 |
337 | it('should set the custom class on the active tab', function() {
338 | testElement.accTabs({
339 | controlActiveClass: 'js-tabs_control-item--custom'
340 | });
341 |
342 | expect(testElement.find('[aria-selected="true"]').parent('li').hasClass('js-tabs_control-item--custom')).toBe(true);
343 | });
344 |
345 | describe('the active panel classes', function() {
346 | var panelActiveClass = 'js-tabs_panel--custom';
347 |
348 | beforeEach(function() {
349 | testElement.accTabs({
350 | panelActiveClass: panelActiveClass
351 | });
352 | });
353 |
354 | it('should set the custom class on the active panel', function() {
355 | expect(testElement.find('div:first').hasClass(panelActiveClass)).toBe(true);
356 | });
357 |
358 | it('should not set the custom class on an inactive panel', function() {
359 | expect(testElement.find('div:eq(1)').hasClass(panelActiveClass)).toBe(false);
360 | });
361 | });
362 |
363 | it('should set the custom controls text', function() {
364 | testElement.accTabs({
365 | controlsText: 'test'
366 | });
367 |
368 | expect(testElement.find('.js-tabs_control-text').text()).toBe('test');
369 | });
370 |
371 | it('should set the custom class on the control text', function() {
372 | testElement.accTabs({
373 | controlsTextClass: 'js-tabs_control-text--custom'
374 | });
375 |
376 | expect(testElement.find('> p').hasClass('js-tabs_control-text--custom')).toBe(true);
377 | });
378 |
379 | it('should set the custom string for tab control ids', function() {
380 | testElement.accTabs({
381 | tabControlId: 'test--'
382 | });
383 |
384 | expect(testElement.find('button[id^="test--"]').length).toBe(3);
385 | });
386 |
387 | it('should set the custom class for the tab panels', function() {
388 | testElement.accTabs({
389 | tabPanelClass: 'test'
390 | });
391 |
392 | expect(testElement.find('> div').hasClass('test')).toBe(true);
393 | });
394 |
395 | it('should set the custom string for the panel ids', function() {
396 | testElement.accTabs({
397 | tabPanelId: 'test--'
398 | });
399 |
400 | expect(testElement.find('div[id^="test--"]').length).toBe(3);
401 | });
402 | });
403 | });
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Fri Mar 14 2014 09:31:07 GMT+0000 (GMT)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['jasmine'],
14 |
15 |
16 | // list of files / patterns to load in the browser
17 | files: [
18 | 'gh-pages/js/vendor/jquery/1.11.1/jquery.min.js',
19 | 'jquery.accTabs.js',
20 | 'jquery.accTabs.spec.js'
21 | ],
22 |
23 |
24 | // list of files to exclude
25 | exclude: [],
26 |
27 | // test results reporter to use
28 | // possible values: 'dots', 'progress'
29 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
30 | reporters: ['progress', 'coverage'],
31 |
32 | // preprocess matching files before serving them to the browser
33 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
34 | preprocessors: {
35 | 'jquery.accTabs.js': ['coverage']
36 | },
37 |
38 |
39 | //configure the reporter
40 | coverageReporter: {
41 | type : 'html',
42 | dir : 'coverage/'
43 | },
44 |
45 |
46 | // web server port
47 | port: 9876,
48 |
49 |
50 | // enable / disable colors in the output (reporters and logs)
51 | colors: true,
52 |
53 |
54 | // level of logging
55 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
56 | logLevel: config.LOG_INFO,
57 |
58 |
59 | // enable / disable watching file and executing tests whenever any file changes
60 | autoWatch: false,
61 |
62 |
63 | // start these browsers
64 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
65 | browsers: ['PhantomJS'],
66 |
67 |
68 | // Continuous Integration mode
69 | // if true, Karma captures browsers, runs the tests and exits
70 | singleRun: true
71 | });
72 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-accessible-tabs",
3 | "version": "0.1.2",
4 | "description": "Creates accessible tabs - a single content area with multiple panels",
5 | "author": "Nomensa ",
6 | "repository": {
7 | "type": "git",
8 | "url": "http://github.com/nomensa/jquery.accessible-tabs"
9 | },
10 | "devDependencies": {
11 | "grunt": "0.4.2",
12 | "grunt-contrib-connect": "~0.8.0",
13 | "grunt-contrib-jshint": "~0.8.0",
14 | "grunt-contrib-uglify": "~0.5.0",
15 | "grunt-contrib-watch": "~0.5.3",
16 | "grunt-jscs": "~0.6.1",
17 | "grunt-karma": "~0.8.0",
18 | "grunt-version": "~0.3.0",
19 | "karma": "~0.12.0",
20 | "karma-jasmine": "~0.2.2",
21 | "karma-phantomjs-launcher": "~0.1.2",
22 | "karma-coverage": "~0.1.5",
23 | "load-grunt-tasks": "~0.2.1",
24 | "time-grunt": "~0.2.8"
25 | },
26 | "engines": {
27 | "node": ">= 0.10.x"
28 | },
29 | "scripts": {
30 | "test": "grunt test --verbose"
31 | }
32 | }
--------------------------------------------------------------------------------