Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE] Experiment with toggling the header colour via a top-level class in the review app #5746

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion packages/govuk-frontend-review/src/app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { filterPath, getDirectories, hasPath } from '@govuk-frontend/lib/files'
import { getStats, modulePaths } from '@govuk-frontend/stats'
import express from 'express'

import featureFlags from './common/lib/feature-flags.json' with { type: 'json' }
import { getFullPageExamples } from './common/lib/files.mjs'
import * as middleware from './common/middleware/index.mjs'
import * as nunjucks from './common/nunjucks/index.mjs'
Expand Down Expand Up @@ -67,6 +68,7 @@ export default async () => {
app.use(middleware.request)
app.use(middleware.robots)
app.use(middleware.banner)
app.use(middleware.featureFlags)

// Add build stats
app.locals.stats = Object.fromEntries(
Expand Down Expand Up @@ -184,9 +186,16 @@ export default async () => {
return next()
}

const showFeatureFlagBanner =
featureFlags.find((flag) =>
flag.affectedComponents.includes(componentName)
) !== undefined

res.render(componentName ? 'component' : 'components', {
componentsFixtures,
componentName
componentName,
showFeatureFlagBanner,
featureFlags: showFeatureFlagBanner && featureFlags
})
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"name": "Recolour",
"description": "An experimentation of changing the colours of some things",
"param": "recolour",
"cookieName": "use-recolour",
"affectedComponents": ["header"]
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import express from 'express'

import featureFlags from '../lib/feature-flags.json' with { type: 'json' }

const router = express.Router()

router.use((req, res, next) => {
// If we have the expected query param in the URL handle the cookie setting

for (const flag of featureFlags) {
if (req.method === 'GET' && flag.param in req.query) {
if (req.query[flag.param] === 'true') {
const maxAgeInDays = 28

res.cookie(flag.cookieName, 'yes', {
maxAge: maxAgeInDays * 24 * 60 * 60 * 1000,
httpOnly: true
})
} else {
res.clearCookie(flag.cookieName)
}

const urlWithoutQueryString = req.url.split('?')[0]
return res.redirect(urlWithoutQueryString)

Check warning

Code scanning / CodeQL

Server-side URL redirect Medium

Untrusted URL redirection depends on a
user-provided value
.
}
}

// Let express carry on and handle the request as usual
next()
})

router.use((req, res, next) => {
res.locals.featureFlagStates = {}

for (const flag of featureFlags) {
if (`${flag.param}Override` in req.query) {
res.locals.featureFlagStates[flag.param] =
req.query[`${flag.param}Override`] !== 'false'
} else {
res.locals.featureFlagStates[flag.param] =
req.cookies?.[flag.cookieName] === 'yes'
}
}
next()
})

router.use((req, res, next) => {
res.locals.showAllFlagStates =
'showAllFlagStates' in req.query && req.query.showAllFlagStates === 'true'
next()
})

export default router
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
export { default as assets } from './assets.mjs'
export { default as banner } from './banner.mjs'
export { default as docs } from './docs.mjs'
export { default as featureFlags } from './feature-flags.mjs'
export { default as request } from './request.mjs'
export { default as robots } from './robots.mjs'
1 change: 1 addition & 0 deletions packages/govuk-frontend-review/src/stylesheets/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ $govuk-suppressed-warnings: ("organisation-colours");
@import "partials/app";
@import "partials/code";
@import "partials/banner";
@import "partials/feature-flag-banner";
@import "partials/organisation-swatch";
@import "partials/prose";
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
@import "govuk/base";
@import "govuk/core";

.app-header--campaign {
.app-header--campaign,
.govuk-feature--recolour .app-header--campaign {
padding-bottom: govuk-spacing(2);
}

.govuk-feature--recolour .app-header--campaign {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ I like how having a global class enabling the feature allows us to easily update the app code to accomodate the flag (though I'd guess in a service, you'd either have or not have the flag enable so would need that less so).

padding-bottom: 0;
border-bottom: $govuk-border-width-wide solid #fff500;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.app-feature-flag-banner {
padding: govuk-spacing(4) 0;
border-bottom: 1px solid govuk-colour("mid-grey");
background-color: govuk-colour("light-grey");
}
6 changes: 5 additions & 1 deletion packages/govuk-frontend-review/src/views/component.njk
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@
</h1>
</div>

{% if showFeatureFlagBanner %}
{% include "./partials/featureFlags.njk" %}
{% endif %}

{% block examples %}
{{ showExamples(componentFixtures) }}
{{ showExamples(componentFixtures, false, showAllFlagStates) }}
{% endblock %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{% extends "layouts/_generic.njk" %}

{% set htmlClasses = "govuk-feature--recolour" if featureFlagStates['recolour'] else "" %}


{% block banner %}
{% include "../partials/exampleBanner.njk" %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% extends "layouts/_generic.njk" %}

{% set htmlClasses = "app-template" %}
{% set htmlClasses = "app-template" + " govuk-feature--recolour" if featureFlagStates['recolour'] else "" %}

{% block pageTitle %}GOV.UK Frontend{% endblock %}

Expand Down
18 changes: 14 additions & 4 deletions packages/govuk-frontend-review/src/views/macros/showExamples.njk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% from "govuk/components/details/macro.njk" import govukDetails %}

{% macro showExamples(componentFixtures, exampleNames) %}
{% macro showExamples(componentFixtures, exampleNames, renderTwoIframes) %}

{% set componentName = componentFixtures.component %}

Expand Down Expand Up @@ -32,9 +32,19 @@
</p>
{% endif %}
</div>
<div class="app-component-preview">
<iframe src="{{ path }}?iframe=true" loading="{{ "eager" if loop.index <= 3 else "lazy" }}" class="js-component-preview app-component-preview__iframe"></iframe>
</div>

{% if renderTwoIframes %}
<div class="app-component-preview">
<iframe src="{{ path }}?iframe=true&recolourOverride=true" loading="{{ "eager" if loop.index <= 3 else "lazy" }}" class="js-component-preview app-component-preview__iframe"></iframe>
</div>
<div class="app-component-preview">
<iframe src="{{ path }}?iframe=true&recolourOverride=false" loading="{{ "eager" if loop.index <= 3 else "lazy" }}" class="js-component-preview app-component-preview__iframe"></iframe>
</div>
{% else %}
<div class="app-component-preview">
<iframe src="{{ path }}?iframe=true" loading="{{ "eager" if loop.index <= 3 else "lazy" }}" class="js-component-preview app-component-preview__iframe"></iframe>
</div>
{% endif %}

<div class="govuk-width-container">
{% set codeExamplesHtml %}
Expand Down
21 changes: 21 additions & 0 deletions packages/govuk-frontend-review/src/views/partials/featureFlags.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div class="app-feature-flag-banner">
<div class="govuk-width-container">
<h2 class="govuk-heading-m">Feature flag control</h2>
<p class="govuk-body">This component is impacted by the following feature flags:</p>

{% for flag in featureFlags %}
<h3 class="govuk-heading-s">{{ flag.name }}</h3>
<p class="govuk-body">{{ flag.description }}</p>
<p class="govuk-body">
<a class="govuk-button govuk-!-margin-bottom-0" href="?{{ flag.param }}={{ not featureFlagStates[flag.param] }}">
Turn flag '{{ flag.name }}' {{ 'off' if featureFlagStates[flag.param] else 'on' }}
</a>
</p>
{% endfor %}
<p class="govuk-body">
<a class="govuk-button govuk-!-margin-bottom-0" href="?showAllFlagStates={{ not showAllFlagStates }}">
{{ 'Only show one example state' if showAllFlagStates else 'Show both states' }}
</a>
</p>
</div>
</div>
82 changes: 82 additions & 0 deletions packages/govuk-frontend/src/govuk/components/header/_index.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@include govuk-exports("govuk/component/header") {
$govuk-header-background: govuk-colour("black");
$govuk-recoloured-header-background: govuk-colour("blue");
$govuk-header-border-color: $govuk-brand-colour;
$govuk-header-border-width: govuk-spacing(2);
$govuk-header-text: govuk-colour("white");
Expand Down Expand Up @@ -352,4 +353,85 @@
}
}
}

.govuk-feature--recolour {
.govuk-header {
border-bottom: 1px solid $govuk-recoloured-header-background;
background: $govuk-recoloured-header-background;
}

.govuk-header__container {
margin-bottom: 0;
padding: 0;
border-bottom: 0;
}

.govuk-header__service-name {
margin: govuk-spacing(2) 0 0;
}

.govuk-header__logo {
@include govuk-responsive-padding($govuk-header-vertical-spacing-value, "top");
}

.govuk-header__navigation {
margin-bottom: 0;
}

.govuk-header__navigation--end {
padding: 0;
}

.govuk-header__navigation-list {
@include govuk-media-query($until: desktop) {
padding-top: govuk-spacing(1);
}
}

.govuk-header__navigation-item {
margin: govuk-spacing(2) 0 govuk-spacing(4);
padding: 0;
border-bottom: 0;

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

a {
@include govuk-typography-weight-regular;
}
}

.govuk-header__navigation-item--active {
border: 0 solid;

@include govuk-media-query($until: desktop) {
margin-left: govuk-spacing(-3);
padding-left: govuk-spacing(2);
border-left-width: govuk-spacing(1);
}

@include govuk-media-query($from: desktop) {
padding-bottom: govuk-spacing(2);
border-bottom-width: govuk-spacing(1);
}

a {
&:link,
&:hover,
&:visited {
color: inherit;
}

&:focus {
color: $govuk-focus-text-colour;
}
}
}

.govuk-header__navigation-item:last-child {
margin-bottom: govuk-spacing(3);
}
}
}