Skip to content

Commit

Permalink
Merge pull request #4205 from alphagov/use-new-components
Browse files Browse the repository at this point in the history
Update components to use `Component`
  • Loading branch information
romaricpascal authored Oct 11, 2024
2 parents bcccc94 + 04a7ba2 commit 25885b2
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 148 deletions.
9 changes: 9 additions & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* As govuk-frontend provides no types, TypeScript will type its exports as `any`,
* but be unable to acknowledge fields inherited from parent classes
* leading to errors when trying to assign or use them.
*
* TypeScript's shorthand ambient modules seem to also make inherited fields typed as `any`.
* https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#shorthand-ambient-module-declarations
*/
declare module "govuk-frontend";
8 changes: 6 additions & 2 deletions src/javascripts/application.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ createAll(Copy)
new OptionsTable()

// Initialise mobile navigation
new Navigation(document)
createAll(Navigation)

// Initialise scrollable container handling
createAll(ScrollContainer)
Expand All @@ -70,7 +70,11 @@ const lazyEmbedObserver = new IntersectionObserver(function (
) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
new EmbedCard(entry.target)
try {
new EmbedCard(entry.target)
} catch (error) {
console.log(error)
}
}
})
})
Expand Down
28 changes: 11 additions & 17 deletions src/javascripts/components/back-to-top.mjs
Original file line number Diff line number Diff line change
@@ -1,34 +1,28 @@
import { Component } from 'govuk-frontend'

