@times-components/ts-components 1.104.1-alpha.58 → 1.104.1-alpha.67

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/components/article-sidebar/ArticleSidebar.js +2 -2
  3. package/dist/components/article-sidebar/styles.js +3 -3
  4. package/dist/components/newsletter-puff/InlineNewsletterPuff.js +23 -25
  5. package/dist/components/newsletter-puff/NewsletterPuff.stories.js +2 -20
  6. package/dist/components/newsletter-puff/__tests__/AutoNewsletterPuff.test.js +2 -20
  7. package/dist/components/newsletter-puff/__tests__/InlineNewsletterPuff.test.js +24 -33
  8. package/dist/components/newsletter-puff/newsletter/Newsletter.d.ts +4 -5
  9. package/dist/components/newsletter-puff/newsletter/Newsletter.js +13 -10
  10. package/dist/components/recommended-articles/__tests__/formatters.test.js +4 -4
  11. package/dist/components/recommended-articles/formatters.d.ts +1 -0
  12. package/dist/components/recommended-articles/formatters.js +2 -2
  13. package/dist/components/social-embed/BlockedEmbedMessage.d.ts +8 -0
  14. package/dist/components/social-embed/BlockedEmbedMessage.js +32 -0
  15. package/dist/components/social-embed/SocialMediaEmbed.d.ts +10 -9
  16. package/dist/components/social-embed/SocialMediaEmbed.js +44 -42
  17. package/dist/components/social-embed/constants.d.ts +12 -0
  18. package/dist/components/social-embed/constants.js +13 -0
  19. package/dist/components/social-embed/helpers/__tests__/vendorConsent.test.d.ts +1 -0
  20. package/dist/components/social-embed/helpers/__tests__/vendorConsent.test.js +53 -0
  21. package/dist/components/social-embed/helpers/enableCookies.d.ts +1 -1
  22. package/dist/components/social-embed/helpers/enableCookies.js +3 -3
  23. package/dist/components/social-embed/helpers/privacyModal.d.ts +1 -4
  24. package/dist/components/social-embed/helpers/privacyModal.js +1 -6
  25. package/dist/components/social-embed/helpers/vendorConsent.d.ts +2 -1
  26. package/dist/components/social-embed/helpers/vendorConsent.js +3 -5
  27. package/dist/components/social-embed/types.d.ts +9 -0
  28. package/dist/components/social-embed/types.js +2 -0
  29. package/dist/fixtures/preview-data/recommended-articles.d.ts +3 -0
  30. package/dist/fixtures/preview-data/recommended-articles.js +11 -1
  31. package/package.json +4 -5
  32. package/rnw.js +1 -1
  33. package/src/components/article-sidebar/ArticleSidebar.tsx +1 -1
  34. package/src/components/article-sidebar/__tests__/__snapshots__/index.test.tsx.snap +5 -5
  35. package/src/components/article-sidebar/styles.ts +3 -3
  36. package/src/components/newsletter-puff/InlineNewsletterPuff.tsx +34 -46
  37. package/src/components/newsletter-puff/NewsletterPuff.stories.tsx +1 -22
  38. package/src/components/newsletter-puff/__tests__/AutoNewsletterPuff.test.tsx +1 -22
  39. package/src/components/newsletter-puff/__tests__/InlineNewsletterPuff.test.tsx +31 -40
  40. package/src/components/newsletter-puff/__tests__/__snapshots__/InlineNewsletterPuff.test.tsx.snap +18 -35
  41. package/src/components/newsletter-puff/newsletter/Newsletter.tsx +26 -26
  42. package/src/components/recommended-articles/__tests__/formatters.test.tsx +3 -5
  43. package/src/components/recommended-articles/formatters.ts +2 -1
  44. package/src/components/social-embed/BlockedEmbedMessage.tsx +67 -0
  45. package/src/components/social-embed/SocialMediaEmbed.tsx +71 -87
  46. package/src/components/social-embed/constants.ts +14 -0
  47. package/src/components/social-embed/helpers/__tests__/vendorConsent.test.ts +77 -0
  48. package/src/components/social-embed/helpers/enableCookies.ts +2 -5
  49. package/src/components/social-embed/helpers/privacyModal.ts +1 -5
  50. package/src/components/social-embed/helpers/vendorConsent.ts +5 -8
  51. package/src/components/social-embed/types.ts +13 -0
  52. package/src/fixtures/preview-data/recommended-articles.ts +19 -0
  53. package/src/types/externs.d.ts +1 -1
