govuk_tech_docs 2.2.2 → 2.4.2

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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.nvmrc +1 -1
  4. data/.travis.yml +2 -0
  5. data/CHANGELOG.md +24 -0
  6. data/example/config/tech-docs.yml +1 -0
  7. data/example/source/single-page-nav.html.md +13 -0
  8. data/govuk_tech_docs.gemspec +2 -1
  9. data/lib/assets/javascripts/_modules/collapsible-navigation.js +7 -7
  10. data/lib/assets/javascripts/_modules/in-page-navigation.js +2 -4
  11. data/lib/assets/stylesheets/_govuk_tech_docs.scss +9 -10
  12. data/lib/assets/stylesheets/modules/_search.scss +4 -25
  13. data/lib/assets/stylesheets/modules/_technical-documentation.scss +1 -1
  14. data/lib/assets/stylesheets/modules/_toc.scss +11 -11
  15. data/lib/govuk_tech_docs/table_of_contents/heading.rb +5 -1
  16. data/lib/govuk_tech_docs/table_of_contents/heading_tree_renderer.rb +2 -2
  17. data/lib/govuk_tech_docs/table_of_contents/helpers.rb +22 -11
  18. data/lib/govuk_tech_docs/tech_docs_html_renderer.rb +1 -1
  19. data/lib/govuk_tech_docs/version.rb +1 -1
  20. data/lib/source/layouts/_header.erb +1 -2
  21. data/lib/source/layouts/layout.erb +3 -1
  22. data/node_modules/govuk-frontend/govuk/_base.scss +3 -0
  23. data/node_modules/govuk-frontend/govuk/all.js +306 -94
  24. data/node_modules/govuk-frontend/govuk/all.scss +1 -3
  25. data/node_modules/govuk-frontend/govuk/components/_all.scss +33 -29
  26. data/node_modules/govuk-frontend/govuk/components/accordion/_accordion.scss +2 -208
  27. data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +197 -0
  28. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +1 -1
  29. data/node_modules/govuk-frontend/govuk/components/back-link/_back-link.scss +2 -65
  30. data/node_modules/govuk-frontend/govuk/components/back-link/_index.scss +99 -0
  31. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_breadcrumbs.scss +2 -118
  32. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +138 -0
  33. data/node_modules/govuk-frontend/govuk/components/button/_button.scss +2 -284
  34. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +288 -0
  35. data/node_modules/govuk-frontend/govuk/components/character-count/_character-count.scss +2 -31
  36. data/node_modules/govuk-frontend/govuk/components/character-count/_index.scss +25 -0
  37. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +17 -9
  38. data/node_modules/govuk-frontend/govuk/components/checkboxes/_checkboxes.scss +2 -308
  39. data/node_modules/govuk-frontend/govuk/components/checkboxes/_index.scss +320 -0
  40. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +129 -24
  41. data/node_modules/govuk-frontend/govuk/components/cookie-banner/_cookie-banner.scss +2 -0
  42. data/node_modules/govuk-frontend/govuk/components/cookie-banner/_index.scss +51 -0
  43. data/node_modules/govuk-frontend/govuk/components/date-input/_date-input.scss +2 -30
  44. data/node_modules/govuk-frontend/govuk/components/date-input/_index.scss +26 -0
  45. data/node_modules/govuk-frontend/govuk/components/details/_details.scss +2 -88
  46. data/node_modules/govuk-frontend/govuk/components/details/_index.scss +87 -0
  47. data/node_modules/govuk-frontend/govuk/components/error-message/_error-message.scss +2 -15
  48. data/node_modules/govuk-frontend/govuk/components/error-message/_index.scss +11 -0
  49. data/node_modules/govuk-frontend/govuk/components/error-summary/_error-summary.scss +2 -59
  50. data/node_modules/govuk-frontend/govuk/components/error-summary/_index.scss +43 -0
  51. data/node_modules/govuk-frontend/govuk/components/fieldset/_fieldset.scss +2 -68
  52. data/node_modules/govuk-frontend/govuk/components/fieldset/_index.scss +64 -0
  53. data/node_modules/govuk-frontend/govuk/components/file-upload/_file-upload.scss +2 -81
  54. data/node_modules/govuk-frontend/govuk/components/file-upload/_index.scss +49 -0
  55. data/node_modules/govuk-frontend/govuk/components/footer/_footer.scss +2 -244
  56. data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +241 -0
  57. data/node_modules/govuk-frontend/govuk/components/header/_header.scss +2 -318
  58. data/node_modules/govuk-frontend/govuk/components/header/_index.scss +331 -0
  59. data/node_modules/govuk-frontend/govuk/components/header/header.js +665 -316
  60. data/node_modules/govuk-frontend/govuk/components/hint/_hint.scss +2 -50
  61. data/node_modules/govuk-frontend/govuk/components/hint/_index.scss +44 -0
  62. data/node_modules/govuk-frontend/govuk/components/input/_index.scss +191 -0
  63. data/node_modules/govuk-frontend/govuk/components/input/_input.scss +2 -103
  64. data/node_modules/govuk-frontend/govuk/components/inset-text/_index.scss +24 -0
  65. data/node_modules/govuk-frontend/govuk/components/inset-text/_inset-text.scss +2 -28
  66. data/node_modules/govuk-frontend/govuk/components/label/_index.scss +41 -0
  67. data/node_modules/govuk-frontend/govuk/components/label/_label.scss +2 -45
  68. data/node_modules/govuk-frontend/govuk/components/notification-banner/_index.scss +89 -0
  69. data/node_modules/govuk-frontend/govuk/components/notification-banner/_notification-banner.scss +2 -0
  70. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +61 -0
  71. data/node_modules/govuk-frontend/govuk/components/panel/_index.scss +44 -0
  72. data/node_modules/govuk-frontend/govuk/components/panel/_panel.scss +2 -44
  73. data/node_modules/govuk-frontend/govuk/components/phase-banner/_index.scss +27 -0
  74. data/node_modules/govuk-frontend/govuk/components/phase-banner/_phase-banner.scss +2 -31
  75. data/node_modules/govuk-frontend/govuk/components/radios/_index.scss +342 -0
  76. data/node_modules/govuk-frontend/govuk/components/radios/_radios.scss +2 -346
  77. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +76 -28
  78. data/node_modules/govuk-frontend/govuk/components/select/_index.scss +49 -0
  79. data/node_modules/govuk-frontend/govuk/components/select/_select.scss +2 -57
  80. data/node_modules/govuk-frontend/govuk/components/skip-link/_index.scss +36 -0
  81. data/node_modules/govuk-frontend/govuk/components/skip-link/_skip-link.scss +2 -37
  82. data/node_modules/govuk-frontend/govuk/components/summary-list/_index.scss +145 -0
  83. data/node_modules/govuk-frontend/govuk/components/summary-list/_summary-list.scss +2 -157
  84. data/node_modules/govuk-frontend/govuk/components/table/_index.scss +71 -0
  85. data/node_modules/govuk-frontend/govuk/components/table/_table.scss +2 -54
  86. data/node_modules/govuk-frontend/govuk/components/tabs/_index.scss +130 -0
  87. data/node_modules/govuk-frontend/govuk/components/tabs/_tabs.scss +2 -142
  88. data/node_modules/govuk-frontend/govuk/components/tag/_index.scss +86 -0
  89. data/node_modules/govuk-frontend/govuk/components/tag/_tag.scss +2 -91
  90. data/node_modules/govuk-frontend/govuk/components/textarea/_index.scss +47 -0
  91. data/node_modules/govuk-frontend/govuk/components/textarea/_textarea.scss +2 -55
  92. data/node_modules/govuk-frontend/govuk/components/warning-text/_index.scss +66 -0
  93. data/node_modules/govuk-frontend/govuk/components/warning-text/_warning-text.scss +2 -60
  94. data/node_modules/govuk-frontend/govuk/core/_global-styles.scss +5 -3
  95. data/node_modules/govuk-frontend/govuk/core/_links.scss +13 -3
  96. data/node_modules/govuk-frontend/govuk/core/_lists.scss +17 -3
  97. data/node_modules/govuk-frontend/govuk/core/_section-break.scss +5 -3
  98. data/node_modules/govuk-frontend/govuk/core/_template.scss +5 -4
  99. data/node_modules/govuk-frontend/govuk/core/_typography.scss +5 -3
  100. data/node_modules/govuk-frontend/govuk/helpers/_clearfix.scss +1 -1
  101. data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +1 -1
  102. data/node_modules/govuk-frontend/govuk/helpers/_device-pixels.scss +3 -3
  103. data/node_modules/govuk-frontend/govuk/helpers/_focused.scss +1 -1
  104. data/node_modules/govuk-frontend/govuk/helpers/_font-faces.scss +9 -11
  105. data/node_modules/govuk-frontend/govuk/helpers/_grid.scss +2 -1
  106. data/node_modules/govuk-frontend/govuk/helpers/_links.scss +246 -33
  107. data/node_modules/govuk-frontend/govuk/helpers/_media-queries.scss +2 -6
  108. data/node_modules/govuk-frontend/govuk/helpers/_shape-arrow.scss +1 -1
  109. data/node_modules/govuk-frontend/govuk/helpers/_spacing.scss +3 -2
  110. data/node_modules/govuk-frontend/govuk/helpers/_typography.scss +8 -7
  111. data/node_modules/govuk-frontend/govuk/helpers/_visually-hidden.scss +1 -1
  112. data/node_modules/govuk-frontend/govuk/objects/_all.scss +1 -0
  113. data/node_modules/govuk-frontend/govuk/objects/_button-group.scss +101 -0
  114. data/node_modules/govuk-frontend/govuk/objects/_form-group.scss +1 -4
  115. data/node_modules/govuk-frontend/govuk/objects/_grid.scss +3 -6
  116. data/node_modules/govuk-frontend/govuk/objects/_main-wrapper.scss +5 -4
  117. data/node_modules/govuk-frontend/govuk/objects/_width-container.scss +6 -4
  118. data/node_modules/govuk-frontend/govuk/overrides/_display.scss +6 -4
  119. data/node_modules/govuk-frontend/govuk/overrides/_spacing.scss +5 -3
  120. data/node_modules/govuk-frontend/govuk/overrides/_typography.scss +5 -3
  121. data/node_modules/govuk-frontend/govuk/overrides/_width.scss +6 -3
  122. data/node_modules/govuk-frontend/govuk/settings/_all.scss +2 -0
  123. data/node_modules/govuk-frontend/govuk/settings/_colours-applied.scss +11 -5
  124. data/node_modules/govuk-frontend/govuk/settings/_colours-organisations.scss +3 -0
  125. data/node_modules/govuk-frontend/govuk/settings/_colours-palette.scss +42 -35
  126. data/node_modules/govuk-frontend/govuk/settings/_compatibility.scss +0 -1
  127. data/node_modules/govuk-frontend/govuk/settings/_ie8.scss +1 -1
  128. data/node_modules/govuk-frontend/govuk/settings/_links.scss +62 -0
  129. data/node_modules/govuk-frontend/govuk/settings/_measurements.scss +4 -5
  130. data/node_modules/govuk-frontend/govuk/settings/_typography-font-families.scss +2 -2
  131. data/node_modules/govuk-frontend/govuk/settings/_typography-font.scss +14 -5
  132. data/node_modules/govuk-frontend/govuk/settings/_typography-responsive.scss +6 -2
  133. data/node_modules/govuk-frontend/govuk/tools/_compatibility.scss +1 -1
  134. data/node_modules/govuk-frontend/govuk/tools/_font-url.scss +1 -4
  135. data/node_modules/govuk-frontend/govuk/tools/_ie8.scss +1 -1
  136. data/node_modules/govuk-frontend/govuk/tools/_image-url.scss +1 -4
  137. data/node_modules/govuk-frontend/govuk/tools/_px-to-em.scss +1 -1
  138. data/node_modules/govuk-frontend/govuk/tools/_px-to-rem.scss +1 -1
  139. data/node_modules/govuk-frontend/govuk/utilities/_visually-hidden.scss +0 -1
  140. data/node_modules/govuk-frontend/govuk/vendor/_sass-mq.scss +0 -4
  141. data/package-lock.json +358 -288
  142. data/package.json +2 -2
  143. metadata +56 -4
