Skip to content
This repository was archived by the owner on Oct 4, 2022. It is now read-only.

Commit 31b54c3

Browse files
authored
Merge pull request #819 from Yoast/12158-add-fragments-for-configuration-wizard
Add step-specific hashes to the URL in the OnboardingWizard
2 parents df888f1 + 06efff0 commit 31b54c3

File tree

2 files changed

+87
-4
lines changed

2 files changed

+87
-4
lines changed

composites/OnboardingWizard/OnboardingWizard.js

+86-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import interpolateComponents from "interpolate-components";
1313
import ArrowForwardIcon from "material-ui/svg-icons/navigation/arrow-forward";
1414
import ArrowBackwardIcon from "material-ui/svg-icons/navigation/arrow-back";
1515
import CloseIcon from "material-ui/svg-icons/navigation/close";
16-
16+
import isUndefined from "lodash/isUndefined";
1717

1818
/**
1919
* The OnboardingWizard class.
@@ -36,8 +36,22 @@ class OnboardingWizard extends React.Component {
3636
errorMessage: "",
3737
};
3838

39+
this.postStep = this.postStep.bind( this );
3940
this.setNextStep = this.setNextStep.bind( this );
4041
this.setPreviousStep = this.setPreviousStep.bind( this );
42+
this.listenToHashChange = this.listenToHashChange.bind( this );
43+
window.addEventListener( "hashchange", this.listenToHashChange, false );
44+
}
45+
46+
/**
47+
* Remove the prepended hashtag from the passed string.
48+
*
49+
* @param {string} stringWithHashtag The string to remove the prepended hashtag from.
50+
*
51+
* @returns {string} The string without prepended hashtag.
52+
*/
53+
removePrependedHashtag( stringWithHashtag ) {
54+
return stringWithHashtag.substring( 1 );
4155
}
4256

4357
/**
@@ -131,6 +145,13 @@ class OnboardingWizard extends React.Component {
131145
* @returns {Object} The first step object
132146
*/
133147
getFirstStep( steps ) {
148+
// Use the hash from the url without the hashtag.
149+
const firstStep = this.removePrependedHashtag( window.location.hash );
150+
151+
if ( firstStep !== "" ) {
152+
return firstStep;
153+
}
154+
// When window.location doesn't have a hash, use the first step of the wizard as default.
134155
return Object.getOwnPropertyNames( steps )[ 0 ];
135156
}
136157

@@ -204,7 +225,16 @@ class OnboardingWizard extends React.Component {
204225
* @returns {Object} The current step.
205226
*/
206227
getCurrentStep() {
207-
return this.state.steps[ this.state.currentStepId ];
228+
const steps = this.state.steps;
229+
const currentStep = steps[ this.state.currentStepId ];
230+
231+
// If the currentStep is undefined because the stepId is invalid, return the first step of the Wizard.
232+
if ( isUndefined( currentStep ) ) {
233+
const firstStepId = Object.keys( steps )[ 0 ];
234+
this.setState( { currentStepId: firstStepId } );
235+
return steps[ firstStepId ];
236+
}
237+
return currentStep;
208238
}
209239

210240
/**
@@ -259,6 +289,59 @@ class OnboardingWizard extends React.Component {
259289
return ( hideButton ) ? "" : <RaisedButton className={ className } { ...attributes } />;
260290
}
261291

292+
/**
293+
* Updates the currentStepId in the state when the hash in the URL changes.
294+
*
295+
* @returns {void}
296+
*/
297+
listenToHashChange() {
298+
// Because the hash starts with a hashtag, we need to do remove the hastag before using it.
299+
this.setState( { currentStepId: this.removePrependedHashtag( window.location.hash ) } );
300+
}
301+
302+
/**
303+
* When the currentStepId in the state changes, return a snapshot with the new currentStepId.
304+
*
305+
* @param {Object} prevProps The previous props.
306+
* @param {Object} prevState The previous state.
307+
*
308+
* @returns {string|null} The currentStepId from after the update.
309+
*/
310+
getSnapshotBeforeUpdate( prevProps, prevState ) {
311+
const currentStepIdAfterUpdate = this.state.currentStepId;
312+
// If there is no change in the currentStepId in the state, do nothing.
313+
if ( prevState.currentStepId === currentStepIdAfterUpdate ) {
314+
return null;
315+
}
316+
317+
// If the new currentStepId is the same as the current location hash, do nothing.
318+
if ( this.removePrependedHashtag( window.location.hash ) === currentStepIdAfterUpdate ) {
319+
return null;
320+
}
321+
322+
return currentStepIdAfterUpdate;
323+
}
324+
325+
/**
326+
* Push the new hash to the history.
327+
*
328+
* Only do this when the new hash differs from the current hash. If we wouldn't check against
329+
* the current hash, it would lead to double hashes when using the browser's previous and next
330+
* buttons.
331+
*
332+
* @param {Object} prevProps The previous props.
333+
* @param {Object} prevState The previous state.
334+
* @param {string} snapshot The currentStepId from after the update.
335+
*
336+
* @returns {void}
337+
*/
338+
componentDidUpdate( prevProps, prevState, snapshot ) {
339+
if ( snapshot !== null ) {
340+
window.history.pushState( null, null, "#" + snapshot );
341+
}
342+
}
343+
344+
262345
/**
263346
* Renders the wizard.
264347
*
@@ -302,7 +385,7 @@ class OnboardingWizard extends React.Component {
302385
<Header headerTitle={ headerTitle } icon={ this.props.headerIcon } />
303386
<StepIndicator
304387
steps={ this.props.steps } stepIndex={ this.getCurrentStepNumber() - 1 }
305-
onClick={ ( stepNumber, evt ) => this.postStep( stepNumber, evt ) }
388+
onClick={ this.postStep }
306389
/>
307390
<main className="yoast-wizard-container">
308391
<div className="yoast-wizard">

grunt/config/eslint.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module.exports = {
22
target: [ "<%= files.components %>" ],
33
options: {
4-
maxWarnings: 323,
4+
maxWarnings: 322,
55
},
66
};

0 commit comments

Comments
 (0)