Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ $govuk-page-width: 1140px;
@import "views/content_block_manager_show";
@import "views/editions/preview";

@import "../../../engines/block_preview/app/assets/stylesheets/application";

.app-js-only {
display: none;
}
Expand Down
8 changes: 8 additions & 0 deletions app/public/lib/public/services.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ def self.publishing_api
@publishing_api ||= publishing_api_client_with_timeout(20)
end

def self.content_store
@content_store ||= GdsApi::ContentStore.new(Plek.find("content-store"))
end

def self.draft_content_store
@draft_content_store ||= GdsApi::ContentStore.new(Plek.find("draft-content-store"))
end

def self.publishing_api_client_with_timeout(timeout)
GdsApi::PublishingApi.new(
Plek.find("publishing-api"),
Expand Down
4 changes: 4 additions & 0 deletions config/features.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@
feature :ga4_form_tracking,
description: "Add GA4 form tracking to Content Block Manager",
default: ContentBlockManager.integration_or_staging? || !Rails.env.production?

feature :show_snippets,
description: "Show snippets in Content Block preview",
default: ContentBlockManager.integration_or_staging? || !Rails.env.production?
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "components/snippets";
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.result-wrapper {
padding: govuk-spacing(5);
background-color: govuk-colour("light-grey");
}

.result {
border: 1px solid govuk-colour("black");
margin-bottom: govuk-spacing(7);
padding: govuk-spacing(5);
color: govuk-colour("black");
background-color: govuk-colour("white");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<%= render "govuk_publishing_components/components/secondary_navigation", {
aria_label: "Preview navigation",
items: [
{
label: "Preview instances (#{snippet_count})",
current: current_tab != "preview",
href: url_for_tab("instances"),
data_attributes: {
gtm: "tab",
},
},
{
label: "Preview document",
current: current_tab == "preview",
href: url_for_tab("preview"),
data_attributes: {
gtm: "tab",
},
},
],
} %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class BlockPreview::TabsComponent < ViewComponent::Base
include BlockPreview::Engine.routes.url_helpers

def initialize(snippet_count:, current_tab:, block:, preview_content:)
@snippet_count = snippet_count
@current_tab = current_tab
@block = block
@preview_content = preview_content
end

private

attr_reader :snippet_count, :current_tab, :block, :preview_content

def url_for_tab(tab)
host_content_preview_path(
edition_id: block.id,
host_content_id: preview_content.content_id,
locale: preview_content.locale,
state: preview_content.state,
tab:,
)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ def show
locale: params[:locale],
state: params[:state].presence || "published",
)
if Flipflop.enabled?(:show_snippets)
@current_tab = params[:tab]
@snippets = BlockPreview::Snippet.for_content_id(
host_content_id,
state: params[:state].presence || "published",
block: @block,
locale: params[:locale],
)

if @current_tab == "preview"
render "show"
else
render "show_with_snippets"
end
else
render "show"
end
end

def form_handler
Expand Down
8 changes: 5 additions & 3 deletions engines/block_preview/app/model/block_preview/content_diff.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class BlockPreview::ContentDiff
def initialize(html, block)
@before = html.at_css('[data-module="govspeak"]')
@before = html
@block = block
end

Expand All @@ -12,8 +12,10 @@ def initialize(html, block)

def diff_fragment
@diff_fragment ||= begin
fragment = Nokogiri::HTML.fragment(Nokodiff.diff(before, after))
fragment.children.first.add_class("compare-editions")
fragment = Nokogiri::HTML.fragment(Nokodiff.diff(before.to_s, after.to_s))
wrapped = Nokogiri::HTML.fragment("<div class='compare-editions'></div>")
wrapped.at_css("div").add_child(fragment)
wrapped.to_html
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module BlockPreview
class PreviewContent
VALID_STATES = %w[published draft].freeze

attr_reader :state
attr_reader :state, :content_id, :block, :base_path, :locale

def initialize(content_id:, block:, base_path: nil, locale: "en", state: "published")
@content_id = content_id
Expand Down Expand Up @@ -33,8 +33,6 @@ def instances_count

private

attr_reader :content_id, :block, :base_path, :locale

def validated_state(state)
return state if VALID_STATES.include?(state)

Expand Down
6 changes: 3 additions & 3 deletions engines/block_preview/app/model/block_preview/preview_html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def add_auth_bypass_token_to_uri(uri)
end

def update_local_link_paths(nokogiri_html)
url = host_content_preview_path(edition_id: block.id, host_content_id: content_id, locale:, state:)
url = host_content_preview_path(edition_id: block.id, host_content_id: content_id, locale:, state:, tab: "preview")
nokogiri_html.css("a").each do |link|
next if link[:href].start_with?("//") || link[:href].start_with?("http")

Expand All @@ -103,7 +103,7 @@ def update_local_link_paths(nokogiri_html)
end

def update_local_form_actions(nokogiri_html, scheme, host)
url = host_content_preview_form_handler_path(edition_id: block.id, host_content_id: content_id, locale:)
url = host_content_preview_form_handler_path(edition_id: block.id, host_content_id: content_id, locale:, tab: "preview")
nokogiri_html.css("main form").each do |form|
form[:action] = "#{url}&url=#{scheme}://#{host}#{form[:action]}&method=#{form[:method]}"
form[:target] = "_parent"
Expand Down Expand Up @@ -143,7 +143,7 @@ def update_js_srcs(nokogiri_html)
def update_preview_with_diff(nokogiri_html)
nokogiri_html.at_css("[data-module=\"govspeak\"]")
.replace(
BlockPreview::ContentDiff.new(nokogiri_html, block).to_s,
BlockPreview::ContentDiff.new(nokogiri_html.at_css('[data-module="govspeak"]'), block).to_s,
)

nokogiri_html
Expand Down
109 changes: 109 additions & 0 deletions engines/block_preview/app/model/block_preview/snippet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
class BlockPreview::Snippet
class << self
def for_content_id(content_id, state:, block:, locale:)
diff = BlockPreview::ContentDiff.new(
html_snippet(content_id, state, locale),
block,
)
from_html(diff.to_s, block)
end

def from_html(html, block)
doc = Nokogiri::HTML(html)
shown_element_paths = []

doc.css(%([data-content-id="#{block.content_id}"])).map { |content_block|
snippet = new(doc, content_block)
next if shown_element_paths.include?(snippet.context_parent.path)

preview_elements = snippet.context_elements.drop_while do |element|
shown_element_paths.include?(element.path)
end

next if preview_elements.empty?

shown_element_paths.concat(preview_elements.map(&:path))

new(doc, content_block, preview_elements:)
}.compact
end

private

def html_snippet(content_id, state, locale)
publishing_api_response = Public::Services.publishing_api.get_content(content_id, locale:)
content_store = state == "published" ? Public::Services.content_store : Public::Services.draft_content_store
content_store_response = content_store.content_item(publishing_api_response["base_path"])
html = if %w[guide travel_advice].include?(content_store_response["document_type"])
content_store_response["details"]["parts"].map { |part| part["body"] }.join("\n")
else
content_store_response["details"]["body"]
end

Nokogiri::HTML.fragment(html)
end
end

def initialize(doc, block, preview_elements: nil)
@doc = doc
@block = block
@preview_elements = preview_elements
end

def to_html
context_elements.map(&:to_s).join("\n")
end

def context_parent_html
context_parent.to_s
end

def context_parent
get_parent
end

def context_elements
@preview_elements || default_preview_elements
end

private

attr_reader :doc, :block

def default_preview_elements
parent = context_parent

[
parent.previous_element&.previous_element,
parent.previous_element,
parent,
parent.next_element,
parent.next_element&.next_element,
].compact
end

def get_parent
parent = block.parent
list_item_ancestor = block.ancestors("li").first
table_cell_ancestor = block.ancestors("td").first

# If the block is within a list item or a table, we want to ensure
# the containing list/table is preserved, so we can show the surrounding
# HTML
if list_item_ancestor
parent = list_item_ancestor.ancestors("ul, ol").first || list_item_ancestor
elsif table_cell_ancestor
parent = table_cell_ancestor.ancestors("table").first || table_cell_ancestor
end

# If there is no previous element, walk up the tree until we find one.
# Stop before leaving element nodes to avoid calling `parent` on a document.
while parent.previous_element.nil?
break unless parent.respond_to?(:parent) && parent.parent&.element?

parent = parent.parent
end

parent
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@
<hr class="govuk-section-break govuk-!-margin-bottom-8 govuk-section-break--visible">
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
<% if Flipflop.enabled?(:show_snippets) %>
<%= render BlockPreview::TabsComponent.new(
current_tab: @current_tab,
snippet_count: @snippets.count,
block: @block,
preview_content: @preview_content,
) %>
<% end %>

<%= turbo_frame_tag "preview_frame_#{@block.id}" do %>
<%= render partial: "iframe" %>
<% end %>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<% content_for :page_title, "Preview content block in host document" %>
<% content_for :context, "Preview #{@block.block_type.humanize.downcase}" %>
<% content_for :title, @preview_content.title %>
<% content_for :title_margin_bottom, 1 %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds govuk-body govuk-!-margin-bottom-0">
<%= render(BlockPreview::PreviewDetailsComponent.new(
block: @block,
preview_content: @preview_content)) %>
</div>
</div>
<hr class="govuk-section-break govuk-!-margin-bottom-8 govuk-section-break--visible">
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
<%= render BlockPreview::TabsComponent.new(
current_tab: @current_tab,
snippet_count: @snippets.count,
block: @block,
preview_content: @preview_content,
) %>

<div class="result-wrapper">
<% @snippets.each do |snippet| %>
<div class="result govspeak compare-editions">
<%= snippet.to_html.html_safe %>
</div>
<% end %>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Feature: Host document preview
And the organisation "Ministry of Example" exists
And a pension content block has been created
And dependent content exists for a content block
And the show_snippets feature flag is not turned on

@javascript
Scenario: GDS editor can preview a host document
Expand Down
Loading
Loading