Skip to content

Commit

Permalink
Merge pull request #687 from alphagov/center-layout
Browse files Browse the repository at this point in the history
Remove fixed layout, introduce back to top component
  • Loading branch information
NickColley committed Jan 24, 2019
2 parents 61383c3 + 2c6d774 commit 29b9cf8
Show file tree
Hide file tree
Showing 20 changed files with 247 additions and 110 deletions.
54 changes: 54 additions & 0 deletions __tests__/back-to-top.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* eslint-env jest */
const configPaths = require('../config/paths.json')
const PORT = configPaths.testPort

let browser
let page
let baseUrl = 'http://localhost:' + PORT

beforeAll(async (done) => {
browser = global.browser
page = await browser.newPage()
await page.evaluateOnNewDocument(() => {
window.__TESTS_RUNNING = true
})
done()
})

afterAll(async (done) => {
await page.close()
done()
})

const BACK_TO_TOP_LINK_SELECTOR = '[data-module="app-back-to-top"] a'

describe('Back to top', () => {
it('is always visible when JavaScript is disabled', async () => {
await page.setJavaScriptEnabled(false)
await page.goto(`${baseUrl}/styles/colour/`, { waitUntil: 'load' })
const isBackToTopVisible = await page.waitForSelector(BACK_TO_TOP_LINK_SELECTOR, { visible: true })
expect(isBackToTopVisible).toBeTruthy()
})
it('is hidden when at the top of the page', async () => {
await page.goto(`${baseUrl}/styles/colour/`, { waitUntil: 'load' })
const isBackToTopHidden = await page.waitForSelector(BACK_TO_TOP_LINK_SELECTOR, { visible: false })
expect(isBackToTopHidden).toBeTruthy()
})
it('is visible when at the bottom of the page', async () => {
await page.goto(`${baseUrl}/styles/colour/`, { waitUntil: 'load' })
// Scroll to the bottom of the page
await page.evaluate(() => window.scrollBy(0, document.body.scrollHeight))
const isBackToTopVisible = await page.waitForSelector(BACK_TO_TOP_LINK_SELECTOR, { visible: true })
expect(isBackToTopVisible).toBeTruthy()
})
it('goes back to the top of the page when interacted with', async () => {
await page.goto(`${baseUrl}/styles/colour/`, { waitUntil: 'load' })
// Scroll to the bottom of the page
await page.evaluate(() => window.scrollBy(0, document.body.scrollHeight))
// Make sure the back to top component is available to click
await page.waitForSelector(BACK_TO_TOP_LINK_SELECTOR, { visible: true })
await page.click(BACK_TO_TOP_LINK_SELECTOR)
const isAtTopOfPage = await page.evaluate(() => window.scrollY === 0)
expect(isAtTopOfPage).toBeTruthy()
})
})
6 changes: 6 additions & 0 deletions src/javascripts/application.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import BackToTop from './components/back-to-top.js'
import common from 'govuk-frontend/common'
import CookieBanner from './components/cookie-banner.js'
import Example from './components/example.js'
Expand Down Expand Up @@ -39,3 +40,8 @@ new MobileNav().init()
// Initialise search
var $searchContainer = document.querySelector('[data-module="app-search"]')
new Search($searchContainer).init()

// Initialise back to top
var $backToTop = document.querySelector('[data-module="app-back-to-top"]')
var $observedElement = document.querySelector('.app-subnav')
new BackToTop($backToTop, { $observedElement: $observedElement }).init()
62 changes: 62 additions & 0 deletions src/javascripts/components/back-to-top.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'govuk-frontend/vendor/polyfills/Function/prototype/bind'

function BackToTop ($module, options) {
this.$module = $module
this.$observedElement = options.$observedElement
this.intersectionRatio = 0
}

