-
Notifications
You must be signed in to change notification settings - Fork 6
Ent 2784 pro bar #1191
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
Ent 2784 pro bar #1191
Changes from 3 commits
1beced3
2018718
f457564
094dccd
eed007a
19c8d48
2e74218
b7a0649
940bc8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,175 @@ | ||
| /** | ||
| * Track a view event for a given DOM selector by dispatching an `oTracking.event`. | ||
| * | ||
| * @param {Object} [options] - Optional configuration. | ||
| * @param {string} [options.selector] - CSS selector for the element to track. | ||
| * @returns {void} | ||
| */ | ||
| const trackBarView = (options) => { | ||
| const selector = options && options.selector | ||
| const elementToTrack = document.querySelector(selector) | ||
| if (!elementToTrack) { | ||
| return | ||
| } | ||
|
|
||
| const eventData = { | ||
| action: 'view', | ||
| category: 'component', | ||
| className: elementToTrack.className, | ||
| url: window.document.location.href || null, | ||
| nodeName: elementToTrack.nodeName, | ||
| component_name: 'pro-bar' | ||
| } | ||
|
|
||
| document.body.dispatchEvent(new CustomEvent('oTracking.event', { detail: eventData, bubbles: true })) | ||
| } | ||
|
|
||
| /** | ||
| * Update the organisation title shown in the coving element. | ||
| * | ||
| * Fetches licence information from the provided proNavigationApi URL and updates | ||
| * the organisation name in the coving element. Emits tracking events on error. | ||
| * | ||
| * @param {Object} options - Configuration options. | ||
| * @param {string} options.proNavigationApi - URL to fetch licence info from. | ||
| * @returns {Promise<void>} | ||
| */ | ||
| const updateTitle = async (options) => { | ||
| if (!isDesktopOrTabletView()) { | ||
| return | ||
| } | ||
| const { proNavigationApi } = options | ||
|
|
||
| const coving = document.querySelector(`.n-layout__pro-coving`) | ||
| const textContainer = document.querySelector('.n-layout__pro-coving-text') | ||
| if (!coving || coving.length === 0) { | ||
| return | ||
| } | ||
|
|
||
| if (!textContainer || textContainer.length === 0) { | ||
| return | ||
| } | ||
|
|
||
| try { | ||
| const licenceInfo = await fetchLicenceInfo(proNavigationApi) | ||
|
|
||
| if (!licenceInfo || !licenceInfo.organisationName) { | ||
| return | ||
| } | ||
|
|
||
| if (licenceInfo.organisationName && licenceInfo.organisationName.length < 51) { | ||
| textContainer.classList.add('is-fading-out') | ||
| setTimeout(() => { | ||
| updateOrganisationName(coving, licenceInfo.organisationName) | ||
| textContainer.classList.remove('is-fading-out') | ||
| textContainer.classList.add('is-fading-in') | ||
| }, 500) | ||
|
||
| } | ||
| } catch (error) { | ||
| const isFetchError = error.message.includes('fetch') | ||
| const eventData = { | ||
| action: isFetchError ? 'fetch' : 'update', | ||
|
||
| category: 'error', | ||
| component_name: 'pro-bar', | ||
| errorMessage: error.message | ||
| } | ||
| document.body.dispatchEvent(new CustomEvent('oTracking.event', { detail: eventData, bubbles: true })) | ||
| } finally { | ||
| setTimeout(() => { | ||
| textContainer.classList.remove('is-fading-out') | ||
| textContainer.classList.remove('is-fading-in') | ||
| }, 510) | ||
|
||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Fetch licence information from the given URL. | ||
| * | ||
| * Uses fetch with credentials included. Throws an Error if the response is not ok. | ||
| * | ||
| * @param {string} url - The API endpoint to fetch licence info from. | ||
| * @returns {Promise<Object>} Resolves with the parsed JSON response. | ||
| * @throws {Error} If the network response is not ok. | ||
| */ | ||
| const fetchLicenceInfo = async (url) => { | ||
| const response = await fetch(url, { credentials: 'include' }) | ||
| if (!response.ok) { | ||
| throw new Error(`Error during licence info fetch! Status: ${response.status}`) | ||
| } | ||
| return response.json() | ||
| } | ||
|
|
||
| /** | ||
| * Update the organisation name within the coving element. | ||
| * | ||
| * @param {Element} covingEl - The coving DOM element that contains the organisation name element. | ||
| * @param {string} organisationName - The organisation name to display. | ||
| * @returns {void} | ||
| */ | ||
| const updateOrganisationName = (covingEl, organisationName) => { | ||
| if (!covingEl || !organisationName) { | ||
| return | ||
| } | ||
|
|
||
| const organisationNameEl = covingEl.querySelector('.n-layout__pro-coving-organisation') | ||
| if (organisationNameEl) { | ||
| organisationNameEl.textContent = organisationName | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Determine if the current device is a desktop or tablet. | ||
| * | ||
| * Checks the user agent to identify mobile devices. Uses the modern `navigator.userAgentData` API | ||
| * if available, otherwise falls back to parsing `navigator.userAgent`. Returns `true` for desktop | ||
| * and tablet devices, `false` for mobile phones. | ||
| * | ||
| * @returns {boolean} `true` if the device is desktop or tablet, `false` if mobile. | ||
| */ | ||
| function isDesktopOrTabletView() { | ||
| if (navigator.userAgentData && navigator.userAgentData.mobile) { | ||
| return !navigator.userAgentData.mobile | ||
| } | ||
|
|
||
| const ua = navigator.userAgent.toLowerCase() | ||
|
|
||
| if (ua.includes('ipad') || (ua.includes('macintosh') && 'ontouchend' in window)) { | ||
| return true | ||
| } | ||
|
|
||
| if (ua.includes('iphone') || ua.includes('ipod')) { | ||
| return false | ||
| } | ||
|
|
||
| if (ua.includes('android') && ua.includes('mobile')) { | ||
| return false | ||
| } | ||
|
|
||
| if ( | ||
| ua.includes('windows phone') || | ||
| ua.includes('blackberry') || | ||
| ua.includes('bb10') || | ||
| ua.includes('opera mini') | ||
| ) { | ||
| return false | ||
| } | ||
|
|
||
| return true | ||
| } | ||
|
|
||
| /** | ||
| * Initialise the ProBar component: track view and update title. | ||
| * | ||
| * @returns {void} | ||
| */ | ||
| const init = () => { | ||
| trackBarView({ selector: '.n-layout__pro-coving' }) | ||
|
|
||
| updateTitle({ | ||
| proNavigationApi: 'https://pro-navigation.ft.com/api/licence/info' | ||
| }) | ||
| } | ||
|
|
||
| export const ProBar = { | ||
| init | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||||||
| @import '@financial-times/o3-foundation/css/professional.css'; | ||||||||
|
|
||||||||
| .n-layout__pro-coving { | ||||||||
| display: flex; | ||||||||
| width: 100%; | ||||||||
| background-color: var(--o3-color-palette-mint); | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thought: it would be good to get this (and the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's a good idea.. however, I will add that to the professional brand only even though the header coving is a layout element now..
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @apaleslimghost is this a use case value you see being used in FT Pink also? |
||||||||
| padding-top: var(--o3-spacing-4xs); | ||||||||
| padding-bottom: var(--o3-spacing-4xs); | ||||||||
|
||||||||
| padding-top: var(--o3-spacing-4xs); | |
| padding-bottom: var(--o3-spacing-4xs); | |
| padding-block: var(--o3-spacing-4xs); |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion:
| padding: 0 var(--o3-spacing-3xs); | |
| padding-inline: var(--o3-spacing-3xs); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import React from 'react' | ||
|
|
||
| export const HeaderCoving = () => { | ||
| return ( | ||
| <div data-o3-brand="professional" className={`n-layout__pro-coving`}> | ||
| <div className={`n-layout__pro-coving-text`}> | ||
| <span className={`n-layout__pro-coving-brand`}>FT PROFESSIONAL</span> | ||
|
||
| <span className={`n-layout__pro-coving-organisation`}></span> | ||
| </div> | ||
| </div> | ||
| ) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion:
querySelectoreither returns the element, ornull; it won't ever return something withlength: 0There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and actually, this can be merged with the
textContainercheck:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch... forgot to remove after changing to querySelector