@financial-times/dotcom-ui-footer 7.3.1 → 7.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/dotcom-ui-footer",
3
- "version": "7.3.1",
3
+ "version": "7.3.2",
4
4
  "description": "",
5
5
  "main": "component.js",
6
6
  "styles": "styles.scss",
@@ -32,7 +32,11 @@
32
32
  "npm": "7.x || 8.x"
33
33
  },
34
34
  "files": [
35
- "dist/"
35
+ "dist/",
36
+ "src/",
37
+ "browser.js",
38
+ "component.js",
39
+ "styles.scss"
36
40
  ],
37
41
  "repository": {
38
42
  "type": "git",
@@ -0,0 +1,227 @@
1
+ import { TNavMenu } from '../../../dotcom-types-navigation'
2
+
3
+ const data: TNavMenu = {
4
+ label: 'Footer',
5
+ items: [
6
+ {
7
+ label: 'Support',
8
+ url: null,
9
+ submenu: {
10
+ label: null,
11
+ items: [
12
+ [
13
+ {
14
+ label: 'View Site Tips',
15
+ url: '/tour',
16
+ submenu: null
17
+ },
18
+ {
19
+ label: 'Help Centre',
20
+ url: 'http://help.ft.com',
21
+ submenu: null
22
+ },
23
+ {
24
+ label: 'About Us',
25
+ url: 'http://www.ft.com/aboutus',
26
+ submenu: null
27
+ },
28
+ {
29
+ label: 'Accessibility',
30
+ url: '/accessibility',
31
+ submenu: null
32
+ },
33
+ {
34
+ label: 'myFT Tour',
35
+ url: '/tour/myft',
36
+ submenu: null
37
+ }
38
+ ]
39
+ ]
40
+ }
41
+ },
42
+ {
43
+ label: 'Legal & Privacy',
44
+ url: null,
45
+ submenu: {
46
+ label: null,
47
+ items: [
48
+ [
49
+ {
50
+ label: 'Terms & Conditions',
51
+ url: 'http://help.ft.com/help/legal-privacy/terms-conditions/',
52
+ submenu: null
53
+ },
54
+ {
55
+ label: 'Privacy',
56
+ url: 'http://help.ft.com/help/legal-privacy/privacy/',
57
+ submenu: null
58
+ },
59
+ {
60
+ label: 'Cookies',
61
+ url: 'http://help.ft.com/help/legal-privacy/cookies/',
62
+ submenu: null
63
+ },
64
+ {
65
+ label: 'Copyright',
66
+ url: 'http://help.ft.com/help/legal-privacy/copyright/copyright-policy/',
67
+ submenu: null
68
+ },
69
+ {
70
+ label: 'Slavery Statement & Policies',
71
+ url: 'https://help.ft.com/help/legal/slavery-statement/',
72
+ submenu: null
73
+ }
74
+ ]
75
+ ]
76
+ }
77
+ },
78
+ {
79
+ label: 'Services',
80
+ url: null,
81
+ submenu: {
82
+ label: null,
83
+ items: [
84
+ [
85
+ {
86
+ label: 'FT Live',
87
+ url: 'http://live.ft.com/',
88
+ submenu: null
89
+ },
90
+ {
91
+ label: 'Share News Tips Securely',
92
+ url: '/securedrop',
93
+ disableTracking: true,
94
+ submenu: null
95
+ },
96
+ {
97
+ label: 'Individual Subscriptions',
98
+ url: 'http://www.ft.com/products',
99
+ submenu: null
100
+ },
101
+ {
102
+ label: 'Group Subscriptions',
103
+ url: 'https://enterprise.ft.com/en-gb/services/group-subscriptions/group-contact-us/?segmentId=383c7f63-abf4-b62d-cb33-4c278e6fdf61&cpccampaign=B2B_link_ft.com_footer',
104
+ submenu: null
105
+ },
106
+ {
107
+ label: 'Republishing',
108
+ url: 'https://enterprise.ft.com/en-gb/services/republishing/',
109
+ submenu: null
110
+ },
111
+ {
112
+ label: 'Contracts & Tenders',
113
+ url: 'http://www.businessesforsale.com/ft2/notices',
114
+ submenu: null
115
+ },
116
+ {
117
+ label: 'Executive Job Search',
118
+ url: 'https://www.exec-appointments.com/',
119
+ submenu: null
120
+ },
121
+ {
122
+ label: 'Advertise with the FT',
123
+ url: 'http://fttoolkit.co.uk/',
124
+ submenu: null
125
+ },
126
+ {
127
+ label: 'Follow the FT on Twitter',
128
+ url: 'https://twitter.com/financialtimes',
129
+ submenu: null
130
+ },
131
+ {
132
+ label: 'FT Transact',
133
+ url: 'https://transact.ft.com/en-gb/',
134
+ submenu: null
135
+ },
136
+ {
137
+ label: 'Secondary Schools',
138
+ url: 'https://enterprise.ft.com/en-gb/services/group-subscriptions/education/',
139
+ submenu: null
140
+ }
141
+ ]
142
+ ]
143
+ }
144
+ },
145
+ {
146
+ label: 'Tools',
147
+ url: null,
148
+ submenu: {
149
+ label: null,
150
+ items: [
151
+ [
152
+ {
153
+ label: 'Portfolio',
154
+ url: 'https://markets.ft.com/data/portfolio/dashboard',
155
+ submenu: null
156
+ },
157
+ {
158
+ label: "Today's Newspaper (ePaper)",
159
+ url: 'https://www.ft.com/todaysnewspaper',
160
+ submenu: null
161
+ },
162
+ {
163
+ label: 'Alerts Hub',
164
+ url: 'http://markets.ft.com/data/alerts/',
165
+ submenu: null
166
+ },
167
+ {
168
+ label: 'Lexicon',
169
+ url: 'http://lexicon.ft.com/',
170
+ submenu: null
171
+ },
172
+ {
173
+ label: 'MBA Rankings',
174
+ url: 'http://rankings.ft.com/businessschoolrankings/global-mba-ranking-2016?ft_site=falcon',
175
+ submenu: null
176
+ },
177
+ {
178
+ label: 'Economic Calendar',
179
+ url: 'https://markets.ft.com/data/world/economic-calendar',
180
+ submenu: null
181
+ },
182
+ {
183
+ label: 'News feed',
184
+ url: '/news-feed',
185
+ submenu: null
186
+ },
187
+ {
188
+ label: 'Newsletters',
189
+ url: '/newsletters',
190
+ submenu: null
191
+ },
192
+ {
193
+ label: 'Currency Converter',
194
+ url: 'https://markets.ft.com/research/Markets/Currencies?segid=70113',
195
+ submenu: null
196
+ }
197
+ ]
198
+ ]
199
+ }
200
+ },
201
+ {
202
+ label: 'Community & Events',
203
+ url: null,
204
+ submenu: {
205
+ label: null,
206
+ items: [
207
+ [
208
+ { label: 'FT Community', url: 'https://www.ft.com/tour/community', submenu: null },
209
+ { label: 'FT Live', url: 'http://live.ft.com/', submenu: null },
210
+ {
211
+ label: 'FT Forums',
212
+ url: 'https://forums.ft.com/',
213
+ submenu: null
214
+ },
215
+ {
216
+ label: 'Board Director Programme',
217
+ url: 'https://bdp.ft.com/',
218
+ submenu: null
219
+ }
220
+ ]
221
+ ]
222
+ }
223
+ }
224
+ ]
225
+ }
226
+
227
+ export default data
@@ -0,0 +1,22 @@
1
+ import { TFooterProps } from '..'
2
+ import footer from './footer'
3
+
4
+ const fixture: TFooterProps = {
5
+ theme: 'dark',
6
+ data: {
7
+ account: null,
8
+ anon: null,
9
+ breadcrumb: null,
10
+ drawer: null,
11
+ editions: null,
12
+ footer,
13
+ navbar: null,
14
+ 'navbar-right': null,
15
+ 'navbar-right-anon': null,
16
+ 'navbar-simple': null,
17
+ subsections: null,
18
+ user: null
19
+ }
20
+ }
21
+
22
+ export default fixture
@@ -0,0 +1,45 @@
1
+ import React from 'react'
2
+ import { OnReady } from '../../../../.storybook/components/OnReady'
3
+
4
+ import storyData from './story-data'
5
+
6
+ import * as footer from '../../browser.js'
7
+ import { Footer, LegalFooter } from '../../src'
8
+ import '../../styles.scss'
9
+
10
+ export default {
11
+ title: 'FT / Footer',
12
+ args: { theme: 'dark' },
13
+ argTypes: {
14
+ theme: {
15
+ control: {
16
+ type: 'radio',
17
+ options: ['dark', 'light']
18
+ }
19
+ }
20
+ }
21
+ }
22
+
23
+ export const _Footer = (args) => {
24
+ return (
25
+ <OnReady callback={() => footer.init()}>
26
+ <Footer {...storyData} {...args} />
27
+ </OnReady>
28
+ )
29
+ }
30
+
31
+ _Footer.story = {
32
+ name: 'footer'
33
+ }
34
+
35
+ export const _LegalFooter = (args) => {
36
+ return (
37
+ <OnReady callback={() => footer.init()}>
38
+ <LegalFooter {...storyData} {...args} />
39
+ </OnReady>
40
+ )
41
+ }
42
+
43
+ _LegalFooter.story = {
44
+ name: 'legal footer'
45
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ import 'jest-enzyme'
5
+ import React from 'react'
6
+ import { mount } from 'enzyme'
7
+
8
+ import dataFixture from '../__stories__/story-data'
9
+ import { Footer, LegalFooter } from '../index'
10
+
11
+ describe('dotcom-ui-footer', () => {
12
+ const footer = mount(<Footer {...dataFixture} />)
13
+ const legalFooter = mount(<LegalFooter {...dataFixture} />)
14
+
15
+ it('renders the available footer variants', () => {
16
+ expect(footer).not.toBeEmptyRender()
17
+ expect(legalFooter).not.toBeEmptyRender()
18
+ })
19
+
20
+ it('renders the copyright section', () => {
21
+ expect(footer.find('.o-footer__copyright')).toExist()
22
+ expect(legalFooter.find('.o-footer__copyright')).toExist()
23
+ })
24
+
25
+ it('renders the Nikkei banner', () => {
26
+ expect(footer.find('.o-footer__copyright')).toExist()
27
+ expect(legalFooter.find('.o-footer__brand-logo')).toExist()
28
+ })
29
+
30
+ describe('standard footer', () => {
31
+ it('renders the navigation sections', () => {
32
+ expect(footer).not.toBeEmptyRender()
33
+ expect(footer.find('.o-footer__matrix')).toIncludeText('Support')
34
+ expect(footer.find('.o-footer__matrix')).toIncludeText('Legal & Privacy')
35
+ expect(footer.find('.o-footer__matrix')).toIncludeText('Services')
36
+ expect(footer.find('.o-footer__matrix')).toIncludeText('Tools')
37
+ })
38
+
39
+ it('does not render the legal links section', () => {
40
+ expect(footer.find('.o-footer__legal-links')).not.toExist()
41
+ })
42
+
43
+ it('renders the More from FT section', () => {
44
+ expect(footer.find('.o-footer__matrix-link__copy')).toExist()
45
+ })
46
+ })
47
+
48
+ describe('legal footer', () => {
49
+ it('does not render the navigation sections', () => {
50
+ expect(legalFooter.find('.o-footer__matrix')).not.toExist()
51
+ })
52
+
53
+ it('renders the legal links section', () => {
54
+ expect(legalFooter.find('.o-footer__legal-links')).toExist()
55
+ })
56
+
57
+ it('does not render the More from FT section', () => {
58
+ expect(legalFooter.find('.o-footer__more-from-ft')).not.toExist()
59
+ })
60
+ })
61
+ })
@@ -0,0 +1,146 @@
1
+ import React from 'react'
2
+ import { TNavMenuItem } from '@financial-times/dotcom-types-navigation'
3
+
4
+ type TLinkProps = {
5
+ item: TNavMenuItem
6
+ [key: string]: any
7
+ }
8
+
9
+ const Link = ({ item, ...props }: TLinkProps) => {
10
+ const disableTracking = { 'data-o-tracking-do-not-track': item.disableTracking ? true : null }
11
+
12
+ return (
13
+ <a {...props} href={item.url} data-trackable={item.label} {...disableTracking}>
14
+ <span className="o-footer__matrix-link__copy">{item.label}</span>
15
+ </a>
16
+ )
17
+ }
18
+
19
+ type TSectionLinksProps = {
20
+ index: number
21
+ submenu: TNavMenuItem[][]
22
+ }
23
+
24
+ const SectionLinks = ({ submenu, index }: TSectionLinksProps) => {
25
+ return (
26
+ <div className="o-footer__matrix-content" id={`o-footer-section-${index}`}>
27
+ {submenu.map((submenuItems, index) => (
28
+ <div className="o-footer__matrix-column" key={`column-${index}`}>
29
+ {submenuItems.map((item, index) => (
30
+ <Link className="o-footer__matrix-link" item={item} key={`link-${index}`} />
31
+ ))}
32
+ </div>
33
+ ))}
34
+ </div>
35
+ )
36
+ }
37
+
38
+ type TSectionTitleProps = {
39
+ index: number
40
+ label: string
41
+ }
42
+
43
+ const SectionTitle = ({ label, index }: TSectionTitleProps) => {
44
+ // On smaller viewports, submenu which span one column is collapsed behind toggles
45
+ const ariaControls = { 'aria-controls': `o-footer-section-${index}` }
46
+ return (
47
+ <h3 className="o-footer__matrix-title" {...ariaControls}>
48
+ {label}
49
+ </h3>
50
+ )
51
+ }
52
+
53
+ type TFooterContentsProps = {
54
+ footerData: TNavMenuItem[]
55
+ }
56
+
57
+ const FooterContents = ({ footerData }: TFooterContentsProps) => (
58
+ <div className="o-footer__row">
59
+ <h2 className="o-normalise-visually-hidden">Useful links</h2>
60
+ <nav className="o-footer__matrix" role="navigation" aria-label="Useful links">
61
+ {footerData.map((item, index) => {
62
+ // The Next navigation API splits footer links data into "columns"
63
+ // <https://github.com/Financial-Times/next-navigation-api/blob/HEAD/server/transforms/footer.js>
64
+ const submenu = item.submenu.items as TNavMenuItem[][]
65
+ return (
66
+ <div
67
+ key={`group-${index}`}
68
+ className={`o-footer__matrix-group o-footer__matrix-group--${submenu.length}`}>
69
+ <SectionTitle label={item.label} index={index} />
70
+ <SectionLinks submenu={submenu} index={index} />
71
+ </div>
72
+ )
73
+ })}
74
+ <div className="o-footer__matrix-group o-footer__matrix-group--1">
75
+ <h3 className="o-footer__matrix-title o-footer__matrix-title--link">
76
+ <a
77
+ className="o-footer__matrix-link o-footer__matrix-link--more"
78
+ id={`o-footer-${footerData.length}`}
79
+ href="https://ft.com/more-from-ft-group">
80
+ <span className="o-footer__matrix-link__copy">More from the FT Group</span>
81
+ </a>
82
+ </h3>
83
+ </div>
84
+ </nav>
85
+ </div>
86
+ )
87
+
88
+ const CopyrightNotice = ({ withoutMarketsData = false }) => {
89
+ const marketsData = withoutMarketsData ? '' : 'Markets data delayed by at least 15 minutes. '
90
+ return (
91
+ <div className="o-footer__copyright">
92
+ <small>
93
+ {`${marketsData}© THE FINANCIAL TIMES LTD ${new Date().getFullYear()}. `}
94
+ <abbr title="Financial Times" aria-label="F T">
95
+ FT
96
+ </abbr>{' '}
97
+ and ‘Financial Times’ are trademarks of The Financial Times Ltd.
98
+ <br />
99
+ The Financial Times and its journalism are subject to a self-regulation regime under the{' '}
100
+ <a href="http://aboutus.ft.com/en-gb/ft-editorial-code/" aria-label="F T Editorial Code of Practice">
101
+ FT Editorial Code of Practice
102
+ </a>
103
+ .
104
+ </small>
105
+ </div>
106
+ )
107
+ }
108
+
109
+ type TCompressedLegalProps = {
110
+ footerData: TNavMenuItem[]
111
+ }
112
+
113
+ const CompressedLegal = ({ footerData }: TCompressedLegalProps) => {
114
+ // Find the legal and privacy column
115
+ const data = footerData.filter((item) => item.label === 'Legal & Privacy')
116
+
117
+ return (
118
+ <React.Fragment>
119
+ {data.map((legal, index) => (
120
+ // The Next navigation API splits footer links data into "columns"
121
+ // <https://github.com/Financial-Times/next-navigation-api/blob/HEAD/server/transforms/footer.js>
122
+ <div key={`column-${index}`}>
123
+ {(legal.submenu.items as TNavMenuItem[][]).map((items, index) => (
124
+ <ul className="o-footer__legal-links" key={`list-${index}`}>
125
+ {items.map((item, index) => (
126
+ <li key={`item-${index}`}>
127
+ <Link item={item} key={`link-${index}`} />
128
+ </li>
129
+ ))}
130
+ </ul>
131
+ ))}
132
+ </div>
133
+ ))}
134
+ </React.Fragment>
135
+ )
136
+ }
137
+
138
+ const NikkeiBrandLogo = () => (
139
+ <div className="o-footer__brand">
140
+ <div className="o-footer__container">
141
+ <div className="o-footer__brand-logo" />
142
+ </div>
143
+ </div>
144
+ )
145
+
146
+ export { SectionLinks, SectionTitle, FooterContents, CompressedLegal, CopyrightNotice, NikkeiBrandLogo }
package/src/index.tsx ADDED
@@ -0,0 +1,39 @@
1
+ import React from 'react'
2
+ import { TNavigationData } from '@financial-times/dotcom-types-navigation'
3
+ import { FooterContents, CopyrightNotice, NikkeiBrandLogo, CompressedLegal } from './components/partials'
4
+
5
+ export type TFooterOptions = {
6
+ theme?: 'dark' | 'light' | string
7
+ }
8
+
9
+ export type TFooterProps = TFooterOptions & {
10
+ data: TNavigationData
11
+ }
12
+
13
+ export function Footer(props: TFooterProps) {
14
+ const footerData = props.data.footer.items
15
+ const theme = props.theme ? `${props.theme}` : 'dark'
16
+ return (
17
+ <footer id="site-footer" className={`o-footer o-footer--theme-${theme}`} data-o-component="o-footer">
18
+ <div className="o-footer__container">
19
+ <FooterContents footerData={footerData} />
20
+ <CopyrightNotice />
21
+ </div>
22
+ <NikkeiBrandLogo />
23
+ </footer>
24
+ )
25
+ }
26
+
27
+ export function LegalFooter(props: TFooterProps) {
28
+ const footerData = props.data.footer.items
29
+ const theme = props.theme ? props.theme : 'dark'
30
+ return (
31
+ <footer id="site-footer" className={`o-footer o-footer--theme-${theme}`} data-o-component="o-footer">
32
+ <div className="o-footer__container">
33
+ <CompressedLegal footerData={footerData} />
34
+ <CopyrightNotice withoutMarketsData={true} />
35
+ </div>
36
+ <NikkeiBrandLogo />
37
+ </footer>
38
+ )
39
+ }
package/styles.scss ADDED
@@ -0,0 +1,5 @@
1
+ // This will be overridden by dotcom-ui-layout, it's necessary here for storybook builds
2
+ $system-code: 'page-kit-footer' !default;
3
+
4
+ @import '@financial-times/o-footer/main';
5
+ @include oFooter();