govuk_publishing_components 35.15.5 → 35.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/govuk_publishing_components_manifest.js +1 -0
  3. data/app/assets/images/option-select/input-icon.svg +3 -0
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-page-views.js +19 -1
  5. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/pii-remover.js +1 -1
  6. data/app/assets/javascripts/govuk_publishing_components/components/option-select.js +312 -0
  7. data/app/assets/stylesheets/govuk_publishing_components/_all_components.scss +1 -0
  8. data/app/assets/stylesheets/govuk_publishing_components/components/_image-card.scss +26 -0
  9. data/app/assets/stylesheets/govuk_publishing_components/components/_option-select.scss +172 -0
  10. data/app/views/govuk_publishing_components/components/_image_card.html.erb +8 -2
  11. data/app/views/govuk_publishing_components/components/_option_select.html.erb +71 -0
  12. data/app/views/govuk_publishing_components/components/docs/image_card.yml +13 -0
  13. data/app/views/govuk_publishing_components/components/docs/option_select.yml +343 -0
  14. data/config/locales/ar.yml +4 -0
  15. data/config/locales/az.yml +4 -0
  16. data/config/locales/be.yml +4 -0
  17. data/config/locales/bg.yml +4 -0
  18. data/config/locales/bn.yml +4 -0
  19. data/config/locales/cs.yml +4 -0
  20. data/config/locales/cy.yml +4 -0
  21. data/config/locales/da.yml +4 -0
  22. data/config/locales/de.yml +4 -0
  23. data/config/locales/dr.yml +4 -0
  24. data/config/locales/el.yml +4 -0
  25. data/config/locales/en.yml +4 -0
  26. data/config/locales/es-419.yml +4 -0
  27. data/config/locales/es.yml +4 -0
  28. data/config/locales/et.yml +4 -0
  29. data/config/locales/fa.yml +4 -0
  30. data/config/locales/fi.yml +4 -0
  31. data/config/locales/fr.yml +4 -0
  32. data/config/locales/gd.yml +4 -0
  33. data/config/locales/gu.yml +4 -0
  34. data/config/locales/he.yml +4 -0
  35. data/config/locales/hi.yml +4 -0
  36. data/config/locales/hr.yml +4 -0
  37. data/config/locales/hu.yml +4 -0
  38. data/config/locales/hy.yml +4 -0
  39. data/config/locales/id.yml +4 -0
  40. data/config/locales/is.yml +4 -0
  41. data/config/locales/it.yml +4 -0
  42. data/config/locales/ja.yml +4 -0
  43. data/config/locales/ka.yml +4 -0
  44. data/config/locales/kk.yml +4 -0
  45. data/config/locales/ko.yml +4 -0
  46. data/config/locales/lt.yml +4 -0
  47. data/config/locales/lv.yml +4 -0
  48. data/config/locales/ms.yml +4 -0
  49. data/config/locales/mt.yml +4 -0
  50. data/config/locales/nl.yml +4 -0
  51. data/config/locales/no.yml +4 -0
  52. data/config/locales/pa-pk.yml +4 -0
  53. data/config/locales/pa.yml +4 -0
  54. data/config/locales/pl.yml +4 -0
  55. data/config/locales/ps.yml +4 -0
  56. data/config/locales/pt.yml +4 -0
  57. data/config/locales/ro.yml +4 -0
  58. data/config/locales/ru.yml +4 -0
  59. data/config/locales/si.yml +4 -0
  60. data/config/locales/sk.yml +4 -0
  61. data/config/locales/sl.yml +4 -0
  62. data/config/locales/so.yml +4 -0
  63. data/config/locales/sq.yml +4 -0
  64. data/config/locales/sr.yml +4 -0
  65. data/config/locales/sv.yml +4 -0
  66. data/config/locales/sw.yml +4 -0
  67. data/config/locales/ta.yml +4 -0
  68. data/config/locales/th.yml +4 -0
  69. data/config/locales/tk.yml +4 -0
  70. data/config/locales/tr.yml +4 -0
  71. data/config/locales/uk.yml +4 -0
  72. data/config/locales/ur.yml +4 -0
  73. data/config/locales/uz.yml +4 -0
  74. data/config/locales/vi.yml +4 -0
  75. data/config/locales/zh-hk.yml +4 -0
  76. data/config/locales/zh-tw.yml +4 -0
  77. data/config/locales/zh.yml +4 -0
  78. data/lib/govuk_publishing_components/presenters/image_card_helper.rb +6 -2
  79. data/lib/govuk_publishing_components/version.rb +1 -1
  80. data/node_modules/axe-core/axe.js +6 -6
  81. data/node_modules/axe-core/axe.min.js +2 -2
  82. data/node_modules/axe-core/locales/_template.json +4 -4
  83. data/node_modules/axe-core/package.json +1 -1
  84. data/node_modules/axe-core/sri-history.json +4 -0
  85. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3117de6a0b4a66caea24d67e4c462c728ef09fdc3cd3503ca0e2429673726631
