Skip to content

Commit

Permalink
Merge pull request #325 from hmrc/fix-header-and-footer-links-when-ge…
Browse files Browse the repository at this point in the history
…nerating-relative-links

TRG-668: Support sites deployed on a path other than "/" when generating header and footer links.
  • Loading branch information
lfdebrux authored Jun 7, 2023
2 parents 4ae3ea6 + 7e16828 commit da78007
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 40 deletions.
2 changes: 1 addition & 1 deletion govuk_tech_docs.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Gem::Specification.new do |spec|

spec.add_dependency "autoprefixer-rails", "~> 10.2"
spec.add_dependency "chronic", "~> 0.10.2"
spec.add_dependency "haml", "< 6.0.0"
spec.add_dependency "middleman", "~> 4.0"
spec.add_dependency "middleman-autoprefixer", "~> 2.10.0"
spec.add_dependency "middleman-compass", ">= 4.0.0"
Expand All @@ -47,7 +48,6 @@ Gem::Specification.new do |spec|
spec.add_dependency "nokogiri"
spec.add_dependency "openapi3_parser", "~> 0.9.0"
spec.add_dependency "redcarpet", "~> 3.5.1"
spec.add_dependency "haml", "< 6.0.0"

spec.add_development_dependency "byebug"
spec.add_development_dependency "capybara", "~> 3.32"
Expand Down
50 changes: 40 additions & 10 deletions lib/govuk_tech_docs/path_helpers.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
require "uri"
module GovukTechDocs
module PathHelpers
def get_path_to_resource(config, resource, current_page)
if config[:relative_links]
resource_path_segments = resource.path.split("/").reject(&:empty?)[0..-2]
resource_file_name = resource.path.split("/")[-1]
# Some useful notes from https://www.rubydoc.info/github/middleman/middleman/Middleman/Sitemap/Resource#url-instance_method :
# 'resource.path' is "The source path of this resource (relative to the source directory, without template extensions)."
# 'resource.destination_path', which is: "The output path in the build directory for this resource."
# 'resource.url' is based on 'resource.destination_path', but is further tweaked to optionally strip the index file and prefixed with any :http_prefix.

path_to_site_root = path_to_site_root config, current_page.path
resource_path = path_to_site_root + resource_path_segments
.push(resource_file_name)
.join("/")
# Calculates the path to the sought resource, taking in to account whether the site has been configured
# to generate relative or absolute links.
# Identifies whether the sought resource is an internal or external target: External targets are returned untouched. Path calculation is performed for internal targets.
# Works for both "Middleman::Sitemap::Resource" resources and plain strings (which may be passed from the site configuration when generating header links).
#
# @param [Object] config
# @param [Object] resource
# @param [Object] current_page
def get_path_to_resource(config, resource, current_page)
if resource.is_a?(Middleman::Sitemap::Resource)
config[:relative_links] ? get_resource_path_relative_to_current_page(config, current_page.path, resource.path) : resource.url
elsif external_url?(resource)
resource
elsif config[:relative_links]
get_resource_path_relative_to_current_page(config, current_page.path, resource)
else
resource_path = resource.url
resource
end
resource_path
end

def path_to_site_root(config, page_path)
Expand All @@ -26,5 +37,24 @@ def path_to_site_root(config, page_path)
end
path_to_site_root
end

private

# Calculates the path to the sought resource, relative to the current page.
# @param [Object] config Middleman config.
# @param [String] current_page path of the current page, from the site root.
# @param [String] resource_path_from_site_root path of the sought resource, from the site root.
def get_resource_path_relative_to_current_page(config, current_page, resource_path_from_site_root)
path_segments = resource_path_from_site_root.split("/").reject(&:empty?)[0..-2]
path_file_name = resource_path_from_site_root.split("/")[-1]

path_to_site_root = path_to_site_root config, current_page
path_to_site_root + path_segments.push(path_file_name).join("/")
end

