@financial-times/dotcom-ui-header 9.0.3 → 9.1.1

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.
@@ -31,14 +31,18 @@ const NavListLeft = (props) => (react_1.default.createElement("ul", { className:
31
31
  })));
32
32
  exports.NavListLeft = NavListLeft;
33
33
  const NavListRight = (props) => {
34
- return props.userIsLoggedIn ? react_1.default.createElement(NavListRightLoggedIn, { items: props.data['navbar-right'].items }) : null;
34
+ return props.userIsLoggedIn ? (react_1.default.createElement(NavListRightLoggedIn, { items: props.data['navbar-right'].items, experimentalAccountEntryTest: props.experimentalAccountEntryTest })) : null;
35
35
  };
36
36
  exports.NavListRight = NavListRight;
37
- const NavListRightLoggedIn = ({ items }) => {
37
+ const NavListRightLoggedIn = ({ items, experimentalAccountEntryTest }) => {
38
+ /**
39
+ * Accounts entry test
40
+ * In this rendering theres a hacky solution to replace "Settings & Account" with "MyFT Feed"
41
+ * Once the test is concluded the real data should be updated accordingly and the hack removed
42
+ */
38
43
  return (react_1.default.createElement("ul", { "data-component": "nav-list--right", className: "o-header__nav-list o-header__nav-list--right", "data-trackable": "user-nav" }, items.map((item, index) => {
39
44
  var _a;
40
- return (react_1.default.createElement("li", { className: "o-header__nav-item", key: `link-${index}` },
41
- react_1.default.createElement("a", { className: "o-header__nav-link", href: (_a = item.url) !== null && _a !== void 0 ? _a : undefined, "data-trackable": item.label }, item.label)));
45
+ return (react_1.default.createElement("li", { className: "o-header__nav-item", key: `link-${index}` }, item.label === 'Settings & Account' && experimentalAccountEntryTest ? (react_1.default.createElement("a", { className: "o-header__nav-link", href: "/myft", "data-trackable": "my-ft" }, "myFT Feed")) : (react_1.default.createElement("a", { className: "o-header__nav-link", href: (_a = item.url) !== null && _a !== void 0 ? _a : undefined, "data-trackable": item.label }, item.label))));
42
46
  })));
43
47
  };
44
48
  const MegaNav = ({ label, meganav, index }) => {
@@ -16,9 +16,10 @@ declare const SubscribeButton: ({ item, variant, className }: {
16
16
  variant?: string | undefined;
17
17
  className?: string | undefined;
18
18
  }) => React.JSX.Element;
19
- declare const TopColumnRightAnon: ({ items, variant }: {
19
+ declare const TopColumnRightAnon: ({ items, variant, experimentalAccountEntryTest }: {
20
20
  items: TNavMenuItem[];
21
21
  variant?: string | undefined;
22
+ experimentalAccountEntryTest?: boolean | undefined;
22
23
  }) => React.JSX.Element;
23
24
  declare const TopColumnRight: (props: THeaderProps) => React.JSX.Element;
24
25
  export { HeaderWrapper, TopWrapper, TopColumnLeft, TopColumnCenter, TopColumnCenterNoLink, TopColumnRight, TopColumnRightAnon, SubscribeButton, SignInLink };
@@ -12,8 +12,27 @@ const DrawerIcon = () => (react_1.default.createElement("a", { href: "#o-header-
12
12
  react_1.default.createElement("span", { className: "o-header__top-link-label" }, "Open side navigation menu")));
13
13
  const SearchIcon = () => (react_1.default.createElement("a", { href: `#o-header-search-primary`, className: "o-header__top-icon-link o-header__top-icon-link--search", "aria-controls": `o-header-search-primary`, title: "Open search bar", "data-trackable": "search-toggle" },
14
14
  react_1.default.createElement("span", { className: "o-header__top-link-label" }, "Open search bar")));
15
- const MyFt = ({ className }) => (react_1.default.createElement("a", { className: `o-header__top-icon-link o-header__top-icon-link--myft ${className}`, id: "o-header-top-link-myft", href: "/myft", "data-trackable": "my-ft", "data-tour-stage": "myFt", "aria-label": "My F T" },
15
+ const TopRightAccountEntry = ({ className, signedIn, experimentalAccountEntryTest }) => {
16
+ if (experimentalAccountEntryTest) {
17
+ return react_1.default.createElement(MyAccountLink, { signedIn: signedIn });
18
+ }
19
+ else {
20
+ return react_1.default.createElement(MyFtLogoLink, { className: className });
21
+ }
22
+ };
23
+ const MyFtLogoLink = ({ className }) => (react_1.default.createElement("a", { className: `o-header__top-icon-link o-header__top-icon-link--myft ${className}`, id: "o-header-top-link-myft", href: "/myft", "data-trackable": "my-ft", "data-tour-stage": "myFt", "aria-label": "My F T" },
16
24
  react_1.default.createElement("span", { className: "o-header__visually-hidden" }, "myFT")));
25
+ const MyAccountLink = ({ signedIn }) => {
26
+ const classNames = 'o-header__top-link ft-header__top-link--myaccount';
27
+ if (signedIn) {
28
+ return (react_1.default.createElement("a", { className: classNames, id: "o-header-top-link-myaccount", href: "/myaccount", "data-trackable": "my-account" },
29
+ react_1.default.createElement("span", null, "My Account")));
30
+ }
31
+ else {
32
+ return (react_1.default.createElement("a", { className: classNames, id: "o-header-top-link-signin", href: "/login?location=/", "data-trackable": "Sign In" },
33
+ react_1.default.createElement("span", null, "Sign In")));
34
+ }
35
+ };
17
36
  const TopWrapper = (props) => (react_1.default.createElement("div", { className: "o-header__row o-header__top", "data-trackable": "header-top" },
18
37
  react_1.default.createElement("div", { className: "o-header__container" },
19
38
  react_1.default.createElement("div", { className: "o-header__top-wrapper" }, props.children))));
@@ -35,7 +54,7 @@ const TopColumnRightLoggedIn = (props) => {
35
54
  const subscribeAction = (_b = (_a = props.data['navbar-right-anon']) === null || _a === void 0 ? void 0 : _a.items) === null || _b === void 0 ? void 0 : _b[1];
36
55
  return (react_1.default.createElement("div", { className: "o-header__top-column o-header__top-column--right" },
37
56
  !props.userIsSubscribed && subscribeAction && (react_1.default.createElement(SubscribeButton, { item: subscribeAction, variant: props.variant, className: "o-header__top-button--hide-m" })),
38
- react_1.default.createElement(MyFt, { className: "" })));
57
+ react_1.default.createElement(TopRightAccountEntry, { className: "", signedIn: true, experimentalAccountEntryTest: props.experimentalAccountEntryTest })));
39
58
  };
40
59
  const SignInLink = ({ item, variant, className }) => {
41
60
  var _a;
@@ -52,13 +71,13 @@ const SubscribeButton = ({ item, variant, className }) => {
52
71
  role: "button", href: (_a = item.url) !== null && _a !== void 0 ? _a : undefined, "data-trackable": item.label }, setTabIndex), item.label));
53
72
  };
54
73
  exports.SubscribeButton = SubscribeButton;
55
- const TopColumnRightAnon = ({ items, variant }) => {
74
+ const TopColumnRightAnon = ({ items, variant, experimentalAccountEntryTest }) => {
56
75
  // If user is anonymous the second list item is styled as a button
57
76
  const [signInAction, subscribeAction] = items;
58
77
  return (react_1.default.createElement("div", { className: "o-header__top-column o-header__top-column--right" },
59
78
  subscribeAction && (react_1.default.createElement(SubscribeButton, { item: subscribeAction, variant: variant, className: "o-header__top-button--hide-m" })),
60
- signInAction && (react_1.default.createElement(SignInLink, { item: signInAction, variant: variant, className: "o-header__top-link--hide-m" })),
61
- react_1.default.createElement(MyFt, { className: "o-header__top-icon-link--show-m" })));
79
+ signInAction && !experimentalAccountEntryTest && (react_1.default.createElement(SignInLink, { item: signInAction, variant: variant, className: "o-header__top-link--hide-m" })),
80
+ react_1.default.createElement(TopRightAccountEntry, { className: "o-header__top-icon-link--show-m", signedIn: false, experimentalAccountEntryTest: experimentalAccountEntryTest })));
62
81
  };
63
82
  exports.TopColumnRightAnon = TopColumnRightAnon;
64
83
  const TopColumnRight = (props) => {
@@ -67,7 +86,7 @@ const TopColumnRight = (props) => {
67
86
  }
68
87
  else {
69
88
  const userNavAnonItems = props.data['navbar-right-anon'].items;
70
- return react_1.default.createElement(TopColumnRightAnon, { items: userNavAnonItems, variant: props.variant });
89
+ return (react_1.default.createElement(TopColumnRightAnon, { items: userNavAnonItems, variant: props.variant, experimentalAccountEntryTest: props.experimentalAccountEntryTest }));
71
90
  }
72
91
  };
73
92
  exports.TopColumnRight = TopColumnRight;
@@ -12,6 +12,7 @@ declare namespace MainHeader {
12
12
  showUserNavigation?: boolean | undefined;
13
13
  showStickyHeader?: boolean | undefined;
14
14
  showMegaNav?: boolean | undefined;
15
+ experimentalAccountEntryTest?: boolean | undefined;
15
16
  };
16
17
  }
17
18
  declare function StickyHeader(props: THeaderProps): React.JSX.Element | null;
@@ -172,8 +172,8 @@
172
172
  "affectsGlobalScope": false
173
173
  },
174
174
  "../src/interfaces.d.ts": {
175
- "version": "684c1dc599277cd5ee34399a10aa4b9d69de5092225d169580c70d0d0902530b",
176
- "signature": "684c1dc599277cd5ee34399a10aa4b9d69de5092225d169580c70d0d0902530b",
175
+ "version": "c76ad9342fa432157cdfb6b7bb9f7b5be28c41e445a03c48a4b51789f0253def",
176
+ "signature": "c76ad9342fa432157cdfb6b7bb9f7b5be28c41e445a03c48a4b51789f0253def",
177
177
  "affectsGlobalScope": false
178
178
  },
179
179
  "../src/components/svg-components/BrandFtMasthead.tsx": {
@@ -182,8 +182,8 @@
182
182
  "affectsGlobalScope": false
183
183
  },
184
184
  "../src/components/top/partials.tsx": {
185
- "version": "6fe24079686c1fafe3d97037422d0bf6145f3248984965d30629f2311450bbae",
186
- "signature": "e8de3d399509eabe8ee7c1c6a53acb889049db993138e9228b9130fc8a2b7491",
185
+ "version": "48aed39291b9dea7e0ad0fefc77be347113dd8e8f7da4413759358aa229bf526",
186
+ "signature": "78ae2b5202a854b8786a4b7797efaf5fdf7a16b22aca84a7125819ea87c66b33",
187
187
  "affectsGlobalScope": false
188
188
  },
189
189
  "../src/utils.ts": {
@@ -192,7 +192,7 @@
192
192
  "affectsGlobalScope": false
193
193
  },
194
194
  "../src/components/navigation/partials.tsx": {
195
- "version": "7b9ea69b87931f148f58cf5ac77d5a54d0f33903c3504ca7ec8f5163d6957ca1",
195
+ "version": "31a0ab1eeb4d7546dee5e81fba9e91ec23118b56344e681cddd1cc4c72e387cb",
196
196
  "signature": "fff57eecd77a2ff5847ac6a4139236fb1671b562d8f5b4f5aa16a2a101212dd0",
197
197
  "affectsGlobalScope": false
198
198
  },
@@ -223,7 +223,7 @@
223
223
  },
224
224
  "../src/index.tsx": {
225
225
  "version": "f076cad62acf3ca2bca2348a445aeef886e7c0db4ae610eafba754942df1fbec",
226
- "signature": "41977069753041b77de842d621e6318454eddb69640cd2f4198268fba7b5cc82",
226
+ "signature": "2e662fa1dc23c31831c88aad3a02c5d1a4bda33d8f7a929735a57f715b525a0f",
227
227
  "affectsGlobalScope": false
228
228
  },
229
229
  "../../../node_modules/@types/aria-query/index.d.ts": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/dotcom-ui-header",
3
- "version": "9.0.3",
3
+ "version": "9.1.1",
4
4
  "description": "",
5
5
  "browser": "browser.js",
6
6
  "main": "component.js",
@@ -22,7 +22,7 @@
22
22
  "author": "",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@financial-times/dotcom-types-navigation": "^9.0.3",
25
+ "@financial-times/dotcom-types-navigation": "^9.1.1",
26
26
  "n-topic-search": "^4.0.0"
27
27
  },
28
28
  "devDependencies": {
@@ -55,6 +55,46 @@ DefaultHeaderWithDrawer.args = {
55
55
  showLogoLink: false
56
56
  }
57
57
 
58
+ export const DefaultHeaderWithDrawerEntryTestAnon = (args) => (
59
+ <OnReady callback={onReadyCallback}>
60
+ <HeaderSimple {...storyData} {...args} />
61
+ <Drawer {...storyData} {...args} />
62
+ </OnReady>
63
+ )
64
+
65
+ DefaultHeaderWithDrawerEntryTestAnon.story = {
66
+ name: 'Default header with drawer - Account entry test [Anon]'
67
+ }
68
+ DefaultHeaderWithDrawerEntryTestAnon.args = {
69
+ showSubNavigation: true,
70
+ showMegaNav: true,
71
+ showUserNavigation: true,
72
+ userIsLoggedIn: false,
73
+ userIsSubscribed: false,
74
+ showLogoLink: false,
75
+ experimentalAccountEntryTest: true
76
+ }
77
+
78
+ export const DefaultHeaderWithDrawerEntryTest = (args) => (
79
+ <OnReady callback={onReadyCallback}>
80
+ <HeaderSimple {...storyData} {...args} />
81
+ <Drawer {...storyData} {...args} />
82
+ </OnReady>
83
+ )
84
+
85
+ DefaultHeaderWithDrawerEntryTest.story = {
86
+ name: 'Default header with drawer - Account entry test'
87
+ }
88
+ DefaultHeaderWithDrawerEntryTest.args = {
89
+ showSubNavigation: true,
90
+ showMegaNav: true,
91
+ showUserNavigation: true,
92
+ userIsLoggedIn: true,
93
+ userIsSubscribed: false,
94
+ showLogoLink: false,
95
+ experimentalAccountEntryTest: true
96
+ }
97
+
58
98
  export const DefaultHeaderWithRightAlignedSubnav = (args) => (
59
99
  <OnReady callback={onReadyCallback}>
60
100
  <HeaderSimple {...profileStoryData} {...args} />
@@ -77,10 +77,26 @@ const NavListLeft = (props: THeaderProps) => (
77
77
  )
78
78
 
79
79
  const NavListRight = (props: THeaderProps) => {
80
- return props.userIsLoggedIn ? <NavListRightLoggedIn items={props.data['navbar-right'].items} /> : null
80
+ return props.userIsLoggedIn ? (
81
+ <NavListRightLoggedIn
82
+ items={props.data['navbar-right'].items}
83
+ experimentalAccountEntryTest={props.experimentalAccountEntryTest}
84
+ />
85
+ ) : null
81
86
  }
82
87
 
83
- const NavListRightLoggedIn = ({ items }: { items: TNavMenuItem[] }) => {
88
+ const NavListRightLoggedIn = ({
89
+ items,
90
+ experimentalAccountEntryTest
91
+ }: {
92
+ items: TNavMenuItem[]
93
+ experimentalAccountEntryTest?: boolean
94
+ }) => {
95
+ /**
96
+ * Accounts entry test
97
+ * In this rendering theres a hacky solution to replace "Settings & Account" with "MyFT Feed"
98
+ * Once the test is concluded the real data should be updated accordingly and the hack removed
99
+ */
84
100
  return (
85
101
  <ul
86
102
  data-component="nav-list--right"
@@ -89,9 +105,15 @@ const NavListRightLoggedIn = ({ items }: { items: TNavMenuItem[] }) => {
89
105
  >
90
106
  {items.map((item, index) => (
91
107
  <li className="o-header__nav-item" key={`link-${index}`}>
92
- <a className="o-header__nav-link" href={item.url ?? undefined} data-trackable={item.label}>
93
- {item.label}
94
- </a>
108
+ {item.label === 'Settings & Account' && experimentalAccountEntryTest ? (
109
+ <a className="o-header__nav-link" href="/myft" data-trackable="my-ft">
110
+ myFT Feed
111
+ </a>
112
+ ) : (
113
+ <a className="o-header__nav-link" href={item.url ?? undefined} data-trackable={item.label}>
114
+ {item.label}
115
+ </a>
116
+ )}
95
117
  </li>
96
118
  ))}
97
119
  </ul>
@@ -39,7 +39,23 @@ const SearchIcon = () => (
39
39
  </a>
40
40
  )
41
41
 
42
- const MyFt = ({ className }: { className?: string }) => (
42
+ const TopRightAccountEntry = ({
43
+ className,
44
+ signedIn,
45
+ experimentalAccountEntryTest
46
+ }: {
47
+ className?: string
48
+ signedIn: boolean
49
+ experimentalAccountEntryTest?: boolean
50
+ }) => {
51
+ if (experimentalAccountEntryTest) {
52
+ return <MyAccountLink signedIn={signedIn} />
53
+ } else {
54
+ return <MyFtLogoLink className={className} />
55
+ }
56
+ }
57
+
58
+ const MyFtLogoLink = ({ className }: { className?: string }) => (
43
59
  <a
44
60
  className={`o-header__top-icon-link o-header__top-icon-link--myft ${className}`}
45
61
  id="o-header-top-link-myft"
@@ -52,6 +68,34 @@ const MyFt = ({ className }: { className?: string }) => (
52
68
  </a>
53
69
  )
54
70
 
71
+ const MyAccountLink = ({ signedIn }: { signedIn: boolean }) => {
72
+ const classNames = 'o-header__top-link ft-header__top-link--myaccount'
73
+
74
+ if (signedIn) {
75
+ return (
76
+ <a
77
+ className={classNames}
78
+ id="o-header-top-link-myaccount"
79
+ href="/myaccount"
80
+ data-trackable="my-account"
81
+ >
82
+ <span>My Account</span>
83
+ </a>
84
+ )
85
+ } else {
86
+ return (
87
+ <a
88
+ className={classNames}
89
+ id="o-header-top-link-signin"
90
+ href="/login?location=/"
91
+ data-trackable="Sign In"
92
+ >
93
+ <span>Sign In</span>
94
+ </a>
95
+ )
96
+ }
97
+ }
98
+
55
99
  const TopWrapper = (props) => (
56
100
  <div className="o-header__row o-header__top" data-trackable="header-top">
57
101
  <div className="o-header__container">
@@ -100,7 +144,11 @@ const TopColumnRightLoggedIn = (props: THeaderProps) => {
100
144
  className="o-header__top-button--hide-m"
101
145
  />
102
146
  )}
103
- <MyFt className="" />
147
+ <TopRightAccountEntry
148
+ className=""
149
+ signedIn={true}
150
+ experimentalAccountEntryTest={props.experimentalAccountEntryTest}
151
+ />
104
152
  </div>
105
153
  )
106
154
  }
@@ -151,18 +199,31 @@ const SubscribeButton = ({
151
199
  )
152
200
  }
153
201
 
154
- const TopColumnRightAnon = ({ items, variant }: { items: TNavMenuItem[]; variant?: string }) => {
202
+ const TopColumnRightAnon = ({
203
+ items,
204
+ variant,
205
+ experimentalAccountEntryTest
206
+ }: {
207
+ items: TNavMenuItem[]
208
+ variant?: string
209
+ experimentalAccountEntryTest?: boolean
210
+ }) => {
155
211
  // If user is anonymous the second list item is styled as a button
156
212
  const [signInAction, subscribeAction] = items
213
+
157
214
  return (
158
215
  <div className="o-header__top-column o-header__top-column--right">
159
216
  {subscribeAction && (
160
217
  <SubscribeButton item={subscribeAction} variant={variant} className="o-header__top-button--hide-m" />
161
218
  )}
162
- {signInAction && (
219
+ {signInAction && !experimentalAccountEntryTest && (
163
220
  <SignInLink item={signInAction} variant={variant} className="o-header__top-link--hide-m" />
164
221
  )}
165
- <MyFt className="o-header__top-icon-link--show-m" />
222
+ <TopRightAccountEntry
223
+ className="o-header__top-icon-link--show-m"
224
+ signedIn={false}
225
+ experimentalAccountEntryTest={experimentalAccountEntryTest}
226
+ />
166
227
  </div>
167
228
  )
168
229
  }
@@ -172,7 +233,13 @@ const TopColumnRight = (props: THeaderProps) => {
172
233
  return <TopColumnRightLoggedIn {...props} />
173
234
  } else {
174
235
  const userNavAnonItems = props.data['navbar-right-anon'].items
175
- return <TopColumnRightAnon items={userNavAnonItems} variant={props.variant} />
236
+ return (
237
+ <TopColumnRightAnon
238
+ items={userNavAnonItems}
239
+ variant={props.variant}
240
+ experimentalAccountEntryTest={props.experimentalAccountEntryTest}
241
+ />
242
+ )
176
243
  }
177
244
  }
178
245
 
package/src/header.scss CHANGED
@@ -3,7 +3,7 @@
3
3
  // Once for a core experience, once for an enhanced experience.
4
4
  // The enhanced version is hidden until toggled with JavaScript.
5
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
6
+ // one enhanced experience search block and reveal for the core
7
7
  // experience if needed.
8
8
  .core [data-o-header-search] {
9
9
  display: block;
@@ -27,3 +27,39 @@
27
27
  .n-typeahead {
28
28
  display: none;
29
29
  }
30
+
31
+ // The styles below are part of an AB test
32
+ // If the test is successful these should be incorporated into o-header
33
+ .ft-header__top-link--myaccount {
34
+ @include oTypographySans(0);
35
+ }
36
+
37
+
38
+ .ft-header__top-link--myaccount span {
39
+ vertical-align: middle;
40
+ // Hide the myaccount/sign in text on smaller screens leaving the icon only
41
+ @include oGridRespondTo($until: 'M') {
42
+ @include oNormaliseVisuallyHidden;
43
+ }
44
+ }
45
+
46
+ // Override the hover styles so the underline
47
+ // Is only under the text and not the icon
48
+ // And is closer to the text
49
+ .ft-header__top-link--myaccount::after {
50
+ width: calc(100% - 32px);
51
+ left: unset;
52
+ right: 0;
53
+ bottom: 8px;
54
+ }
55
+
56
+ .ft-header__top-link--myaccount::before {
57
+ content: '';
58
+ display: block;
59
+ @include oIconsContent(
60
+ $icon-name: 'user',
61
+ $color: oColorsByName('black'),
62
+ $size: 32
63
+ );
64
+ vertical-align: middle;
65
+ }
@@ -10,6 +10,12 @@ export type THeaderOptions = {
10
10
  showStickyHeader?: boolean
11
11
  showMegaNav?: boolean
12
12
  showLogoLink?: boolean
13
+ /*
14
+ * experimentalAccountEntryTest is an experimental feature switch
15
+ * This is being run as an AB test and should be removed afterwards
16
+ * This option shouldn't be used by anyone without consulting the CP Retention team first
17
+ */
18
+ experimentalAccountEntryTest?: boolean
13
19
  }
14
20
 
15
21
  export type THeaderProps = THeaderOptions & {