4
- data.tar.gz: 164a43d2a5ccc732f37fbdf6eb862fc500311f473b9cf1aca998515d6c978c14
3
+ metadata.gz: d631949b7bdb6aa90aff397986118f929cbae4071ef96b3a623c123c6496224d
4
+ data.tar.gz: ef75a103619c27bc964664673a17475579132d0b909e254fce0afcc6316256d9
5
5
  SHA512:
6
- metadata.gz: f9e56a63dc6f33e2df012aa4993cdd9404d07d8c96ecfe39ffe253676aa443deabadc47062b4a9443ee8e2f0fa89943629dd145b8e673f9c1ddef6e56fb5624d
7
- data.tar.gz: ca4f944dbcbf4d77171f797bc1f3b16caad24b513dbb2c49d0749edba6570f9acb701922cd0fa4d33259ec2b80ebbabd5ad8e9390bfa2fb0e54aa58e5eab14d5
6
+ metadata.gz: '01786b90a26218b6b5dff054852d4e67a77f36d307029ff6fd03f8b15cd005e9264f8719a03f78f05dc776f284c24c972eb6101940e8afd41a44a696c3b404f5'
7
+ data.tar.gz: 775eb1304962e2349b3e19c8ce7acfb92dc927443a7a01253692909c174f6b5e196129d40104f773794b2ac8e1674e0c65361e605808910f01163b9d69ad5f6a
@@ -60,6 +60,7 @@
60
60
  //= link govuk_publishing_components/components/_metadata.css
61
61
  //= link govuk_publishing_components/components/_modal-dialogue.css
62
62
  //= link govuk_publishing_components/components/_notice.css
63
+ //= link govuk_publishing_components/components/_option-select.css
63
64
  //= link govuk_publishing_components/components/_organisation-logo.css
64
65
  //= link govuk_publishing_components/components/_panel.css
65
66
  //= link govuk_publishing_components/components/_phase-banner.css
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" width="40" height="40">
2
+ <path d="M25.7 24.8L21.9 21c.7-1 1.1-2.2 1.1-3.5 0-3.6-2.9-6.5-6.5-6.5S10 13.9 10 17.5s2.9 6.5 6.5 6.5c1.6 0 3-.6 4.1-1.5l3.7 3.7 1.4-1.4zM12 17.5c0-2.5 2-4.5 4.5-4.5s4.5 2 4.5 4.5-2 4.5-4.5 4.5-4.5-2-4.5-4.5z" fill="currentColor" />
3
+ </svg>
@@ -59,7 +59,8 @@ window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analytics
59
59
  devolved_nations_banner: this.getElementAttribute('data-ga4-devolved-nations-banner') || undefined,
60
60
  cookie_banner: document.querySelector('[data-ga4-cookie-banner]') ? 'true' : undefined,
61
61
  intervention: this.getInterventionPresence(),
62
- query_string: this.getQueryString()
62
+ query_string: this.getQueryString(),
63
+ search_term: this.getSearchTerm()
63
64
  }
64
65
  }
65
66
  window.GOVUK.analyticsGa4.core.sendData(data)
@@ -74,6 +75,23 @@ window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analytics
74
75
  return window.location.search
75
76
  },
76
77
 