def external_url?(url)
uri = URI.parse(url)
uri.scheme || uri.to_s.split("/")[0]&.include?(".")
end
end
end
2 changes: 1 addition & 1 deletion lib/source/layouts/_footer.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<ul class="govuk-footer__inline-list">
<% config[:tech_docs][:footer_links].each do |title, path| %>
<li class="govuk-footer__inline-list-item">
<a class="govuk-footer__link" href="<%= url_for path %>"><%= title %></a>
<a class="govuk-footer__link" href="<%= get_path_to_resource config, path, current_page %>"><%= title %></a>
</li>
<% end %>
</ul>
Expand Down
4 changes: 2 additions & 2 deletions lib/source/layouts/_header.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="govuk-header__container govuk-header__container--full-width">
<div class="govuk-header__logo">
<% if config[:tech_docs][:service_link] %>
<a href="<%= url_for config[:tech_docs][:service_link] %>" class="govuk-header__link govuk-header__link--homepage">
<a href="<%= get_path_to_resource config, config[:tech_docs][:service_link], current_page %>" class="govuk-header__link govuk-header__link--homepage">
<% else %>
<span class="govuk-header__link govuk-header__link--homepage">
<% end %>
Expand Down Expand Up @@ -46,7 +46,7 @@
<ul id="navigation" class="govuk-header__navigation-list">
<% config[:tech_docs][:header_links].each do |title, path| %>
<li class="govuk-header__navigation-item<% if active_page(path) %> govuk-header__navigation-item--active<% end %>">
<a class="govuk-header__link" href="<%= url_for path %>"><%= title %></a>
<a class="govuk-header__link" href="<%= get_path_to_resource config, path, current_page %>"><%= title %></a>
</li>
<% end %>
</ul>
Expand Down
5 changes: 5 additions & 0 deletions spec/govuk_tech_docs/doubles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def create_resource_double(**kwargs)
resource = instance_double(Middleman::Sitemap::Resource, **kwargs)
allow(resource).to receive(:is_a?).with(Middleman::Sitemap::Resource).and_return(true)
resource
end
10 changes: 6 additions & 4 deletions spec/govuk_tech_docs/pages_spec.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
require "govuk_tech_docs/doubles"

