govuk_frontend_toolkit 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -49,6 +49,19 @@ conditionals and typography mixins you should add:
49
49
  @import '_conditionals';
50
50
  @import '_typography';
51
51
 
52
+ ## Updating the version of the toolkit that's included with the gem
53
+
54
+ 1. Find the commit of [the toolkit][govuk_frontend_toolkit] you want to update to. In this
55
+ repository, `cd app/assets` and then run `git checkout <NEW TOOLKIT COMMIT>` to change
56
+ the git submodule
57
+ 2. Update the version number in `lib/govuk_frontend_toolkit/version.rb`
58
+ 3. Commit those two changes together and push them.
59
+
60
+ Do not create a version tag in this repository - that's handled automatically by the
61
+ job that publishes the gem to RubyGems.
62
+
52
63
  ## Licence
53
64
 
54
65
  Released under the MIT Licence, a copy of which can be found in the file `LICENCE`.
66
+
67
+ [govuk_frontend_toolkit]: https://github.com/alphagov/govuk_frontend_toolkit
@@ -7,8 +7,8 @@ module.exports = function(grunt) {
7
7
  'javascripts/**/*.js'
8
8
  ],
9
9
  options: {
10
- specs: 'spec/*Spec.js',
11
- helpers: 'spec/*Helper.js'
10
+ specs: 'spec/unit/*Spec.js',
11
+ helpers: 'spec/unit/*Helper.js'
12
12
  }
13
13
  }
14
14
  }
data/app/assets/README.md CHANGED
@@ -48,6 +48,12 @@ Install Node 0.8 or higher and PhantomJS.
48
48
  $ npm install
49
49
  $ npm test
50
50
 
51
+ ### Using the local test runner
52
+
53
+ The test suite can be run by opening the `./spec/support/LocalTestRunner.html` file in a browser for a more detailed trace of errors.
54
+
55
+ The files for unit tests and any supporting JavaScript should be added to `./spec/manifest.js` file.
56
+
51
57
  ## Usage
52
58
 
53
59
  At the top of a Sass file in your project you should use an `@import` rule
@@ -822,6 +828,24 @@ GOVUK.stickAtTopWhenScrolling.init();
822
828
  If you also include the `stopScrollingAtFooter` JavaScript this will also try
823
829
  and stop the elements before they get to the bottom.
824
830
 
831
+ ## Selection buttons
832
+
833
+ Script to support a specific design of radio buttons and checkboxes wrapped in `<label>` tags:
834
+
835
+ <label>
836
+ <input type="radio" name="size" value="medium" />
837
+ </label>
838
+
839
+ When the input is focused or its `checked` attribute is set, classes are added to their parent labels so their styling can show this.
840
+
841
+ To apply this behaviour to elements with the above HTML pattern, call the `GOVUK.selectionButtons` function with their inputs:
842
+
843
+ ```
844
+ var $buttons = $("label input[type='radio'], label input[type='checkbox']");
845
+ GOVUK.selectionButtons($buttons);
846
+ ```
847
+
848
+ Note that `GOVUK.selectionButtons` and the constructors it wraps, `GOVUK.RadioButtons` and `GOVUK.CheckboxButtons` use the `bind.js` polyfill.
825
849
 
826
850
  ## Licence
827
851
 
