Merge pull request #130 from alphagov/nav-2.0

Fix the navigation and menus on frontend
This commit is contained in:
Adam Shimali
2016-02-01 13:58:36 +00:00
37 changed files with 124 additions and 400 deletions

View File

@@ -1,199 +0,0 @@
// Copied from:
// https://github.com/alphagov/govuk_elements/blob/4926897dc7734db2fc5e5ebb6acdc97f86e22e50/public/javascripts/vendor/details.polyfill.js
// When this is moved to GOV.UK Frontend Toolkit, we should import it from there
// instead
// <details> polyfill
// http://caniuse.com/#feat=details
// FF Support for HTML5's <details> and <summary>
// 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
if (!NATIVE_DETAILS) {
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);
})();

View File

@@ -21,27 +21,31 @@
}
}
.phase-tag {
@include phase-tag(beta);
}
@media (min-width: 641px) {
.phase-tag {
font-size: 16px;
line-height: 1.25;
margin-top: 7px;
}
}
@media (max-width: 641px) {
.phase-tag {
margin-top: 11px;
}
#global-header-bar {
background-color: $red;
}
#global-header #logo {
white-space: nowrap;
font-size: 27px;
line-height: 32px;
img {
padding-right: 8px;
}
}
@include media(desktop) {
#proposition-menu {
float: right;
}
#global-header .header-proposition #proposition-links li {
padding: 0 0 0 15px;
}
}
a:visited {

View File

@@ -7,7 +7,7 @@
color: $white;
display: block;
padding: $gutter-half;
margin: 0 0 $gutter 0;
margin: $gutter-half 0 $gutter 0;
text-align: center;
position: relative;

View File

@@ -1,32 +0,0 @@
%dropdown,
.dropdown {
position: relative;
&-toggle {
color: $link-colour;
position: relative;
cursor: pointer;
outline-offset: 2px;
&:hover {
color: $link-hover-colour;
text-decoration: underline;
}
}
a {
display: block;
margin-top: 7px;
padding-left: 20px;
&:hover {
text-decoration: underline;
}
}
}

View File

@@ -1,36 +0,0 @@
.management-navigation {
@extend %site-width-container;
@include core-16();
border-bottom: 1px solid $border-colour;
padding: 10px 0 8px;
a {
&:link,
&:visited {
text-decoration: none;
}
&:hover {
color: $link-hover-colour;
text-decoration: underline;
}
}
&-account {
@include media(tablet) {
text-align: right;
a {
margin-left: 20px;
}
}
}
}

View File

@@ -2,18 +2,18 @@
padding: 3px 0 0 0;
li {
@include core-16();
margin: 3px 0;
}
ul {
ul, h2 {
@include core-16;
border-bottom: 1px solid $border-colour;
margin: 10px 20px 0 0;
list-style-type: none;
padding: 0 0 8px 0;
}
h2 {
font-weight: bold;
}
li {
margin: 7px 0 0 0;
}

View File

@@ -41,8 +41,6 @@
@import 'components/banner';
@import 'components/textbox';
@import 'components/browse-list';
@import 'components/management-navigation';
@import 'components/dropdown';
@import 'components/email-message';
@import 'components/api-key';

View File

@@ -10,7 +10,7 @@ from app.main.forms import AddServiceForm
def add_service():
form = AddServiceForm(services_dao.find_all_service_names)
services = services_dao.get_services(current_user.id)
if len(services) > 0:
if len(services) == 0:
heading = 'Set up notifications for your service'
else:
heading = 'Add a new service'

View File

@@ -8,12 +8,6 @@ from app.main import main
@login_required
def choose_service():
services = services_dao.get_services(current_user.id)
# If there is only one service redirect
# to the service dashboard.
if len(services['data']) == 1:
session['service_name'] = services['data'][0]['name']
return redirect(url_for(
'main.service_dashboard', service_id=services['data'][0]['id']))
return render_template(
'views/choose-service.html',
services=[services_dao.ServicesBrowsableItem(x) for x in services['data']])

View File

@@ -28,10 +28,23 @@
{% block header_class %}with-proposition{% endblock %}
{% block proposition_header %}
<div class="header-proposition">
<div class='content'>
<div class="phase-banner-beta">
<strong class="phase-tag">BETA</strong>
</div>
<div class="content">
{% if current_user.is_authenticated() %}
<a href="#proposition-links" class="js-header-toggle menu">Menu</a>
<nav id="proposition-menu">
<ul id="proposition-links">
<li>
<a href="{{ url_for('main.user_profile') }}">{{ current_user.name }}</a>
</li>
<li>
<a href="{{ url_for('main.choose_service') }}">Switch service</a>
</li>
<li>
<a href="{{ url_for('main.sign_out')}}">Sign out</a>
</li>
</ul>
</nav>
{% endif %}
</div>
</div>
{% endblock %}
@@ -47,42 +60,8 @@
{% endif %}
{% block content %}
{% if current_user.is_authenticated() and session['service_name'] != None %}
<nav class="management-navigation">
<div class="grid-row">
<div class="column-half">
<details class="dropdown">
<summary class="dropdown-toggle">
{{ session.get('service_name', 'Choose service') }}
</summary>
<div>
<a href="{{ url_for('main.choose_service') }}">Switch service</a>
<a href="{{ url_for('main.add_service') }}">Add a new service to GOV.UK Notify</a>
</div>
</details>
</div>
<div class="column-half management-navigation-account">
<a href="{{ url_for('main.user_profile') }}">{{ current_user.name }}</a>
<a href="{{ url_for('main.sign_out')}}">Sign out</a>
</div>
</div>
</nav>
{% endif %}
<main id="content" role="main" class="page-container">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
{{ banner(
message,
'default' if category == 'default' else 'dangerous',
delete_button="Yes, delete this template" if 'delete' == category else None
)}}
{% endfor %}
{% endif %}
{% endwith %}
{% block fullwidth_content %}{% endblock %}
{% block fullwidth_content %}{% endblock %}
</main>
{% endblock %}

View File

@@ -1,10 +1,9 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% block page_title %}Unauthorized{% endblock %}
{% block fullwidth_content %}
<div class="grid-row">
<div class="column-two-thirds">
<h1>401</h1>
<p>You are not authorized to see this page.</p>
</div>
</div>
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">
<h1>401</h1>
<p>You are not authorized to see this page.</p>
</div>
{% endblock %}

View File

@@ -1,6 +1,6 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% block page_title %}Page not found{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">
<h1 class="heading-xlarge">

View File

@@ -1,12 +1,12 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% block page_title %}Server error{% endblock %}
{% block fullwidth_content %}
<div class="grid-row">
<div class="column-two-thirds">
<h1 class="heading-xlarge">
Sorry, we're experiencing technical difficulties
</h1>
<p>Try again later.</p>
</div>
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">
<h1 class="heading-xlarge">
Sorry, we're experiencing technical difficulties
</h1>
<p>Try again later.</p>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,12 @@
{% from "components/banner.html" import banner %}
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
{{ banner(
message,
'default' if category == 'default' else 'dangerous',
delete_button="Yes, delete this template" if 'delete' == category else None
)}}
{% endfor %}
{% endif %}
{% endwith %}

