Skip to content

Commit 0cfdd20

Browse files
authored
Merge pull request #1202 from Financial-Times/BOLT-211-add-mobile-dropdown-options
Bolt 211 add mobile dropdown options
2 parents bb129f0 + b361cf1 commit 0cfdd20

16 files changed

Lines changed: 623 additions & 25 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
describe('Sub-navigation (/subnav)', () => {
2+
beforeAll(async () => {
3+
await page.goto('http://localhost:3456?enableSubNavigation=true', { waitUntil: 'load' })
4+
})
5+
6+
it('renders the primary header navigation', async () => {
7+
await expect(page).toMatchElement('#site-navigation')
8+
await expect(page).toMatchElement('.o-header__nav')
9+
})
10+
11+
it('renders the subnav container and items', async () => {
12+
await expect(page).toMatchElement('.o-header__subnav')
13+
await expect(page).toMatchElement('.o-header__subnav-item')
14+
})
15+
16+
it('renders subnav dropdowns as initially hidden/collapsed', async () => {
17+
await expect(page).toMatchElement('.o-header__subnav-dropdown[aria-hidden="true"]')
18+
await expect(page).toMatchElement('.o-header__subnav-dropdown[aria-expanded="false"]')
19+
})
20+
21+
it('renders subnav dropdown items and links', async () => {
22+
await expect(page).toMatchElement('.o-header__subnav-dropdown')
23+
await expect(page).toMatchElement('.o-header__subnav-dropdown-item')
24+
await expect(page).toMatchElement('.o-header__subnav-dropdown-link')
25+
})
26+
})

examples/ft-ui/client/main.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
@import '@financial-times/o3-foundation/css/core.css';
2-