BackToTop.prototype.init = function () {
var $observedElement = this.$observedElement

// If there's no element for the back to top to follow, exit early.
if (!$observedElement) {
return
}

if (!('IntersectionObserver' in window)) {
// If there's no support fallback to regular sticky behaviour
return this.update()
}

// Create new IntersectionObserver
var observer = new window.IntersectionObserver(function (entries) {
// Available data when an intersection happens
// Back to top visibility
// Element enters the viewport
if (entries[0].intersectionRatio !== 0) {
// How much of the element is visible
this.intersectionRatio = entries[0].intersectionRatio
// Element leaves the viewport
} else {
this.intersectionRatio = 0
}
this.update()
}.bind(this), {
// Call the observer, when the element enters the viewport,
// when 25%, 50%, 75% and the whole element are visible
threshold: [0, 0.25, 0.5, 0.75, 1]
})

observer.observe($observedElement)
}

BackToTop.prototype.update = function () {
var thresholdPercent = (this.intersectionRatio * 100)

if (thresholdPercent === 100) {
this.hide()
} else if (thresholdPercent < 90) {
this.show()
}
}

BackToTop.prototype.hide = function () {
this.$module.classList.add('app-back-to-top--hidden')
}

BackToTop.prototype.show = function () {
this.$module.classList.remove('app-back-to-top--hidden')
}

export default BackToTop
21 changes: 21 additions & 0 deletions src/stylesheets/components/_back-to-top.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.app-back-to-top {
position: -webkit-sticky; // Needed for Safari on OSX
position: sticky; // sass-lint:disable-line no-duplicate-properties
top: govuk-spacing(6);
margin-bottom: govuk-spacing(6);
}

.app-back-to-top__icon {
display: inline-block;
width: .8em;
height: 1em;
margin-top: -(govuk-spacing(1));
margin-right: govuk-spacing(2);
vertical-align: middle;
}

@supports (position: sticky) {
.js-enabled .app-back-to-top--hidden {
display: none;
}
}
12 changes: 12 additions & 0 deletions src/stylesheets/components/_banner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.app-phase-banner {
@include govuk-media-query($until: tablet) {
margin-right: 0;
margin-left: 0;
padding-right: govuk-spacing(3);
padding-left: govuk-spacing(3);
}

@include govuk-media-query($from: tablet) {
border-bottom: 0;
}
}
6 changes: 0 additions & 6 deletions src/stylesheets/components/_cookie-banner.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,14 @@
width: 100%;

padding-top: govuk-spacing(3);
padding-right: govuk-spacing(3);
padding-bottom: govuk-spacing(3);
padding-left: govuk-spacing(3);
background-color: lighten(desaturate(govuk-colour("light-blue"), 8.46), 42.55);
}

.app-cookie-banner {
display: none;
}

.app-cookie-banner__message {
margin: 0;
}

