diff --git a/app/__init__.py b/app/__init__.py index 770a42779..0436f5e98 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -743,6 +743,7 @@ def add_template_filters(application): format_phone_number_human_readable, format_thousands, id_safe, + convert_to_boolean, ]: application.add_template_filter(fn) diff --git a/app/assets/javascripts/detailsPolyfill.js b/app/assets/javascripts/detailsPolyfill.js deleted file mode 100644 index ce0692195..000000000 --- a/app/assets/javascripts/detailsPolyfill.js +++ /dev/null @@ -1,196 +0,0 @@ -// From -// https://github.com/alphagov/govuk_elements/blob/4926897dc7734db2fc5e5ebb6acdc97f86e22e50/public/javascripts/vendor/details.polyfill.js -// -// --- -// -//
polyfill -// http://caniuse.com/#feat=details - -// FF Support for HTML5's
and -// https://bugzilla.mozilla.org/show_bug.cgi?id=591737 - -// http://www.sitepoint.com/fixing-the-details-element/ - -(function () { - 'use strict'; - - var NATIVE_DETAILS = typeof document.createElement('details').open === 'boolean'; - - // Add event construct for modern browsers or IE - // which fires the callback with a pre-converted target reference - function addEvent(node, type, callback) { - if (node.addEventListener) { - node.addEventListener(type, function (e) { - callback(e, e.target); - }, false); - } else if (node.attachEvent) { - node.attachEvent('on' + type, function (e) { - callback(e, e.srcElement); - }); - } - } - - // Handle cross-modal click events - function addClickEvent(node, callback) { - // Prevent space(32) from scrolling the page - addEvent(node, 'keypress', function (e, target) { - if (target.nodeName === 'SUMMARY') { - if (e.keyCode === 32) { - if (e.preventDefault) { - e.preventDefault(); - } else { - e.returnValue = false; - } - } - } - }); - // When the key comes up - check if it is enter(13) or space(32) - addEvent(node, 'keyup', function (e, target) { - if (e.keyCode === 13 || e.keyCode === 32) { callback(e, target); } - }); - addEvent(node, 'mouseup', function (e, target) { - callback(e, target); - }); - } - - // Get the nearest ancestor element of a node that matches a given tag name - function getAncestor(node, match) { - do { - if (!node || node.nodeName.toLowerCase() === match) { - break; - } - } while ((node = node.parentNode)); - - return node; - } - - // Create a started flag so we can prevent the initialisation - // function firing from both DOMContentLoaded and window.onload - var started = false; - - // Initialisation function - function addDetailsPolyfill(list) { - - // If this has already happened, just return - // else set the flag so it doesn't happen again - if (started) { - return; - } - started = true; - - // Get the collection of details elements, but if that's empty - // then we don't need to bother with the rest of the scripting - if ((list = document.getElementsByTagName('details')).length === 0) { - return; - } - - // else iterate through them to apply their initial state - var n = list.length, i = 0; - for (i; i < n; i++) { - var details = list[i]; - - // Save shortcuts to the inner summary and content elements - details.__summary = details.getElementsByTagName('summary').item(0); - details.__content = details.getElementsByTagName('div').item(0); - - // If the content doesn't have an ID, assign it one now - // which we'll need for the summary's aria-controls assignment - if (!details.__content.id) { - details.__content.id = 'details-content-' + i; - } - - // Add ARIA role="group" to details - details.setAttribute('role', 'group'); - - // Add role=button to summary - details.__summary.setAttribute('role', 'button'); - - // Add aria-controls - details.__summary.setAttribute('aria-controls', details.__content.id); - - // Set tabIndex so the summary is keyboard accessible for non-native elements - // http://www.saliences.com/browserBugs/tabIndex.html - if (!NATIVE_DETAILS) { - details.__summary.tabIndex = 0; - } - - // Detect initial open state - var openAttr = details.getAttribute('open') !== null; - if (openAttr === true) { - details.__summary.setAttribute('aria-expanded', 'true'); - details.__content.setAttribute('aria-hidden', 'false'); - } else { - details.__summary.setAttribute('aria-expanded', 'false'); - details.__content.setAttribute('aria-hidden', 'true'); - if (!NATIVE_DETAILS) { - details.__content.style.display = 'none'; - } - } - - // Create a circular reference from the summary back to its - // parent details element, for convenience in the click handler - details.__summary.__details = details; - - // If this is not a native implementation, create an arrow - // inside the summary - - var twisty = document.createElement('i'); - - if (openAttr === true) { - twisty.className = 'arrow arrow-open'; - twisty.appendChild(document.createTextNode('\u25bc')); - } else { - twisty.className = 'arrow arrow-closed'; - twisty.appendChild(document.createTextNode('\u25ba')); - } - - details.__summary.__twisty = details.__summary.insertBefore(twisty, details.__summary.firstChild); - details.__summary.__twisty.setAttribute('aria-hidden', 'true'); - - } - - // Define a statechange function that updates aria-expanded and style.display - // Also update the arrow position - function statechange(summary) { - - var expanded = summary.__details.__summary.getAttribute('aria-expanded') === 'true'; - var hidden = summary.__details.__content.getAttribute('aria-hidden') === 'true'; - - summary.__details.__summary.setAttribute('aria-expanded', (expanded ? 'false' : 'true')); - summary.__details.__content.setAttribute('aria-hidden', (hidden ? 'false' : 'true')); - - if (!NATIVE_DETAILS) { - summary.__details.__content.style.display = (expanded ? 'none' : ''); - - var hasOpenAttr = summary.__details.getAttribute('open') !== null; - if (!hasOpenAttr) { - summary.__details.setAttribute('open', 'open'); - } else { - summary.__details.removeAttribute('open'); - } - } - - if (summary.__twisty) { - summary.__twisty.firstChild.nodeValue = (expanded ? '\u25ba' : '\u25bc'); - summary.__twisty.setAttribute('class', (expanded ? 'arrow arrow-closed' : 'arrow arrow-open')); - } - - return true; - } - - // Bind a click event to handle summary elements - addClickEvent(document, function(e, summary) { - if (!(summary = getAncestor(summary, 'summary'))) { - return true; - } - return statechange(summary); - }); - } - - // Bind two load events for modern and older browsers - // If the first one fires it will set a flag to block the second one - // but if it's not supported then the second one will fire - addEvent(document, 'DOMContentLoaded', addDetailsPolyfill); - addEvent(window, 'load', addDetailsPolyfill); - -})(); diff --git a/app/assets/javascripts/modules/all.mjs b/app/assets/javascripts/modules/all.mjs index 7940c17df..7041044d3 100644 --- a/app/assets/javascripts/modules/all.mjs +++ b/app/assets/javascripts/modules/all.mjs @@ -7,6 +7,21 @@ // Exported items will be added to the window.GOVUK namespace. // For example, `export { Frontend }` will assign `Frontend` to `window.Frontend` import Header from 'govuk-frontend/components/header/header'; +import Details from 'govuk-frontend/components/details/details'; + +/** + * TODO: Ideally this would be a NodeList.prototype.forEach polyfill + * This seems to fail in IE8, requires more investigation. + * See: https://github.com/imagitama/nodelist-foreach-polyfill + */ +function nodeListForEach (nodes, callback) { + if (window.NodeList.prototype.forEach) { + return nodes.forEach(callback) + } + for (var i = 0; i < nodes.length; i++) { + callback.call(window, nodes[i], i, nodes); + } +} // Copy of the initAll function from https://github.com/alphagov/govuk-frontend/blob/v2.13.0/src/all.js // except it only includes, and initialises, the components used by this application. @@ -18,6 +33,12 @@ function initAll (options) { // Defaults to the entire document if nothing is set. var scope = typeof options.scope !== 'undefined' ? options.scope : document + // Find all global details elements to enhance. + var $details = scope.querySelectorAll('details') + nodeListForEach($details, function ($detail) { + new Details($detail).init() + }) + // Find first header module to enhance. var $toggleButton = scope.querySelector('[data-module="header"]') new Header($toggleButton).init() @@ -26,6 +47,7 @@ function initAll (options) { // Create separate namespace for GOVUK Frontend. var Frontend = { "Header": Header, + "Details": Details, "initAll": initAll } diff --git a/app/assets/stylesheets/govuk-frontend/_all.scss b/app/assets/stylesheets/govuk-frontend/_all.scss index 760d837cc..9ecefd894 100644 --- a/app/assets/stylesheets/govuk-frontend/_all.scss +++ b/app/assets/stylesheets/govuk-frontend/_all.scss @@ -17,6 +17,7 @@ $govuk-assets-path: "/static/"; @import 'components/header/_header'; @import 'components/footer/_footer'; @import 'components/back-link/_back-link'; +@import 'components/details/_details'; @import "utilities/all"; @import "overrides/all"; diff --git a/app/assets/stylesheets/views/api.scss b/app/assets/stylesheets/views/api.scss index 98e21685e..4049c43c5 100644 --- a/app/assets/stylesheets/views/api.scss +++ b/app/assets/stylesheets/views/api.scss @@ -1,6 +1,8 @@ .api-notifications { font-family: monospace; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; border-bottom: 1px solid $border-colour; &-item { @@ -8,38 +10,59 @@ border-top: 1px solid $border-colour; padding: 10px 0 0 0; - &-title { - color: $link-colour; + &__heading, + &__data, + &__view { + font-family: monospace; + } + + &__heading { + display: block; + margin-bottom: $gutter-half; + + &::before { + top: -1.3em; + } + } + + &__meta { + + display: block; + color: $secondary-text-colour; text-decoration: none; - display: block; + + &-key, + &-time { + color: $secondary-text-colour; + display: inline-block; + width: auto; + } + + @include govuk-media-query($from: tablet) { + &-key, + &-time { + width: 50%; + } + + &-time { + text-align: right; + } + } + } - &-recipient { - display: inline; - } + &__data { - &-meta { - display: block; - color: $secondary-text-colour; - } + border-left: none; + padding-left: 25px; - &-time { - text-align: right; - } + &-name { + color: $secondary-text-colour; + } - &-key { - display: inline-block; - padding-left: 46px; - } - - &-data { - - padding-left: 31px; - color: $secondary-text-colour; - - &-item { - padding-bottom: 15px; + &-value { color: $text-colour; + padding-bottom: 15px; } } diff --git a/app/templates/admin_template.html b/app/templates/admin_template.html index fc709c298..4d705ad73 100644 --- a/app/templates/admin_template.html +++ b/app/templates/admin_template.html @@ -18,6 +18,9 @@ .govuk-header__container { border-color: {{header_colour}} } + {% block meta_format_detection %} + + {% endblock %} {% block meta %} {% endblock %} {% endblock %} diff --git a/app/templates/views/api/index.html b/app/templates/views/api/index.html index 9397a97b5..cebb5442d 100644 --- a/app/templates/views/api/index.html +++ b/app/templates/views/api/index.html @@ -39,43 +39,45 @@
{% if not api_notifications.notifications %}
-

