Skip to content

Commit

Permalink
Present hero images inside landing page blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
richardTowers committed Nov 8, 2024
1 parent c354b6d commit e5be42e
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 3 deletions.
73 changes: 70 additions & 3 deletions app/presenters/publishing_api/landing_page_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class LandingPagePresenter

attr_reader :update_type

IMAGE_PATTERN = /^\[Image:\s*(.*?)\s*\]/

def initialize(item, update_type: nil)
@item = item
@update_type = update_type || default_update_type(item)
Expand Down Expand Up @@ -50,10 +52,75 @@ def details
body = YAML.load(item.body, permitted_classes: [Date])
.merge(PayloadBuilder::Attachments.for(item))
extends_slug = body.delete("extends")
return body unless extends_slug
if extends_slug
extends = Document.find_by(slug: extends_slug).latest_edition
extends_body = YAML.load(extends.body, permitted_classes: [Date])
body.reverse_merge!(extends_body)
end

recursively_expand_images(body.deep_symbolize_keys)
end

def recursively_expand_images(input)
case input
in { type: "hero", image: { sources: { desktop:, tablet:, mobile: } }, **rest }
{
type: "hero",
image: present_hero_image(desktop, tablet, mobile),
**recursively_expand_images(rest),
}
in Hash => h
h.transform_values { recursively_expand_images(_1) }
in Array => a
a.map { recursively_expand_images(_1) }
else
input
end
end

def present_hero_image(desktop, tablet, mobile)
images = find_images(desktop, tablet, mobile)
return { errors: ["Some image expressions weren't correctly formatted, or images could not be found"] } if images.any?(&:nil?)
return { errors: ["Some image variants hadn't finished uploading"] } unless images.map(&:image_data).all?(&:all_asset_variants_uploaded?)

extends = Document.find_by(slug: extends_slug).latest_edition
YAML.load(extends.body, permitted_classes: [Date]).merge(body)
desktop_image, tablet_image, mobile_image = images

{
alt: present_alt_text(images),
sources: {
desktop: desktop_image.url(:hero_desktop_1x),
desktop_2x: desktop_image.url(:hero_desktop_2x),
tablet: tablet_image.url(:hero_tablet_1x),
tablet_2x: tablet_image.url(:hero_tablet_2x),
mobile: mobile_image.url(:hero_mobile_1x),
mobile_2x: mobile_image.url(:hero_mobile_2x),
},
}
end

def find_images(*image_expressions)
image_expressions.map do |image_expression|
match = IMAGE_PATTERN.match(image_expression)
if match.nil?
nil
else
image_id = match.captures.first
item.images.find { _1.filename == image_id }
end
end
end

def present_alt_text(images)
unique_alt_text = images.map(&:alt_text).compact.uniq
case unique_alt_text
in []
nil
in [alt_text]
alt_text
in Array
warn("Different images had different alt text, using the first option")
unique_alt_text.first
end
end
end
end
20 changes: 20 additions & 0 deletions test/factories/image_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@
end
end

factory :hero_image_data, class: ImageData do
file { image_fixture_file }

image_kind { "hero_desktop" }

