Skip to content

Commit

Permalink
Guided Tours: Add Site Title tour (#8627)
Browse files Browse the repository at this point in the history
* Initial implementation of Site Title tour

* Lint fixes

* wip: got it to "work" by removing a few things

... but it still only appears after a few seconds and / or a bit of scrolling and / or clicking around.

* debug: are we "waiting" somewhere ...?

* Update tour steps

* fix last step

* Implemented selectedSiteHasDefaultSiteTitle

* copy tweaks

* add user age selector

* tour start conditions, add config option for site-title tour

* Editorial copy changes

* hotfix arrow for settings step

* framework: fix positioning for xAboveBelow

* add tagline step

* make actionstep work with & without gridicon present

* add userCanEditSettingsOfSelectedSite selector

* add new condition for site title tour

* use translated site title for comparsions

* small css tweak

* add a/b test for tour

* remove specific styling and let framework handle it

* remove dev debugging statements

* removed extraneous selector for a/b

* declare internal/external dependencies

* copy updates

* rewrite user age selectors

* use better selector naming convention

* fix for failing test

* use /stats as a path

* disable a/b test by default

* only laod tour on desktop for now

* sort imports

* code style improvements

* fixed indentation

* add check for parsed date being valid

* remove translate calls

* add explanation to hasDefaultSiteTitle

* use startsWith from lodash

* make sure there's whitespace after the `strong` part in the last step

* update a/b test date and tour version
  • Loading branch information
marekhrabe authored Dec 7, 2016
1 parent d629ffc commit a3803e0
Show file tree
Hide file tree
Showing 12 changed files with 206 additions and 24 deletions.
2 changes: 2 additions & 0 deletions client/layout/guided-tours/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { combineTours } from 'layout/guided-tours/config-elements';
import { MainTour } from 'layout/guided-tours/main-tour';
import { DesignShowcaseWelcomeTour } from 'layout/guided-tours/design-showcase-welcome-tour';
import { ThemeSheetWelcomeTour } from 'layout/guided-tours/theme-sheet-welcome-tour';
import { SiteTitleTour } from 'layout/guided-tours/site-title-tour';

export default combineTours( {
main: MainTour,
designShowcaseWelcome: DesignShowcaseWelcomeTour,
themeSheetWelcomeTour: ThemeSheetWelcomeTour,
siteTitle: SiteTitleTour,
} );
8 changes: 4 additions & 4 deletions client/layout/guided-tours/positioning.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,25 @@ const helpers = {
yBelow: ( bottom ) => {
return bottom + DIALOG_PADDING;
},
xAboveBelow: ( left, right ) => {
xAboveBelow: ( left, right, width ) => {
if ( ( left + DIALOG_WIDTH + DIALOG_PADDING ) < document.documentElement.clientWidth ) {
return left + DIALOG_PADDING;
} else if ( right - DIALOG_WIDTH - DIALOG_PADDING > 0 ) {
return right - DIALOG_WIDTH - DIALOG_PADDING;
return right - ( DIALOG_WIDTH - width );
}
return DIALOG_PADDING;
},
};

const dialogPositioners = {
below: ( rect ) => {
const x = helpers.xAboveBelow( rect.left, rect.right );
const x = helpers.xAboveBelow( rect.left, rect.right, rect.width );
const y = helpers.yBelow( rect.bottom );

return { x, y };
},
above: ( rect ) => {
const x = helpers.xAboveBelow( rect.left, rect.right );
const x = helpers.xAboveBelow( rect.left, rect.right, rect.width );
const y = helpers.yAbove( rect.top );

return { x, y };
Expand Down
122 changes: 122 additions & 0 deletions client/layout/guided-tours/site-title-tour.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* External dependencies
*/
import React from 'react';
import { translate } from 'i18n-calypso';
import { overEvery as and } from 'lodash';

/**
* Internal dependencies
*/
import {
ButtonRow,
Continue,
Link,
makeTour,
Next,
Quit,
Step,
Tour,
} from 'layout/guided-tours/config-elements';
import {
hasSelectedSiteDefaultSiteTitle,
isUserOlderThan,
isEnabled,
canUserEditSettingsOfSelectedSite,
isAbTestInVariant,
} from 'state/ui/guided-tours/contexts';
import Gridicon from 'components/gridicon';
import { isDesktop } from 'lib/viewport';

const TWO_DAYS_IN_MILLISECONDS = 2 * 1000 * 3600 * 24;

export const SiteTitleTour = makeTour(
<Tour
name="siteTitle"
version="20161207"
path="/stats"
when={ and(
isEnabled( 'guided-tours/site-title' ),
isDesktop,
hasSelectedSiteDefaultSiteTitle,
canUserEditSettingsOfSelectedSite,
isUserOlderThan( TWO_DAYS_IN_MILLISECONDS ),
isAbTestInVariant( 'siteTitleTour', 'enabled' )
) }
>
<Step name="init" placement="right" next="click-settings">
<p>
Hey there! We noticed you haven't changed the title of your site yet.
Want to change it?
</p>
<ButtonRow>
<Next step="click-settings">Yes, please!</Next>
<Quit>No thanks</Quit>
</ButtonRow>
</Step>

<Step name="click-settings"
target="settings"
arrow="left-top"
placement="beside"
scrollContainer=".sidebar__region"
shouldScrollTo
>
<Continue target="settings" step="site-title-input" click>
Click <strong><Gridicon icon="cog" size={ 24 } /> Settings</strong> to continue.
</Continue>
</Step>

<Step name="site-title-input"
target="site-title-input"
arrow="top-left"
placement="below"
>
<p>
You can change the site title here. The site title appears in places
like the top of your web browser and in search results.
</p>
<ButtonRow>
<Next step="site-tagline-input">Looks Good!</Next>
<Quit>Cancel</Quit>
</ButtonRow>
</Step>

<Step name="site-tagline-input"
target="site-tagline-input"
arrow="top-left"
placement="below"
>
<p>
This is the tagline of your site. It should explain what your site
is about in few words. It usually appears right bellow your site title.
</p>
<ButtonRow>
<Next step="click-save">Great!</Next>
<Quit>Cancel</Quit>
</ButtonRow>
</Step>

<Step name="click-save"
target="settings-site-profile-save"
arrow="top-right"
placement="below"
>
<Continue target="settings-site-profile-save" step="finish" click>
Don't forget to save your changes.
</Continue>
</Step>

<Step name="finish" placement="center">
<p>
<strong>That's it!</strong> Your visitors can now easily identify your website by its title.
</p>
<ButtonRow>
<Quit primary>{ translate( "We're all done!" ) }</Quit>
</ButtonRow>
<Link href="https://learn.wordpress.com">
{ translate( 'Learn more about WordPress.com' ) }
</Link>
</Step>
</Tour>
);
14 changes: 8 additions & 6 deletions client/layout/guided-tours/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
strong {
color: $white;
}

&:last-child {
margin-bottom: 0;
}
}

.gridicon[height="16"] {
Expand Down Expand Up @@ -100,10 +104,12 @@
color: lighten( $gray, 10 );
margin-bottom: 0;
font-style: italic;
line-height: 24px;
min-height: 24px;

.gridicon {
position: relative;
top: 7px;
vertical-align: middle;
margin-top: -4px;
}

.external-link {
Expand All @@ -115,10 +121,6 @@
}
}

.guided-tours__actionstep-instructions {
margin-top: -7px;
}

// style the pure text representation of the actionstep icon
.guided-tours__actionstep-text {
position: relative;
Expand Down
1 change: 1 addition & 0 deletions client/layout/sidebar/button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default React.createClass( {
target={ isExternal( this.props.href ) ? '_blank' : null }
className="sidebar__button"
onMouseEnter={ this.preload }
data-tip-target={ this.props.tipTarget }
>
{ this.props.children || this.translate( 'Add' ) }
</a>
Expand Down
12 changes: 11 additions & 1 deletion client/lib/abtest/active-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,15 @@ module.exports = {
},
defaultVariation: 'showSurveyStep',
allowAnyLocale: true,
}
},

siteTitleTour: {
datestamp: '20161207',
variations: {
disabled: 100,
enabled: 0,
},
defaultVariation: 'disabled',
allowExistingUsers: true,
},
};
3 changes: 2 additions & 1 deletion client/my-sites/sidebar/sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,8 @@ export class MySitesSidebar extends Component {
link={ siteSettingsLink }
onNavigate={ this.onNavigate }
icon="cog"
preloadSectionName="settings" />
preloadSectionName="settings"
tipTarget="settings" />
);
}

Expand Down
8 changes: 5 additions & 3 deletions client/my-sites/site-settings/form-general.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ const FormGeneral = React.createClass( {
valueLink={ this.linkState( 'blogname' ) }
disabled={ this.state.fetchingSettings }
onClick={ this.onRecordEvent( 'Clicked Site Title Field' ) }
onKeyPress={ this.onRecordEventOnce( 'typedTitle', 'Typed in Site Title Field' ) } />
onKeyPress={ this.onRecordEventOnce( 'typedTitle', 'Typed in Site Title Field' ) }
data-tip-target="site-title-input" />
</FormFieldset>
<FormFieldset>
<FormLabel htmlFor="blogdescription">{ this.translate( 'Site Tagline' ) }</FormLabel>
Expand All @@ -151,7 +152,8 @@ const FormGeneral = React.createClass( {
valueLink={ this.linkState( 'blogdescription' ) }
disabled={ this.state.fetchingSettings }
onClick={ this.onRecordEvent( 'Clicked Site Site Tagline Field' ) }
onKeyPress={ this.onRecordEventOnce( 'typedTagline', 'Typed in Site Site Tagline Field' ) } />
onKeyPress={ this.onRecordEventOnce( 'typedTagline', 'Typed in Site Site Tagline Field' ) }
data-tip-target="site-tagline-input" />
<FormSettingExplanation>
{ this.translate( 'In a few words, explain what this site is about.' ) }
</FormSettingExplanation>
Expand Down Expand Up @@ -573,7 +575,7 @@ const FormGeneral = React.createClass( {
compact={ true }
onClick={ this.handleSubmitForm }
primary={ true }

data-tip-target="settings-site-profile-save"
type="submit"
disabled={ this.state.fetchingSettings || this.state.submittingForm }>
{ this.state.submittingForm
Expand Down
18 changes: 18 additions & 0 deletions client/state/sites/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
some,
split,
includes,
startsWith,
} from 'lodash';
import i18n from 'i18n-calypso';

Expand Down Expand Up @@ -985,3 +986,20 @@ export function getCustomizerUrl( state, siteId ) {
'return': returnUrl
}, adminUrl );
}

/*
* Returns true if the site has unchanged site title
*
* @param {Object} state Global state tree
* @param {Object} siteId Site ID
* @return {Boolean} True if site title is default, false otherwise.
*/
export const hasDefaultSiteTitle = ( state, siteId ) => {
const site = getRawSite( state, siteId );
if ( ! site ) {
return null;
}
const slug = getSiteSlug( state, siteId );
// we are using startsWith here, as getSiteSlug returns "slug.wordpress.com"
return site.name === i18n.translate( 'Site Title' ) || startsWith( slug, site.name );
};
40 changes: 31 additions & 9 deletions client/state/ui/guided-tours/contexts.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
* Internal dependencies
*/
import config from 'config';
import { getSectionName, isPreviewShowing as isPreviewShowingSelector, getSelectedSite } from 'state/ui/selectors';
import { getLastAction } from 'state/ui/action-log/selectors';
import { getCurrentUser } from 'state/current-user/selectors';
import { abtest } from 'lib/abtest';
import {
getSectionName,
getSelectedSite,
getSelectedSiteId,
isPreviewShowing as isPreviewShowingSelector,
} from 'state/ui/selectors';
import { getLastAction } from 'state/ui/action-log/selectors';
import { getCurrentUser, canCurrentUser } from 'state/current-user/selectors';
import { hasDefaultSiteTitle } from 'state/sites/selectors';

export const inSection = sectionName => state =>
getSectionName( state ) === sectionName;
Expand All @@ -19,15 +25,21 @@ export const isPreviewNotShowing = state =>
export const isPreviewShowing = state =>
isPreviewShowingSelector( state );

const timeSinceUserRegistration = state => {
const user = getCurrentUser( state );
const registrationDate = user && Date.parse( user.date );
return registrationDate ? ( Date.now() - registrationDate ) : false;
};

const WEEK_IN_MILLISECONDS = 7 * 1000 * 3600 * 24;
export const isNewUser = state => {
const user = getCurrentUser( state );
if ( ! user ) {
return false;
}
const userAge = timeSinceUserRegistration( state );
return userAge !== false ? userAge <= WEEK_IN_MILLISECONDS : false;
};

const creation = Date.parse( user.date );
return ( Date.now() - creation ) <= WEEK_IN_MILLISECONDS;
export const isUserOlderThan = age => state => {
const userAge = timeSinceUserRegistration( state );
return userAge !== false ? userAge >= age : false;
};

export const hasUserInteractedWithComponent = componentName => state =>
Expand All @@ -41,3 +53,13 @@ export const isSelectedSiteCustomizable = state =>

export const isAbTestInVariant = ( testName, variant ) => () =>
abtest( testName ) === variant;

export const hasSelectedSiteDefaultSiteTitle = state => {
const siteId = getSelectedSiteId( state );
return siteId ? hasDefaultSiteTitle( state, siteId ) : false;
};

export const canUserEditSettingsOfSelectedSite = state => {
const siteId = getSelectedSiteId( state );
return siteId ? canCurrentUser( state, siteId, 'manage_options' ) : false;
};
1 change: 1 addition & 0 deletions config/development.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"guided-tours": true,
"guided-tours/main": true,
"guided-tours/design-showcase-welcome": true,
"guided-tours/site-title": true,
"guided-tours/theme-sheet-welcome": true,
"guided-tours/themes": false,
"help": true,
Expand Down
1 change: 1 addition & 0 deletions config/wpcalypso.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"guided-tours": true,
"guided-tours/main": false,
"guided-tours/design-showcase-welcome": true,
"guided-tours/site-title": true,
"guided-tours/theme-sheet-welcome": true,
"guided-tours/themes": true,
"help": true,
Expand Down

0 comments on commit a3803e0

Please sign in to comment.