@@ -1,55 +1,74 @@
1
- import React, { useEffect, useState } from 'react';
2
- import {
3
- AllowButton,
4
- CardContainer,
5
- EnableButton,
6
- LinkPrivacyManager,
7
- Paragraph,
8
- Title,
9
- CustomIconContainer,
10
- Header
11
- } from './styles';
12
- import { InfoIcon } from '../inline-message/InfoIcon';
13
- import { openPrivacyModal, ModalType } from './helpers/privacyModal';
14
- // @ts-ignore
15
- import InteractiveWrapper from '@times-components/interactive-wrapper';
1
+ import React, { FC, useEffect, useState } from 'react';
2
+ import { BlockedEmbedMessage } from './BlockedEmbedMessage';
16
3
  import { checkVendorConsent } from './helpers/vendorConsent';
17
- import { getVendorTitle } from './helpers/getVendorTitle';
18
- import { enableCookies } from './helpers/enableCookies';
19
- import { socialMediaVendors } from './helpers/socialMediaVendors';
20
-
21
- export declare type SocialEmbedProps = {
22
- element: any;
23
- url?: string;
24
- vendorName: string;
25
- id: string;
26
- };
4
+ import { eventStatus } from './constants';
5
+ import { TcData, VendorName } from './types';
27
6
 
28
7
  declare global {
29
8
  interface Window {
30
9
  __tcfapi?: (
31
10
  command: string,
32
11
  version: number,
33
- callback: (data: any, success: boolean) => void
12
+ callback: (data: TcData, success: boolean) => void,
13
+ listenerId?: number
34
14
  ) => void;
35
15
  }
36
16
  }
37
17
 