@include govuk-media-query($media-type: print) {
.app-cookie-banner {
display: none !important;
Expand Down
7 changes: 0 additions & 7 deletions src/stylesheets/components/_footer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@
// GOV.UK Frontend footer adapted for full width

@include govuk-exports("app-footer") {
.app-footer {
@include govuk-media-query($from: tablet) {
display: flex;
flex-direction: column;
flex: 1 0 auto;
}
}

.app-width-container--full {
min-width: calc(100% - #{$govuk-gutter * 2});
Expand Down
7 changes: 6 additions & 1 deletion src/stylesheets/components/_header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@
@include govuk-exports("app-header") {

.app-header {
padding: govuk-spacing(2) govuk-spacing(3);
box-sizing: border-box;
width: 100%;
border-bottom: 10px solid govuk-colour("blue");
color: govuk-colour("white");
background: govuk-colour("black");
@include govuk-clearfix;

@include govuk-media-query($from: desktop) {
padding: govuk-spacing(2) 0;
}
}

.app-header__logotype {
Expand Down
10 changes: 6 additions & 4 deletions src/stylesheets/components/_navigation.scss
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
.app-navigation {
$navigation-height: 53px;
padding-right: govuk-spacing(3);
padding-left: govuk-spacing(3);
background-color: $app-light-grey;
box-sizing: border-box;
@include govuk-font(19, $weight: bold);
width: 100%;

@include govuk-media-query($until: tablet) {
display: none;
}

@include govuk-media-query($from: tablet) {
margin-left: -(govuk-spacing(3));
}

&__list {
margin: 0;
padding: 0;
list-style: none;

Expand Down
37 changes: 7 additions & 30 deletions src/stylesheets/components/_pane.scss
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
@include govuk-exports("app-pane") {
$toc-width: 300px;
$toc-width: 260px;
$toc-width-tablet: 210px;

.app-pane.app-pane--enabled {
$pane-height: 100vh;
overflow: hidden;

@include govuk-media-query($from: tablet) {
display: flex;
flex-direction: column;
}

@include govuk-media-query($from: tablet, $and: "(orientation: portrait)") {
height: $pane-height;
}

@include govuk-media-query($from: desktop) {
height: $pane-height;
}
}

.app-pane__header {
Expand All @@ -35,6 +24,7 @@
.app-pane__nav {
@include govuk-media-query($from: tablet) {
display: flex;
background-color: $app-light-grey;
flex-direction: column;
flex: 1 0 auto;
}
Expand All @@ -45,22 +35,17 @@
display: flex;
position: relative;
min-height: 0;
overflow: hidden;
flex: 1 1 100%;
overflow: inherit;
}

> * {
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
}
@include govuk-media-query(1160px) {
width: 100%;
}
}

.app-pane__subnav {
border-right: 1px solid $govuk-border-colour;
@include govuk-media-query($from: tablet) {
width: $toc-width-tablet;
border-right: 1px solid $govuk-border-colour;
flex: 0 0 auto;
}
@include govuk-media-query($from: desktop) {
Expand All @@ -72,15 +57,8 @@
@include govuk-media-query($from: tablet) {
display: flex;
min-width: 0;
margin-left: auto;
flex: 1 1 auto;
flex: 1 1 100%;
flex-direction: column;

// Stick footer to bottom of screen if content is shorter than viewport
main {
display: block;
flex: 1 0 auto;
}
}
}

Expand All @@ -105,7 +83,6 @@
.app-pane__content {
margin-left: -1px;
overflow-x: hidden;
border-left: 1px solid $govuk-border-colour;
}
}
}
6 changes: 5 additions & 1 deletion src/stylesheets/components/_subnav.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
@include govuk-exports("app-subnav") {

.app-subnav {
padding: govuk-spacing(3);
padding: govuk-spacing(6) govuk-spacing(3) 0 0;
@include govuk-font(16);

@include govuk-media-query($from: tablet) {
margin-left: -(govuk-spacing(3));
}
}

.app-subnav__section {
Expand Down
26 changes: 10 additions & 16 deletions src/stylesheets/main.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
$govuk-page-width: 1100px !default;

@import "govuk-frontend/all";

// App-specific variables
$app-light-grey: #f8f8f8;
$app-code-color: #dd1144;

// App-specific components
@import "components/back-to-top";
@import "components/banner";
@import "components/contact-panel";
@import "components/cookie-banner";
@import "components/example";
Expand Down Expand Up @@ -46,21 +50,6 @@ body {
}
}

// Mirrors Bootstrap 4 - open for discussion
$app-breakpoint-widescreen: 1200px;

// This will be coming to FE later but making it app specific for now
.app-site-width-container {
@media (min-width: $app-breakpoint-widescreen) {
max-width: 1100px;
}
}

// This layout is currently used on error pages like 404
.app-site-width-container--constraint {
max-width: 960px;
}

// This is consistent with Elements - will be changed in FE
[class*="govuk-grid-column"] {
@include govuk-media-query($until: desktop) {
Expand All @@ -79,7 +68,12 @@ $app-breakpoint-widescreen: 1200px;

.app-content {

@include govuk-responsive-padding(6);
padding: govuk-spacing(3) govuk-spacing(0);

@include govuk-media-query($from: tablet) {
padding: govuk-spacing(6);
padding-right: 0;
}

h1 {
max-width: 15em;
Expand Down
Loading

0 comments on commit 29b9cf8

Please sign in to comment.