Skip to content

Commit

Permalink
Merge pull request #2218 from alphagov/edit-pop
Browse files Browse the repository at this point in the history
Create an 'edit' popular links page
  • Loading branch information
syed-ali-tw authored Jun 27, 2024
2 parents 24515ad + 5b0c177 commit 8811da5
Show file tree
Hide file tree
Showing 18 changed files with 449 additions and 46 deletions.
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
@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
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

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

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 }

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

context "#update" do
setup do
@popular_links = FactoryBot.create(:popular_links, state: "draft")
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
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

0 comments on commit 8811da5

Please sign in to comment.