• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / 41e0a1d1f695fd44d9952bb2417fefb0e94d089e

13 Dec 2024 05:03PM UTC coverage: 54.386%. Remained the same
41e0a1d1f695fd44d9952bb2417fefb0e94d089e

Pull #21918

github

web-flow
Merge 2072c8ea7 into b602854da
Pull Request #21918: Upgrade eslint to 9

7678 of 13701 branches covered (56.04%)

Branch coverage included in aggregate %.

4 of 10 new or added lines in 10 files covered. (40.0%)

4 existing lines in 4 files now uncovered.

29938 of 55464 relevant lines covered (53.98%)

41379.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/packages/js/src/help-scout-beacon.js
1
import { render, useState, Fragment } from "@wordpress/element";
2
import styled, { createGlobalStyle } from "styled-components";
3
import { __ } from "@wordpress/i18n";
4

5
const BeaconOffset = createGlobalStyle`
×
6
        @media only screen and (min-width: 1024px) {
7
                .BeaconFabButtonFrame.BeaconFabButtonFrame {
8
                        ${ ( props => props.isRtl === "1" ? "left" : "right" ) }: 340px !important;
×
9
                }
10
        }
11
`;
12

13
/**
14
 * Render a component in a newly created div.
15
 *
16
 * @param {wp.Component} component The component to render.
17
 *
18
 * @returns {void}
19
 */
20
function renderComponent( component ) {
21
        const element = document.createElement( "div" );
×
22
        element.setAttribute( "id", "yoast-helpscout-beacon" );
×
23

24
        render( component, element );
×
25

26
        document.body.appendChild( element );
×
27
}
28

29
/**
30
 * Checks whether the current page contains upsells.
31
 *
32
 * @returns {boolean} Whether the current page contains upsells.
33
 */
34
function pageHasUpsells() {
35
        return !! document.getElementById( "sidebar" );
×
36
}
37

38
/**
39
 * Loads the session data for the current session.
40
 *
41
 * @param {string} sessionData Optional. JSON encoded session data to pass to the HelpScout Beacon.
42
 *
43
 * @returns {void}
44
 */
45
function loadHelpScoutSessionData( sessionData ) {
46
        if ( sessionData !== "" ) {
×
47
                sessionData = JSON.parse( sessionData );
×
48
                if ( typeof sessionData.name !== "undefined" && typeof sessionData.email !== "undefined" ) {
×
49
                        // eslint-disable-next-line new-cap
50
                        window.Beacon( "prefill", {
×
51
                                name: sessionData.name,
52
                                email: sessionData.email,
53
                        } );
54

55
                        delete sessionData.name;
×
56
                        delete sessionData.email;
×
57
                }
58

59
                // eslint-disable-next-line new-cap
60
                window.Beacon( "session-data", sessionData );
×
61
        }
62
}
63

64
/**
65
 * Loads the HelpScout Beacon script.
66
 *
67
 * @param {string} beaconId    The ID to pass to the HelpScout Beacon.
68
 * @param {string} sessionData Optional. JSON encoded session data to pass to the HelpScout Beacon.
69
 *
70
 * @returns {void}
71
 */
