diff --git a/app/assets/images/service-manual/icons-plus-minus.png b/app/assets/images/service-manual/icons-plus-minus.png new file mode 100644 index 000000000..37f2e249e Binary files /dev/null and b/app/assets/images/service-manual/icons-plus-minus.png differ diff --git a/app/assets/images/service-manual/icons-plus-minus.svg b/app/assets/images/service-manual/icons-plus-minus.svg new file mode 100644 index 000000000..579aad27a --- /dev/null +++ b/app/assets/images/service-manual/icons-plus-minus.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/images/service-manual/mail-icon-x2.png b/app/assets/images/service-manual/mail-icon-x2.png new file mode 100644 index 000000000..c59aaf29f Binary files /dev/null and b/app/assets/images/service-manual/mail-icon-x2.png differ diff --git a/app/assets/images/service-manual/placeholder.jpg b/app/assets/images/service-manual/placeholder.jpg new file mode 100644 index 000000000..21151a5b2 Binary files /dev/null and b/app/assets/images/service-manual/placeholder.jpg differ diff --git a/app/assets/images/service-manual/search-button.png b/app/assets/images/service-manual/search-button.png new file mode 100644 index 000000000..d29c06ad5 Binary files /dev/null and b/app/assets/images/service-manual/search-button.png differ diff --git a/app/assets/javascripts/modules/highlight-active-section-heading.js b/app/assets/javascripts/modules/highlight-active-section-heading.js new file mode 100644 index 000000000..d4c6fa86b --- /dev/null +++ b/app/assets/javascripts/modules/highlight-active-section-heading.js @@ -0,0 +1,156 @@ +window.GOVUK = window.GOVUK || {} +window.GOVUK.Modules = window.GOVUK.Modules || {}; + +(function (Modules) { + function HighlightActiveSectionHeading ($module) { + this.$module = $module + this._hasResized = true + this._hasScrolled = true + this._interval = 50 + this.anchorIDs = [] + } + + HighlightActiveSectionHeading.prototype.init = function () { + window.addEventListener('resize', function () { this._hasResized = true }.bind(this)) + window.addEventListener('scroll', function () { this._hasScrolled = true }.bind(this)) + + setInterval(this.checkResize.bind(this), this._interval) + setInterval(this.checkScroll.bind(this), this._interval) + + this.anchors = this.$module.querySelectorAll('.js-page-contents a') + this.getAnchors() + + this.checkResize() + this.checkScroll() + } + + HighlightActiveSectionHeading.prototype.checkResize = function () { + if (this._hasResized) { + this._hasResized = false + this._hasScrolled = true + } + } + + HighlightActiveSectionHeading.prototype.checkScroll = function () { + if (this._hasScrolled) { + this._hasScrolled = false + var windowDimensions = this.getWindowDimensions() + if (windowDimensions.width <= 768) { + this.removeActiveItem() + } else { + this.updateActiveNavItem() + } + } + } + + HighlightActiveSectionHeading.prototype.getWindowDimensions = function () { + return { + height: window.innerHeight, + width: window.innerWidth + } + } + + HighlightActiveSectionHeading.prototype.getAnchors = function () { + for (var i = 0; i < this.anchors.length; i++) { + var anchorID = this.anchors[i].getAttribute('href') + // e.g. anchorIDs['#meeting-the-digital-service-standard', '#understand-your-users', '#research-continually'] + this.anchorIDs.push(anchorID) + } + } + + HighlightActiveSectionHeading.prototype.updateActiveNavItem = function () { + var windowVerticalPosition = this.getWindowPositions() + var footerPosition = this.getFooterPosition() + + for (var i = 0; i < this.anchors.length; i++) { + var theID = this.anchorIDs[i] + var theNextID = this.anchorIDs[i + 1] + + var $theID = document.getElementById(theID.substring(1)) // remove the # at the start + var $theNextID = theNextID ? document.getElementById(theNextID.substring(1)) : null // remove the # at the start + + var headingPosition = this.getHeadingPosition($theID) + if (!headingPosition) { + return + } + + headingPosition = headingPosition - 53 // fix the offset from top of page + + if (theNextID) { + var nextHeadingPosition = this.getNextHeadingPosition($theNextID) + } + + var distanceBetweenHeadings = this.getDistanceBetweenHeadings(headingPosition, nextHeadingPosition) + var isPastHeading + + if (distanceBetweenHeadings) { + isPastHeading = (windowVerticalPosition >= headingPosition && windowVerticalPosition < (headingPosition + distanceBetweenHeadings)) + } else { + // when distanceBetweenHeadings is false (as there isn't a next heading) + isPastHeading = (windowVerticalPosition >= headingPosition && windowVerticalPosition < footerPosition) + } + + if (isPastHeading) { + this.setActiveItem(theID) + } + } + } + + HighlightActiveSectionHeading.prototype.getFooterPosition = function () { + var footer = document.querySelector('.govuk-footer') + if (footer) { + return this.getElementPosition(footer) + } + } + + // these two functions call getElementPosition because the test needs to individually + // override them - otherwise we could combine these four functions into one + HighlightActiveSectionHeading.prototype.getHeadingPosition = function (element) { + return this.getElementPosition(element) + } + + HighlightActiveSectionHeading.prototype.getNextHeadingPosition = function (element) { + return this.getHeadingPosition(element) + } + + HighlightActiveSectionHeading.prototype.getElementPosition = function (element) { + if (element) { + var rect = element.getBoundingClientRect() + var offset = { + top: rect.top + window.scrollY, + left: rect.left + window.scrollX + } + return offset.top + } + } + + HighlightActiveSectionHeading.prototype.getDistanceBetweenHeadings = function (headingPosition, nextHeadingPosition) { + var distanceBetweenHeadings = (nextHeadingPosition - headingPosition) + return distanceBetweenHeadings + } + + HighlightActiveSectionHeading.prototype.setActiveItem = function (theID) { + for (var i = 0; i < this.anchors.length; i++) { + var href = this.anchors[i].getAttribute('href') + if (href === theID) { + this.anchors[i].classList.add('active') + } else { + this.anchors[i].classList.remove('active') + } + } + } + + HighlightActiveSectionHeading.prototype.removeActiveItem = function () { + for (var i = 0; i < this.anchors.length; i++) { + this.anchors[i].classList.remove('active') + } + } + + HighlightActiveSectionHeading.prototype.getWindowPositions = function () { + var doc = document.documentElement + var top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) + return top + } + + Modules.HighlightActiveSectionHeading = HighlightActiveSectionHeading +})(window.GOVUK.Modules) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index a1a25dad0..bebc4e14f 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -11,7 +11,6 @@ $govuk-include-default-font-face: false; // Components from govuk_publishing_components gem @import "govuk_publishing_components/govuk_frontend_support"; - @import "govuk_publishing_components/components/accordion"; @import "govuk_publishing_components/components/attachment"; @import "govuk_publishing_components/components/back-link"; @@ -62,6 +61,22 @@ $govuk-include-default-font-face: false; @import "helpers/publisher-metadata-with-logo"; @import "helpers/attachments"; @import "helpers/sticky-element-container"; +@import "helpers/full-width"; + +// modules for service-manual pages +@import "modules/change-history"; +@import "modules/collection"; +@import "modules/community-contact"; +@import "modules/govspeak-wrapper"; +@import "modules/hero"; +@import "modules/notice"; +@import "modules/page-contents"; +@import "modules/page-header"; +@import "modules/panel"; +@import "modules/related-content"; +@import "modules/service-standard-point"; +@import "modules/sticky"; +@import "modules/typography"; // Components from this application @import "components/*"; @@ -86,6 +101,7 @@ $govuk-include-default-font-face: false; @import "views/corporate-information-page"; @import "views/travel-advice"; @import "views/contact"; +@import "views/service_manual_guide"; @import "views/specialist-document"; @import "views/answer"; @import "views/help-page"; diff --git a/app/assets/stylesheets/helpers/_full-width.scss b/app/assets/stylesheets/helpers/_full-width.scss new file mode 100644 index 000000000..458ebd736 --- /dev/null +++ b/app/assets/stylesheets/helpers/_full-width.scss @@ -0,0 +1,11 @@ +.app-full-width { + #global-header-bar { // stylelint-disable-line selector-max-id + display: none; + } +} + +// stylelint-disable declaration-no-important +.app-\!-full-width-override { + margin: auto !important; + max-width: none !important; +} diff --git a/app/assets/stylesheets/mixins/_margins.scss b/app/assets/stylesheets/mixins/_margins.scss index cddca903f..1828d6d98 100644 --- a/app/assets/stylesheets/mixins/_margins.scss +++ b/app/assets/stylesheets/mixins/_margins.scss @@ -22,3 +22,25 @@ .responsive-bottom-margin { @include responsive-bottom-margin; } + +// Service Manuals +@mixin gutter-bottom-margin { + margin-bottom: govuk-spacing(6); +} + +@mixin gutter-top-margin { + margin-top: govuk-spacing(6); +} + +@mixin gutter-vertical-margins { + @include gutter-bottom-margin; + @include gutter-top-margin; +} + +@mixin gutter-bottom-margin-to-double { + margin-bottom: govuk-spacing(3); + + @include govuk-media-query($from: tablet) { + margin-bottom: govuk-spacing(9); + } +} diff --git a/app/assets/stylesheets/modules/_change-history.scss b/app/assets/stylesheets/modules/_change-history.scss new file mode 100644 index 000000000..484d8ba16 --- /dev/null +++ b/app/assets/stylesheets/modules/_change-history.scss @@ -0,0 +1,11 @@ +.app-change-history__latest-change { + dd { + margin: 0; + padding: 0; + } +} + +.app-change-history__change-note { + color: $govuk-secondary-text-colour; + white-space: pre-line; +} diff --git a/app/assets/stylesheets/modules/_collection.scss b/app/assets/stylesheets/modules/_collection.scss new file mode 100644 index 000000000..25299b22b --- /dev/null +++ b/app/assets/stylesheets/modules/_collection.scss @@ -0,0 +1,19 @@ +.app-collection { + border-bottom: 1px solid $govuk-border-colour; + margin-bottom: govuk-spacing(3); + padding-bottom: govuk-spacing(3); + + @include govuk-media-query($from: tablet) { + margin-bottom: govuk-spacing(9); + padding-bottom: govuk-spacing(6); + } + + &:last-child { + border-bottom: none; + margin-bottom: 0; + } + + &__description { + color: $govuk-secondary-text-colour; + } +} diff --git a/app/assets/stylesheets/modules/_community-contact.scss b/app/assets/stylesheets/modules/_community-contact.scss new file mode 100644 index 000000000..0c7bc3236 --- /dev/null +++ b/app/assets/stylesheets/modules/_community-contact.scss @@ -0,0 +1,6 @@ +.app-community-contact { + @include govuk-media-query($from: tablet) { + text-align: right; + padding-top: govuk-spacing(8); + } +} diff --git a/app/assets/stylesheets/modules/_govspeak-wrapper.scss b/app/assets/stylesheets/modules/_govspeak-wrapper.scss new file mode 100644 index 000000000..d05c328b8 --- /dev/null +++ b/app/assets/stylesheets/modules/_govspeak-wrapper.scss @@ -0,0 +1,7 @@ +.govspeak-wrapper { + padding-top: .9375em; + + @include govuk-media-query($from: tablet) { + padding-top: 1.875em; + } +} diff --git a/app/assets/stylesheets/modules/_hero.scss b/app/assets/stylesheets/modules/_hero.scss new file mode 100644 index 000000000..4f1b33d97 --- /dev/null +++ b/app/assets/stylesheets/modules/_hero.scss @@ -0,0 +1,25 @@ +.app-hero { + background-color: $govuk-brand-colour; + color: govuk-colour("white"); + margin-bottom: govuk-spacing(3); + position: relative; + top: -1px; + + @include govuk-media-query($from: tablet) { + margin-bottom: govuk-spacing(6); + } + + a:link, + a:visited { + @include govuk-typography-weight-bold; + } +} + +.app-hero-lead { + line-height: 1.35; +} + +.app-hero__heading--inverse, +.app-hero__body--inverse { + color: govuk-colour("white"); +} diff --git a/app/assets/stylesheets/modules/_notice.scss b/app/assets/stylesheets/modules/_notice.scss new file mode 100644 index 000000000..af1646d51 --- /dev/null +++ b/app/assets/stylesheets/modules/_notice.scss @@ -0,0 +1,20 @@ +.notice { + clear: both; + @include responsive-bottom-margin; + padding: govuk-spacing(3); + + border: 5px solid $govuk-brand-colour; + + @include govuk-media-query($from: tablet) { + padding: govuk-spacing(4); + } + + &__title { + @include govuk-font(36, $weight: bold); + margin-bottom: govuk-spacing(2); + } + + &__description { + @include govuk-font(19); + } +} diff --git a/app/assets/stylesheets/modules/_page-contents.scss b/app/assets/stylesheets/modules/_page-contents.scss new file mode 100644 index 000000000..c36404896 --- /dev/null +++ b/app/assets/stylesheets/modules/_page-contents.scss @@ -0,0 +1,18 @@ +.app-page-contents { + &__list li { + margin-left: govuk-spacing(0); + padding-right: govuk-spacing(3); + + &:before { + content: "– "; + display: inline-block; + padding-right: govuk-spacing(1); + } + } + + // Styles required by GOVUK.HighlightActiveNavItem JS + &__list .active { + letter-spacing: 0; + font-weight: bold; + } +} diff --git a/app/assets/stylesheets/modules/_page-header.scss b/app/assets/stylesheets/modules/_page-header.scss new file mode 100644 index 000000000..daaf0e597 --- /dev/null +++ b/app/assets/stylesheets/modules/_page-header.scss @@ -0,0 +1,23 @@ +.app-page-header { + &__intro { + p { + @include govuk-text-colour; + @include govuk-font($size: 19); + @include govuk-responsive-margin(4, "bottom"); + } + + a { + @include govuk-link-common; + @include govuk-link-style-default; + } + } + + &__heading { + margin-bottom: govuk-spacing(3); + + @include govuk-media-query($from: tablet) { + margin-bottom: govuk-spacing(7) + govuk-spacing(1); + margin-top: govuk-spacing(7) + govuk-spacing(1); + } + } +} diff --git a/app/assets/stylesheets/modules/_panel.scss b/app/assets/stylesheets/modules/_panel.scss new file mode 100644 index 000000000..218f62134 --- /dev/null +++ b/app/assets/stylesheets/modules/_panel.scss @@ -0,0 +1,3 @@ +.panel { + border-top: 1px solid $govuk-border-colour; +} diff --git a/app/assets/stylesheets/modules/_related-content.scss b/app/assets/stylesheets/modules/_related-content.scss new file mode 100644 index 000000000..b22f56ab4 --- /dev/null +++ b/app/assets/stylesheets/modules/_related-content.scss @@ -0,0 +1,14 @@ +.related { + @include govuk-media-query($from: tablet) { + border-top: 1px solid $govuk-border-colour; + } +} + +.related-item { + &__email-link { + padding-left: 25px; + font-weight: bold; + background: image-url("service-manual/mail-icon-x2.png") 0 40% no-repeat; + background-size: 20px 14px; + } +} diff --git a/app/assets/stylesheets/modules/_service-standard-point.scss b/app/assets/stylesheets/modules/_service-standard-point.scss new file mode 100644 index 000000000..8822aa35b --- /dev/null +++ b/app/assets/stylesheets/modules/_service-standard-point.scss @@ -0,0 +1,39 @@ +.app-service-standard-point { + border-top: 1px solid $govuk-border-colour; + margin-top: govuk-spacing(5); + padding-top: govuk-spacing(5); + + &:first-child { + margin-top: govuk-spacing(0); + } + + &__title { + margin-left: govuk-spacing(6); + + @include govuk-media-query($from: tablet) { + margin-left: govuk-spacing(8); + } + } + + &__number { + float: left; + margin-left: -(govuk-spacing(6)); + + @include govuk-media-query($from: tablet) { + margin-left: -(govuk-spacing(8)); + } + } + + &__details { + margin-bottom: govuk-spacing(2); + margin-left: govuk-spacing(6); + + @include govuk-media-query($from: tablet) { + margin-left: govuk-spacing(8); + } + } + + &__link { + margin-top: govuk-spacing(2); + } +} diff --git a/app/assets/stylesheets/modules/_sticky.scss b/app/assets/stylesheets/modules/_sticky.scss new file mode 100644 index 000000000..444b4a699 --- /dev/null +++ b/app/assets/stylesheets/modules/_sticky.scss @@ -0,0 +1,6 @@ +.app-sticky-element { + @include govuk-media-query($from: tablet) { + position: sticky; + top: 0; + } +} diff --git a/app/assets/stylesheets/modules/_typography.scss b/app/assets/stylesheets/modules/_typography.scss new file mode 100644 index 000000000..90d0ba47f --- /dev/null +++ b/app/assets/stylesheets/modules/_typography.scss @@ -0,0 +1,5 @@ +// Text +.app-body, +.app-body-s { + line-height: 1.45; +} diff --git a/app/assets/stylesheets/views/_service_manual_guide.scss b/app/assets/stylesheets/views/_service_manual_guide.scss new file mode 100644 index 000000000..8a312b74c --- /dev/null +++ b/app/assets/stylesheets/views/_service_manual_guide.scss @@ -0,0 +1,3 @@ +.js-enabled .app-change-history__past.govuk-list.js-hidden { + display: none; +} diff --git a/app/controllers/content_items_controller.rb b/app/controllers/content_items_controller.rb index 0cc05bfd3..d758ad25d 100644 --- a/app/controllers/content_items_controller.rb +++ b/app/controllers/content_items_controller.rb @@ -1,5 +1,9 @@ +require "slimmer/headers" + class ContentItemsController < ApplicationController include GovukPersonalisation::ControllerConcern + include Slimmer::Headers + include Slimmer::Template rescue_from GdsApi::HTTPForbidden, with: :error_403 rescue_from GdsApi::HTTPNotFound, with: :error_notfound @@ -22,7 +26,9 @@ def show set_expiry - if is_history_page? + if is_service_manual? + show_service_manual_page + elsif is_history_page? show_history_page else set_use_recommended_related_links_header @@ -76,6 +82,58 @@ def show_history_page end end + def is_service_manual? + @content_item.document_type.include?("service_manual") + end + + def show_service_manual_page + slimmer_template(service_manual_layout) + configure_header_search + + has_custom_breadcrumbs = %w[ + service_manual_guide + service_manual_service_standard + service_manual_topic + service_manual_homepage + ] + + @do_not_show_breadcrumbs = + has_custom_breadcrumbs.include?(@content_item.document_type) + + with_locale do + render content_item_template + end + end + + def service_manual_layout + types = %w[service_manual_homepage service_manual_service_toolkit] + types.include?(@content_item.document_type) ? "gem_layout_full_width_no_footer_navigation" : "gem_layout_no_footer_navigation" + end + + def configure_header_search + if @content_item.present? && !@content_item.include_search_in_header? + remove_header_search + else + scope_header_search_to_service_manual + end + end + + def scope_header_search_to_service_manual + # Slimmer is middleware which wraps the service manual in the GOV.UK header + # and footer. We set a response header so that Slimmer adds a hidden field + # to the header search to scope the search results to just the service + # manual. + set_slimmer_headers( + search_parameters: { + "filter_manual" => "/service-manual", + }.to_json, + ) + end + + def remove_header_search + set_slimmer_headers(remove_search: true) + end + def show_error_message @error = true show diff --git a/app/helpers/service_manual_phase_label_helper.rb b/app/helpers/service_manual_phase_label_helper.rb new file mode 100644 index 000000000..69e70ae23 --- /dev/null +++ b/app/helpers/service_manual_phase_label_helper.rb @@ -0,0 +1,9 @@ +module ServiceManualPhaseLabelHelper + def render_phase_label(presented_object, message) + if presented_object.respond_to?(:phase) && %w[alpha beta].include?(presented_object.phase) + render "govuk_publishing_components/components/phase_banner", + phase: presented_object.phase, + message: + end + end +end diff --git a/app/helpers/service_manual_topic_helper.rb b/app/helpers/service_manual_topic_helper.rb new file mode 100644 index 000000000..b7c0567e3 --- /dev/null +++ b/app/helpers/service_manual_topic_helper.rb @@ -0,0 +1,9 @@ +module ServiceManualTopicHelper + def topic_related_communities_title(communities) + if communities.length == 1 + "Join the #{communities.first[:title]}" + else + "Join the community" + end + end +end diff --git a/app/presenters/content_item_presenter.rb b/app/presenters/content_item_presenter.rb index 682241201..45176be91 100644 --- a/app/presenters/content_item_presenter.rb +++ b/app/presenters/content_item_presenter.rb @@ -92,6 +92,10 @@ def show_phase_banner? phase.in?(%w[alpha beta]) end + def show_service_manual_phase_banner? + false + end + def render_guide_as_single_page? # /how-to-vote content_id == "9315bc67-33e7-42e9-8dea-e022f56dabfa" && voting_is_open? diff --git a/app/presenters/service_manual_guide_presenter.rb b/app/presenters/service_manual_guide_presenter.rb new file mode 100644 index 000000000..5f0c3d3a0 --- /dev/null +++ b/app/presenters/service_manual_guide_presenter.rb @@ -0,0 +1,88 @@ +class ServiceManualGuidePresenter < ServiceManualPresenter + ContentOwner = Struct.new(:title, :href) + Change = Struct.new(:public_timestamp, :note) + + def body + @body ||= details.fetch("body", {}) + end + + def header_links + header_links = details.fetch("header_links", {}) + Array(header_links).map { |h| ActiveSupport::HashWithIndifferentAccess.new(h) } + end + + def content_owners + links_content_owners_attributes.map do |content_owner_attributes| + ContentOwner.new(content_owner_attributes["title"], content_owner_attributes["base_path"]) + end + end + + def category_title + category["title"] if category.present? + end + + def breadcrumbs + crumbs = [{ title: "Service manual", url: "/service-manual" }] + crumbs << { title: category["title"], url: category["base_path"] } if category + crumbs + end + + def show_description? + details["show_description"].present? + end + + def public_updated_at + timestamp = content_item["public_updated_at"] + + Time.zone.parse(timestamp) if timestamp + end + + def visible_updated_at + public_updated_at || updated_at + end + + def latest_change + change = change_history.first + if change.present? + Change.new( + visible_updated_at, + change["note"], + ) + end + end + + def previous_changes + change_history.drop(1).map do |change| + Change.new( + Time.zone.parse(change["public_timestamp"]), + change["note"], + ) + end + end + +private + + def links_content_owners_attributes + content_item.to_hash.fetch("links", {}).fetch("content_owners", []) + end + + def category + topic || parent + end + + def parent + @parent ||= Array(links["parent"]).first + end + + def topic + @topic ||= Array(links["service_manual_topics"]).first + end + + def change_history + @change_history ||= details.fetch("change_history", {}) + end + + def updated_at + Time.zone.parse(content_item["updated_at"]) + end +end diff --git a/app/presenters/service_manual_homepage_presenter.rb b/app/presenters/service_manual_homepage_presenter.rb new file mode 100644 index 000000000..eac608d7e --- /dev/null +++ b/app/presenters/service_manual_homepage_presenter.rb @@ -0,0 +1,19 @@ +class ServiceManualHomepagePresenter < ServiceManualPresenter + def include_search_in_header? + false + end + + def topics + unsorted_topics.sort_by { |topic| topic["title"] } + end + + def phase + "beta" + end + +private + + def unsorted_topics + @unsorted_topics ||= links["children"] || [] + end +end diff --git a/app/presenters/service_manual_presenter.rb b/app/presenters/service_manual_presenter.rb new file mode 100644 index 000000000..80d2fe57e --- /dev/null +++ b/app/presenters/service_manual_presenter.rb @@ -0,0 +1,21 @@ +class ServiceManualPresenter < ContentItemPresenter + def links + @links ||= content_item["links"] || {} + end + + def details + @details ||= content_item["details"] || {} + end + + def include_search_in_header? + true + end + + def show_service_manual_phase_banner? + true + end + + def show_phase_banner? + false + end +end diff --git a/app/presenters/service_manual_service_standard_presenter.rb b/app/presenters/service_manual_service_standard_presenter.rb new file mode 100644 index 000000000..d0ae89c55 --- /dev/null +++ b/app/presenters/service_manual_service_standard_presenter.rb @@ -0,0 +1,56 @@ +class ServiceManualServiceStandardPresenter < ServiceManualPresenter + attr_reader :poster_url + + def initialize(*) + super + @poster_url = details["poster_url"] + end + + def points + Point.load(points_attributes).sort + end + + def breadcrumbs + [ + { title: "Service manual", url: "/service-manual" }, + ] + end + + def email_alert_signup_link + "/email-signup?link=#{content_item['base_path']}" + end + +private + + def points_attributes + @points_attributes ||= links["children"] || [] + end + + class Point + include Comparable + + attr_reader :title, :description, :base_path + + def self.load(points_attributes) + points_attributes.map { |point_attributes| new(point_attributes) } + end + + def initialize(attributes, *_args) + @title = attributes["title"] + @description = attributes["description"] + @base_path = attributes["base_path"] + end + + def <=>(other) + number <=> other.number + end + + def title_without_number + @title_without_number ||= title.sub(/\A(\d*)\.(\s*)/, "") + end + + def number + @number ||= Integer(title.scan(/\A(\d*)/)[0][0]) + end + end +end diff --git a/app/presenters/service_manual_service_toolkit_presenter.rb b/app/presenters/service_manual_service_toolkit_presenter.rb new file mode 100644 index 000000000..8aec7c66c --- /dev/null +++ b/app/presenters/service_manual_service_toolkit_presenter.rb @@ -0,0 +1,9 @@ +class ServiceManualServiceToolkitPresenter < ServiceManualPresenter + def include_search_in_header? + false + end + + def collections + details.fetch("collections", []) + end +end diff --git a/app/presenters/service_manual_topic_presenter.rb b/app/presenters/service_manual_topic_presenter.rb new file mode 100644 index 000000000..8cee850bd --- /dev/null +++ b/app/presenters/service_manual_topic_presenter.rb @@ -0,0 +1,122 @@ +class ServiceManualTopicPresenter < ServiceManualPresenter + ContentOwner = Struct.new(:title, :href) + + attr_reader :visually_collapsed + alias_method :visually_collapsed?, :visually_collapsed + + def initialize(content_item, *args) + super + @visually_collapsed = content_item["details"]["visually_collapsed"] + end + + def breadcrumbs + parent_breadcrumbs + end + + def groups + linked_items = content_item["links"]["linked_items"] + topic_groups = Array(content_item["details"]["groups"]).map do |group_data| + ServiceManualTopicPresenter::TopicGroup.new(group_data, linked_items) + end + topic_groups.select(&:present?) + end + + def content_owners + @content_owners ||= Array(content_item["links"]["content_owners"]).map do |data| + ContentOwner.new(data["title"], data["base_path"]) + end + end + + def email_alert_signup_link + "/email-signup?link=#{content_item['base_path']}" + end + + def phase + "beta" + end + + def visually_expanded? + !visually_collapsed? + end + + def display_as_accordion? + groups.count > 2 && visually_collapsed? + end + + def accordion_content + # Each accordion needs a hash, as shown in the GOV.UK Publishing Components + # guide: https://components.publishing.service.gov.uk/component-guide/accordion + # + # This method returns the content in the required shape from the hash + # supplied by the `groups` method. + + groups.each.with_index(1).map do |section, index| + { + data_attributes: { + ga4: { + event_name: "select_content", + type: "accordion", + text: section.name, + index:, + index_total: groups.length, + }, + }, + heading: { + text: section.name, + }, + summary: { + text: section.description, + }, + content: { + html: accordion_section_links(section.linked_items), + }, + expanded: visually_expanded?, + } + end + end + +private + + def accordion_section_links(links) + # Expects `links` to be an array of hashes containing `href` and `label` + # for the link. For example: + # + # ```ruby + # [ + # { + # label: 'Link to example', + # href: 'http://example.com' + # } + # ] + # ``` + # + # This will return santitised HTML in a string. The above example would + # return: + # + # ```html + # + # ``` + + links = links.map do |linked_item| + link_html = ActionController::Base.helpers.link_to(linked_item.label, linked_item.href, class: "govuk-link") + "
  • #{link_html}
  • " + end + + list = "" + + ActionController::Base.helpers.sanitize(list) + end + + def parent_breadcrumbs + [ + { + title: "Service manual", + url: "/service-manual", + }, + ] + end +end diff --git a/app/presenters/service_manual_topic_presenter/linked_item.rb b/app/presenters/service_manual_topic_presenter/linked_item.rb new file mode 100644 index 000000000..1589dfa91 --- /dev/null +++ b/app/presenters/service_manual_topic_presenter/linked_item.rb @@ -0,0 +1,24 @@ +class ServiceManualTopicPresenter::LinkedItem + attr_reader :content_id + + def initialize(content_id, linked_items) + @content_id = content_id + @linked_items = linked_items + end + + def label + details["title"] + end + + def href + details["base_path"] + end + + delegate :present?, to: :details + +private + + def details + @details ||= @linked_items.find { |ld| ld["content_id"] == content_id } + end +end diff --git a/app/presenters/service_manual_topic_presenter/topic_group.rb b/app/presenters/service_manual_topic_presenter/topic_group.rb new file mode 100644 index 000000000..309642dda --- /dev/null +++ b/app/presenters/service_manual_topic_presenter/topic_group.rb @@ -0,0 +1,22 @@ +class ServiceManualTopicPresenter::TopicGroup + attr_reader :name, :description, :data + + def initialize(data, linked_items) + @data = data + @linked_items = linked_items + + @name = data["name"] + @description = data["description"] + end + + def linked_items + linked_items = Array(data["content_ids"]).map do |content_id| + ServiceManualTopicPresenter::LinkedItem.new(content_id, @linked_items) + end + linked_items.select(&:present?) + end + + def present? + linked_items.any? + end +end diff --git a/app/views/content_items/service_manual_guide.html.erb b/app/views/content_items/service_manual_guide.html.erb new file mode 100644 index 000000000..74e3c3f56 --- /dev/null +++ b/app/views/content_items/service_manual_guide.html.erb @@ -0,0 +1,122 @@ +<%= content_for :title, "#{@content_item.title} - Service Manual" %> + +<% content_for :extra_head do %> + <%= machine_readable_metadata( + schema: :article + ) %> +<% end %> + +<% content_for :phase_message do %> + <%= render "shared/custom_phase_message", phase: @content_item.phase %> +<% end %> + +
    + <%= render "govuk_publishing_components/components/breadcrumbs", { + breadcrumbs: @content_item.breadcrumbs, + collapse_on_mobile: true + } %> + + +
    +
    +
    + <%= render "govuk_publishing_components/components/title", { + context: @content_item.category_title, + title: @content_item.title, + margin_bottom: 8 + } %> + <% if @content_item.show_description? %> +

    + <%= @content_item.description %> +

    + <% end %> +
    +
    +
    +

    + <%= link_to "Give feedback about this page", "/contact/govuk", class: "govuk-link" %> +

    +
    +
    + + +
    + +
    + +
    +
    + +
    +
    + <%= render "govuk_publishing_components/components/heading", { + text: "Page contents:", + heading_level: 2, + font_size: "s", + margin_bottom: 1, + } %> +
    +
      + <% @content_item.header_links.each do |header_link| %> +
    • + <%= link_to(header_link[:title], header_link[:href], class: 'govuk-link') %> +
    • + <% end %> +
    +
    + +
    +
    +
    + <%= render "govuk_publishing_components/components/govspeak", content: @content_item.body.html_safe %> +
    +
    +
    + <% if @content_item.content_owners.any? %> + <%= render "govuk_publishing_components/components/metadata", { + last_updated: "#{time_ago_in_words(@content_item.visible_updated_at)} ago", + from: @content_item.content_owners.map { |content_owner| + link_to(content_owner.title, content_owner.href) + } + } %> + <% end %> + <% if @content_item.latest_change.present? %> +
    +
    Last update:
    +
    + <%= render partial: 'shared/change_history', locals: {change: @content_item.latest_change} %> +
    +
    + <% end %> +
    + <% if @content_item.previous_changes.present? %> +

    + +

    +
      + <% @content_item.previous_changes.each do |previous_change| %> +
    1. + <%= render partial: 'shared/change_history', locals: { change: previous_change, compressed: true} %> +
    2. + <% end %> +
    + <% end %> +
    +
    +
    +
    diff --git a/app/views/content_items/service_manual_homepage.html.erb b/app/views/content_items/service_manual_homepage.html.erb new file mode 100644 index 000000000..e65f7e8ba --- /dev/null +++ b/app/views/content_items/service_manual_homepage.html.erb @@ -0,0 +1,84 @@ +<% + content_for :title, "Service Manual" + content_for :phase_message do + render 'shared/custom_phase_message', phase: @content_item.phase + end + content_for :body_classes, 'app-full-width' +%> + +
    +
    +
    +
    +
    + <%= render "govuk_publishing_components/components/title", { + title: "Service Manual", + inverse: true, + margin_top: 0, + margin_bottom: 6, + } %> +

    + Helping teams to create and run great public services that meet the <%= link_to 'Service Standard', '/service-manual/service-standard', class: 'govuk-link govuk-link--inverse' %>. +

    +
    + + <%= render "govuk_publishing_components/components/search", { + label_text: "Search the service manual", + name: "q", + on_govuk_blue: true, + type: "search" + } %> +
    +
    +
    +

    + <%= link_to 'Contact the Service Manual team', '/contact/govuk', class: 'govuk-link govuk-link--inverse' %> + with any comments or questions. +

    +
    +
    +
    +
    +
    + +
    +
    + <% @content_item.topics.each_slice(3) do |row_of_topics| %> +
    + <% row_of_topics.each do |topic| %> +
    + <%= render "govuk_publishing_components/components/heading", { + text: sanitize(link_to topic["title"], topic["base_path"], class: 'govuk-link'), + heading_level: 2, + font_size: "s", + margin_bottom: 1, + } %> +

    <%= topic["description"] %>

    +
    + <% end %> +
    + <% end %> + +
    +
    +
    + <%= render "govuk_publishing_components/components/heading", { + text: "The Service Standard", + font_size: "m", + } %> +

    The <%= link_to 'Service Standard', '/service-manual/service-standard', class: 'govuk-link' %> provides the principles of building a good service. This manual explains what teams can do to build great services that will meet the standard.

    +
    +
    + +
    +
    + <%= render "govuk_publishing_components/components/heading", { + text: "Communities of practice", + font_size: "m", + } %> +

    You can view the <%= link_to 'communities of practice', '/service-manual/communities', class: 'govuk-link' %> to find more learning resources, see who has written the guidance in the manual and connect with digital people like you from across government.

    +
    +
    +
    +
    +
    diff --git a/app/views/content_items/service_manual_service_standard.html.erb b/app/views/content_items/service_manual_service_standard.html.erb new file mode 100644 index 000000000..239ff6256 --- /dev/null +++ b/app/views/content_items/service_manual_service_standard.html.erb @@ -0,0 +1,64 @@ +<%= content_for :title, "#{@content_item.title} - Service Manual" %> + +<% content_for :phase_message do %> + <%= render 'shared/custom_phase_message', phase: @content_item.phase %> +<% end %> +
    + <%= render "govuk_publishing_components/components/breadcrumbs", { + breadcrumbs: @content_item.breadcrumbs, + collapse_on_mobile: true + } %> + + +
    +
    +
    + <%= render "govuk_publishing_components/components/title", { + title: @content_item.title, + } %> +

    + <%= @content_item.content_item["description"] %> +

    +
    + <%= @content_item.content_item["details"]["body"].html_safe %> +
    +
    +
    +
    + + +
    +
    +
      + <% @content_item.points.each do |point| %> +
    1. +

      + <%= point.number %>. + <%= point.title_without_number %> +

      +
      + <%= "

      #{point.description}

      " if point.description.present? %> + +
      +
    2. + <% end %> +
    +
    + +
    + +
    +
    +
    \ No newline at end of file diff --git a/app/views/content_items/service_manual_service_toolkit.html.erb b/app/views/content_items/service_manual_service_toolkit.html.erb new file mode 100644 index 000000000..3e04b184b --- /dev/null +++ b/app/views/content_items/service_manual_service_toolkit.html.erb @@ -0,0 +1,57 @@ +<% + content_for :title, "Service Toolkit" + content_for :header_title, "Service Toolkit" + content_for :body_classes, "app-full-width" +%> +
    +
    +
    +
    +
    + <%= render "govuk_publishing_components/components/title", { + title: "Design and build government services", + inverse: true, + margin_top: 0, + margin_bottom: 6, + } %> +

    + All you need to design, build and run services that meet government standards. +

    +
    +
    +
    +
    +
    + +
    + <% @content_item.collections.each do |collection| %> +
    +
    +
    + <%= render "govuk_publishing_components/components/heading", { + text: collection["title"], + font_size: "l", + margin_bottom: 1, + id: collection["title"].parameterize, + } %> +

    <%= collection["description"] %>

    +
    +
    + <% collection["links"].each_slice(2) do |row_of_links| %> +
    + <% row_of_links.each do | link | %> +
    + <%= render "govuk_publishing_components/components/heading", { + text: sanitize(link_to link["title"], link["url"], class: "govuk-link"), + heading_level: 3, + font_size: "m", + margin_bottom: 2, + } %> + +
    + <% end %> +
    + <% end %> +
    + <% end %> +
    diff --git a/app/views/content_items/service_manual_topic.html.erb b/app/views/content_items/service_manual_topic.html.erb new file mode 100644 index 000000000..3dec1f99a --- /dev/null +++ b/app/views/content_items/service_manual_topic.html.erb @@ -0,0 +1,92 @@ +<%= content_for :title, "#{@content_item.title} - Service Manual" %> + +<% content_for :phase_message do %> + <%= render 'shared/custom_phase_message', phase: @content_item.phase %> +<% end %> +
    + + <%= render "govuk_publishing_components/components/breadcrumbs", { + breadcrumbs: @content_item.breadcrumbs, + collapse_on_mobile: true + } %> + +
    +
    + <%= render "govuk_publishing_components/components/title", { + title: @content_item.title, + margin_bottom: 7 + } %> +

    + <%= @content_item.description %> +

    +
    +
    + +
    +
    + <% if @content_item.display_as_accordion? %> + <% items = @content_item.accordion_content %> + <%= render "govuk_publishing_components/components/accordion", { + ga4_tracking: true, + items: items, + } %> + <% else %> + <% @content_item.groups.each_with_index do |link_group| %> + <% if link_group.name.present? %> +
    + <%= render "govuk_publishing_components/components/heading", { + text: link_group.name, + font_size: "m", + margin_bottom: 1, + id: link_group.name.parameterize, + } %> + <% if link_group.description.present? %> +

    <%= link_group.description %>

    + <% end %> +
    + <% end %> + <% + link_items = [] + + link_group.linked_items.each do |linked_item| + link_items << sanitize(link_to linked_item.label, linked_item.href, class: 'govuk-link') + end + %> + <%= render "govuk_publishing_components/components/list", { + items: link_items + } %> + <% end %> + <% end %> +
    + +
    + +
    + +
    +
    \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index db6cdc318..db9782442 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -23,7 +23,7 @@ <%= csrf_meta_tags %> <%= render 'govuk_publishing_components/components/meta_tags', content_item: @content_item.content_item %> - <% if @content_item.description %> + <% if @content_item.description.present? %> <% end %> @@ -31,8 +31,20 @@
    - <% if @content_item.show_phase_banner? %> - <%= render 'govuk_publishing_components/components/phase_banner', phase: @content_item.phase %> + + <% if @content_item.show_phase_banner? || @content_item.show_service_manual_phase_banner? %> +
    +
    +
    + <% if @content_item.show_phase_banner? %> + <%= render 'govuk_publishing_components/components/phase_banner', phase: @content_item.phase %> + <% end %> + <% if @content_item.show_service_manual_phase_banner? %> + <%= render_phase_label @content_item, content_for(:phase_message) %> + <% end %> +
    +
    +
    <% end %> <% unless @do_not_show_breadcrumbs %> diff --git a/app/views/shared/_change_history.html.erb b/app/views/shared/_change_history.html.erb new file mode 100644 index 000000000..333f992f9 --- /dev/null +++ b/app/views/shared/_change_history.html.erb @@ -0,0 +1,12 @@ +<% + time = change.public_timestamp + compressed |= false + time_class = compressed ? 'govuk-heading-s' : 'govuk-heading-m' + time_class << ' govuk-!-margin-bottom-1' +%> + +

    + <%= change.note.strip %> +

    diff --git a/app/views/shared/_custom_phase_message.html.erb b/app/views/shared/_custom_phase_message.html.erb new file mode 100644 index 000000000..8d8e66740 --- /dev/null +++ b/app/views/shared/_custom_phase_message.html.erb @@ -0,0 +1 @@ +<%= link_to "Contact the Service Manual team", "/service-manual/communities/contact-the-service-manual-and-service-standard-team", class: "govuk-link" %> if you have feedback, questions or suggestions. diff --git a/app/views/shared/_email_signup.html.erb b/app/views/shared/_email_signup.html.erb new file mode 100644 index 000000000..b0c28fac6 --- /dev/null +++ b/app/views/shared/_email_signup.html.erb @@ -0,0 +1,11 @@ + diff --git a/config/locales/ar.yml b/config/locales/ar.yml index b6bde24cc..a0516193f 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -775,6 +775,9 @@ ar: proposed_date: الإصدار المقترح reason_for_change: سبب التغيير release_date: تاريخ الإصدار + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: ينصح FCO بعدم السفر إلى بعض أجزاء البلد إلا للضرورة. diff --git a/config/locales/az.yml b/config/locales/az.yml index c085db856..e604b3a51 100644 --- a/config/locales/az.yml +++ b/config/locales/az.yml @@ -475,6 +475,9 @@ az: proposed_date: Təklif edilmiş buraxılış reason_for_change: Dəyişikliyin səbəbi release_date: Buraxılış tarixi + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO ölkənin bölgələrinə zəruri səyahət istisna olmaqla heç birini məsləhət görmürlər. diff --git a/config/locales/be.yml b/config/locales/be.yml index c041a0ed8..a4dfc160c 100644 --- a/config/locales/be.yml +++ b/config/locales/be.yml @@ -625,6 +625,9 @@ be: proposed_date: Прапанаваны рэліз reason_for_change: Прычына змены release_date: Дата рэлізу + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Міністэрства замежных спраў і па справах Садружнасці FCO раяць устрымацца ад усіх падарожжаў, апроч самых патрэбных, у некаторыя раёны краіны. diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 45a607567..c4ebad059 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -475,6 +475,9 @@ bg: proposed_date: Предложено издание reason_for_change: Причина за промяната release_date: Дата на издаване + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO препоръчва да не се пътува до някои части на страната, освен ако това не е необходимо. diff --git a/config/locales/bn.yml b/config/locales/bn.yml index 432aee2a5..12bbfd7a6 100644 --- a/config/locales/bn.yml +++ b/config/locales/bn.yml @@ -475,6 +475,9 @@ bn: proposed_date: প্রস্তাবিত প্রকাশ reason_for_change: পরিবর্তন করার কারণ release_date: প্রকাশের তারিখ + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO দেশের কিছু অংশে শুধু প্রয়োজনীয় ভ্রমণ ছাড়া সকল ধরনের ভ্রমণের বিরুদ্ধে পরামর্শ দিয়েছে। diff --git a/config/locales/cs.yml b/config/locales/cs.yml index d59ba8e0f..df484f0a8 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -550,6 +550,9 @@ cs: proposed_date: Navrhované uvolnění reason_for_change: Důvod změny release_date: Datum vydání + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Úřad pro zahraniční věci, společenství národů a rozvoj nedoporučuje cestovat do některých částí země, s výjimkou nezbytných. diff --git a/config/locales/cy.yml b/config/locales/cy.yml index a7c05b268..0470575c6 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -775,6 +775,9 @@ cy: proposed_date: Rhyddhau arfaethedig reason_for_change: Rheswm dros y newid release_date: Dyddiad rhyddhau + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Mae'r Swyddfa Dramor, y Gymanwlad a Datblygu yn cynghori yn erbyn pob taith, oni fo'n hanfodol, i rannau o'r wlad. diff --git a/config/locales/da.yml b/config/locales/da.yml index 97c566ca5..ca18c214f 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -487,6 +487,9 @@ da: proposed_date: Foreslået udgivelse reason_for_change: Årsag til ændring release_date: Udgivelsesdato + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Den FCO fraråde alle, men væsentlige rejser til dele af landet. diff --git a/config/locales/de.yml b/config/locales/de.yml index 240e00013..9f1e43902 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -475,6 +475,9 @@ de: proposed_date: Geplante Veröffentlichung reason_for_change: Grund für die Änderung release_date: Veröffentlichungsdatum + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Das FCO rät von allen Reisen in Teile des Landes ab, die nicht unbedingt erforderlich sind. diff --git a/config/locales/dr.yml b/config/locales/dr.yml index 4547faf12..d94ed4712 100644 --- a/config/locales/dr.yml +++ b/config/locales/dr.yml @@ -478,6 +478,9 @@ dr: proposed_date: انتشار پیشنهاد شده reason_for_change: دلیل تغییر release_date: تاریخ انتشار + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: اینFCO در مورد هر گونه سفر ضروری به تمام نقاط کشور توصیه مینماید. diff --git a/config/locales/el.yml b/config/locales/el.yml index 06a6dc4e1..0d30e91c9 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -475,6 +475,9 @@ el: proposed_date: Προτεινόμενη κυκλοφορία reason_for_change: Λόγος για την αλλαγή release_date: Ημερομηνία κυκλοφορίας + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Το FCO σας συμβουλεύει να αποφύγετε τα ταξίδια, πλην των απαραίτητων, σε μέρη της χώρας. diff --git a/config/locales/en.yml b/config/locales/en.yml index 83335a780..609be2ce8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -486,6 +486,9 @@ en: still_current_at: Still current at summary: Summary updated: Updated + time: + formats: + short_ordinal: '%e %B %Y' unpublishing: page_title: No longer available summary: The information on this page has been removed because it was published in error. diff --git a/config/locales/es-419.yml b/config/locales/es-419.yml index 599f8442d..3f5780fa2 100644 --- a/config/locales/es-419.yml +++ b/config/locales/es-419.yml @@ -475,6 +475,9 @@ es-419: proposed_date: Propuesta de publicación reason_for_change: Motivo de la modificación release_date: Fecha de publicación + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: El FCO desaconseja todos los viajes que no sean imprescindibles a algunas zonas del país. diff --git a/config/locales/es.yml b/config/locales/es.yml index 949960d69..8fb9c471e 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -475,6 +475,9 @@ es: proposed_date: Publicación propuesta reason_for_change: Motivo del cambio release_date: Fecha de lanzamiento + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: La FCO aconseja que no se viaje a partes del país, salvo viajes esenciales. diff --git a/config/locales/et.yml b/config/locales/et.yml index 60d5a7799..9ed0cb6b1 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -475,6 +475,9 @@ et: proposed_date: Kavandatud avaldamine reason_for_change: Põhjus muutumiseks release_date: Väljaande kuupäev + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO soovitab mitte reisida riigi osadesse muidu kui hädavajadusel. diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 4a42df909..b15e8f86c 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -475,6 +475,9 @@ fa: proposed_date: انتاشر پیشنهادی reason_for_change: دلیل تغییر release_date: تاریخ انتشار + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: وزارت امور خارجه بریتانیا (FCO) توصیه می‌کند از سفرهای غیر ضروری به بخش‌هایی از این کشور خودداری کنید. diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 52fe5e76e..da99a9adf 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -475,6 +475,9 @@ fi: proposed_date: Ehdotettu julkaisu reason_for_change: Muutoksen syy release_date: Julkaisupäivä + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO kehottaa välttämään kaikkia muita kuin välttämättömiä matkoja osissa maata. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 8cb16bf68..bbfdd4e94 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -475,6 +475,9 @@ fr: proposed_date: Version proposée reason_for_change: Raison du changement release_date: Date de sortie + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Le Bureau des affaires étrangères et du Commonwealth (FCO) déconseille tout voyage dans certaines parties du pays, à l'exception de ceux qui sont indispensables. diff --git a/config/locales/gd.yml b/config/locales/gd.yml index facd59b54..adee072b3 100644 --- a/config/locales/gd.yml +++ b/config/locales/gd.yml @@ -625,6 +625,9 @@ gd: proposed_date: Scaipeadh beartaithe reason_for_change: Cúis leis an athrú release_date: Dáta foilsithe + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Molann an FCO gach taisteal ach an taisteal is riachtanaí chuig áiteanna áirithe sa tír a sheachaint. diff --git a/config/locales/gu.yml b/config/locales/gu.yml index d0870ab27..e3ac59511 100644 --- a/config/locales/gu.yml +++ b/config/locales/gu.yml @@ -475,6 +475,9 @@ gu: proposed_date: પ્રસ્તાવિત રીલીઝ reason_for_change: પરીવર્તન માટેનું કારણ release_date: રીલીઝ તારીખ + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: વિદેશી - કોમનવેલ્થ અને વિકાસ કચેરી જરૂરી મુસાફરી સિવાય દેશના કેટલાક ભાગોમાં પ્રવાસ ન કરવાની સલાહ આપે છે. diff --git a/config/locales/he.yml b/config/locales/he.yml index aab341046..b5f3fe1cc 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -475,6 +475,9 @@ he: proposed_date: הצעה לפרסם reason_for_change: סיבה לשינוי release_date: תאריך פרסם + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: ' FCO להמליץ על כל נסיעות מלבד חיוניות לחלקי הארץ.' diff --git a/config/locales/hi.yml b/config/locales/hi.yml index fe205a8b7..5ef227a7a 100644 --- a/config/locales/hi.yml +++ b/config/locales/hi.yml @@ -475,6 +475,9 @@ hi: proposed_date: प्रस्तावित विज्ञप्ति reason_for_change: बदलाव के कारण release_date: जारी करने की तारीख + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO देश के कुछ हिस्सों में अनिवार्य यात्रा को छोड़कर बाकी सबके विरुद्ध सलाह देता है। diff --git a/config/locales/hr.yml b/config/locales/hr.yml index 8f51392f7..3e18b29b2 100644 --- a/config/locales/hr.yml +++ b/config/locales/hr.yml @@ -550,6 +550,9 @@ hr: proposed_date: Predloženo puštanje na slobodu reason_for_change: Razlog za promjenu release_date: Datum izlaska + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO savjetuje protiv svih, osim bitnih putovanja u dijelova zemlje. diff --git a/config/locales/hu.yml b/config/locales/hu.yml index e23858aa5..84783b4fc 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -475,6 +475,9 @@ hu: proposed_date: Javasolt kiadás reason_for_change: Változás oka release_date: Kiadás időpontja + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: A FCO azt javasolja, hogy az ország bizonyos részeire csak feltétlenül szükséges esetben utazzanak. diff --git a/config/locales/hy.yml b/config/locales/hy.yml index 6551801cd..a1874d342 100644 --- a/config/locales/hy.yml +++ b/config/locales/hy.yml @@ -475,6 +475,9 @@ hy: proposed_date: Առաջարկվող հրապարակում reason_for_change: Փոփոխության պատճառը release_date: Հրապարակման ամսաթիվ + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO-ը խորհուրդ է տալիս չմեկնել տվյալ երկրի ամբողջ տարածք, բացառությամբ էական նշանակություն ունեցող հատվածների։ diff --git a/config/locales/id.yml b/config/locales/id.yml index 9dfcd6362..84f7a8dc1 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -400,6 +400,9 @@ id: proposed_date: Rilis yang diusulkan reason_for_change: Alasan perubahan release_date: Tanggal rilis + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: The FCO melarang semua perjalanan yang tidak esensial ke bagian-bagian dari negara itu. diff --git a/config/locales/is.yml b/config/locales/is.yml index b85c68060..c5d03015c 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -475,6 +475,9 @@ is: proposed_date: Fyrirhuguð útgáfa reason_for_change: Ástæða fyrir breytingu release_date: Útgáfudagsetning + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO ráðleggur gegn öllum ferðalögum til hluta landsins nema þeim allra nauðsynlegustu. diff --git a/config/locales/it.yml b/config/locales/it.yml index b258224e2..2595d5c03 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -475,6 +475,9 @@ it: proposed_date: Rilascio proposto reason_for_change: Motivo della modifica release_date: Data di rilascio + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: L’FCO sconsiglia tutti i viaggi tranne quelli essenziali in alcune parti del paese. diff --git a/config/locales/ja.yml b/config/locales/ja.yml index be98eb1ad..c16f0dad1 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -400,6 +400,9 @@ ja: proposed_date: 予定されている発表 reason_for_change: 変更理由 release_date: 発表日程 + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO は、国内一部地域への必要不可欠ものを除くすべての移動を控えるよう勧告しています。 diff --git a/config/locales/ka.yml b/config/locales/ka.yml index 8ab9836e3..5b6999ef3 100644 --- a/config/locales/ka.yml +++ b/config/locales/ka.yml @@ -475,6 +475,9 @@ ka: proposed_date: შეთავაზებული გაცემა reason_for_change: შეცვლის მიზეზი release_date: გამოშვების თარიღი + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: The FCO რჩევა ყველას წინააღმდეგ, გარდა ესენციური მოგზაურობებისა ქვეყნის ზოგიერთ ნაწილებში. diff --git a/config/locales/kk.yml b/config/locales/kk.yml index e1240d17d..68b1c29f4 100644 --- a/config/locales/kk.yml +++ b/config/locales/kk.yml @@ -475,6 +475,9 @@ kk: proposed_date: Ұсынылған шығарылым reason_for_change: Өзгеріс себебі release_date: Шығарылым күні + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO маңыздысынан басқа елдің барлық аймақтарына барудан бас тартуға кеңес береді. diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 797433488..83d2a68c2 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -400,6 +400,9 @@ ko: proposed_date: 제안 공개 reason_for_change: 변경 이유 release_date: 공개 날짜 + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO 는 해당 국가의 각지를 여행하지 말라고 권고합니다. diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 63e591efd..b36f4d8ba 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -550,6 +550,9 @@ lt: proposed_date: Siūloma paskelbimo data reason_for_change: Pakeitimo priežastis release_date: Paskelbimo data + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO pataria keliauti į skirtingas šalies vietas tik būtinais tikslais. diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 7651b8e67..89776f5f4 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -475,6 +475,9 @@ lv: proposed_date: Paredzētā publiskošana reason_for_change: Izmaiņu iemesls release_date: Publiskošanas datums + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO iesaka izvairīties no jebkādas ceļošanas, izņemot neatliekamus braucienus uz noteiktām valsts daļām. diff --git a/config/locales/ms.yml b/config/locales/ms.yml index d04ca9c21..0c3760d09 100644 --- a/config/locales/ms.yml +++ b/config/locales/ms.yml @@ -400,6 +400,9 @@ ms: proposed_date: Tarikh yang dicadangkan reason_for_change: Sebab bagi penukaran release_date: Tarikh hebahan + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: The FCO menasihati supaya tidak membuat perjalanan kecuali yang perlu sahaja ke bahagian-bahagian tertentu negara ini. diff --git a/config/locales/mt.yml b/config/locales/mt.yml index baa024736..65c2abc83 100644 --- a/config/locales/mt.yml +++ b/config/locales/mt.yml @@ -625,6 +625,9 @@ mt: proposed_date: Rilaxx propost reason_for_change: Raġuni għall-bidla release_date: Data tar-rilaxx + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Il- FCO jirrakkomanda li ma jsiru l-ebda vjaġġi ħlief dawk essenzjali. diff --git a/config/locales/ne.yml b/config/locales/ne.yml index 942bc5222..0672e7f6f 100644 --- a/config/locales/ne.yml +++ b/config/locales/ne.yml @@ -475,6 +475,9 @@ ne: proposed_date: प्रस्तावित बिज्ञप्ति reason_for_change: परिवर्तनको कारण release_date: बिज्ञप्ति मिति + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: विदेश, राष्ट्रमंडल र विकास कार्यालय ले जरूरी यात्रा बाहेक देशको केहि भागमा सबै यात्रा बिरूद्ध सल्लाह दिएको छ। diff --git a/config/locales/nl.yml b/config/locales/nl.yml index de4a7e2f2..edb8df3b1 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -475,6 +475,9 @@ nl: proposed_date: Voorgestelde vrijgave reason_for_change: Reden voor wijziging release_date: Publicatiedatum + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Het FCO raadt alle, behalve essentiële, reizen naar delen van het land af. diff --git a/config/locales/no.yml b/config/locales/no.yml index 5f48bc2cc..6fc870953 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -475,6 +475,9 @@ proposed_date: Foreslått utgivelse reason_for_change: Årsak til endring release_date: Utgivelsesdato + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO fraråder alle reiser, unntatt viktige reiser, til deler av landet. diff --git a/config/locales/pa-pk.yml b/config/locales/pa-pk.yml index d7c68139b..f7750b612 100644 --- a/config/locales/pa-pk.yml +++ b/config/locales/pa-pk.yml @@ -475,6 +475,9 @@ pa-pk: proposed_date: تجویز کر کے جاری کیتا گیا reason_for_change: بدلن دی وجہ release_date: جاری کرن دی تاریخ + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: ایہFCO مُلک دے سارے علاقیاں وچ ضروری سفر کرن لئی کیتی گئی نصحیت۔ diff --git a/config/locales/pa.yml b/config/locales/pa.yml index bd004d3c0..56f1f6ae1 100644 --- a/config/locales/pa.yml +++ b/config/locales/pa.yml @@ -475,6 +475,9 @@ pa: proposed_date: ਪ੍ਰਸਤਾਵਿਤ ਰਿਹਾਈ reason_for_change: ਤਬਦੀਲੀ ਦਾ ਕਾਰਨ release_date: ਰਿਹਾਈ ਤਾਰੀਖ + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO ਦੇਸ਼ ਦੇ ਕੁਝ ਹਿੱਸਿਆਂ ਦੀ ਜ਼ਰੂਰੀ ਯਾਤਰਾ ਨੂੰ ਛੱਡ ਕੇ ਬਾਕੀ ਦੇ ਵਿਰੁੱਧ ਸਲਾਹ ਦਿੰਦਾ ਹੈ। diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 515c0d831..d43326c62 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -625,6 +625,9 @@ pl: proposed_date: Proponowana publikacja reason_for_change: Powód zmiany release_date: Data publikacji + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO odradza wszelkie podróże do niektórych części kraju, z wyjątkiem tych niezbędnych. diff --git a/config/locales/ps.yml b/config/locales/ps.yml index 90c78a793..64c52823f 100644 --- a/config/locales/ps.yml +++ b/config/locales/ps.yml @@ -475,6 +475,9 @@ ps: proposed_date: وړاندیز شوی خوشې کول reason_for_change: د بدلون لپاره دلیل release_date: دخپریدو نیټه + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: The FCO د هیواد برخو ته د لازمي سفر پرته ټولو ته مشوره ورکوي. diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 89aed5a41..74146b70e 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -475,6 +475,9 @@ pt: proposed_date: Divulgação proposta reason_for_change: Motivo de alteração release_date: Data de divulgação + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: O FCO desaconselha todas as viagens, exceto as essenciais, a algumas zonas do país. diff --git a/config/locales/ro.yml b/config/locales/ro.yml index f2baf43f8..2d7ec9dff 100644 --- a/config/locales/ro.yml +++ b/config/locales/ro.yml @@ -550,6 +550,9 @@ ro: proposed_date: Data propusă reason_for_change: Motivul schimbării release_date: Data publicării + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO nu recomandă călătoriile neesențiale în anumite părți ale țării. diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 1b5e63ee4..b7cb3452d 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -625,6 +625,9 @@ ru: proposed_date: Предложенный выпуск reason_for_change: Причина изменения release_date: Дата выпуска + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: The FCO Все поездки не реккомендуются, за исключением необходимых в части страны. diff --git a/config/locales/si.yml b/config/locales/si.yml index 1b30fe962..88d724c38 100644 --- a/config/locales/si.yml +++ b/config/locales/si.yml @@ -475,6 +475,9 @@ si: proposed_date: යෝජිත නිකුතුව reason_for_change: වෙනස් වීමට හේතුව release_date: නිකුත් කිරීමේ දිනය + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO රටේ කොටස් වලට සියලු සංචාරවලට එරෙහිව උපදෙස් ලබා දෙන නමුත් අත්‍යවශ්‍ය සංවාරයක යෙදිය හැකිය. diff --git a/config/locales/sk.yml b/config/locales/sk.yml index cffefa707..fb96ade33 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -550,6 +550,9 @@ sk: proposed_date: Navrhované vydanie reason_for_change: Dôvod zmeny release_date: Dátum vydania + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Ministerstvo zahraničných vecí a Commonwealthu FCO neodporúča cestovať do niektorých častí krajiny okrem nevyhnutných prípadov. diff --git a/config/locales/sl.yml b/config/locales/sl.yml index cd0136c96..b4bace4f9 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -625,6 +625,9 @@ sl: proposed_date: Predlagana objava reason_for_change: Razlog za spremembo release_date: Datum objave + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO odsvetuje vsa nenujna potovanja v dele države. diff --git a/config/locales/so.yml b/config/locales/so.yml index b574b39d1..d2f5da1ef 100644 --- a/config/locales/so.yml +++ b/config/locales/so.yml @@ -475,6 +475,9 @@ so: proposed_date: Ku dhawaaqis dib u dhacday reason_for_change: Sababta loo badalay release_date: Taariikhda sii daynta + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: The FCO talo dhamaan kasoo wada horjeeda lakin socdaal muhiim ah oo qaybaha dalka oo dhan ah. diff --git a/config/locales/sq.yml b/config/locales/sq.yml index 332249bb2..c86c6a70f 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -475,6 +475,9 @@ sq: proposed_date: Publikimi i propozuar reason_for_change: Arsye për të ndryshuar release_date: Data e publikimit + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: The FCO këshillon kundër të gjitha udhëtimeve përveçse thelbësore në pjesë të vendit. diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 26a468ea7..3715fc736 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -550,6 +550,9 @@ sr: proposed_date: Predloženi datum objavljivanja reason_for_change: Razlog za izmenu release_date: Datum objavljivanja + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO ne preporučuje putovanja koja nisu esencijalna za delove zemlje. diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 366a2649d..1d570d02a 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -475,6 +475,9 @@ sv: proposed_date: Föreslagna offentliggöranden reason_for_change: Skäl till ändringen release_date: Datum för offentliggörandet + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO avråder från alla utom nödvändiga resor till delar av landet. diff --git a/config/locales/sw.yml b/config/locales/sw.yml index bca1d1ef8..6e6df2585 100644 --- a/config/locales/sw.yml +++ b/config/locales/sw.yml @@ -475,6 +475,9 @@ sw: proposed_date: Tarehe ya kutangazwa inayopendekezwa reason_for_change: Sababu ya kubadilishwa release_date: Tarehe ya kutangazwa + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Ushauri wa FCO dhidi ya usafiri wote isipokuwa usafiri muhimu katika sehemu fulani za nchi. diff --git a/config/locales/ta.yml b/config/locales/ta.yml index f6fec7c04..7a8375f0c 100644 --- a/config/locales/ta.yml +++ b/config/locales/ta.yml @@ -475,6 +475,9 @@ ta: proposed_date: முன்மொழியப்பட்ட வெளியீடு reason_for_change: மாற்றத்துக்கான காரணம் release_date: வெளியீட்டுத் தேதி + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: அயல்நாடு, காமன்வெல்த் அலுவலகம் நாட்டின் பகுதிகளுக்கு அத்தியாவசிய பயணம் செய்வது குறித்து அனைவருக்குமான ஆலோசனை தருகிறது. diff --git a/config/locales/th.yml b/config/locales/th.yml index 2c91cabc5..583c9ba64 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -400,6 +400,9 @@ th: proposed_date: วันเผยแพร่ที่เสนอ reason_for_change: สาเหตุที่เปลี่ยน release_date: วันเผยแพร่ + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO ไม่แนะนำให้เดินทางไปยังบางส่วนของประเทศ นอกจากมีเหตุจำเป็น diff --git a/config/locales/tk.yml b/config/locales/tk.yml index 2eb09ae36..2cd1ab1f5 100644 --- a/config/locales/tk.yml +++ b/config/locales/tk.yml @@ -475,6 +475,9 @@ tk: proposed_date: Teklip edilen goýberiliş reason_for_change: Üýtgedilmeginiň sebäbi release_date: Goýberiliş senesi + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO ýurduň käbir ýerlerine zerur syýahatdan başga-da maslahat berýär. diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 40698bbf3..b4000ed11 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -475,6 +475,9 @@ tr: proposed_date: Önerilen açıklama reason_for_change: Değişiklik gerekçesi release_date: Açıklama tarihi + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO ülkenin bazı bölgelerine zorunlu seyahatler haricinde seyahat edilmemesini tavsiye etmektedir. diff --git a/config/locales/uk.yml b/config/locales/uk.yml index fe0fdbfe0..284802701 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -625,6 +625,9 @@ uk: proposed_date: Пропонований випуск reason_for_change: Причина змін release_date: Дата виходу + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: Міністерство закордонних справ(FCO) забороняє будь-які поїздки, крім необхідних, до певних областей країни. diff --git a/config/locales/ur.yml b/config/locales/ur.yml index aa9478cb4..90de9c1ce 100644 --- a/config/locales/ur.yml +++ b/config/locales/ur.yml @@ -475,6 +475,9 @@ ur: proposed_date: تجویز کردہ اجراء reason_for_change: تبدیلی کی وجہ release_date: اجراء کی تاریخ + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO سوائے ملک کے کچھ حصوں میں ضروری سفر کرنے کے ہر قسم کے سفر کے خلاف مشاورت فراہم کرتا ہے۔ diff --git a/config/locales/uz.yml b/config/locales/uz.yml index a5fa2ed6c..a1dbd4ea4 100644 --- a/config/locales/uz.yml +++ b/config/locales/uz.yml @@ -475,6 +475,9 @@ uz: proposed_date: Режалаштирилган нашр reason_for_change: Ўзгариш киритилиш сабаблари release_date: Нашр санаси + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: The Ташқи ишлар ва Ҳамдўстлик ишлари бўйича вазирлиги ўта заруратсиз мамлакатнинг муайян минтақаларига сафарларни амалга оширишни тавсия этмайди. diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 9010bacaa..5ea19d0c6 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -400,6 +400,9 @@ vi: proposed_date: Thông tin được đề nghị phát hành reason_for_change: Lý do thay đổi release_date: Ngày phát hành + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO khuyến cáo chỉ nên đi đến một số nơi ở quốc gia này khi thật cần thiết. diff --git a/config/locales/yi.yml b/config/locales/yi.yml index 69ed6e7f7..2df72c22d 100644 --- a/config/locales/yi.yml +++ b/config/locales/yi.yml @@ -475,6 +475,9 @@ yi: proposed_date: reason_for_change: release_date: + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: diff --git a/config/locales/zh-hk.yml b/config/locales/zh-hk.yml index c6a3d875a..dfcea1058 100644 --- a/config/locales/zh-hk.yml +++ b/config/locales/zh-hk.yml @@ -400,6 +400,9 @@ zh-hk: proposed_date: 擬議發佈 reason_for_change: 更改原因 release_date: 發佈日期 + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: FCO 建議,如非必要,請勿前往該國部份地區。 diff --git a/config/locales/zh-tw.yml b/config/locales/zh-tw.yml index b7e317c84..2dac28915 100644 --- a/config/locales/zh-tw.yml +++ b/config/locales/zh-tw.yml @@ -400,6 +400,9 @@ zh-tw: proposed_date: 建議發佈 reason_for_change: 變更理由 release_date: 發佈日期 + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: 外交和英聯邦事務部 建議非必要的旅行,不要前往該國部分地區。 diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 1ba2e6ac8..c8ca1e275 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -400,6 +400,9 @@ zh: proposed_date: 拟议发布 reason_for_change: 变更原因 release_date: 发布日期 + time: + formats: + short_ordinal: '%e %B %Y' travel_advice: alert_status: avoid_all_but_essential_travel_to_parts_html: 'FCO 建议除非必要,否则不要前往该国部分地区。 ' diff --git a/config/routes.rb b/config/routes.rb index c70637623..66b229bb7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,6 +18,12 @@ get "/government/get-involved" => "get_involved#show" + get "/service-manual/search", + to: redirect { |_, request| + query = request.query_parameters.merge(filter_manual: "/service-manual").to_query + "/search?#{query}" + } + get "*path/:variant" => "content_items#show", constraints: { variant: /print/, diff --git a/test/controllers/content_items_controller_test.rb b/test/controllers/content_items_controller_test.rb index 69993e8cc..3335915df 100644 --- a/test/controllers/content_items_controller_test.rb +++ b/test/controllers/content_items_controller_test.rb @@ -402,6 +402,31 @@ class ContentItemsControllerTest < ActionController::TestCase end end + test "renders service_manual_guides" do + content_item = content_store_has_schema_example("service_manual_guide", "service_manual_guide") + + get :show, params: { path: path_for(content_item) } + assert_response :success + assert_equal content_item["title"], assigns[:content_item].title + end + + test "guides should tell slimmer to scope search results to the manual" do + content_item = content_store_has_schema_example("service_manual_guide", "service_manual_guide") + + get :show, params: { path: path_for(content_item) } + assert_equal( + { filter_manual: "/service-manual" }.to_json, + @response.headers[Slimmer::Headers::SEARCH_PARAMETERS_HEADER], + ) + end + + test "the homepage should tell slimmer not to include a search box in the header" do + content_item = content_store_has_schema_example("service_manual_homepage", "service_manual_homepage") + + get :show, params: { path: path_for(content_item) } + assert_equal "true", @response.headers[Slimmer::Headers::REMOVE_SEARCH_HEADER] + end + def path_for(content_item, locale = nil) base_path = content_item["base_path"].sub(/^\//, "") base_path.gsub!(/\.#{locale}$/, "") if locale diff --git a/test/helpers/service_manual_topic_helper_test.rb b/test/helpers/service_manual_topic_helper_test.rb new file mode 100644 index 000000000..f184e25fa --- /dev/null +++ b/test/helpers/service_manual_topic_helper_test.rb @@ -0,0 +1,18 @@ +require "test_helper" + +class ServiceManualTopicHelperTest < ActionView::TestCase + test "topic_related_communities_title invites you to join the only related community" do + communities = [{ title: "Agile delivery community" }] + + assert_equal "Join the Agile delivery community", + topic_related_communities_title(communities) + end + + test "topic_related_communities_title invites you to join the community as a " \ + "whole if there are multiple communities" do + communities = [{ title: "Agile delivery community" }, { title: "User research community" }] + + assert_equal "Join the community", + topic_related_communities_title(communities) + end +end diff --git a/test/integration/guide_test.rb b/test/integration/guide_test.rb index f7ed79747..65ba7ea82 100644 --- a/test/integration/guide_test.rb +++ b/test/integration/guide_test.rb @@ -17,7 +17,7 @@ class GuideTest < ActionDispatch::IntegrationTest end test "draft access tokens are appended to part links within navigation" do - setup_and_visit_content_item("guide", "?token=some_token") + setup_and_visit_content_item_with_params("guide", "?token=some_token") assert page.has_css?('.gem-c-contents-list a[href$="?token=some_token"]') end diff --git a/test/integration/service_manual_guide_test.rb b/test/integration/service_manual_guide_test.rb new file mode 100644 index 000000000..6b064f81e --- /dev/null +++ b/test/integration/service_manual_guide_test.rb @@ -0,0 +1,123 @@ +require "test_helper" + +class ServiceManualGuideTest < ActionDispatch::IntegrationTest + test "shows the time it was saved if it hasn't been published yet" do + skip("time_ago_in_words helper is failing to fetch translations in the test env only") + now = "2015-10-10T09:00:00+00:00" + last_saved_at = "2015-10-10T08:55:00+00:00" + + travel_to(now) do + setup_and_visit_content_item("service_manual_guide", "updated_at" => last_saved_at) + within(".app-change-history") do + assert page.has_content?("5 minutes ago") + end + end + end + + test "shows the time it was published if it has been published" do + skip("time_ago_in_words helper is failing to fetch translations in the test env only") + travel_to Time.zone.local(2015, 10, 10, 0, 0, 0) do + setup_and_visit_content_item("service_manual_guide") + + within(".app-change-history") do + assert page.has_content?("about 16 hours ago") + end + end + end + + test "service manual guide shows content owners" do + setup_and_visit_content_item("service_manual_guide") + + within(".app-metadata--heading") do + assert page.has_link?("Agile delivery community") + end + end + + test "the breadcrumb contains the topic" do + setup_and_visit_content_item("service_manual_guide") + + within(".gem-c-breadcrumbs") do + assert page.has_link?("Service manual") + assert page.has_link?("Agile") + end + end + + test "service manual guide does not show published by" do + setup_and_visit_content_item("service_manual_guide_community") + + within(".gem-c-metadata") do + assert_not page.has_content?("Published by") + end + end + + test "displays the description for a point" do + setup_and_visit_content_item("point_page") + + within(".app-page-header__summary") do + assert page.has_content?("Research to develop a deep knowledge of who the service users are") + end + end + + test "does not display the description for a normal guide" do + setup_and_visit_content_item("service_manual_guide") + + assert_not page.has_css?(".app-page-header__summary") + end + + test "displays a link to give feedback" do + setup_and_visit_content_item("service_manual_guide") + + assert page.has_link?("Give feedback about this page") + end + + test "displays the published date of the most recent change" do + setup_and_visit_content_item("service_manual_guide") + + within(".app-change-history") do + assert_text "Last update:\n9 October 2015" + end + end + + test "displays the most recent change history for a guide" do + setup_and_visit_content_item("service_manual_guide") + + within(".app-change-history") do + assert page.has_content? "This is our latest change" + end + end + + test "displays the change history for a guide" do + setup_and_visit_content_item("service_manual_guide") + + within(".app-change-history__past") do + assert page.has_content? "This is another change" + assert page.has_content? "Guidance first published" + end + end + + test "omits the previous history if there is only one change" do + setup_and_visit_content_item("service_manual_guide", + "details" => { + "change_history" => [ + { + "public_timestamp" => "2015-09-01T08:17:10+00:00", + "note" => "Guidance first published", + }, + ], + }) + + assert_not page.has_content? "Show all page updates" + assert_not page.has_css? ".app-change-history__past" + end + + test "omits the latest change and previous change if the guide has no history" do + setup_and_visit_content_item("service_manual_guide", + "details" => { + "change_history" => [], + }) + + assert_not page.has_content? "Last update:" + assert_not page.has_content? "Show all page updates" + assert_not page.has_css? ".app-change-history__past" + end +end diff --git a/test/integration/service_manual_homepage_test.rb b/test/integration/service_manual_homepage_test.rb new file mode 100644 index 000000000..f464aed01 --- /dev/null +++ b/test/integration/service_manual_homepage_test.rb @@ -0,0 +1,53 @@ +require "test_helper" + +class ServiceManualHomepageTest < ActionDispatch::IntegrationTest + test "the homepage displays the introductory text" do + setup_and_visit_content_item("service_manual_homepage") + + assert page.has_content? <<~TEXT.squish + Helping teams to create and run great public services that meet the + Service Standard. + TEXT + end + + test "the homepage includes a feedback link" do + setup_and_visit_content_item("service_manual_homepage") + + assert page.has_content?( + "Contact the Service Manual team with any comments or questions.", + ) + + assert page.has_link? "Contact the Service Manual team", href: "/contact/govuk" + end + + test "the homepage includes the titles and descriptions of associated topics" do + setup_and_visit_content_item("service_manual_homepage") + + assert page.has_content? "Agile delivery\nHow to work in an agile way: principles, tools and governance." + assert page.has_link? "Agile delivery", href: "/service-manual/agile-delivery" + end + + test "the homepage includes a link to the service standard" do + setup_and_visit_content_item("service_manual_homepage") + + assert page.has_content? <<~TEXT.squish + The Service Standard provides the principles of building a good service. + This manual explains what teams can do to build great services that will + meet the standard. + TEXT + + assert page.has_link? "Service Standard", href: "/service-manual/service-standard" + end + + test "the homepage includes a link to the communities of practise" do + setup_and_visit_content_item("service_manual_homepage") + + assert page.has_content? <<~TEXT.squish + You can view the communities of practice to find more learning + resources, see who has written the guidance in the manual and connect + with digital people like you from across government. + TEXT + + assert page.has_link? "communities of practice", href: "/service-manual/communities" + end +end diff --git a/test/integration/service_manual_phase_label_test.rb b/test/integration/service_manual_phase_label_test.rb new file mode 100644 index 000000000..d321b3f03 --- /dev/null +++ b/test/integration/service_manual_phase_label_test.rb @@ -0,0 +1,43 @@ +require "test_helper" + +class ServiceManualPhaseLabelTest < ActionDispatch::IntegrationTest + test "renders custom message for service manual guide pages" do + guide = govuk_content_schema_example("service_manual_guide", "service_manual_guide") + guide["phase"] = "beta" + guide["base_path"] = "/service-manual/beta" + stub_content_store_has_item(guide["base_path"], guide) + visit guide["base_path"] + + assert page.has_content?("Contact the Service Manual team") + end + + test "renders custom message for service manual homepage" do + homepage = content_store_has_schema_example( + "service_manual_homepage", + "service_manual_homepage", + ) + + visit homepage["base_path"] + + assert page.has_content?("Contact the Service Manual team") + end + + test "alpha phase label is displayed for a guide in phase 'alpha'" do + guide = govuk_content_schema_example("service_manual_guide", "service_manual_guide") + guide["phase"] = "alpha" + guide["base_path"] = "/service-manual/alpha" + stub_content_store_has_item(guide["base_path"], guide) + visit guide["base_path"] + + assert_text("ALPHA") + end + + test "No phase label is displayed for a guide without a phase field" do + guide = govuk_content_schema_example("service_manual_guide", "service_manual_guide") + guide["phase"] = nil + stub_content_store_has_item(guide["base_path"], guide) + visit guide["base_path"] + + assert_not page.has_content?("Contact the Service Manual team") + end +end diff --git a/test/integration/service_manual_search_redirect_test.rb b/test/integration/service_manual_search_redirect_test.rb new file mode 100644 index 000000000..429f1fda7 --- /dev/null +++ b/test/integration/service_manual_search_redirect_test.rb @@ -0,0 +1,8 @@ +require "test_helper" + +class ServiceManualSearchRedirectTest < ActionDispatch::IntegrationTest + test "the legacy search URL redirects to /search and retains parameters" do + get "/service-manual/search?q=bananas" + assert_redirected_to "/search?filter_manual=%2Fservice-manual&q=bananas" + end +end diff --git a/test/integration/service_manual_service_standard_test.rb b/test/integration/service_manual_service_standard_test.rb new file mode 100644 index 000000000..2c529b38a --- /dev/null +++ b/test/integration/service_manual_service_standard_test.rb @@ -0,0 +1,69 @@ +require "test_helper" + +class ServiceManualServiceStandardTest < ActionDispatch::IntegrationTest + test "service standard page has a title, summary and intro" do + setup_and_visit_content_item("service_manual_service_standard", + "title" => "Service Standard", + "description" => "The Service Standard is a set of 14 criteria.", + "details" => { + "body" => "All public facing transactional services must meet the standard.", + }) + + assert page.has_css?(".gem-c-title__text", text: "Service Standard"), "No title found" + assert page.has_css?(".app-page-header__summary", text: "The Service Standard is a set of 14 criteria"), "No description found" + assert page.has_css?(".app-page-header__intro", text: "All public facing transactional services must meet the standard."), "No body found" + end + + test "service standard page has points" do + setup_and_visit_content_item("service_manual_service_standard") + + assert_equal 3, points.length + + within(points[0]) do + assert_text("1.\nUnderstand user needs") + assert page.has_content?(/Research to develop a deep knowledge/), "Description not found" + assert page.has_link?("Read more about point 1", href: "/service-manual/service-standard/understand-user-needs"), "Link not found" + end + + within(points[1]) do + assert_text("2.\nDo ongoing user research") + assert page.has_content?(/Put a plan in place/), "Description not found" + assert page.has_link?("Read more about point 2", href: "/service-manual/service-standard/do-ongoing-user-research"), "Link not found" + end + + within(points[2]) do + assert_text("3.\nHave a multidisciplinary team") + assert page.has_content?(/Put in place a sustainable multidisciplinary/), "Description not found" + assert page.has_link?("Read more about point 3", href: "/service-manual/service-standard/have-a-multidisciplinary-team"), "Link not found" + end + end + + test "each point has an anchor tag so that they can be linked to externally" do + setup_and_visit_content_item("service_manual_service_standard") + + within("#criterion-1") do + assert_text("1.\nUnderstand user needs") + end + + within("#criterion-2") do + assert_text("2.\nDo ongoing user research") + end + + within("#criterion-3") do + assert_text("3.\nHave a multidisciplinary team") + end + end + + test "it includes a link to subscribe for email alerts" do + setup_and_visit_content_item("service_manual_service_standard") + + assert page.has_link?( + "email", + href: "/email-signup?link=/service-manual/service-standard", + ) + end + + def points + find_all(".app-service-standard-point") + end +end diff --git a/test/integration/service_manual_topic_test.rb b/test/integration/service_manual_topic_test.rb new file mode 100644 index 000000000..819f92ff3 --- /dev/null +++ b/test/integration/service_manual_topic_test.rb @@ -0,0 +1,71 @@ +require "test_helper" + +class ServiceManualTopicTest < ActionDispatch::IntegrationTest + def topic_example + @topic_example ||= govuk_content_schema_example("service_manual_topic", "service_manual_topic") + end + + test "it uses topic description as meta description" do + stub_content_store_has_item("/service-manual/test-topic", topic_example.to_json) + + visit "/service-manual/test-topic" + + assert page.has_css?('meta[name="description"]', visible: false) + tag = page.find 'meta[name="description"]', visible: false + assert_equal topic_example["description"], tag["content"] + end + + test "it doesn't write a meta description if there is none" do + topic_example[:description] = "" + stub_content_store_has_item("/service-manual/test-topic-no-description", topic_example.to_json) + + visit "/service-manual/test-topic-no-description" + + assert_not page.has_css?('meta[name="description"]', visible: false) + end + + test "it lists communities in the sidebar" do + setup_and_visit_content_item("service_manual_topic") + + within(".related-communities") do + assert page.has_link?( + "Agile delivery community", + href: "/service-manual/communities/agile-delivery-community", + ) + assert page.has_link?( + "User research community", + href: "/service-manual/communities/user-research-community", + ) + end + end + + test "it doesn't display content in accordian if not eligible" do + setup_and_visit_content_item("service_manual_topic") + + assert_not page.has_css?(".gem-c-accordion") + end + + test "it displays content using an accordian if eligible" do + content_item = govuk_content_schema_example("service_manual_topic", "service_manual_topic") + third_linked_item = { content_id: SecureRandom.uuid, title: "linky", base_path: "/basey" } + third_group = { name: "Group 3", description: "The third group", content_ids: [third_linked_item[:content_id]] } + + content_item["links"]["linked_items"] << third_linked_item + content_item["details"]["groups"] << third_group + content_item["details"]["visually_collapsed"] = true + + stub_content_store_has_item(content_item["base_path"], content_item) + visit content_item["base_path"] + + assert page.has_css?(".gem-c-accordion") + end + + test "it includes a link to subscribe for email alerts" do + setup_and_visit_content_item("service_manual_topic") + + assert page.has_link?( + "email", + href: "/email-signup?link=/service-manual/test-expanded-topic", + ) + end +end diff --git a/test/integration/service_standard_service_toolkit_test.rb b/test/integration/service_standard_service_toolkit_test.rb new file mode 100644 index 000000000..4dbfc80b2 --- /dev/null +++ b/test/integration/service_standard_service_toolkit_test.rb @@ -0,0 +1,111 @@ +require "test_helper" + +class ServiceManualServiceToolkitTest < ActionDispatch::IntegrationTest + test "the service toolkit can be visited" do + setup_and_visit_content_item("service_manual_service_toolkit") + + assert page.has_title? "Service Toolkit" + end + + test "the service toolkit does not include the new style feedback form" do + setup_and_visit_content_item("service_manual_service_toolkit") + + assert_not page.has_css?(".improve-this-page"), + "Improve this page component should not be present on the page" + end + + test "the service toolkit displays the introductory hero" do + setup_and_visit_content_item("service_manual_service_toolkit") + + assert page.has_content? <<~TEXT.chomp + Design and build government services + All you need to design, build and run services that meet government standards. + TEXT + end + + test "the homepage includes both collections" do + setup_and_visit_content_item("service_manual_service_toolkit") + + assert_equal 2, collections.length, "Expected to find 2 collections" + end + + test "the homepage includes the titles for both collections" do + setup_and_visit_content_item("service_manual_service_toolkit") + + within(the_first_collection) do + assert page.has_content? "Standards" + end + + within(the_second_collection) do + assert page.has_content? "Buying" + end + end + + test "the homepage includes the descriptions for both collections" do + setup_and_visit_content_item("service_manual_service_toolkit") + + within(the_first_collection) do + assert page.has_content? "Meet the standards for government services" + end + + within(the_second_collection) do + assert page.has_content? "Extra skills, people and technology to help build your service" + end + end + + test "the homepage includes the links from all collections" do + setup_and_visit_content_item( + "service_manual_service_toolkit", + "details" => { + "collections" => [ + { + "title" => "Standards", + "description" => "Meet the standards for government services", + "links" => [ + { + "title" => "Service Standard", + "url" => "https://www.gov.uk/service-manual/service-standard", + "description" => "", + }, + ], + }, + { + "title" => "Buying", + "description" => "Skills and technology for building digital services", + "links" => [ + { + "title" => "Digital Marketplace", + "url" => "https://www.gov.uk/digital-marketplace", + "description" => "", + }, + ], + }, + ], + }, + ) + + within(the_first_collection) do + assert page.has_link? "Service Standard", + href: "https://www.gov.uk/service-manual/service-standard" + end + + within(the_second_collection) do + assert page.has_link? "Digital Marketplace", + href: "https://www.gov.uk/digital-marketplace" + end + end + +private + + def collections + find_all(".app-collection") + end + + def the_first_collection + collections[0] + end + + def the_second_collection + collections[1] + end +end diff --git a/test/presenters/service_manual_guide_presenter_test.rb b/test/presenters/service_manual_guide_presenter_test.rb new file mode 100644 index 000000000..f740865e7 --- /dev/null +++ b/test/presenters/service_manual_guide_presenter_test.rb @@ -0,0 +1,141 @@ +require "presenter_test_helper" + +class ServiceManualGuidePresenterTest < PresenterTestCase + def schema_name + "service_manual_guide" + end + + test "presents the basic details required to display a Service Manual Guide" do + assert_equal "Agile Delivery", presented_item.title + assert_equal "service_manual_guide", presented_item.document_type + assert presented_item.body.size > 10 + assert presented_item.header_links.size >= 1 + + content_owner = presented_item.content_owners.first + assert content_owner.title.present? + assert content_owner.href.present? + end + + test "breadcrumbs have a root and a topic link" do + guide = presented_item + assert_equal [ + { title: "Service manual", url: "/service-manual" }, + { title: "Agile", url: "/service-manual/agile" }, + ], + guide.breadcrumbs + end + + test "breadcrumbs gracefully omit topic if it's not present" do + presented_item = presented_item(schema_name, "links" => {}) + assert_equal [ + { title: "Service manual", url: "/service-manual" }, + ], + presented_item.breadcrumbs + end + + test "#category_title is the title of the category" do + guide = presented_item + assert_equal "Agile", guide.category_title + end + + test "#category_title is the title of the parent for a point" do + content_item = GovukSchemas::Example.find("service_manual_guide", example_name: "point_page") + + presenter = present_example(content_item) + + assert presenter.category_title, "The Service Standard" + end + + test "#category_title can be empty" do + guide = presented_item(schema_name, "links" => {}) + assert_nil guide.category_title + end + + test "#content_owners when stored in the links" do + guide = presented_item(schema_name, + "details" => { "content_owner" => nil }, + "links" => { "content_owners" => [{ + "content_id" => "e5f09422-bf55-417c-b520-8a42cb409814", + "title" => "Agile delivery community", + "base_path" => "/service-manual/communities/agile-delivery-community", + }] }) + + expected = [ + ServiceManualGuidePresenter::ContentOwner.new( + "Agile delivery community", + "/service-manual/communities/agile-delivery-community", + ), + ] + assert_equal expected, guide.content_owners + end + + test "#show_description? is false if not set" do + assert_not presented_item(schema_name, {}).show_description? + end + + test "#public_updated_at returns a time" do + assert_kind_of Time, presented_item.public_updated_at + end + + test "#public_updated_at returns nil if not available" do + example = govuk_content_schema_example("service_manual_guide", "service_manual_guide") + content_item = example.merge("public_updated_at" => nil) + guide = present_example(content_item) + + assert_nil guide.public_updated_at + end + + test "#visible_updated_at returns the public_updated_at" do + timestamp = "2015-10-10T09:00:00+00:00" + guide = presented_item(schema_name, "public_updated_at" => timestamp) + + assert_equal guide.visible_updated_at, Time.zone.parse(timestamp) + end + + test "#visible_updated_at returns the updated_at time if the public_updated_at hasn't yet been set" do + timestamp = "2015-10-10T09:00:00+00:00" + example = schema_item("service_manual_guide", "service_manual_guide") + content_item = example.merge("updated_at" => timestamp, "public_updated_at" => nil) + guide = present_example(content_item) + + assert_equal guide.visible_updated_at, Time.zone.parse(timestamp) + end + + test "#latest_change returns the details for the most recent change" do + expected_history = ServiceManualGuidePresenter::Change.new( + Time.zone.parse("2015-10-09T08:17:10+00:00"), + "This is our latest change", + ) + + assert_equal expected_history, presented_item.latest_change + end + + test "#latest_change timestamp is the updated_at time if public_updated_at hasn't been set" do + timestamp = "2015-10-07T09:00:00+00:00" + example = schema_item("service_manual_guide", "service_manual_guide") + content_item = example.merge("updated_at" => timestamp, "public_updated_at" => nil) + guide = present_example(content_item) + + expected_history = ServiceManualGuidePresenter::Change.new( + Time.zone.parse(timestamp), + "This is our latest change", + ) + + assert_equal expected_history, guide.latest_change + end + + test "#previous_changes returns the change history for the guide" do + expected_history = [ + ServiceManualGuidePresenter::Change.new( + Time.zone.parse("2015-09-09T08:17:10+00:00"), + "This is another change", + ), + ServiceManualGuidePresenter::Change.new( + Time.zone.parse("2015-09-01T08:17:10+00:00"), + "Guidance first published", + ), + ] + + assert_equal expected_history, presented_item.previous_changes + end +end diff --git a/test/presenters/service_manual_homepage_presenter_test.rb b/test/presenters/service_manual_homepage_presenter_test.rb new file mode 100644 index 000000000..1728f3452 --- /dev/null +++ b/test/presenters/service_manual_homepage_presenter_test.rb @@ -0,0 +1,26 @@ +require "presenter_test_helper" + +class ServiceManualHomepagePresenterTest < PresenterTestCase + def schema_name + "service_manual_homepage" + end + test "#topics returns the children in the links, ordered alphabetically" do + homepage = presented_item(schema_name, + "links" => { + "children" => [ + { "title" => "Agile Delivery" }, + { "title" => "Helping people to use your service" }, + { "title" => "Funding and procurement" }, + ], + }) + + assert_equal( + [ + { "title" => "Agile Delivery" }, + { "title" => "Funding and procurement" }, + { "title" => "Helping people to use your service" }, + ], + homepage.topics, + ) + end +end diff --git a/test/presenters/service_manual_presenter_test.rb b/test/presenters/service_manual_presenter_test.rb new file mode 100644 index 000000000..b45934f33 --- /dev/null +++ b/test/presenters/service_manual_presenter_test.rb @@ -0,0 +1,13 @@ +require "presenter_test_helper" + +class ServiceManualPresenterTest < PresenterTestCase + test "#links" do + presenter = create_presenter(ServiceManualPresenter, content_item: { "links" => { "something" => [] } }) + assert_equal ({ "something" => [] }), presenter.links + end + + test "#details" do + presenter = create_presenter(ServiceManualPresenter, content_item: { "description" => "Description" }) + assert_equal "Description", presenter.description + end +end diff --git a/test/presenters/service_manual_service_standard_presenter_test.rb b/test/presenters/service_manual_service_standard_presenter_test.rb new file mode 100644 index 000000000..cd96a0ec2 --- /dev/null +++ b/test/presenters/service_manual_service_standard_presenter_test.rb @@ -0,0 +1,77 @@ +require "presenter_test_helper" + +class ServiceManualServiceStandardPresenterTest < PresenterTestCase + def schema_name + "service_manual_service_standard" + end + + test "#points gets points from the details" do + points = presented_item.points + + assert(points.any? { |point_hash| point_hash.title == "1. Understand user needs" }) + assert(points.any? { |point_hash| point_hash.title == "2. Do ongoing user research" }) + end + + test "#points returns points ordered numerically" do + content_item_hash = { + "links" => { + "children" => [ + { "title" => "3. Title" }, + { "title" => "1. Title" }, + { "title" => "9. Title" }, + { "title" => "10. Title" }, + { "title" => "5. Title" }, + { "title" => "6. Title" }, + { "title" => "2. Title" }, + { "title" => "4. Title" }, + { "title" => "7. Title" }, + { "title" => "11. Title" }, + { "title" => "8. Title" }, + ], + }, + } + + points_titles = + presented_item(schema_name, content_item_hash).points.map(&:title) + + assert_equal points_titles, + [ + "1. Title", + "2. Title", + "3. Title", + "4. Title", + "5. Title", + "6. Title", + "7. Title", + "8. Title", + "9. Title", + "10. Title", + "11. Title", + ] + end + + test "#points is empty if there aren't any points in the content item" do + content_item_hash = { "links" => {} } + assert presented_item(schema_name, content_item_hash).points.empty? + end + + test "#breadcrumbs contains a link to the service manual root" do + content_item_hash = { + "title" => "Service Standard", + } + + assert presented_item(schema_name, content_item_hash).breadcrumbs, + [ + { title: "Service manual", url: "/service-manual" }, + ] + end + + test "#email_alert_signup returns a link to the email alert signup" do + assert_equal "/email-signup?link=/service-manual/service-standard", + presented_item.email_alert_signup_link + end + + test "#poster_url returns a link to the service standard poster" do + assert_equal "http://example.com/service-standard-poster.pdf", presented_item.poster_url + end +end diff --git a/test/presenters/service_manual_topic_presenter/topic_group_test.rb b/test/presenters/service_manual_topic_presenter/topic_group_test.rb new file mode 100644 index 000000000..4af1d5beb --- /dev/null +++ b/test/presenters/service_manual_topic_presenter/topic_group_test.rb @@ -0,0 +1,46 @@ +require "test_helper" + +class ServiceManualTopicPresenter::TopicGroupTest < ActiveSupport::TestCase + def described_class + ServiceManualTopicPresenter::TopicGroup + end + + def group_data + { + "name" => "Fruits", + "description" => "Nice fruits", + "content_ids" => %w[111 222], + } + end + + test "pertains order in the groups hash" do + linked_items = [ + { "content_id" => "222", "title" => "Bananas" }, + { "content_id" => "111", "title" => "Apples" }, + ] + group = described_class.new(group_data, linked_items) + + assert_equal %w[Apples Bananas], group.linked_items.map(&:label) + end + + test "omits grouped but unpublished linked items (not in the links hash)" do + linked_items = [{ "content_id" => "222", "title" => "Bananas" }] + group = described_class.new(group_data, linked_items) + + assert_equal %w[Bananas], group.linked_items.map(&:label) + end + + test "present? returns true if there are no visible grouped links" do + linked_items = [{ "content_id" => "222", "title" => "Bananas" }] + group = described_class.new(group_data, linked_items) + + assert group.present? + end + + test "present? returns false if there are no visible grouped links" do + linked_items = [] + group = described_class.new(group_data, linked_items) + + assert_not group.present? + end +end diff --git a/test/presenters/service_manual_topic_presenter_test.rb b/test/presenters/service_manual_topic_presenter_test.rb new file mode 100644 index 000000000..f4759258f --- /dev/null +++ b/test/presenters/service_manual_topic_presenter_test.rb @@ -0,0 +1,93 @@ +require "presenter_test_helper" + +class ServiceManualTopicPresenterTest < PresenterTestCase + def schema_name + "service_manual_topic" + end + + test "presents the basic details required to display a Service Manual Topic" do + topic = presented_item(schema_name, "title" => "Agile", "description" => "Agile Test Description") + assert_equal "Agile", topic.title + assert_equal "Agile Test Description", topic.description + assert topic.document_type.present? + assert topic.locale.present? + end + + test "loads link groups" do + topic = presented_item + + assert_equal 2, topic.groups.size + assert_equal ["Group 1", "Group 2"], topic.groups.map(&:name) + assert_equal([true, true], topic.groups.map { |lg| lg.description.present? }) + end + + test 'loads linked items within link groups and populates them with data from "links" based on content_id' do + groups = presented_item.groups + assert_equal [2, 1], groups.map(&:linked_items).map(&:size) + + group_items = groups.find { |li| li.name == "Group 1" }.linked_items + assert_equal %w[Accessibility Addresses], group_items.map(&:label) + assert_equal ["/service-manual/user-centred-design/accessibility", "/service-manual/user-centred-design/resources/patterns/addresses"], group_items.map(&:href) + end + + test "returns accordion content data" do + accordion_content = presented_item.accordion_content + first_accordion_section = { + data_attributes: { + ga4: { + event_name: "select_content", + type: "accordion", + text: "Group 1", + index: 1, + index_total: 2, + }, + }, + heading: { text: "Group 1" }, + summary: { text: "The first group" }, + content: { html: "" }, + expanded: true, + } + + assert_equal 2, accordion_content.count + assert_equal first_accordion_section, accordion_content.first + end + + test "does not fail if there are no linked_groups" do + topic = presented_item(schema_name, "details" => { "groups" => nil }) + + assert_equal [], topic.groups + end + + test "omits groups that have no published linked items" do + topic = presented_item(schema_name, "links" => { "linked_items" => [] }) # unpublished content_ids are filtered out from content-store responses + assert_equal 0, topic.groups.size + end + + test "#content_owners loads the data into objects" do + topic = presented_item(schema_name, "links" => { "content_owners" => [ + { "title" => "Design Community", "base_path" => "/service-manual/design-community" }, + { "title" => "Agile Community", "base_path" => "/service-manual/agile-community" }, + ] }) + assert_equal 2, topic.content_owners.size + design_community = topic.content_owners.first + assert_equal "Design Community", design_community.title + assert_equal "/service-manual/design-community", design_community.href + agile_community = topic.content_owners.last + assert_equal "Agile Community", agile_community.title + assert_equal "/service-manual/agile-community", agile_community.href + end + + test "#breadcrumbs links to the root path" do + topic = presented_item + + expected_breadcrumbs = [ + { title: "Service manual", url: "/service-manual" }, + ] + assert_equal expected_breadcrumbs, topic.breadcrumbs + end + + test "#email_alert_signup returns a link to the email alert signup" do + assert_equal "/email-signup?link=/service-manual/test-expanded-topic", + presented_item.email_alert_signup_link + end +end diff --git a/test/support/govuk_content_schema_examples.rb b/test/support/govuk_content_schema_examples.rb index e74dce25c..ae776d9ab 100644 --- a/test/support/govuk_content_schema_examples.rb +++ b/test/support/govuk_content_schema_examples.rb @@ -46,6 +46,10 @@ def supported_schemas case_study coming_soon html_publication + service_manual_guide + service_manual_homepage + service_manual_service_standard + service_manual_topic statistics_announcement take_part topical_event_about_page diff --git a/test/test_helper.rb b/test/test_helper.rb index 78939fd40..d76be7685 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -50,6 +50,8 @@ def set_skip_slimmer_header class ActionDispatch::IntegrationTest # Make the Capybara DSL available in all integration tests include Capybara::DSL + include ActionView::Helpers::DateHelper + include I18n def teardown Capybara.reset_sessions! @@ -159,13 +161,20 @@ def assert_footer_has_published_dates(first_published = nil, last_updated = nil, assert_has_published_dates(first_published, last_updated, history_link:) end - def setup_and_visit_content_item(name, parameter_string = "") + def setup_and_visit_content_item(name, overrides = {}, parameter_string = "") @content_item = get_content_example(name).tap do |item| - stub_content_store_has_item(item["base_path"], item.to_json) - visit_with_cachebust("#{item['base_path']}#{parameter_string}") + content_item = item.deep_merge(overrides) + stub_content_store_has_item(content_item["base_path"], content_item.to_json) + visit_with_cachebust("#{content_item['base_path']}#{parameter_string}") end end + def setup_and_visit_content_item_with_params(name, parameter_string = "") + @content_item = get_content_example(name) + stub_content_store_has_item(@content_item["base_path"], @content_item.to_json) + visit_with_cachebust("#{@content_item['base_path']}#{parameter_string}") + end + def setup_and_visit_html_publication(name, parameter_string = "") @content_item = get_content_example(name).tap do |item| parent = item["links"]["parent"][0]