after(:build) do |image_data|
image_data.assets << build(:asset, asset_manager_id: "asset_manager_id_original", variant: Asset.variants[:original], filename: image_data.filename)
variants = %W[#{image_data.image_kind}_2x #{image_data.image_kind}_1x]
variants.each do |variant|
image_data.assets << build(:asset, asset_manager_id: "asset_manager_id_#{variant}", variant: Asset.variants[variant], filename: "#{variant}_#{image_data.filename}")
end

# Defining this method is a bit of a hack, but with FactoryBot created model,
# the file_url method just returns nil, which makes it less useful for testing
def image_data.file_url(variant)
"http://asset-manager/#{assets.find_by(variant:).asset_manager_id}"
end
end
end

factory :image_data, parent: :generic_image_data, traits: [:jpg]
factory :image_data_for_svg, parent: :generic_image_data, traits: [:svg]
factory :image_data_with_no_assets, parent: :generic_image_data
Expand Down
Binary file added test/fixtures/hero_image_desktop_2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/hero_image_mobile_2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/hero_image_tablet_2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 128 additions & 0 deletions test/unit/app/presenters/publishing_api/landing_page_presenter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,132 @@ class PublishingApi::LandingPagePresenterTest < ActiveSupport::TestCase

assert_equal expected_details, presented_content[:details].deep_stringify_keys
end

test "it recursively expands images in the body" do
body = <<~YAML
blocks:
- type: hero
image:
sources:
desktop: "[Image: hero_image_desktop_2x.png]"
tablet: "[Image: hero_image_tablet_2x.png]"
mobile: "[Image: hero_image_mobile_2x.png]"
hero_content:
blocks:
- type: govspeak
content: "some content"
- type: grid_container
blocks:
- type: hero
image:
sources:
desktop: "[Image: hero_image_desktop_2x.png]"
tablet: "[Image: hero_image_tablet_2x.png]"
mobile: "[Image: hero_image_mobile_2x.png]"
hero_content:
blocks:
- type: govspeak
content: "some content"
YAML

landing_page = create(
:landing_page,
document: create(:document, id: 12_346, slug: "/landing-page/with-images"),
body:,
title: "Landing Page title",
summary: "Landing Page summary",
first_published_at: @first_published_at = Time.zone.now,
updated_at: 1.year.ago,
images: [
build(:image, image_data: build(:hero_image_data, image_kind: "hero_desktop", file: upload_fixture("hero_image_desktop_2x.png", "image/png"))),
build(:image, image_data: build(:hero_image_data, image_kind: "hero_tablet", file: upload_fixture("hero_image_tablet_2x.png", "image/png"))),
build(:image, image_data: build(:hero_image_data, image_kind: "hero_mobile", file: upload_fixture("hero_image_mobile_2x.png", "image/png"))),
],
)

presented_landing_page = PublishingApi::LandingPagePresenter.new(landing_page)
presented_content = I18n.with_locale("en") { presented_landing_page.content }

assert_pattern do
presented_content[:details].deep_symbolize_keys => {
blocks: [
{
type: "hero",
image: {
sources: {
desktop_2x: "http://asset-manager/asset_manager_id_hero_desktop_2x",
desktop: "http://asset-manager/asset_manager_id_hero_desktop_1x",
tablet_2x: "http://asset-manager/asset_manager_id_hero_tablet_2x",
tablet: "http://asset-manager/asset_manager_id_hero_tablet_1x",
mobile_2x: "http://asset-manager/asset_manager_id_hero_mobile_2x",
mobile: "http://asset-manager/asset_manager_id_hero_mobile_1x",
}
},
hero_content: {
blocks: [ { type: "govspeak", content: String } ]
}
},
{
type: "grid_container",
blocks: [{
type: "hero",
image: {
sources: {
desktop_2x: "http://asset-manager/asset_manager_id_hero_desktop_2x",
desktop: "http://asset-manager/asset_manager_id_hero_desktop_1x",
tablet_2x: "http://asset-manager/asset_manager_id_hero_tablet_2x",
tablet: "http://asset-manager/asset_manager_id_hero_tablet_1x",
mobile_2x: "http://asset-manager/asset_manager_id_hero_mobile_2x",
mobile: "http://asset-manager/asset_manager_id_hero_mobile_1x",
}
},
hero_content: {
blocks: [ { type: "govspeak", content: String } ]
}
}],
},
]}
end
end

test "it presents errors if files are not found" do
body = <<~YAML
blocks:
- type: hero
image:
sources:
desktop: "[Image: non-existent-file.jpg]"
tablet: "[Image: non-existent-file.jpg]"
mobile: "[Image: non-existent-file.jpg]"
YAML

landing_page = create(
:landing_page,
document: create(:document, id: 12_346, slug: "/landing-page/with-images"),
body:,
title: "Landing Page title",
summary: "Landing Page summary",
first_published_at: @first_published_at = Time.zone.now,
updated_at: 1.year.ago,
images: [],
)

presented_landing_page = PublishingApi::LandingPagePresenter.new(landing_page)
presented_content = I18n.with_locale("en") { presented_landing_page.content }
details = presented_content[:details].deep_symbolize_keys

assert_pattern do
details =>
{
blocks: [
{
type: "hero",
image: {
errors: ["Some image expressions weren't correctly formatted, or images could not be found"],
}
},
],
}
end
end
end

0 comments on commit e5be42e

Please sign in to comment.