72
function loadHelpScout( beaconId, sessionData = "" ) {
×
73
        // This IIFE is directly from HelpScout to insert their beacon.
74
        ( function( window, document ) {
×
75
                let beacon = window.Beacon || function() {};
×
76

77
                /**
78
                 * Inserts the HelpScout beacon script.
79
                 *
80
                 * @returns {void}
81
                 */
82
                function insertScript() {
83
                        const domScriptElement = document.getElementsByTagName( "script" )[ 0 ];
×
84
                        const scriptElement = document.createElement( "script" );
×
85

86
                        scriptElement.type = "text/javascript";
×
87
                        scriptElement.async = true;
×
88
                        scriptElement.src = "https://beacon-v2.helpscout.net";
×
89
                        domScriptElement.parentNode.insertBefore( scriptElement, domScriptElement );
×
90
                }
91

92
                if ( window.Beacon = beacon = function( method, options, data ) {
×
93
                        window.Beacon.readyQueue.push( { method: method, options: options, data: data } );
×
94
                }, beacon.readyQueue = [], "complete" === document.readyState ) {
95
                        return insertScript();
×
96
                }
97

98
                if ( window.attachEvent ) {
×
99
                        window.attachEvent( "onload", insertScript );
×
100
                } else {
101
                        window.addEventListener( "load", insertScript, false );
×
102
                }
103
        }( window, document, window.Beacon || function() {} ) );
×
104

105
        // eslint-disable-next-line new-cap
106
        window.Beacon( "init", beaconId );
×
107
        loadHelpScoutSessionData( sessionData );
×
108

109
        if ( window.wpseoAdminGlobalL10n.isRtl === "1" ) {
×
110
                // eslint-disable-next-line new-cap
111
                window.Beacon( "config", { display: { position: "left" } } );
×
112
        }
113

114
        if ( pageHasUpsells() ) {
×
115
                renderComponent( <BeaconOffset isRtl={ window.wpseoAdminGlobalL10n.isRtl } /> );
×
116
        }
117
}
118

119
/**
120
 * Loads a button that, when clicked, asks the user's consent before possibly loading in the HelpScout beacon.
121
 *
122
 * @param {string} beaconId    The ID to pass to the HelpScout Beacon.
123
 * @param {string} sessionData Optional. JSON encoded session data to pass to the HelpScout Beacon.
124
 *
125
 * @returns {void}
126
 */
