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

Yoast / wordpress-seo / f58711a939378578f715cfb5ab966e6d6a91bfa2

21 Nov 2024 12:49PM CUT coverage: 54.68% (-0.008%) from 54.688%
f58711a939378578f715cfb5ab966e6d6a91bfa2

Pull #21833

github

web-flow
Merge e68dfd5f1 into 6774f0888
Pull Request #21833: Fix admin notices in alert center

7594 of 13598 branches covered (55.85%)

Branch coverage included in aggregate %.

0 of 25 new or added lines in 4 files covered. (0.0%)

22 existing lines in 4 files now uncovered.

29746 of 54690 relevant lines covered (54.39%)

41965.17 hits per line

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

0.0
/packages/js/src/first-time-configuration/first-time-configuration-steps.js
1
import apiFetch from "@wordpress/api-fetch";
2
import { useDispatch } from "@wordpress/data";
3
import { useCallback, useReducer, useState, useEffect } from "@wordpress/element";
4
import { __ } from "@wordpress/i18n";
5
import { uniq } from "lodash";
6
import { STORE_NAME } from "../general/constants";
7
import { configurationReducer } from "./tailwind-components/helpers/index.js";
8
import SocialProfilesStep from "./tailwind-components/steps/social-profiles/social-profiles-step";
9
import Stepper, { Step } from "./tailwind-components/stepper";
10
import { ContinueButton, EditButton, ConfigurationStepButtons } from "./tailwind-components/configuration-stepper-buttons";
11
import { getInitialActiveStepIndex } from "./stepper-helper";
12
import IndexationStep from "./tailwind-components/steps/indexation/indexation-step";
13
import SiteRepresentationStep from "./tailwind-components/steps/site-representation/site-representation-step";
14
import PersonalPreferencesStep from "./tailwind-components/steps/personal-preferences/personal-preferences-step";
15
import FinishStep from "./tailwind-components/steps/finish/finish-step";
16
import { STEPS } from "./constants";
17

18
/* eslint-disable complexity */
19
/* eslint-disable react/jsx-no-bind */
20
/**
21
 * Updates the site representation in the database.
22
 *
23
 * @param {Object} state The state to save.
24
 *
25
 * @returns {Promise|boolean} A promise, or false if the call fails.
26
 */
27
async function updateSiteRepresentation( state ) {
28
        // Revert emptyChoice to the actual default: "company";
29
        const siteRepresentation = {
×
30
                /* eslint-disable camelcase */
31
                company_or_person: state.companyOrPerson === "emptyChoice" ? "company" : state.companyOrPerson,
×
32
                company_name: state.companyName,
33
                company_logo: state.companyLogo,
34
                company_logo_id: state.companyLogoId ? state.companyLogoId : 0,
×
35
                website_name: state.websiteName,
36
                person_logo: state.personLogo,
37
                person_logo_id: state.personLogoId ? state.personLogoId : 0,
×
38
                company_or_person_user_id: state.personId,
39
                /* eslint-enable camelcase */
40
        };
41

42
        const response = await apiFetch( {
×
43
                path: "yoast/v1/configuration/site_representation",
44
                method: "POST",
45
                data: siteRepresentation,
46
        } );
47
        return await response.json;
×
48
}
49

50
/**
51
 * Updates the social profiles in the database.
52
 *
53
 * @param {Object} state The state to save.
54
 *
55
 * @returns {Promise|boolean} A promise, or false if the call fails.
56
 */
57
async function updateSocialProfiles( state ) {
58
        const socialProfiles = {
×
59
                /* eslint-disable camelcase */
60
                facebook_site: state.socialProfiles.facebookUrl,
61
                twitter_site: state.socialProfiles.twitterUsername,
62
                other_social_urls: state.socialProfiles.otherSocialUrls,
63
                /* eslint-enable camelcase */
64
        };
65

66
        const response = await apiFetch( {
×
67
                path: "yoast/v1/configuration/social_profiles",
68
                method: "POST",
69
                data: socialProfiles,
70
        } );
71
        return await response.json;
×
72
}
73
/**
74
 * Updates the tracking option in the database.
75
 *
76
 * @param {Object} state The state to save.
77
 *
78
 * @returns {Promise|boolean} A promise, or false if the call fails.
79
 */
