diff --git a/CHANGELOG.md b/CHANGELOG.md index 099bb3f2c9..b2f77ee356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ useful summary for people upgrading their application, not a replication of the commit log. +## Unreleased + +* [BREAKING] Data attributes for option select button ([PR #3750](https://github.com/alphagov/govuk_publishing_components/pull/3750)) + ## 36.1.0 * Add heading option to file input component ([PR #3755](https://github.com/alphagov/govuk_publishing_components/pull/3755)) diff --git a/app/assets/javascripts/govuk_publishing_components/components/option-select.js b/app/assets/javascripts/govuk_publishing_components/components/option-select.js index d05c991ced..9e54e62c51 100644 --- a/app/assets/javascripts/govuk_publishing_components/components/option-select.js +++ b/app/assets/javascripts/govuk_publishing_components/components/option-select.js @@ -166,17 +166,24 @@ window.GOVUK.Modules = window.GOVUK.Modules || {}; button.setAttribute('aria-expanded', true) button.setAttribute('id', containerHead.getAttribute('id')) button.setAttribute('aria-controls', this.$optionsContainer.getAttribute('id')) - button.innerHTML = jsContainerHeadHTML - containerHead.parentNode.replaceChild(button, containerHead) - // GA4 Accordion tracking. Relies on the ga4-finder-tracker setting the index first, so we wrap this in a custom event. - window.addEventListener('ga4-filter-indexes-added', function () { - if (window.GOVUK.analyticsGa4) { - if (window.GOVUK.analyticsGa4.Ga4FinderTracker) { - window.GOVUK.analyticsGa4.Ga4FinderTracker.addFilterButtonTracking(button, button.innerHTML) + var buttonAttributes = this.$optionSelect.getAttribute('data-button-data-attributes') + if (buttonAttributes) { + try { + buttonAttributes = JSON.parse(buttonAttributes) + for (var rawKey in buttonAttributes) { + var key = rawKey.replace(/_/i, '-').toLowerCase() + var rawValue = buttonAttributes[rawKey] + var value = typeof rawValue === 'object' ? JSON.stringify(rawValue) : rawValue + button.setAttribute('data-' + key, value) } + } catch (e) { + console.error('Error with option select button data attributes, invalid JSON passed' + e.message, window.location) } - }) + } + + button.innerHTML = jsContainerHeadHTML + containerHead.parentNode.replaceChild(button, containerHead) } OptionSelect.prototype.attachCheckedCounter = function attachCheckedCounter (checkedString) { diff --git a/app/views/govuk_publishing_components/components/_option_select.html.erb b/app/views/govuk_publishing_components/components/_option_select.html.erb index a3e93e69a5..37e4e9da39 100644 --- a/app/views/govuk_publishing_components/components/_option_select.html.erb +++ b/app/views/govuk_publishing_components/components/_option_select.html.erb @@ -37,6 +37,7 @@ helper.add_data_attribute({ "closed-on-load-mobile": "true" }) if local_assigns.include?(:closed_on_load_mobile) && closed_on_load_mobile helper.add_data_attribute({ "input-aria-controls": aria_controls_id }) if local_assigns.include?(:aria_controls_id) helper.add_data_attribute({ "filter-element": filter_element }) if show_filter + helper.add_data_attribute({ "button-data-attributes": button_data_attributes }) if local_assigns.include?(:button_data_attributes) options_container_classes = %w[gem-c-option-select__container js-options-container] options_container_classes << "gem-c-option-select__container--large" if large diff --git a/app/views/govuk_publishing_components/components/docs/option_select.yml b/app/views/govuk_publishing_components/components/docs/option_select.yml index c1a6edf40f..89f7dbe025 100644 --- a/app/views/govuk_publishing_components/components/docs/option_select.yml +++ b/app/views/govuk_publishing_components/components/docs/option_select.yml @@ -156,6 +156,24 @@ examples: track_label: "high_heels" track_options: dimension28: 1 + with_button_data_attributes: + description: Allows data attributes to be passed to the component to be added to the expand/collapse button. The attributes are written to the parent element then read by the JavaScript and applied to the button. This is used for tracking purposes. + data: + key: market_sector + title: Market sector + options_container_id: list_of_sectors + button_data_attributes: + ga4_expandable: "" + ga4_event: + event_name: "select_content" + type: "finder" + options: + - value: aerospace + label: Aerospace + id: aerospace + - value: agriculture-environment-and-natural-resources + label: Agriculture, environment and natural resources + id: agriculture-environment-and-natural-resources with_filter: description: Adds a filter to allow users to narrow the checkboxes down. Checkboxes will only show if they match what the user has typed, or if they are already checked. The filter is case insensitive and strips out punctuation characters and duplicate whitespace, and sees '&' and 'and' as the same, to make filtering easier. data: diff --git a/spec/components/option_select_spec.rb b/spec/components/option_select_spec.rb index 49c3883fc5..c1cac239b7 100644 --- a/spec/components/option_select_spec.rb +++ b/spec/components/option_select_spec.rb @@ -117,6 +117,20 @@ def option_select_with_tracking_arguments expect(rendered).to have_no_selector(".gem-c-option-select__count") end + it "accepts button data attributes" do + options = option_select_arguments + button_attrs = { + ga4_expandable: "true", + ga4_event: { + event_name: "select_content", + type: "finder", + }, + } + options[:button_data_attributes] = button_attrs + render_component(options) + expect(rendered).to have_selector(".gem-c-option-select[data-button-data-attributes='#{button_attrs.to_json}']") + end + def expect_label_and_checked_checkbox(label, id, value) expect_label_and_checkbox(label, id, value, checked: true) end diff --git a/spec/javascripts/components/option-select-spec.js b/spec/javascripts/components/option-select-spec.js index 6d3b6b98d0..fec08daa06 100644 --- a/spec/javascripts/components/option-select-spec.js +++ b/spec/javascripts/components/option-select-spec.js @@ -152,6 +152,32 @@ describe('An option select component', function () { expect($($element).find('button')).toBeDefined() }) + + it('accepts data attributes to be applied to the button element', function () { + $element = document.createElement('div') + $element.innerHTML = html + var buttonAttrs = { + ga4_expandable: '', + ga4_event: { + event_name: 'select_content', + type: 'finder' + } + } + $element.querySelector('.gem-c-option-select').setAttribute('data-button-data-attributes', JSON.stringify(buttonAttrs)) + + new GOVUK.Modules.OptionSelect($element.querySelector('.gem-c-option-select')).init() + expect($($element).find('.gem-c-option-select__button').attr('data-ga4-expandable')).toBe('') + expect($($element).find('.gem-c-option-select__button').attr('data-ga4-event')).toBe(JSON.stringify(buttonAttrs.ga4_event)) + }) + + it('does not error if invalid data attributes are passed for the button element', function () { + $element = document.createElement('div') + $element.innerHTML = html + $element.querySelector('.gem-c-option-select').setAttribute('data-button-data-attributes', 'not JSON') + + new GOVUK.Modules.OptionSelect($element.querySelector('.gem-c-option-select')).init() + expect($($element).find('.gem-c-option-select__button').attr('data-ga4-expandable')).toBe(undefined) + }) }) describe('toggleOptionSelect', function () {