View File

@@ -1,7 +1,7 @@
<nav class="navigation">
<ul>
<li><a href="{{ url_for('.service_dashboard', service_id=service_id) }}">Dashboard</a></li>
</ul>
<h2 class="navigation-service-name">
<a href="{{ url_for('.service_dashboard', service_id=service_id) }}">{{ session.get('service_name', 'Service') }}</a>
</h2>
<ul>
<li><a href="{{ url_for('.send_sms', service_id=service_id) }}">Send text messages</a></li>
<li><a href="{{ url_for('.send_email', service_id=service_id) }}">Send emails</a></li>

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
@@ -6,7 +6,7 @@
GOV.UK Notify | Set up service
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,11 +1,11 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/browse-list.html" import browse_list %}
{% block page_title %}
GOV.UK Notify | Dashboard
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<h1 class="heading-xlarge">
Choose service

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
@@ -6,7 +6,7 @@
GOV.UK Notify
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
@@ -6,7 +6,7 @@
GOV.UK Notify
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
@@ -6,7 +6,7 @@
GOV.UK Notify
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,10 +1,10 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% block page_title %}
GOV.UK Notify |
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,10 +1,10 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% block page_title %}
GOV.UK Notify | Create a user account
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
@@ -6,7 +6,7 @@
GOV.UK Notify | Create an account
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,10 +1,10 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% block page_title %}
GOV.UK Notify | Get started
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
@@ -6,7 +6,7 @@
Sign in
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/banner.html" import banner %}
{% from "components/big-number.html" import big_number %}
@@ -13,7 +13,7 @@
Styleguide GOV.UK Notify
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<h1 class="heading-xlarge">
Styleguide

View File

@@ -1,10 +1,10 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% block page_title %}
GOV.UK Notify
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<!-- this page is just a hack for building out pages - same functionality as text-not-received, but for the register from an invite flow -->

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
@@ -6,7 +6,7 @@
GOV.UK Notify
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
@@ -6,7 +6,7 @@
GOV.UK Notify | Text verification
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,11 +1,11 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/table.html" import list_table, row, field %}
{% block page_title %}
GOV.UK Notify | Your profile
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<h1 class="heading-xlarge">Your profile</h1>

View File

@@ -1,10 +1,10 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% block page_title %}
GOV.UK Notify
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,10 +1,10 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% block page_title %}
GOV.UK Notify | Confirm mobile number
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,4 +1,4 @@
{% extends "admin_template.html" %}
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
@@ -6,7 +6,7 @@
GOV.UK Notify | Confirm email address and mobile number
{% endblock %}
{% block fullwidth_content %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-two-thirds">

View File

@@ -1,5 +1,4 @@
{% extends "admin_template.html" %}
{% from "components/banner.html" import banner %}
{% block fullwidth_content %}
<div class="grid-row">
@@ -11,6 +10,7 @@
'Your service is in restricted mode. You can only send notifications to yourself.',
'info'
) }}
{% include 'flash_messages.html' %}
{% block maincolumn_content %}{% endblock %}
</div>
</div>

View File

@@ -0,0 +1,6 @@
{% extends "admin_template.html" %}
{% block fullwidth_content %}
{% include 'flash_messages.html' %}
{% block maincolumn_content %}{% endblock %}
{% endblock %}

View File

@@ -34,7 +34,6 @@ gulp.task('javascripts', () => gulp
.src([
paths.npm + 'govuk_frontend_toolkit/javascripts/govuk/modules.js',
paths.npm + 'govuk_frontend_toolkit/javascripts/govuk/selection-buttons.js',
paths.src + 'javascripts/detailsPolyfill.js',
paths.src + 'javascripts/apiKey.js',
paths.src + 'javascripts/highlightTags.js',
paths.src + 'javascripts/main.js'

View File

@@ -14,7 +14,7 @@ def test_get_should_render_add_service_template(app_,
client.login(api_user_active)
response = client.get(url_for('main.add_service'))
assert response.status_code == 200
assert 'Set up notifications for your service' in response.get_data(as_text=True)
assert 'Add a new service' in response.get_data(as_text=True)
def test_should_add_service_and_redirect_to_next_page(app_,