78
+ getSearchTerm: function () {
79
+ var queryString = this.getSearch()
80
+
81
+ if (!queryString) {
82
+ return undefined
83
+ }
84
+
85
+ var searchTerm = queryString.match(/keywords=([^&]*)/)
86
+ if (!searchTerm) {
87
+ return undefined
88
+ }
89
+
90
+ searchTerm = searchTerm[0].replace('keywords=', '')
91
+ searchTerm = this.PIIRemover.stripPIIWithOverride(searchTerm, true, true)
92
+ return searchTerm
93
+ },
94
+
77
95
  getQueryString: function () {
78
96
  var queryString = this.getSearch()
79
97
  if (queryString) {
@@ -2,7 +2,7 @@
2
2
  'use strict'
3
3
 
4
4
  var GOVUK = global.GOVUK || {}
5
- var EMAIL_PATTERN = /[^\s=/?&#]+(?:@|%40)[^\s=/?&]+/g
5
+ var EMAIL_PATTERN = /[^\s=/?&#+]+(?:@|%40)[^\s=/?&+]+/g
6
6
  var POSTCODE_PATTERN = /\b[A-PR-UWYZ][A-HJ-Z]?[0-9][0-9A-HJKMNPR-Y]?(?:[\s+]|%20)*[0-9](?!refund)[ABD-HJLNPQ-Z]{2,3}\b/gi
7
7
  var DATE_PATTERN_NUMERIC = /\d{4}(-?)\d{2}(-?)\d{2}/g
8
8
  var DATE_PATTERN_STRING = /\d{1,2}\s(January|February|March|April|May|June|July|August|September|October|November|December)\s\d{4}/g
@@ -0,0 +1,312 @@
1
+ window.GOVUK = window.GOVUK || {}
2
+ window.GOVUK.Modules = window.GOVUK.Modules || {};
3
+
4
+ (function (Modules) {
5
+ /* This JavaScript provides two functional enhancements to option-select components:
6
+ 1) A count that shows how many results have been checked in the option-container
7
+ 2) Open/closing of the list of checkboxes
8
+ */
9
+ function OptionSelect ($module) {
10
+ this.$optionSelect = $module
11
+ this.$options = this.$optionSelect.querySelectorAll("input[type='checkbox']")
12
+ this.$optionsContainer = this.$optionSelect.querySelector('.js-options-container')
13
+ this.$optionList = this.$optionsContainer.querySelector('.js-auto-height-inner')
14
+ this.$allCheckboxes = this.$optionsContainer.querySelectorAll('.govuk-checkboxes__item')
15
+ this.hasFilter = this.$optionSelect.getAttribute('data-filter-element') || ''
16
+
17
+ this.checkedCheckboxes = []
18
+
19
+ this.mq = window.matchMedia('(min-width: 641px)')
20
+ this.isClosedOnLoad = this.$optionSelect.getAttribute('data-closed-on-load')
21
+ this.isClosedOnLoadMobile = this.$optionSelect.getAttribute('data-closed-on-load-mobile')
22
+ }
23
+
24
+ OptionSelect.prototype.init = function () {
25
+ if (this.hasFilter.length) {
26
+ var filterEl = document.createElement('div')
27
+ filterEl.innerHTML = this.hasFilter
28
+
29
+ var optionSelectFilter = document.createElement('div')
30
+ optionSelectFilter.classList.add('gem-c-option-select__filter')
31
+ optionSelectFilter.innerHTML = filterEl.childNodes[0].nodeValue
32
+
33
+ this.$optionsContainer.parentNode.insertBefore(optionSelectFilter, this.$optionsContainer)
34
+
35
+ this.$filter = this.$optionSelect.querySelector('input[name="option-select-filter"]')
36
+ this.$filterCount = document.getElementById(this.$filter.getAttribute('aria-describedby'))
37
+ this.filterTextSingle = ' ' + this.$filterCount.getAttribute('data-single')
38
+ this.filterTextMultiple = ' ' + this.$filterCount.getAttribute('data-multiple')
39
+ this.filterTextSelected = ' ' + this.$filterCount.getAttribute('data-selected')
40
+ this.checkboxLabels = []
41
+ this.filterTimeout = 0
42
+
43
+ this.getAllCheckedCheckboxes()
44
+ for (var i = 0; i < this.$allCheckboxes.length; i++) {
45
+ this.checkboxLabels.push(this.cleanString(this.$allCheckboxes[i].textContent))
46
+ }
47
+
48
+ this.$filter.addEventListener('keyup', this.typeFilterText.bind(this))
49
+ }
50
+
51
+ // Attach listener to update checked count
52
+ this.$optionsContainer.querySelector('.gem-c-checkboxes__list').addEventListener('change', this.updateCheckedCount.bind(this))
53
+
54
+ // Replace div.container-head with a button
55
+ this.replaceHeadingSpanWithButton()
56
+
57
+ // Add js-collapsible class to parent for CSS
58
+ this.$optionSelect.classList.add('js-collapsible')
59
+
60
+ // Add open/close listeners
61
+ var button = this.$optionSelect.querySelector('.js-container-button')
62
+ button.addEventListener('click', this.toggleOptionSelect.bind(this))
63
+
64
+ // Toggle option visibility depending on screen size (min-width: 641px) and
65
+ // presence of any 'closed' properties (`data-closed-on-load`, `data-closed-on-load-mobile`).
66
+ // See https://github.com/alphagov/govuk-frontend/blob/main/packages/govuk-frontend/src/govuk/settings/_media-queries.scss#L10-L14
67
+ if (this.mq.matches) {
68
+ this.toggleVisibility(true)
69
+ } else {
70
+ this.toggleVisibility(false)
71
+ }
72
+
73
+ var checkedString = this.checkedString()
74
+ if (checkedString) {
75
+ this.attachCheckedCounter(checkedString)
76
+ }
77
+ }
78
+
79
+ OptionSelect.prototype.toggleVisibility = function (isTabletOrLarger) {
80
+ if (isTabletOrLarger) {
81
+ if (this.isClosedOnLoad === 'true') {
82
+ this.close()
83
+ } else {
84
+ this.setupHeight()
85
+ }
86
+ } else {
87
+ if (this.isClosedOnLoadMobile === 'true' || this.isClosedOnLoad === 'true') {
88
+ this.close()
89
+ } else {
90
+ this.setContainerHeight(201)
91
+ }
92
+ }
93
+ }
94
+
95
+ OptionSelect.prototype.typeFilterText = function (event) {
96
+ event.stopPropagation()
97
+ var ENTER_KEY = 13
98
+
99
+ if (event.keyCode !== ENTER_KEY) {
100
+ clearTimeout(this.filterTimeout)
101
+ this.filterTimeout = setTimeout(
102
+ function () { this.doFilter(this) }.bind(this),
103
+ 300
104
+ )
105
+ } else {
106
+ event.preventDefault() // prevents finder forms from being submitted when user presses ENTER
107
+ }
108
+ }
109
+
110
+ OptionSelect.prototype.cleanString = function cleanString (text) {
111
+ text = text.replace(/&/g, 'and')
112
+ text = text.replace(/[’',:–-]/g, '') // remove punctuation characters
113
+ text = text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // escape special characters
114
+ return text.trim().replace(/\s\s+/g, ' ').toLowerCase() // replace multiple spaces with one
115
+ }
116
+
117
+ OptionSelect.prototype.getAllCheckedCheckboxes = function getAllCheckedCheckboxes () {
118
+ this.checkedCheckboxes = []
119
+
120
+ for (var i = 0; i < this.$options.length; i++) {
121
+ if (this.$options[i].checked) {
122
+ this.checkedCheckboxes.push(i)
123
+ }
124
+ }
125
+ }
126
+
127
+ OptionSelect.prototype.doFilter = function doFilter (obj) {
128
+ var filterBy = obj.cleanString(obj.$filter.value)
129
+ var showCheckboxes = obj.checkedCheckboxes.slice()
130
+ var i = 0
131
+
132
+ for (i = 0; i < obj.$allCheckboxes.length; i++) {
133
+ if (showCheckboxes.indexOf(i) === -1 && obj.checkboxLabels[i].search(filterBy) !== -1) {
134
+ showCheckboxes.push(i)
135
+ }
136
+ }
137
+
138
+ for (i = 0; i < obj.$allCheckboxes.length; i++) {
139
+ obj.$allCheckboxes[i].style.display = 'none'
140
+ }
141
+
142
+ for (i = 0; i < showCheckboxes.length; i++) {
143
+ obj.$allCheckboxes[showCheckboxes[i]].style.display = 'block'
144
+ }
145
+
146
+ var lenChecked = obj.$optionsContainer.querySelectorAll('.govuk-checkboxes__input:checked').length
147
+ var len = showCheckboxes.length + lenChecked
148
+ var html = len + (len === 1 ? obj.filterTextSingle : obj.filterTextMultiple) + ', ' + lenChecked + obj.filterTextSelected
149
+ obj.$filterCount.innerHTML = html
150
+ }
151
+
152
+ OptionSelect.prototype.replaceHeadingSpanWithButton = function replaceHeadingSpanWithButton () {
153
+ /* Replace the span within the heading with a button element. This is based on feedback from Léonie Watson.
154
+ * The button has all of the accessibility hooks that are used by screen readers and etc.
155
+ * We do this in the JavaScript because if the JavaScript is not active then the button shouldn't
156
+ * be there as there is no JS to handle the click event.
157
+ */
158
+ var containerHead = this.$optionSelect.querySelector('.js-container-button')
159
+ var jsContainerHeadHTML = containerHead.innerHTML
160
+
161
+ // Create button and replace the preexisting html with the button.
162
+ var button = document.createElement('button')
163
+ button.setAttribute('class', 'js-container-button gem-c-option-select__title gem-c-option-select__button')
164
+ // Add type button to override default type submit when this component is used within a form
165
+ button.setAttribute('type', 'button')
166
+ button.setAttribute('aria-expanded', true)
167
+ button.setAttribute('id', containerHead.getAttribute('id'))
168
+ button.setAttribute('aria-controls', this.$optionsContainer.getAttribute('id'))
169
+ button.innerHTML = jsContainerHeadHTML
170
+ containerHead.parentNode.replaceChild(button, containerHead)
171
+
172
+ // GA4 Accordion tracking. Relies on the ga4-finder-tracker setting the index first, so we wrap this in a custom event.
173
+ window.addEventListener('ga4-filter-indexes-added', function () {
174
+ if (window.GOVUK.analyticsGa4) {
175
+ if (window.GOVUK.analyticsGa4.Ga4FinderTracker) {
176
+ window.GOVUK.analyticsGa4.Ga4FinderTracker.addFilterButtonTracking(button, button.innerHTML)
177
+ }
178
+ }
179
+ })
180
+ }
181
+
182
+ OptionSelect.prototype.attachCheckedCounter = function attachCheckedCounter (checkedString) {
183
+ var element = document.createElement('div')
184
+ element.setAttribute('class', 'gem-c-option-select__selected-counter js-selected-counter')
185
+ element.innerHTML = checkedString
186
+ this.$optionSelect.querySelector('.js-container-button').insertAdjacentElement('afterend', element)
187
+ }
188
+
189
+ OptionSelect.prototype.updateCheckedCount = function updateCheckedCount () {
190
+ var checkedString = this.checkedString()
191
+ var checkedStringElement = this.$optionSelect.querySelector('.js-selected-counter')
192
+
193
+ if (checkedString) {
194
+ if (checkedStringElement === null) {
195
+ this.attachCheckedCounter(checkedString)
196
+ } else {
197
+ checkedStringElement.textContent = checkedString
198
+ }
199
+ } else if (checkedStringElement) {
200
+ checkedStringElement.parentNode.removeChild(checkedStringElement)
201
+ }
202
+ }
203
+
204
+ OptionSelect.prototype.checkedString = function checkedString () {
205
+ this.getAllCheckedCheckboxes()
206
+ var count = this.checkedCheckboxes.length
207
+ var checkedString = false
208
+ if (count > 0) {
209
+ checkedString = count + ' selected'
210
+ }
211
+
212
+ return checkedString
213
+ }
214
+
215
+ OptionSelect.prototype.toggleOptionSelect = function toggleOptionSelect (e) {
216
+ if (this.isClosed()) {
217
+ this.open()
218
+ } else {
219
+ this.close()
220
+ }
221
+ e.preventDefault()
222
+ }
223
+
224
+ OptionSelect.prototype.open = function open () {
225
+ if (this.isClosed()) {
226
+ this.$optionSelect.querySelector('.js-container-button').setAttribute('aria-expanded', true)
227
+ this.$optionSelect.classList.remove('js-closed')
228
+ this.$optionSelect.classList.add('js-opened')
229
+ if (!this.$optionsContainer.style.height) {
230
+ this.setupHeight()
231
+ }
232
+ }
233
+ }
234
+
235
+ OptionSelect.prototype.close = function close () {
236
+ this.$optionSelect.classList.remove('js-opened')
237
+ this.$optionSelect.classList.add('js-closed')
238
+ this.$optionSelect.querySelector('.js-container-button').setAttribute('aria-expanded', false)
239
+ }
240
+
241
+ OptionSelect.prototype.isClosed = function isClosed () {
242
+ return this.$optionSelect.classList.contains('js-closed')
243
+ }
244
+
245
+ OptionSelect.prototype.setContainerHeight = function setContainerHeight (height) {
246
+ this.$optionsContainer.style.height = height + 'px'
247
+ }
248
+
249
+ OptionSelect.prototype.isCheckboxVisible = function isCheckboxVisible (option) {
250
+ var initialOptionContainerHeight = this.$optionsContainer.clientHeight
251
+ var optionListOffsetTop = this.$optionList.getBoundingClientRect().top
252
+ var distanceFromTopOfContainer = option.getBoundingClientRect().top - optionListOffsetTop
253
+ return distanceFromTopOfContainer < initialOptionContainerHeight
254
+ }
255
+
256
+ OptionSelect.prototype.getVisibleCheckboxes = function getVisibleCheckboxes () {
257
+ var visibleCheckboxes = []
258
+ for (var i = 0; i < this.$options.length; i++) {
259
+ if (this.isCheckboxVisible(this.$options[i])) {
260
+ visibleCheckboxes.push(this.$options[i])
261
+ }
262
+ }
263
+
264
+ // add an extra checkbox, if the label of the first is too long it collapses onto itself
265
+ if (this.$options[visibleCheckboxes.length]) {
266
+ visibleCheckboxes.push(this.$options[visibleCheckboxes.length])
267
+ }
268
+ return visibleCheckboxes
269
+ }
270
+
271
+ OptionSelect.prototype.isFacetsContainerHidden = function isFacetsContainerHidden () {
272
+ var facetsContent = this.$optionSelect.parentElement
273
+ var isFacetsContentHidden = false
274
+ // check whether this is hidden by progressive disclosure,
275
+ // because height calculations won't work
276
+ // would use offsetParent === null but for IE10+
277
+ if (facetsContent) {
278
+ isFacetsContentHidden = !(facetsContent.offsetWidth || facetsContent.offsetHeight || facetsContent.getClientRects().length)
279
+ }
280
+
281
+ return isFacetsContentHidden
282
+ }
283
+
284
+ OptionSelect.prototype.setupHeight = function setupHeight () {
285
+ var initialOptionContainerHeight = this.$optionsContainer.clientHeight
286
+ var height = this.$optionList.offsetHeight
287
+
288
+ var isFacetsContainerHidden = this.isFacetsContainerHidden()
289
+
290
+ if (isFacetsContainerHidden) {
291
+ initialOptionContainerHeight = 200
292
+ height = 200
293
+ }
294
+
295
+ // Resize if the list is only slightly bigger than its container
296
+ // If isFacetsContainerHidden is true, then 200 < 250
297
+ // And the container height is always set to 201px
298
+ if (height < initialOptionContainerHeight + 50) {
299
+ this.setContainerHeight(height + 1)
300
+ return
301
+ }
302
+
303
+ // Resize to cut last item cleanly in half
304
+ var visibleCheckboxes = this.getVisibleCheckboxes()
305
+
306
+ var lastVisibleCheckbox = visibleCheckboxes[visibleCheckboxes.length - 1]
307
+ var position = lastVisibleCheckbox.parentNode.offsetTop // parent element is relative
308
+ this.setContainerHeight(position + (lastVisibleCheckbox.clientHeight / 1.5))
309
+ }
310
+
311
+ Modules.OptionSelect = OptionSelect
312
+ })(window.GOVUK.Modules)
@@ -57,6 +57,7 @@ $govuk-new-link-styles: true;
57
57
  @import "components/metadata";
58
58
  @import "components/modal-dialogue";
59
59
  @import "components/notice";
60
+ @import "components/option-select";
60
61
  @import "components/organisation-logo";
61
62
  @import "components/panel";
62
63
  @import "components/phase-banner";
@@ -41,6 +41,13 @@
41
41
  }
42
42
  }
43
43
 
44
+ @include govuk-media-query($from: tablet) {
45
+ .gem-c-image-card.gem-c-image-card--two-thirds {
46
+ margin: 0 (- govuk-spacing(3)) govuk-spacing(6) (- govuk-spacing(3));
47
+ display: block;
48
+ }
49
+ }
50
+
44
51
  @include govuk-media-query($from: mobile, $until: tablet) {
45
52
  .gem-c-image-card {
46
53
  margin: 0 (- govuk-spacing(3)) govuk-spacing(6) (- govuk-spacing(3));
@@ -53,6 +60,25 @@
53
60
  .gem-c-image-card__text-wrapper {
54
61
  @include govuk-grid-column($width: one-half, $at: mobile);
55
62
  }
63
+
64
+ .gem-c-image-card__image-wrapper.gem-c-image-card__image-wrapper--one-third {
65
+ @include govuk-grid-column($width: one-third, $float: right, $at: mobile);
66
+ }
67
+
68
+ .gem-c-image-card__text-wrapper.gem-c-image-card__text-wrapper--two-thirds {
69
+ @include govuk-grid-column($width: two-thirds, $float: right, $at: mobile);
70
+ }
71
+ }
72
+
73
+ @include govuk-media-query($from: tablet) {
74
+ .gem-c-image-card__image-wrapper.gem-c-image-card__image-wrapper--one-third {
75
+ @include govuk-grid-column($width: one-third, $float: right, $at: tablet);
76
+ }
77
+
78
+ .gem-c-image-card__text-wrapper.gem-c-image-card__text-wrapper--two-thirds {
79
+ @include govuk-grid-column($width: two-thirds, $float: right, $at: tablet);
80
+ padding-left: 0;
81
+ }
56
82
  }
57
83
 
58
84
  .gem-c-image-card__image {
@@ -0,0 +1,172 @@
1
+ @import "govuk_publishing_components/individual_component_support";
2
+
3
+ .gem-c-option-select {
4
+ position: relative;
5
+ padding: 0 0 govuk-spacing(2);
6
+ margin-bottom: govuk-spacing(2);
7
+ border-bottom: 1px solid $govuk-border-colour;
8
+
9
+ @include govuk-media-query($from: desktop) {
10
+ // Redefine scrollbars on desktop where these lists are scrollable
11
+ // so they are always visible in option lists
12
+ ::-webkit-scrollbar {
13
+ -webkit-appearance: none;
14
+ width: 7px;
15
+ }
16
+
17
+ ::-webkit-scrollbar-thumb {
18
+ border-radius: 4px;
19
+
20
+ background-color: rgba(0, 0, 0, .5);
21
+ -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, .87);
22
+ }
23
+ }
24
+
25
+ .gem-c-checkboxes {
26
+ margin: 0;
27
+ }
28
+ }
29
+
30
+ .gem-c-option-select__title {
31
+ @include govuk-font(19);
32
+ margin: 0;
33
+ }
34
+
35
+ .gem-c-option-select__button {
36
+ z-index: 1;
37
+ background: none;
38
+ border: 0;
39
+ text-align: left;
40
+ padding: 0;
41
+ cursor: pointer;
42
+ color: $govuk-link-colour;
43
+
44
+ &:hover {
45
+ text-decoration: underline;
46
+ text-underline-offset: .1em;
47
+ @include govuk-link-hover-decoration;
48
+ }
49
+
50
+ &::-moz-focus-inner {
51
+ border: 0;
52
+ }
53
+
54
+ &:focus {
55
+ outline: none;
56
+ text-decoration: none;
57
+ @include govuk-focused-text;
58
+ }
59
+
60
+ &[disabled] {
61
+ background-image: none;
62
+ color: inherit;
63
+ }
64
+
65
+ // Extend the touch area of the button to span the heading
66
+ &:after {
67
+ content: "";
68
+ position: absolute;
69
+ top: 0;
70
+ right: 0;
71
+ bottom: 0;
72
+ left: 0;
73
+ z-index: 2;
74
+ }
75
+ }
76
+
77
+ .gem-c-option-select__icon {
78
+ display: none;
79
+ position: absolute;
80
+ top: 0;
81
+ left: 9px;
82
+ width: 30px;
83
+ height: 40px;
84
+ fill: govuk-colour("black");
85
+ }
86
+
87
+ .gem-c-option-select__container {
88
+ position: relative;
89
+ max-height: 200px;
90
+ overflow-y: auto;
91
+ overflow-x: hidden;
92
+ background-color: govuk-colour("white");
93
+
94
+ &:focus {
95
+ outline: 0;
96
+ }
97
+ }
98
+
99
+ .gem-c-option-select__container--large {
100
+ max-height: 600px;
101
+ }
102
+
103
+ .gem-c-option-select__container-inner {
104
+ padding: govuk-spacing(1) 13px;
105
+ }
106
+
107
+ .gem-c-option-select__filter {
108
+ position: relative;
109
+ background: govuk-colour("white");
110
+ padding: 13px 13px govuk-spacing(2) 13px;
111
+ }
112
+
113
+ .gem-c-option-select__filter-input {
114
+ @include govuk-font(19);
115
+ background: image-url("option-select/input-icon.svg") govuk-colour("white") no-repeat -5px -3px;
116
+
117
+ @include govuk-media-query($from: tablet) {
118
+ @include govuk-font(16);
119
+ }
120
+ }
121
+
122
+ .gem-c-option-select__filter-input.govuk-input {
123
+ padding-left: 33px;
124
+ }
125
+
126
+ .js-enabled {
127
+ .gem-c-option-select__heading {
128
+ position: relative;
129
+ padding: 10px 8px 5px 43px;
130
+ margin: 0;
131
+ }
132
+
133
+ [aria-expanded="true"] ~ .gem-c-option-select__icon--up {
134
+ display: block;
135
+ }
136
+
137
+ [aria-expanded="false"] ~ .gem-c-option-select__icon--down {
138
+ display: block;
139
+ }
140
+
141
+ .gem-c-option-select__container {
142
+ height: 200px;
143
+ }
144
+
145
+ .gem-c-option-select__container--large {
146
+ height: 600px;
147
+ }
148
+
149
+ [data-closed-on-load="true"] .gem-c-option-select__container {
150
+ display: none;
151
+ }
152
+ }
153
+
154
+ .gem-c-option-select__selected-counter {
155
+ @include govuk-font($size: 14);
156
+ color: $govuk-text-colour;
157
+ margin-top: 3px;
158
+ }
159
+
160
+ .gem-c-option-select.js-closed {
161
+ .gem-c-option-select__filter,
162
+ .gem-c-option-select__container {
163
+ display: none;
164
+ }
165
+ }
166
+
167
+ .gem-c-option-select.js-opened {
168
+ .gem-c-option-select__filter,
169
+ .gem-c-option-select__container {
170
+ display: block;
171
+ }
172
+ }
@@ -7,11 +7,17 @@
7
7
  brand_helper = GovukPublishingComponents::AppHelpers::BrandHelper.new(brand)
8
8
  card_helper = GovukPublishingComponents::Presenters::ImageCardHelper.new(local_assigns, brand_helper)
9
9
  shared_helper = GovukPublishingComponents::Presenters::SharedHelper.new(local_assigns)
10
+
10
11
  classes = %w[gem-c-image-card]
12
+ classes << "govuk-grid-row" if card_helper.two_thirds
11
13
  classes << "gem-c-image-card--large" if card_helper.large
14
+ classes << "gem-c-image-card--two-thirds" if card_helper.two_thirds
12
15
  classes << "gem-c-image-card--no-image" unless (card_helper.image_src || card_helper.youtube_video_id)
13
16
  classes << brand_helper.brand_class if brand_helper.brand_class
14
17
 
18
+ text_wrapper_classes = %w[gem-c-image-card__text-wrapper]
19
+ text_wrapper_classes << "gem-c-image-card__text-wrapper--two-thirds" if card_helper.two_thirds
20
+
15
21
  font_size ||= card_helper.large ? 'm' : 's'
16
22
  heading_class = %w[gem-c-image-card__title]
17
23
  heading_class << shared_helper.get_heading_size(font_size, 's')
@@ -33,7 +39,7 @@
33
39
  %>
34
40
  <% if card_helper.href || card_helper.extra_details.any? %>
35
41
  <%= content_tag(:div, class: classes, "data-module": data_modules, lang: card_helper.lang) do %>
36
- <div class="gem-c-image-card__text-wrapper">
42
+ <%= content_tag(:div, class: text_wrapper_classes) do %>
37
43
  <div class="gem-c-image-card__header-and-context-wrapper">
38
44
  <% if card_helper.heading_text %>
39
45
  <%= content_tag(shared_helper.get_heading_level, class: heading_class) do %>
@@ -69,7 +75,7 @@
69
75
  <% if card_helper.metadata %>
70
76
  <p class="gem-c-image-card__metadata"><%= card_helper.metadata %></p>
71
77
  <% end %>
72
- </div>
78
+ <% end %>
73
79
  <%= card_helper.media %>
74
80
  <% end %>
75
81
  <% end %>