diff --git a/app/models/edition.rb b/app/models/edition.rb
index 109b0d02568..ed3c17e1e4a 100644
--- a/app/models/edition.rb
+++ b/app/models/edition.rb
@@ -54,6 +54,7 @@ class Edition < ApplicationRecord
validates_each :first_published_at do |record, attr, value|
record.errors.add(attr, "can't be set to a future date") if value && Time.zone.now < value
end
+ validate :scheduled_publication, :valid_date
UNMODIFIABLE_STATES = %w[scheduled published superseded deleted].freeze
FROZEN_STATES = %w[superseded deleted].freeze
@@ -714,6 +715,31 @@ def publishing_api_presenter
PublishingApi::GenericEditionPresenter
end
+ def scheduled_publication=(date)
+ if date.is_a?(Hash)
+ @date_field_validity = {} if @date_field_validity.nil?
+
+ begin
+ raise ArgumentError if date.values.any?(&:nil?)
+ raise ArgumentError if date.values.any? { |date_part| date_part.to_i.zero? }
+
+ Date.new(date[1], date[2], date[3])
+ @date_field_validity[:scheduled_publication] = true
+ rescue ArgumentError
+ @date_field_validity[:scheduled_publication] = false
+ date = nil
+ end
+ end
+
+ super(date)
+ end
+
+ def valid_date
+ if @date_field_validity.present? && @date_field_validity[:scheduled_publication] == false
+ errors.add(:scheduled_publication, "must be a valid date in the format XX XX XXXX")
+ end
+ end
+
private
def date_for_government
diff --git a/app/views/admin/editions/_scheduled_publication_fields.html.erb b/app/views/admin/editions/_scheduled_publication_fields.html.erb
index 345a2ed418c..361f76377bc 100644
--- a/app/views/admin/editions/_scheduled_publication_fields.html.erb
+++ b/app/views/admin/editions/_scheduled_publication_fields.html.erb
@@ -1,6 +1,13 @@
<%= hidden_field_tag :scheduled_publication_active, 0, id: "" %>
+ <% ## The date input needs a custom edition_scheduled_publication id passed into the day input so we can anchor error to the first input. %>
+
+ <%
+ hour_param = params.dig("edition", "scheduled_publication(4i)")
+ minute_param = params.dig("edition", "scheduled_publication(5i)")
+ %>
+
<%= render "govuk_publishing_components/components/checkboxes", {
name: "scheduled_publication_active",
id: "scheduled_publication_active",
@@ -12,32 +19,41 @@
{
label: "Schedule for publication",
value: 1,
- checked: edition.scheduled_publication.present?,
- conditional: render("components/datetime_fields", {
+ checked: params[:scheduled_publication_active] || edition.scheduled_publication.present?,
+ conditional: render("components/datetime_fields_with_govuk_date_component", {
field_name: "scheduled_publication",
prefix: "edition",
error_items: errors_for(edition.errors, :scheduled_publication),
date_heading: "Date",
- date_hint: "For example, 01 August 2022",
+ date_hint: "For example, 01 08 2022",
time_hint: "For example, 09:30 or 19:30",
year: {
- value: edition.scheduled_publication&.year || Time.zone.today.year,
+ value: params.dig("edition", "scheduled_publication(1i)") || edition.scheduled_publication&.year || Time.zone.today.year,
id: "edition_scheduled_publication_1i",
+ name: "edition[scheduled_publication(1i)]",
+ label: "Year",
+ width: 4,
},
month: {
- value: edition.scheduled_publication&.month || Time.zone.today.month,
+ value: params.dig("edition", "scheduled_publication(2i)") || edition.scheduled_publication&.month || Time.zone.today.month,
id: "edition_scheduled_publication_2i",
+ name: "edition[scheduled_publication(2i)]",
+ label: "Month",
+ width: 2,
},
day: {
- value: edition.scheduled_publication&.day || Time.zone.today.day,
- id: "edition_scheduled_publication_3i",
+ value: params.dig("edition", "scheduled_publication(3i)") || edition.scheduled_publication&.day || Time.zone.today.day,
+ id: "edition_scheduled_publication",
+ name: "edition[scheduled_publication(3i)]",
+ label: "Day",
+ width: 2,
},
hour: {
- value: edition.scheduled_publication&.hour || 9,
+ value: hour_param ? hour_param.to_i : (edition.scheduled_publication&.hour || 9),
id: "edition_scheduled_publication_4i",
},
minute: {
- value: edition.scheduled_publication&.min || 30,
+ value: minute_param ? minute_param.to_i : (edition.scheduled_publication&.min || 30),
id: "edition_scheduled_publication_5i",
},
})
diff --git a/app/views/components/_datetime_fields_with_govuk_date_component.html.erb b/app/views/components/_datetime_fields_with_govuk_date_component.html.erb
new file mode 100644
index 00000000000..70a2a6e67f6
--- /dev/null
+++ b/app/views/components/_datetime_fields_with_govuk_date_component.html.erb
@@ -0,0 +1,118 @@
+<%
+ prefix = prefix
+ field_name = field_name
+ id = id
+ date_id = "#{id}_date"
+ date_only ||= false
+ date_heading ||= nil
+
+ error_items = error_items ||= nil
+
+ heading_level = heading_level ||= nil
+ heading_size = heading_size ||= nil
+
+ date_hint = date_hint ||= nil
+
+ time_hint = time_hint ||= nil
+ time_hint_id = "time-hint-#{SecureRandom.hex(4)}"
+
+ year ||= {}
+
+ month ||= {}
+
+ day ||= {}
+
+ hour ||= {}
+ hour_value = hour[:value]
+ hour_select_id = hour[:id] || "select-hour-#{SecureRandom.hex(4)}"
+ hour_label_id = "hour-#{SecureRandom.hex(4)}"
+
+ minute ||= {}
+ minute_value = minute[:value]
+ minute_select_id = minute[:id] || "select-minute-#{SecureRandom.hex(4)}"
+ minute_label_id = "minute-#{SecureRandom.hex(4)}"
+
+ root_classes = %w[app-c-datetime-fields govuk-form-group]
+ root_classes << "govuk-form-group--error" if error_items.present?
+ data_attributes ||= {}
+%>
+
+<%= tag.div class: root_classes, data: data_attributes, id: id do %>
+ <% unless date_only && !date_heading %>
+ <%= render "govuk_publishing_components/components/heading", {
+ text: date_heading || "Date (required)",
+ heading_level: heading_level || 3,
+ font_size: heading_size || "m",
+ padding: true,
+ } %>
+ <% end %>
+
+ <%= render "govuk_publishing_components/components/date_input", {
+ id: date_id,
+ hint: date_hint,
+ error_items: error_items,
+ items: [day, month, year]
+ } %>
+
+ <% unless date_only %>
+
+ <%= render "govuk_publishing_components/components/heading", {
+ text: "Time",
+ heading_level: heading_level || 3,
+ font_size: heading_size || "m",
+ padding: true,
+ } %>
+
+
+ <% if time_hint %>
+ <%= render "govuk_publishing_components/components/hint", {
+ text: time_hint,
+ id: time_hint_id
+ } %>
+ <% end %>
+
+
+
+ <%= render "govuk_publishing_components/components/label", {
+ text: "Hour",
+ html_for: hour_select_id,
+ id: hour_label_id,
+ } %>
+
+ <%= select_hour hour_value,
+ {
+ include_blank: true,
+ prefix: prefix,
+ field_name: "#{field_name}(4i)"
+ },
+ {
+ id: hour_select_id,
+ class: "govuk-select app-c-datetime-fields__date-time-input",
+ "aria-describedby": "#{hour_label_id} #{time_hint_id if time_hint.present?}".strip
+ } %>
+
+
+
:
+
+
+ <%= render "govuk_publishing_components/components/label", {
+ text: "Minute",
+ html_for: minute_select_id,
+ id: minute_label_id,
+ } %>
+
+ <%= select_minute minute_value,
+ {
+ include_blank: true,
+ prefix: prefix,
+ field_name: "#{field_name}(5i)"
+ },
+ {
+ id: minute_select_id,
+ class: "govuk-select app-c-datetime-fields__date-time-input",
+ "aria-describedby": "#{minute_label_id} #{time_hint_id if time_hint.present?}".strip
+ } %>
+
+
+ <% end %>
+<% end %>
\ No newline at end of file
diff --git a/test/support/admin_edition_controller_scheduled_publishing_test_helpers.rb b/test/support/admin_edition_controller_scheduled_publishing_test_helpers.rb
index e4768159b0a..d94253944e7 100644
--- a/test/support/admin_edition_controller_scheduled_publishing_test_helpers.rb
+++ b/test/support/admin_edition_controller_scheduled_publishing_test_helpers.rb
@@ -21,7 +21,8 @@ def should_allow_scheduled_publication_of(edition_type)
assert_select "form#new_edition" do
assert_select "input[type=checkbox][name='scheduled_publication_active']"
- assert_select "select[name*='edition[scheduled_publication']", count: 5
+ assert_select "input[type=text][name*='edition[scheduled_publication']", count: 3
+ assert_select "select[name*='edition[scheduled_publication']", count: 2
end
end
@@ -121,9 +122,9 @@ def should_allow_scheduled_publication_of(edition_type)
assert_select "form#edit_edition" do
assert_select "input[type=checkbox][name='scheduled_publication_active'][checked='checked']"
- assert_select "select[name='edition[scheduled_publication(1i)]'] option[value='#{Time.zone.today.year + 1}'][selected='selected']"
- assert_select "select[name='edition[scheduled_publication(2i)]'] option[value='6'][selected='selected']"
- assert_select "select[name='edition[scheduled_publication(3i)]'] option[value='3'][selected='selected']"
+ assert_select "input[type=text][name='edition[scheduled_publication(1i)]'][value='#{Time.zone.today.year + 1}']"
+ assert_select "input[type=text][name='edition[scheduled_publication(2i)]'][value='6']"
+ assert_select "input[type=text][name='edition[scheduled_publication(3i)]'][value='3']"
assert_select "select[name='edition[scheduled_publication(4i)]'] option[value='10'][selected='selected']"
assert_select "select[name='edition[scheduled_publication(5i)]'] option[value='30'][selected='selected']"
end
@@ -138,9 +139,9 @@ def should_allow_scheduled_publication_of(edition_type)
assert_select "form#edit_edition" do
assert_select "input[type=checkbox][name='scheduled_publication_active']"
assert_select "input[type=checkbox][name='scheduled_publication_active'][checked='checked']", count: 0
- assert_select "select[name='edition[scheduled_publication(1i)]'] option[value='#{date.year}'][selected='selected']"
- assert_select "select[name='edition[scheduled_publication(2i)]'] option[value='#{date.month}'][selected='selected']"
- assert_select "select[name='edition[scheduled_publication(3i)]'] option[value='#{date.day}'][selected='selected']"
+ assert_select "input[name='edition[scheduled_publication(1i)]'][value='#{date.year}']"
+ assert_select "input[name='edition[scheduled_publication(2i)]'][value='#{date.month}']"
+ assert_select "input[name='edition[scheduled_publication(3i)]'][value='#{date.day}']"
assert_select "select[name='edition[scheduled_publication(4i)]'] option[value='09'][selected='selected']"
assert_select "select[name='edition[scheduled_publication(5i)]'] option[value='30'][selected='selected']"
end
diff --git a/test/unit/app/models/edition/validation_test.rb b/test/unit/app/models/edition/validation_test.rb
index a47cf9a463c..d060d3bab1b 100644
--- a/test/unit/app/models/edition/validation_test.rb
+++ b/test/unit/app/models/edition/validation_test.rb
@@ -194,4 +194,24 @@ class Edition::ValidationTest < ActiveSupport::TestCase
edition.supporting_organisations = [organisation1]
assert edition.valid?
end
+
+ test "should be valid when scheduled publication date is a valid date" do
+ edition = build(:draft_edition, scheduled_publication: { 1 => 2023, 2 => 9, 3 => 10 })
+ assert edition.valid?
+ end
+
+ test "should be invalid when scheduled publication date is an invalid date" do
+ edition = build(:draft_edition, scheduled_publication: { 1 => 2023, 2 => 9, 3 => 40 })
+ assert_not edition.valid?
+ end
+
+ test "should be invalid when scheduled publication date is partially completed" do
+ edition = build(:draft_edition, scheduled_publication: { 1 => 2023, 2 => nil, 3 => 9 })
+ assert_not edition.valid?
+ end
+
+ test "should be invalid when not all scheduled publication date parts are numeric" do
+ edition = build(:draft_edition, scheduled_publication: { 1 => 2023, 2 => "January", 3 => 20 })
+ assert_not edition.valid?
+ end
end