+

When you send messages via the API they’ll appear here.

-

+

Notify deletes messages after 7 days.

{% endif %} {% for notification in api_notifications.notifications %} -
- -

+
+ +

+ {{ notification.to }} + + + + {{notification.key_name}} + + + + +

- - - {{notification.key_name}} - - - - -
-
-
+
+
{% for key in [ 'id', 'client_reference', 'notification_type', 'created_at', 'updated_at', 'sent_at', 'status' ] %} {% if notification[key] %} -
{{ key }}:
-
{{ notification[key] }}
+
{{ key }}:
+
{{ notification[key] }}
{% endif %} {% endfor %} {% if notification.status not in ('pending-virus-check', 'virus-scan-failed') %} - View {{ message_count_label(1, notification.template.template_type, suffix='') }} + View {{ message_count_label(1, notification.template.template_type, suffix='') }} {% endif %}
@@ -84,11 +86,11 @@ {% if api_notifications.notifications %}
{% if api_notifications.notifications|length == 50 %} -

+

Only showing the first 50 messages.

{% endif %} -

+

Notify deletes messages after 7 days.

diff --git a/app/templates/views/get-started.html b/app/templates/views/get-started.html index 09e165a8d..0d20b17b9 100644 --- a/app/templates/views/get-started.html +++ b/app/templates/views/get-started.html @@ -1,6 +1,7 @@ {% extends "content_template.html" %} {% from "components/table.html" import mapping_table, row, text_field, edit_field, field with context %} {% from "components/sub-navigation.html" import sub_navigation %} +{% from "components/details/macro.njk" import govukDetails %} {% block per_page_title %} Get started @@ -14,21 +15,22 @@
  • Check if GOV.UK Notify is right for you

    Read about our features, pricing and roadmap.

    -
    - Organisations that can use Notify -
    -

    Notify is available to:

    -
      -
    • central government departments
    • -
    • local authorities
    • -
    • state-funded schools
    • -
    • housing associations
    • -
    • the NHS
    • -
    • companies owned by local or central government that deliver services on their behalf
    • -
    -

    Notify is not currently available to charities.

    -
    -
    + {{ govukDetails({ + "summaryText": "Organisations that can use Notify", + "html": ''' +
    +

    Notify is available to:

    +
      +
    • central government departments
    • +
    • local authorities
    • +
    • state-funded schools
    • +
    • housing associations
    • +
    • the NHS
    • +
    • companies owned by local or central government that deliver services on their behalf
    • +
    +

    Notify is not currently available to charities.

    +
    ''' + }) }}
  • diff --git a/app/templates/views/platform-admin/index.html b/app/templates/views/platform-admin/index.html index 6da4e87d2..c404f78c3 100644 --- a/app/templates/views/platform-admin/index.html +++ b/app/templates/views/platform-admin/index.html @@ -4,6 +4,7 @@ {% from "components/message-count-label.html" import message_count_label %} {% from "components/status-box.html" import status_box %} {% from "components/form.html" import form_wrapper %} +{% from "components/details/macro.njk" import govukDetails %} {% block per_page_title %} Platform admin @@ -14,15 +15,21 @@

    Summary

    -
    - Apply filters + + {% set details_content %} {% call form_wrapper(method="get") %} {{ textbox(form.start_date, hint="Enter start date in format YYYY-MM-DD") }} {{ textbox(form.end_date, hint="Enter end date in format YYYY-MM-DD") }}
    {% endcall %} -
    + {% endset %} + + {{ govukDetails({ + "summaryText": "Apply filters", + "html": details_content, + "open": form.errors | convert_to_boolean + }) }}
    {% for noti_type in global_stats %} diff --git a/app/templates/views/platform-admin/services.html b/app/templates/views/platform-admin/services.html index c10abb095..c42a76af7 100644 --- a/app/templates/views/platform-admin/services.html +++ b/app/templates/views/platform-admin/services.html @@ -6,6 +6,7 @@ {% from "components/message-count-label.html" import message_count_label %} {% from "components/table.html" import mapping_table, field, stats_fields, row_group, row, right_aligned_field_heading, hidden_field_heading, text_field %} {% from "components/form.html" import form_wrapper %} +{% from "components/details/macro.njk" import govukDetails %} {% macro stats_fields(channel, data) -%} @@ -101,16 +102,21 @@ {{ page_title|capitalize }}
  • -
    - Apply filters - {% call form_wrapper(method="get") %} - {{ textbox(form.start_date, hint="Enter start date in format YYYY-MM-DD") }} - {{ textbox(form.end_date, hint="Enter end date in format YYYY-MM-DD") }} - {{ checkbox(form.include_from_test_key) }} -
    - - {% endcall %} -
    + + {% set details_content %} + {% call form_wrapper(method="get") %} + {{ textbox(form.start_date, hint="Enter start date in format YYYY-MM-DD") }} + {{ textbox(form.end_date, hint="Enter end date in format YYYY-MM-DD") }} + {{ checkbox(form.include_from_test_key) }} +
    + + {% endcall %} + {% endset %} + + {{ govukDetails({ + "summaryText": "Apply filters", + "html": details_content + }) }} {% include "views/platform-admin/_global_stats.html" %} diff --git a/gulpfile.js b/gulpfile.js index ff2903295..257aef277 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -63,7 +63,8 @@ const copy = { 'skip-link', 'header', 'footer', - 'back-link' + 'back-link', + 'details' ]; let done = 0; @@ -143,7 +144,6 @@ const javascripts = () => { paths.src + 'javascripts/govuk/cookie-functions.js', paths.src + 'javascripts/cookieMessage.js', paths.src + 'javascripts/stick-to-window-when-scrolling.js', - paths.src + 'javascripts/detailsPolyfill.js', paths.src + 'javascripts/apiKey.js', paths.src + 'javascripts/autofocus.js', paths.src + 'javascripts/enhancedTextbox.js', diff --git a/tests/app/main/views/test_api_integration.py b/tests/app/main/views/test_api_integration.py index 326f68d1e..5ba9a68c1 100644 --- a/tests/app/main/views/test_api_integration.py +++ b/tests/app/main/views/test_api_integration.py @@ -37,7 +37,7 @@ def test_should_show_api_page( rows = page.find_all('details') assert len(rows) == 5 for row in rows: - assert row.find('h3').string.strip() == '07123456789' + assert row.select('h3 .govuk-details__summary-text')[0].string.strip() == '07123456789' def test_should_show_api_page_with_lots_of_notifications(