diff --git a/app/assets/javascripts/govuk_publishing_components/components/accordion.js b/app/assets/javascripts/govuk_publishing_components/components/accordion.js index f7a1fac093..aa69b86a70 100644 --- a/app/assets/javascripts/govuk_publishing_components/components/accordion.js +++ b/app/assets/javascripts/govuk_publishing_components/components/accordion.js @@ -1,5 +1,84 @@ +/* global nodeListForEach */ +// = require ../vendor/polyfills/common.js // This component relies on JavaScript from GOV.UK Frontend // = require govuk/components/accordion/accordion.js window.GOVUK = window.GOVUK || {} window.GOVUK.Modules = window.GOVUK.Modules || {} -window.GOVUK.Modules.Accordion = window.GOVUKFrontend +window.GOVUK.Modules.GovukAccordion = window.GOVUKFrontend.Accordion; + +(function (Modules) { + function GemAccordion ($module) { + this.$module = $module + this.sectionClass = 'govuk-accordion__section' + this.sectionHeaderClass = 'govuk-accordion__section-header' + + // Translated component content and language attribute pulled from data attributes + this.$module.actions = {} + this.$module.actions.locale = this.$module.getAttribute('data-locale') + this.$module.actions.showText = this.$module.getAttribute('data-show-text') + this.$module.actions.hideText = this.$module.getAttribute('data-hide-text') + this.$module.actions.showAllText = this.$module.getAttribute('data-show-all-text') + this.$module.actions.hideAllText = this.$module.getAttribute('data-hide-all-text') + this.$module.actions.thisSectionVisuallyHidden = this.$module.getAttribute('data-this-section-visually-hidden') + } + + GemAccordion.prototype.init = function () { + // Indicate that JavaScript has worked + this.$module.classList.add('gem-c-accordion--active') + + // Feature flag for anchor tag navigation used on manuals + if (this.$module.getAttribute('data-anchor-navigation') === 'true') { + this.openByAnchorOnLoad() + this.addEventListenersForAnchors() + } + } + + // Navigate to and open accordions with anchored content on page load if a hash is present + GemAccordion.prototype.openByAnchorOnLoad = function () { + var splitHash = window.location.hash.split('#')[1] + + if (window.location.hash && document.getElementById(splitHash)) { + this.openForAnchor(splitHash) + } + } + + // Add event listeners for links to open accordion sections when navigated to using said anchor links on the page + // Adding an event listener to all anchor link a tags in an accordion is risky but we circumvent this risk partially by only being a layer of accordion behaviour instead of any sort of change to link behaviour + GemAccordion.prototype.addEventListenersForAnchors = function () { + var links = this.$module.querySelectorAll('.' + this.sectionInnerContent + ' a[href*="#"]') + nodeListForEach(links, function (link) { + if (link.pathname === window.location.pathname) { + link.addEventListener('click', this.openForAnchor.bind(this, link.hash.split('#')[1])) + } + }.bind(this)) + } + + // Find the parent accordion section for the given id and open it + GemAccordion.prototype.openForAnchor = function (hash) { + var target = document.getElementById(hash) + var $section = this.getContainingSection(target) + var $header = $section.querySelector('.' + this.sectionHeaderClass) + + // Pass the selected section to "Accordion.prototype.onSectionToggle" to open + $header.click() + } + + // Loop through the given id's ancestors until the parent section class is found + GemAccordion.prototype.getContainingSection = function (target) { + while (!target.classList.contains(this.sectionClass)) { + target = target.parentElement + } + return target + } + + GemAccordion.prototype.filterLocale = function (key) { + if (this.$module.actions.locale && this.$module.actions.locale.indexOf('{') !== -1) { + var locales = JSON.parse(this.$module.actions.locale) + return locales[key] + } else if (this.$module.actions.locale) { + return this.$module.actions.locale + } + } + + Modules.GemAccordion = GemAccordion +})(window.GOVUK.Modules) diff --git a/app/views/govuk_publishing_components/components/_accordion.html.erb b/app/views/govuk_publishing_components/components/_accordion.html.erb index faaa3e57ca..1dfa109052 100644 --- a/app/views/govuk_publishing_components/components/_accordion.html.erb +++ b/app/views/govuk_publishing_components/components/_accordion.html.erb @@ -8,7 +8,7 @@ anchor_navigation ||= false accordion_classes = %w(gem-c-accordion govuk-accordion) - accordion_classes << 'govuk-accordion--condensed' if condensed + accordion_classes << 'gem-c-accordion--condensed' if condensed accordion_classes << (shared_helper.get_margin_bottom) translations = { @@ -22,7 +22,24 @@ locales = {} data_attributes ||= {} - data_attributes[:module] = 'govuk-accordion' + data_attributes[:module] = 'govuk-accordion gem-accordion' + data_attributes[:anchor_navigation] = anchor_navigation + + translations.each do |key, translation| + locales[key] = shared_helper.t_locale(translation) + data_attributes[key] = t(translation) + end + + unique_locales = locales.values.uniq + + if unique_locales.length > 1 + data_attributes[:locale] = locales + else + if unique_locales[0] != I18n.locale + data_attributes[:locale] = unique_locales[0] + end + end + %> <% if items.any? %> <%= tag.div(class: accordion_classes, id: id, data: data_attributes) do %> @@ -46,8 +63,16 @@ <%= content_tag( shared_helper.get_heading_level, - content_tag('span', item[:heading][:text], class: "govuk-accordion__section-button", id: "#{id}-heading-#{index}", data: item[:data_attributes]), - class: 'govuk-accordion__section-heading' + content_tag( + 'span', + item[:heading][:text], + class: "govuk-accordion__section-button", + id: "#{id}-heading-#{index}" + + ), + class: 'govuk-accordion__section-heading', + id: item[:heading][:id], + data: item[:data_attributes] ) %> <%= tag.div(item[:summary][:text], id: "#{id}-summary-#{index}", class: summary_classes) if item[:summary].present? %> diff --git a/app/views/govuk_publishing_components/components/docs/accordion.yml b/app/views/govuk_publishing_components/components/docs/accordion.yml index bb44607e55..3f506faa90 100644 --- a/app/views/govuk_publishing_components/components/docs/accordion.yml +++ b/app/views/govuk_publishing_components/components/docs/accordion.yml @@ -309,98 +309,3 @@ examples: text: How people read content: html:

