govuk_elements_rails 0.1.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.
- checksums.yaml +7 -0
- data/LICENCE +20 -0
- data/README.md +54 -0
- data/lib/govuk_elements_rails.rb +4 -0
- data/lib/govuk_elements_rails/engine.rb +4 -0
- data/lib/govuk_elements_rails/version.rb +5 -0
- data/vendor/assets/javascripts/bind.js +40 -0
- data/vendor/assets/javascripts/details.polyfill.js +158 -0
- data/vendor/assets/javascripts/selection-buttons.js +137 -0
- data/vendor/assets/stylesheets/elements-page-ie6.scss +5 -0
- data/vendor/assets/stylesheets/elements-page-ie7.scss +4 -0
- data/vendor/assets/stylesheets/elements-page-ie8.scss +4 -0
- data/vendor/assets/stylesheets/elements-page.scss +333 -0
- data/vendor/assets/stylesheets/elements/_buttons.scss +44 -0
- data/vendor/assets/stylesheets/elements/_details.scss +39 -0
- data/vendor/assets/stylesheets/elements/_elements-typography.scss +179 -0
- data/vendor/assets/stylesheets/elements/_forms.scss +166 -0
- data/vendor/assets/stylesheets/elements/_helpers.scss +46 -0
- data/vendor/assets/stylesheets/elements/_icons.scss +224 -0
- data/vendor/assets/stylesheets/elements/_layout.scss +67 -0
- data/vendor/assets/stylesheets/elements/_lists.scss +32 -0
- data/vendor/assets/stylesheets/elements/_panels.scss +29 -0
- data/vendor/assets/stylesheets/elements/_reset.scss +33 -0
- data/vendor/assets/stylesheets/elements/_tables.scss +26 -0
- data/vendor/assets/stylesheets/elements/forms/_form-block-labels.scss +67 -0
- data/vendor/assets/stylesheets/elements/forms/_form-date.scss +46 -0
- data/vendor/assets/stylesheets/elements/forms/_form-validation.scss +68 -0
- data/vendor/assets/stylesheets/main-ie6.scss +5 -0
- data/vendor/assets/stylesheets/main-ie7.scss +4 -0
- data/vendor/assets/stylesheets/main-ie8.scss +4 -0
- data/vendor/assets/stylesheets/main.scss +36 -0
- data/vendor/assets/stylesheets/prism.scss +144 -0
- data/vendor/assets/stylesheets/service-design-manual/helpers/_breadcrumbs.scss +81 -0
- data/vendor/assets/stylesheets/service-design-manual/helpers/_page-header.scss +28 -0
- data/vendor/assets/stylesheets/service-design-manual/styleguide/_colours.scss +3 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0e4507b35a48b80125447d05206cad3cfe6f39f0
|
4
|
+
data.tar.gz: 98b68bd115ef63a93316504d741f4130c486f493
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 10ecd7e53790c3d74e3853f4d40b0f83d786a8d6af4e222d7c0ebd4c27c95ffad1780d0a4615a138aa2b2f58a649c847f15dca8aa95014c30e234c2574e27429
|
7
|
+
data.tar.gz: ff14ffac1525b39272214c70e6e6c408377de2365fbff171f6386bb3a221169dd3d561fe88f41aae4f7fd51324133776e199a5805a9ab3928052c687f916b27a
|
data/LICENCE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 HM Government
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# GOV.UK Elements Rails gem
|
2
|
+
|
3
|
+
A gem wrapper around [govuk_elements](http://github.com/alphagov/govuk_elements)
|
4
|
+
that pulls stylesheet and javascript files into a Rails app.
|
5
|
+
|
6
|
+
## Installing
|
7
|
+
|
8
|
+
Just include `govuk_elements_rails` in your `Gemfile`. It
|
9
|
+
automatically attaches itself to your asset path so the static/SCSS
|
10
|
+
files will be available to the asset pipeline.
|
11
|
+
|
12
|
+
### Development
|
13
|
+
|
14
|
+
If you are installing from git, ensure you enable submodules like so:
|
15
|
+
|
16
|
+
gem 'govuk_elements_rails', :git => "https://github.com/ministryofjustice/govuk_elements_rails.git", :submodules => true
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
At the top of a Sass file in your Rails project you should use an `@import` rule
|
21
|
+
to include the file for the mixins you require. For example here are all the
|
22
|
+
imports possible:
|
23
|
+
|
24
|
+
@import 'elements/helpers';
|
25
|
+
@import 'elements/reset';
|
26
|
+
@import 'elements/elements-typography';
|
27
|
+
@import 'elements/layout';
|
28
|
+
|
29
|
+
@import 'elements/forms';
|
30
|
+
@import 'elements/tables';
|
31
|
+
@import 'elements/buttons';
|
32
|
+
@import 'elements/details';
|
33
|
+
@import 'elements/lists';
|
34
|
+
@import 'elements/panels';
|
35
|
+
@import "elements/icons";
|
36
|
+
|
37
|
+
In the `app/assets/javascripts/application.js` file in your Rails project use
|
38
|
+
`require` rule to include the files for the javascript enhancements you require.
|
39
|
+
For example here are all the requires possible at present:
|
40
|
+
|
41
|
+
// from govuk_elements gem
|
42
|
+
//= require details.polyfill
|
43
|
+
//= require bind
|
44
|
+
//= require selection-buttons
|
45
|
+
|
46
|
+
## Alternate ways to reuse GOV.UK Elements
|
47
|
+
|
48
|
+
There are other alternate ways to include GOV.UK Elements files in a Rails
|
49
|
+
project, for example via `Bower`. Feel free to use an alternate approach if it
|
50
|
+
is more appropriate for your team.
|
51
|
+
|
52
|
+
## Feedback
|
53
|
+
|
54
|
+
Please provide feedback via [GitHub issues](https://github.com/ministryofjustice/govuk_elements_rails/issues).
|
@@ -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,158 @@
|
|
1
|
+
// <details> polyfill
|
2
|
+
// http://caniuse.com/#feat=details
|
3
|
+
|
4
|
+
// FF Support for HTML5's <details> and <summary>
|
5
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=591737
|
6
|
+
|
7
|
+
// http://www.sitepoint.com/fixing-the-details-element/
|
8
|
+
|
9
|
+
(function () {
|
10
|
+
|
11
|
+
// Add event construct for modern browsers or IE
|
12
|
+
// which fires the callback with a pre-converted target reference
|
13
|
+
function addEvent(node, type, callback) {
|
14
|
+
if (node.addEventListener) {
|
15
|
+
node.addEventListener(type, function (e) {
|
16
|
+
callback(e, e.target);
|
17
|
+
}, false);
|
18
|
+
} else if (node.attachEvent) {
|
19
|
+
node.attachEvent('on' + type, function (e) {
|
20
|
+
callback(e, e.srcElement);
|
21
|
+
});
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
// Handle cross-modal click events
|
26
|
+
function addClickEvent(node, callback) {
|
27
|
+
var keydown = false;
|
28
|
+
addEvent(node, 'keydown', function () {
|
29
|
+
keydown = true;
|
30
|
+
});
|
31
|
+
addEvent(node, 'keyup', function (e, target) {
|
32
|
+
keydown = false;
|
33
|
+
if (e.keyCode === 13) { callback(e, target); }
|
34
|
+
});
|
35
|
+
addEvent(node, 'click', function (e, target) {
|
36
|
+
if (!keydown) { callback(e, target); }
|
37
|
+
});
|
38
|
+
}
|
39
|
+
|
40
|
+
// Get the nearest ancestor element of a node that matches a given tag name
|
41
|
+
function getAncestor(node, match) {
|
42
|
+
do {
|
43
|
+
if (!node || node.nodeName.toLowerCase() === match) {
|
44
|
+
break;
|
45
|
+
}
|
46
|
+
} while (node = node.parentNode);
|
47
|
+
|
48
|
+
return node;
|
49
|
+
}
|
50
|
+
|
51
|
+
// Create a started flag so we can prevent the initialisation
|
52
|
+
// function firing from both DOMContentLoaded and window.onload
|
53
|
+
var started = false;
|
54
|
+
|
55
|
+
// Initialisation function
|
56
|
+
function addDetailsPolyfill(list) {
|
57
|
+
|
58
|
+
// If this has already happened, just return
|
59
|
+
// else set the flag so it doesn't happen again
|
60
|
+
if (started) {
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
started = true;
|
64
|
+
|
65
|
+
// Get the collection of details elements, but if that's empty
|
66
|
+
// then we don't need to bother with the rest of the scripting
|
67
|
+
if ((list = document.getElementsByTagName('details')).length === 0) {
|
68
|
+
return;
|
69
|
+
}
|
70
|
+
|
71
|
+
// else iterate through them to apply their initial state
|
72
|
+
var n = list.length, i = 0;
|
73
|
+
for (n; i < n; i++) {
|
74
|
+
var details = list[i];
|
75
|
+
|
76
|
+
// Detect native implementations
|
77
|
+
details.__native = typeof(details.open) == 'boolean';
|
78
|
+
|
79
|
+
// Save shortcuts to the inner summary and content elements
|
80
|
+
details.__summary = details.getElementsByTagName('summary').item(0);
|
81
|
+
details.__content = details.getElementsByTagName('div').item(0);
|
82
|
+
|
83
|
+
// If the content doesn't have an ID, assign it one now
|
84
|
+
// which we'll need for the summary's aria-controls assignment
|
85
|
+
if (!details.__content.id) {
|
86
|
+
details.__content.id = 'details-content-' + i;
|
87
|
+
}
|
88
|
+
|
89
|
+
// Add role=button to summary
|
90
|
+
details.__summary.setAttribute('role', 'button');
|
91
|
+
|
92
|
+
// Add aria-controls
|
93
|
+
details.__summary.setAttribute('aria-controls', details.__content.id);
|
94
|
+
|
95
|
+
// Set tabindex so the summary is keyboard accessible
|
96
|
+
details.__summary.setAttribute('tabindex', '0');
|
97
|
+
|
98
|
+
// Detect initial open/closed state
|
99
|
+
var detailsAttr = details.hasAttribute('open');
|
100
|
+
if (typeof detailsAttr !== 'undefined' && detailsAttr !== false) {
|
101
|
+
details.__summary.setAttribute('aria-expanded', 'true');
|
102
|
+
details.__content.setAttribute('aria-hidden', 'false');
|
103
|
+
} else {
|
104
|
+
details.__summary.setAttribute('aria-expanded', 'false');
|
105
|
+
details.__content.setAttribute('aria-hidden', 'true');
|
106
|
+
details.__content.style.display = 'none';
|
107
|
+
}
|
108
|
+
|
109
|
+
// Create a circular reference from the summary back to its
|
110
|
+
// parent details element, for convenience in the click handler
|
111
|
+
details.__summary.__details = details;
|
112
|
+
|
113
|
+
// If this is not a native implementation, create an arrow
|
114
|
+
// inside the summary, saving its reference as a summary property
|
115
|
+
if (!details.__native) {
|
116
|
+
var twisty = document.createElement('i');
|
117
|
+
twisty.className = 'arrow arrow-closed';
|
118
|
+
twisty.appendChild(document.createTextNode('\u25ba'));
|
119
|
+
details.__summary.__twisty = details.__summary.insertBefore(twisty, details.__summary.firstChild);
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
// Define a statechange function that updates aria-expanded and style.display
|
124
|
+
// Also update the arrow position
|
125
|
+
function statechange(summary) {
|
126
|
+
|
127
|
+
// Update aria-expanded attribute on click
|
128
|
+
var expanded = summary.__details.__summary.getAttribute('aria-expanded') == 'true';
|
129
|
+
var hidden = summary.__details.__content.getAttribute('aria-hidden') == 'true';
|
130
|
+
|
131
|
+
summary.__details.__summary.setAttribute('aria-expanded', (expanded ? 'false' : 'true'));
|
132
|
+
summary.__details.__content.setAttribute('aria-hidden', (hidden ? 'false' : 'true'));
|
133
|
+
summary.__details.__content.style.display = (expanded ? 'none' : 'block');
|
134
|
+
|
135
|
+
if (summary.__twisty) {
|
136
|
+
summary.__twisty.firstChild.nodeValue = (expanded ? '\u25ba' : '\u25bc');
|
137
|
+
summary.__twisty.setAttribute('class', (expanded ? 'arrow arrow-closed' : 'arrow arrow-open'));
|
138
|
+
}
|
139
|
+
|
140
|
+
return true;
|
141
|
+
}
|
142
|
+
|
143
|
+
// Bind a click event to handle summary elements
|
144
|
+
addClickEvent(document, function(e, summary) {
|
145
|
+
if (!(summary = getAncestor(summary, 'summary'))) {
|
146
|
+
return true;
|
147
|
+
}
|
148
|
+
return statechange(summary);
|
149
|
+
});
|
150
|
+
}
|
151
|
+
|
152
|
+
// Bind two load events for modern and older browsers
|
153
|
+
// If the first one fires it will set a flag to block the second one
|
154
|
+
// but if it's not supported then the second one will fire
|
155
|
+
addEvent(document, 'DOMContentLoaded', addDetailsPolyfill);
|
156
|
+
addEvent(window, 'load', addDetailsPolyfill);
|
157
|
+
|
158
|
+
})();
|
@@ -0,0 +1,137 @@
|
|
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
|
+
|
60
|
+
this.selections = {};
|
61
|
+
$.each(this.$elms, function (index, elm) {
|
62
|
+
var $elm = $(elm),
|
63
|
+
radioName = $elm.attr('name');
|
64
|
+
|
65
|
+
if (typeof this.selections[radioName] === 'undefined') {
|
66
|
+
this.selections[radioName] = false;
|
67
|
+
}
|
68
|
+
if ($elm.is(':checked')) {
|
69
|
+
selectionEventHandler($elm);
|
70
|
+
}
|
71
|
+
}.bind(this));
|
72
|
+
};
|
73
|
+
RadioButtons.prototype.bindEvents = function () {
|
74
|
+
BaseButtons.prototype.bindEvents.call(this);
|
75
|
+
};
|
76
|
+
RadioButtons.prototype.markSelected = function ($elm) {
|
77
|
+
var radioName = $elm.attr('name'),
|
78
|
+
$previousSelection = this.selections[radioName];
|
79
|
+
|
80
|
+
if ($previousSelection) {
|
81
|
+
$previousSelection.parent('label').removeClass(this.selectedClass);
|
82
|
+
}
|
83
|
+
$elm.parent('label').addClass(this.selectedClass);
|
84
|
+
this.selections[radioName] = $elm;
|
85
|
+
};
|
86
|
+
RadioButtons.prototype.markFocused = function ($elm) {
|
87
|
+
BaseButtons.prototype.markFocused.apply(this, arguments);
|
88
|
+
};
|
89
|
+
|
90
|
+
var CheckboxButtons = function ($elms, opts) {
|
91
|
+
BaseButtons.apply(this, arguments);
|
92
|
+
};
|
93
|
+
CheckboxButtons.prototype.setEventNames = function () {
|
94
|
+
BaseButtons.prototype.setEventNames.call(this);
|
95
|
+
};
|
96
|
+
CheckboxButtons.prototype.getSelections = function () {
|
97
|
+
var selectionEventHandler = this.markSelected.bind(this);
|
98
|
+
|
99
|
+
this.$elms.each(function (idx, elm) {
|
100
|
+
var $elm = $(elm);
|
101
|
+
|
102
|
+
if ($elm.is(':checked')) {
|
103
|
+
selectionEventHandler($elm);
|
104
|
+
}
|
105
|
+
});
|
106
|
+
};
|
107
|
+
CheckboxButtons.prototype.bindEvents = function () {
|
108
|
+
BaseButtons.prototype.bindEvents.call(this);
|
109
|
+
};
|
110
|
+
CheckboxButtons.prototype.markSelected = function ($elm) {
|
111
|
+
if ($elm.is(':checked')) {
|
112
|
+
$elm.parent('label').addClass(this.selectedClass);
|
113
|
+
} else {
|
114
|
+
$elm.parent('label').removeClass(this.selectedClass);
|
115
|
+
}
|
116
|
+
};
|
117
|
+
CheckboxButtons.prototype.markFocused = function ($elm) {
|
118
|
+
BaseButtons.prototype.markFocused.apply(this, arguments);
|
119
|
+
};
|
120
|
+
|
121
|
+
root.GOVUK.RadioButtons = RadioButtons;
|
122
|
+
root.GOVUK.CheckboxButtons = CheckboxButtons;
|
123
|
+
|
124
|
+
var selectionButtons = function ($elms, opts) {
|
125
|
+
var $radios = $elms.filter('[type=radio]'),
|
126
|
+
$checkboxes = $elms.filter('[type=checkbox]');
|
127
|
+
|
128
|
+
if ($radios) {
|
129
|
+
new GOVUK.RadioButtons($radios, opts);
|
130
|
+
}
|
131
|
+
if ($checkboxes) {
|
132
|
+
new GOVUK.CheckboxButtons($checkboxes, opts);
|
133
|
+
}
|
134
|
+
};
|
135
|
+
|
136
|
+
root.GOVUK.selectionButtons = selectionButtons;
|
137
|
+
}).call(this);
|