@@ -0,0 +1,320 @@
1
+ @import "../error-message/index";
2
+ @import "../fieldset/index";
3
+ @import "../hint/index";
4
+ @import "../label/index";
5
+
6
+ @include govuk-exports("govuk/component/checkboxes") {
7
+
8
+ $govuk-touch-target-size: 44px;
9
+ $govuk-checkboxes-size: 40px;
10
+ $govuk-small-checkboxes-size: 24px;
11
+ $govuk-checkboxes-label-padding-left-right: govuk-spacing(3);
12
+
13
+ .govuk-checkboxes__item {
14
+ @include govuk-font($size: 19);
15
+
16
+ display: block;
17
+ position: relative;
18
+
19
+ min-height: $govuk-checkboxes-size;
20
+
21
+ margin-bottom: govuk-spacing(2);
22
+ padding-left: $govuk-checkboxes-size;
23
+
24
+ clear: left;
25
+ }
26
+
27
+ .govuk-checkboxes__item:last-child,
28
+ .govuk-checkboxes__item:last-of-type {
29
+ margin-bottom: 0;
30
+ }
31
+
32
+ .govuk-checkboxes__input {
33
+ $input-offset: ($govuk-touch-target-size - $govuk-checkboxes-size) / 2;
34
+
35
+ cursor: pointer;
36
+
37
+ // IE8 doesn’t support pseudo-elements, so we don’t want to hide native
38
+ // elements there.
39
+ @include govuk-not-ie8 {
40
+ position: absolute;
41
+
42
+ z-index: 1;
43
+ top: $input-offset * -1;
44
+ left: $input-offset * -1;
45
+
46
+ width: $govuk-touch-target-size;
47
+ height: $govuk-touch-target-size;
48
+ margin: 0;
49
+
50
+ opacity: 0;
51
+ }
52
+
53
+ @include govuk-if-ie8 {
54
+ margin-top: 10px;
55
+ margin-right: $govuk-checkboxes-size / -2;
56
+ margin-left: $govuk-checkboxes-size / -2;
57
+ float: left;
58
+
59
+ // add focus outline to input
60
+ &:focus {
61
+ outline: $govuk-focus-width solid $govuk-focus-colour;
62
+ }
63
+ }
64
+ }
65
+
66
+ .govuk-checkboxes__label {
67
+ display: inline-block;
68
+ margin-bottom: 0;
69
+ padding: 8px $govuk-checkboxes-label-padding-left-right govuk-spacing(1);
70
+ cursor: pointer;
71
+ // remove 300ms pause on mobile
72
+ -ms-touch-action: manipulation;
73
+ touch-action: manipulation;
74
+ }
75
+
76
+ @include govuk-not-ie8 {
77
+ // [ ] Check box
78
+ .govuk-checkboxes__label:before {
79
+ content: "";
80
+ box-sizing: border-box;
81
+ position: absolute;
82
+ top: 0;
83
+ left: 0;
84
+ width: $govuk-checkboxes-size;
85
+ height: $govuk-checkboxes-size;
86
+ border: $govuk-border-width-form-element solid currentColor;
87
+ background: transparent;
88
+ }
89
+
90
+ // ✔ Check mark
91
+ //
92
+ // The check mark is a box with a border on the left and bottom side (└──),
93
+ // rotated 45 degrees
94
+ .govuk-checkboxes__label:after {
95
+ content: "";
96
+ box-sizing: border-box;
97
+
98
+ position: absolute;
99
+ top: 11px;
100
+ left: 9px;
101
+ width: 23px;
102
+ height: 12px;
103
+
104
+ -webkit-transform: rotate(-45deg);
105
+
106
+ -ms-transform: rotate(-45deg);
107
+
108
+ transform: rotate(-45deg);
109
+ border: solid;
110
+ border-width: 0 0 5px 5px;
111
+ // Fix bug in IE11 caused by transform rotate (-45deg).
112
+ // See: alphagov/govuk_elements/issues/518
113
+ border-top-color: transparent;
114
+
115
+ opacity: 0;
116
+
117
+ background: transparent;
118
+ }
119
+ }
120
+
121
+ .govuk-checkboxes__hint {
122
+ display: block;
123
+ padding-right: $govuk-checkboxes-label-padding-left-right;
124
+ padding-left: $govuk-checkboxes-label-padding-left-right;
125
+ }
126
+
127
+ // Focused state
128
+ .govuk-checkboxes__input:focus + .govuk-checkboxes__label:before {
129
+ border-width: 4px;
130
+ box-shadow: 0 0 0 $govuk-focus-width $govuk-focus-colour;
131
+ }
132
+
133
+ // Selected state
134
+ .govuk-checkboxes__input:checked + .govuk-checkboxes__label:after {
135
+ opacity: 1;
136
+ }
137
+
138
+ // Disabled state
139
+ .govuk-checkboxes__input:disabled,
140
+ .govuk-checkboxes__input:disabled + .govuk-checkboxes__label {
141
+ cursor: default;
142
+ }
143
+
144
+ .govuk-checkboxes__input:disabled + .govuk-checkboxes__label {
145
+ opacity: .5;
146
+ }
147
+
148
+ // =========================================================
149
+ // Dividers ('or')
150
+ // =========================================================
151
+
152
+ .govuk-checkboxes__divider {
153
+ $govuk-divider-size: $govuk-checkboxes-size !default;
154
+ @include govuk-font($size: 19);
155
+ @include govuk-text-colour;
156
+ width: $govuk-divider-size;
157
+ margin-bottom: govuk-spacing(2);
158
+ text-align: center;
159
+ }
160
+
161
+ // =========================================================
162
+ // Conditional reveals
163
+ // =========================================================
164
+
165
+ // The narrow border is used in the conditional reveals because the border has
166
+ // to be an even number in order to be centred under the 40px checkbox or radio.
167
+ $conditional-border-width: $govuk-border-width-narrow;
168
+ // Calculate the amount of padding needed to keep the border centered against the checkbox.
169
+ $conditional-border-padding: ($govuk-checkboxes-size / 2) - ($conditional-border-width / 2);
170
+ // Move the border centered with the checkbox
171
+ $conditional-margin-left: $conditional-border-padding;
172
+ // Move the contents of the conditional inline with the label
173
+ $conditional-padding-left: $conditional-border-padding + $govuk-checkboxes-label-padding-left-right;
174
+
175
+ .govuk-checkboxes__conditional {
176
+ @include govuk-responsive-margin(4, "bottom");
177
+ margin-left: $conditional-margin-left;
178
+ padding-left: $conditional-padding-left;
179
+ border-left: $conditional-border-width solid $govuk-border-colour;
180
+
181
+ .js-enabled &--hidden {
182
+ display: none;
183
+ }
184
+
185
+ & > :last-child {
186
+ margin-bottom: 0;
187
+ }
188
+ }
189
+
190
+ // =========================================================
191
+ // Small checkboxes
192
+ // =========================================================
193
+
194
+ .govuk-checkboxes--small {
195
+
196
+ $input-offset: ($govuk-touch-target-size - $govuk-small-checkboxes-size) / 2;
197
+ $label-offset: $govuk-touch-target-size - $input-offset;
198
+
199
+ .govuk-checkboxes__item {
200
+ @include govuk-clearfix;
201
+ min-height: 0;
202
+ margin-bottom: 0;
203
+ padding-left: $label-offset;
204
+ float: left;
205
+ }
206
+
207
+ // Shift the touch target into the left margin so that the visible edge of
208
+ // the control is aligned
209
+ //
210
+ // ┆What colours do you like?
211
+ // ┌┆───┐
212
+ // │┆[] │ Purple
213
+ // └┆▲──┘
214
+ // ▲┆└─ Check box pseudo element, aligned with margin
215
+ // └─── Touch target (invisible input), shifted into the margin
216
+ .govuk-checkboxes__input {
217
+ @include govuk-not-ie8 {
218
+ left: $input-offset * -1;
219
+ }
220
+
221
+ @include govuk-if-ie8 {
222
+ margin-left: $govuk-small-checkboxes-size * -1;
223
+ }
224
+ }
225
+
226
+ // Adjust the size and position of the label.
227
+ //
228
+ // Unlike larger checkboxes, we also have to float the label in order to
229
+ // 'shrink' it, preventing the hover state from kicking in across the full
230
+ // width of the parent element.
231
+ .govuk-checkboxes__label {
232
+ margin-top: -2px;
233
+ padding: 13px govuk-spacing(3) 13px 1px;
234
+ float: left;
235
+
236
+ @include govuk-media-query($from: tablet) {
237
+ padding: 11px govuk-spacing(3) 10px 1px;
238
+ }
239
+ }
240
+
241
+ // [ ] Check box
242
+ //
243
+ // Reduce the size of the check box [1], vertically center it within the
244
+ // touch target [2]
245
+ .govuk-checkboxes__label:before {
246
+ top: $input-offset - $govuk-border-width-form-element; // 2
247
+ width: $govuk-small-checkboxes-size; // 1
248
+ height: $govuk-small-checkboxes-size; // 1
249
+ }
250
+
251
+ // ✔ Check mark
252
+ //
253
+ // Reduce the size of the check mark and re-align within the checkbox
254
+ .govuk-checkboxes__label:after {
255
+ top: 15px;
256
+ left: 6px;
257
+ width: 12px;
258
+ height: 6.5px;
259
+ border-width: 0 0 3px 3px;
260
+ }
261
+
262
+ // Fix position of hint with small checkboxes
263
+ //
264
+ // Do not use hints with small checkboxes – because they're within the input
265
+ // wrapper they trigger the hover state, but clicking them doesn't actually
266
+ // activate the control.
267
+ //
268
+ // (If you do use them, they won't look completely broken... but seriously,
269
+ // don't use them)
270
+ .govuk-checkboxes__hint {
271
+ padding: 0;
272
+ clear: both;
273
+ }
274
+
275
+ // Align conditional reveals with small checkboxes
276
+ .govuk-checkboxes__conditional {
277
+ $margin-left: ($govuk-small-checkboxes-size / 2) - ($conditional-border-width / 2);
278
+ margin-left: $margin-left;
279
+ padding-left: $label-offset - ($margin-left + $conditional-border-width);
280
+ clear: both;
281
+ }
282
+
283
+ // Hover state for small checkboxes.
284
+ //
285
+ // We use a hover state for small checkboxes because the touch target size
286
+ // is so much larger than their visible size, and so we need to provide
287
+ // feedback to the user as to which checkbox they will select when their
288
+ // cursor is outside of the visible area.
289
+ .govuk-checkboxes__item:hover .govuk-checkboxes__input:not(:disabled) + .govuk-checkboxes__label:before {
290
+ box-shadow: 0 0 0 $govuk-hover-width $govuk-hover-colour;
291
+ }
292
+
293
+ // Because we've overridden the border-shadow provided by the focus state,
294
+ // we need to redefine that too.
295
+ //
296
+ // We use two box shadows, one that restores the original focus state [1]
297
+ // and another that then applies the hover state [2].
298
+ .govuk-checkboxes__item:hover .govuk-checkboxes__input:focus + .govuk-checkboxes__label:before {
299
+ box-shadow:
300
+ 0 0 0 $govuk-focus-width $govuk-focus-colour, // 1
301
+ 0 0 0 $govuk-hover-width $govuk-hover-colour; // 2
302
+ }
303
+
304
+ // For devices that explicitly don't support hover, don't provide a hover
305
+ // state (e.g. on touch devices like iOS).
306
+ //
307
+ // We can't use `@media (hover: hover)` because we wouldn't get the hover
308
+ // state in browsers that don't support `@media (hover)` (like Internet
309
+ // Explorer) – so we have to 'undo' the hover state instead.
310
+ @media (hover: none), (pointer: coarse) {
311
+ .govuk-checkboxes__item:hover .govuk-checkboxes__input:not(:disabled) + .govuk-checkboxes__label:before {
312
+ box-shadow: initial;
313
+ }
314
+
315
+ .govuk-checkboxes__item:hover .govuk-checkboxes__input:focus + .govuk-checkboxes__label:before {
316
+ box-shadow: 0 0 0 $govuk-focus-width $govuk-focus-colour;
317
+ }
318
+ }
319
+ }
320
+ }
@@ -1033,52 +1033,157 @@ function Checkboxes ($module) {
1033
1033
  this.$inputs = $module.querySelectorAll('input[type="checkbox"]');
1034
1034
  }