examples/ft-ui/constants.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
export const subnavData = [
2+
{ label: 'Monetary Policy Radar', url: '/central-banks/monetary-policy-radar', selected: false },
3+
{
4+
label: 'Federal Reserve',
5+
url: null,
6+
selected: false,
7+
subnavDropdownOptions: [
8+
{ label: 'Coverage', url: '/banks/federal-reserve/coverage', trackable: 'FED_DROPDOWN_COVERAGE' },
9+
{
10+
label: 'Policy Rate Scenarios',
11+
url: '/banks/federal-reserve/policy-rate-scenarios',
12+
trackable: 'FED_DROPDOWN_POLICY_RATE_SCENARIOS'
13+
},
14+
{
15+
label: 'Doves and Hawks',
16+
url: '/banks/federal-reserve/doves-and-hawks',
17+
trackable: 'FED_DROPDOWN_DOVES_AND_HAWKS'
18+
},
19+
{
20+
label: 'Central bankers views',
21+
url: '/banks/federal-reserve/views',
22+
trackable: 'FED_DROPDOWN_CENTRAL_BANKERS_VIEWS'
23+
},
24+
{
25+
label: 'Profile of central bankers',
26+
url: '/banks/federal-reserve/profiles',
27+
trackable: 'FED_DROPDOWN_PROFILES'
28+
},
29+
{
30+
label: "Analysts' views",
31+
url: '/banks/federal-reserve/analysts-views/all-estimates',
32+
trackable: 'FED_DROPDOWN_ANALYSTS_VIEWS'
33+
},
34+
{ label: 'Data', url: '/banks/federal-reserve/data', trackable: 'FED_DROPDOWN_DATA' },
35+
{ label: 'Calendar', url: '/banks/federal-reserve/calendar', trackable: 'FED_DROPDOWN_CALENDAR' }
36+
],
37+
isSubnavDropdownEnabled: true
38+
},
39+
{
40+
label: 'European Central Bank',
41+
url: null,
42+
selected: false,
43+
subnavDropdownOptions: [
44+
{ label: 'Coverage', url: '/banks/european-central-bank/coverage', trackable: 'ECB_DROPDOWN_COVERAGE' },
45+
{
46+
label: 'Policy Rate Scenarios',
47+
url: '/banks/european-central-bank/policy-rate-scenarios',
48+
trackable: 'ECB_DROPDOWN_POLICY_RATE_SCENARIOS'
49+
},
50+
{
51+
label: 'Doves and Hawks',
52+
url: '/banks/european-central-bank/doves-and-hawks',
53+
trackable: 'ECB_DROPDOWN_DOVES_AND_HAWKS'
54+
},
55+
{
56+
label: 'Central bankers views',
57+
url: '/banks/european-central-bank/views',
58+
trackable: 'ECB_DROPDOWN_CENTRAL_BANKERS_VIEWS'
59+
},
60+
{
61+
label: 'Profile of central bankers',
62+
url: '/banks/european-central-bank/profiles',
63+
trackable: 'ECB_DROPDOWN_PROFILES'
64+
},
65+
{
66+
label: "Analysts' views",
67+
url: '/banks/european-central-bank/analysts-views/all-estimates',
68+
trackable: 'ECB_DROPDOWN_ANALYSTS_VIEWS'
69+
},
70+
{ label: 'Data', url: '/banks/european-central-bank/data', trackable: 'ECB_DROPDOWN_DATA' },
71+
{ label: 'Calendar', url: '/banks/european-central-bank/calendar', trackable: 'ECB_DROPDOWN_CALENDAR' }
72+
],
73+
isSubnavDropdownEnabled: true
74+
},
75+
{
76+
label: 'Bank of England',
77+
url: null,
78+
selected: false,
79+
subnavDropdownOptions: [
80+
{ label: 'Coverage', url: '/banks/bank-of-england/coverage', trackable: 'BOE_DROPDOWN_COVERAGE' },
81+
{
82+
label: 'Policy Rate Scenarios',
83+
url: '/banks/bank-of-england/policy-rate-scenarios',
84+
trackable: 'BOE_DROPDOWN_POLICY_RATE_SCENARIOS'
85+
},
86+
{
87+
label: 'Doves and Hawks',
88+
url: '/banks/bank-of-england/doves-and-hawks',
89+
trackable: 'BOE_DROPDOWN_DOVES_AND_HAWKS'
90+
},
91+
{
92+
label: 'Central bankers views',
93+
url: '/banks/bank-of-england/views',
94+
trackable: 'BOE_DROPDOWN_CENTRAL_BANKERS_VIEWS'
95+
},
96+
{
97+
label: 'Profile of central bankers',
98+
url: '/banks/bank-of-england/profiles',
99+
trackable: 'BOE_DROPDOWN_PROFILES'
100+
},
101+
{
102+
label: "Analysts' views",
103+
url: '/banks/bank-of-england/analysts-views/all-estimates',
104+
trackable: 'BOE_DROPDOWN_ANALYSTS_VIEWS'
105+
},
106+
{ label: 'Data', url: '/banks/bank-of-england/data', trackable: 'BOE_DROPDOWN_DATA' },
107+
{ label: 'Calendar', url: '/banks/bank-of-england/calendar', trackable: 'BOE_DROPDOWN_CALENDAR' }
108+
],
109+
isSubnavDropdownEnabled: true
110+
},
111+
{
112+
label: 'Bank of Japan',
113+
url: null,
114+
selected: false,
115+
subnavDropdownOptions: [
116+
{ label: 'Coverage', url: '/banks/bank-of-japan/coverage', trackable: 'BOJ_DROPDOWN_COVERAGE' },
117+
{
118+
label: 'Policy Rate Scenarios',
119+
url: '/banks/bank-of-japan/policy-rate-scenarios',
120+
trackable: 'BOJ_DROPDOWN_POLICY_RATE_SCENARIOS'
121+
},
122+
{
123+
label: 'Doves and Hawks',
124+
url: '/banks/bank-of-japan/doves-and-hawks',
125+
trackable: 'BOJ_DROPDOWN_DOVES_AND_HAWKS'
126+
},
127+
{
128+
label: 'Central bankers views',
129+
url: '/banks/bank-of-japan/views',
130+
trackable: 'BOJ_DROPDOWN_CENTRAL_BANKERS_VIEWS'
131+
},
132+
{
133+
label: 'Profile of central bankers',
134+
url: '/banks/bank-of-japan/profiles',
135+
trackable: 'BOJ_DROPDOWN_PROFILES'
136+
},
137+
{
138+
label: "Analysts' views",
139+
url: '/banks/bank-of-japan/analysts-views/all-estimates',
140+
trackable: 'BOJ_DROPDOWN_ANALYSTS_VIEWS'
141+
},
142+
{ label: 'Data', url: '/banks/bank-of-japan/data', trackable: 'BOJ_DROPDOWN_DATA' },
143+
{ label: 'Calendar', url: '/banks/bank-of-japan/calendar', trackable: 'BOJ_DROPDOWN_CALENDAR' }
144+
],
145+
isSubnavDropdownEnabled: true
146+
},
147+
{ label: 'Global Coverage', url: '/banks/global/coverage', selected: false }
148+
]

examples/ft-ui/server/app.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { homeController } from './controllers/home.jsx'
55

66
export const app = express()
77

8-
app.use(navigation.init())
8+
app.use((req, res, next) => {
9+
const enableSubNavigation = req.query?.enableSubNavigation === 'true'
10+
return navigation.init({ enableSubNavigation })(req, res, next)
11+
})
12+
913
app.use(assets.init({ hostStaticAssets: true }))
1014

1115
app.use('/public', express.static('./public'))