/**
* Website back to top link
*/
class BackToTop {
class BackToTop extends Component {
static moduleName = 'app-back-to-top'

/**
* @param {Element} $module - HTML element
*/
constructor($module) {
if (
!($module instanceof HTMLElement) ||
!document.body.classList.contains('govuk-frontend-supported')
) {
return this
}
super($module)

this.$module = $module
const $footer = document.querySelector('.app-footer')
const $subNav = document.querySelector('.app-subnav')

// Check if we can use Intersection Observers
if (!('IntersectionObserver' in window)) {
// If there's no support fallback to regular behaviour
// Since JavaScript is enabled we can remove the default hidden state
this.$module.classList.remove('app-back-to-top--hidden')
this.$root.classList.remove('app-back-to-top--hidden')
return this
}

const $footer = document.querySelector('.app-footer')
const $subNav = document.querySelector('.app-subnav')

// Check if there is anything to observe
if (!$footer || !$subNav) {
return this
}
Expand All @@ -53,17 +47,17 @@ class BackToTop {

// If the subnav or the footer not visible then fix the back to top link to follow the user
if (subNavIsIntersecting || footerIsIntersecting) {
this.$module.classList.remove('app-back-to-top--fixed')
this.$root.classList.remove('app-back-to-top--fixed')
} else {
this.$module.classList.add('app-back-to-top--fixed')
this.$root.classList.add('app-back-to-top--fixed')
}

// If the subnav is visible but you can see it all at once, then a back to top link is likely not as useful.
// We hide the link but make it focusable for screen readers users who might still find it useful.
if (subNavIsIntersecting && subNavIntersectionRatio === 1) {
this.$module.classList.add('app-back-to-top--hidden')
this.$root.classList.add('app-back-to-top--hidden')
} else {
this.$module.classList.remove('app-back-to-top--hidden')
this.$root.classList.remove('app-back-to-top--hidden')
}
})

Expand Down
55 changes: 29 additions & 26 deletions src/javascripts/components/cookie-banner.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Component } from 'govuk-frontend'

import * as CookieFunctions from './cookie-functions.mjs'

const cookieBannerAcceptSelector = '.js-cookie-banner-accept'
Expand All @@ -10,26 +12,36 @@ const cookieConfirmationRejectSelector = '.js-cookie-banner-confirmation-reject'
/**
* Website cookie banner
*/
class CookieBanner {
class CookieBanner extends Component {
/**
* Check support of CookieBanner
*/
static checkSupport() {
Component.checkSupport()

if (CookieBanner.onCookiesPage()) {
throw Error('Cancelled initialisation as on cookie page')
}
}

/**
* Check if on the Cookies page
*
* @returns {boolean} Returns true if on the Cookies page
*/
static onCookiesPage() {
return window.location.pathname === '/cookies/'
}

static moduleName = 'govuk-cookie-banner'
/**
* @param {Element} $module - HTML element
*/
constructor($module) {
if (
!($module instanceof HTMLElement) ||
!document.body.classList.contains('govuk-frontend-supported') ||
// Exit if we're on the cookies page to avoid circular journeys
this.onCookiesPage()
) {
return this
}
super($module)

this.$cookieBanner = $module
this.cookieCategory =
(this.$cookieBanner.dataset &&
this.$cookieBanner.dataset.cookieCategory) ||
'analytics'
(this.$root.dataset && this.$root.dataset.cookieCategory) || 'analytics'

const $acceptButton = $module.querySelector(cookieBannerAcceptSelector)
const $rejectButton = $module.querySelector(cookieBannerRejectSelector)
Expand Down Expand Up @@ -60,7 +72,7 @@ class CookieBanner {
this.$cookieMessage = $cookieMessage
this.$cookieConfirmationAccept = $cookieConfirmationAccept
this.$cookieConfirmationReject = $cookieConfirmationReject
this.$cookieBannerHideButtons = $cookieBannerHideButtons
this.$rootHideButtons = $cookieBannerHideButtons

// Show the cookie banner to users who have not consented or have an
// outdated consent cookie
Expand All @@ -74,13 +86,13 @@ class CookieBanner {
// set previously
CookieFunctions.resetCookies()

this.$cookieBanner.removeAttribute('hidden')
this.$root.removeAttribute('hidden')
}

this.$acceptButton.addEventListener('click', () => this.acceptCookies())
this.$rejectButton.addEventListener('click', () => this.rejectCookies())

this.$cookieBannerHideButtons.forEach(($cookieBannerHideButton) => {
this.$rootHideButtons.forEach(($cookieBannerHideButton) => {
$cookieBannerHideButton.addEventListener('click', () => this.hideBanner())
})
}
Expand All @@ -89,7 +101,7 @@ class CookieBanner {
* Hide banner
*/
hideBanner() {
this.$cookieBanner.setAttribute('hidden', 'true')
this.$root.setAttribute('hidden', 'true')
}

/**
Expand Down Expand Up @@ -135,15 +147,6 @@ class CookieBanner {

confirmationMessage.focus()
}

/**
* Check if on the Cookies page
*
* @returns {boolean} Returns true if on the Cookies page
*/
onCookiesPage() {
return window.location.pathname === '/cookies/'
}
}

export default CookieBanner
17 changes: 6 additions & 11 deletions src/javascripts/components/cookies-page.mjs
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import { Component } from 'govuk-frontend'

import { getConsentCookie, setConsentCookie } from './cookie-functions.mjs'

/**
* Website cookies page
*/
class CookiesPage {
class CookiesPage extends Component {
static moduleName = 'app-cookies-page'
/**
* @param {Element} $module - HTML element
*/
constructor($module) {
if (
!($module instanceof HTMLElement) ||
!document.body.classList.contains('govuk-frontend-supported')
) {
return this
}

this.$page = $module
super($module)

const $cookieForm = this.$page.querySelector('.js-cookies-page-form')
const $cookieForm = this.$root.querySelector('.js-cookies-page-form')
if (!($cookieForm instanceof HTMLFormElement)) {
return this
}
Expand All @@ -43,7 +38,7 @@ class CookiesPage {
this.$cookieFormFieldsets = $cookieFormFieldsets
this.$cookieFormButton = $cookieFormButton

const $successNotification = this.$page.querySelector(
const $successNotification = this.$root.querySelector(
'.js-cookies-page-success'
)
if ($successNotification instanceof HTMLElement) {
Expand Down
30 changes: 17 additions & 13 deletions src/javascripts/components/copy.mjs
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import ClipboardJS from 'clipboard'
import { Component } from 'govuk-frontend'

/**
* Copy button for code examples
*/
class Copy {
class Copy extends Component {
static moduleName = 'app-copy'

/**
* @param {Element} $module - HTML element
* Check if ClipboardJS is supported
*/
constructor($module) {
if (
!($module instanceof HTMLElement) ||
!document.body.classList.contains('govuk-frontend-supported') ||
!ClipboardJS.isSupported()
) {
return this
static checkSupport() {
Component.checkSupport()

if (!ClipboardJS.isSupported()) {
throw Error('ClipboardJS not supported in this browser')
}
}

this.$module = $module
/**
* @param {Element} $module - HTML element
*/
constructor($module) {
super($module)

this.$pre = this.$module.querySelector('pre')
this.$pre = this.$root.querySelector('pre')
// TODO: Throw once GOV.UK Frontend exports its errors

/** @type {number | null} */
Expand All @@ -34,8 +38,8 @@ class Copy {
this.$status.className = 'govuk-visually-hidden'
this.$status.setAttribute('aria-live', 'assertive')

this.$module.prepend(this.$status)
this.$module.prepend(this.$button)
this.$root.prepend(this.$status)
this.$root.prepend(this.$button)

const $clipboard = new ClipboardJS(this.$button, {
target: () => this.$pre
Expand Down
37 changes: 24 additions & 13 deletions src/javascripts/components/embed-card.mjs
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import { Component } from 'govuk-frontend'

import { getConsentCookie } from './cookie-functions.mjs'

/**
* Embed Card Youtube functionality
*/
class EmbedCard {
class EmbedCard extends Component {
/**
* @param {Element} $module - HTML element
* Check EmbedCard support
*/
constructor($module) {
if (
!($module instanceof HTMLElement) ||
!document.body.classList.contains('govuk-frontend-supported')
) {
return this
static checkSupport() {
Component.checkSupport()

const consentCookie = getConsentCookie()

if (!consentCookie || (consentCookie && !consentCookie.campaign)) {
throw Error('Campaign consent cookies not accepted')
}
}

static moduleName = 'app-embed-card'

this.$module = $module
/**
* @param {Element} $module - HTML element
*/
constructor($module) {
super($module)

this.replacePlaceholder()
}
Expand All @@ -26,17 +36,17 @@ class EmbedCard {
* Replaces the placeholder with the iframe if cookies are set.
*/
replacePlaceholder() {
if (this.$module.querySelector('iframe')) {
if (this.$root.querySelector('iframe')) {
return
}

const consentCookie = getConsentCookie()

if (consentCookie && consentCookie.campaign) {
const placeholder = this.$module.querySelector(
const placeholder = this.$root.querySelector(
'.app-embed-card__placeholder'
)
const placeholderText = this.$module.querySelector(
const placeholderText = this.$root.querySelector(
'.app-embed-card__placeholder-text'
)

Expand All @@ -51,7 +61,7 @@ class EmbedCard {

placeholder.remove()

const iframeContainer = this.$module.querySelector(
const iframeContainer = this.$root.querySelector(
'.app-embed-card__placeholder-iframe-container'
)
iframeContainer.appendChild(iframe)
Expand All @@ -65,6 +75,7 @@ class EmbedCard {
*
* @param {string} ytId - YouTube ID
* @param {string} title - Title for iFrame (for screen readers)
* @returns {HTMLElement} - iframe element
*/
createIframe(ytId, title) {
const iframe = document.createElement('IFRAME')
Expand Down
Loading

0 comments on commit 25885b2

Please sign in to comment.