1035
1035
 
1036
+ /**
1037
+ * Initialise Checkboxes
1038
+ *
1039
+ * Checkboxes can be associated with a 'conditionally revealed' content block –
1040
+ * for example, a checkbox for 'Phone' could reveal an additional form field for
1041
+ * the user to enter their phone number.
1042
+ *
1043
+ * These associations are made using a `data-aria-controls` attribute, which is
1044
+ * promoted to an aria-controls attribute during initialisation.
1045
+ *
1046
+ * We also need to restore the state of any conditional reveals on the page (for
1047
+ * example if the user has navigated back), and set up event handlers to keep
1048
+ * the reveal in sync with the checkbox state.
1049
+ */
1036
1050
  Checkboxes.prototype.init = function () {
1037
1051
  var $module = this.$module;
1038
1052
  var $inputs = this.$inputs;
1039
1053
 
1040
- /**
1041
- * Loop over all items with [data-controls]
1042
- * Check if they have a matching conditional reveal
1043
- * If they do, assign attributes.
1044
- **/
1045
1054
  nodeListForEach($inputs, function ($input) {
1046
- var controls = $input.getAttribute('data-aria-controls');
1055
+ var target = $input.getAttribute('data-aria-controls');
1047
1056
 
1048
- // Check if input controls anything
1049
- // Check if content exists, before setting attributes.
1050
- if (!controls || !$module.querySelector('#' + controls)) {
1057
+ // Skip checkboxes without data-aria-controls attributes, or where the
1058
+ // target element does not exist.
1059
+ if (!target || !$module.querySelector('#' + target)) {
1051
1060
  return
1052
1061
  }
1053
1062
 
1054
- // If we have content that is controlled, set attributes.
1055
- $input.setAttribute('aria-controls', controls);
1063
+ // Promote the data-aria-controls attribute to a aria-controls attribute
1064
+ // so that the relationship is exposed in the AOM
1065
+ $input.setAttribute('aria-controls', target);
1056
1066
  $input.removeAttribute('data-aria-controls');
1057
- this.setAttributes($input);
1058
- }.bind(this));
1067
+ });
1068
+
1069
+ // When the page is restored after navigating 'back' in some browsers the
1070
+ // state of form controls is not restored until *after* the DOMContentLoaded
1071
+ // event is fired, so we need to sync after the pageshow event in browsers
1072
+ // that support it.
1073
+ if ('onpageshow' in window) {
1074
+ window.addEventListener('pageshow', this.syncAllConditionalReveals.bind(this));
1075
+ } else {
1076
+ window.addEventListener('DOMContentLoaded', this.syncAllConditionalReveals.bind(this));
1077
+ }
1078
+
1079
+ // Although we've set up handlers to sync state on the pageshow or
1080
+ // DOMContentLoaded event, init could be called after those events have fired,
1081
+ // for example if they are added to the page dynamically, so sync now too.
1082
+ this.syncAllConditionalReveals();
1059
1083
 