@@ -0,0 +1,139 @@
1
+ (function () {
2
+ "use strict"
3
+ var root = this,
4
+ $ = root.jQuery;
5
+
6
+ if (typeof GOVUK === 'undefined') { root.GOVUK = {}; }
7
+
8
+ var BaseButtons = function ($elms, opts) {
9
+ this.$elms = $elms;
10
+ this.selectedClass = 'selected';
11
+ this.focusedClass = 'focused';
12
+ if (opts !== undefined) {
13
+ $.each(opts, function (optionName, optionObj) {
14
+ this[optionName] = optionObj;
15
+ }.bind(this));
16
+ }
17
+ this.setEventNames();
18
+ this.getSelections();
19
+ this.bindEvents();
20
+ };
21
+ BaseButtons.prototype.setEventNames = function () {
22
+ this.selectionEvents = 'click';
23
+ this.focusEvents = 'focus blur';
24
+ };
25
+ BaseButtons.prototype.markFocused = function ($elm, state) {
26
+ var elmId = $elm.attr('id');
27
+
28
+ if (state === 'focused') {
29
+ $elm.parent('label').addClass(this.focusedClass);
30
+ } else {
31
+ $elm.parent('label').removeClass(this.focusedClass);
32
+ }
33
+ };
34
+ BaseButtons.prototype.bindEvents = function () {
35
+ var selectionEventHandler = this.markSelected.bind(this),
36
+ focusEventHandler = this.markFocused.bind(this);
37
+
38
+ this.$elms
39
+ .on(this.selectionEvents, function (e) {
40
+ selectionEventHandler($(e.target));
41
+ })
42
+ .on(this.focusEvents, function (e) {
43
+ var state = (e.type === 'focus') ? 'focused' : 'blurred';
44
+
45
+ focusEventHandler($(e.target), state);
46
+ });
47
+ };
48
+
49
+ var RadioButtons = function ($elms, opts) {
50
+ BaseButtons.apply(this, arguments);
51
+ };
52
+ RadioButtons.prototype.setEventNames = function () {
53
+ // some browsers fire the 'click' when the selected radio changes by keyboard
54
+ this.selectionEvents = 'click change';
55
+ this.focusEvents = 'focus blur';
56
+ };
57
+ RadioButtons.prototype.getSelections = function () {
58
+ var selectionEventHandler = this.markSelected.bind(this),
59
+ selections = {};
60
+
61
+ $.each(this.$elms, function (index, elm) {
62
+ var $elm = $(elm),
63
+ radioName = $elm.attr('name');
64
+
65
+ if (typeof selections[radioName] === 'undefined') {
66
+ selections[radioName] = false;
67
+ }
68
+ if ($elm.is(':checked')) {
69
+ selectionEventHandler($elm);
70
+ selections[radioName] = $elm;
71
+ }
72
+ });
73
+ this.selections = selections;
74
+ };
75
+ RadioButtons.prototype.bindEvents = function () {
76
+ BaseButtons.prototype.bindEvents.call(this);
77
+ };
78
+ RadioButtons.prototype.markSelected = function ($elm) {
79
+ var radioName = $elm.attr('name'),
80
+ $previousSelection = this.selections[radioName];
81
+
82
+ if ($previousSelection) {
83
+ $previousSelection.parent('label').removeClass(this.selectedClass);
84
+ }
85
+ $elm.parent('label').addClass(this.selectedClass);
86
+ this.selections[radioName] = $elm;
87
+ };
88
+ RadioButtons.prototype.markFocused = function ($elm) {
89
+ BaseButtons.prototype.markFocused.apply(this, arguments);
90
+ };
91
+
92
+ var CheckboxButtons = function ($elms, opts) {
93
+ BaseButtons.apply(this, arguments);
94
+ };
95
+ CheckboxButtons.prototype.setEventNames = function () {
96
+ BaseButtons.prototype.setEventNames.call(this);
97
+ };
98
+ CheckboxButtons.prototype.getSelections = function () {
99
+ var selectionEventHandler = this.markSelected.bind(this);
100
+
101
+ this.$elms.each(function (idx, elm) {
102
+ var $elm = $(elm);
103
+
104
+ if ($elm.is(':checked')) {
105
+ selectionEventHandler($elm);
106
+ }
107
+ });
108
+ };
109
+ CheckboxButtons.prototype.bindEvents = function () {
110
+ BaseButtons.prototype.bindEvents.call(this);
111
+ };
112
+ CheckboxButtons.prototype.markSelected = function ($elm) {
113
+ if ($elm.is(':checked')) {
114
+ $elm.parent('label').addClass(this.selectedClass);
115
+ } else {
116
+ $elm.parent('label').removeClass(this.selectedClass);
117
+ }
118
+ };
119
+ CheckboxButtons.prototype.markFocused = function ($elm) {
120
+ BaseButtons.prototype.markFocused.apply(this, arguments);
121
+ };
122
+
123
+ root.GOVUK.RadioButtons = RadioButtons;
124
+ root.GOVUK.CheckboxButtons = CheckboxButtons;
125
+
126
+ var selectionButtons = function ($elms, opts) {
127
+ var $radios = $elms.filter('[type=radio]'),
128
+ $checkboxes = $elms.filter('[type=checkbox]');
129
+
130
+ if ($radios) {
131
+ new GOVUK.RadioButtons($radios, opts);
132
+ }
133
+ if ($checkboxes) {
134
+ new GOVUK.CheckboxButtons($checkboxes, opts);
135
+ }
136
+ };
137
+
138
+ root.GOVUK.selectionButtons = selectionButtons;
139
+ }).call(this);
@@ -0,0 +1,40 @@
1
+ // Function.prototype.bind
2
+ //
3
+ // A polyfill for Function.prototype.bind. Which lets you bind a defined
4
+ // value to the `this` keyword in a function call.
5
+ //
6
+ // Bind is natively supported in:
7
+ // IE9+
8
+ // Chrome 7+
9
+ // Firefox 4+
10
+ // Safari 5.1.4+
11
+ // iOS 6+
12
+ // Android Browser 4+
13
+ // Chrome for Android 0.16+
14
+ //
15
+ // Originally from:
16
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
17
+ if (!Function.prototype.bind) {
18
+ Function.prototype.bind = function (oThis) {
19
+ if (typeof this !== "function") {
20
+ // closest thing possible to the ECMAScript 5
21
+ // internal IsCallable function
22
+ throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
23
+ }
24
+
25
+ var aArgs = Array.prototype.slice.call(arguments, 1),
26
+ fToBind = this,
27
+ fNOP = function () {},
28
+ fBound = function () {
29
+ return fToBind.apply(this instanceof fNOP && oThis
30
+ ? this
31
+ : oThis,
32
+ aArgs.concat(Array.prototype.slice.call(arguments)));
33
+ };
34
+
35
+ fNOP.prototype = this.prototype;
36
+ fBound.prototype = new fNOP();
37
+
38
+ return fBound;
39
+ };
40
+ }
@@ -0,0 +1,341 @@
1
+ describe("selection-buttons", function () {
2
+ var $radioButtons,
3
+ $radioLabels,
4
+ $checkboxButtons,
5
+ $checkboxLabels;
6
+
7
+ beforeEach(function () {
8
+ $radioLabels = $(
9
+ '<label class="selectable">' +
10
+ 'Small' +
11
+ '<input type="radio" name="size" id="small" value="small" />' +
12
+ '</label>' +
13
+ '<label class="selectable">' +
14
+ 'Medium' +
15
+ '<input type="radio" name="size" id="medium" value="medium" />' +
16
+ '</label>' +
17
+ '<label class="selectable">' +
18
+ 'Large' +
19
+ '<input type="radio" name="size" id="large" value="large" />' +
20
+ '</label>'
21
+ );
22
+ $checkboxLabels = $(
23
+ '<label class="selectable">' +
24
+ 'Eggs' +
25
+ '<input id="eggs" name="food" value="eggs" type="checkbox" />' +
26
+ '</label>' +
27
+ '<label class="selectable">' +
28
+ 'Bread' +
29
+ '<input id="bread" name="food" value="bread" type="checkbox" />' +
30
+ '</label>' +
31
+ '<label class="selectable">' +
32
+ 'Fruit' +
33
+ '<input id="fruit" name="food" value="fruit" type="checkbox" />' +
34
+ '</label>'
35
+ );
36
+ $radioButtons = $radioLabels.find('input');
37
+ $checkboxButtons = $checkboxLabels.find('input');
38
+ $(document.body).append($radioLabels);
39
+ $(document.body).append($checkboxLabels);
40
+ });
41
+
42
+ afterEach(function () {
43
+ $radioLabels.remove();
44
+ $checkboxLabels.remove();
45
+ });
46
+
47
+ describe("RadioButtons", function () {
48
+ it("Should create a new instance with the correct interface", function () {
49
+ var buttons = new GOVUK.RadioButtons($radioButtons);
50
+
51
+ expect(buttons.getSelections).toBeDefined();
52
+ expect(buttons.bindEvents).toBeDefined();
53
+ expect(buttons.markSelected).toBeDefined();
54
+ expect(buttons.markFocused).toBeDefined();
55
+ });
56
+
57
+ it("Should set the selectedClass property if sent in as an option", function () {
58
+ var buttons = new GOVUK.RadioButtons($radioButtons, { 'selectedClass' : 'selectable-selected' });
59
+
60
+ expect(buttons.selectedClass).toEqual('selectable-selected');
61
+ });
62
+
63
+ it("Should set the focusedClass property if sent in as an option", function () {
64
+ var buttons = new GOVUK.RadioButtons($radioButtons, { 'focusedClass' : 'selectable-focused' });
65
+
66
+ expect(buttons.focusedClass).toEqual('selectable-focused');
67
+ });
68
+
69
+ describe("getSelections method", function () {
70
+ it("Should mark the label of any checked radios as selected", function () {
71
+ var radioButtonsMock = {
72
+ 'markSelected' : function () {},
73
+ '$elms' : $radioButtons
74
+ };
75
+
76
+ $radioButtons.eq(0).attr('checked', true);
77
+ spyOn(radioButtonsMock, 'markSelected');
78
+ GOVUK.RadioButtons.prototype.getSelections.call(radioButtonsMock);
79
+ expect(radioButtonsMock.markSelected).toHaveBeenCalled();
80
+ });
81
+ });
82
+
83
+ describe("setEventNames method", function () {
84
+ it("Should set the selectionEvents and focusEvents properties on the instance", function () {
85
+ var radioButtonsMock = {};
86
+
87
+ GOVUK.RadioButtons.prototype.setEventNames.call(radioButtonsMock);
88
+ expect(typeof radioButtonsMock.focusEvents !== 'undefined').toBe(true);
89
+ expect(typeof radioButtonsMock.selectionEvents !== 'undefined').toBe(true);
90
+ });
91
+ });
92
+
93
+ describe("bindEvents method", function () {
94
+ it("Should bind click and change events to each radio", function () {
95
+ var radioButtonsMock = {
96
+ '$elms' : $radioButtons,
97
+ 'selectionEvents' : 'click change',
98
+ 'focusEvents' : 'focus blur',
99
+ 'markSelected' : function () {},
100
+ 'markFocused' : function () {}
101
+ },
102
+ eventsBound = false;
103
+
104
+ spyOn($.fn, 'on').andCallFake(function (evt, func) {
105
+ if (evt === 'click change') {
106
+ eventsBound = true;
107
+ }
108
+ return $.fn;
109
+ });
110
+ expect($.fn.on.calls.length).toEqual(0);
111
+ GOVUK.RadioButtons.prototype.bindEvents.call(radioButtonsMock);
112
+ expect($.fn.on).toHaveBeenCalled();
113
+ expect(eventsBound).toEqual(true);
114
+ });
115
+
116
+ it("Should call the markSelected method on any checked radio that's the target of an event", function () {
117
+ var radioButtonsMock = {
118
+ '$elms' : $radioButtons,
119
+ 'selectionEvents' : 'click change',
120
+ 'focusEvents' : 'focus blur',
121
+ 'markSelected' : function () {},
122
+ 'markFocused' : function () {}
123
+ },
124
+ eventsBound = false;
125
+
126
+ spyOn($.fn, 'on').andCallFake(function (evt, func) {
127
+ if (evt === 'click change') {
128
+ callback = func;
129
+ }
130
+ return $.fn;
131
+ });
132
+ spyOn(radioButtonsMock, 'markSelected');
133
+ radioButtonsMock.$elms.eq(0).attr('checked', true);
134
+ GOVUK.RadioButtons.prototype.bindEvents.call(radioButtonsMock);
135
+ callback({ 'target' : radioButtonsMock.$elms[0] });
136
+ expect(radioButtonsMock.markSelected).toHaveBeenCalled();
137
+ });
138
+ });
139
+
140
+ describe("markSelected method", function () {
141
+ it("Should add the selectedClass class to the label of the sent in radio", function () {
142
+ var radioButtonsMock = {
143
+ 'selections' : {
144
+ 'size' : false
145
+ },
146
+ 'selectedClass' : 'selected'
147
+ },
148
+ $clickedRadio = $radioButtons.eq(0);
149
+
150
+ GOVUK.RadioButtons.prototype.markSelected.call(radioButtonsMock, $clickedRadio);
151
+ expect($clickedRadio.parent('label').hasClass('selected')).toEqual(true);
152
+ });
153
+
154
+ it("Should remove the selectedClass class from the label of the previously selected radio", function () {
155
+ var radioButtonsMock = {
156
+ 'selections' : {
157
+ 'size' : $radioButtons.eq(1)
158
+ },
159
+ 'selectedClass' : 'selected'
160
+ },
161
+ $clickedRadio = $radioButtons.eq(0);
162
+
163
+ $radioLabels.eq(1).addClass('selected');
164
+ GOVUK.RadioButtons.prototype.markSelected.call(radioButtonsMock, $clickedRadio);
165
+ expect($('#medium').parent('label').hasClass('selected')).toEqual(false);
166
+
167
+ });
168
+ });
169
+
170
+ describe("markFocused method", function () {
171
+ var radioButtonsMock = {
172
+ 'focused' : false,
173
+ 'focusedClass' : 'focused'
174
+ };
175
+
176
+ it("Should add the focusedClass class to the sent radio if it is focused", function () {
177
+ GOVUK.RadioButtons.prototype.markFocused.apply(radioButtonsMock, [$radioButtons.eq(0), 'focused']);
178
+
179
+ expect($radioLabels.eq(0).hasClass(radioButtonsMock.focusedClass)).toBe(true);
180
+ });
181
+
182
+ it("Should remove the focusedClass class from the sent radio if it is blurred", function () {
183
+ $radioLabels.eq(0).addClass(radioButtonsMock.focusedClass);
184
+ GOVUK.RadioButtons.prototype.markFocused.apply(radioButtonsMock, [$radioButtons.eq(0), 'blurred']);
185
+
186
+ expect($radioLabels.eq(0).hasClass(radioButtonsMock.focusedClass)).toBe(false);
187
+ });
188
+ });
189
+ });
190
+
191
+ describe("CheckboxButtons", function () {
192
+ it("Should create a new instance with the correct interface", function () {
193
+ var buttons = new GOVUK.CheckboxButtons($checkboxButtons);
194
+
195
+ expect(buttons.getSelections).toBeDefined();
196
+ expect(buttons.bindEvents).toBeDefined();
197
+ expect(buttons.markSelected).toBeDefined();
198
+ expect(buttons.markFocused).toBeDefined();
199
+ });
200
+
201
+ describe("getSelections method", function () {
202
+ it("Should add the selectedClass class to the label of a checkbox that is checked", function () {
203
+ var checkboxButtonsMock = {
204
+ '$elms' : $checkboxButtons,
205
+ 'markSelected' : function () {}
206
+ };
207
+
208
+ checkboxButtonsMock.$elms.eq(0).attr('checked', true);
209
+ spyOn(checkboxButtonsMock, 'markSelected');
210
+ GOVUK.CheckboxButtons.prototype.getSelections.call(checkboxButtonsMock);
211
+ expect(checkboxButtonsMock.markSelected).toHaveBeenCalled();
212
+ });
213
+ });
214
+
215
+ describe("setEventNames method", function () {
216
+ it("Should set the selectionEvents and focusEvents properties on the instance", function () {
217
+ var checkboxButtonsMock = {};
218
+
219
+ GOVUK.CheckboxButtons.prototype.setEventNames.call(checkboxButtonsMock);
220
+ expect(typeof checkboxButtonsMock.focusEvents !== 'undefined').toBe(true);
221
+ expect(typeof checkboxButtonsMock.selectionEvents !== 'undefined').toBe(true);
222
+ });
223
+ });
224
+
225
+ describe("bindEvents method", function () {
226
+ var checkboxButtonsMock;
227
+
228
+ beforeEach(function () {
229
+ checkboxButtonsMock = {
230
+ '$elms' : $checkboxButtons
231
+ };
232
+ });
233
+
234
+ it("Should add a click event to each checkbox that fires the markSelected method", function () {
235
+ var eventCalled = false;
236
+
237
+ checkboxButtonsMock.markSelected = function () {};
238
+ checkboxButtonsMock.markFocused = function () {};
239
+ checkboxButtonsMock.selectionEvents = 'click';
240
+ checkboxButtonsMock.focusEvents = 'focus blur';
241
+ spyOn(checkboxButtonsMock, 'markSelected');
242
+ spyOn($.fn, 'on').andCallFake(function (evt, func) {
243
+ if (evt === 'click') {
244
+ eventCalled = true;
245
+ callback = func;
246
+ }
247
+ return $.fn;
248
+ });
249
+ $checkboxButtons.eq(0).attr('checked', true);
250
+ GOVUK.CheckboxButtons.prototype.bindEvents.call(checkboxButtonsMock);
251
+ expect(eventCalled).toBe(true);
252
+ callback({ 'target' : $checkboxButtons.eq(0) });
253
+ expect(checkboxButtonsMock.markSelected).toHaveBeenCalled();
254
+ });
255
+
256
+ it("Should add focus and blur events to each checkbox that fires the markFocused method", function () {
257
+ var eventCalled = false;
258
+
259
+ checkboxButtonsMock.markFocused = function () {};
260
+ checkboxButtonsMock.markSelected = function () {};
261
+ checkboxButtonsMock.selectionEvents = 'click';
262
+ checkboxButtonsMock.focusEvents = 'focus blur';
263
+ spyOn(checkboxButtonsMock, 'markFocused');
264
+ spyOn($.fn, 'on').andCallFake(function (evt, func) {
265
+ if (evt === 'focus blur') {
266
+ eventCalled = true;
267
+ callback = func;
268
+ }
269
+ return $.fn;
270
+ });
271
+ GOVUK.CheckboxButtons.prototype.bindEvents.call(checkboxButtonsMock);
272
+ expect(eventCalled).toBe(true);
273
+ callback({
274
+ 'target' : $checkboxButtons.eq(0),
275
+ 'type' : 'focus'
276
+ });
277
+ expect(checkboxButtonsMock.markFocused).toHaveBeenCalled();
278
+ });
279
+ });
280
+
281
+ describe("markSelected method", function () {
282
+ var checkboxButtonsMock = {
283
+ 'selectedClass' : 'selected'
284
+ };
285
+
286
+ it("Should add the selectedClass class to a checked checkbox", function () {
287
+ $checkboxButtons.eq(0).attr('checked', true);
288
+ GOVUK.CheckboxButtons.prototype.markSelected.call(checkboxButtonsMock, $checkboxButtons.eq(0));
289
+ expect($checkboxLabels.eq(0).hasClass(checkboxButtonsMock.selectedClass)).toBe(true);
290
+ });
291
+
292
+ it("Should remove the selectedClass class from an unchecked checkbox", function () {
293
+ $checkboxButtons.eq(0).addClass(checkboxButtonsMock.selectedClass);
294
+ GOVUK.CheckboxButtons.prototype.markSelected.call(checkboxButtonsMock, $checkboxButtons.eq(0));
295
+ expect($checkboxLabels.eq(0).hasClass(checkboxButtonsMock.selectedClass)).toBe(false);
296
+ });
297
+ });
298
+
299
+ describe("markFocused method", function () {
300
+ var checkboxButtonsMock = {
301
+ 'focused' : false,
302
+ 'focusedClass' : 'focused'
303
+ };
304
+
305
+ it("Should add the focusedClass class to the sent radio if it is focused", function () {
306
+ GOVUK.CheckboxButtons.prototype.markFocused.apply(checkboxButtonsMock, [$checkboxButtons.eq(0), 'focused']);
307
+
308
+ expect($checkboxLabels.eq(0).hasClass(checkboxButtonsMock.focusedClass)).toBe(true);
309
+ });
310
+
311
+ it("Should remove the focusedClass class from the sent radio if it is blurred", function () {
312
+ $checkboxLabels.eq(0).addClass(checkboxButtonsMock.focusedClass);
313
+ GOVUK.CheckboxButtons.prototype.markFocused.apply(checkboxButtonsMock, [$checkboxButtons.eq(0), 'blurred']);
314
+
315
+ expect($checkboxLabels.eq(0).hasClass(checkboxButtonsMock.focusedClass)).toBe(false);
316
+ });
317
+ });
318
+ });
319
+
320
+ describe("selectionButtons", function () {
321
+ it("Should create an instance of RadioButtons for a set of radios", function () {
322
+ spyOn(GOVUK, 'RadioButtons');
323
+ GOVUK.selectionButtons($radioButtons);
324
+ expect(GOVUK.RadioButtons).toHaveBeenCalled();
325
+ });
326
+
327
+ it("Should create an instance of CheckboxButtons for a set of checkboxes", function () {
328
+ spyOn(GOVUK, 'CheckboxButtons');
329
+ GOVUK.selectionButtons($checkboxButtons);
330
+ expect(GOVUK.CheckboxButtons).toHaveBeenCalled();
331
+ });
332
+
333
+ it("Should create instances of RadioButtons and CheckboxButtons for a set containing radios and checkboxes", function () {
334
+ spyOn(GOVUK, 'RadioButtons');
335
+ spyOn(GOVUK, 'CheckboxButtons');
336
+ GOVUK.selectionButtons($checkboxButtons.add($radioButtons));
337
+ expect(GOVUK.RadioButtons).toHaveBeenCalled();
338
+ expect(GOVUK.CheckboxButtons).toHaveBeenCalled();
339
+ });
340
+ });
341
+ });
@@ -0,0 +1,15 @@
1
+ // files are loaded from the /spec/support folder so paths are relative to that
2
+ var manifest = {
3
+ support : [
4
+ '../../node_modules/jquery-browser/lib/jquery.js',
5
+ '../../javascripts/govuk/multivariate-test.js',
6
+ '../../javascripts/govuk/primary-links.js',
7
+ '../../javascripts/govuk/stick-at-top-when-scrolling.js',
8
+ '../../javascripts/govuk/stop-scrolling-at-footer.js'
9
+ ],
10
+ test : [
11
+ '../unit/MultivariateTestSpec.js',
12
+ '../unit/PrimaryLinksSpec.js',
13
+ '../unit/StickAtTopWhenScrollingSpec.js'
14
+ ]
15
+ };
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://http://www.w3.org/TR/html4/loose.dtd">
2
+ <html>
3
+ <head>
4
+ <title>Jasmine Test Runner</title>
5
+
6
+ <link rel="stylesheet" type="text/css" href="../../node_modules/grunt-contrib-jasmine/vendor/jasmine-1.3.1/jasmine.css">
7
+ <style>
8
+ #wrapper { display: none; }
9
+ </style>
10
+
11
+ <!-- JASMINE FILES -->
12
+ <script type="text/javascript" src="../../node_modules/grunt-contrib-jasmine/vendor/jasmine-1.3.1/jasmine.js"></script>
13
+ <script type="text/javascript" src="../../node_modules/grunt-contrib-jasmine/vendor/jasmine-1.3.1/jasmine-html.js"></script>
14
+
15
+ <script type="text/javascript" src="./console-runner.js"></script>
16
+ <script type="text/javascript" src="./load.js"></script>
17
+ </head>
18
+ <body>
19
+
20
+ </body>
21
+ </html>
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Jasmine Reporter that outputs test results to the browser console.
3
+ * Useful for running in a headless environment such as PhantomJs, ZombieJs etc.
4
+ *
5
+ * Usage (from your html file that loads jasmine):
6
+ * jasmine.getEnv().addReporter(new jasmine.ConsoleReporter());
7
+ * jasmine.getEnv().execute();
8
+ */
9
+
10
+ (function(jasmine, console) {
11
+ if (!jasmine) {
12
+ throw "jasmine library isn't loaded!";
13
+ }
14
+
15
+ var ANSI = {}
16
+ ANSI.color_map = {
17
+ "green": 32,
18
+ "red": 31
19
+ }
20
+
21
+ ANSI.colorize_text = function(text, color) {
22
+ var color_code = this.color_map[color];
23
+ return "\033[" + color_code + "m" + text + "\033[0m";
24
+ }
25
+
26
+ var ConsoleReporter = function() {
27
+ if (!console || !console.log) { throw "console isn't present!"; }
28
+ this.status = this.statuses.stopped;
29
+ };
30
+
31
+ var proto = ConsoleReporter.prototype;
32
+ proto.statuses = {
33
+ stopped : "stopped",
34
+ running : "running",
35
+ fail : "fail",
36
+ success : "success"
37
+ };
38
+
39
+ proto.reportRunnerStarting = function(runner) {
40
+ this.status = this.statuses.running;
41
+ this.start_time = (new Date()).getTime();
42
+ this.executed_specs = 0;
43
+ this.passed_specs = 0;
44
+ this.log("Starting...");
45
+ };
46
+
47
+ proto.reportRunnerResults = function(runner) {
48
+ var failed = this.executed_specs - this.passed_specs;
49
+ var spec_str = this.executed_specs + (this.executed_specs === 1 ? " spec, " : " specs, ");
50
+ var fail_str = failed + (failed === 1 ? " failure in " : " failures in ");
51
+ var color = (failed > 0)? "red" : "green";
52
+ var dur = (new Date()).getTime() - this.start_time;
53
+
54
+ this.log("");
55
+ this.log("Finished");
56
+ this.log("-----------------");
57
+ this.log(spec_str + fail_str + (dur/1000) + "s.", color);
58
+
59
+ this.status = (failed > 0)? this.statuses.fail : this.statuses.success;
60
+
61
+ /* Print something that signals that testing is over so that headless browsers
62
+ like PhantomJs know when to terminate. */
63
+ this.log("");
64
+ this.log("ConsoleReporter finished");
65
+ };
66
+
67
+
68
+ proto.reportSpecStarting = function(spec) {
69
+ this.executed_specs++;
70
+ };
71
+
72
+ proto.reportSpecResults = function(spec) {
73
+ if (spec.results().passed()) {
74
+ this.passed_specs++;
75
+ return;
76
+ }
77
+
78
+ var resultText = spec.suite.description + " : " + spec.description;
79
+ this.log(resultText, "red");
80
+
81
+ var items = spec.results().getItems()
82
+ for (var i = 0; i < items.length; i++) {
83
+ var trace = items[i].trace.stack || items[i].trace;
84
+ this.log(trace, "red");
85
+ }
86
+ };
87
+
88
+ proto.reportSuiteResults = function(suite) {
89
+ if (!suite.parentSuite) { return; }
90
+ var results = suite.results();
91
+ var failed = results.totalCount - results.passedCount;
92
+ var color = (failed > 0)? "red" : "green";
93
+ this.log(suite.description + ": " + results.passedCount + " of " + results.totalCount + " passed.", color);
94
+ };
95
+
96
+ proto.log = function(str, color) {
97
+ var text = (color != undefined)? ANSI.colorize_text(str, color) : str;
98
+ console.log(text)
99
+ };
100
+
101
+ jasmine.ConsoleReporter = ConsoleReporter;
102
+ })(jasmine, console);
103
+
@@ -0,0 +1,55 @@
1
+ (function (root) {
2
+ "use strict"
3
+ var loadedScripts = 0,
4
+ totalScripts,
5
+ merge,
6
+ loadScript,
7
+ runJasmine,
8
+ manifestScript;
9
+
10
+ merge = function (arrays) {
11
+ var resultingArray = [],
12
+ workingArray,
13
+ a, b, x, y;
14
+
15
+ for (a = 0, b = arrays.length; a < b; a++) {
16
+ workingArray = arrays[a];
17
+ for (x = 0, y = workingArray.length; x < y; x++) {
18
+ resultingArray.push(workingArray[x]);
19
+ }
20
+ }
21
+ return resultingArray;
22
+ };
23
+ loadScript = function (src, nextIdx) {
24
+ var script = document.createElement('script'),
25
+ nextScript;
26
+
27
+ script.type = 'text/javascript';
28
+ script.src = src;
29
+
30
+ document.getElementsByTagName('head')[0].appendChild(script);
31
+ if (nextIdx === undefined) { return script; }
32
+ script.onload = function () {
33
+ if (nextIdx < totalScripts.length) {
34
+ loadScript(totalScripts[nextIdx], nextIdx + 1);
35
+ } else {
36
+ runJasmine();
37
+ }
38
+ };
39
+ return script;
40
+ };
41
+ runJasmine = function () {
42
+ var console_reporter = new jasmine.ConsoleReporter()
43
+ jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
44
+ jasmine.getEnv().addReporter(console_reporter);
45
+ jasmine.getEnv().execute();
46
+ };
47
+ manifestScript = loadScript('../manifest.js');
48
+
49
+ manifestScript.onload = function () {
50
+ var idx = 0;
51
+
52
+ totalScripts = merge([manifest.support, manifest.test]);
53
+ loadScript(totalScripts[idx], idx + 1);
54
+ };
55
+ }).call(this);
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Runs a Jasmine Suite from an html page.
3
+ * `page` is a PhantomJs page object.
4
+ * `exit_func` is the function to call in order to exit the script.
5
+ */
6
+
7
+ var PhantomJasmineRunner, address, page, runner;
8
+
9
+ PhantomJasmineRunner = (function() {
10
+
11
+ function PhantomJasmineRunner(page, exit_func) {
12
+ this.page = page;
13
+ this.exit_func = exit_func != null ? exit_func : phantom.exit;
14
+ this.tries = 0;
15
+ this.max_tries = 10;
16
+ }
17
+
18
+ PhantomJasmineRunner.prototype.get_status = function() {
19
+ return this.page.evaluate(function() {
20
+ return console_reporter.status;
21
+ });
22
+ };
23
+
24
+ PhantomJasmineRunner.prototype.terminate = function() {
25
+ switch (this.get_status()) {
26
+ case "success":
27
+ return this.exit_func(0);
28
+ case "fail":
29
+ return this.exit_func(1);
30
+ default:
31
+ return this.exit_func(2);
32
+ }
33
+ };
34
+
35
+ return PhantomJasmineRunner;
36
+
37
+ })();
38
+
39
+ if (phantom.args.length === 0) {
40
+ console.log("Need a url as the argument");
41
+ phantom.exit(1);
42
+ }
43
+
44
+ page = new WebPage();
45
+
46
+ runner = new PhantomJasmineRunner(page);
47
+
48
+ page.onConsoleMessage = function(msg) {
49
+ console.log(msg);
50
+ if (msg === "ConsoleReporter finished") {
51
+ return runner.terminate();
52
+ }
53
+ };
54
+
55
+ address = phantom.args[0];
56
+
57
+ page.open(address, function(status) {
58
+ if (status !== "success") {
59
+ console.log("can't load the address!");
60
+ return phantom.exit(1);
61
+ }
62
+ });
@@ -1,3 +1,3 @@
1
1
  module GovUKFrontendToolkit
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk_frontend_toolkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-06-26 00:00:00.000000000 Z
12
+ date: 2014-07-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -224,16 +224,24 @@ files:
224
224
  - app/assets/images/player-icon-volume.png
