gatsby-core-theme 44.20.0 → 44.21.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 +21 -0
- package/gatsby-browser.js +20 -1
- package/package.json +1 -1
- package/src/components/atoms/button/operator-cta.js +14 -37
- package/src/components/atoms/button/operator-cta.test.js +82 -44
- package/src/components/atoms/pretty-link/index.js +12 -41
- package/src/components/atoms/pretty-link/pretty-link.test.js +54 -0
- package/src/helpers/validateData.mjs +27 -9
- package/src/hooks/useCtaClickHandler/index.js +82 -0
- package/src/hooks/useCtaClickHandler/useCtaClickHandler.test.js +145 -0
- package/src/resolver/index.mjs +2 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
# [44.21.0](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.20.0...v44.21.0) (2026-04-07)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* apply INP fix to core theme level ([76bc451](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/76bc4516d27376203f6eafa71a2ca04456c10a20))
|
|
7
|
+
* fix test by mocking the useCtaClickHandler ([882f602](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/882f602797a4b0605cb0d16f71604906f6303552))
|
|
8
|
+
* fix test by mocking the useCtaClickHandler ([99b6f73](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/99b6f7324d09f053182ffb019ce2cb79d9fb4b22))
|
|
9
|
+
* improve code ([7d5ec29](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/7d5ec29d6db3105082de5b1f2267b434b8f2ff06))
|
|
10
|
+
* remove unused param ([aaa3e22](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/aaa3e22478d5cff7c5dfdbae8da7736d3e5a3ddc))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
* Merge branch 'improve-code' into 'master' ([1d4be07](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/1d4be073aa4727c500452713fb88d46216e2c60c))
|
|
14
|
+
* Merge branch 'feat/gatsby-browser-yield-thread' into 'master' ([d107be8](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/d107be8a617e1c24ee4598310be7f160247329c6))
|
|
15
|
+
* Merge branch 'inp-fix' into 'master' ([c892c47](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/c892c47ee11e8b4635c043764ec47b210b9ba192))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* yield before script loading ([ad0e7de](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/ad0e7de84050d8cdf7a5c3dc503f7dd11d67f63b))
|
|
21
|
+
|
|
1
22
|
# [44.20.0](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.19.2...v44.20.0) (2026-04-03)
|
|
2
23
|
|
|
3
24
|
|
package/gatsby-browser.js
CHANGED
|
@@ -16,6 +16,17 @@ require("./src/styles/base/_reset.scss");
|
|
|
16
16
|
require("./src/styles/base/_spacing.scss");
|
|
17
17
|
require("./src/styles/layouts/_grid.scss");
|
|
18
18
|
|
|
19
|
+
async function yieldToMain() {
|
|
20
|
+
if (globalThis.scheduler?.yield) {
|
|
21
|
+
return scheduler.yield();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Fall back to yielding with setTimeout.
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
setTimeout(resolve, 0);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
19
30
|
function initGTM() {
|
|
20
31
|
if (
|
|
21
32
|
window.loadGTM === false ||
|
|
@@ -129,7 +140,8 @@ const piguard = () => {
|
|
|
129
140
|
document.head.appendChild(script);
|
|
130
141
|
};
|
|
131
142
|
|
|
132
|
-
function scrollEvent(event) {
|
|
143
|
+
async function scrollEvent(event) {
|
|
144
|
+
await yieldToMain();
|
|
133
145
|
initGTM();
|
|
134
146
|
|
|
135
147
|
if (
|
|
@@ -173,6 +185,13 @@ function scrollEvent(event) {
|
|
|
173
185
|
}
|
|
174
186
|
|
|
175
187
|
exports.onClientEntry = () => {
|
|
188
|
+
if (
|
|
189
|
+
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
190
|
+
navigator.userAgent
|
|
191
|
+
)
|
|
192
|
+
) {
|
|
193
|
+
scrollEvent(null);
|
|
194
|
+
}
|
|
176
195
|
if (process.env.PPC === "true") {
|
|
177
196
|
scrollEvent(null);
|
|
178
197
|
} else {
|
package/package.json
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
/* eslint-disable no-unused-expressions */
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
|
-
import { globalHistory } from '@reach/router';
|
|
6
5
|
import useTranslate from '~hooks/useTranslate/useTranslate';
|
|
7
6
|
import { prettyTracker } from '~helpers/getters';
|
|
8
|
-
import {
|
|
9
|
-
import { getTrackingAPIParams, trackerLinkActive } from '~helpers/tracker';
|
|
10
|
-
import keygen from '~helpers/keygen';
|
|
7
|
+
import { trackerLinkActive } from '~helpers/tracker';
|
|
11
8
|
import styles from './button.module.scss';
|
|
9
|
+
import useCtaClickHandler from '../../../hooks/useCtaClickHandler/index';
|
|
12
10
|
|
|
13
11
|
const OperatorCtaButton = ({
|
|
14
12
|
operator,
|
|
@@ -43,10 +41,6 @@ const OperatorCtaButton = ({
|
|
|
43
41
|
}) => {
|
|
44
42
|
const status = operator?.status || ''
|
|
45
43
|
const trackerType = tracker?.toLowerCase()?.replace(' ', '_')
|
|
46
|
-
const referer =
|
|
47
|
-
typeof document !== 'undefined' && document?.referrer
|
|
48
|
-
? document.referrer
|
|
49
|
-
: '';
|
|
50
44
|
|
|
51
45
|
const prettyLink = `${prettyTracker(
|
|
52
46
|
operator,
|
|
@@ -54,6 +48,16 @@ const OperatorCtaButton = ({
|
|
|
54
48
|
false
|
|
55
49
|
)}`
|
|
56
50
|
|
|
51
|
+
const { ctaRef, handleCtaClick } = useCtaClickHandler({
|
|
52
|
+
pageTemplate,
|
|
53
|
+
moduleName,
|
|
54
|
+
tracker,
|
|
55
|
+
clickedElement,
|
|
56
|
+
modulePosition,
|
|
57
|
+
itemPosition,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
|
|
57
61
|
const translateBtn =
|
|
58
62
|
status && translationsObj[status]
|
|
59
63
|
? // eslint-disable-next-line react-hooks/rules-of-hooks
|
|
@@ -63,40 +67,13 @@ const OperatorCtaButton = ({
|
|
|
63
67
|
)
|
|
64
68
|
: 'Claim'
|
|
65
69
|
|
|
66
|
-
const affObject = JSON.stringify(
|
|
67
|
-
getTrackingAPIParams(
|
|
68
|
-
pageTemplate,
|
|
69
|
-
moduleName,
|
|
70
|
-
tracker,
|
|
71
|
-
clickedElement,
|
|
72
|
-
globalHistory.location.href,
|
|
73
|
-
referer,
|
|
74
|
-
modulePosition,
|
|
75
|
-
itemPosition
|
|
76
|
-
)
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
const onCTAClick = () => {
|
|
80
|
-
process.env.IS_TRACKING_SSR &&
|
|
81
|
-
setCookie(
|
|
82
|
-
'affObject',
|
|
83
|
-
affObject,
|
|
84
|
-
1,
|
|
85
|
-
'/'
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
if (process.env.ENABLE_PIXEL === 'true' && typeof window.fbq !== 'undefined') {
|
|
89
|
-
// Call fbq track event
|
|
90
|
-
window.fbq('track', 'InitiateCheckout', { keyID: keygen() })
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
70
|
const classes = `${styles[buttonType] || ''} ${status && styles[status] ? styles[status] : ''} ${buttonSize ? styles[`${buttonSize}_size`] : ''
|
|
95
71
|
} `;
|
|
96
72
|
|
|
97
73
|
return (
|
|
98
74
|
trackerLinkActive(operator, tracker) && status === "active" ? (
|
|
99
75
|
<a
|
|
76
|
+
ref={ctaRef}
|
|
100
77
|
href={prettyLink || "#"}
|
|
101
78
|
title={`${typeof translateBtn === "string" ? translateBtn : ""} ${titleSuffix || ""
|
|
102
79
|
}`.trimEnd()}
|
|
@@ -105,7 +82,7 @@ const OperatorCtaButton = ({
|
|
|
105
82
|
className={`${classes} ${gtmClass}`}
|
|
106
83
|
target="_blank"
|
|
107
84
|
rel={rel}
|
|
108
|
-
onClick={
|
|
85
|
+
onClick={handleCtaClick}
|
|
109
86
|
>
|
|
110
87
|
{translateBtn}
|
|
111
88
|
{icon && icon}
|
|
@@ -4,6 +4,9 @@ import React from 'react';
|
|
|
4
4
|
import { render, cleanup, fireEvent } from '@testing-library/react';
|
|
5
5
|
import '@testing-library/jest-dom/extend-expect';
|
|
6
6
|
import OperatorCtaButton from './operator-cta';
|
|
7
|
+
import useCtaClickHandler from '../../../hooks/useCtaClickHandler/index';
|
|
8
|
+
|
|
9
|
+
jest.mock('../../../hooks/useCtaClickHandler/index', () => jest.fn());
|
|
7
10
|
|
|
8
11
|
const data = (activeStatus = 'active', links = true) => ({
|
|
9
12
|
operator: {
|
|
@@ -24,62 +27,35 @@ const data = (activeStatus = 'active', links = true) => ({
|
|
|
24
27
|
});
|
|
25
28
|
|
|
26
29
|
describe('OperatorCtaButton Component', () => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} else {
|
|
33
|
-
expect(button).toBeNull();
|
|
34
|
-
}
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
useCtaClickHandler.mockImplementation(() => ({
|
|
32
|
+
ctaRef: { current: null },
|
|
33
|
+
handleCtaClick: jest.fn(),
|
|
34
|
+
}));
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const button = queryByRole('link');
|
|
40
|
-
if (button) {
|
|
41
|
-
expect(getByText('Not Accepting New Players')).toBeInTheDocument();
|
|
42
|
-
} else {
|
|
43
|
-
expect(button).toBeNull();
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('OperatorCtaButton with status not_recommended', () => {
|
|
48
|
-
const { getByText, queryByRole } = render(<OperatorCtaButton {...data('not_recommended')} />);
|
|
49
|
-
const button = queryByRole('link');
|
|
50
|
-
if (button) {
|
|
51
|
-
expect(getByText("Not Recommended")).toBeInTheDocument();
|
|
52
|
-
} else {
|
|
53
|
-
expect(button).toBeNull();
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('OperatorCtaButton with status coming_soon', () => {
|
|
58
|
-
const { getByText, queryByRole } = render(<OperatorCtaButton {...data('coming_soon')} />);
|
|
59
|
-
const button = queryByRole('link');
|
|
60
|
-
if (button) {
|
|
61
|
-
expect(getByText("Soon Available")).toBeInTheDocument();
|
|
62
|
-
} else {
|
|
63
|
-
expect(button).toBeNull();
|
|
64
|
-
}
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
jest.clearAllMocks();
|
|
65
39
|
});
|
|
66
40
|
|
|
67
41
|
test('Affiliate object in cookie', () => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
value: {
|
|
73
|
-
href: url,
|
|
74
|
-
},
|
|
75
|
-
writable: true, // possibility to override
|
|
42
|
+
const mockClickHandler = jest.fn((e) => {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
document.cookie =
|
|
45
|
+
'affObject={"page_type":"test","module":"test2","request_url":"http://localhost/","tracker_name":"testing"}';
|
|
76
46
|
});
|
|
47
|
+
useCtaClickHandler.mockImplementation(() => ({
|
|
48
|
+
ctaRef: { current: null },
|
|
49
|
+
handleCtaClick: mockClickHandler,
|
|
50
|
+
}));
|
|
51
|
+
|
|
77
52
|
const { container } = render(
|
|
78
53
|
<OperatorCtaButton {...data()} pageTemplate="test" moduleName="test2" tracker="testing" />
|
|
79
54
|
);
|
|
80
55
|
const button = container.querySelector('a');
|
|
81
56
|
if (button) {
|
|
82
57
|
fireEvent.click(button);
|
|
58
|
+
expect(mockClickHandler).toHaveBeenCalled();
|
|
83
59
|
const affObject = JSON.parse(document.cookie.split('=')[1]);
|
|
84
60
|
expect(affObject.page_type).toEqual('test');
|
|
85
61
|
expect(affObject.module).toEqual('test2');
|
|
@@ -91,10 +67,20 @@ describe('OperatorCtaButton Component', () => {
|
|
|
91
67
|
});
|
|
92
68
|
|
|
93
69
|
test('Default Affiliate object in cookie', () => {
|
|
70
|
+
const mockClickHandler = jest.fn((e) => {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
document.cookie =
|
|
73
|
+
'affObject={"page_type":"default","request_url":"http://localhost/"}';
|
|
74
|
+
});
|
|
75
|
+
useCtaClickHandler.mockImplementation(() => ({
|
|
76
|
+
ctaRef: { current: null },
|
|
77
|
+
handleCtaClick: mockClickHandler,
|
|
78
|
+
}));
|
|
94
79
|
const { container } = render(<OperatorCtaButton {...data()} />);
|
|
95
80
|
const button = container.querySelector('a');
|
|
96
81
|
if (button) {
|
|
97
82
|
fireEvent.click(button);
|
|
83
|
+
expect(mockClickHandler).toHaveBeenCalled();
|
|
98
84
|
const affObject = JSON.parse(document.cookie.split('=')[1]);
|
|
99
85
|
expect(affObject.page_type).toEqual('default');
|
|
100
86
|
expect(affObject.request_url).toEqual('http://localhost/');
|
|
@@ -108,6 +94,58 @@ describe('OperatorCtaButton Component', () => {
|
|
|
108
94
|
const button = queryByRole('link');
|
|
109
95
|
expect(button).toBeNull();
|
|
110
96
|
});
|
|
97
|
+
|
|
98
|
+
test('Passes tracking props to cta click hook', () => {
|
|
99
|
+
render(<OperatorCtaButton {...data()} moduleName="test-module" tracker="testing" />);
|
|
100
|
+
expect(useCtaClickHandler).toHaveBeenCalledWith({
|
|
101
|
+
pageTemplate: 'default',
|
|
102
|
+
moduleName: 'test-module',
|
|
103
|
+
tracker: 'testing',
|
|
104
|
+
clickedElement: 'cta',
|
|
105
|
+
modulePosition: undefined,
|
|
106
|
+
itemPosition: undefined,
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('OperatorCtaButton with status active', () => {
|
|
111
|
+
const { getByText, queryByRole } = render(<OperatorCtaButton {...data()} />);
|
|
112
|
+
const button = queryByRole('link');
|
|
113
|
+
if (button) {
|
|
114
|
+
expect(getByText("Visit")).toBeInTheDocument();
|
|
115
|
+
} else {
|
|
116
|
+
expect(button).toBeNull();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('OperatorCtaButton with status inactive', () => {
|
|
121
|
+
const { getByText, queryByRole } = render(<OperatorCtaButton {...data('inactive')} />);
|
|
122
|
+
const button = queryByRole('link');
|
|
123
|
+
if (button) {
|
|
124
|
+
expect(getByText('Not Accepting New Players')).toBeInTheDocument();
|
|
125
|
+
} else {
|
|
126
|
+
expect(button).toBeNull();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('OperatorCtaButton with status not_recommended', () => {
|
|
131
|
+
const { getByText, queryByRole } = render(<OperatorCtaButton {...data('not_recommended')} />);
|
|
132
|
+
const button = queryByRole('link');
|
|
133
|
+
if (button) {
|
|
134
|
+
expect(getByText("Not Recommended")).toBeInTheDocument();
|
|
135
|
+
} else {
|
|
136
|
+
expect(button).toBeNull();
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('OperatorCtaButton with status coming_soon', () => {
|
|
141
|
+
const { getByText, queryByRole } = render(<OperatorCtaButton {...data('coming_soon')} />);
|
|
142
|
+
const button = queryByRole('link');
|
|
143
|
+
if (button) {
|
|
144
|
+
expect(getByText("Soon Available")).toBeInTheDocument();
|
|
145
|
+
} else {
|
|
146
|
+
expect(button).toBeNull();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
111
149
|
});
|
|
112
150
|
|
|
113
151
|
afterEach(() => {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/* eslint-disable react/jsx-no-target-blank */
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import PropTypes from 'prop-types'
|
|
4
|
-
import { globalHistory } from '@reach/router'
|
|
5
4
|
import { prettyTracker } from '~helpers/getters'
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import { getTrackingAPIParams, trackerLinkActive } from '~helpers/tracker'
|
|
5
|
+
import { trackerLinkActive } from '~helpers/tracker'
|
|
6
|
+
import useCtaClickHandler from '../../../hooks/useCtaClickHandler/index'
|
|
9
7
|
|
|
10
8
|
const PrettyLink = ({
|
|
11
9
|
operator,
|
|
@@ -22,57 +20,30 @@ const PrettyLink = ({
|
|
|
22
20
|
itemPosition
|
|
23
21
|
}) => {
|
|
24
22
|
const prettyLink = prettyTracker(operator, tracker, false, pageTemplate)
|
|
25
|
-
const referer =
|
|
26
|
-
typeof document !== 'undefined' && document?.referrer
|
|
27
|
-
? document.referrer
|
|
28
|
-
: ''
|
|
29
|
-
|
|
30
|
-
const affObject = JSON.stringify(
|
|
31
|
-
getTrackingAPIParams(
|
|
32
|
-
pageTemplate,
|
|
33
|
-
moduleName,
|
|
34
|
-
tracker,
|
|
35
|
-
clickedElement,
|
|
36
|
-
globalHistory.location.href,
|
|
37
|
-
referer,
|
|
38
|
-
modulePosition,
|
|
39
|
-
itemPosition
|
|
40
|
-
)
|
|
41
|
-
)
|
|
42
23
|
|
|
24
|
+
const { ctaRef, handleCtaClick } = useCtaClickHandler({
|
|
25
|
+
pageTemplate,
|
|
26
|
+
moduleName,
|
|
27
|
+
tracker,
|
|
28
|
+
clickedElement,
|
|
29
|
+
modulePosition,
|
|
30
|
+
itemPosition,
|
|
31
|
+
});
|
|
43
32
|
|
|
44
|
-
const onCTAClick = () => {
|
|
45
|
-
// eslint-disable-next-line no-unused-expressions
|
|
46
|
-
process.env.IS_TRACKING_SSR &&
|
|
47
|
-
setCookie(
|
|
48
|
-
'affObject',
|
|
49
|
-
affObject,
|
|
50
|
-
1,
|
|
51
|
-
'/'
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
process.env.ENABLE_PIXEL === 'true' &&
|
|
56
|
-
typeof window.fbq !== 'undefined'
|
|
57
|
-
) {
|
|
58
|
-
// Call fbq track event
|
|
59
|
-
window.fbq('track', 'InitiateCheckout', { keyID: keygen() })
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
33
|
const isLinkActive =
|
|
64
34
|
(prettyLink && trackerLinkActive(operator, tracker)) || directPrettyLink
|
|
65
35
|
const hrefLink = prettyLink || directPrettyLink || '#'
|
|
66
36
|
|
|
67
37
|
return isLinkActive ? (
|
|
68
38
|
<a
|
|
39
|
+
ref={ctaRef}
|
|
69
40
|
href={hrefLink}
|
|
70
41
|
title={`${operator?.name || ''} ${titleSuffix || ''}`.trimEnd() || ''}
|
|
71
42
|
className={className}
|
|
72
43
|
target="_blank"
|
|
73
44
|
rel={rel}
|
|
74
45
|
aria-label={`${operator?.name} ${titleSuffix || 'Link'}`}
|
|
75
|
-
onClick={
|
|
46
|
+
onClick={handleCtaClick}
|
|
76
47
|
>
|
|
77
48
|
{children}
|
|
78
49
|
</a>
|
|
@@ -5,6 +5,9 @@ import { render, cleanup, fireEvent } from '@testing-library/react';
|
|
|
5
5
|
import '@testing-library/jest-dom/extend-expect';
|
|
6
6
|
|
|
7
7
|
import PrettyLink from '.';
|
|
8
|
+
import useCtaClickHandler from '../../../hooks/useCtaClickHandler/index';
|
|
9
|
+
|
|
10
|
+
jest.mock('../../../hooks/useCtaClickHandler/index', () => jest.fn());
|
|
8
11
|
|
|
9
12
|
const data = (activeStatus = 'active') => ({
|
|
10
13
|
operator: {
|
|
@@ -21,6 +24,17 @@ const data = (activeStatus = 'active') => ({
|
|
|
21
24
|
});
|
|
22
25
|
|
|
23
26
|
describe('PrettyLink Component', () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
useCtaClickHandler.mockImplementation(() => ({
|
|
29
|
+
ctaRef: { current: null },
|
|
30
|
+
handleCtaClick: jest.fn(),
|
|
31
|
+
}));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
jest.clearAllMocks();
|
|
36
|
+
});
|
|
37
|
+
|
|
24
38
|
test('PrettyLink children and link', () => {
|
|
25
39
|
const { getByText, container } = render(<PrettyLink {...data()} />);
|
|
26
40
|
expect(getByText('Test children').tagName.toLowerCase()).toBe('h1');
|
|
@@ -28,6 +42,15 @@ describe('PrettyLink Component', () => {
|
|
|
28
42
|
});
|
|
29
43
|
|
|
30
44
|
test('Affiliate object in cookie', () => {
|
|
45
|
+
const mockClickHandler = jest.fn((e) => {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
document.cookie =
|
|
48
|
+
'affObject={"page_type":"test","module":"test2","request_url":"http://localhost/","tracker_name":"testing","clicked_element":"cta"}';
|
|
49
|
+
});
|
|
50
|
+
useCtaClickHandler.mockImplementation(() => ({
|
|
51
|
+
ctaRef: { current: null },
|
|
52
|
+
handleCtaClick: mockClickHandler,
|
|
53
|
+
}));
|
|
31
54
|
const { container } = render(
|
|
32
55
|
<PrettyLink
|
|
33
56
|
{...data()}
|
|
@@ -39,6 +62,7 @@ describe('PrettyLink Component', () => {
|
|
|
39
62
|
);
|
|
40
63
|
const button = container.querySelector('a');
|
|
41
64
|
fireEvent.click(button);
|
|
65
|
+
expect(mockClickHandler).toHaveBeenCalled();
|
|
42
66
|
const affObject = JSON.parse(document.cookie.split('=')[1]);
|
|
43
67
|
expect(affObject.page_type).toEqual('test');
|
|
44
68
|
expect(affObject.module).toEqual('test2');
|
|
@@ -62,13 +86,43 @@ describe('PrettyLink Component', () => {
|
|
|
62
86
|
});
|
|
63
87
|
|
|
64
88
|
test('Default Affiliate object in cookie', () => {
|
|
89
|
+
const mockClickHandler = jest.fn((e) => {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
document.cookie =
|
|
92
|
+
'affObject={"page_type":"default","request_url":"http://localhost/"}';
|
|
93
|
+
});
|
|
94
|
+
useCtaClickHandler.mockImplementation(() => ({
|
|
95
|
+
ctaRef: { current: null },
|
|
96
|
+
handleCtaClick: mockClickHandler,
|
|
97
|
+
}));
|
|
65
98
|
const { container } = render(<PrettyLink {...data()} />);
|
|
66
99
|
const button = container.querySelector('a');
|
|
67
100
|
fireEvent.click(button);
|
|
101
|
+
expect(mockClickHandler).toHaveBeenCalled();
|
|
68
102
|
const affObject = JSON.parse(document.cookie.split('=')[1]);
|
|
69
103
|
expect(affObject.page_type).toEqual('default');
|
|
70
104
|
expect(affObject.request_url).toEqual('http://localhost/');
|
|
71
105
|
});
|
|
106
|
+
|
|
107
|
+
test('Passes tracking props to cta click hook', () => {
|
|
108
|
+
render(
|
|
109
|
+
<PrettyLink
|
|
110
|
+
{...data()}
|
|
111
|
+
pageTemplate="test"
|
|
112
|
+
moduleName="test2"
|
|
113
|
+
tracker="testing"
|
|
114
|
+
clickedElement="cta"
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
expect(useCtaClickHandler).toHaveBeenCalledWith({
|
|
118
|
+
pageTemplate: 'test',
|
|
119
|
+
moduleName: 'test2',
|
|
120
|
+
tracker: 'testing',
|
|
121
|
+
clickedElement: 'cta',
|
|
122
|
+
modulePosition: undefined,
|
|
123
|
+
itemPosition: undefined,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
72
126
|
});
|
|
73
127
|
|
|
74
128
|
afterEach(() => {
|
|
@@ -1,17 +1,35 @@
|
|
|
1
|
-
/* eslint-disable no-useless-return */
|
|
2
1
|
// eslint-disable-next-line import/prefer-default-export
|
|
3
2
|
import { excludeStatusInactiveToplist } from '../constants/excluded.mjs';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const INACTIVE_SHORTCODES = new Set([
|
|
5
|
+
'inactive_operator_toplist',
|
|
6
|
+
'inactive_sportsbook_toplist',
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
export default function checkForInactiveOperatorToplist(item, status, res, pageType) {
|
|
10
|
+
const shortCode = item?.items?.[0]?.value;
|
|
7
11
|
const siteName = process.env.GATSBY_SITE_NAME;
|
|
8
|
-
const excludeStatus =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return
|
|
12
|
+
const excludeStatus =
|
|
13
|
+
excludeStatusInactiveToplist[siteName] || excludeStatusInactiveToplist.default;
|
|
14
|
+
|
|
15
|
+
if (item.name === 'top_list') {
|
|
16
|
+
|
|
17
|
+
if (!INACTIVE_SHORTCODES.has(shortCode)) return false;
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if (excludeStatus.includes(status)) return true;
|
|
21
|
+
|
|
22
|
+
const expectedShortcode =
|
|
23
|
+
pageType === 'sportsbook'
|
|
24
|
+
? 'inactive_sportsbook_toplist'
|
|
25
|
+
: 'inactive_operator_toplist';
|
|
26
|
+
|
|
27
|
+
return shortCode !== expectedShortcode;
|
|
14
28
|
}
|
|
15
29
|
|
|
30
|
+
if (item.name === 'cards_v2') {
|
|
31
|
+
return status === 'active' && res === 'pre_main_operators';
|
|
32
|
+
}
|
|
33
|
+
|
|
16
34
|
return false;
|
|
17
35
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
import { globalHistory } from '@reach/router';
|
|
3
|
+
import { setCookie } from '~helpers/cookies';
|
|
4
|
+
import keygen from '~helpers/keygen';
|
|
5
|
+
import { getTrackingAPIParams } from '~helpers/tracker';
|
|
6
|
+
|
|
7
|
+
const useCtaClickHandler = ({
|
|
8
|
+
pageTemplate,
|
|
9
|
+
moduleName,
|
|
10
|
+
tracker,
|
|
11
|
+
clickedElement,
|
|
12
|
+
modulePosition,
|
|
13
|
+
itemPosition,
|
|
14
|
+
}) => {
|
|
15
|
+
const referer =
|
|
16
|
+
typeof document !== 'undefined' && document?.referrer
|
|
17
|
+
? document.referrer
|
|
18
|
+
: '';
|
|
19
|
+
|
|
20
|
+
const affObject = JSON.stringify(
|
|
21
|
+
getTrackingAPIParams(
|
|
22
|
+
pageTemplate,
|
|
23
|
+
moduleName,
|
|
24
|
+
tracker,
|
|
25
|
+
clickedElement,
|
|
26
|
+
globalHistory.location.href,
|
|
27
|
+
referer,
|
|
28
|
+
modulePosition,
|
|
29
|
+
itemPosition
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const ctaRef = useRef(null);
|
|
34
|
+
|
|
35
|
+
const fireTracking = useCallback(() => {
|
|
36
|
+
if (process.env.IS_TRACKING_SSR) {
|
|
37
|
+
setCookie('affObject', affObject, 1, '/');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (
|
|
41
|
+
process.env.ENABLE_PIXEL === 'true' &&
|
|
42
|
+
typeof window.fbq !== 'undefined'
|
|
43
|
+
) {
|
|
44
|
+
window.fbq('track', 'InitiateCheckout', { keyID: keygen() });
|
|
45
|
+
}
|
|
46
|
+
}, [affObject]);
|
|
47
|
+
|
|
48
|
+
const handleCtaClick = useCallback(
|
|
49
|
+
(e) => {
|
|
50
|
+
// Ignore synthetic events dispatched for GTM
|
|
51
|
+
if (!e.isTrusted) {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Block GTM's click listener from running synchronously (it listens on document)
|
|
57
|
+
e.stopPropagation();
|
|
58
|
+
|
|
59
|
+
const anchor = ctaRef.current;
|
|
60
|
+
if (!anchor) return;
|
|
61
|
+
|
|
62
|
+
requestAnimationFrame(() => {
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
fireTracking();
|
|
65
|
+
|
|
66
|
+
// Re-notify GTM with a synthetic click so it can still track the event
|
|
67
|
+
const gtmEvent = new MouseEvent('click', {
|
|
68
|
+
bubbles: true,
|
|
69
|
+
cancelable: true,
|
|
70
|
+
view: window,
|
|
71
|
+
});
|
|
72
|
+
anchor.dispatchEvent(gtmEvent);
|
|
73
|
+
}, 0);
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
[fireTracking]
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return { ctaRef, handleCtaClick };
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default useCtaClickHandler;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
2
|
+
import useCtaClickHandler from './index';
|
|
3
|
+
import { setCookie } from '~helpers/cookies';
|
|
4
|
+
import keygen from '~helpers/keygen';
|
|
5
|
+
import { getTrackingAPIParams } from '~helpers/tracker';
|
|
6
|
+
|
|
7
|
+
jest.mock('@reach/router', () => ({
|
|
8
|
+
globalHistory: {
|
|
9
|
+
location: {
|
|
10
|
+
href: 'http://localhost/',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
jest.mock('~helpers/cookies', () => ({
|
|
16
|
+
setCookie: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
jest.mock('~helpers/keygen', () => jest.fn());
|
|
20
|
+
|
|
21
|
+
jest.mock('~helpers/tracker', () => ({
|
|
22
|
+
getTrackingAPIParams: jest.fn(),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
describe('useCtaClickHandler', () => {
|
|
26
|
+
const originalTrackingSsr = process.env.IS_TRACKING_SSR;
|
|
27
|
+
const originalEnablePixel = process.env.ENABLE_PIXEL;
|
|
28
|
+
const originalFbq = window.fbq;
|
|
29
|
+
const originalRequestAnimationFrame = global.requestAnimationFrame;
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
jest.clearAllMocks();
|
|
33
|
+
process.env.IS_TRACKING_SSR = 'true';
|
|
34
|
+
process.env.ENABLE_PIXEL = 'false';
|
|
35
|
+
getTrackingAPIParams.mockReturnValue({
|
|
36
|
+
page_type: 'default',
|
|
37
|
+
request_url: 'http://localhost/',
|
|
38
|
+
});
|
|
39
|
+
keygen.mockReturnValue('generated-key');
|
|
40
|
+
global.requestAnimationFrame = (cb) => cb();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
process.env.IS_TRACKING_SSR = originalTrackingSsr;
|
|
45
|
+
process.env.ENABLE_PIXEL = originalEnablePixel;
|
|
46
|
+
window.fbq = originalFbq;
|
|
47
|
+
global.requestAnimationFrame = originalRequestAnimationFrame;
|
|
48
|
+
jest.useRealTimers();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('builds tracking params from hook arguments', () => {
|
|
52
|
+
renderHook(() =>
|
|
53
|
+
useCtaClickHandler({
|
|
54
|
+
pageTemplate: 'review',
|
|
55
|
+
moduleName: 'top-list',
|
|
56
|
+
tracker: 'main',
|
|
57
|
+
clickedElement: 'cta',
|
|
58
|
+
modulePosition: 1,
|
|
59
|
+
itemPosition: 2,
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
expect(getTrackingAPIParams).toHaveBeenCalledWith(
|
|
64
|
+
'review',
|
|
65
|
+
'top-list',
|
|
66
|
+
'main',
|
|
67
|
+
'cta',
|
|
68
|
+
'http://localhost/',
|
|
69
|
+
'',
|
|
70
|
+
1,
|
|
71
|
+
2
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('ignores non-trusted click events', () => {
|
|
76
|
+
const { result } = renderHook(() =>
|
|
77
|
+
useCtaClickHandler({
|
|
78
|
+
pageTemplate: 'default',
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
const event = {
|
|
82
|
+
isTrusted: false,
|
|
83
|
+
preventDefault: jest.fn(),
|
|
84
|
+
stopPropagation: jest.fn(),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
result.current.handleCtaClick(event);
|
|
88
|
+
|
|
89
|
+
expect(event.preventDefault).toHaveBeenCalled();
|
|
90
|
+
expect(event.stopPropagation).not.toHaveBeenCalled();
|
|
91
|
+
expect(setCookie).not.toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('does nothing when trusted click has no anchor ref', () => {
|
|
95
|
+
const { result } = renderHook(() =>
|
|
96
|
+
useCtaClickHandler({
|
|
97
|
+
pageTemplate: 'default',
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
const event = {
|
|
101
|
+
isTrusted: true,
|
|
102
|
+
preventDefault: jest.fn(),
|
|
103
|
+
stopPropagation: jest.fn(),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
result.current.handleCtaClick(event);
|
|
107
|
+
|
|
108
|
+
expect(event.stopPropagation).toHaveBeenCalled();
|
|
109
|
+
expect(setCookie).not.toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('fires tracking and redispatches click for trusted events', () => {
|
|
113
|
+
jest.useFakeTimers();
|
|
114
|
+
process.env.ENABLE_PIXEL = 'true';
|
|
115
|
+
window.fbq = jest.fn();
|
|
116
|
+
|
|
117
|
+
const { result } = renderHook(() =>
|
|
118
|
+
useCtaClickHandler({
|
|
119
|
+
pageTemplate: 'default',
|
|
120
|
+
tracker: 'main',
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
const dispatchEvent = jest.fn();
|
|
124
|
+
result.current.ctaRef.current = { dispatchEvent };
|
|
125
|
+
const event = {
|
|
126
|
+
isTrusted: true,
|
|
127
|
+
preventDefault: jest.fn(),
|
|
128
|
+
stopPropagation: jest.fn(),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
result.current.handleCtaClick(event);
|
|
132
|
+
jest.runAllTimers();
|
|
133
|
+
|
|
134
|
+
expect(setCookie).toHaveBeenCalledWith(
|
|
135
|
+
'affObject',
|
|
136
|
+
JSON.stringify({ page_type: 'default', request_url: 'http://localhost/' }),
|
|
137
|
+
1,
|
|
138
|
+
'/'
|
|
139
|
+
);
|
|
140
|
+
expect(window.fbq).toHaveBeenCalledWith('track', 'InitiateCheckout', {
|
|
141
|
+
keyID: 'generated-key',
|
|
142
|
+
});
|
|
143
|
+
expect(dispatchEvent).toHaveBeenCalledTimes(1);
|
|
144
|
+
});
|
|
145
|
+
});
|
package/src/resolver/index.mjs
CHANGED
|
@@ -627,24 +627,11 @@ export default {
|
|
|
627
627
|
checkForInactiveOperatorToplist(
|
|
628
628
|
item,
|
|
629
629
|
page.relation.status,
|
|
630
|
-
res
|
|
630
|
+
res,
|
|
631
|
+
page.relation.type
|
|
631
632
|
)
|
|
632
633
|
)
|
|
633
634
|
return;
|
|
634
|
-
|
|
635
|
-
if (item.name === "top_list") {
|
|
636
|
-
const shortCode = item?.items?.[0]?.value;
|
|
637
|
-
const isSportsbook =
|
|
638
|
-
page?.relation?.type === "sportsbook";
|
|
639
|
-
|
|
640
|
-
const shouldSkip =
|
|
641
|
-
(isSportsbook &&
|
|
642
|
-
shortCode === "inactive_operator_toplist") ||
|
|
643
|
-
(!isSportsbook &&
|
|
644
|
-
shortCode === "inactive_sportsbook_toplist");
|
|
645
|
-
|
|
646
|
-
if (shouldSkip) return;
|
|
647
|
-
}
|
|
648
635
|
|
|
649
636
|
//pre main games section will show only on inactive game page
|
|
650
637
|
const isActiveGamePage = page?.relation?.type === "game" || page?.relation?.status === "active";
|