127
function loadHelpScoutConsent( beaconId, sessionData = null ) {
×
128
        const Frame = styled.div`
×
129
                border-radius: 60px;
130
                height: 60px;
131
                position: fixed;
132
                transform: scale(1);
133
                width: 60px;
134
                z-index: 1049;
135
                bottom: 40px;
136
                box-shadow: rgba(0, 0, 0, 0.1) 0 4px 7px;
137
                ${ ( props => props.isRtl === "1" ? "left" : "right" ) }: 40px;
×
138
                top: auto;
139
                border-width: initial;
140
                border-style: none;
141
                border-color: initial;
142
                border-image: initial;
143
                transition: box-shadow 250ms ease 0s, opacity 0.4s ease 0s, scale 1000ms ease-in-out 0s, transform 0.2s ease-in-out 0s;
144
        `;
145

146
        const SvgContainer = styled.span`
×
147
                -webkit-box-align: center;
148
                align-items: center;
149
                color: white;
150
                cursor: pointer;
151
                display: flex;
152
                height: 100%;
153
                -webkit-box-pack: center;
154
                justify-content: center;
155
                left: 0;
156
                pointer-events: none;
157
                position: absolute;
158
                text-indent: -99999px;
159
                top: 0;
160
                width: 60px;
161
                will-change: opacity, transform;
162
                opacity: 1 !important;
163
                transform: rotate(0deg) scale(1) !important;
164
                transition: opacity 80ms linear 0s, transform 160ms linear 0s;
165
        `;
166

167
        /**
168
         * Initializes the SpeechBubble component.
169
         *
170
         * @constructor
171
         *
172
         * @returns {wp.Element} A SpeechBubble element.
173
         */
174
        const SpeechBubble = () => {
×
175
                return (
×
176
                        <SvgContainer>
177
                                <svg xmlns="http://www.w3.org/2000/svg" width="52" height="52">
178
                                        <path d="M27.031 32h-2.488v-2.046c0-.635.077-1.21.232-1.72.154-.513.366-.972.639-1.381.272-.41.58-.779.923-1.109.345-.328.694-.652 1.049-.97l.995-.854a6.432 6.432 0 0 0 1.475-1.568c.39-.59.585-1.329.585-2.216 0-.635-.117-1.203-.355-1.703a3.7 3.7 0 0 0-.96-1.263 4.305 4.305 0 0 0-1.401-.783A5.324 5.324 0 0 0 26 16.114c-1.28 0-2.316.375-3.11 1.124-.795.75-1.286 1.705-1.475 2.865L19 19.693c.356-1.772 1.166-3.165 2.434-4.176C22.701 14.507 24.26 14 26.107 14c.947 0 1.842.131 2.682.392.84.262 1.57.648 2.185 1.16a5.652 5.652 0 0 1 1.475 1.892c.368.75.551 1.602.551 2.556 0 .728-.083 1.364-.248 1.909a5.315 5.315 0 0 1-.693 1.467 6.276 6.276 0 0 1-1.048 1.176c-.403.351-.83.71-1.28 1.073-.498.387-.918.738-1.26 1.057a4.698 4.698 0 0 0-.836 1.006 3.847 3.847 0 0 0-.462 1.176c-.095.432-.142.955-.142 1.568V32zM26 37a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z" fill="#FFF" />
179
                                </svg>
180
                        </SvgContainer>
181
                );
182
        };
183

184
        const Button = styled.button`
×
185
                -webkit-appearance: none;
186
                -webkit-box-align: center;
187
                align-items: center;
188
                bottom: 0;
189
                display: block;
190
                height: 60px;
191
                -webkit-box-pack: center;
192
                justify-content: center;
193
                line-height: 60px;
194
                position: relative;
195
                user-select: none;
196
                z-index: 899;
197
                background-color: rgb(164, 40, 106);
198
                color: white;
199
                cursor: pointer;
200
                min-width: 60px;
201
                -webkit-tap-highlight-color: transparent;
202
                border-radius: 200px;
203
                margin: 0;
204
                outline: none;
205
                padding: 0;
206
                border-width: initial;
207
                border-style: none;
208
                border-color: initial;
209
                border-image: initial;
210
                transition: background-color 200ms linear 0s, transform 200ms linear 0s;
211
        `;
212

213
        /**
214
         * Initializes the HelpScoutBeaconAskConsentButton component.
215
         *
216
         * @constructor
217
         *
218
         * @returns {wp.Element} A HelpScoutBeaconAskConsentButton element.
219
         */
220
        const HelpScoutBeaconAskConsentButton = () => {
×
221
                const [ show, setShow ] = useState( true );
×
222
                const hasUpsells = pageHasUpsells();
×
223

224
                /**
225
                 * Loads HelpScout beacon and then disables the ask consent button.
226
                 *
227
                 * @returns {void}
228
                 */
229
                function onClick() {
230
                        const askConsentText = __(
×
231
                                "When you click OK we will open our HelpScout beacon where you can find answers to your questions. This beacon will load our support data and also potentially set cookies.",
232
                                "wordpress-seo"
233
                        );
234

235
                        // eslint-disable-next-line no-alert
236
                        if ( window.confirm( askConsentText ) ) {
×
UNCOV
237
                                loadHelpScout( beaconId, sessionData );
×
238
                                // eslint-disable-next-line new-cap
239
                                window.Beacon( "open" );
×
240

241
                                // Hide the consent asking button only after the HelpScout button is visible. There is no callback for that though.
242
                                window.setTimeout( () => {
×
243
                                        setShow( false );
×
244
                                }, 1000 );
245
                        }
246
                }
247

248
                return (
×
249
                        <Fragment>
250
                                { hasUpsells && <BeaconOffset isRtl={ window.wpseoAdminGlobalL10n.isRtl } /> }
×
251
                                { show && <Frame className={ hasUpsells ? "BeaconFabButtonFrame" : "" } isRtl={ window.wpseoAdminGlobalL10n.isRtl }>
×
252
                                        <Button type="button" onClick={ onClick }>
253
                                                <SpeechBubble />
254
                                        </Button>
255
                                </Frame> }
256
                        </Fragment>
257
                );
258
        };
259

260
        renderComponent( <HelpScoutBeaconAskConsentButton /> );
×
261
}
262

263
window.wpseoHelpScoutBeacon = loadHelpScout;
×
264
window.wpseoHelpScoutBeaconConsent = loadHelpScoutConsent;
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc