Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GuidedTours: Add steps for site preview #5553

Merged
merged 1 commit into from
May 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/components/web-preview/toolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const PreviewToolbar = React.createClass( {
{ this.props.showClose &&
<button
className="web-preview__close"
data-tip-target="web-preview__close"
onClick={ this.props.onClose }
aria-label={ this.translate( 'Close preview' ) }
>
Expand Down
20 changes: 20 additions & 0 deletions client/layout/guided-tours/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import React from 'react';
/**
* Internal dependencies
*/
import config from 'config';
import i18n from 'lib/mixins/i18n';

function get() {
Expand Down Expand Up @@ -39,6 +40,25 @@ function get() {
type: 'BasicStep',
target: 'sidebar',
placement: 'beside',
next: config.isEnabled( 'preview-layout' ) ? 'preview' : 'themes',
},
preview: {
target: 'site-card-preview',
type: 'ActionStep',
placement: 'beside',
text: i18n.translate( '{{strong}}Preview:{{/strong}} Click here to see what your site looks like.', {
components: {
strong: <strong />,
}
} ),
next: 'close-preview',
},
'close-preview': {
target: 'web-preview__close',
type: 'ActionStep',
placement: 'beside',
icon: 'cross-small',
text: i18n.translate( 'Take a look at your site—and then close the site preview. You can come back here anytime.' ),
next: 'themes',
},
themes: {
Expand Down
34 changes: 31 additions & 3 deletions client/layout/guided-tours/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
*/
import React, { Component } from 'react';
import { connect } from 'react-redux';
import debugFactory from 'debug';

/**
* Internal dependencies
*/
import localize from 'lib/mixins/i18n/localize';
import { getSelectedSite } from 'state/ui/selectors';
import { getGuidedTourState } from 'state/ui/guided-tours/selectors';
import { nextGuidedTourStep, quitGuidedTour } from 'state/ui/guided-tours/actions';
import { errorNotice } from 'state/notices/actions';
import { query } from './positioning';
import {
BasicStep,
Expand All @@ -18,6 +21,9 @@ import {
FinishStep,
ActionStep,
} from './steps';
import wait from './wait';

const debug = debugFactory( 'calypso:guided-tours' );

class GuidedTours extends Component {
constructor() {
Expand Down Expand Up @@ -59,13 +65,34 @@ class GuidedTours extends Component {

next() {
const nextStepName = this.props.tourState.stepConfig.next;
this.props.nextGuidedTourStep( { stepName: nextStepName } );
const nextStepConfig = this.props.tourState.nextStepConfig;

const nextTargetFound = () => {
if ( nextStepConfig && nextStepConfig.target ) {
const target = this.getTipTargets()[nextStepConfig.target];
return target && target.getBoundingClientRect().left >= 0;
}
return true;
};
const proceedToNextStep = () => {
this.props.nextGuidedTourStep( { stepName: nextStepName } );
};
const abortTour = () => {
const ERROR_WAITED_TOO_LONG = 'waited too long for next target';
debug( ERROR_WAITED_TOO_LONG );
this.props.errorNotice(
this.props.translate( 'There was a problem with the tour — sorry!' ),
{ duration: 8000 }
);
this.quit( { error: ERROR_WAITED_TOO_LONG } );
};
wait( { condition: nextTargetFound, consequence: proceedToNextStep, onError: abortTour } );
}

quit( options = {} ) {
this.currentTarget && this.currentTarget.classList.remove( 'guided-tours__overlay' );
this.props.quitGuidedTour( Object.assign( {
stepName: this.props.tourState.stepName
stepName: this.props.tourState.stepName,
}, options ) );
}

Expand Down Expand Up @@ -107,4 +134,5 @@ export default connect( ( state ) => ( {
} ), {
nextGuidedTourStep,
quitGuidedTour,
} )( GuidedTours );
errorNotice,
} )( localize( GuidedTours ) );
18 changes: 12 additions & 6 deletions client/layout/guided-tours/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class ActionStep extends Component {
const { targetSlug = false, onNext } = this.props;
const target = targetForSlug( targetSlug );

if ( onNext && target.addEventListener ) {
if ( onNext && target && target.addEventListener ) {
target.addEventListener( 'click', onNext );
}
}
Expand All @@ -124,7 +124,7 @@ class ActionStep extends Component {
const { targetSlug = false, onNext } = this.props;
const target = targetForSlug( targetSlug );

if ( onNext && target.removeEventListener ) {
if ( onNext && target && target.removeEventListener ) {
target.removeEventListener( 'click', onNext );
}
}
Expand All @@ -137,14 +137,19 @@ class ActionStep extends Component {

const { text } = this.props;

let components = {};
if ( this.props.icon ) {
components.gridicon = <Gridicon icon={ this.props.icon } size={ 24 } />
} else {
components.gridicon = <span className="guided-tours__bullseye-text">○</span>
}

return (
<Card className="guided-tours__step" style={ stepCoords } >
<p className="guided-tours__step-text">{ text }</p>
<p className="guided-tours__bullseye-instructions">
{ this.props.translate( 'Click the {{gridicon/}} to continue…', {
components: {
gridicon: <Gridicon icon={ this.props.icon } size={ 24 } />
}
{ this.props.translate( 'Click the {{gridicon/}} to continue.', {
components: components
} ) }
</p>
<Pointer style={ pointerCoords } />
Expand Down Expand Up @@ -174,6 +179,7 @@ ActionStep.propTypes = {
PropTypes.string,
PropTypes.array
] ),
icon: PropTypes.string,
next: PropTypes.string,
onNext: PropTypes.func.isRequired,
onQuit: PropTypes.func.isRequired,
Expand Down
9 changes: 9 additions & 0 deletions client/layout/guided-tours/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,12 @@ $zoom-scale: 5; // the multiplier determining the size of the animated rings
.guided-tours__bullseye-ring:after {
animation-delay: #{ $animation-speed / 4 };
}

// the pure text representation of the bullseye dot
.guided-tours__bullseye-text {
position: relative;
top: 3px;
font-style: normal;
font-size: 190%;
line-height: 0;
}
29 changes: 29 additions & 0 deletions client/layout/guided-tours/wait.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* External dependencies
*/
import noop from 'lodash/noop';

const WAIT_INITIAL = 1; // initial wait in milliseconds
const WAIT_MULTIPLIER = 2;
const WAIT_MAX = 2048; // give up waiting when delay has grown to ~4 seconds

const wait = ( { condition, consequence, delay = 0, onError = noop } ) => {
if ( condition() ) {
consequence();
return;
}

if ( delay >= WAIT_MAX ) {
onError();
return;
}

window.setTimeout( wait.bind( null, {
condition,
consequence,
delay: delay ? delay * WAIT_MULTIPLIER : WAIT_INITIAL,
onError,
} ), delay );
};

export default wait;
1 change: 1 addition & 0 deletions client/my-sites/current-site/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ module.exports = React.createClass( {
externalLink={ true }
onClick={ this.previewSite }
onSelect={ this.previewSite }
tipTarget="site-card-preview"
ref="site" />
: <AllSites sites={ this.props.sites.get() } />
}
Expand Down
1 change: 1 addition & 0 deletions client/my-sites/site/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export default React.createClass( {
{ ! this.state.showMoreActions
? <a className="site__content"
href={ this.props.homeLink ? site.URL : this.props.href }
data-tip-target={ this.props.tipTarget }
target={ this.props.externalLink && ! this.state.showMoreActions && '_blank' }
title={ this.props.homeLink
? this.translate( 'View "%(title)s"', { args: { title: site.title } } )
Expand Down
3 changes: 2 additions & 1 deletion client/state/ui/guided-tours/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function showGuidedTour( { shouldShow, shouldDelay = false, tour = 'main'
return shouldDelay ? showAction : withAnalytics( trackEvent, showAction );
}

export function quitGuidedTour( { tour = 'main', stepName, finished } ) {
export function quitGuidedTour( { tour = 'main', stepName, finished, error } ) {
const quitAction = {
type: GUIDED_TOUR_UPDATE,
shouldShow: false,
Expand All @@ -49,6 +49,7 @@ export function quitGuidedTour( { tour = 'main', stepName, finished } ) {
step: stepName,
tour_version: guidedToursConfig.version,
tour,
error,
} );

return withAnalytics( trackEvent, quitAction );
Expand Down
15 changes: 13 additions & 2 deletions client/state/ui/guided-tours/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,21 @@ export const getGuidedTourState = createSelector(
const tourState = getRawGuidedTourState( state );
const { shouldReallyShow, stepName = '' } = tourState;
const stepConfig = getToursConfig()[ stepName ] || false;
const nextStepConfig = getToursConfig()[ stepConfig.next ] || false;

const shouldShow = !! (
! isSectionLoading( state ) &&
shouldReallyShow
);

return Object.assign( {}, tourState, {
stepConfig,
shouldShow: !!( ! isSectionLoading( state ) && shouldReallyShow ),
nextStepConfig,
shouldShow,
} );
},
state => [ getRawGuidedTourState( state ), isSectionLoading( state ) ]
state => [
getRawGuidedTourState( state ),
isSectionLoading( state ),
]
);
2 changes: 1 addition & 1 deletion client/state/ui/guided-tours/test/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe( 'selectors', () => {
}
} );

expect( tourState ).to.deep.equal( { shouldShow: false, stepConfig: false } );
expect( tourState ).to.deep.equal( { shouldShow: false, stepConfig: false, nextStepConfig: false } );
} );

it( 'should include the config of the current tour step', () => {
Expand Down