examples/ft-ui/server/controllers/home.jsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import React from 'react'
22
import ReactDOM from 'react-dom/server'
33
import { Shell } from '@financial-times/dotcom-ui-shell'
44
import { Layout } from '@financial-times/dotcom-ui-layout'
5+
import { subnavData } from '../../constants'
6+
import SubnavDemo from '../views/SubnavDemo.jsx'
7+
import HelloWorld from '../views/HelloWorld.jsx'
58

69
const flags = {
710
ads: true,
@@ -16,15 +19,17 @@ export function homeController(request, response, next) {
1619
edition: response.locals.navigation.editions.current.id
1720
}
1821

19-
const pageData = {
20-
title: 'Hello World!',
21-
contents: '<div align="center"><p>Hello, welcome to Page Kit.</p></div>'
22-
}
22+
const enableSubNavigation = request.query.enableSubNavigation === 'true'
2323

24-
if (request.query['ads-first-party-gtm']) {
25-
flags['ads-first-party-gtm'] = true
26-
appContext.appName = 'home-page'
27-
}
24+
const pageData = enableSubNavigation
25+
? {
26+
title: 'Subnavigation demo',
27+
contents: ReactDOM.renderToStaticMarkup(<SubnavDemo />)
28+
}
29+
: {
30+
title: 'Hello World!',
31+
contents: ReactDOM.renderToStaticMarkup(<HelloWorld />)
32+
}
2833

2934
const { assetLoader } = response.locals
3035

@@ -41,10 +46,15 @@ export function homeController(request, response, next) {
4146

4247
const userIsLoggedIn = request.query.userIsLoggedIn
4348
const userIsSubscribed = request.query.userIsSubscribed === 'true'
44-
const showRestartSubscriptionButton = flags.showRestartSubscriptionButton;
49+
const showRestartSubscriptionButton = flags.showRestartSubscriptionButton
50+
51+
const navigationData = response.locals.navigation
52+
if (enableSubNavigation) {
53+
navigationData.subsections = subnavData
54+
}
4555

4656
const layoutProps = {
47-
navigationData: response.locals.navigation,
57+
navigationData,
4858
headerOptions: userIsLoggedIn
4959
? { userIsAnonymous: false, userIsLoggedIn: true, userIsSubscribed, showRestartSubscriptionButton }
5060
: { userIsAnonymous: true, userIsLoggedIn: false, userIsSubscribed, showRestartSubscriptionButton }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react'
2+
3+
export default function HelloWorld() {
4+
return (
5+
<div align="center">
6+
<p>Hello, welcome to Page Kit.</p>
7+
</div>
8+
)
9+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react'
2+
3+
export default function SubnavDemo() {
4+
return (
5+
<div className="content">
6+
<div align="center">
7+
<p className="o3-type-title-lg">Subnavigation</p>
8+
</div>
9+
10+
<div>
11+
<p>
12+
This demo shows how the bank navigation items in the sub-navigation can support dropdown options
13+
initially designed for MPR.
14+
</p>
15+
<p>The navigation data structure includes:</p>
16+
<ul>
17+
<li>
18+
<code>subnavDropdownOptions</code> - array of dropdown options with label, url, and trackable
19+
properties
20+
</li>
21+
<li>Full URLs for each dropdown option, providing complete flexibility for routing</li>
22+
<li>
23+
<code>trackable</code> identifier for each dropdown option
24+
</li>
25+
<li>
26+
<code>isSubnavDropdownEnabled</code> - boolean which enables or disables the dropdowns for a menu
27+
item.
28+
</li>
29+
</ul>
30+
</div>
31+
</div>
32+
)
33+
}

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dotcom-types-navigation/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ export type TNavMenuWithColumns = {
4848
items: TNavMenuItem[][]
4949
}
5050

51+
export type TNavDropdownOption = {
52+
label: string
53+
url: string
54+
trackable: string
55+
}
56+
5157
export type TNavMenuItem = {
5258
label: string
5359
url: string | null
@@ -56,6 +62,8 @@ export type TNavMenuItem = {
5662
meganav?: TNavMeganav[]
5763
disableTracking?: boolean
5864
index?: number
65+
isSubnavDropdownEnabled?: boolean
66+
subnavDropdownOptions?: TNavDropdownOption[]
5967
}
6068

6169
export type TNavMeganav = INavMeganavSections | INavMeganavArticles

packages/dotcom-ui-header/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
},
3131
"peerDependencies": {
3232
"@financial-times/logo-images": "^1.10.1",
33-
"@financial-times/o-header": "^15.3.1",
33+
"@financial-times/o-header": "^15.4.0",
3434
"@financial-times/o3-button": "^3.15.0",
3535
"n-topic-search": "^10.1.1",
3636
"preact": "^10.23.2",

0 commit comments

Comments
 (0)