@washingtonpost/subs-de-inputs 0.5.7 → 1.0.0-react18.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.
@@ -1,4 +1,4 @@
1
- import { getCookie, WPGeo, JSON_HEADERS, ResponseStatus, ENDPOINTS, isLoggedIn, listenToCookieStore } from '@washingtonpost/subs-sdk';
1
+ import { getCookie, WPGeo, JSON_HEADERS, ResponseStatus, ENDPOINTS } from '@washingtonpost/subs-sdk';
2
2
  import React, { useState, useEffect } from 'react';
3
3
  import { Icon, theme, Select, styled } from '@washingtonpost/wpds-ui-kit';
4
4
  import { useWindowSize, useScript, ScriptStatus } from '@washingtonpost/subs-hooks';
@@ -11,12 +11,6 @@ const CollectionBehaviors = {
11
11
  const AttributesState = {
12
12
  SUCCESS: '100'
13
13
  };
14
- const DeleteAttributeState = {
15
- SUCCESS: '150',
16
- SYSTEM_ERROR: '151',
17
- INVALID_ATTRIBUTE_NAME: '152',
18
- INVALID_ATTRIBUTE_NOT_EXISTS: '153'
19
- };
20
14
  const IngestType = {
21
15
  EXPLICIT: 'explicit',
22
16
  IMPLICIT: 'implicit'
@@ -47,10 +41,9 @@ const hasRequiredPrivacyCookies = () => {
47
41
 
48
42
  const base = `${ENDPOINTS.base}/de/v1`;
49
43
  const attributesCache = {};
50
- const getAttributes = async _ref => {
51
- let {
52
- fieldName
53
- } = _ref;
44
+ const getAttributes = async ({
45
+ fieldName
46
+ }) => {
54
47
  if (attributesCache[fieldName]) {
55
48
  return attributesCache[fieldName];
56
49
  }
@@ -75,58 +68,36 @@ const getAttributes = async _ref => {
75
68
  return [];
76
69
  }
77
70
  };
78
-
79
- const sendGAEvent = props => {
80
- if (typeof window === 'undefined') {
81
- if (process.env.NODE_ENV !== "production") console.warn('NO WINDOW');
82
- return;
83
- }
84
- // Initialize dataLayer if needed
85
- window.dataLayer = window.dataLayer || [];
86
- const eventData = {
87
- ...props
88
- };
89
- window.dataLayer.push(eventData);
90
- };
91
- const sendToGA = async _ref => {
92
- let {
93
- submitData: {
94
- fieldName,
95
- value
96
- },
97
- source
98
- } = _ref;
99
- sendGAEvent({
100
- event: 'site-onpage-click',
101
- action: 'site-onpage-click',
102
- category: 'profile',
103
- label: fieldName,
104
- 'de-label': fieldName,
105
- [fieldName]: value,
106
- section: 'profile',
107
- subsection: source
108
- });
109
- return true;
110
- };
111
-
112
- const base$1 = `${ENDPOINTS.base}/de/v1`;
113
- const ingest = async _ref => {
114
- let {
115
- submitData: {
116
- fieldName,
117
- value
118
- },
119
- source
120
- } = _ref;
121
- const url = `${base$1}/ingest`;
71
+ const ingest = async ({
72
+ submitData: {
73
+ fieldName,
74
+ value
75
+ },
76
+ type = IngestType.IMPLICIT,
77
+ source
78
+ }) => {
79
+ const url = `${base}/ingest`;
122
80
  const wapo_login_id = getCookie('wapo_login_id');
81
+ if (!hasRequiredPrivacyCookies()) {
82
+ throw new Error('does not satisfy cookie check');
83
+ }
84
+ let attributeInfo = attributesCache[fieldName];
85
+ if (!attributeInfo) {
86
+ attributeInfo = await getAttributes({
87
+ fieldName
88
+ });
89
+ }
90
+ if (attributeInfo[0] && attributeInfo[0].name === fieldName && attributeInfo[0].collection_behavior === CollectionBehaviors.DO_NOT_COLLECT) {
91
+ throw new Error('do not collect');
92
+ }
123
93
  const jucid = localStorage.getItem('uuid');
124
94
  const ga = getCookie('_ga');
125
95
  const payload = {
126
96
  jucid,
127
97
  ga,
128
- type: IngestType.EXPLICIT,
98
+ type,
129
99
  wapo_login_id,
100
+ // TODO: move this to BE to read from cookie headers
130
101
  data: {
131
102
  [fieldName]: [value]
132
103
  },
@@ -149,51 +120,6 @@ const ingest = async _ref => {
149
120
  }
150
121
  };
151
122
 
152
- const isAnonymousWebview = () => {
153
- if (typeof window === 'undefined') {
154
- return false;
155
- }
156
- const wp_wv = getCookie('wp_wv');
157
- return !!(wp_wv && !isLoggedIn());
158
- };
159
-
160
- const push = async _ref => {
161
- let {
162
- submitData,
163
- source
164
- } = _ref;
165
- if (!hasRequiredPrivacyCookies()) {
166
- throw new Error('does not satisfy cookie check');
167
- }
168
- if (isAnonymousWebview()) {
169
- throw new Error('does not satisfy cookie check');
170
- }
171
- const {
172
- fieldName
173
- } = submitData;
174
- const attributeInfo = await getAttributes({
175
- fieldName
176
- });
177
- if (attributeInfo[0] && attributeInfo[0].name === fieldName && attributeInfo[0].collection_behavior === CollectionBehaviors.DO_NOT_COLLECT) {
178
- throw new Error('do not collect');
179
- }
180
- const type = attributeInfo[0] && attributeInfo[0].explicit === true ? IngestType.EXPLICIT : IngestType.IMPLICIT;
181
- if (!attributeInfo[0] && process.env.NODE_ENV !== "production") {
182
- console.warn(`no attribute info found for ${fieldName}, assuming implicit`);
183
- }
184
- if (type === IngestType.EXPLICIT) {
185
- return ingest({
186
- submitData,
187
- source
188
- });
189
- } else {
190
- return sendToGA({
191
- submitData,
192
- source
193
- });
194
- }
195
- };
196
-
197
123
  const StyledMobileSelect = /*#__PURE__*/styled('select', {
198
124
  padding: '12px 16px 12px 6px',
199
125
  display: 'flex',
@@ -235,21 +161,20 @@ const StyledMobileOption = /*#__PURE__*/styled('option', {
235
161
  fontSize: 'inherit',
236
162
  color: 'inherit'
237
163
  });
238
- /**
239
- * Dropdown component. Uses wpds-ui-kit on desktop and native select on mobile.
240
- * @param {IDropdownProps} props The props.
241
- * @returns {React.ReactElement} The dropdown.
164
+ /**
165
+ * Dropdown component. Uses wpds-ui-kit on desktop and native select on mobile.
166
+ * @param {IDropdownProps} props The props.
167
+ * @returns {React.ReactElement} The dropdown.
242
168
  */
243
- const Dropdown = _ref => {
244
- let {
245
- id,
246
- label,
247
- values,
248
- required = false,
249
- defaultValue,
250
- onChange = () => {},
251
- disabled = false
252
- } = _ref;
169
+ const Dropdown = ({
170
+ id,
171
+ label,
172
+ values,
173
+ required = false,
174
+ defaultValue,
175
+ onChange = () => {},
176
+ disabled = false
177
+ }) => {
253
178
  const [answer, setAnswer] = useState();
254
179
  const {
255
180
  isMobileSize
@@ -280,7 +205,6 @@ const Dropdown = _ref => {
280
205
  id: "",
281
206
  required: required,
282
207
  onChange: e => setAnswer(e.target.value),
283
- placeholder: label,
284
208
  ...disabledProp
285
209
  }, React.createElement("label", null, label), React.createElement(StyledMobileOption, {
286
210
  value: "",
@@ -296,7 +220,7 @@ const Dropdown = _ref => {
296
220
  }, value))), React.createElement(Icon, {
297
221
  label: "",
298
222
  size: "100",
299
- fill: theme.colors['gray80'],
223
+ fill: theme.colors.gray80,
300
224
  style: {
301
225
  pointerEvents: 'none',
302
226
  position: 'absolute',
@@ -328,20 +252,19 @@ const Dropdown = _ref => {
328
252
  };
329
253
 
330
254
  const scriptSrc = `${ENDPOINTS.staticAssets === 'https://subscribe.washingtonpost.com/static' ? 'https://www.washingtonpost.com/subscribe/static/' : ENDPOINTS.staticAssets}/de-utils/twpdeu.min.js`;
331
- const DESelect = _ref => {
332
- let {
333
- source,
334
- fieldName,
335
- label,
336
- dataDictionaryConfig,
337
- defaultValue,
338
- disabled,
339
- submit,
340
- onChange = () => {},
341
- onFinished = () => {},
342
- valuesFilter = () => true,
343
- children
344
- } = _ref;
255
+ const DESelect = ({
256
+ source,
257
+ fieldName,
258
+ label,
259
+ dataDictionaryConfig,
260
+ defaultValue,
261
+ disabled,
262
+ submit,
263
+ onChange = () => {},
264
+ onFinished = () => {},
265
+ valuesFilter = () => true,
266
+ children
267
+ }) => {
345
268
  const [config, setConfig] = useState(dataDictionaryConfig);
346
269
  const [selected, setSelected] = useState('');
347
270
  const scriptStatus = useScript(scriptSrc);
@@ -369,14 +292,16 @@ const DESelect = _ref => {
369
292
  const submitSelected = async () => {
370
293
  try {
371
294
  var _window2;
295
+ // TODO: Log to GA?
372
296
  const result = await ((_window2 = window) === null || _window2 === void 0 || (_window2 = _window2.__twpdeu) === null || _window2 === void 0 ? void 0 : _window2.push({
373
297
  submitData: {
374
298
  fieldName,
375
299
  value: selected
376
300
  },
301
+ type: config !== null && config !== void 0 && config.explicit ? IngestType.EXPLICIT : IngestType.IMPLICIT,
377
302
  source
378
303
  }));
379
- const isError = result === true ? false : result ? result.status !== ResponseStatus.SUCCESS : true;
304
+ const isError = result ? result.status !== ResponseStatus.SUCCESS : true;
380
305
  onFinished({
381
306
  isFinished: true,
382
307
  isError
@@ -400,8 +325,7 @@ const DESelect = _ref => {
400
325
  disabled: true
401
326
  } : {};
402
327
  // sort and filter out archived values
403
- // Note: config.values may be readonly
404
- const values = config ? [...config.values].sort((a, b) => a.order - b.order).filter(value => value.archived !== true).filter(valuesFilter) : [];
328
+ const values = config ? config.values.sort((a, b) => a.order - b.order).filter(value => value.archived !== true).filter(valuesFilter) : [];
405
329
  return React.createElement(SelectWrapper, null, children && React.createElement(Select.Root, {
406
330
  onValueChange: e => {
407
331
  setSelected(e);
@@ -443,206 +367,5 @@ const SelectWrapper = /*#__PURE__*/styled('div', {
443
367
  }
444
368
  });
445
369
 
446
- const configSrc = `${ENDPOINTS.base === 'https://subscribe.washingtonpost.com' ? 'https://www.washingtonpost.com/subscribe' : ENDPOINTS.base}/config/de/disclosure.json`;
447
- const getConfig = async () => {
448
- let myConfig = undefined;
449
- // step 1: fetch config
450
- const response = await fetch(configSrc);
451
- const remoteConfig = await response.json();
452
- // step 2: figure out which part of the config to use
453
- // if country- or region-specific config found, use that
454
- const {
455
- country_code,
456
- intl_region
457
- } = WPGeo();
458
- Object.keys(remoteConfig).forEach(configKey => {
459
- if (country_code && configKey.split('|').includes(country_code.toLowerCase())) {
460
- myConfig = remoteConfig[configKey];
461
- } else if (intl_region === 'EEA' && configKey === 'eea') {
462
- myConfig = remoteConfig[configKey];
463
- }
464
- });
465
- // TODO: Check for billing country also
466
- // else if no country-specific config, use the default config
467
- if (typeof myConfig === 'undefined' && remoteConfig['_']) {
468
- myConfig = remoteConfig['_'];
469
- }
470
- return myConfig;
471
- };
472
-
473
- const hydrateLinks = str => {
474
- const array = str.split(/({{PRIVACY_POLICY}})/g);
475
- const chunks = array.map(str => {
476
- if (str === '{{PRIVACY_POLICY}}') {
477
- return React.createElement("a", {
478
- target: "_blank",
479
- style: {
480
- color: 'inherit'
481
- },
482
- className: "underline",
483
- href: "https://www.washingtonpost.com/privacy-policy/"
484
- }, "Privacy Policy");
485
- }
486
- return str;
487
- });
488
- const toReturn = chunks.reduce((prev, current) => React.createElement(React.Fragment, null, prev, current), React.createElement(React.Fragment, null));
489
- return toReturn;
490
- };
491
-
492
- const COOKIE = 'OptanonAlertBoxClosed';
493
- const checkCookie = () => {
494
- const value = getCookie(COOKIE) || '';
495
- // Wed May 15 2024 06:29:23 GMT-0500 (Central Daylight Time)
496
- // "Invalid date" is 12 characters long
497
- return value.length > 12;
498
- };
499
-
500
- const COOKIE$1 = 'OptanonAlertBoxClosed';
501
- const useOneTrustAlertBoxClosed = _ref => {
502
- let {
503
- allowCookieStore
504
- } = _ref;
505
- const [alertBoxClosed, setAlertBoxClosed] = useState();
506
- const [listenToCookieStore$1, setListenToCookieStore] = useState(false);
507
- const [listenToTcfApi, setListenToTcfApi] = useState(false);
508
- useEffect(() => {
509
- var _window;
510
- if (checkCookie()) {
511
- setAlertBoxClosed(true);
512
- return;
513
- }
514
- if (!window.__tcfapi) {
515
- console.warn('warning: __tcfapi not found');
516
- }
517
- if ((_window = window) !== null && _window !== void 0 && _window.cookieStore && allowCookieStore) {
518
- setListenToCookieStore(true);
519
- } else if (window.__tcfapi) {
520
- setListenToTcfApi(true);
521
- } else {
522
- console.warn('warning: neither cookieStore nor __tcfapi found');
523
- }
524
- }, []);
525
- useEffect(() => {
526
- let cleanupFn = () => {};
527
- if (listenToCookieStore$1 && window.cookieStore) {
528
- cleanupFn = listenToCookieStore(COOKIE$1, () => {
529
- if (checkCookie()) {
530
- setAlertBoxClosed(true);
531
- } else {
532
- setAlertBoxClosed(false);
533
- }
534
- });
535
- }
536
- return cleanupFn || (() => {});
537
- }, [listenToCookieStore$1]);
538
- useEffect(() => {
539
- let listenerId;
540
- if (listenToTcfApi && window.__tcfapi) {
541
- const callback = (_tcData, success) => {
542
- if (success) {
543
- listenerId = _tcData.listenerId;
544
- // tcData.eventStatus can be:
545
- // tcloaded means user has made a choice and we’re ready to check it
546
- // cmpuishown means the banner is shown
547
- // useractioncomplete means the user has interacted with the banner
548
- // but actually if the result for any of these is true, we just use the value of the cookie
549
- if (checkCookie()) {
550
- setAlertBoxClosed(true);
551
- }
552
- }
553
- };
554
- window.__tcfapi('addEventListener', 2, callback);
555
- }
556
- // cleanup fn
557
- return () => {
558
- if (window.__tcfapi && listenerId) window.__tcfapi('removeEventListener', 2, success => {
559
- console.debug(success);
560
- }, listenerId);
561
- };
562
- }, [listenToTcfApi]);
563
- return {
564
- alertBoxClosed,
565
- listenToCookieStore: listenToCookieStore$1,
566
- listenToTcfApi
567
- };
568
- };
569
-
570
- const DEDisclosure = _ref => {
571
- let {
572
- onFinished = () => {},
573
- allowCookieStore = true
574
- } = _ref;
575
- const [disclosure, setDisclosure] = useState(null);
576
- const [disclosureRendering, setDisclosureRendering] = useState(null);
577
- const [myConfig, setMyConfig] = useState();
578
- const {
579
- alertBoxClosed
580
- } = useOneTrustAlertBoxClosed({
581
- allowCookieStore
582
- });
583
- useEffect(() => {
584
- (async () => {
585
- const config = await getConfig();
586
- setMyConfig(config);
587
- if (!config) {
588
- console.error('No config found');
589
- }
590
- })();
591
- }, []);
592
- useEffect(() => {
593
- if (myConfig) {
594
- // step 3: set disclosure based on config
595
- // if config says to check onetrust, check onetrust
596
- if ('checkBannerStatus' in myConfig && myConfig.checkBannerStatus) {
597
- // check if onetrust is closed
598
- // if it is, show the after banner disclosure
599
- // if it is not, show the before banner disclosure
600
- if (alertBoxClosed) {
601
- setDisclosure(myConfig.disclosure_afterbanner);
602
- } else {
603
- setDisclosure(myConfig.disclosure_beforebanner);
604
- }
605
- } else if ('disclosure' in myConfig) {
606
- setDisclosure(myConfig.disclosure);
607
- } else {
608
- console.error('Invalid config');
609
- }
610
- }
611
- }, [myConfig, alertBoxClosed]);
612
- useEffect(() => {
613
- if (disclosure && Array.isArray(disclosure)) {
614
- setDisclosureRendering(disclosure.reduce((prev, current) => {
615
- return React.createElement(React.Fragment, null, prev, React.createElement("p", null, hydrateLinks(current)));
616
- }, React.createElement(React.Fragment, null)));
617
- // Is it ok to fire `onFinished` if still waiting for onetrust to load on the page?
618
- onFinished({
619
- isFinished: true,
620
- isError: false
621
- });
622
- }
623
- }, [disclosure]);
624
- return disclosure === null ? React.createElement("div", {
625
- "data-test-id": "de-disclosure-loading"
626
- }) : React.createElement("div", {
627
- "data-test-id": "de-disclosure"
628
- }, disclosureRendering);
629
- };
630
-
631
- const FirstPartyIngestDataTypes = {
632
- JOB_LEVEL: 'profile_job_level',
633
- JOB_INDUSTRY: 'profile_job_industry',
634
- JOB_TITLE: 'profile_job_title',
635
- PERSONAL_GOALS: 'personal_goals',
636
- HOBBIES: 'hobbies',
637
- PROFESSIONAL_GOALS: 'professional_goals',
638
- INDUSTRY: 'industry',
639
- NEWS_LOCATION: 'news_location',
640
- NY_PERSONAL_GOALS: 'new_year_personal_goals',
641
- NY_HOBBIES: 'new_year_hobbies',
642
- NY_PROFESSIONAL_GOALS: 'new_year_professional_goals',
643
- NY_INDUSTRY: 'new_year_industry',
644
- NY_NEWS_LOCATION: 'new_year_news_location'
645
- };
646
-
647
- export { AttributesState, CollectionBehaviors, DEDisclosure, DESelect, DeleteAttributeState, FirstPartyIngestDataTypes, IngestResponseState, IngestType, getAttributes, hasRequiredPrivacyCookies, push };
370
+ export { AttributesState, CollectionBehaviors, DESelect, IngestResponseState, IngestType, getAttributes, hasRequiredPrivacyCookies, ingest };
648
371
  //# sourceMappingURL=subs-de-inputs.esm.js.map