38
- export const SocialMediaEmbed: React.FC<SocialEmbedProps> = ({
39
- element,
40
- vendorName
18
+ export type SocialMediaEmbedProps = {
19
+ element?: any;
20
+ url: string;
21
+ vendorName: VendorName;
22
+ id: string;
23
+ };
24
+
25
+ export const SocialMediaEmbed: FC<SocialMediaEmbedProps> = ({
26
+ vendorName,
27
+ url
41
28
  }) => {
42
- const [allowedOnce, setAllowedOnce] = useState(false);
43
- const [isSocialAllowed, setIsSocialAllowed] = useState(false);
29
+ const [isSocialEmbedAllowed, setIsSocialEmbedAllowed] = useState(false);
30
+ const [data, setData] = useState<TcData | null>(null);
44
31
 
45
- useEffect(
46
- () => {
47
- // tslint:disable-next-line:no-console
48
- console.log('useEffect enterred', element);
49
- checkVendorConsent(vendorName, setIsSocialAllowed);
50
- },
51
- [vendorName, allowedOnce, isSocialAllowed]
52
- );
32
+ useEffect(() => {
33
+ if (window.__tcfapi) {
34
+ window.__tcfapi('addEventListener', 2, (tcData, success) => {
35
+ if (success && tcData.eventStatus === eventStatus.tcLoaded) {
36
+ setData({
37
+ cmpStatus: tcData.cmpStatus,
38
+ eventStatus: tcData.eventStatus,
39
+ listenerId: tcData.listenerId
40
+ });
41
+
42
+ checkVendorConsent(vendorName, setIsSocialEmbedAllowed);
43
+ }
44
+ if (success && tcData.eventStatus === eventStatus.userActionComplete) {
45
+ setData({
46
+ cmpStatus: tcData.cmpStatus,
47
+ eventStatus: tcData.eventStatus,
48
+ listenerId: tcData.listenerId
49
+ });
50
+
51
+ checkVendorConsent(vendorName, setIsSocialEmbedAllowed);
52
+ }
53
+ });
54
+ }
55
+
56
+ return () => {
57
+ if (window.__tcfapi && data && data.listenerId) {
58
+ window.__tcfapi(
59
+ 'removeEventListener',
60
+ 2,
61
+ success => {
62
+ if (success) {
63
+ // tslint:disable-next-line:no-console
64
+ console.log(success);
65
+ }
66
+ },
67
+ data.listenerId
68
+ );
69
+ }
70
+ };
71
+ }, []);
53
72
 
54
73
  useEffect(
55
74
  () => {
@@ -63,62 +82,27 @@ export const SocialMediaEmbed: React.FC<SocialEmbedProps> = ({
63
82
  script.async = true;
64
83
  script.src = 'https://platform.twitter.com/widgets.js';
65
84
 
85
+ let link = document.createElement('a');
86
+ link.href = `${url}?ref_src=twsrc%5Etfw`;
87
+
66
88
  socialEmbedContainer.appendChild(script);
89
+ socialEmbedContainer.appendChild(link);
67
90
  }
68
91
  },
69
- [isSocialAllowed, allowedOnce]
92
+ [isSocialEmbedAllowed]
70
93
  );
71
94
 
72
- const allowCookiesOnce = () => {
73
- setAllowedOnce(true);
74
- setIsSocialAllowed(true);
75
- };
76
-
77
- const handlePrivacyManagerClick = (
78
- e: React.MouseEvent<HTMLAnchorElement>
79
- ) => {
80
- e.preventDefault();
81
- openPrivacyModal(
82
- ModalType.GDPR,
83
- window.__TIMES_CONFIG__.sourcepoint.gdprMessageId
84
- );
85
- };
86
- // tslint:disable-next-line:no-console
87
- console.log('allowedOnce', allowedOnce);
88
95
  // tslint:disable-next-line:no-console
89
- console.log('allowedOnce || isSocialAllowed', allowedOnce || isSocialAllowed);
96
+ console.log('tcData', data);
90
97
  // tslint:disable-next-line:no-console
91
- console.log('allowedOnce && isSocialAllowed', allowedOnce && isSocialAllowed);
98
+ console.log('isSocialEmbedAllowed', isSocialEmbedAllowed);
92
99
 
93
- return isSocialAllowed || allowedOnce ? (
94
- <div className="social-embed">
95
- <blockquote className="twitter-tweet">
96
- <a href={`${element.attributes.url}?ref_src=twsrc%5Etfw`} />
97
- </blockquote>
98
- </div>
100
+ return isSocialEmbedAllowed ? (
101
+ <blockquote className="social-embed" />
99
102
  ) : (
100
- <CardContainer>
101
- <Header>
102
- <CustomIconContainer>
103
- <InfoIcon />
104
- </CustomIconContainer>
105
- <Title>
106
- {getVendorTitle(vendorName, socialMediaVendors)} content blocked
107
- </Title>
108
- </Header>
109
- <Paragraph>
110
- Please enable cookies and other technologies to view this content. You
111
- can update your cookies preferences any time using{' '}
112
- <LinkPrivacyManager href="#" onClick={handlePrivacyManagerClick}>
113
- privacy manager.
114
- </LinkPrivacyManager>
115
- </Paragraph>
116
- <EnableButton
117
- onClick={() => enableCookies(vendorName, setIsSocialAllowed)}
118
- >
119
- Enable cookies
120
- </EnableButton>
121
- <AllowButton onClick={allowCookiesOnce}>Allow cookies once</AllowButton>
122
- </CardContainer>
103
+ <BlockedEmbedMessage
104
+ vendorName={vendorName}
105
+ setIsSocialEmbedAllowed={setIsSocialEmbedAllowed}
106
+ />
123
107
  );
124
108
  };
@@ -0,0 +1,14 @@
1
+ export const modalType = {
2
+ GDPR: 'gdpr',
3
+ CCPA: 'ccpa'
4
+ } as const;
5
+
6
+ export const eventStatus = {
7
+ tcLoaded: 'tcloaded',
8
+ cmpIsShown: 'cmpuishown',
9
+ userActionComplete: 'useractioncomplete'
10
+ } as const;
11
+
12
+ export const vendors = {
13
+ twitter: 'twitter'
14
+ } as const;
@@ -0,0 +1,77 @@
1
+ import { checkVendorConsent } from '../vendorConsent';
2
+ import { VendorName } from '../../types';
3
+
4
+ describe('checkVendorConsent', () => {
5
+ let setIsSocialEmbedAllowed: jest.Mock;
6
+
7
+ beforeEach(() => {
8
+ setIsSocialEmbedAllowed = jest.fn();
9
+ });
10
+
11
+ afterEach(() => {
12
+ jest.resetAllMocks();
13
+ });
14
+
15
+ it('calls setIsSocialEmbedAllowed with true if vendor consent is found', () => {
16
+ const mockVendorName: VendorName = 'twitter';
17
+ // Mock window.__tcfapi function
18
+ (window as any).__tcfapi = jest.fn((command, version, callback) => {
19
+ // tslint:disable-next-line:no-console
20
+ console.log('command, version', command, version);
21
+ callback(
22
+ { consentedVendors: [{ name: 'twitter' }] },
23
+ true // success
24
+ );
25
+ });
26
+
27
+ checkVendorConsent(mockVendorName, setIsSocialEmbedAllowed);
28
+
29
+ expect(setIsSocialEmbedAllowed).toHaveBeenCalledWith(true);
30
+ expect((window as any).__tcfapi).toHaveBeenCalledWith(
31
+ 'getCustomVendorConsents',
32
+ 2,
33
+ expect.any(Function)
34
+ );
35
+ });
36
+
37
+ it('calls setIsSocialEmbedAllowed with false if vendor consent is not found', () => {
38
+ const mockVendorName: VendorName = 'twitter';
39
+ (window as any).__tcfapi = jest.fn((command, version, callback) => {
40
+ // tslint:disable-next-line:no-console
41
+ console.log('command, version', command, version);
42
+ callback({ consentedVendors: [{ name: 'Other Vendor' }] }, true);
43
+ });
44
+
45
+ checkVendorConsent(mockVendorName, setIsSocialEmbedAllowed);
46
+
47
+ expect(setIsSocialEmbedAllowed).toHaveBeenCalledWith(false);
48
+ });
49
+
50
+ it('logs an error message if fetching consent data fails', () => {
51
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
52
+ const mockVendorName: VendorName = 'twitter';
53
+ (window as any).__tcfapi = jest.fn((command, version, callback) => {
54
+ // tslint:disable-next-line:no-console
55
+ console.log('command, version', command, version);
56
+ callback(null, false); // indicate failure
57
+ });
58
+
59
+ checkVendorConsent(mockVendorName, setIsSocialEmbedAllowed);
60
+
61
+ expect(setIsSocialEmbedAllowed).not.toHaveBeenCalled();
62
+ expect(consoleSpy).toHaveBeenCalledWith(
63
+ `Error fetching consent data or ${mockVendorName} embed not allowed`
64
+ );
65
+
66
+ consoleSpy.mockRestore();
67
+ });
68
+
69
+ it('does nothing if window.__tcfapi is undefined', () => {
70
+ delete (window as any).__tcfapi;
71
+ const mockVendorName: VendorName = 'twitter';
72
+
73
+ checkVendorConsent(mockVendorName, setIsSocialEmbedAllowed);
74
+
75
+ expect(setIsSocialEmbedAllowed).not.toHaveBeenCalled();
76
+ });
77
+ });
@@ -1,12 +1,9 @@
1
1
  import { socialMediaVendors } from './socialMediaVendors';
2
2
 
3
- export const enableCookies = (
4
- vendorName: string,
5
- setIsSocialAllowed: (allowed: boolean) => void
6
- ) => {
3
+ export const enableCookies = (vendorName: string) => {
7
4
  const onCustomConsent = (_: any, success: boolean) => {
8
5
  if (success) {
9
- setIsSocialAllowed(true);
6
+ return true;
10
7
  }
11
8
  return null;
12
9
  };
@@ -1,9 +1,5 @@
1
1
  import get from 'lodash.get';
2
-
3
- export enum ModalType {
4
- GDPR = 'gdpr',
5
- CCPA = 'ccpa'
6
- }
2
+ import { ModalType } from '../types';
7
3
 
8
4
  export const openPrivacyModal = (type: ModalType, messageId: string) => {
9
5
  const loadModal = get(window, `_sp_.${type}.loadPrivacyManagerModal`);
@@ -1,6 +1,8 @@
1
+ import { VendorName } from '../types';
2
+
1
3
  export const checkVendorConsent = (
2
- vendorName: string,
3
- setIsSocialAllowed: (allowed: boolean) => void
4
+ vendorName: VendorName,
5
+ setIsSocialEmbedAllowed: (isSocialVendorAllowed: boolean) => void
4
6
  ) => {
5
7
  if (window.__tcfapi) {
6
8
  window.__tcfapi(
@@ -12,12 +14,7 @@ export const checkVendorConsent = (
12
14
  (vendor: { name: string }) =>
13
15
  vendor.name.toLowerCase() === vendorName.toLowerCase()
14
16
  );
15
- setIsSocialAllowed(isSocialVendorAllowed);
16
- // tslint:disable-next-line:no-console
17
- console.log(
18
- `Consent check for ${vendorName}:`,
19
- isSocialVendorAllowed
20
- );
17
+ setIsSocialEmbedAllowed(isSocialVendorAllowed);
21
18
  } else {
22
19
  // tslint:disable-next-line:no-console
23
20
  console.log(
@@ -0,0 +1,13 @@
1
+ import { eventStatus, vendors, modalType } from './constants';
2
+
3
+ export type EventStatus = typeof eventStatus[keyof typeof eventStatus];
4
+
5
+ export type TcData = {
6
+ cmpStatus: string;
7
+ eventStatus: EventStatus;
8
+ listenerId: number;
9
+ };
10
+
11
+ export type VendorName = typeof vendors[keyof typeof vendors];
12
+
13
+ export type ModalType = typeof modalType[keyof typeof modalType];
@@ -4,6 +4,8 @@ export default {
4
4
  {
5
5
  url:
6
6
  'https://www.thetimes.co.uk/article/save-or-splurge-what-experts-spend-their-own-money-on-tdd65qlj6',
7
+ categoryPath:
8
+ 'l1/l2/article/save-or-splurge-what-experts-spend-their-own-money-on-tdd65qlj6',
7
9
  slug: 'save-or-splurge-what-experts-spend-their-own-money-on',
8
10
  label: null,
9
11
  headline: 'Save or splurge: what experts spend their own money on',
@@ -51,6 +53,8 @@ export default {
51
53
  {
52
54
  url:
53
55
  'https://www.thetimes.co.uk/article/lieutenant-colonel-ian-crooke-obituary-t0b890wgp',
56
+ categoryPath:
57
+ 'l1/l2/article/lieutenant-colonel-ian-crooke-obituary-t0b890wgp',
54
58
  slug: 'lieutenant-colonel-ian-crooke-obituary',
55
59
  label: 'Obituary',
56
60
  headline: 'Lieutenant Colonel Ian Crooke',
@@ -78,6 +82,8 @@ export default {
78
82
  {
79
83
  url:
80
84
  'https://www.thetimes.co.uk/article/is-the-party-over-for-boris-johnson-k8s0jxv6r',
85
+ categoryPath:
86
+ 'l1/l2/article/is-the-party-over-for-boris-johnson-k8s0jxv6r',
81
87
  slug: 'is-the-party-over-for-boris-johnson',
82
88
  label: 'RED BOX | PATRICK MAGUIRE',
83
89
  headline: 'Is the party over for Johnson?',
@@ -118,6 +124,8 @@ export default {
118
124
  {
119
125
  url:
120
126
  'https://www.thetimes.co.uk/article/quentin-letts-a-pm-his-party-and-the-country-left-firmly-in-limp-on-territory-klvfgrj8g',
127
+ categoryPath:
128
+ 'l1/l2/article/quentin-letts-a-pm-his-party-and-the-country-left-firmly-in-limp-on-territory-klvfgrj8g',
121
129
  slug:
122
130
  'quentin-letts-a-pm-his-party-and-the-country-left-firmly-in-limp-on-territory',
123
131
  label: 'Political Sketch',
@@ -152,6 +160,8 @@ export default {
152
160
  {
153
161
  url:
154
162
  'https://www.thetimes.co.uk/article/boris-johnson-should-look-for-an-honourable-exit-7fkgkl2rq',
163
+ categoryPath:
164
+ 'l1/l2/article/boris-johnson-should-look-for-an-honourable-exit-7fkgkl2rq',
155
165
  slug: 'boris-johnson-should-look-for-an-honourable-exit',
156
166
  label: 'WILLIAM HAGUE | COMMENT',
157
167
  headline: 'Hague: Johnson should look for an honourable exit',
@@ -184,6 +194,8 @@ export default {
184
194
  {
185
195
  url:
186
196
  'https://www.thetimes.co.uk/article/carrie-johnson-and-hard-times-at-no-10-hfmkr8ssn',
197
+ categoryPath:
198
+ 'l1/l2/article/carrie-johnson-and-hard-times-at-no-10-hfmkr8ssn',
187
199
  slug: 'carrie-johnson-and-hard-times-at-no-10',
188
200
  label: 'POLITICS',
189
201
  headline: 'Carrie Johnson and hard times at No 10',
@@ -216,6 +228,8 @@ export default {
216
228
  {
217
229
  url:
218
230
  'https://www.thetimes.co.uk/article/dead-man-walking-times-commentators-give-their-verdicts-boris-johnson-confidence-vote-0f59md3bt',
231
+ categoryPath:
232
+ 'l1/l2/article/dead-man-walking-times-commentators-give-their-verdicts-boris-johnson-confidence-vote-0f59md3bt',
219
233
  slug:
220
234
  'dead-man-walking-times-commentators-give-their-verdicts-boris-johnson-confidence-vote',
221
235
  label: 'Politics',
@@ -269,6 +283,7 @@ export default {
269
283
  {
270
284
  url:
271
285
  'https://www.thetimes.co.uk/article/margot-heuman-obituary-9fdm8h3hr',
286
+ categoryPath: 'l1/l2/article/margot-heuman-obituary-9fdm8h3hr',
272
287
  slug: 'margot-heuman-obituary',
273
288
  label: 'Obituary',
274
289
  headline: 'Margot Heuman',
@@ -296,6 +311,8 @@ export default {
296
311
  {
297
312
  url:
298
313
  'https://www.thetimes.co.uk/article/the-times-view-on-tory-confidence-in-boris-johnson-pyrrhic-victory-wl5fl2jb2',
314
+ categoryPath:
315
+ 'l1/l2/article/the-times-view-on-tory-confidence-in-boris-johnson-pyrrhic-victory-wl5fl2jb2',
299
316
  slug:
300
317
  'the-times-view-on-tory-confidence-in-boris-johnson-pyrrhic-victory',
301
318
  label: 'LEADING ARTICLE',
@@ -329,6 +346,8 @@ export default {
329
346
  {
330
347
  url:
331
348
  'https://www.thetimes.co.uk/article/boris-johnson-allies-took-time-saving-big-dog-leadership-contest-rn5dzbw8h',
349
+ categoryPath:
350
+ 'l1/l2/article/boris-johnson-allies-took-time-saving-big-dog-leadership-contest-rn5dzbw8h',
332
351
  slug:
333
352
  'boris-johnson-allies-took-time-saving-big-dog-leadership-contest',
334
353
  label: 'Politics',
@@ -24,6 +24,7 @@ declare module '@times-components/ts-slices' {
24
24
  export type SliceArticle = {
25
25
  id?: string;
26
26
  url?: string;
27
+ categoryPath?: string;
27
28
  label?: string;
28
29
  byline?: string;
29
30
  headline: string;
@@ -194,7 +195,6 @@ declare module '@times-components/provider' {
194
195
  declare module '@times-components/provider-queries' {
195
196
  import { DocumentNode } from 'graphql';
196
197
  export const getNewsletter: DocumentNode;
197
- export const subscribeNewsletter: DocumentNode;
198
198
  }
199
199
 
200
200
  declare module '@times-components/provider-test-tools' {