govuk_frontend_toolkit 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +13 -0
- data/app/assets/Gruntfile.js +2 -2
- data/app/assets/README.md +24 -0
- data/app/assets/javascripts/govuk/selection-buttons.js +139 -0
- data/app/assets/javascripts/vendor/polyfills/bind.js +40 -0
- data/app/assets/spec/SelectionButtonSpec.js +341 -0
- data/app/assets/spec/manifest.js +15 -0
- data/app/assets/spec/support/LocalTestRunner.html +21 -0
- data/app/assets/spec/support/console-runner.js +103 -0
- data/app/assets/spec/support/load.js +55 -0
- data/app/assets/spec/support/run_jasmine_test.js +62 -0
- data/app/assets/spec/{MultivariateTestSpec.js → unit/MultivariateTestSpec.js} +0 -0
- data/app/assets/spec/{PrimaryLinksSpec.js → unit/PrimaryLinksSpec.js} +0 -0
- data/app/assets/spec/{StickAtTopWhenScrollingSpec.js → unit/StickAtTopWhenScrollingSpec.js} +0 -0
- data/lib/govuk_frontend_toolkit/version.rb +1 -1
- metadata +15 -7
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
|
data/app/assets/Gruntfile.js
CHANGED
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
|
+
});
|
File without changes
|
File without changes
|
File without changes
|
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
|
+
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-
|
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/
|
235
|
-
- app/assets/spec/
|
236
|
-
- app/assets/spec/
|
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:
|
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:
|
284
|
+
hash: 1799776564228403607
|
277
285
|
requirements: []
|
278
286
|
rubyforge_project:
|
279
287
|
rubygems_version: 1.8.23
|