80
async function updateTracking( state ) {
81
        if ( state.tracking !== 0 && state.tracking !== 1 ) {
×
82
                throw "Value not set!";
×
83
        }
84

85
        const tracking = {
×
86
                tracking: state.tracking,
87
        };
88

89
        const response = await apiFetch( {
×
90
                path: "yoast/v1/configuration/enable_tracking",
91
                method: "POST",
92
                data: tracking,
93
        } );
94
        return await response.json;
×
95
}
96

97
/**
98
 * Saves the first time configuration finished steps in the database.
99
 *
100
 * @param {Array} finishedSteps Array of finished steps.
101
 *
102
 * @returns {Promise|boolean} A promise, or false if the call fails.
103
 */
104
async function saveFinishedSteps( finishedSteps ) {
105
        const response = await apiFetch( {
×
106
                path: "yoast/v1/configuration/save_configuration_state",
107
                method: "POST",
108
                data: { finishedSteps },
109
        } );
110
        return await response.json;
×
111
}
112

113
/**
114
 * Calculates the initial state from the window object.
115
 *
116
 * @param {Object}   windowObject   The object to base the initial state on.
117
 * @param {function} isStepFinished A function to determine whether a step is finished.
118
 *
119
 * @returns {Object} The initial state.
120
 */
121
function calculateInitialState( windowObject, isStepFinished ) {
122
        const {
123
                companyName,
124
                companyLogo,
125
                companyOrPersonOptions,
126
                shouldForceCompany,
127
                fallbackCompanyName,
128
                websiteName,
129
                fallbackWebsiteName,
130
        } = windowObject;
×
131
        let { companyOrPerson } = windowObject;
×
132
        if ( ( companyOrPerson === "company" && ( ! companyName && ! companyLogo ) && ! isStepFinished( STEPS.siteRepresentation ) ) || shouldForceCompany ) {
×
133
                // Set the stage for a prefilled step 2 in case the customer does seem to have consciously finished step 2 without setting data.
134
                companyOrPerson = "company";
×
135
        }
136

137
        return {
×
138
                ...windowObject,
139
                personId: Number( windowObject.personId ),
140
                personLogoId: Number( windowObject.personLogoId ),
141
                companyLogoId: Number( windowObject.companyLogoId ),
142
                tracking: Number( windowObject.tracking ),
143
                companyOrPerson,
144
                companyOrPersonOptions,
145
                errorFields: [],
146
                stepErrors: {},
147
                editedSteps: [],
148
                companyName: companyName || fallbackCompanyName,
×
149
                websiteName: websiteName || fallbackWebsiteName,
×
150
        };
151
}
152

153
/* eslint-enable max-len, react/prop-types */
154

155
/* eslint-disable max-statements */
156
/**
157
 * The first time configuration.
158
 *
159
 * @returns {WPElement} The FirstTimeConfigurationSteps component.
160
 */
