From 0128619ea1b75f57c7a3c3231411a7273e2dfcbb Mon Sep 17 00:00:00 2001 From: Mateusz Grotek Date: Thu, 26 Sep 2024 15:23:12 +0100 Subject: [PATCH] Use breadcrumbs based on organisation if no parent Use breadcrumbs based on ancestors, if the document has a linked parent. Otherwise use breadcrumbs based on the first normal (non-world) organisation the page is linked to. If there are no linked organisations use taxon based breadcrumbs, or if taxons are also not linked to, the default breadcrumbs pointing to the homepage. --- CHANGELOG.md | 4 + .../docs/contextual_breadcrumbs.yml | 3 +- lib/govuk_publishing_components.rb | 1 + .../presenters/breadcrumb_selector.rb | 5 ++ ...tent_breadcrumbs_based_on_organisations.rb | 36 ++++++++ .../presenters/contextual_navigation.rb | 12 +++ .../components/contextual_breadcrumbs_spec.rb | 16 ++++ .../presenters/breadcrumb_selector_spec.rb | 89 +++++++++++++++++++ ...breadcrumbs_based_on_organisations_spec.rb | 25 ++++++ .../presenters/contextual_navigation_spec.rb | 74 +++++++++++++++ 10 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 lib/govuk_publishing_components/presenters/content_breadcrumbs_based_on_organisations.rb create mode 100644 spec/lib/govuk_publishing_components/presenters/breadcrumb_selector_spec.rb create mode 100644 spec/lib/govuk_publishing_components/presenters/content_breadcrumbs_based_on_organisations_spec.rb create mode 100644 spec/lib/govuk_publishing_components/presenters/contextual_navigation_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index ac990c776b..ca4c5f0c8a 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 + +* Use the first organisation to generate breadcrumbs, if no parent linked ([PR #4270](https://github.com/alphagov/govuk_publishing_components/pull/4270)) + ## 44.7.0 * Contents list add margin_bottom option ([PR #4333](https://github.com/alphagov/govuk_publishing_components/pull/4333)) diff --git a/app/views/govuk_publishing_components/components/docs/contextual_breadcrumbs.yml b/app/views/govuk_publishing_components/components/docs/contextual_breadcrumbs.yml index 7045bc65ff..bc96261b1f 100644 --- a/app/views/govuk_publishing_components/components/docs/contextual_breadcrumbs.yml +++ b/app/views/govuk_publishing_components/components/docs/contextual_breadcrumbs.yml @@ -4,11 +4,12 @@ body: | This is a complex component that calls other components. For more accurate preview with real data, see the [contextual navigation preview][preview]. - There are 3 main variants of the component: + There are 4 main variants of the component: - Step by step, which uses the [step by step header][header] - Parent breadcrumb, which uses the `parent` link of the page with the [breadcrumbs component][breadcrumbs] - Taxon breadcrumb, which uses the `taxons` link of the page with the [breadcrumbs component][breadcrumbs] + - Organisation breadcrumb, which uses the `organisations` link of the page with the [breadcrumbs component][breadcrumbs] It must always used [together with the contextual sidebar][sidebar] and [footer]. diff --git a/lib/govuk_publishing_components.rb b/lib/govuk_publishing_components.rb index 2f0cb6c4b0..55e6ab342b 100644 --- a/lib/govuk_publishing_components.rb +++ b/lib/govuk_publishing_components.rb @@ -24,6 +24,7 @@ require "govuk_publishing_components/presenters/page_with_step_by_step_navigation" require "govuk_publishing_components/presenters/public_layout_helper" require "govuk_publishing_components/presenters/content_breadcrumbs_based_on_ancestors" +require "govuk_publishing_components/presenters/content_breadcrumbs_based_on_organisations" require "govuk_publishing_components/presenters/content_breadcrumbs_based_on_taxons" require "govuk_publishing_components/presenters/checkboxes_helper" require "govuk_publishing_components/presenters/select_helper" diff --git a/lib/govuk_publishing_components/presenters/breadcrumb_selector.rb b/lib/govuk_publishing_components/presenters/breadcrumb_selector.rb index e301df2e35..f4594034d4 100644 --- a/lib/govuk_publishing_components/presenters/breadcrumb_selector.rb +++ b/lib/govuk_publishing_components/presenters/breadcrumb_selector.rb @@ -55,6 +55,11 @@ def options(navigation) step_by_step: false, breadcrumbs: navigation.breadcrumbs, } + elsif navigation.content_is_a_corporate_information_page? && navigation.content_has_related_organisations? + { + step_by_step: false, + breadcrumbs: navigation.organisation_breadcrumbs, + } elsif navigation.use_taxon_breadcrumbs? { step_by_step: false, diff --git a/lib/govuk_publishing_components/presenters/content_breadcrumbs_based_on_organisations.rb b/lib/govuk_publishing_components/presenters/content_breadcrumbs_based_on_organisations.rb new file mode 100644 index 0000000000..a03f308400 --- /dev/null +++ b/lib/govuk_publishing_components/presenters/content_breadcrumbs_based_on_organisations.rb @@ -0,0 +1,36 @@ +module GovukPublishingComponents + module Presenters + # @private + class ContentBreadcrumbsBasedOnOrganisations + def self.call(content_item) + new(content_item).breadcrumbs + end + + def initialize(content_item) + @content_item = ContentItem.new(content_item) + end + + def breadcrumbs + [ + { title: "Home", url: "/" }, + { title: "Organisations", url: "/government/organisations" }, + *organisation_breadcrumbs_items, + ] + end + + private + + attr_reader :content_item + + def organisation_breadcrumbs_items + first_related_organisation = ContentItem.new(content_item.related_organisations.first) + return [] unless first_related_organisation.present? + + [{ + title: first_related_organisation.title, + url: first_related_organisation.base_path, + }] + end + end + end +end diff --git a/lib/govuk_publishing_components/presenters/contextual_navigation.rb b/lib/govuk_publishing_components/presenters/contextual_navigation.rb index 419d850480..04936877c2 100644 --- a/lib/govuk_publishing_components/presenters/contextual_navigation.rb +++ b/lib/govuk_publishing_components/presenters/contextual_navigation.rb @@ -33,6 +33,10 @@ def taxon_breadcrumbs @taxon_breadcrumbs ||= ContentBreadcrumbsBasedOnTaxons.call(content_item) end + def organisation_breadcrumbs + @organisation_breadcrumbs ||= ContentBreadcrumbsBasedOnOrganisations.call(content_item) + end + def breadcrumbs breadcrumbs_based_on_ancestors end @@ -72,6 +76,10 @@ def content_has_curated_related_items? content_item.dig("links", "ordered_related_items").present? && content_item.dig("links", "parent").present? end + def content_has_related_organisations? + ContentItem.new(content_item).related_organisations.present? + end + def content_is_tagged_to_a_live_taxon? content_item.dig("links", "taxons").to_a.any? { |taxon| taxon["phase"] == "live" } end @@ -80,6 +88,10 @@ def content_is_a_specialist_document? content_item["schema_name"] == "specialist_document" end + def content_is_a_corporate_information_page? + content_item["schema_name"] == "corporate_information_page" + end + def content_is_a_html_publication? content_item["document_type"] == "html_publication" end diff --git a/spec/components/contextual_breadcrumbs_spec.rb b/spec/components/contextual_breadcrumbs_spec.rb index 1fe0851c5f..28ffd825e0 100644 --- a/spec/components/contextual_breadcrumbs_spec.rb +++ b/spec/components/contextual_breadcrumbs_spec.rb @@ -97,6 +97,22 @@ def set_live_taxons(content_item) assert_select ".gem-c-breadcrumbs.gem-c-breadcrumbs--inverse" end + it "renders parent-based breadcrumbs if the content item is a corporate information page with a parent" do + content_item = example_document_for("corporate_information_page", "corporate_information_page_complaints") + render_component(content_item:) + assert_select "a", text: "Home" + assert_select "a", text: "Department of Health" + assert_select "a", text: "About us" + end + + it "renders organisation breadcrumbs if the content item is a corporate information page with no parent but with a linked organisation" do + content_item = example_document_for("corporate_information_page", "best-practice-about-page") + render_component(content_item:) + assert_select "a", text: "Home" + assert_select "a", text: "Organisations" + assert_select "a", text: "Intellectual Property Office" + end + it "renders no taxon breadcrumbs if they are not live" do content_item = example_document_for("guide", "guide") content_item = remove_mainstream_browse(content_item) diff --git a/spec/lib/govuk_publishing_components/presenters/breadcrumb_selector_spec.rb b/spec/lib/govuk_publishing_components/presenters/breadcrumb_selector_spec.rb new file mode 100644 index 0000000000..a75ca414c0 --- /dev/null +++ b/spec/lib/govuk_publishing_components/presenters/breadcrumb_selector_spec.rb @@ -0,0 +1,89 @@ +require "spec_helper" + +RSpec.describe GovukPublishingComponents::Presenters::BreadcrumbSelector do + subject do + described_class.new( + content_item, + request, + prioritise_taxon_breadcrumbs, + disable_ga4, + ) + end + let(:example) { %w[guide guide] } + let(:content_item) { example_document_for(example.first, example.second) } + let(:path) { content_item["base_path"] } + let(:query_parameters) { {} } + let(:request) { instance_double("ActionDispatch::Request", path:, query_parameters:) } + let(:prioritise_taxon_breadcrumbs) { false } + let(:disable_ga4) { false } + + def example_document_for(schema_name, example_name) + GovukSchemas::Example.find(schema_name, example_name:) + end + + describe "#initialize" do + it "does not raise exception" do + expect { subject }.not_to raise_error + end + end + + describe "#breadcrumbs" do + context "when content item is corporate information page with parent" do + let(:example) { %w[corporate_information_page corporate_information_page_complaints] } + + it "returns breadcrumbs based on ancestors" do + expect(subject.breadcrumbs).to eq([ + { title: "Home", url: "/" }, + { title: "Department of Health", url: "/government/organisations/department-of-health" }, + { title: "About us", url: "/government/organisations/department-of-health/about" }, + ]) + end + end + + context "when content item is corporate information page without parent but with organisation" do + let(:example) { %w[corporate_information_page best-practice-about-page] } + + it "returns breadcrumbs based on organisation" do + expect(subject.breadcrumbs).to eq([ + { title: "Home", url: "/" }, + { title: "Organisations", url: "/government/organisations" }, + { title: "Intellectual Property Office", url: "/government/organisations/intellectual-property-office" }, + ]) + end + end + + context "when content item is corporate information page without parent, organisations but with taxon" do + let(:content_item) do + item = example_document_for("corporate_information_page", "best-practice-about-page") + item.dig("links", "organisations").clear + item["links"]["taxons"] = [{ + "base_path" => "/corporate-information", + "title" => "Corporate information", + "phase" => "live", + }] + item + end + + it "returns breadcrumbs based on taxons" do + expect(subject.breadcrumbs).to eq([ + { title: "Home", url: "/", is_page_parent: false }, + { title: "Corporate information", url: "/corporate-information", is_page_parent: true }, + ]) + end + end + + context "when content item is corporate information page without parent, organisations and taxons" do + let(:content_item) do + item = example_document_for("corporate_information_page", "best-practice-about-page") + item.dig("links", "organisations").clear + item + end + + it "returns default breadcrumbs" do + expect(subject.breadcrumbs).to eq([ + { title: "Home", url: "/" }, + ]) + end + end + end +end diff --git a/spec/lib/govuk_publishing_components/presenters/content_breadcrumbs_based_on_organisations_spec.rb b/spec/lib/govuk_publishing_components/presenters/content_breadcrumbs_based_on_organisations_spec.rb new file mode 100644 index 0000000000..71df0d944e --- /dev/null +++ b/spec/lib/govuk_publishing_components/presenters/content_breadcrumbs_based_on_organisations_spec.rb @@ -0,0 +1,25 @@ +require "spec_helper" + +RSpec.describe GovukPublishingComponents::Presenters::ContentBreadcrumbsBasedOnOrganisations do + subject { described_class.new(content_item) } + let(:content_item) { GovukSchemas::Example.find("document_collection", example_name: "document_collection") } + + describe "#initialize" do + it "does not raise exception" do + expect { subject }.not_to raise_error + end + end + + describe "#breadcrumbs" do + it "returns breadcrumbs based on organisation" do + expect(subject.breadcrumbs).to eq([ + { title: "Home", url: "/" }, + { title: "Organisations", url: "/government/organisations" }, + { + title: "Driver and Vehicle Standards Agency", + url: "/government/organisations/driver-and-vehicle-standards-agency", + }, + ]) + end + end +end diff --git a/spec/lib/govuk_publishing_components/presenters/contextual_navigation_spec.rb b/spec/lib/govuk_publishing_components/presenters/contextual_navigation_spec.rb new file mode 100644 index 0000000000..1a38fbc734 --- /dev/null +++ b/spec/lib/govuk_publishing_components/presenters/contextual_navigation_spec.rb @@ -0,0 +1,74 @@ +require "spec_helper" + +RSpec.describe GovukPublishingComponents::Presenters::ContextualNavigation do + subject { described_class.new(content_item, request) } + let(:example) { %w[guide guide] } + let(:content_item) { example_document_for(example.first, example.second) } + let(:path) { content_item["base_path"] } + let(:query_parameters) { {} } + let(:request) { instance_double("ActionDispatch::Request", path:, query_parameters:) } + + def example_document_for(schema_name, example_name) + GovukSchemas::Example.find(schema_name, example_name:) + end + + describe "#initialize" do + it "does not raise exception" do + expect { subject }.not_to raise_error + end + end + + describe "#organisation_breadcrumbs" do + it "calls ContentBreadcrumbsBasedOnOrganisation" do + called_class = GovukPublishingComponents::Presenters::ContentBreadcrumbsBasedOnOrganisations + expect(called_class).to receive(:call) + .with(content_item) + .and_return([]) + subject.organisation_breadcrumbs + end + end + + describe "#content_has_related_organisations?" do + context "when content item is linked to no organisations" do + let(:example) { %w[homepage homepage_with_popular_links_on_govuk] } + + it "returns false" do + expect(subject.content_has_related_organisations?).to be(false) + end + end + + context "when content item is only linked to world organisations" do + let(:example) { %w[worldwide_corporate_information_page worldwide_corporate_information_page] } + + it "returns false" do + expect(subject.content_has_related_organisations?).to be(false) + end + end + + context "when content item is linked to at least 1 normal organisation" do + let(:example) { %w[document_collection document_collection] } + + it "returns true" do + expect(subject.content_has_related_organisations?).to be(true) + end + end + end + + describe "#content_is_a_corporate_information_page?" do + context "when content item is corporate information page" do + let(:example) { %w[corporate_information_page corporate_information_page] } + + it "returns true" do + expect(subject.content_is_a_corporate_information_page?).to be(true) + end + end + + context "when content item is not corporate information page" do + let(:example) { %w[guide guide] } + + it "returns false" do + expect(subject.content_is_a_corporate_information_page?).to be(false) + end + end + end +end