RSpec.describe GovukTechDocs::Pages do
describe "#to_json" do
it "returns the pages as JSON when using absolute links" do
current_page = double(path: "/api/pages.json")
sitemap = double(resources: [
double(url: "/a.html", data: double(title: "A thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "0 days")),
double(url: "/b.html", data: double(title: "B thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "2 days")),
create_resource_double(url: "/a.html", data: double(title: "A thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "0 days")),
create_resource_double(url: "/b.html", data: double(title: "B thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "2 days")),
])

json = described_class.new(sitemap, {}, current_page).to_json
Expand All @@ -18,8 +20,8 @@
it "returns the pages as JSON when using relative links" do
current_page = double(path: "/api/pages.json")
sitemap = double(resources: [
double(url: "/a.html", path: "/a.html", data: double(title: "A thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "0 days")),
double(url: "/b/c.html", path: "/b/c.html", data: double(title: "B thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "2 days")),
create_resource_double(url: "/a.html", path: "/a.html", data: double(title: "A thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "0 days")),
create_resource_double(url: "/b/c.html", path: "/b/c.html", data: double(title: "B thing", owner_slack: "#2ndline", last_reviewed_on: Date.yesterday, review_in: "2 days")),
])

json = described_class.new(sitemap, { relative_links: true }, current_page).to_json
Expand Down
104 changes: 82 additions & 22 deletions spec/govuk_tech_docs/path_helpers_spec.rb
Original file line number Diff line number Diff line change
@@ -1,63 +1,123 @@
require "govuk_tech_docs/doubles"

RSpec.describe GovukTechDocs::PathHelpers do
include GovukTechDocs::PathHelpers

describe "#get_path_to_resource" do
it "calculates the path to a resource when using absolute links" do
context "calculate paths for internal resources" do
resource_url = "/documentation/introduction/index.html"

config = {}
resource = double("resource", url: resource_url)
it "when the site is configured to generate absolute links" do
config = {}

resource = create_resource_double(url: resource_url)
resource_path = get_path_to_resource(config, resource, nil)

expect(resource_path).to eql(resource_url)
end

it "when the site is configured to generate relative links" do
config = {
relative_links: true,
}

resource_path = get_path_to_resource(config, resource, nil)
expect(resource_path).to eql(resource_url)
current_page_path = "/documentation/introduction/section-one/index.html"
current_page = double("current_page", path: current_page_path)
resource = create_resource_double(url: resource_url, path: resource_url)
resource_path = get_path_to_resource(config, resource, current_page)

expect(resource_path).to eql("../../../documentation/introduction/index.html")
end
end

it "calculates the path to a resource when using relative links" do
context "calculate paths for internal page paths (which are represented as strings)" do
current_page_path = "/documentation/introduction/section-one/index.html"
url = "/documentation/introduction/index.html"

config = {
relative_links: true,
}
resource = double("resource", url: url, path: url)
current_page = double("current_page", path: current_page_path)
it "when the site is configured to generate absolute links" do
config = {}

url = "/documentation/introduction/index.html"
current_page = double("current_page", path: current_page_path)
resource_path = get_path_to_resource(config, url, current_page)

expect(resource_path).to eql(url)
end

it "when the site is configured to generate relative links" do
config = {
relative_links: true,
}

url = "/documentation/introduction/index.html"
current_page = double("current_page", path: current_page_path)
resource_path = get_path_to_resource(config, url, current_page)

expect(resource_path).to eql("../../../documentation/introduction/index.html")
end
end

context "calculate URLs for external URLs" do
external_urls = [
"https://www.example.com/some-page.html", # With scheme included.
"www.example.com/some-page.html", # With scheme omitted.
]
external_urls.each do |external_url|
current_page_path = "/documentation/introduction/section-one/index.html"

it "when the site is configured to generate absolute links" do
config = {}

resource_path = get_path_to_resource(config, resource, current_page)
expect(resource_path).to eql("../../../documentation/introduction/index.html")
current_page = double("current_page", path: current_page_path)
resource_path = get_path_to_resource(config, external_url, current_page)

expect(resource_path).to eql(external_url)
end

it "when the site is configured to generate relative links" do
config = {
relative_links: true,
}

current_page = double("current_page", path: current_page_path)
resource_path = get_path_to_resource(config, external_url, current_page)

expect(resource_path).to eql(external_url)
end
end
end
end

describe "#path_to_site_root" do
it "calculates the path from a page to the site root when using absolute links" do
page_path = "/documentation/introduction/index.html"

it "calculates the path from a page to the site root when the site is configured to generate absolute links" do
config = {
http_prefix: "/", # This is Middleman's default setting.
}

page_path = "/documentation/introduction/index.html"
path_to_site_root = path_to_site_root(config, page_path)

expect(path_to_site_root).to eql("/")
end

it "calculates the path from a page to the site root when a middleman http prefix" do
page_path = "/bananas/documentation/introduction/index.html"

config = {
http_prefix: "/bananas",
}

page_path = "/bananas/documentation/introduction/index.html"
path_to_site_root = path_to_site_root(config, page_path)

expect(path_to_site_root).to eql("/bananas/")
end

it "calculates the path from a page to the site root when using relative links" do
page_path = "/documentation/introduction/index.html"

it "calculates the path from a page to the site root when the site is configured to generate relative links" do
config = {
relative_links: true,
}

page_path = "/documentation/introduction/index.html"
path_to_site_root = path_to_site_root(config, page_path)

expect(path_to_site_root).to eql("../../")
end
end
Expand Down
4 changes: 4 additions & 0 deletions spec/table_of_contents/helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ def render(_layout)
def add_children(children)
@children.concat children
end

def is_a?(klass)
klass.to_s == "Middleman::Sitemap::Resource"
end
end,
)
end
Expand Down

0 comments on commit da78007

Please sign in to comment.