225
225
  - app/assets/javascripts/govuk/multivariate-test.js
226
226
  - app/assets/javascripts/govuk/primary-links.js
227
+ - app/assets/javascripts/govuk/selection-buttons.js
227
228
  - app/assets/javascripts/govuk/stick-at-top-when-scrolling.js
228
229
  - app/assets/javascripts/govuk/stop-scrolling-at-footer.js
229
230
  - app/assets/javascripts/govuk_toolkit.js
230
231
  - app/assets/javascripts/stageprompt.js
231
232
  - app/assets/javascripts/vendor/jquery/jquery.player.min.js
233
+ - app/assets/javascripts/vendor/polyfills/bind.js
232
234
  - app/assets/jenkins.sh
233
235
  - app/assets/package.json
234
- - app/assets/spec/MultivariateTestSpec.js
235
- - app/assets/spec/PrimaryLinksSpec.js
236
- - app/assets/spec/StickAtTopWhenScrollingSpec.js
236
+ - app/assets/spec/SelectionButtonSpec.js
237
+ - app/assets/spec/manifest.js
238
+ - app/assets/spec/support/LocalTestRunner.html
239
+ - app/assets/spec/support/console-runner.js
240
+ - app/assets/spec/support/load.js
241
+ - app/assets/spec/support/run_jasmine_test.js
242
+ - app/assets/spec/unit/MultivariateTestSpec.js
243
+ - app/assets/spec/unit/PrimaryLinksSpec.js
244
+ - app/assets/spec/unit/StickAtTopWhenScrollingSpec.js
237
245
  - app/assets/stylesheets/.gitkeep
238
246
  - app/assets/stylesheets/_colours.scss
239
247
  - app/assets/stylesheets/_conditionals.scss
@@ -264,7 +272,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
264
272
  version: '0'
265
273
  segments:
266
274
  - 0
267
- hash: -3563452542867821047
275
+ hash: 1799776564228403607
268
276
  required_rubygems_version: !ruby/object:Gem::Requirement
269
277
  none: false
270
278
  requirements:
@@ -273,7 +281,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
273
281
  version: '0'
274
282
  segments:
275
283
  - 0
276
- hash: -3563452542867821047
284
+ hash: 1799776564228403607
277
285
  requirements: []
278
286
  rubyforge_project:
279
287
  rubygems_version: 1.8.23