@financial-times/dotcom-ui-header 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -0
- package/bower.json +9 -0
- package/browser.js +25 -0
- package/component.js +1 -0
- package/dist/node/components/drawer/additionalPartials.js +36 -0
- package/dist/node/components/drawer/topLevelPartials.js +56 -0
- package/dist/node/components/navigation/partials.js +83 -0
- package/dist/node/components/search/partials.js +20 -0
- package/dist/node/components/sticky/partials.js +59 -0
- package/dist/node/components/sub-navigation/partials.js +36 -0
- package/dist/node/components/svg-components/BrandFtMasthead.js +11 -0
- package/dist/node/components/top/partials.js +35 -0
- package/dist/node/index.js +78 -0
- package/dist/node/utils.js +6 -0
- package/package.json +44 -0
- package/screenshots/header-navigation.png +0 -0
- package/screenshots/header-sub-navigation.png +0 -0
- package/screenshots/header-top-search.png +0 -0
- package/scripts/convertSvgsToReactComponents.js +23 -0
- package/src/components/drawer/additionalPartials.tsx +97 -0
- package/src/components/drawer/topLevelPartials.tsx +153 -0
- package/src/components/navigation/partials.tsx +212 -0
- package/src/components/search/partials.tsx +52 -0
- package/src/components/sticky/partials.tsx +137 -0
- package/src/components/sub-navigation/partials.tsx +101 -0
- package/src/components/svg-components/BrandFtMasthead.tsx +16 -0
- package/src/components/top/partials.tsx +90 -0
- package/src/header.scss +29 -0
- package/src/index.tsx +123 -0
- package/src/interfaces.d.ts +18 -0
- package/src/utils.ts +5 -0
- package/styles.scss +14 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/* WARN: This file looks similar to ../top/partials */
|
|
2
|
+
/* This is the sticky header variant */
|
|
3
|
+
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import { NavListRightAnon } from '../navigation/partials'
|
|
6
|
+
import { THeaderProps } from '../../interfaces'
|
|
7
|
+
|
|
8
|
+
const StickyHeaderWrapper = (props: THeaderProps & { children: React.ReactNode }) => (
|
|
9
|
+
<header
|
|
10
|
+
className={`o-header o-header--simple o-header--sticky o--if-js`}
|
|
11
|
+
data-o-component="o-header"
|
|
12
|
+
data-o-header--sticky
|
|
13
|
+
aria-hidden="true">
|
|
14
|
+
{props.children}
|
|
15
|
+
</header>
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const DrawerIconSticky = () => (
|
|
19
|
+
<a
|
|
20
|
+
href="#"
|
|
21
|
+
className="o-header__top-link o-header__top-link--menu"
|
|
22
|
+
aria-controls="o-header-drawer"
|
|
23
|
+
data-trackable="drawer-toggle"
|
|
24
|
+
tabIndex={-1}>
|
|
25
|
+
<span className="o-header__top-link-label">Menu</span>
|
|
26
|
+
</a>
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const SearchIconSticky = () => (
|
|
30
|
+
<a
|
|
31
|
+
href="#"
|
|
32
|
+
className="o-header__top-link o-header__top-link--search"
|
|
33
|
+
aria-controls="o-header-search-sticky"
|
|
34
|
+
data-trackable="search-toggle"
|
|
35
|
+
tabIndex={-1}>
|
|
36
|
+
<span className="o-header__top-link-label">Search</span>
|
|
37
|
+
</a>
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const Navigation = (props: THeaderProps) => (
|
|
41
|
+
<div className="o-header__top-takeover">
|
|
42
|
+
<div className="o-header__nav">
|
|
43
|
+
<ul className="o-header__nav-list o-header__nav-list--left" data-trackable="primary-nav">
|
|
44
|
+
{props.data.navbar.items.map((item, index) => (
|
|
45
|
+
<li className="o-header__nav-item" key={`link-${index}`}>
|
|
46
|
+
<a
|
|
47
|
+
className="o-header__nav-link o-header__nav-link--primary"
|
|
48
|
+
href={item.url}
|
|
49
|
+
data-trackable={item.label}
|
|
50
|
+
tabIndex={-1}>
|
|
51
|
+
{item.label}
|
|
52
|
+
</a>
|
|
53
|
+
</li>
|
|
54
|
+
))}
|
|
55
|
+
</ul>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const Logo = () => (
|
|
61
|
+
<a
|
|
62
|
+
className="o-header__top-logo o-header__hide--L"
|
|
63
|
+
data-trackable="logo"
|
|
64
|
+
href="/"
|
|
65
|
+
title="Go to Financial Times homepage"
|
|
66
|
+
tabIndex={-1}>
|
|
67
|
+
<span className="o-header__visually-hidden">Financial Times</span>
|
|
68
|
+
</a>
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const NavListRightAnonSticky = (props: THeaderProps) => {
|
|
72
|
+
const navbarItems = props.data['navbar-right-anon'].items
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div className="o-header__nav">
|
|
76
|
+
<NavListRightAnon items={navbarItems} variant="sticky" />
|
|
77
|
+
</div>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const MyFtSticky = () => (
|
|
82
|
+
<a
|
|
83
|
+
className="o-header__top-link o-header__top-link--myft"
|
|
84
|
+
href="/myft"
|
|
85
|
+
data-trackable="my-ft"
|
|
86
|
+
tabIndex={-1}>
|
|
87
|
+
<span className="o-header__visually-hidden">myFT</span>
|
|
88
|
+
</a>
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
const TopWrapperSticky = (props) => (
|
|
92
|
+
<div className="o-header__row o-header__top" data-trackable="header-sticky">
|
|
93
|
+
<div className="o-header__container">
|
|
94
|
+
<div className="o-header__top-wrapper">{props.children}</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const TopColumnLeftSticky = () => {
|
|
100
|
+
return (
|
|
101
|
+
<div className="o-header__top-column o-header__top-column--left">
|
|
102
|
+
<DrawerIconSticky />
|
|
103
|
+
<SearchIconSticky />
|
|
104
|
+
</div>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
const TopColumnCenterSticky = (props: THeaderProps) => {
|
|
108
|
+
return (
|
|
109
|
+
<div className="o-header__top-column o-header__top-column--center">
|
|
110
|
+
<Navigation {...props} />
|
|
111
|
+
<Logo />
|
|
112
|
+
</div>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// This behaviour is similar to `NavListRight` in '../navigation/partials' but:
|
|
117
|
+
// - The sticky header renders either the `navbar-right-anon` data or the myFT component
|
|
118
|
+
// - The normal header renders either the `navbar-right-anon` or the `navbar-right` data
|
|
119
|
+
const TopColumnRightSticky = (props: THeaderProps) => {
|
|
120
|
+
let children = null
|
|
121
|
+
|
|
122
|
+
if (props.userIsLoggedIn) {
|
|
123
|
+
children = <MyFtSticky />
|
|
124
|
+
} else if (props.showUserNavigation) {
|
|
125
|
+
children = <NavListRightAnonSticky {...props} />
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return <div className="o-header__top-column o-header__top-column--right">{children}</div>
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
StickyHeaderWrapper,
|
|
133
|
+
TopWrapperSticky,
|
|
134
|
+
TopColumnLeftSticky,
|
|
135
|
+
TopColumnCenterSticky,
|
|
136
|
+
TopColumnRightSticky
|
|
137
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ariaSelected } from '../../utils'
|
|
3
|
+
import { THeaderProps } from '../../interfaces'
|
|
4
|
+
import { TNavMenuItem } from '@financial-times/dotcom-types-navigation'
|
|
5
|
+
|
|
6
|
+
const SubNavigation = (props: THeaderProps) => (
|
|
7
|
+
<SubNavigationWrapper>
|
|
8
|
+
<BreadCrumb items={props.data.breadcrumb} />
|
|
9
|
+
<SubSections items={props.data.subsections} />
|
|
10
|
+
<SubSections items={props.data['subsections-right']} rightAlignment={true} />
|
|
11
|
+
</SubNavigationWrapper>
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
const SubNavigationWrapper = (props) => (
|
|
15
|
+
<div
|
|
16
|
+
className="o-header__subnav"
|
|
17
|
+
role="navigation"
|
|
18
|
+
aria-label="Sub navigation"
|
|
19
|
+
data-o-header-subnav
|
|
20
|
+
data-trackable="header-subnav">
|
|
21
|
+
<div className="o-header__container">
|
|
22
|
+
<div className="o-header__subnav-wrap-outside">
|
|
23
|
+
<div className="o-header__subnav-wrap-inside" data-o-header-subnav-wrapper>
|
|
24
|
+
<div className="o-header__subnav-content">{props.children}</div>
|
|
25
|
+
</div>
|
|
26
|
+
{/* Implements subNavigation scrolling at smaller viewports */}
|
|
27
|
+
<button
|
|
28
|
+
className="o-header__subnav-button o-header__subnav-button--left"
|
|
29
|
+
title="scroll left"
|
|
30
|
+
aria-label="scroll left"
|
|
31
|
+
aria-hidden="true"
|
|
32
|
+
disabled
|
|
33
|
+
/>
|
|
34
|
+
<button
|
|
35
|
+
className="o-header__subnav-button o-header__subnav-button--right"
|
|
36
|
+
title="scroll right"
|
|
37
|
+
aria-label="scroll right"
|
|
38
|
+
aria-hidden="true"
|
|
39
|
+
disabled
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const BreadCrumb = ({ items }: { items: TNavMenuItem[] }) => (
|
|
47
|
+
<ol
|
|
48
|
+
className="o-header__subnav-list o-header__subnav-list--breadcrumb"
|
|
49
|
+
aria-label="Breadcrumb"
|
|
50
|
+
data-trackable="breadcrumb">
|
|
51
|
+
{items.map((item, index) => {
|
|
52
|
+
const selected = item.selected ? 'o-header__subnav-link--highlight' : ''
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<li className="o-header__subnav-item" key={`item-${index}`}>
|
|
56
|
+
<a
|
|
57
|
+
className={`o-header__subnav-link ${selected}`}
|
|
58
|
+
href={item.url}
|
|
59
|
+
{...ariaSelected(item)}
|
|
60
|
+
data-trackable={item.label}>
|
|
61
|
+
{item.label}
|
|
62
|
+
</a>
|
|
63
|
+
</li>
|
|
64
|
+
)
|
|
65
|
+
})}
|
|
66
|
+
</ol>
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
const SubSections = ({ items, rightAlignment }: { items: TNavMenuItem[]; rightAlignment?: boolean }) => {
|
|
70
|
+
if (!items || items.length === 0) {
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<ul
|
|
76
|
+
className={
|
|
77
|
+
'o-header__subnav-list o-header__subnav-list--children' +
|
|
78
|
+
(rightAlignment ? ' o-header__subnav-list--right' : '')
|
|
79
|
+
}
|
|
80
|
+
aria-label={rightAlignment ? 'Additional Sub Navigation' : 'Subsections'}
|
|
81
|
+
data-trackable="subsections">
|
|
82
|
+
{items.map((item, index) => {
|
|
83
|
+
const selected = item.selected ? 'o-header__subnav-link--highlight' : ''
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<li className="o-header__subnav-item" key={`item-${index}`}>
|
|
87
|
+
<a
|
|
88
|
+
className={`o-header__subnav-link ${selected}`}
|
|
89
|
+
href={item.url}
|
|
90
|
+
{...ariaSelected(item)}
|
|
91
|
+
data-trackable={item.label}>
|
|
92
|
+
{item.label}
|
|
93
|
+
</a>
|
|
94
|
+
</li>
|
|
95
|
+
)
|
|
96
|
+
})}
|
|
97
|
+
</ul>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export { SubNavigation }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// **THIS IS AN AUTO-GENERATED FILE (`npm run build:svg-to-react`) - DO NOT EDIT MANUALLY.**
|
|
2
|
+
|
|
3
|
+
import React from 'react'
|
|
4
|
+
|
|
5
|
+
const BrandFtMasthead = ({ title, ...props }) => (
|
|
6
|
+
<svg viewBox="0 0 1054 86" {...props}>
|
|
7
|
+
{title === undefined ? <title>{'brand-ft-masthead'}</title> : title ? <title>{title}</title> : null}
|
|
8
|
+
<path
|
|
9
|
+
d="M26.177 72.609c0 5.938 1.697 7.295 12.554 7.295v3.732H.9v-3.732c7.464 0 10.01-.679 10.01-7.125V12.215c0-6.447-2.546-7.126-10.01-7.126V1.357h63.448l.34 22.563h-3.054C59.937 6.956 55.696 5.43 39.919 5.43H26.008v33.59h11.196c10.688 0 11.367-1.697 12.215-10.179h3.054v24.599h-3.054c-.848-8.482-1.527-10.01-12.215-10.01H26.008v29.18h.17zm46.314 11.027v-3.732c7.465 0 10.01-.679 10.01-7.125V12.215c0-6.447-2.545-7.126-10.01-7.126V1.357h35.287V5.09c-7.465 0-10.01.679-10.01 7.126v60.564c0 6.446 2.545 7.125 10.01 7.125v3.732H72.49zm115.36 1.357l-56.323-69.725V72.44c0 6.617 4.58 7.634 12.385 7.634v3.733h-29.858v-3.733c7.803 0 12.045-1.017 12.045-7.634V8.991c-3.563-3.732-6.108-3.902-12.045-3.902V1.357h26.465l43.09 55.475V12.554c0-6.616-4.58-7.634-12.384-7.634V1.357h30.027V5.09c-7.803 0-12.045 1.018-12.045 7.635v72.27h-1.357zm40.207-1.357h-29.689v-3.732c7.804 0 11.367-1.018 13.911-7.804L239.085.509h7.464l28.84 72.1c2.545 6.447 3.732 7.125 9.67 7.125v3.732h-34.438v-3.732c10.518 0 11.536-.848 8.99-7.125l-8.481-21.884h-25.787L217.71 71.93c-2.375 6.447 1.357 7.804 10.518 7.804v3.902h-.17zm-1.188-37.153h22.393l-11.705-29.518-10.688 29.518zm135.548 38.51l-56.153-69.725V72.44c0 6.617 4.58 7.634 12.384 7.634v3.733h-29.18v-3.733c7.126 0 11.367-1.017 11.367-7.634V9.161c-4.071-3.732-7.125-4.072-13.91-4.072V1.357h28.16l43.09 55.475V12.554c0-6.616-4.58-7.634-12.383-7.634V1.357h29.858V5.09c-7.804 0-12.045 1.018-12.045 7.635v72.27h-1.188zm83.297-83.805h2.036l1.187 24.598-3.053.17c-2.036-14.08-9.5-21.545-23.242-21.545-15.268 0-26.804 13.063-26.804 33.081 0 25.617 16.116 40.206 33.08 40.206 7.296 0 13.912-2.035 20.358-8.99l2.376 2.374c-5.26 7.465-15.608 13.742-29.52 13.742-20.696 0-41.732-15.608-41.732-41.734C380.4 17.813 399.57 0 422.813 0c11.027 0 16.795 4.75 19.848 4.75 1.357 0 2.375-1.187 3.054-3.562zm12.723 82.448v-3.732c7.465 0 10.01-.679 10.01-7.125V12.215c0-6.447-2.545-7.126-10.01-7.126V1.357h35.287V5.09c-7.464 0-10.01.679-10.01 7.126v60.564c0 6.446 2.546 7.125 10.01 7.125v3.732h-35.287zm68.538 0h-27.653v-3.732c6.108 0 9.331-1.018 11.876-7.804L538.003.509h7.464l28.84 72.1c2.545 6.447 3.733 7.125 9.67 7.125v3.732H549.54v-3.732c10.518 0 11.536-.848 8.991-7.125l-8.482-21.884h-25.786l-7.635 21.205c-2.375 6.447 1.358 7.804 10.519 7.804v3.902h-.17zm-1.188-37.153h22.394l-11.536-29.518-10.858 29.518zm63.957 37.153v-3.732c7.465 0 10.01-.679 10.01-7.125V12.215c0-6.447-2.545-7.126-10.01-7.126V1.357h35.117V5.09c-7.464 0-9.84.679-9.84 7.126v61.073c0 5.428 2.715 6.107 7.126 6.107h4.241c15.947 0 21.036-2.375 25.447-20.527l3.054.339-2.545 24.26h-62.6v.17zM760.75 1.357l.339 23.92h-3.054C756.34 7.634 752.098 5.43 736.32 5.43h-5.089v67.18c0 6.447 2.375 7.295 12.554 7.295v3.732h-40.376v-3.732c10.179 0 12.723-1.018 12.723-7.295V5.429h-5.089c-15.777 0-20.018 2.205-21.715 19.848h-3.053l.339-23.92h74.136zm7.973 82.28v-3.733c7.465 0 10.01-.679 10.01-7.125V12.215c0-6.447-2.545-7.126-10.01-7.126V1.357h35.287V5.09c-7.465 0-10.01.679-10.01 7.126v60.564c0 6.446 2.545 7.125 10.01 7.125v3.732h-35.287zM910.21 1.356V5.09c-7.465 0-10.688.34-10.01 6.956l6.447 61.073c.679 6.277 3.054 6.955 10.518 6.955v3.733h-35.117v-3.733c7.295 0 9.84-.678 9.331-6.955L884.762 8.99l-25.956 76.172h-1.018L832.34 8.822l-6.108 64.126c-.678 6.447 3.733 7.125 11.197 7.125v3.733h-27.483v-3.733c7.465 0 10.01-1.187 10.518-7.125l6.447-61.073c.679-6.446-2.545-6.955-10.01-6.955V1.357h28.84l17.305 56.153 18.661-56.153h28.5zm65.653 52.082h-3.053c-.849-8.482-1.527-10.01-12.215-10.01H948.04v29.859c0 5.428 2.715 6.107 7.125 6.107h6.786c15.947 0 21.036-2.375 25.447-20.527l3.054.339-2.884 24.26h-64.805v-3.733c7.464 0 10.009-.678 10.009-7.125V12.215c0-6.447-2.545-7.126-10.01-7.126V1.357h62.261l.34 20.527h-3.054c-1.866-14.59-5.598-16.286-21.885-16.286H948.21v33.42h12.554c10.687 0 11.366-1.696 12.214-10.178h3.054v24.599h-.17zm65.484 13.232c0-7.464-4.75-11.196-12.893-15.777l-13.063-6.786c-9.84-5.259-15.607-11.027-15.607-21.375C999.783 9.84 1010.81 0 1025.23 0c9.84 0 14.929 4.75 17.813 4.75 1.866 0 2.714-1.187 3.562-3.562h2.375l1.188 23.072-3.054.17c-1.696-11.198-9.67-19.85-20.866-19.85-8.483 0-14.081 5.09-14.081 12.215 0 7.804 5.937 11.027 12.554 14.59l11.196 5.937c10.519 5.768 17.983 11.536 17.983 22.563 0 14.59-12.554 24.939-28.161 24.939-11.028 0-16.456-5.26-19.34-5.26-1.866 0-2.884 1.697-3.732 4.242h-2.376l-1.696-24.43 3.054-.339c2.375 15.268 12.893 21.545 23.411 21.545 8.822-.17 16.286-4.071 16.286-13.91z"
|
|
10
|
+
fill="#231F20"
|
|
11
|
+
fillRule="evenodd"
|
|
12
|
+
/>
|
|
13
|
+
</svg>
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
export default BrandFtMasthead
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import BrandFtMastheadSvg from '../svg-components/BrandFtMasthead'
|
|
3
|
+
|
|
4
|
+
const HeaderWrapper = (props) => (
|
|
5
|
+
<header
|
|
6
|
+
id="site-navigation"
|
|
7
|
+
className={`o-header o-header--${props.variant || 'simple'}`}
|
|
8
|
+
data-o-component="o-header"
|
|
9
|
+
data-o-header--no-js={true}
|
|
10
|
+
tabIndex={-1}>
|
|
11
|
+
{props.children}
|
|
12
|
+
</header>
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const DrawerIcon = () => (
|
|
16
|
+
<a
|
|
17
|
+
href="#o-header-drawer"
|
|
18
|
+
className="o-header__top-link o-header__top-link--menu"
|
|
19
|
+
aria-controls="o-header-drawer"
|
|
20
|
+
title="Open side navigation menu"
|
|
21
|
+
data-trackable="drawer-toggle">
|
|
22
|
+
<span className="o-header__top-link-label">Open side navigation menu</span>
|
|
23
|
+
</a>
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
const SearchIcon = () => (
|
|
27
|
+
<a
|
|
28
|
+
href={`#o-header-search-primary`}
|
|
29
|
+
className="o-header__top-link o-header__top-link--search"
|
|
30
|
+
aria-controls={`o-header-search-primary`}
|
|
31
|
+
title="Open search bar"
|
|
32
|
+
data-trackable="search-toggle">
|
|
33
|
+
<span className="o-header__top-link-label">Open search bar</span>
|
|
34
|
+
</a>
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const MyFt = () => (
|
|
38
|
+
<a
|
|
39
|
+
className="o-header__top-link o-header__top-link--myft"
|
|
40
|
+
href="/myft"
|
|
41
|
+
data-trackable="my-ft"
|
|
42
|
+
data-tour-stage="myFt"
|
|
43
|
+
aria-label="My F T">
|
|
44
|
+
<span className="o-header__visually-hidden">myFT</span>
|
|
45
|
+
</a>
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
const TopWrapper = (props) => (
|
|
49
|
+
<div className="o-header__row o-header__top" data-trackable="header-top">
|
|
50
|
+
<div className="o-header__container">
|
|
51
|
+
<div className="o-header__top-wrapper">{props.children}</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
const TopColumnLeft = () => (
|
|
57
|
+
<div className="o-header__top-column o-header__top-column--left">
|
|
58
|
+
<DrawerIcon />
|
|
59
|
+
<SearchIcon />
|
|
60
|
+
</div>
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const TopColumnCenter = () => (
|
|
64
|
+
<div className="o-header__top-column o-header__top-column--center">
|
|
65
|
+
<a
|
|
66
|
+
className="o-header__top-logo"
|
|
67
|
+
style={{ backgroundImage: 'none' }}
|
|
68
|
+
data-trackable="logo"
|
|
69
|
+
href="/"
|
|
70
|
+
title="Go to Financial Times homepage">
|
|
71
|
+
<BrandFtMastheadSvg title="Financial Times" />
|
|
72
|
+
</a>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const TopColumnCenterNoLink = () => (
|
|
77
|
+
<div className="o-header__top-column o-header__top-column--center">
|
|
78
|
+
<div className="o-header__top-logo" style={{ backgroundImage: 'none' }}>
|
|
79
|
+
<BrandFtMastheadSvg title="Financial Times" />
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
const TopColumnRight = () => (
|
|
85
|
+
<div className="o-header__top-column o-header__top-column--right">
|
|
86
|
+
<MyFt />
|
|
87
|
+
</div>
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
export { HeaderWrapper, TopWrapper, TopColumnLeft, TopColumnCenter, TopColumnCenterNoLink, TopColumnRight }
|
package/src/header.scss
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Search
|
|
2
|
+
// At the time of writing o-header duplicates search markup.
|
|
3
|
+
// Once for a core experience, once for an enhanced experience.
|
|
4
|
+
// The enhanced version is hidden until toggled with JavaScript.
|
|
5
|
+
// Instead since we can rely on the `core` class we can use the
|
|
6
|
+
// one enhanced experience search block and reveal for the core
|
|
7
|
+
// experience if needed.
|
|
8
|
+
.core [data-o-header-search] {
|
|
9
|
+
display: block;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// z-indexes
|
|
13
|
+
.o-header__mega {
|
|
14
|
+
@include nUiZIndexFor('meganav');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.o-header__drawer {
|
|
18
|
+
@include nUiZIndexFor('drawer');
|
|
19
|
+
display: block;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.o-header--sticky {
|
|
23
|
+
@include nUiZIndexFor('sticky-header');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Search typeahead
|
|
27
|
+
.n-typeahead {
|
|
28
|
+
display: none;
|
|
29
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
HeaderWrapper,
|
|
4
|
+
TopWrapper,
|
|
5
|
+
TopColumnLeft,
|
|
6
|
+
TopColumnCenter,
|
|
7
|
+
TopColumnCenterNoLink,
|
|
8
|
+
TopColumnRight
|
|
9
|
+
} from './components/top/partials'
|
|
10
|
+
import {
|
|
11
|
+
NavListLeft,
|
|
12
|
+
NavListRight,
|
|
13
|
+
NavDesktop,
|
|
14
|
+
UserActionsNav,
|
|
15
|
+
MobileNav
|
|
16
|
+
} from './components/navigation/partials'
|
|
17
|
+
import {
|
|
18
|
+
StickyHeaderWrapper,
|
|
19
|
+
TopWrapperSticky,
|
|
20
|
+
TopColumnCenterSticky,
|
|
21
|
+
TopColumnLeftSticky,
|
|
22
|
+
TopColumnRightSticky
|
|
23
|
+
} from './components/sticky/partials'
|
|
24
|
+
import { SubNavigation } from './components/sub-navigation/partials'
|
|
25
|
+
import { IncludeDrawer } from './components/drawer/topLevelPartials'
|
|
26
|
+
import { Search } from './components/search/partials'
|
|
27
|
+
|
|
28
|
+
import { THeaderProps, THeaderOptions } from './interfaces'
|
|
29
|
+
|
|
30
|
+
const defaultProps: Partial<THeaderOptions> = {
|
|
31
|
+
showSubNavigation: true,
|
|
32
|
+
showUserNavigation: true,
|
|
33
|
+
userIsAnonymous: true,
|
|
34
|
+
userIsLoggedIn: false,
|
|
35
|
+
showStickyHeader: true,
|
|
36
|
+
showMegaNav: true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function MainHeader(props: THeaderProps) {
|
|
40
|
+
const includeUserActionsNav = props.showUserNavigation && !props.userIsLoggedIn
|
|
41
|
+
const includeSubNavigation = props.showSubNavigation && (props.data.breadcrumb || props.data.subsections)
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<HeaderWrapper {...props}>
|
|
45
|
+
{includeUserActionsNav ? <UserActionsNav {...props} /> : null}
|
|
46
|
+
<TopWrapper>
|
|
47
|
+
<TopColumnLeft />
|
|
48
|
+
{props.showLogoLink ? <TopColumnCenter /> : <TopColumnCenterNoLink />}
|
|
49
|
+
<TopColumnRight />
|
|
50
|
+
</TopWrapper>
|
|
51
|
+
<Search instance="primary" />
|
|
52
|
+
<MobileNav {...props} />
|
|
53
|
+
<NavDesktop>
|
|
54
|
+
<NavListLeft {...props} />
|
|
55
|
+
{props.showUserNavigation ? <NavListRight {...props} /> : null}
|
|
56
|
+
</NavDesktop>
|
|
57
|
+
{includeSubNavigation ? <SubNavigation {...props} /> : null}
|
|
58
|
+
</HeaderWrapper>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
MainHeader.defaultProps = { ...defaultProps, showLogoLink: true }
|
|
63
|
+
|
|
64
|
+
function StickyHeader(props: THeaderProps) {
|
|
65
|
+
return props.showStickyHeader ? (
|
|
66
|
+
<StickyHeaderWrapper {...props}>
|
|
67
|
+
<TopWrapperSticky>
|
|
68
|
+
<TopColumnLeftSticky />
|
|
69
|
+
<TopColumnCenterSticky {...props} />
|
|
70
|
+
<TopColumnRightSticky {...props} />
|
|
71
|
+
</TopWrapperSticky>
|
|
72
|
+
<Search instance="sticky" />
|
|
73
|
+
</StickyHeaderWrapper>
|
|
74
|
+
) : null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
StickyHeader.defaultProps = defaultProps
|
|
78
|
+
|
|
79
|
+
function Header(props: THeaderProps) {
|
|
80
|
+
return (
|
|
81
|
+
<React.Fragment>
|
|
82
|
+
<MainHeader {...props} />
|
|
83
|
+
<StickyHeader {...props} />
|
|
84
|
+
</React.Fragment>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Header.defaultProps = defaultProps
|
|
89
|
+
|
|
90
|
+
function LogoOnly(props: Pick<THeaderProps, 'variant' | 'showLogoLink'>) {
|
|
91
|
+
return (
|
|
92
|
+
<HeaderWrapper {...props}>
|
|
93
|
+
<TopWrapper>{props.showLogoLink ? <TopColumnCenter /> : <TopColumnCenterNoLink />}</TopWrapper>
|
|
94
|
+
</HeaderWrapper>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
LogoOnly.defaultProps = defaultProps
|
|
99
|
+
|
|
100
|
+
function Drawer(props: THeaderProps) {
|
|
101
|
+
return <IncludeDrawer {...props} />
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
Drawer.defaultProps = defaultProps
|
|
105
|
+
|
|
106
|
+
function NoOutboundLinksHeader(props: THeaderProps) {
|
|
107
|
+
const includeUserActionsNav = props.showUserNavigation && !props.userIsLoggedIn
|
|
108
|
+
const includeSubNavigation = props.showSubNavigation && (props.data.breadcrumb || props.data.subsections)
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<HeaderWrapper {...props}>
|
|
112
|
+
{includeUserActionsNav ? <UserActionsNav {...props} /> : null}
|
|
113
|
+
<TopWrapper>{props.showLogoLink ? <TopColumnCenter /> : <TopColumnCenterNoLink />}</TopWrapper>
|
|
114
|
+
<NavDesktop>{props.showUserNavigation ? <NavListRight {...props} /> : null}</NavDesktop>
|
|
115
|
+
{includeSubNavigation ? <SubNavigation {...props} /> : null}
|
|
116
|
+
</HeaderWrapper>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
NoOutboundLinksHeader.defaultProps = defaultProps
|
|
121
|
+
|
|
122
|
+
export { Header, MainHeader, StickyHeader, LogoOnly, NoOutboundLinksHeader, Drawer }
|
|
123
|
+
export type { THeaderProps }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TNavigationData } from '@financial-times/dotcom-types-navigation'
|
|
2
|
+
|
|
3
|
+
export type THeaderOptions = {
|
|
4
|
+
variant?: THeaderVariant
|
|
5
|
+
userIsAnonymous?: boolean
|
|
6
|
+
userIsLoggedIn?: boolean
|
|
7
|
+
showSubNavigation?: boolean
|
|
8
|
+
showUserNavigation?: boolean
|
|
9
|
+
showStickyHeader?: boolean
|
|
10
|
+
showMegaNav?: boolean
|
|
11
|
+
showLogoLink?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type THeaderProps = THeaderOptions & {
|
|
15
|
+
data: TNavigationData
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type THeaderVariant = 'simple' | 'large-logo'
|
package/src/utils.ts
ADDED
package/styles.scss
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// This will be overridden by dotcom-ui-layout, it's necessary here for storybook builds
|
|
2
|
+
$system-code: 'page-kit-header' !default;
|
|
3
|
+
|
|
4
|
+
@import "n-ui-foundations/mixins";
|
|
5
|
+
|
|
6
|
+
// We don't need the sub-brand or transparent header styles so disable them.
|
|
7
|
+
// TODO: move drawer styles into a separate stylesheet which can be lazy loaded?
|
|
8
|
+
@import "o-header/main";
|
|
9
|
+
@include oHeader(('top', 'nav', 'subnav', 'search', 'megamenu', 'drawer', 'anon', 'sticky', 'simple'));
|
|
10
|
+
|
|
11
|
+
@import "src/header";
|
|
12
|
+
|
|
13
|
+
@import "n-topic-search/main";
|
|
14
|
+
@include nTopicSearch;
|