Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create an 'edit' popular links page #2218

Merged
merged 4 commits into from
Jun 27, 2024
Merged
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
1 change: 1 addition & 0 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ $govuk-page-width: 1140px;
@import "govuk_publishing_components/all_components";
@import "downtimes";
@import "summary-card";
@import "sidebar_components";
6 changes: 6 additions & 0 deletions app/assets/stylesheets/sidebar_components.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.sidebar-components {
.govuk-button {
width: 100%;
}
border-top: 2px solid $govuk-brand-colour;
}
48 changes: 47 additions & 1 deletion app/controllers/homepage_controller.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,54 @@
class HomepageController < ApplicationController
layout "design_system"
before_action :fetch_latest_popular_link

def show
@latest_popular_links = PopularLinksEdition.last
render "homepage/popular_links/show"
end

def create
@latest_popular_links = @latest_popular_links.create_draft_popular_links_from_last_record
render "homepage/popular_links/show"
end

def edit
render "homepage/popular_links/edit"
end

def update
update_link_items
flash[:success] = "Popular links draft saved.".html_safe
redirect_to show_popular_links_path
rescue StandardError
render "homepage/popular_links/edit"
end

def publish
publish_latest_popular_links
render "homepage/popular_links/show"
end

private

def fetch_latest_popular_link
anatron marked this conversation as resolved.
Show resolved Hide resolved
@latest_popular_links = PopularLinksEdition.last
end

def update_link_items
@latest_popular_links.link_items = remove_leading_and_trailing_url_spaces(params[:popular_links].values)
@latest_popular_links.save!
end

def remove_leading_and_trailing_url_spaces(links)
link_items = []
links.each do |link|
link[:url] = link[:url].strip
link_items << link
end
link_items
end

def publish_latest_popular_links
@latest_popular_links.publish_popular_links
end
end
6 changes: 3 additions & 3 deletions app/helpers/errors_helper.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
module ErrorsHelper
def errors_for(errors, attribute)
def errors_for(errors, attribute, use_full_message: true)
return nil if errors.blank?

errors.filter_map { |error|
if error.attribute == attribute
{
text: error.full_message,
text: use_full_message ? error.full_message : error.message,
}
end
}
.presence
.presence
end
end
27 changes: 27 additions & 0 deletions app/helpers/popular_links_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,31 @@ def popular_link_rows(item)
rows << { key: "URL", value: item[:url] }
rows.compact
end

def primary_button_for(model, url, text)
form_for model, url:, method: :post do
render "govuk_publishing_components/components/button", {
text:,
margin_bottom: 3,
}
end
end

def secondary_button_for(model, url, text)
form_for model, url:, method: :post do
render "govuk_publishing_components/components/button", {
text:,
margin_bottom: 3,
secondary_solid: true,
}
end
end

def primary_link_button_for(url, text)
render "govuk_publishing_components/components/button", {
text:,
href: url,
margin_bottom: 3,
}
end
end
1 change: 1 addition & 0 deletions app/models/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Action
PUBLISH = "publish".freeze,
ARCHIVE = "archive".freeze,
NEW_VERSION = "new_version".freeze,
PUBLISH_POPULAR_LINKS = "publish_popular_links".freeze,
].freeze

NON_STATUS_ACTIONS = [
Expand Down
6 changes: 5 additions & 1 deletion app/models/edition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class ResurrectionError < RuntimeError
validates :version_number, presence: true, uniqueness: { scope: :panopticon_id }, unless: :popular_links_edition?
validates :panopticon_id, presence: true, unless: :popular_links_edition?
validates_with SafeHtml, unless: :popular_links_edition?
validates_with LinkValidator, on: :update, unless: :archived? || :popular_links_edition?
validates_with LinkValidator, on: :update, unless: :archived_or_popular_links?
validates_with ReviewerValidator
validates :change_note, presence: { if: :major_change }

Expand Down Expand Up @@ -519,4 +519,8 @@ def common_type_specific_field_keys(target_class)
def popular_links_edition?
instance_of?(::PopularLinksEdition)
end

def archived_or_popular_links?
archived? || popular_links_edition?
end
end
32 changes: 26 additions & 6 deletions app/models/popular_links_edition.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
class PopularLinksEdition < Edition
field :link_items, type: Array
validate :six_link_items_present?
validate :all_urls_and_titles_are_present?
validate :six_link_items_present
validate :all_valid_urls_and_titles_are_present

def six_link_items_present?
def six_link_items_present
anatron marked this conversation as resolved.
Show resolved Hide resolved
errors.add(:link_items, "6 links are required") if link_items.count != 6
end

def all_urls_and_titles_are_present?
def all_valid_urls_and_titles_are_present
link_items.each_with_index do |item, index|
errors.add(:item, "A URL is required for Link #{index + 1}") unless item.key?(:url)
errors.add(:item, "A Title is required for Link #{index + 1}") unless item.key?(:title)
errors.add("url#{index + 1}", "URL is required for Link #{index + 1}") unless url_present?(item)
errors.add("title#{index + 1}", "Title is required for Link #{index + 1}") unless title_present?(item)
errors.add("url#{index + 1}", "URL is invalid for Link #{index + 1}, all URLs should have at least one '.' and no spaces.") if url_present?(item) && url_has_spaces_or_has_no_dot?(item[:url])
end
end

def url_has_spaces_or_has_no_dot?(url)
url.include?(" ") || url.exclude?(".")
end

def title_present?(item)
item.key?(:title) && !item[:title].empty?
end

def url_present?(item)
item.key?(:url) && !item[:url].empty?
end

def create_draft_popular_links_from_last_record
last_popular_links = PopularLinksEdition.last
popular_links = PopularLinksEdition.new(title: last_popular_links.title, link_items: last_popular_links.link_items, version_number: last_popular_links.version_number.next)
popular_links.save!
popular_links
end
end
4 changes: 4 additions & 0 deletions app/models/workflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ class CannotDeletePublishedPublication < RuntimeError; end
transition all => :archived, :unless => :archived?
end

event :publish_popular_links do
transition %i[draft] => :published
end

state :in_review do
validates :review_requested_at, presence: true
end
Expand Down
25 changes: 25 additions & 0 deletions app/views/homepage/popular_links/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<%= render "govuk_publishing_components/components/fieldset", {
legend_text: "Link #{index+1}",
heading_level: 2,
heading_size: "m",
} do %>
<%= render "govuk_publishing_components/components/input", {
label: {
text: "Title",
},
name: "popular_links[#{index + 1}][title]",
id: "title#{index + 1}",
value: item[:title],
error_items: errors_for(form.object.errors, "title#{index + 1}".to_sym, use_full_message: false),
} %>

<%= render "govuk_publishing_components/components/input", {
label: {
text: "URL",
},
name: "popular_links[#{index + 1}][url]",
id: "url#{index + 1}",
value: item[:url],
error_items: errors_for(form.object.errors, "url#{index + 1}".to_sym, use_full_message: false),
} %>
<% end %>
13 changes: 13 additions & 0 deletions app/views/homepage/popular_links/_sidebar.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div class="sidebar-components">
<%= render "govuk_publishing_components/components/heading", {
text: "Options",
font_size: "s",
padding: true
} %>
<% if @latest_popular_links.state == "published" %>
<%= primary_button_for(@latest_popular_links, create_popular_links_path, "Create new edition") %>
<% else %>
<%= primary_link_button_for(edit_popular_links_path(@latest_popular_links), "Edit popular links") %>
<%= secondary_button_for(@latest_popular_links, publish_popular_links_path(@latest_popular_links), "Publish") %>
<% end %>
</div>
32 changes: 32 additions & 0 deletions app/views/homepage/popular_links/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<% content_for :page_title, 'Edit popular links' %>
<% content_for :title, 'Edit popular links' %>
<% content_for :title_context, 'Popular on GOV.UK' %>
<% unless @latest_popular_links.errors.empty? %>
<% content_for :error_summary do %>
<%= render("govuk_publishing_components/components/error_summary", {
id: "error-summary",
title: "There is a problem",
items: @latest_popular_links.errors.map do |error|
{
text: error.message,
href: "##{error.attribute.to_s}",
}
end
}) %>
<% end %>
<% end %>

<div class="govuk-grid-column-two-thirds">
<%= form_for @latest_popular_links, url: update_popular_links_path(@latest_popular_links), method: "patch" do |form| %>
<% @latest_popular_links.link_items.each_with_index do |item, index| %>
<%= render "homepage/popular_links/form", item:, index:, form: %>
<% end %>
<div class="govuk-button-group">
<%= render("govuk_publishing_components/components/button", {
text: "Save",
type: "submit",
}) %>
<%= link_to("Cancel", show_popular_links_path, class: "govuk-link govuk-link--no-visited-state") %>
</div>
<% end %>
</div>
3 changes: 3 additions & 0 deletions app/views/homepage/popular_links/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
<%= render "homepage/popular_links/link", item:, index: %>
<% end %>
</div>
<div class = "govuk-grid-column-one-third" >
<%= render "homepage/popular_links/sidebar" %>
</div>
6 changes: 5 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@

get "/govuk-sitemap.xml" => "sitemap#index"

get "/homepage/popular-links" => "homepage#show"
get "/homepage/popular-links" => "homepage#show", as: "show_popular_links"
post "/homepage/popular-links/create" => "homepage#create", as: "create_popular_links"
get "/homepage/popular-links/:id" => "homepage#edit", as: "edit_popular_links"
patch "/homepage/popular-links/:id" => "homepage#update", as: "update_popular_links"
post "/homepage/popular-links/:id/publish" => "homepage#publish", as: "publish_popular_links"

mount GovukAdminTemplate::Engine, at: "/style-guide"
mount Flipflop::Engine => "/flipflop"
Expand Down
97 changes: 97 additions & 0 deletions test/functional/homepage_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,101 @@ class HomepageControllerTest < ActionController::TestCase
assert_template "homepage/popular_links/show"
end
end

context "#create" do
setup do
@popular_links = FactoryBot.create(:popular_links, state: "published")
end
anatron marked this conversation as resolved.
Show resolved Hide resolved

should "render show template" do
post :create, params: { id: @popular_links.id }

assert_response :ok
assert_template "homepage/popular_links/show"
end

should "create a new draft popular links" do
assert_equal 1, PopularLinksEdition.count
assert_equal "published", PopularLinksEdition.last.state
anatron marked this conversation as resolved.
Show resolved Hide resolved

post :create, params: { id: @popular_links.id }

assert_equal 2, PopularLinksEdition.count
assert_equal "draft", PopularLinksEdition.last.state
end
end

context "#edit" do
should "render edit template" do
popular_links = FactoryBot.create(:popular_links, state: "published")

post :edit, params: { id: popular_links.id }
anatron marked this conversation as resolved.
Show resolved Hide resolved

assert_response :ok
assert_template "homepage/popular_links/edit"
end
end

context "#update" do
setup do
@popular_links = FactoryBot.create(:popular_links, state: "draft")
anatron marked this conversation as resolved.
Show resolved Hide resolved
end

should "update latest PopularLinksEdition with changed title and url" do
assert_equal "title1", @popular_links.link_items[0][:title]
assert_equal "https://www.url1.com", @popular_links.link_items[0][:url]

new_title = "title has changed"
new_url = "https://www.changedurl.com"
patch :update, params: { id: @popular_links.id,
"popular_links" =>
{ "1" => { "title" => new_title, "url" => new_url },
"2" => { "title" => "title2", "url" => "https://www.url2.com" },
"3" => { "title" => "title3", "url" => "https://www.url3.com" },
"4" => { "title" => "title4", "url" => "https://www.url4.com" },
"5" => { "title" => "title5", "url" => "https://www.url5.com" },
"6" => { "title" => "title6", "url" => "https://www.url6.com" } } }

assert_equal new_title, PopularLinksEdition.last.link_items[0][:title]
assert_equal new_url, PopularLinksEdition.last.link_items[0][:url]
end

should "redirect to show path on success" do
anatron marked this conversation as resolved.
Show resolved Hide resolved
new_title = "title has changed"
patch :update, params: { id: @popular_links.id,
"popular_links" =>
{ "1" => { "title" => new_title, "url" => "https://www.url1.com" },
"2" => { "title" => "title2", "url" => "https://www.url2.com" },
"3" => { "title" => "title3", "url" => "https://www.url3.com" },
"4" => { "title" => "title4", "url" => "https://www.url4.com" },
"5" => { "title" => "title5", "url" => "https://www.url5.com" },
"6" => { "title" => "title6", "url" => "https://www.url6.com" } } }

assert_redirected_to show_popular_links_path
end

should "render edit template on errors" do
patch :update, params: { id: @popular_links.id,
"popular_links" =>
{ "1" => { "title" => "title has changed", "url" => "https://www.url1.com" } } }

assert_template "homepage/popular_links/edit"
end
end

context "#publish" do
setup do
@popular_links = FactoryBot.create(:popular_links, state: "draft")
end

should "publish latest draft popular links and render show template" do
assert_equal "draft", PopularLinksEdition.last.state

post :publish, params: { id: @popular_links.id }

assert_response :ok
assert_template "homepage/popular_links/show"
assert_equal "published", PopularLinksEdition.last.state
end
end
end
Loading
Loading