Skip to content

Commit

Permalink
Themes: added Support tab to theme sheet (#5282)
Browse files Browse the repository at this point in the history
* Add: Support page, Refactored tab code
* Refactored: main content tabs now render using separate functions
* Themes: Use existing isPremium() helper in Theme Sheet
* Themes: Add `stylesheet` field to theme-details store
* Add: "footer" line separator graphic
  • Loading branch information
folletto committed May 9, 2016
1 parent 9fe24f3 commit d7a8da8
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 45 deletions.
84 changes: 61 additions & 23 deletions client/my-sites/theme/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import React from 'react';
import { connect } from 'react-redux';
import page from 'page';
import { isPremium } from 'my-sites/themes/helpers';

/**
* Internal dependencies
Expand All @@ -21,10 +22,12 @@ import SectionNav from 'components/section-nav';
import NavTabs from 'components/section-nav/tabs';
import NavItem from 'components/section-nav/item';
import Card from 'components/card';
import Gridicon from 'components/gridicon';
import { signup, purchase, activate } from 'state/themes/actions';
import i18n from 'lib/mixins/i18n';
import { getSelectedSite } from 'state/ui/selectors';
import { getSiteSlug } from 'state/sites/selectors';
import Helpers from 'my-sites/themes/helpers';

const ThemeSheet = React.createClass( {
displayName: 'ThemeSheet',
Expand All @@ -39,6 +42,7 @@ const ThemeSheet = React.createClass( {
descriptionLong: React.PropTypes.string,
supportDocumentation: React.PropTypes.string,
taxonomies: React.PropTypes.object,
stylesheet: React.PropTypes.string,
isLoggedIn: React.PropTypes.bool,
// Connected props
selectedSite: React.PropTypes.object,
Expand All @@ -53,38 +57,20 @@ const ThemeSheet = React.createClass( {
page.back();
},

isPremium() {
return this.props.price.value > 0;
},

onPrimaryClick() {
// TODO: if active -> customize (could use theme slug from selected site)

if ( ! this.props.isLoggedIn ) {
this.props.dispatch( signup( this.props ) );
// TODO: use site picker if no selected site
} else if ( this.isPremium() ) {
} else if ( isPremium( this.props ) ) {
// TODO: check theme is not already purchased
this.props.dispatch( purchase( this.props, this.props.selectedSite, 'showcase-sheet' ) );
} else {
this.props.dispatch( activate( this.props, this.props.selectedSite, 'showcase-sheet' ) );
}
},

getContentElement( section ) {
return {
overview: <div dangerouslySetInnerHTML={ { __html: this.props.descriptionLong } } />,
setup: <div dangerouslySetInnerHTML={ { __html: this.props.supportDocumentation } } />,
support: <div>Visit the support forum</div>,
}[ section ];
},

getDangerousElements( section ) {
const priceElement = <span className="themes__sheet-action-bar-cost" dangerouslySetInnerHTML={ { __html: this.props.price } } />;
const themeContentElement = this.getContentElement( section );
return { priceElement, themeContentElement };
},

getValidSections() {
const validSections = [];
validSections.push( 'overview' );
Expand Down Expand Up @@ -150,7 +136,59 @@ const ThemeSheet = React.createClass( {
);
},

renderFeatures() {
renderSectionContent( section ) {
return {
overview: this.renderOverviewTab(),
setup: this.renderSetupTab(),
support: this.renderSupportTab(),
}[ section ];
},

renderOverviewTab() {
return (
<div>
<Card className="themes__sheet-content">
<div dangerouslySetInnerHTML={ { __html: this.props.descriptionLong } } />
</Card>
{ this.renderFeaturesCard() }
</div>
);
},

renderSetupTab() {
return (
<div>
<Card className="themes__sheet-content">
<div dangerouslySetInnerHTML={ { __html: this.props.supportDocumentation } } />
</Card>
</div>
);
},

renderSupportTab() {
return (
<div>
<Card className="themes__sheet-card-support">
<Gridicon icon="comment" size={ 48 } />
<div className="themes__sheet-card-support-details">
{ i18n.translate( 'Need extra help?' ) }
<small>{ i18n.translate( 'Visit the theme support forum' ) }</small>
</div>
<Button primary={ true } href={ Helpers.getForumUrl( this.props ) }>Visit forum</Button>
</Card>
<Card className="themes__sheet-card-support">
<Gridicon icon="briefcase" size={ 48 } />
<div className="themes__sheet-card-support-details">
{ i18n.translate( 'Need CSS help? ' ) }
<small>{ i18n.translate( 'Visit the CSS customization forum' ) }</small>
</div>
<Button href="//en.forums.wordpress.com/forum/css-customization">Visit forum</Button>
</Card>
</div>
);
},

renderFeaturesCard() {
const themeFeatures = this.props.taxonomies && this.props.taxonomies.features instanceof Array
? this.props.taxonomies.features.map( function( item, i ) {
return ( <li key={ 'theme-features-item-' + i++ }><span>{ item.name }</span></li> );
Expand All @@ -177,7 +215,7 @@ const ThemeSheet = React.createClass( {
}

const section = this.validateSection( this.props.section );
const { themeContentElement, priceElement } = this.getDangerousElements( section );
const priceElement = <span className="themes__sheet-action-bar-cost" dangerouslySetInnerHTML={ { __html: this.props.price } } />;

return (
<Main className="themes__sheet">
Expand All @@ -194,8 +232,8 @@ const ThemeSheet = React.createClass( {
</HeaderCake>
<div className="themes__sheet-content">
{ this.renderSectionNav( section ) }
<Card className="themes__sheet-content">{ themeContentElement }</Card>
{ this.renderFeatures() }
{ this.renderSectionContent( section ) }
<div className="footer__line"><Gridicon icon="my-sites" /></div>
</div>
</div>
<div className="themes__sheet-column-right">
Expand Down
50 changes: 50 additions & 0 deletions client/my-sites/themes/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,56 @@
}
}

.themes__sheet-card-support {
display: flex;
align-items: center;

&:not(:last-child) {
margin-bottom: 0;
}

@include breakpoint( "<660px" ) {
flex-wrap: wrap;
}

.gridicon {
color: lighten( $gray, 20% );
flex: 0 0 auto;
}

.button {
flex: 0 0 auto;

@include breakpoint( "<660px" ) {
flex: 1 1 100%;
margin-top: 20px;
text-align: center;
}
}
}

.themes__sheet-card-support-details {
flex: 1 1 20px;
padding: 0 20px;

small {
display: block;
color: $gray;
}
}

.footer__line {
color: lighten( $gray, 20% );
border-top: 1px solid lighten( $gray, 20% );
margin: 32px 0 20px;

.gridicon {
display: block;
margin: -12px auto 0;
background: $gray-light;
}
}

@include breakpoint( "<660px" ) {
.themes__sheet-columns {
flex-direction: column-reverse;
Expand Down
45 changes: 23 additions & 22 deletions client/state/themes/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ export function fetchThemes( site ) {
dispatch( receiveThemes( themes, site, queryParams, responseTime ) );
} )
.catch( error => receiveServerError( error ) );
}
};
}

export function fetchNextPage( site ) {
return dispatch => {
dispatch( incrementThemesPage( site ) );
dispatch( fetchThemes( site ) );
}
};
}

export function query( params ) {
Expand All @@ -75,7 +75,7 @@ export function incrementThemesPage( site ) {
return {
type: THEMES_INCREMENT_PAGE,
site: site
}
};
}

export function fetchCurrentTheme( site ) {
Expand All @@ -89,7 +89,7 @@ export function fetchCurrentTheme( site ) {
themeId: theme.id,
themeName: theme.name,
themeCost: theme.cost
} )
} );
} ); // TODO: add .catch() error handler
};
}
Expand All @@ -99,12 +99,12 @@ export function fetchThemeDetails( id ) {
wpcom.undocumented().themeDetails( id )
.then( themeDetails => {
debug( 'Received theme details', themeDetails );
dispatch( receiveThemeDetails( themeDetails ) )
dispatch( receiveThemeDetails( themeDetails ) );
} )
.catch( error => {
dispatch( receiveServerError( error ) )
dispatch( receiveServerError( error ) );
} );
}
};
}

export function receiveThemeDetails( theme ) {
Expand All @@ -119,8 +119,9 @@ export function receiveThemeDetails( theme ) {
themeDescriptionLong: theme.description_long,
themeSupportDocumentation: theme.extended ? theme.extended.support_documentation : undefined,
themeTaxonomies: theme.taxonomies,
themeStylesheet: theme.stylesheet,
};
};
}

export function receiveServerError( error ) {
return {
Expand Down Expand Up @@ -162,7 +163,7 @@ export function receiveThemes( data, site, queryParams, responseTime ) {
: themeAction;

dispatch( action );
}
};
}

export function activate( theme, site, source = 'unknown' ) {
Expand All @@ -180,7 +181,7 @@ export function activate( theme, site, source = 'unknown' ) {
.catch( error => {
dispatch( receiveServerError( error ) );
} );
}
};
}

export function activated( theme, site, source = 'unknown', purchased = false ) {
Expand Down Expand Up @@ -212,14 +213,14 @@ export function activated( theme, site, source = 'unknown', purchased = false )

dispatch( withAnalytics( trackThemeActivation, action ) );
} );
}
};
};
}

export function clearActivated() {
return {
type: THEME_CLEAR_ACTIVATED
};
};
}

export function signup( theme ) {
return dispatch => {
Expand All @@ -233,7 +234,7 @@ export function signup( theme ) {
// `ThemeHelpers.navigateTo` uses `page()` here, which messes with `pushState`,
// which we don't want here, since we're navigating away from Calypso.
window.location = signupUrl;
}
};
}

// Might be obsolete, since in my-sites/themes, we're using `getUrl()` for Details
Expand All @@ -247,8 +248,8 @@ export function details( theme, site ) {
} );

ThemeHelpers.navigateTo( detailsUrl, site.jetpack );
}
};
};
}

// Might be obsolete, since in my-sites/themes, we're using `getUrl()` for Support
export function support( theme, site ) {
Expand All @@ -261,8 +262,8 @@ export function support( theme, site ) {
} );

ThemeHelpers.navigateTo( supportUrl, site.jetpack );
}
};
};
}

export function preview( theme, site ) {
return dispatch => {
Expand All @@ -274,7 +275,7 @@ export function preview( theme, site ) {
} );

ThemeHelpers.navigateTo( previewUrl, site.jetpack );
}
};
}

export function customize( theme, site ) {
Expand All @@ -287,8 +288,8 @@ export function customize( theme, site ) {
} );

ThemeHelpers.navigateTo( customizeUrl, site.jetpack );
}
};
};
}

export function purchase( theme, site, source = 'unknown' ) {
const CartActions = require( 'lib/upgrades/actions' );
Expand All @@ -306,5 +307,5 @@ export function purchase( theme, site, source = 'unknown' ) {
site: site
} );
} );
}
};
}
1 change: 1 addition & 0 deletions client/state/themes/theme-details/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default ( state = Map(), action ) => {
descriptionLong: action.themeDescriptionLong,
supportDocumentation: action.themeSupportDocumentation,
taxonomies: action.themeTaxonomies,
stylesheet: action.themeStylesheet,
} ) );
case DESERIALIZE:
return Map();
Expand Down
2 changes: 2 additions & 0 deletions client/state/themes/theme-details/test/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe( 'reducer', () => {
themeDescription: 'the best theme ever invented',
themeDescriptionLong: 'the plato form of a theme',
themeSupportDocumentation: 'support comes from within',
themeStylesheet: 'premium/mood',
themeTaxonomies: {
features: [ {
term_id: null,
Expand All @@ -58,6 +59,7 @@ describe( 'reducer', () => {
description: 'the best theme ever invented',
descriptionLong: 'the plato form of a theme',
supportDocumentation: 'support comes from within',
stylesheet: 'premium/mood',
taxonomies: {
features: [ {
term_id: null,
Expand Down

0 comments on commit d7a8da8

Please sign in to comment.