This is the content for How people read.

- condensed_layout: - description: | - This layout is for when a smaller accordion is required. Since smaller screens trigger a single column layout, this modifier only makes the accordion smaller when viewed on large screens. - data: - condensed: true - items: - - heading: - text: Understanding agile project management - content: - html: - '' - - heading: - text: Working with agile methods - summary: - text: Workspaces, tools and techniques, user stories, planning. - content: - html: - '' - - heading: - text: Governing agile services - content: - html: - '' - - heading: - text: Phases of an agile project - content: - html: - '' diff --git a/spec/components/accordion_spec.rb b/spec/components/accordion_spec.rb index 99e6b670fa..4298d79441 100644 --- a/spec/components/accordion_spec.rb +++ b/spec/components/accordion_spec.rb @@ -189,7 +189,7 @@ def component_name ], } render_component(test_data) - assert_select "[data-module='govuk-accordion']", count: 1 + assert_select "[data-module='govuk-accordion gem-c-accordion']", count: 1 end it '`data-module="govuk-accordion"` attribute is present when custom data attributes given' do @@ -216,7 +216,7 @@ def component_name ], } render_component(test_data) - assert_select "[data-module='govuk-accordion']", count: 1 + assert_select "[data-module='govuk-accordion gem-c-accordion']", count: 1 assert_select "[data-gtm]", count: 2 assert_select "[data-gtm='this-is-gtm']", count: 1 assert_select "[data-gtm='this-is-a-second-gtm']", count: 1 @@ -248,8 +248,7 @@ def component_name it "adds id to heading when attribute passed" do test_data = { - id: "condensed-layout", - condensed: true, + anchor_navigation: true, items: [ { heading: { text: "Heading 1", id: "heading-with-id" }, @@ -264,25 +263,9 @@ def component_name ], } render_component(test_data) - assert_select ".gem-c-accordion__section-heading", count: 2 - assert_select ".gem-c-accordion__section-heading#heading-with-id", count: 1 - assert_select ".gem-c-accordion__section-heading:not([id])", count: 1 - end - - it "condensed class added correctly" do - test_data = { - id: "condensed-layout", - condensed: true, - items: [ - { - heading: { text: "Heading 1" }, - summary: { text: "Summary 1." }, - content: { html: "

Content 1.

" }, - }, - ], - } - render_component(test_data) - assert_select ".govuk-accordion.govuk-accordion--condensed", count: 1 + assert_select ".govuk-accordion__section-heading", count: 2 + assert_select ".govuk-accordion__section-heading#heading-with-id", count: 1 + assert_select ".govuk-accordion__section-heading:not([id])", count: 1 end it "loop index starts at one, not zero (thanks Nunjucks.)" do