@washingtonpost/subs-de-inputs 1.10.2 → 1.11.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.
- package/dist/components/DEDisclosure/DisclosureWithBannerStatus.d.ts +2 -3
- package/dist/components/DEDisclosure/DisclosureWithoutBannerStatus.d.ts +2 -3
- package/dist/components/DEDisclosure/hooks/useOnetrust.d.ts +1 -1
- package/dist/components/DEDisclosure/index.d.ts +1 -2
- package/dist/components/DEDisclosure/utils/getConfig.d.ts +1 -1
- package/dist/components/DEDisclosure/utils/hydrateLinks.d.ts +1 -1
- package/dist/components/DESelect/Dropdown.d.ts +1 -1
- package/dist/components/DESelect/index.d.ts +2 -2
- package/dist/index.cjs +702 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +664 -7
- package/dist/index.js.map +1 -0
- package/dist/interfaces/index.d.ts +1 -1
- package/dist/interfaces/twpdeu.d.ts +2 -2
- package/dist/services/getAttributes.d.ts +1 -1
- package/dist/services/ingest.d.ts +2 -2
- package/dist/utils/push.d.ts +2 -2
- package/package.json +13 -17
- package/dist/subs-de-inputs.cjs.development.js +0 -783
- package/dist/subs-de-inputs.cjs.development.js.map +0 -1
- package/dist/subs-de-inputs.cjs.production.min.js +0 -2
- package/dist/subs-de-inputs.cjs.production.min.js.map +0 -1
- package/dist/subs-de-inputs.esm.js +0 -769
- package/dist/subs-de-inputs.esm.js.map +0 -1
|
@@ -1,769 +0,0 @@
|
|
|
1
|
-
import { getCookie, WPGeo, DEFAULT_HEADERS, ResponseStatus, ENDPOINTS, isLoggedIn, listenToCookieStore } from '@washingtonpost/subs-sdk';
|
|
2
|
-
import React, { useState, useEffect } from 'react';
|
|
3
|
-
import { Icon, theme, Select, styled } from '@washingtonpost/wpds-ui-kit';
|
|
4
|
-
import { useWindowSize, useScript, ScriptStatus } from '@washingtonpost/subs-hooks';
|
|
5
|
-
import { ChevronDown } from '@washingtonpost/wpds-assets';
|
|
6
|
-
|
|
7
|
-
const CollectionBehaviors = {
|
|
8
|
-
COLLECT: 'COLLECT',
|
|
9
|
-
DO_NOT_COLLECT: 'DO_NOT_COLLECT'
|
|
10
|
-
};
|
|
11
|
-
const AttributesState = {
|
|
12
|
-
SUCCESS: '100'
|
|
13
|
-
};
|
|
14
|
-
// https://github.com/WashPost/subs-be-user-data-enrichment/blob/8e8f8460c59adbe9c83b50f368bff1d3300bfd6b/src/main/java/washpost/paywall/userdataenrichment/model/ResponseState.java#L19
|
|
15
|
-
const DeleteAttributeState = {
|
|
16
|
-
SUCCESS: '150',
|
|
17
|
-
SYSTEM_ERROR: '151',
|
|
18
|
-
INVALID_ATTRIBUTE_NAME: '152',
|
|
19
|
-
INVALID_ATTRIBUTE_NOT_EXISTS: '153'
|
|
20
|
-
};
|
|
21
|
-
const IngestType = {
|
|
22
|
-
EXPLICIT: 'explicit',
|
|
23
|
-
IMPLICIT: 'implicit'
|
|
24
|
-
};
|
|
25
|
-
const IngestResponseState = {
|
|
26
|
-
SUCCESS: '100',
|
|
27
|
-
SYSTEM_ERROR: '101',
|
|
28
|
-
INVALID_TYPE: '102',
|
|
29
|
-
INVALID_IDENTIFIER: '103',
|
|
30
|
-
INVALID_DATA: '104',
|
|
31
|
-
INVALID_ATTRIBUTE_DEFINITION: '105',
|
|
32
|
-
INVALID_META_DEFINITION: '106',
|
|
33
|
-
UNAUTHENTICATED: '107',
|
|
34
|
-
MISMATCHED_IDENTIFIER: '108',
|
|
35
|
-
DISABLED_ATTRIBUTE_DEFINITION: '109',
|
|
36
|
-
DO_NOT_COLLECT: '110'
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const COOKIE$2 = 'OptanonConsent';
|
|
40
|
-
/**
|
|
41
|
-
* Checks the users OptanonConsent cookie to determine if the user has allowed targeting.
|
|
42
|
-
* Returns true or false if the flag is found in the cookie, null otherwise.
|
|
43
|
-
* @returns {boolean | null}
|
|
44
|
-
*/
|
|
45
|
-
const checkConsentCookieForAllowTargeting = () => {
|
|
46
|
-
const value = getCookie(COOKIE$2) || '';
|
|
47
|
-
return value.includes('C0004%3A1') ? true : value.includes('C0004%3A0') ? false : null;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const COOKIE$1 = 'OptanonAlertBoxClosed';
|
|
51
|
-
const checkAlertBoxClosedCookie = () => {
|
|
52
|
-
const value = getCookie(COOKIE$1) || '';
|
|
53
|
-
// Wed May 15 2024 06:29:23 GMT-0500 (Central Daylight Time)
|
|
54
|
-
// "Invalid date" is 12 characters long
|
|
55
|
-
return value.length > 12;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Checks privacy cookies to decide if we can send up 1pd
|
|
60
|
-
* If US, checks that wp_usp exists
|
|
61
|
-
* Else If OptAnonConsent cookie exists, checks the value of targeting cookies consent
|
|
62
|
-
* Else If EEA, always returns true
|
|
63
|
-
* Else, returns false
|
|
64
|
-
* @returns {boolean}
|
|
65
|
-
*/
|
|
66
|
-
const hasRequiredPrivacyCookies = () => {
|
|
67
|
-
if (typeof window === 'undefined') {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
const {
|
|
71
|
-
intl_region,
|
|
72
|
-
country_code: countryCode
|
|
73
|
-
} = WPGeo();
|
|
74
|
-
if (countryCode === 'US') {
|
|
75
|
-
return !!getCookie('wp_usp');
|
|
76
|
-
}
|
|
77
|
-
if (window.pageType === 'onboarding') {
|
|
78
|
-
const gdprAllowTargarting = checkConsentCookieForAllowTargeting();
|
|
79
|
-
if (typeof gdprAllowTargarting === 'boolean' && checkAlertBoxClosedCookie()) {
|
|
80
|
-
return gdprAllowTargarting;
|
|
81
|
-
}
|
|
82
|
-
// Downstream systems
|
|
83
|
-
// Checking for window.pageType === 'onboarding' to avoid being true on graphics articles etc for now -- MNI-710
|
|
84
|
-
if (intl_region === 'EEA' && window.pageType === 'onboarding') {
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return false;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const base$1 = `${ENDPOINTS.base}/de/v1`;
|
|
92
|
-
const attributesCache = {};
|
|
93
|
-
const getAttributes = async ({
|
|
94
|
-
fieldName
|
|
95
|
-
}) => {
|
|
96
|
-
if (attributesCache[fieldName]) {
|
|
97
|
-
return attributesCache[fieldName];
|
|
98
|
-
}
|
|
99
|
-
const fieldNames = [fieldName];
|
|
100
|
-
try {
|
|
101
|
-
const url = new URL(`${base$1}/attributes`);
|
|
102
|
-
url.searchParams.set('attributes', fieldNames.join(','));
|
|
103
|
-
const data = await fetch(url.toString(), {
|
|
104
|
-
credentials: 'include',
|
|
105
|
-
headers: DEFAULT_HEADERS
|
|
106
|
-
});
|
|
107
|
-
const json = await data.json();
|
|
108
|
-
if (data.ok && json.status === ResponseStatus.SUCCESS) {
|
|
109
|
-
const attributes = json.attributes || [];
|
|
110
|
-
attributesCache[fieldName] = attributes;
|
|
111
|
-
return attributes;
|
|
112
|
-
}
|
|
113
|
-
return [];
|
|
114
|
-
} catch (e) {
|
|
115
|
-
console.debug(e);
|
|
116
|
-
return [];
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const sendGAEvent = props => {
|
|
121
|
-
if (typeof window === 'undefined') {
|
|
122
|
-
if (process.env.NODE_ENV !== "production") console.warn('NO WINDOW');
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
// Initialize dataLayer if needed
|
|
126
|
-
window.dataLayer = window.dataLayer || [];
|
|
127
|
-
const eventData = {
|
|
128
|
-
...props
|
|
129
|
-
};
|
|
130
|
-
window.dataLayer.push(eventData);
|
|
131
|
-
};
|
|
132
|
-
const sendToGA = async ({
|
|
133
|
-
submitData: {
|
|
134
|
-
fieldName,
|
|
135
|
-
value
|
|
136
|
-
},
|
|
137
|
-
source
|
|
138
|
-
}) => {
|
|
139
|
-
sendGAEvent({
|
|
140
|
-
event: 'site-onpage-click',
|
|
141
|
-
action: 'site-onpage-click',
|
|
142
|
-
category: 'profile',
|
|
143
|
-
label: fieldName,
|
|
144
|
-
'de-label': fieldName,
|
|
145
|
-
[fieldName]: value,
|
|
146
|
-
section: 'profile',
|
|
147
|
-
subsection: source
|
|
148
|
-
});
|
|
149
|
-
return true;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const base = `${ENDPOINTS.base}/de/v1`;
|
|
153
|
-
const ingest = async ({
|
|
154
|
-
submitData: {
|
|
155
|
-
fieldName,
|
|
156
|
-
value
|
|
157
|
-
},
|
|
158
|
-
source
|
|
159
|
-
}) => {
|
|
160
|
-
const url = `${base}/ingest`;
|
|
161
|
-
const wapo_login_id = getCookie('wapo_login_id');
|
|
162
|
-
const jucid = localStorage.getItem('uuid');
|
|
163
|
-
const ga = getCookie('_ga');
|
|
164
|
-
const payload = {
|
|
165
|
-
jucid,
|
|
166
|
-
ga,
|
|
167
|
-
type: IngestType.EXPLICIT,
|
|
168
|
-
wapo_login_id,
|
|
169
|
-
// TODO: move this to BE to read from cookie headers
|
|
170
|
-
data: {
|
|
171
|
-
[fieldName]: [value]
|
|
172
|
-
},
|
|
173
|
-
metadata: {
|
|
174
|
-
source
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
try {
|
|
178
|
-
const response = await fetch(url, {
|
|
179
|
-
method: 'POST',
|
|
180
|
-
credentials: 'include',
|
|
181
|
-
headers: DEFAULT_HEADERS,
|
|
182
|
-
body: JSON.stringify(payload)
|
|
183
|
-
});
|
|
184
|
-
const json = await response.json();
|
|
185
|
-
return json;
|
|
186
|
-
} catch (e) {
|
|
187
|
-
console.debug(e);
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const isAnonymousWebview = () => {
|
|
193
|
-
if (typeof window === 'undefined') {
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
196
|
-
const wp_wv = getCookie('wp_wv');
|
|
197
|
-
return !!(wp_wv && !isLoggedIn());
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
const push = async ({
|
|
201
|
-
submitData,
|
|
202
|
-
source
|
|
203
|
-
}) => {
|
|
204
|
-
if (!hasRequiredPrivacyCookies()) {
|
|
205
|
-
throw new Error('does not satisfy cookie check');
|
|
206
|
-
}
|
|
207
|
-
if (isAnonymousWebview()) {
|
|
208
|
-
throw new Error('does not satisfy cookie check');
|
|
209
|
-
}
|
|
210
|
-
const {
|
|
211
|
-
fieldName
|
|
212
|
-
} = submitData;
|
|
213
|
-
const attributeInfo = await getAttributes({
|
|
214
|
-
fieldName
|
|
215
|
-
});
|
|
216
|
-
if (attributeInfo[0] && attributeInfo[0].name === fieldName && attributeInfo[0].collection_behavior === CollectionBehaviors.DO_NOT_COLLECT) {
|
|
217
|
-
throw new Error('do not collect');
|
|
218
|
-
}
|
|
219
|
-
const type = attributeInfo[0] && attributeInfo[0].explicit === true ? IngestType.EXPLICIT : IngestType.IMPLICIT;
|
|
220
|
-
if (!attributeInfo[0] && process.env.NODE_ENV !== "production") {
|
|
221
|
-
console.warn(`no attribute info found for ${fieldName}, assuming implicit`);
|
|
222
|
-
}
|
|
223
|
-
if (type === IngestType.EXPLICIT) {
|
|
224
|
-
return ingest({
|
|
225
|
-
submitData,
|
|
226
|
-
source
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
return sendToGA({
|
|
230
|
-
submitData,
|
|
231
|
-
source
|
|
232
|
-
});
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
const StyledMobileSelect = /*#__PURE__*/styled('select', {
|
|
236
|
-
padding: '12px 16px 12px 6px',
|
|
237
|
-
display: 'flex',
|
|
238
|
-
justifyContent: 'space-between',
|
|
239
|
-
width: '100%',
|
|
240
|
-
backgroundColor: '$secondary',
|
|
241
|
-
color: '$primary',
|
|
242
|
-
fontFamily: '$meta',
|
|
243
|
-
fontSize: '$100',
|
|
244
|
-
fontWeight: '$light',
|
|
245
|
-
lineHeight: '$125',
|
|
246
|
-
paddingBlockRight: '$125',
|
|
247
|
-
textOverflow: 'ellipsis',
|
|
248
|
-
position: 'relative',
|
|
249
|
-
borderColor: 'transparent',
|
|
250
|
-
borderRightWidth: '10px',
|
|
251
|
-
borderRightColor: 'transparent',
|
|
252
|
-
appearance: 'none',
|
|
253
|
-
'-webkit-appearance': 'none',
|
|
254
|
-
'&:disabled': {
|
|
255
|
-
backgroundColor: theme.colors.disabled,
|
|
256
|
-
borderColor: theme.colors.disabled,
|
|
257
|
-
color: theme.colors.onDisabled,
|
|
258
|
-
cursor: 'not-allowed'
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
const StyledSelectWrapper = /*#__PURE__*/styled('div', {
|
|
262
|
-
width: '100%',
|
|
263
|
-
maxWidth: '380px',
|
|
264
|
-
borderRadius: '$012',
|
|
265
|
-
borderColor: '$subtle',
|
|
266
|
-
borderStyle: 'solid',
|
|
267
|
-
borderWidth: '1px',
|
|
268
|
-
backgroundColor: '$secondary',
|
|
269
|
-
position: 'relative'
|
|
270
|
-
});
|
|
271
|
-
const StyledMobileOption = /*#__PURE__*/styled('option', {
|
|
272
|
-
fontFamily: 'inherit',
|
|
273
|
-
fontSize: 'inherit',
|
|
274
|
-
color: 'inherit'
|
|
275
|
-
});
|
|
276
|
-
/**
|
|
277
|
-
* Dropdown component. Uses wpds-ui-kit on desktop and native select on mobile.
|
|
278
|
-
* @param {IDropdownProps} props The props.
|
|
279
|
-
* @returns {React.ReactElement} The dropdown.
|
|
280
|
-
*/
|
|
281
|
-
const Dropdown = ({
|
|
282
|
-
id,
|
|
283
|
-
label,
|
|
284
|
-
values,
|
|
285
|
-
required = false,
|
|
286
|
-
existingValue,
|
|
287
|
-
onChange = () => {},
|
|
288
|
-
disabled = false,
|
|
289
|
-
valueSelectedByDefault
|
|
290
|
-
}) => {
|
|
291
|
-
const [answer, setAnswer] = useState();
|
|
292
|
-
const {
|
|
293
|
-
isMobileSize
|
|
294
|
-
} = useWindowSize();
|
|
295
|
-
useEffect(() => {
|
|
296
|
-
if (answer) onChange(answer);
|
|
297
|
-
}, [answer]);
|
|
298
|
-
const disabledProp = disabled ? {
|
|
299
|
-
disabled: true
|
|
300
|
-
} : {};
|
|
301
|
-
const presetDropdownValue = existingValue || valueSelectedByDefault;
|
|
302
|
-
// helps maintain state between WPDS and native dropdowns
|
|
303
|
-
const defaultValueProp = answer ? {
|
|
304
|
-
defaultValue: answer
|
|
305
|
-
} : presetDropdownValue ? {
|
|
306
|
-
defaultValue: presetDropdownValue
|
|
307
|
-
} : {};
|
|
308
|
-
const defaultValuePropMobile = value => {
|
|
309
|
-
if (answer) {
|
|
310
|
-
return value === answer ? {
|
|
311
|
-
selected: true
|
|
312
|
-
} : {};
|
|
313
|
-
}
|
|
314
|
-
return value === presetDropdownValue ? {
|
|
315
|
-
selected: true
|
|
316
|
-
} : {};
|
|
317
|
-
};
|
|
318
|
-
return isMobileSize ? React.createElement(StyledSelectWrapper, null, React.createElement(StyledMobileSelect, {
|
|
319
|
-
id: "",
|
|
320
|
-
required: required,
|
|
321
|
-
onChange: e => setAnswer(e.target.value),
|
|
322
|
-
...disabledProp
|
|
323
|
-
}, React.createElement("label", null, label), React.createElement(StyledMobileOption, {
|
|
324
|
-
value: "",
|
|
325
|
-
disabled: true,
|
|
326
|
-
selected: true,
|
|
327
|
-
style: {
|
|
328
|
-
color: '#666666'
|
|
329
|
-
}
|
|
330
|
-
}, label), values.map(value => React.createElement(StyledMobileOption, {
|
|
331
|
-
value: value,
|
|
332
|
-
key: value,
|
|
333
|
-
...defaultValuePropMobile(value)
|
|
334
|
-
}, value))), React.createElement(Icon, {
|
|
335
|
-
label: "",
|
|
336
|
-
size: "100",
|
|
337
|
-
fill: theme.colors.gray80,
|
|
338
|
-
style: {
|
|
339
|
-
pointerEvents: 'none',
|
|
340
|
-
position: 'absolute',
|
|
341
|
-
right: '10px',
|
|
342
|
-
top: '50%',
|
|
343
|
-
transform: 'translateY(-50%)'
|
|
344
|
-
}
|
|
345
|
-
}, React.createElement(ChevronDown, {
|
|
346
|
-
style: {
|
|
347
|
-
position: 'absolute',
|
|
348
|
-
right: '10px'
|
|
349
|
-
}
|
|
350
|
-
}))) : React.createElement(Select.Root, {
|
|
351
|
-
onValueChange: e => setAnswer(e),
|
|
352
|
-
required: required,
|
|
353
|
-
...defaultValueProp,
|
|
354
|
-
...disabledProp
|
|
355
|
-
}, React.createElement(Select.Trigger, {
|
|
356
|
-
"data-test-id": `${id}-select-trigger`
|
|
357
|
-
}, React.createElement(Select.Label, null, label), React.createElement(Select.Value, null)), React.createElement(Select.Content, {
|
|
358
|
-
css: {
|
|
359
|
-
zIndex: theme.zIndices.page
|
|
360
|
-
},
|
|
361
|
-
"data-test-id": `${id}-select-content`
|
|
362
|
-
}, values.map(value => React.createElement(Select.Item, {
|
|
363
|
-
value: value,
|
|
364
|
-
key: value
|
|
365
|
-
}, value))));
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
const scriptSrc = `${ENDPOINTS.base}/de-utils/twpdeu.min.js`;
|
|
369
|
-
const SelectWrapper = /*#__PURE__*/styled('div', {
|
|
370
|
-
boxSizing: 'border-box',
|
|
371
|
-
display: 'flex',
|
|
372
|
-
marginBottom: '$100',
|
|
373
|
-
flexDirection: 'column',
|
|
374
|
-
'& button': {
|
|
375
|
-
padding: '1px 6px'
|
|
376
|
-
},
|
|
377
|
-
'& *': {
|
|
378
|
-
boxSizing: 'border-box'
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
const DESelect = ({
|
|
382
|
-
source,
|
|
383
|
-
fieldName,
|
|
384
|
-
label,
|
|
385
|
-
dataDictionaryConfig,
|
|
386
|
-
existingValue,
|
|
387
|
-
disabled,
|
|
388
|
-
submit,
|
|
389
|
-
onChange = () => {},
|
|
390
|
-
onFinished = () => {},
|
|
391
|
-
valuesFilter = () => true,
|
|
392
|
-
children,
|
|
393
|
-
valueSelectedByDefault
|
|
394
|
-
}) => {
|
|
395
|
-
const [config, setConfig] = useState(dataDictionaryConfig);
|
|
396
|
-
const [selected, setSelected] = useState(!existingValue && valueSelectedByDefault ? valueSelectedByDefault : '');
|
|
397
|
-
const scriptStatus = useScript(scriptSrc);
|
|
398
|
-
useEffect(() => {
|
|
399
|
-
const fetchConfig = async () => {
|
|
400
|
-
try {
|
|
401
|
-
var _window;
|
|
402
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
403
|
-
const config = await ((_window = window) === null || _window === void 0 || (_window = _window.__twpdeu) === null || _window === void 0 ? void 0 : _window.getFieldConfigs({
|
|
404
|
-
fieldName
|
|
405
|
-
}));
|
|
406
|
-
if (config) {
|
|
407
|
-
setConfig(config[0]);
|
|
408
|
-
} else {
|
|
409
|
-
console.error('unable to get config', fieldName);
|
|
410
|
-
}
|
|
411
|
-
} catch (e) {
|
|
412
|
-
console.warn('unable to get config', fieldName, e);
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
if (scriptStatus === ScriptStatus.READY && !(children || config)) {
|
|
416
|
-
fetchConfig();
|
|
417
|
-
}
|
|
418
|
-
}, [scriptStatus]);
|
|
419
|
-
useEffect(() => {
|
|
420
|
-
const submitSelected = async () => {
|
|
421
|
-
try {
|
|
422
|
-
var _window2;
|
|
423
|
-
const result = await ((_window2 = window) === null || _window2 === void 0 || (_window2 = _window2.__twpdeu) === null || _window2 === void 0 ? void 0 : _window2.push({
|
|
424
|
-
submitData: {
|
|
425
|
-
fieldName,
|
|
426
|
-
value: selected
|
|
427
|
-
},
|
|
428
|
-
source
|
|
429
|
-
}));
|
|
430
|
-
const isError = result === true ? false : result ? result.status !== ResponseStatus.SUCCESS : true;
|
|
431
|
-
onFinished({
|
|
432
|
-
isFinished: true,
|
|
433
|
-
isError
|
|
434
|
-
});
|
|
435
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
436
|
-
} catch (e) {
|
|
437
|
-
onFinished({
|
|
438
|
-
isFinished: false,
|
|
439
|
-
isError: true
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
};
|
|
443
|
-
if (scriptStatus === ScriptStatus.READY && submit && selected) {
|
|
444
|
-
submitSelected();
|
|
445
|
-
}
|
|
446
|
-
}, [scriptStatus, submit]);
|
|
447
|
-
const defaultValueProp = existingValue && config ? {
|
|
448
|
-
defaultValue: existingValue
|
|
449
|
-
} : {};
|
|
450
|
-
const isLoading = !(children || config);
|
|
451
|
-
const disabledProp = disabled || isLoading ? {
|
|
452
|
-
disabled: true
|
|
453
|
-
} : {};
|
|
454
|
-
// sort and filter out archived values
|
|
455
|
-
// Note: config.values may be readonly
|
|
456
|
-
const values = config ? [...config.values].sort((a, b) => a.order - b.order).filter(value => value.archived !== true).filter(valuesFilter) : [];
|
|
457
|
-
return React.createElement(SelectWrapper, null, children && React.createElement(Select.Root, {
|
|
458
|
-
onValueChange: e => {
|
|
459
|
-
setSelected(e);
|
|
460
|
-
onChange({
|
|
461
|
-
value: e
|
|
462
|
-
});
|
|
463
|
-
},
|
|
464
|
-
...defaultValueProp,
|
|
465
|
-
...disabledProp
|
|
466
|
-
}, children), !children && !config && React.createElement(Dropdown, {
|
|
467
|
-
id: 'loading',
|
|
468
|
-
label: 'Loading...',
|
|
469
|
-
values: [],
|
|
470
|
-
disabled: true
|
|
471
|
-
}), !children && config && React.createElement(Dropdown, {
|
|
472
|
-
id: config.name,
|
|
473
|
-
label: label || config.name,
|
|
474
|
-
onChange: e => {
|
|
475
|
-
setSelected(e);
|
|
476
|
-
onChange({
|
|
477
|
-
value: e
|
|
478
|
-
});
|
|
479
|
-
},
|
|
480
|
-
values: values.map(value => value.name),
|
|
481
|
-
existingValue: existingValue,
|
|
482
|
-
disabled: disabled,
|
|
483
|
-
valueSelectedByDefault: valueSelectedByDefault
|
|
484
|
-
}));
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
const configSrc = `${ENDPOINTS.base === 'https://subscribe.washingtonpost.com' ? 'https://www.washingtonpost.com/subscribe' : ENDPOINTS.base}/config/de/disclosure.json`;
|
|
488
|
-
const getConfig = async () => {
|
|
489
|
-
let myConfig;
|
|
490
|
-
// step 1: fetch config
|
|
491
|
-
const response = await fetch(configSrc);
|
|
492
|
-
const remoteConfig = await response.json();
|
|
493
|
-
// step 2: figure out which part of the config to use
|
|
494
|
-
// if country- or region-specific config found, use that
|
|
495
|
-
const {
|
|
496
|
-
country_code,
|
|
497
|
-
intl_region
|
|
498
|
-
} = WPGeo();
|
|
499
|
-
Object.keys(remoteConfig).forEach(configKey => {
|
|
500
|
-
if (country_code && configKey.split('|').includes(country_code.toLowerCase())) {
|
|
501
|
-
myConfig = remoteConfig[configKey];
|
|
502
|
-
} else if (intl_region === 'EEA' && configKey === 'eea') {
|
|
503
|
-
myConfig = remoteConfig[configKey];
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
// TODO: Check for billing country also
|
|
507
|
-
// else if no country-specific config, use the default config
|
|
508
|
-
if (typeof myConfig === 'undefined' && remoteConfig._) {
|
|
509
|
-
myConfig = remoteConfig._;
|
|
510
|
-
}
|
|
511
|
-
return myConfig;
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
const hydrateLinks = (str, onLinkClick = () => {}) => {
|
|
515
|
-
const array = str.split(/({{PRIVACY_POLICY}})/g);
|
|
516
|
-
const chunks = array.map((chunk, i) => {
|
|
517
|
-
if (chunk === '{{PRIVACY_POLICY}}') {
|
|
518
|
-
return React.createElement("a", {
|
|
519
|
-
key: `privacy-link-${i}`,
|
|
520
|
-
rel: "noopener noreferrer",
|
|
521
|
-
target: "_blank",
|
|
522
|
-
style: {
|
|
523
|
-
color: 'inherit'
|
|
524
|
-
},
|
|
525
|
-
className: "underline",
|
|
526
|
-
href: "https://www.washingtonpost.com/privacy-policy/",
|
|
527
|
-
onClick: e => onLinkClick(e)
|
|
528
|
-
}, "Privacy Policy");
|
|
529
|
-
}
|
|
530
|
-
return chunk;
|
|
531
|
-
});
|
|
532
|
-
const toReturn = chunks.reduce((prev, current) => React.createElement(React.Fragment, null, prev, current), React.createElement(React.Fragment, null));
|
|
533
|
-
return toReturn;
|
|
534
|
-
};
|
|
535
|
-
|
|
536
|
-
const COOKIE = 'OptanonConsent';
|
|
537
|
-
const checkOptanonConsentCookie = () => {
|
|
538
|
-
const value = getCookie(COOKIE) || '';
|
|
539
|
-
return value.length > 12;
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
const CONSENT_COOKIE = 'OptanonConsent';
|
|
543
|
-
const ALERT_BOX_COOKIE = 'OptanonAlertBoxClosed';
|
|
544
|
-
const useOnetrust = ({
|
|
545
|
-
allowCookieStore
|
|
546
|
-
}) => {
|
|
547
|
-
const [consentCookieExists, setConsentCookieExists] = useState();
|
|
548
|
-
const [alertBoxClosed, setAlertBoxClosed] = useState();
|
|
549
|
-
const [listenToCookieStore$1, setListenToCookieStore] = useState(false);
|
|
550
|
-
const [listenToTcfApi, setListenToTcfApi] = useState(false);
|
|
551
|
-
useEffect(() => {
|
|
552
|
-
var _window;
|
|
553
|
-
if (checkOptanonConsentCookie()) {
|
|
554
|
-
setConsentCookieExists(true);
|
|
555
|
-
}
|
|
556
|
-
if (checkAlertBoxClosedCookie()) {
|
|
557
|
-
setAlertBoxClosed(true);
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
if (!window.__tcfapi) {
|
|
561
|
-
console.warn('warning: __tcfapi not found');
|
|
562
|
-
}
|
|
563
|
-
if ((_window = window) !== null && _window !== void 0 && _window.cookieStore && allowCookieStore) {
|
|
564
|
-
setListenToCookieStore(true);
|
|
565
|
-
} else if (window.__tcfapi) {
|
|
566
|
-
setListenToTcfApi(true);
|
|
567
|
-
} else {
|
|
568
|
-
console.warn('warning: neither cookieStore nor __tcfapi found');
|
|
569
|
-
}
|
|
570
|
-
}, []);
|
|
571
|
-
useEffect(() => {
|
|
572
|
-
const cleanupFns = [];
|
|
573
|
-
if (listenToCookieStore$1 && window.cookieStore) {
|
|
574
|
-
const cleanupFn = listenToCookieStore(CONSENT_COOKIE, () => {
|
|
575
|
-
if (checkOptanonConsentCookie()) {
|
|
576
|
-
setConsentCookieExists(true);
|
|
577
|
-
}
|
|
578
|
-
});
|
|
579
|
-
cleanupFns.push(cleanupFn);
|
|
580
|
-
const cleanupFn2 = listenToCookieStore(ALERT_BOX_COOKIE, () => {
|
|
581
|
-
if (checkAlertBoxClosedCookie()) {
|
|
582
|
-
setAlertBoxClosed(true);
|
|
583
|
-
} else {
|
|
584
|
-
setAlertBoxClosed(false);
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
cleanupFns.push(cleanupFn2);
|
|
588
|
-
}
|
|
589
|
-
return () => {
|
|
590
|
-
cleanupFns.forEach(fn => fn && fn());
|
|
591
|
-
};
|
|
592
|
-
}, [listenToCookieStore$1]);
|
|
593
|
-
useEffect(() => {
|
|
594
|
-
let listenerId;
|
|
595
|
-
if (listenToTcfApi && window.__tcfapi) {
|
|
596
|
-
const callback = (_tcData, success) => {
|
|
597
|
-
if (success) {
|
|
598
|
-
listenerId = _tcData.listenerId;
|
|
599
|
-
if (checkOptanonConsentCookie()) {
|
|
600
|
-
setConsentCookieExists(true);
|
|
601
|
-
}
|
|
602
|
-
// tcData.eventStatus can be:
|
|
603
|
-
// tcloaded means user has made a choice and we’re ready to check it
|
|
604
|
-
// cmpuishown means the banner is shown
|
|
605
|
-
// useractioncomplete means the user has interacted with the banner
|
|
606
|
-
// but actually if the result for any of these is true, we just use the value of the cookie
|
|
607
|
-
if (checkAlertBoxClosedCookie()) {
|
|
608
|
-
setAlertBoxClosed(true);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
};
|
|
612
|
-
window.__tcfapi('addEventListener', 2, callback);
|
|
613
|
-
}
|
|
614
|
-
// cleanup fn
|
|
615
|
-
return () => {
|
|
616
|
-
if (window.__tcfapi && listenerId) window.__tcfapi('removeEventListener', 2, success => {
|
|
617
|
-
console.debug(success);
|
|
618
|
-
}, listenerId);
|
|
619
|
-
};
|
|
620
|
-
}, [listenToTcfApi]);
|
|
621
|
-
return {
|
|
622
|
-
consentCookieExists,
|
|
623
|
-
alertBoxClosed,
|
|
624
|
-
listenToCookieStore: listenToCookieStore$1,
|
|
625
|
-
listenToTcfApi
|
|
626
|
-
};
|
|
627
|
-
};
|
|
628
|
-
|
|
629
|
-
const DEDisclosureWithBannerStatus = ({
|
|
630
|
-
config,
|
|
631
|
-
onFinished,
|
|
632
|
-
allowCookieStore = true,
|
|
633
|
-
onPrivacyPolicyClick = () => {}
|
|
634
|
-
}) => {
|
|
635
|
-
const [disclosure, setDisclosure] = useState(null);
|
|
636
|
-
const [disclosureRendering, setDisclosureRendering] = useState(null);
|
|
637
|
-
const {
|
|
638
|
-
alertBoxClosed,
|
|
639
|
-
consentCookieExists
|
|
640
|
-
} = useOnetrust({
|
|
641
|
-
allowCookieStore
|
|
642
|
-
});
|
|
643
|
-
useEffect(() => {
|
|
644
|
-
if (config) {
|
|
645
|
-
// step 3: set disclosure based on config
|
|
646
|
-
if (alertBoxClosed) {
|
|
647
|
-
setDisclosure(config.disclosure_afterbanner);
|
|
648
|
-
} else {
|
|
649
|
-
setDisclosure(config.disclosure_beforebanner);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
}, [alertBoxClosed]);
|
|
653
|
-
useEffect(() => {
|
|
654
|
-
if (disclosure && Array.isArray(disclosure)) {
|
|
655
|
-
setDisclosureRendering(disclosure.reduce((prev, current) => React.createElement(React.Fragment, null, prev, React.createElement("p", null, hydrateLinks(current, onPrivacyPolicyClick))), React.createElement(React.Fragment, null)));
|
|
656
|
-
}
|
|
657
|
-
}, [disclosure]);
|
|
658
|
-
useEffect(() => {
|
|
659
|
-
if (disclosureRendering && consentCookieExists) {
|
|
660
|
-
onFinished({
|
|
661
|
-
isFinished: true,
|
|
662
|
-
isError: false
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
}, [disclosureRendering, consentCookieExists]);
|
|
666
|
-
return disclosureRendering;
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
const DEDisclosureWithoutBannerStatus = ({
|
|
670
|
-
config,
|
|
671
|
-
onFinished,
|
|
672
|
-
onPrivacyPolicyClick = () => {}
|
|
673
|
-
}) => {
|
|
674
|
-
const [disclosure, setDisclosure] = useState(null);
|
|
675
|
-
const [disclosureRendering, setDisclosureRendering] = useState(null);
|
|
676
|
-
useEffect(() => {
|
|
677
|
-
if (config) {
|
|
678
|
-
setDisclosure(config.disclosure);
|
|
679
|
-
}
|
|
680
|
-
}, [config]);
|
|
681
|
-
useEffect(() => {
|
|
682
|
-
if (disclosure && Array.isArray(disclosure)) {
|
|
683
|
-
setDisclosureRendering(disclosure.reduce((prev, current) => React.createElement(React.Fragment, null, prev, React.createElement("p", null, hydrateLinks(current, onPrivacyPolicyClick))), React.createElement(React.Fragment, null)));
|
|
684
|
-
}
|
|
685
|
-
}, [disclosure]);
|
|
686
|
-
useEffect(() => {
|
|
687
|
-
if (disclosureRendering) {
|
|
688
|
-
onFinished({
|
|
689
|
-
isFinished: true,
|
|
690
|
-
isError: false
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
}, [disclosureRendering]);
|
|
694
|
-
return disclosureRendering;
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
const DEDisclosure = ({
|
|
698
|
-
onFinished = () => {},
|
|
699
|
-
allowCookieStore = true,
|
|
700
|
-
onPrivacyPolicyClick = _e => {}
|
|
701
|
-
}) => {
|
|
702
|
-
const [disclosureRendering, setDisclosureRendering] = useState(null);
|
|
703
|
-
const [myConfig, setMyConfig] = useState();
|
|
704
|
-
// const { alertBoxClosed } = useOneTrustAlertBoxClosed({ allowCookieStore });
|
|
705
|
-
useEffect(() => {
|
|
706
|
-
(async () => {
|
|
707
|
-
const config = await getConfig();
|
|
708
|
-
setMyConfig(config);
|
|
709
|
-
if (!config) {
|
|
710
|
-
console.error('No config found');
|
|
711
|
-
}
|
|
712
|
-
})();
|
|
713
|
-
}, []);
|
|
714
|
-
useEffect(() => {
|
|
715
|
-
if (myConfig) {
|
|
716
|
-
// step 3: set disclosure based on config
|
|
717
|
-
// if config says to check onetrust, check onetrust
|
|
718
|
-
if ('checkBannerStatus' in myConfig && myConfig.checkBannerStatus) {
|
|
719
|
-
// check if onetrust is closed
|
|
720
|
-
// if it is, show the after banner disclosure
|
|
721
|
-
// if it is not, show the before banner disclosure
|
|
722
|
-
setDisclosureRendering(React.createElement(DEDisclosureWithBannerStatus, {
|
|
723
|
-
config: myConfig,
|
|
724
|
-
allowCookieStore: allowCookieStore,
|
|
725
|
-
onFinished: onFinished,
|
|
726
|
-
onPrivacyPolicyClick: onPrivacyPolicyClick
|
|
727
|
-
}));
|
|
728
|
-
} else if ('disclosure' in myConfig) {
|
|
729
|
-
setDisclosureRendering(React.createElement(DEDisclosureWithoutBannerStatus, {
|
|
730
|
-
config: myConfig,
|
|
731
|
-
onFinished: onFinished,
|
|
732
|
-
onPrivacyPolicyClick: onPrivacyPolicyClick
|
|
733
|
-
}));
|
|
734
|
-
} else {
|
|
735
|
-
console.error('Invalid config');
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}, [myConfig]);
|
|
739
|
-
if (disclosureRendering) {
|
|
740
|
-
return React.createElement("div", {
|
|
741
|
-
"data-test-id": "de-disclosure"
|
|
742
|
-
}, disclosureRendering);
|
|
743
|
-
}
|
|
744
|
-
return React.createElement("div", {
|
|
745
|
-
"data-test-id": "de-disclosure-loading"
|
|
746
|
-
});
|
|
747
|
-
};
|
|
748
|
-
|
|
749
|
-
const FirstPartyIngestDataTypes = {
|
|
750
|
-
JOB_LEVEL: 'profile_job_level',
|
|
751
|
-
JOB_INDUSTRY: 'profile_job_industry',
|
|
752
|
-
JOB_TITLE: 'profile_job_title',
|
|
753
|
-
PERSONAL_GOALS: 'personal_goals',
|
|
754
|
-
HOBBIES: 'hobbies',
|
|
755
|
-
PROFESSIONAL_GOALS: 'professional_goals',
|
|
756
|
-
INDUSTRY: 'industry',
|
|
757
|
-
NEWS_LOCATION: 'news_location',
|
|
758
|
-
NY_PERSONAL_GOALS: 'new_year_personal_goals',
|
|
759
|
-
NY_HOBBIES: 'new_year_hobbies',
|
|
760
|
-
NY_PROFESSIONAL_GOALS: 'new_year_professional_goals',
|
|
761
|
-
NY_INDUSTRY: 'new_year_industry',
|
|
762
|
-
NY_NEWS_LOCATION: 'new_year_news_location',
|
|
763
|
-
EDU_ROLE: 'profile_edu_role',
|
|
764
|
-
EDU_MAJOR: 'profile_edu_major',
|
|
765
|
-
EDU_GRADUATION_YEAR: 'profile_edu_graduation_year'
|
|
766
|
-
};
|
|
767
|
-
|
|
768
|
-
export { AttributesState, CollectionBehaviors, DEDisclosure, DESelect, DeleteAttributeState, FirstPartyIngestDataTypes, IngestResponseState, IngestType, getAttributes, hasRequiredPrivacyCookies, push };
|
|
769
|
-
//# sourceMappingURL=subs-de-inputs.esm.js.map
|