161
export default function FirstTimeConfigurationSteps() {
NEW
162
        const { removeAlert, dismissNotice, restoreNotice } = useDispatch( STORE_NAME );
×
163
        const [ finishedSteps, setFinishedSteps ] = useState( window.wpseoFirstTimeConfigurationData.finishedSteps );
×
164

165
        const isStepFinished = useCallback( ( stepId ) => {
×
166
                return finishedSteps.includes( stepId );
×
167
        }, [ finishedSteps ] );
168

169
        const finishSteps = useCallback( ( stepId ) => {
×
170
                setFinishedSteps( prevState => uniq( [ ...prevState, stepId ] ) );
×
171
        }, [ setFinishedSteps ] );
172

173
        useEffect( () => {
×
174
                saveFinishedSteps( finishedSteps );
×
175
                window.wpseoFirstTimeConfigurationData.finishedSteps = finishedSteps;
×
176
        }, [ finishedSteps ] );
177

178
        const [ state, dispatch ] = useReducer( configurationReducer, {
×
179
                ...calculateInitialState( window.wpseoFirstTimeConfigurationData, isStepFinished ),
180
        } );
181
        const [ indexingState, setIndexingState ] = useState( () => window.yoastIndexingData.amount === "0" ? "already_done" : "idle" );
×
182
        const [ siteRepresentationEmpty, setSiteRepresentationEmpty ] = useState( false );
×
183
        const [ showRunIndexationAlert, setShowRunIndexationAlert ] = useState( false );
×
184

185
        const setStepError = useCallback( ( step, message ) => {
×
186
                dispatch( { type: "SET_STEP_ERROR", payload: { step, message } } );
×
187
        }, [] );
188

189
        const removeStepError = useCallback( ( step ) => {
×
190
                dispatch( { type: "REMOVE_STEP_ERROR", payload: step } );
×
191
        }, [] );
192

193
        /* Briefly override window variable and remove indexing notices, because indexingstate is reinitialized when navigating back and forth
194
        without triggering a reload, whereas the window variable remains stale. */
195
        useEffect( () => {
×
196
                if ( indexingState === "completed" ) {
×
197
                        removeAlert( "wpseo-reindex" );
×
198
                        window.yoastIndexingData.amount = "0";
×
199
                }
200
        }, [ indexingState, removeAlert ] );
201

202
        const isStep1Finished = isStepFinished( STEPS.optimizeSeoData );
×
203
        const isStep2Finished = isStepFinished( STEPS.siteRepresentation );
×
204
        const isStep3Finished = isStepFinished( STEPS.socialProfiles );
×
205
        const isStep4Finished = isStepFinished( STEPS.personalPreferences );
×
206

207
        const setTracking = useCallback( ( value ) => {
×
208
                dispatch( { type: "SET_TRACKING", payload: parseInt( value, 10 ) } );
×
209
        } );
210

211
        const setErrorFields = useCallback( ( value ) => {
×
212
                dispatch( { type: "SET_ERROR_FIELDS", payload: value } );
×
213
        } );
214

215
        const resolveLocalNotice = useCallback( () => {
×
216
                if ( state.companyLogo !== "" && state.companyLogoId !== 0 && state.companyName !== "" ) {
×
NEW
217
                        dismissNotice( "yoast-local-missing-organization-info-notice" );
×
218
                } else {
NEW
219
                        restoreNotice( "yoast-local-missing-organization-info-notice" );
×
220
                }
221
        }, [ dismissNotice, restoreNotice, state.companyLogo, state.companyLogoId, state.companyName ] );
222

223
        const resolveFTCNotice = useCallback( () => {
×
NEW
224
                dismissNotice( "yoast-first-time-configuration-notice" );
×
225
        }, [ dismissNotice ] );
226

227
        const isCompanyAndEmpty = state.companyOrPerson === "company" && ( ! state.companyName || ( ! state.companyLogo && ! state.companyLogoFallback ) || ! state.websiteName );
×
228
        const isPersonAndEmpty = state.companyOrPerson === "person" && ( ! state.personId || ( ! state.personLogo && ! state.personLogoFallback ) || ! state.websiteName );
×
229

230
        /**
231
         * Runs checks of finishing the site representation step.
232
         *
233
         * @returns {Boolean|Promise} Returns either a Boolean for success/failure or a Promise that will resolve into a Boolean.
234
         */
235
        function updateOnFinishSiteRepresentation() {
236
                if ( ! siteRepresentationEmpty && isCompanyAndEmpty ) {
×
237
                        setSiteRepresentationEmpty( true );
×
238
                        return false;
×
239
                } else if ( ! siteRepresentationEmpty && isPersonAndEmpty ) {
×
240
                        setSiteRepresentationEmpty( true );
×
241
                        return false;
×
242
                } else if ( ! siteRepresentationEmpty && state.companyOrPerson === "emptyChoice" ) {
×
243
                        setSiteRepresentationEmpty( true );
×
244
                        return false;
×
245
                }
246
                setSiteRepresentationEmpty( state.companyOrPerson === "emptyChoice" || isCompanyAndEmpty || isPersonAndEmpty );
×
247
                return updateSiteRepresentation( state )
×
248
                        .then( () => {
249
                                setErrorFields( [] );
×
250
                                removeStepError( STEPS.siteRepresentation );
×
251
                                finishSteps( STEPS.siteRepresentation );
×
252
                                window.wpseoFirstTimeConfigurationData = { ...window.wpseoFirstTimeConfigurationData,  ...state };
×
253

254
                                resolveLocalNotice();
×
255

256
                                return true;
×
257
                        } )
258
                        .catch( ( e ) => {
259
                                if ( e.failures ) {
×
260
                                        setErrorFields( e.failures );
×
261
                                        return false;
×
262
                                }
263
                                if ( e.message ) {
×
264
                                        setStepError( STEPS.siteRepresentation, e.message );
×
265
                                }
266
                                return false;
×
267
                        } );
268
        }
269

270
        /**
271
         * Runs checks of finishing the social profiles step.
272
         *
273
         * @returns {Promise|boolean} Returns either a Boolean for success/failure or a Promise.
274
         */
275
        function updateOnFinishSocialProfiles() {
276
                if ( state.companyOrPerson === "person" ) {
×
277
                        finishSteps( STEPS.socialProfiles );
×
278
                        return true;
×
279
                }
280

281
                return updateSocialProfiles( state )
×
282
                        .then( ( response ) => {
283
                                if ( response.success === false ) {
×
284
                                        setErrorFields( response.failures );
×
285
                                        return Promise.reject( "There were errors saving social profiles" );
×
286
                                }
287
                                return response;
×
288
                        } )
289
                        .then( () => {
290
                                setErrorFields( [] );
×
291
                                removeStepError( STEPS.socialProfiles );
×
292
                                finishSteps( STEPS.socialProfiles );
×
293
                        } )
294
                        .then( () => {
295
                                window.wpseoFirstTimeConfigurationData.socialProfiles = state.socialProfiles;
×
296
                                return true;
×
297
                        } )
298
                        .catch(
299
                                ( e ) => {
300
                                        if ( e.failures ) {
×
301
                                                setErrorFields( e.failures );
×
302
                                        }
303
                                        if ( e.message ) {
×
304
                                                setStepError( STEPS.socialProfiles, e.message );
×
305
                                        }
306
                                        return false;
×
307
                                }
308
                        );
309
        }
310

311
        /**
312
         * Runs checks of finishing the enable tracking step.
313
         *
314
         * @returns {Promise|boolean} Returns either a Boolean for success/failure or a Promise.
315
         */
316
        function updateOnFinishPersonalPreferences() {
317
                return updateTracking( state )
×
318
                        .then( () => finishSteps( STEPS.personalPreferences ) )
×
319
                        .then( () => {
320
                                removeStepError( STEPS.personalPreferences );
×
321
                                window.wpseoFirstTimeConfigurationData.tracking = state.tracking;
×
322

323
                                resolveFTCNotice();
×
324

325
                                return true;
×
326
                        } )
327
                        .catch( e => {
328
                                if ( e.message ) {
×
329
                                        setStepError( STEPS.personalPreferences, e.message );
×
330
                                }
331
                                return false;
×
332
                        } );
333
        }
334

335
        const onOrganizationOrPersonChange = useCallback(
×
336
                ( value ) => dispatch( { type: "SET_COMPANY_OR_PERSON", payload: value } ),
×
337
                [ dispatch ]
338
        );
339

340
        const isStepperFinished = [
×
341
                isStep1Finished,
342
                isStep2Finished,
343
                isStep3Finished,
344
                isStep4Finished,
345
        ].every( Boolean );
346

347
        /* Duplicate site representation, because in reality, the first step cannot be saved.
348
        It's considered "finished" once at least the site representation has been done. */
349
        const savedSteps = [
×
350
                isStepFinished( STEPS.optimizeSeoData ),
351
                isStepFinished( STEPS.siteRepresentation ),
352
                isStepFinished( STEPS.socialProfiles ),
353
                isStepFinished( STEPS.personalPreferences ),
354
                isStepperFinished,
355
        ];
356

357
        const [ activeStepIndex, setActiveStepIndex ] = useState( getInitialActiveStepIndex( savedSteps ) );
×
358

359
        const [ stepperFinishedOnce, setStepperFinishedOnce ] = useState( isStepperFinished );
×
360
        const [ isStepBeingEdited, setIsStepBeingEdited ] = useState( false );
×
361
        const [ showEditButton, setShowEditButton ] = useState( stepperFinishedOnce && ! isStepBeingEdited );
×
362

363
        /**
364
         * Save and continue functionality for the Indexation step.
365
         *
366
         * @returns {boolean} Whether the stepper can continue to the next step.
367
         */
368
        function beforeContinueIndexationStep() {
369
                // When: not already showing the alert AND indexation state is "idle" (not yet interacted with) AND indexation is not disabled.
370
                if ( ! showRunIndexationAlert && indexingState === "idle" && window.yoastIndexingData.disabled !== "1" ) {
×
371
                        // Then: show an alert to notify users that indexation is helpful.
372
                        setShowRunIndexationAlert( true );
×
373
                        return false;
×
374
                }
375

376
                setIsStepBeingEdited( false );
×
377
                finishSteps( STEPS.optimizeSeoData );
×
378
                return true;
×
379
        }
380

381
        /**
382
         * Steps to take before editing.
383
         *
384
         * @returns {boolean} Always returns true to satisfy button needs.
385
         */
386
        function beforeEditing() {
387
                setShowEditButton( false );
×
388
                setIsStepBeingEdited( true );
×
389
                return true;
×
390
        }
391

392
        // The first time isStepperFinished is true, set stepperFinishedOnce to true.
393
        useEffect( () => {
×
394
                if ( isStepperFinished ) {
×
395
                        setStepperFinishedOnce( true );
×
396
                }
397
        }, [ isStepperFinished ] );
398

399
        // If stepperFinishedOnce changes or isStepBeingEdited changes, evaluate edit button state.
400
        useEffect( () => {
×
401
                setShowEditButton( stepperFinishedOnce && ! isStepBeingEdited );
×
402
        }, [ stepperFinishedOnce, isStepBeingEdited ] );
403

404
        /* eslint-disable max-len */
405
        useEffect( () => {
×
406
                /**
407
                 * Prevents the submission of the form upon pressing enter.
408
                 *
409
                 * @param {KeyboardEvent} event The keydown event this function is listening to.
410
                 *
411
                 * @returns {void}
412
                 */
413
                function preventEnterSubmit( event ) {
414
                        if ( event.key === "Enter" && document.querySelector( ".nav-tab.nav-tab-active" ).id === "first-time-configuration-tab" && event.target.tagName === "INPUT" ) {
×
415
                                event.preventDefault();
×
416
                        }
417
                }
418

419
                addEventListener( "keydown", preventEnterSubmit );
×
420
                return () => removeEventListener( "keydown", preventEnterSubmit );
×
421
        }, [] );
422

423
        // Used by admin.js to decide whether to show the confirmation dialog when user switches tabs in General.
424
        useEffect( () => {
×
425
                if ( state.editedSteps.includes( activeStepIndex + 1 ) || indexingState === "in_progress" ) {
×
426
                        window.isStepBeingEdited = true;
×
427
                } else {
428
                        window.isStepBeingEdited = false;
×
429
                }
430
        }, [ state.editedSteps, indexingState, activeStepIndex ] );
431

432
        /**
433
         * Handles the "before page unloads" event.
434
         *
435
         * @param {Window} event The "before page unloads" event.
436
         *
437
         * @returns {void}
438
         */
439
        const beforeUnloadEventHandler = useCallback( ( event ) => {
×
440
                /* Show the pop-up modal if the user wants to leave the first time configuration if:
441
                 - the current step is being edited but not saved, or
442
                 - the indexation process is still in progress
443
                 */
444
                if ( state.editedSteps.includes( activeStepIndex + 1 ) || indexingState === "in_progress" ) {
×
445
                        // Show the pup-up modal only if the user is in the first time configuration tab
446
                        if ( location.href.indexOf( "page=wpseo_dashboard#top#first-time-configuration" ) !== -1 || location.href.indexOf( "page=wpseo_dashboard#/first-time-configuration" ) !== -1 ) {
×
447
                                event.preventDefault();
×
448
                                event.returnValue = "";
×
449
                        }
450
                }
451
        }, [ state.editedSteps, indexingState, activeStepIndex ] );
452

453
        useEffect( () => {
×
454
                window.addEventListener( "beforeunload", beforeUnloadEventHandler );
×
455

456
                return () => {
×
457
                        window.removeEventListener( "beforeunload", beforeUnloadEventHandler );
×
458
                };
459
        }, [ beforeUnloadEventHandler ] );
460

461
        return (
×
462
                <Stepper
463
                        setActiveStepIndex={ setActiveStepIndex }
464
                        activeStepIndex={ activeStepIndex }
465
                        isStepperFinished={ isStepperFinished }
466
                >
467
                        <Step>
468
                                <Step.Header
469
                                        name={ __( "SEO data optimization", "wordpress-seo" ) }
470
                                        isFinished={ isStep1Finished }
471
                                >
472
                                        <EditButton
473
                                                stepId={ STEPS.optimizeSeoData }
474
                                                beforeGo={ beforeEditing }
475
                                                isVisible={ showEditButton }
476
                                                additionalClasses={ "yst-ml-auto" }
477
                                        >
478
                                                { __( "Edit", "wordpress-seo" ) }
479
                                        </EditButton>
480
                                </Step.Header>
481
                                <Step.Content>
482
                                        <IndexationStep
483
                                                setIndexingState={ setIndexingState } indexingState={ indexingState }
484
                                                showRunIndexationAlert={ showRunIndexationAlert } isStepperFinished={ isStepperFinished }
485
                                        />
486
                                        <ContinueButton
487
                                                stepId={ STEPS.optimizeSeoData }
488
                                                additionalClasses="yst-mt-12"
489
                                                beforeGo={ beforeContinueIndexationStep }
490
                                                destination={ stepperFinishedOnce ? "last" : 1 }
×
491
                                        >
492
                                                { __( "Continue", "wordpress-seo" ) }
493
                                        </ContinueButton>
494
                                </Step.Content>
495
                        </Step>
496
                        <Step>
497
                                <Step.Header
498
                                        name={ __( "Site representation", "wordpress-seo" ) }
499
                                        isFinished={ isStep2Finished }
500
                                >
501
                                        <EditButton
502
                                                stepId={ STEPS.siteRepresentation }
503
                                                beforeGo={ beforeEditing }
504
                                                isVisible={ showEditButton }
505
                                                additionalClasses={ "yst-ml-auto" }
506
                                        >
507
                                                { __( "Edit", "wordpress-seo" ) }
508
                                        </EditButton>
509
                                </Step.Header>
510
                                <Step.Content>
511
                                        <SiteRepresentationStep
512
                                                onOrganizationOrPersonChange={ onOrganizationOrPersonChange }
513
                                                dispatch={ dispatch }
514
                                                state={ state }
515
                                                siteRepresentationEmpty={ siteRepresentationEmpty }
516
                                        />
517
                                        <Step.Error id="yoast-site-representation-step-error" message={ state.stepErrors[ STEPS.siteRepresentation ] || "" } />
×
518
                                        <ConfigurationStepButtons
519
                                                stepId={ STEPS.siteRepresentation }
520
                                                stepperFinishedOnce={ stepperFinishedOnce }
521
                                                saveFunction={ updateOnFinishSiteRepresentation }
522
                                                setEditState={ setIsStepBeingEdited }
523
                                        />
524
                                </Step.Content>
525
                        </Step>
526
                        <Step>
527
                                <Step.Header
528
                                        name={ __( "Social profiles", "wordpress-seo" ) }
529
                                        isFinished={ isStep3Finished }
530
                                >
531
                                        <EditButton
532
                                                stepId={ STEPS.socialProfiles }
533
                                                beforeGo={ beforeEditing }
534
                                                isVisible={ showEditButton }
535
                                                additionalClasses={ "yst-ml-auto" }
536
                                        >
537
                                                { __( "Edit", "wordpress-seo" ) }
538
                                        </EditButton>
539
                                </Step.Header>
540
                                <Step.Content>
541
                                        <SocialProfilesStep state={ state } dispatch={ dispatch } setErrorFields={ setErrorFields } />
542
                                        <Step.Error id="yoast-social-profiles-step-error" message={ state.stepErrors[ STEPS.socialProfiles ] || "" } />
×
543
                                        <ConfigurationStepButtons
544
                                                stepId={ STEPS.socialProfiles }
545
                                                stepperFinishedOnce={ stepperFinishedOnce }
546
                                                saveFunction={ updateOnFinishSocialProfiles }
547
                                                setEditState={ setIsStepBeingEdited }
548
                                        />
549
                                </Step.Content>
550
                        </Step>
551
                        <Step>
552
                                <Step.Header
553
                                        name={ __( "Personal preferences", "wordpress-seo" ) }
554
                                        isFinished={ isStep4Finished }
555
                                >
556
                                        <EditButton
557
                                                stepId={ STEPS.personalPreferences }
558
                                                beforeGo={ beforeEditing }
559
                                                isVisible={ showEditButton }
560
                                                additionalClasses={ "yst-ml-auto" }
561
                                        >
562
                                                { __( "Edit", "wordpress-seo" ) }
563
                                        </EditButton>
564
                                </Step.Header>
565
                                <Step.Content>
566
                                        <PersonalPreferencesStep state={ state } setTracking={ setTracking } />
567
                                        <Step.Error id="yoast-personal-preferences-step-error" message={ state.stepErrors[ STEPS.personalPreferences ] || "" } />
×
568
                                        <ConfigurationStepButtons
569
                                                stepId={ STEPS.personalPreferences }
570
                                                stepperFinishedOnce={ stepperFinishedOnce }
571
                                                saveFunction={ updateOnFinishPersonalPreferences }
572
                                                setEditState={ setIsStepBeingEdited }
573
                                        />
574
                                </Step.Content>
575
                        </Step>
576
                        <Step>
577
                                <Step.Header
578
                                        name={ __( "Finish configuration", "wordpress-seo" ) }
579
                                        isFinished={ isStepperFinished }
580
                                />
581
                                <Step.Content>
582
                                        <FinishStep />
583
                                </Step.Content>
584
                        </Step>
585
                </Stepper>
586
        );
587
}
588

589
/* eslint-enable max-len */
590
/* eslint-enable complexity */
591
/* eslint-enable react/jsx-no-bind */
592
/* eslint-enable max-statements */
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