@newskit-render/core 1.66.0 → 1.68.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/CHANGELOG.md +133 -0
- package/README.md +16 -0
- package/__tests__/pages/[articleSlug].test.tsx +1 -55
- package/__tests__/pages/__snapshots__/home.test.tsx.snap +657 -626
- package/__tests__/pages/__snapshots__/relatedArticles.test.tsx.snap +651 -0
- package/__tests__/pages/relatedArticles.test.tsx +23 -11
- package/components/article/__tests__/__snapshots__/index.test.tsx.snap +1091 -1060
- package/components/footer/index.tsx +1 -1
- package/components/header/banner-messages.ts +45 -0
- package/components/header/index.tsx +31 -287
- package/components/header/navigation-links.ts +20 -0
- package/components/layout/LayoutTemplate.tsx +4 -1
- package/config/__tests__/index.test.ts +53 -0
- package/config/environment.ts +67 -0
- package/config/index.ts +2 -85
- package/config/multiTenancy.ts +12 -0
- package/{app-context → context/app-context}/AppContext.test.tsx +7 -3
- package/{app-context/AppContext.tsx → context/app-context/index.tsx} +5 -1
- package/context/index.tsx +2 -0
- package/context/multi-tenancy/MultiTenancy.test.tsx +47 -0
- package/context/multi-tenancy/index.tsx +31 -0
- package/css/index.ts +224 -0
- package/cypress/support/commands.js +8 -4
- package/helpers/__tests__/createThemeDropdownObject.test.ts +3 -3
- package/helpers/__tests__/getRecommendation.test.ts +62 -0
- package/helpers/createThemeDropdownObject.ts +3 -3
- package/helpers/getRecommendations.ts +29 -0
- package/helpers/global-types.ts +8 -0
- package/{__tests__/pages/mocks.ts → helpers/mocks/getRecommendationsMock.ts} +2 -6
- package/helpers/multiTenancy.ts +19 -0
- package/jest.config.js +1 -2
- package/package.json +11 -9
- package/pages/[section]/[articleId]/[articleSlug].tsx +17 -10
- package/pages/[section]/[articleId]/relatedArticles.tsx +49 -40
- package/pages/_app.tsx +42 -257
- package/pages/account/cancellation/index.tsx +1 -1
- package/pages/account/edit/[field].tsx +1 -1
- package/pages/account/index.tsx +1 -1
- package/pages/account/newsletters-and-alerts/index.tsx +1 -1
- package/pages/account/payment/index.tsx +1 -1
- package/pages/account/subscription-and-billing/index.tsx +1 -1
- package/pages/api/auth/[...nextauth].ts +5 -1
- package/pages/api/recommendations/[...slug].ts +21 -0
- package/pages/checkout/account-creation/index.tsx +1 -1
- package/pages/checkout/payment-details/index.tsx +1 -1
- package/pages/help-hub/[id]/index.tsx +22 -9
- package/pages/help-hub/index.tsx +22 -9
- package/pages/help-hub/results.tsx +24 -0
- package/theme/strings/demo.ts +1 -0
- package/theme/strings/index.ts +1 -0
- package/components/header/index.test.tsx +0 -73
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import { MainGrid } from '../layout/MainGrid'
|
|
14
14
|
import NewsKitLogoShort from '../common/NewskitLogoShort'
|
|
15
15
|
import NavLink from '../common/NavLink'
|
|
16
|
-
import { AppContext } from '../../
|
|
16
|
+
import { AppContext } from '../../context'
|
|
17
17
|
import { getYear } from '../../helpers/getYear'
|
|
18
18
|
|
|
19
19
|
const StyledFooter = styled.div`
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const pastDueBanner = {
|
|
2
|
+
firstNotice: {
|
|
3
|
+
title: "We haven't been able to take payment",
|
|
4
|
+
text:
|
|
5
|
+
'You may need to update your payment details to keep your subscription.',
|
|
6
|
+
button: 'Update payment details',
|
|
7
|
+
},
|
|
8
|
+
secondNotice: {
|
|
9
|
+
title: 'Act now to keep your subscription',
|
|
10
|
+
text:
|
|
11
|
+
'We’ve tried several times, but haven’t been able to take payment. Please update your payment details to keep your subscription.',
|
|
12
|
+
button: 'Update payment details',
|
|
13
|
+
},
|
|
14
|
+
terminated: {
|
|
15
|
+
title: 'Your subscription has been terminated',
|
|
16
|
+
phoneNumber: 'XXXX-XXX-XXXX',
|
|
17
|
+
text:
|
|
18
|
+
'We didn’t receive payment for your subscription. To reactivate it, please call ##PHONE_NUMBER##.',
|
|
19
|
+
dismissDays: 7,
|
|
20
|
+
},
|
|
21
|
+
cancelled: {
|
|
22
|
+
title: 'Your subscription has been cancelled.',
|
|
23
|
+
phoneNumber: 'XXXX-XXX-XXXX',
|
|
24
|
+
text:
|
|
25
|
+
'You’ll no longer have access to subscription benefits. To re-activate call ##PHONE_NUMBER##.',
|
|
26
|
+
dismissDays: 7,
|
|
27
|
+
},
|
|
28
|
+
toBeCancelled: {
|
|
29
|
+
title: 'Your subscription will end soon.',
|
|
30
|
+
phoneNumber: 'XXXX-XXX-XXXX',
|
|
31
|
+
text:
|
|
32
|
+
'You have cancelled your subscription and will lose access to all benefits on ##DATE##. To re-activate your subscription call ##PHONE_NUMBER##.',
|
|
33
|
+
},
|
|
34
|
+
toBeCancelledWithRefund: {
|
|
35
|
+
title: 'Your subscription will end soon.',
|
|
36
|
+
phoneNumber: '0800 xxxx xxxxx',
|
|
37
|
+
text:
|
|
38
|
+
'We have successfully cancelled your subscription and will be processing your refund shortly. If you have any question please call ##PHONE_NUMBER##.',
|
|
39
|
+
dismissDays: 7,
|
|
40
|
+
},
|
|
41
|
+
treshold: {
|
|
42
|
+
firstNotice: 26,
|
|
43
|
+
secondNotice: 30,
|
|
44
|
+
},
|
|
45
|
+
}
|
|
@@ -1,314 +1,58 @@
|
|
|
1
|
-
import React, { useContext
|
|
2
|
-
import {
|
|
3
|
-
Cell,
|
|
4
|
-
styled,
|
|
5
|
-
getColorCssFromTheme,
|
|
6
|
-
getSizingCssFromTheme,
|
|
7
|
-
getFontsFromTheme,
|
|
8
|
-
Visible,
|
|
9
|
-
getMediaQueryFromTheme,
|
|
10
|
-
Stack,
|
|
11
|
-
Block,
|
|
12
|
-
IconFilledMenu,
|
|
13
|
-
Image,
|
|
14
|
-
} from 'newskit'
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import { styled, Block } from 'newskit'
|
|
15
3
|
import { UserData } from '@newskit-render/my-account'
|
|
16
4
|
import {
|
|
17
|
-
NextLink,
|
|
18
|
-
theSunTheme,
|
|
19
|
-
timesTheme,
|
|
20
|
-
virginRadioTheme,
|
|
21
|
-
ThemeDropdown,
|
|
22
5
|
PastDueBannerExternal,
|
|
6
|
+
NavigationPrimary,
|
|
23
7
|
} from '@newskit-render/shared-components'
|
|
24
8
|
import {
|
|
25
9
|
renderCustomLightTheme as LightTheme,
|
|
26
10
|
renderCustomDarkTheme as DarkTheme,
|
|
27
11
|
} from '../../theme'
|
|
28
|
-
import {
|
|
29
|
-
import
|
|
30
|
-
import
|
|
31
|
-
import
|
|
32
|
-
import { AppContext } from '../../app-context/AppContext'
|
|
33
|
-
import { handleEnterKeyPress } from '../../helpers/a11y'
|
|
34
|
-
import IconNavLink from '../common/iconNavLink'
|
|
35
|
-
import { IconAccount } from '../common/icons/IconAccount'
|
|
12
|
+
import { AppContext } from '../../context'
|
|
13
|
+
import { createThemeDropdownObject } from '../../helpers/createThemeDropdownObject'
|
|
14
|
+
import { sectionLinks } from './navigation-links'
|
|
15
|
+
import { pastDueBanner } from './banner-messages'
|
|
36
16
|
|
|
37
17
|
export const headerSize = 'sizing100'
|
|
38
18
|
|
|
39
|
-
const StyledHeader = styled.div`
|
|
40
|
-
${getSizingCssFromTheme('height', headerSize)}
|
|
41
|
-
${getColorCssFromTheme('backgroundColor', 'interfaceBrand010')};
|
|
42
|
-
a,
|
|
43
|
-
a:visited:not(:disabled) {
|
|
44
|
-
${getColorCssFromTheme('color', 'white')};
|
|
45
|
-
font-weight: bold;
|
|
46
|
-
line-height: ${getFontsFromTheme('fontLineHeight040')};
|
|
47
|
-
}
|
|
48
|
-
${getMediaQueryFromTheme('xs')} {
|
|
49
|
-
${getSizingCssFromTheme('paddingTop', 'sizing040')};
|
|
50
|
-
${getSizingCssFromTheme('paddingBottom', 'sizing040')};
|
|
51
|
-
${getSizingCssFromTheme('paddingLeft', 'sizing000')};
|
|
52
|
-
${getSizingCssFromTheme('paddingRight', 'sizing000')};
|
|
53
|
-
}
|
|
54
|
-
${getMediaQueryFromTheme('lg')} {
|
|
55
|
-
${getSizingCssFromTheme('paddingTop', 'sizing040')};
|
|
56
|
-
${getSizingCssFromTheme('paddingBottom', 'sizing040')};
|
|
57
|
-
${getSizingCssFromTheme('paddingLeft', 'sizing090')};
|
|
58
|
-
${getSizingCssFromTheme('paddingRight', 'sizing090')};
|
|
59
|
-
}
|
|
60
|
-
`
|
|
61
|
-
|
|
62
|
-
const pastDueBanner = {
|
|
63
|
-
firstNotice: {
|
|
64
|
-
title: "We haven't been able to take payment",
|
|
65
|
-
text:
|
|
66
|
-
'You may need to update your payment details to keep your subscription.',
|
|
67
|
-
button: 'Update payment details',
|
|
68
|
-
},
|
|
69
|
-
secondNotice: {
|
|
70
|
-
title: 'Act now to keep your subscription',
|
|
71
|
-
text:
|
|
72
|
-
'We’ve tried several times, but haven’t been able to take payment. Please update your payment details to keep your subscription.',
|
|
73
|
-
button: 'Update payment details',
|
|
74
|
-
},
|
|
75
|
-
terminated: {
|
|
76
|
-
title: 'Your subscription has been terminated',
|
|
77
|
-
phoneNumber: 'XXXX-XXX-XXXX',
|
|
78
|
-
text:
|
|
79
|
-
'We didn’t receive payment for your subscription. To reactivate it, please call ##PHONE_NUMBER##.',
|
|
80
|
-
dismissDays: 7,
|
|
81
|
-
},
|
|
82
|
-
cancelled: {
|
|
83
|
-
title: 'Your subscription has been cancelled.',
|
|
84
|
-
phoneNumber: 'XXXX-XXX-XXXX',
|
|
85
|
-
text:
|
|
86
|
-
'You’ll no longer have access to subscription benefits. To re-activate call ##PHONE_NUMBER##.',
|
|
87
|
-
dismissDays: 7,
|
|
88
|
-
},
|
|
89
|
-
toBeCancelled: {
|
|
90
|
-
title: 'Your subscription will end soon.',
|
|
91
|
-
phoneNumber: 'XXXX-XXX-XXXX',
|
|
92
|
-
text:
|
|
93
|
-
'You have cancelled your subscription and will lose access to all benefits on ##DATE##. To re-activate your subscription call ##PHONE_NUMBER##.',
|
|
94
|
-
},
|
|
95
|
-
toBeCancelledWithRefund: {
|
|
96
|
-
title: 'Your subscription will end soon.',
|
|
97
|
-
phoneNumber: '0800 xxxx xxxxx',
|
|
98
|
-
text:
|
|
99
|
-
'We have successfully cancelled your subscription and will be processing your refund shortly. If you have any question please call ##PHONE_NUMBER##.',
|
|
100
|
-
dismissDays: 7,
|
|
101
|
-
},
|
|
102
|
-
treshold: {
|
|
103
|
-
firstNotice: 26,
|
|
104
|
-
secondNotice: 30,
|
|
105
|
-
},
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
type NavigationProps = {
|
|
109
|
-
show: boolean
|
|
110
|
-
}
|
|
111
|
-
|
|
112
19
|
const BannerContainer = styled(Block)`
|
|
113
20
|
position: sticky;
|
|
114
21
|
top: 0;
|
|
115
22
|
z-index: 1;
|
|
116
23
|
`
|
|
117
24
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
display: ${(props) => (props.show ? 'block' : 'none')};
|
|
128
|
-
|
|
129
|
-
${getMediaQueryFromTheme('md')} {
|
|
130
|
-
display: flex;
|
|
131
|
-
position: relative;
|
|
132
|
-
overflow: hidden;
|
|
133
|
-
width: initial;
|
|
134
|
-
min-height: initial;
|
|
135
|
-
background-color: initial;
|
|
136
|
-
left: initial;
|
|
137
|
-
top: initial;
|
|
138
|
-
z-index: 1;
|
|
139
|
-
${getSizingCssFromTheme('height', 'sizing080')}
|
|
140
|
-
}
|
|
141
|
-
`
|
|
142
|
-
|
|
143
|
-
const Navigation = styled.div`
|
|
144
|
-
display: flex;
|
|
145
|
-
flex-flow: row wrap;
|
|
146
|
-
justify-content: space-between;
|
|
147
|
-
flex-direction: column;
|
|
148
|
-
align-items: center;
|
|
149
|
-
|
|
150
|
-
${getMediaQueryFromTheme('md')} {
|
|
151
|
-
justify-content: flex-end;
|
|
152
|
-
flex-direction: row;
|
|
25
|
+
const Header: React.FC<{ user: UserData }> = ({ user }) => {
|
|
26
|
+
const navigationPrimary = {
|
|
27
|
+
nav: sectionLinks,
|
|
28
|
+
loggedInUser: !!user,
|
|
29
|
+
gridOverrides: {
|
|
30
|
+
width: '100%',
|
|
31
|
+
maxWidth: '1920px',
|
|
32
|
+
marginInline: 'auto',
|
|
33
|
+
},
|
|
153
34
|
}
|
|
154
|
-
`
|
|
155
|
-
|
|
156
|
-
const NavButton = ({
|
|
157
|
-
buttonText,
|
|
158
|
-
href,
|
|
159
|
-
ariaLabel,
|
|
160
|
-
}: {
|
|
161
|
-
buttonText: string
|
|
162
|
-
href: string
|
|
163
|
-
isLast?: boolean
|
|
164
|
-
ariaLabel?: string
|
|
165
|
-
}) => (
|
|
166
|
-
<Block
|
|
167
|
-
as="span"
|
|
168
|
-
spaceInline={{
|
|
169
|
-
xs: 'space000',
|
|
170
|
-
md: 'space040',
|
|
171
|
-
}}
|
|
172
|
-
spaceStack={{
|
|
173
|
-
xs: 'space050',
|
|
174
|
-
md: 'space000',
|
|
175
|
-
}}
|
|
176
|
-
>
|
|
177
|
-
<NavLink href={href} ariaLabel={ariaLabel} buttonText={buttonText} />
|
|
178
|
-
</Block>
|
|
179
|
-
)
|
|
180
35
|
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
'The-Times': {
|
|
185
|
-
src: 'navigationPrimary-brandMark-times.svg',
|
|
186
|
-
width: '150px',
|
|
187
|
-
},
|
|
188
|
-
}
|
|
36
|
+
const { theme, setTheme } = useContext(AppContext)
|
|
37
|
+
const themeDropdownObject = createThemeDropdownObject(setTheme)
|
|
38
|
+
const { sharedTheme, ...restThemes } = themeDropdownObject.options
|
|
189
39
|
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
40
|
+
const themeDropdownObjectUpdated = {
|
|
41
|
+
...themeDropdownObject,
|
|
42
|
+
options: {
|
|
43
|
+
LightTheme,
|
|
44
|
+
DarkTheme,
|
|
45
|
+
...restThemes,
|
|
46
|
+
},
|
|
195
47
|
}
|
|
196
|
-
const { theme, setTheme } = useContext(AppContext)
|
|
197
48
|
|
|
198
49
|
return (
|
|
199
50
|
<>
|
|
200
|
-
<
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
stackDistribution="space-between"
|
|
206
|
-
wrap="nowrap"
|
|
207
|
-
>
|
|
208
|
-
<NextLink
|
|
209
|
-
type="standalone"
|
|
210
|
-
overrides={{ stylePreset: 'linkStandaloneInverse' }}
|
|
211
|
-
href="/"
|
|
212
|
-
aria-label="Logo Link"
|
|
213
|
-
>
|
|
214
|
-
{!clientNavigationLogos[theme.name] ? (
|
|
215
|
-
<>
|
|
216
|
-
<Visible md lg xl>
|
|
217
|
-
<NewsKitLogoFull color="white" size="sizing120" />
|
|
218
|
-
</Visible>
|
|
219
|
-
<Visible xs sm>
|
|
220
|
-
<NewsKitLogoShort color="white" size="sizing120" />
|
|
221
|
-
</Visible>
|
|
222
|
-
</>
|
|
223
|
-
) : (
|
|
224
|
-
<Block>
|
|
225
|
-
<Image
|
|
226
|
-
alt=""
|
|
227
|
-
src={`/MyAccount/${
|
|
228
|
-
clientNavigationLogos[theme.name].src
|
|
229
|
-
}`}
|
|
230
|
-
overrides={{
|
|
231
|
-
width: clientNavigationLogos[theme.name].width,
|
|
232
|
-
}}
|
|
233
|
-
/>
|
|
234
|
-
</Block>
|
|
235
|
-
)}
|
|
236
|
-
</NextLink>
|
|
237
|
-
<Stack
|
|
238
|
-
flow="horizontal-center"
|
|
239
|
-
stackDistribution="flex-end"
|
|
240
|
-
wrap="nowrap"
|
|
241
|
-
>
|
|
242
|
-
<NavigationContainer
|
|
243
|
-
show={showMenu}
|
|
244
|
-
data-testid="NavigationContainer"
|
|
245
|
-
>
|
|
246
|
-
<Navigation data-testid="Navigation">
|
|
247
|
-
{showMenu && (
|
|
248
|
-
<Block
|
|
249
|
-
spaceStack="space050"
|
|
250
|
-
data-testid="mobile-block-space"
|
|
251
|
-
/>
|
|
252
|
-
)}
|
|
253
|
-
<NavButton
|
|
254
|
-
buttonText="Section One"
|
|
255
|
-
href="/section-one"
|
|
256
|
-
ariaLabel="Section One Link"
|
|
257
|
-
/>
|
|
258
|
-
<NavButton
|
|
259
|
-
buttonText="Section Two"
|
|
260
|
-
href="/section-two"
|
|
261
|
-
ariaLabel="Section Two Link"
|
|
262
|
-
/>
|
|
263
|
-
<NavButton
|
|
264
|
-
buttonText="Section Three"
|
|
265
|
-
href="/section-three"
|
|
266
|
-
ariaLabel="Section Three Link"
|
|
267
|
-
/>
|
|
268
|
-
{user && (
|
|
269
|
-
<IconNavLink
|
|
270
|
-
icon={<IconAccount data-testid="account-link" />}
|
|
271
|
-
href="/account"
|
|
272
|
-
text="Account"
|
|
273
|
-
spaceInline="space040"
|
|
274
|
-
typographyPreset="utilityMeta020"
|
|
275
|
-
stylePreset="linkStandaloneInverse"
|
|
276
|
-
/>
|
|
277
|
-
)}
|
|
278
|
-
</Navigation>
|
|
279
|
-
</NavigationContainer>
|
|
280
|
-
<Visible xs sm>
|
|
281
|
-
<Block spaceInline="space050">
|
|
282
|
-
<IconFilledMenu
|
|
283
|
-
overrides={{
|
|
284
|
-
size: 'iconSize030',
|
|
285
|
-
stylePreset: 'inkInverse',
|
|
286
|
-
}}
|
|
287
|
-
onClick={mobileMenuControl}
|
|
288
|
-
onKeyDown={handleEnterKeyPress(mobileMenuControl)}
|
|
289
|
-
role="button"
|
|
290
|
-
tabIndex={0}
|
|
291
|
-
aria-label="Mobile Menu Button"
|
|
292
|
-
data-testid="mobile-menu-btn"
|
|
293
|
-
/>
|
|
294
|
-
</Block>
|
|
295
|
-
</Visible>
|
|
296
|
-
<ThemeDropdown
|
|
297
|
-
theme={theme}
|
|
298
|
-
setTheme={setTheme}
|
|
299
|
-
options={{
|
|
300
|
-
LightTheme,
|
|
301
|
-
DarkTheme,
|
|
302
|
-
theSunTheme,
|
|
303
|
-
timesTheme,
|
|
304
|
-
virginRadioTheme,
|
|
305
|
-
}}
|
|
306
|
-
/>
|
|
307
|
-
</Stack>
|
|
308
|
-
</Stack>
|
|
309
|
-
</Cell>
|
|
310
|
-
</MainGrid>
|
|
311
|
-
</StyledHeader>
|
|
51
|
+
<NavigationPrimary
|
|
52
|
+
{...navigationPrimary}
|
|
53
|
+
customTheme={theme}
|
|
54
|
+
themeDropdownObject={themeDropdownObjectUpdated}
|
|
55
|
+
/>
|
|
312
56
|
<PastDueBannerExternal
|
|
313
57
|
pastDueBanner={pastDueBanner}
|
|
314
58
|
user={user}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const sectionLinks = [
|
|
2
|
+
{
|
|
3
|
+
text: 'Section One',
|
|
4
|
+
link: '/section-one',
|
|
5
|
+
icon: null,
|
|
6
|
+
ariaLabel: 'Section One Link',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
text: 'Section Two',
|
|
10
|
+
link: '/section-two',
|
|
11
|
+
icon: null,
|
|
12
|
+
ariaLabel: 'Section Two Link',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
text: 'Section Three',
|
|
16
|
+
link: '/section-three',
|
|
17
|
+
icon: null,
|
|
18
|
+
ariaLabel: 'Section Three Link',
|
|
19
|
+
},
|
|
20
|
+
]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { UserData } from '@newskit-render/my-account'
|
|
3
|
+
import { Cell } from 'newskit'
|
|
3
4
|
import Header from '../header'
|
|
4
5
|
import Footer from '../footer'
|
|
5
6
|
import { Gutter } from './Gutter'
|
|
@@ -14,7 +15,9 @@ const LayoutTemplate: React.FC<LayoutProps> = ({
|
|
|
14
15
|
<>
|
|
15
16
|
<Header user={user as UserData} />
|
|
16
17
|
<Gutter>
|
|
17
|
-
<MainGrid {...mainGridProps}>
|
|
18
|
+
<MainGrid {...mainGridProps}>
|
|
19
|
+
<Cell xs={12}>{children}</Cell>
|
|
20
|
+
</MainGrid>
|
|
18
21
|
</Gutter>
|
|
19
22
|
<Footer />
|
|
20
23
|
</>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getSanitizedConfig } from '../index'
|
|
2
|
+
|
|
3
|
+
const originalEnv = process.env
|
|
4
|
+
const { window } = global
|
|
5
|
+
|
|
6
|
+
beforeAll(() => {
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
delete global.window
|
|
9
|
+
})
|
|
10
|
+
afterAll(() => {
|
|
11
|
+
global.window = window
|
|
12
|
+
})
|
|
13
|
+
describe('getSanitizedConfig', () => {
|
|
14
|
+
it('should return required config values', () => {
|
|
15
|
+
process.env = {
|
|
16
|
+
...originalEnv,
|
|
17
|
+
SITE_HOST: 'host',
|
|
18
|
+
NEWSKIT_API_ENV_URL: 'url',
|
|
19
|
+
NEWSKIT_API_X_API_KEY: 'key',
|
|
20
|
+
OKTA_CLIENT_ID: 'oktaId',
|
|
21
|
+
OKTA_CLIENT_SECRET: 'oktasecret',
|
|
22
|
+
OKTA_DOMAIN: 'domain',
|
|
23
|
+
PUBLISHER: 'publisher',
|
|
24
|
+
}
|
|
25
|
+
const res = getSanitizedConfig()
|
|
26
|
+
|
|
27
|
+
expect(res).toEqual(
|
|
28
|
+
expect.objectContaining({
|
|
29
|
+
publisher: 'publisher',
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
process.env = {
|
|
33
|
+
...originalEnv,
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should throw error when NEWSKIT_API_ENV_URL is missing', () => {
|
|
38
|
+
process.env = {
|
|
39
|
+
...originalEnv,
|
|
40
|
+
SITE_HOST: 'host',
|
|
41
|
+
NEWSKIT_API_X_API_KEY: 'key',
|
|
42
|
+
OKTA_CLIENT_ID: 'oktaId',
|
|
43
|
+
OKTA_CLIENT_SECRET: 'oktasecret',
|
|
44
|
+
OKTA_DOMAIN: 'domain',
|
|
45
|
+
PUBLISHER: 'publisher',
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
getSanitizedConfig()
|
|
49
|
+
} catch (e) {
|
|
50
|
+
expect(e.message).toEqual('Missing key NEWSKIT_API_ENV_URL in env')
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const requiredConfigFields: string[] = [
|
|
2
|
+
'SITE_HOST',
|
|
3
|
+
'NEWSKIT_API_ENV_URL',
|
|
4
|
+
'NEWSKIT_API_X_API_KEY',
|
|
5
|
+
'OKTA_CLIENT_ID',
|
|
6
|
+
'OKTA_CLIENT_SECRET',
|
|
7
|
+
'OKTA_DOMAIN',
|
|
8
|
+
'PUBLISHER',
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
export const getSanitizedConfig = () => {
|
|
12
|
+
for (const key of requiredConfigFields) {
|
|
13
|
+
const value = process.env[key]
|
|
14
|
+
if (
|
|
15
|
+
typeof window == 'undefined' &&
|
|
16
|
+
(value === undefined || value.length <= 0)
|
|
17
|
+
) {
|
|
18
|
+
throw new Error(`Missing key ${key} in env`)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
oktaClientId: process.env.OKTA_CLIENT_ID as string,
|
|
23
|
+
oktaClientSecret: process.env.OKTA_CLIENT_SECRET as string,
|
|
24
|
+
oktaDomain: process.env.OKTA_DOMAIN as string,
|
|
25
|
+
newskitApiEnvUrl: process.env.NEWSKIT_API_ENV_URL as string,
|
|
26
|
+
newskitApiXApiKey: process.env.NEWSKIT_API_X_API_KEY as string,
|
|
27
|
+
optimizelysdkKey: process.env.OPTIMIZELY_SDK_KEY as string,
|
|
28
|
+
siteHost: process.env.SITE_HOST as string,
|
|
29
|
+
sitemapFirstPublicationDate: process.env
|
|
30
|
+
.SITEMAP_FIRST_PUBLICATION_DATE as string,
|
|
31
|
+
sitemapPublicationName: process.env.SITEMAP_PUBLICATION_NAME as string,
|
|
32
|
+
newRelicEnabled: process.env.NEW_RELIC_ENABLED as string,
|
|
33
|
+
experimentationWeb: process.env.EXPERIMENTATION_WEB as string,
|
|
34
|
+
sourcepointAccountId: process.env.SOURCEPOINT_ACCOUNT_ID as string,
|
|
35
|
+
sourcepointPropertyHref: process.env.SOURCEPOINT_PROPERTY_HREF as string,
|
|
36
|
+
tealiumAccountId: process.env.TEALIUM_ACCOUNT_ID as string,
|
|
37
|
+
tealiumProfileId: process.env.TEALIUM_PROFILE_ID as string,
|
|
38
|
+
tealiumEnv: process.env.TEALIUM_ENV as string,
|
|
39
|
+
twitterUsername: process.env.TWITTER_USERNAME as string,
|
|
40
|
+
gscId: process.env.GSC_ID as string,
|
|
41
|
+
publisher: process.env.PUBLISHER as string,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const config = getSanitizedConfig()
|
|
46
|
+
|
|
47
|
+
export const {
|
|
48
|
+
oktaClientId,
|
|
49
|
+
oktaClientSecret,
|
|
50
|
+
oktaDomain,
|
|
51
|
+
newskitApiEnvUrl,
|
|
52
|
+
newskitApiXApiKey,
|
|
53
|
+
optimizelysdkKey,
|
|
54
|
+
siteHost,
|
|
55
|
+
sitemapFirstPublicationDate,
|
|
56
|
+
sitemapPublicationName,
|
|
57
|
+
newRelicEnabled,
|
|
58
|
+
experimentationWeb,
|
|
59
|
+
sourcepointAccountId,
|
|
60
|
+
sourcepointPropertyHref,
|
|
61
|
+
tealiumAccountId,
|
|
62
|
+
tealiumProfileId,
|
|
63
|
+
tealiumEnv,
|
|
64
|
+
twitterUsername,
|
|
65
|
+
gscId,
|
|
66
|
+
publisher,
|
|
67
|
+
} = config
|
package/config/index.ts
CHANGED
|
@@ -1,85 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
OKTA_CLIENT_SECRET,
|
|
4
|
-
OKTA_DOMAIN,
|
|
5
|
-
NEWSKIT_API_ENV_URL,
|
|
6
|
-
NEWSKIT_API_X_API_KEY,
|
|
7
|
-
SITE_HOST,
|
|
8
|
-
OPTIMIZELY_SDK_KEY,
|
|
9
|
-
SITEMAP_FIRST_PUBLICATION_DATE,
|
|
10
|
-
SITEMAP_PUBLICATION_NAME,
|
|
11
|
-
NEW_RELIC_ENABLED,
|
|
12
|
-
EXPERIMENTATION_WEB,
|
|
13
|
-
SOURCEPOINT_ACCOUNT_ID,
|
|
14
|
-
SOURCEPOINT_PROPERTY_HREF,
|
|
15
|
-
TEALIUM_ACCOUNT_ID,
|
|
16
|
-
TEALIUM_PROFILE_ID,
|
|
17
|
-
TEALIUM_ENV,
|
|
18
|
-
TWITTER_USERNAME,
|
|
19
|
-
GSC_ID,
|
|
20
|
-
PUBLISHER,
|
|
21
|
-
} = process?.env
|
|
22
|
-
|
|
23
|
-
const requiredConfigFields: string[] = [
|
|
24
|
-
'SITE_HOST',
|
|
25
|
-
'NEWSKIT_API_ENV_URL',
|
|
26
|
-
'NEWSKIT_API_X_API_KEY',
|
|
27
|
-
'OKTA_CLIENT_ID',
|
|
28
|
-
'OKTA_CLIENT_SECRET',
|
|
29
|
-
'OKTA_DOMAIN',
|
|
30
|
-
'PUBLISHER',
|
|
31
|
-
]
|
|
32
|
-
const getSanitizedConfig = () => {
|
|
33
|
-
for (const key of requiredConfigFields) {
|
|
34
|
-
const value = process.env[key]
|
|
35
|
-
if (
|
|
36
|
-
typeof window == 'undefined' &&
|
|
37
|
-
(value === undefined || value.length <= 0)
|
|
38
|
-
) {
|
|
39
|
-
throw new Error(`Missing key ${key} in env`)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return {
|
|
43
|
-
oktaClientId: OKTA_CLIENT_ID,
|
|
44
|
-
oktaClientSecret: OKTA_CLIENT_SECRET,
|
|
45
|
-
oktaDomain: OKTA_DOMAIN,
|
|
46
|
-
newskitApiEnvUrl: NEWSKIT_API_ENV_URL,
|
|
47
|
-
newskitApiXApiKey: NEWSKIT_API_X_API_KEY,
|
|
48
|
-
optimizelysdkKey: OPTIMIZELY_SDK_KEY,
|
|
49
|
-
siteHost: SITE_HOST,
|
|
50
|
-
sitemapFirstPublicationDate: SITEMAP_FIRST_PUBLICATION_DATE,
|
|
51
|
-
sitemapPublicationName: SITEMAP_PUBLICATION_NAME,
|
|
52
|
-
newRelicEnabled: NEW_RELIC_ENABLED,
|
|
53
|
-
experimentationWeb: EXPERIMENTATION_WEB,
|
|
54
|
-
sourcepointAccountId: SOURCEPOINT_ACCOUNT_ID,
|
|
55
|
-
sourcepointPropertyHref: SOURCEPOINT_PROPERTY_HREF,
|
|
56
|
-
tealiumAccountId: TEALIUM_ACCOUNT_ID,
|
|
57
|
-
tealiumProfileId: TEALIUM_PROFILE_ID,
|
|
58
|
-
tealiumEnv: TEALIUM_ENV,
|
|
59
|
-
twitterUsername: TWITTER_USERNAME,
|
|
60
|
-
gscId: GSC_ID,
|
|
61
|
-
publisher: PUBLISHER,
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
const config = getSanitizedConfig()
|
|
65
|
-
export const {
|
|
66
|
-
oktaClientId,
|
|
67
|
-
oktaClientSecret,
|
|
68
|
-
oktaDomain,
|
|
69
|
-
newskitApiEnvUrl,
|
|
70
|
-
newskitApiXApiKey,
|
|
71
|
-
optimizelysdkKey,
|
|
72
|
-
siteHost,
|
|
73
|
-
sitemapFirstPublicationDate,
|
|
74
|
-
sitemapPublicationName,
|
|
75
|
-
newRelicEnabled,
|
|
76
|
-
experimentationWeb,
|
|
77
|
-
sourcepointAccountId,
|
|
78
|
-
sourcepointPropertyHref,
|
|
79
|
-
tealiumAccountId,
|
|
80
|
-
tealiumProfileId,
|
|
81
|
-
tealiumEnv,
|
|
82
|
-
twitterUsername,
|
|
83
|
-
gscId,
|
|
84
|
-
publisher,
|
|
85
|
-
} = config
|
|
1
|
+
export * from './environment'
|
|
2
|
+
export * from './multiTenancy'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { sharedTheme, timesTheme } from '@newskit-render/shared-components'
|
|
2
|
+
import { Publisher } from '@newskit-render/api'
|
|
3
|
+
import { demo } from '../theme/strings'
|
|
4
|
+
|
|
5
|
+
export const translationsMap = {
|
|
6
|
+
[Publisher.DEMO]: demo,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const themesMap = {
|
|
10
|
+
[Publisher.DEMO]: sharedTheme,
|
|
11
|
+
[Publisher.TIMES]: timesTheme,
|
|
12
|
+
}
|
|
@@ -2,12 +2,16 @@ import React from 'react'
|
|
|
2
2
|
import { render } from 'react-dom'
|
|
3
3
|
import { act } from 'react-dom/test-utils'
|
|
4
4
|
import { sharedTheme } from '@newskit-render/shared-components'
|
|
5
|
-
import { renderCustomDarkTheme } from '
|
|
6
|
-
import { AppContextProvider, AppContext } from '
|
|
5
|
+
import { renderCustomDarkTheme } from '../../theme'
|
|
6
|
+
import { AppContextProvider, AppContext } from '.'
|
|
7
7
|
|
|
8
8
|
const container = document.createElement('div')
|
|
9
9
|
document.body.appendChild(container)
|
|
10
10
|
|
|
11
|
+
jest.mock('../multi-tenancy', () => ({
|
|
12
|
+
useMultiTenancy: jest.fn().mockReturnValue({ tenant: 'DEMO' }),
|
|
13
|
+
}))
|
|
14
|
+
|
|
11
15
|
describe('AppContext tests', () => {
|
|
12
16
|
test('renders the content with the default theme', () => {
|
|
13
17
|
render(
|
|
@@ -32,7 +36,7 @@ describe('AppContext tests', () => {
|
|
|
32
36
|
type="button"
|
|
33
37
|
aria-label="btn"
|
|
34
38
|
data-testid="button"
|
|
35
|
-
onClick={(
|
|
39
|
+
onClick={(_) => setTheme(renderCustomDarkTheme)}
|
|
36
40
|
/>
|
|
37
41
|
{JSON.stringify(theme)}
|
|
38
42
|
</div>
|