1060
- // Handle events
1061
1084
  $module.addEventListener('click', this.handleClick.bind(this));
1062
1085
  };
1063
1086
 
1064
- Checkboxes.prototype.setAttributes = function ($input) {
1065
- var inputIsChecked = $input.checked;
1066
- $input.setAttribute('aria-expanded', inputIsChecked);
1087
+ /**
1088
+ * Sync the conditional reveal states for all inputs in this $module.
1089
+ */
1090
+ Checkboxes.prototype.syncAllConditionalReveals = function () {
1091
+ nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this));
1092
+ };
1093
+
1094
+ /**
1095
+ * Sync conditional reveal with the input state
1096
+ *
1097
+ * Synchronise the visibility of the conditional reveal, and its accessible
1098
+ * state, with the input's checked state.
1099
+ *
1100
+ * @param {HTMLInputElement} $input Checkbox input
1101
+ */
1102
+ Checkboxes.prototype.syncConditionalRevealWithInputState = function ($input) {
1103
+ var $target = this.$module.querySelector('#' + $input.getAttribute('aria-controls'));
1067
1104
 
1068
- var $content = this.$module.querySelector('#' + $input.getAttribute('aria-controls'));
1069
- if ($content) {
1070
- $content.classList.toggle('govuk-checkboxes__conditional--hidden', !inputIsChecked);
1105
+ if ($target && $target.classList.contains('govuk-checkboxes__conditional')) {
1106
+ var inputIsChecked = $input.checked;
1107
+
1108
+ $input.setAttribute('aria-expanded', inputIsChecked);
1109
+ $target.classList.toggle('govuk-checkboxes__conditional--hidden', !inputIsChecked);
1071
1110
  }
1072
1111
  };
1073
1112
 
1113
+ /**
1114
+ * Uncheck other checkboxes
1115
+ *
1116
+ * Find any other checkbox inputs with the same name value, and uncheck them.
1117
+ * This is useful for when a “None of these" checkbox is checked.
1118
+ */
1119
+ Checkboxes.prototype.unCheckAllInputsExcept = function ($input) {
1120
+ var allInputsWithSameName = document.querySelectorAll('input[type="checkbox"][name="' + $input.name + '"]');
1121
+
1122
+ nodeListForEach(allInputsWithSameName, function ($inputWithSameName) {
1123
+ var hasSameFormOwner = ($input.form === $inputWithSameName.form);
1124
+ if (hasSameFormOwner && $inputWithSameName !== $input) {
1125
+ $inputWithSameName.checked = false;
1126
+ }
1127
+ });
1128
+
1129
+ this.syncAllConditionalReveals();
1130
+ };
1131
+
1132
+ /**
1133
+ * Uncheck exclusive inputs
1134
+ *
1135
+ * Find any checkbox inputs with the same name value and the 'exclusive' behaviour,
1136
+ * and uncheck them. This helps prevent someone checking both a regular checkbox and a
1137
+ * "None of these" checkbox in the same fieldset.
1138
+ */
1139
+ Checkboxes.prototype.unCheckExclusiveInputs = function ($input) {
1140
+ var allInputsWithSameNameAndExclusiveBehaviour = document.querySelectorAll(
1141
+ 'input[data-behaviour="exclusive"][type="checkbox"][name="' + $input.name + '"]'
1142
+ );
1143
+
1144
+ nodeListForEach(allInputsWithSameNameAndExclusiveBehaviour, function ($exclusiveInput) {
1145
+ var hasSameFormOwner = ($input.form === $exclusiveInput.form);
1146
+ if (hasSameFormOwner) {
1147
+ $exclusiveInput.checked = false;
1148
+ }
1149
+ });
1150
+
1151
+ this.syncAllConditionalReveals();
1152
+ };
1153
+
1154
+ /**
1155
+ * Click event handler
1156
+ *
1157
+ * Handle a click within the $module – if the click occurred on a checkbox, sync
1158
+ * the state of any associated conditional reveal with the checkbox state.
1159
+ *
1160
+ * @param {MouseEvent} event Click event
1161
+ */
1074
1162
  Checkboxes.prototype.handleClick = function (event) {
1075
1163
  var $target = event.target;
1076
1164
 
1077
- // If a checkbox with aria-controls, handle click
1078
- var isCheckbox = $target.getAttribute('type') === 'checkbox';
1165
+ // Ignore clicks on things that aren't checkbox inputs
1166
+ if ($target.type !== 'checkbox') {
1167
+ return
1168
+ }
1169
+
1170
+ // If the checkbox conditionally-reveals some content, sync the state
1079
1171
  var hasAriaControls = $target.getAttribute('aria-controls');
1080
- if (isCheckbox && hasAriaControls) {
1081
- this.setAttributes($target);
1172
+ if (hasAriaControls) {
1173
+ this.syncConditionalRevealWithInputState($target);
1174
+ }
1175
+
1176
+ // No further behaviour needed for unchecking
1177
+ if (!$target.checked) {
1178
+ return
1179
+ }
1180
+
1181
+ // Handle 'exclusive' checkbox behaviour (ie "None of these")
1182
+ var hasBehaviourExclusive = ($target.getAttribute('data-behaviour') === 'exclusive');
1183
+ if (hasBehaviourExclusive) {
1184
+ this.unCheckAllInputsExcept($target);
1185
+ } else {
1186
+ this.unCheckExclusiveInputs($target);
1082
1187
  }
1083
1188
  };
1084
1189