mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-25 19:48:50 +03:00
Updated account home page UI for free member
refs https://github.com/TryGhost/members.js/issues/20 - Updates UI for free member's account page based on spec - Updates popup to show account home page like normal modal - Updates tests
This commit is contained in:
parent
21df87e388
commit
67a07893b0
@ -35,19 +35,6 @@ const Styles = {
|
|||||||
height: '60%',
|
height: '60%',
|
||||||
backgroundColor: 'white'
|
backgroundColor: 'white'
|
||||||
},
|
},
|
||||||
menu: {
|
|
||||||
position: 'fixed',
|
|
||||||
padding: '0',
|
|
||||||
outline: '0',
|
|
||||||
bottom: '100px',
|
|
||||||
right: '20px',
|
|
||||||
borderRadius: '8px',
|
|
||||||
boxShadow: 'rgba(0, 0, 0, 0.16) 0px 5px 40px',
|
|
||||||
opacity: '1',
|
|
||||||
overflow: 'hidden',
|
|
||||||
height: '60%',
|
|
||||||
backgroundColor: 'white'
|
|
||||||
},
|
|
||||||
signin: {
|
signin: {
|
||||||
minHeight: '200px',
|
minHeight: '200px',
|
||||||
maxHeight: '330px'
|
maxHeight: '330px'
|
||||||
@ -57,9 +44,9 @@ const Styles = {
|
|||||||
maxHeight: '620px'
|
maxHeight: '620px'
|
||||||
},
|
},
|
||||||
accountHome: {
|
accountHome: {
|
||||||
width: '280px',
|
width: '500px',
|
||||||
minHeight: '200px',
|
minHeight: '300px',
|
||||||
maxHeight: '240px'
|
maxHeight: '350px'
|
||||||
},
|
},
|
||||||
magiclink: {
|
magiclink: {
|
||||||
minHeight: '230px',
|
minHeight: '230px',
|
||||||
@ -144,9 +131,8 @@ export default class PopupModal extends React.Component {
|
|||||||
|
|
||||||
renderFrameContainer() {
|
renderFrameContainer() {
|
||||||
const page = this.context.page;
|
const page = this.context.page;
|
||||||
const commonStyle = this.context.page === 'accountHome' ? Styles.frame.menu : Styles.frame.common;
|
|
||||||
const frameStyle = {
|
const frameStyle = {
|
||||||
...commonStyle,
|
...Styles.frame.common,
|
||||||
...Styles.frame[page]
|
...Styles.frame[page]
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import {ParentContext} from '../ParentContext';
|
import {ParentContext} from '../ParentContext';
|
||||||
|
import MemberAvatar from '../common/MemberGravatar';
|
||||||
|
import ActionButton from '../common/ActionButton';
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
@ -10,108 +12,6 @@ export default class AccountHomePage extends React.Component {
|
|||||||
this.context.onAction('signout');
|
this.context.onAction('signout');
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePlanCheckout(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const plan = e.target.name;
|
|
||||||
const email = this.context.member.email;
|
|
||||||
this.context.onAction('checkoutPlan', {email, plan});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPlanSelectButton({name}) {
|
|
||||||
const buttonStyle = {
|
|
||||||
display: 'inline-block',
|
|
||||||
height: '38px',
|
|
||||||
border: '0',
|
|
||||||
fontSize: '14px',
|
|
||||||
fontWeight: '300',
|
|
||||||
textAlign: 'center',
|
|
||||||
textDecoration: 'none',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
borderRadius: '5px',
|
|
||||||
cursor: 'pointer',
|
|
||||||
transition: '.4s ease',
|
|
||||||
color: '#fff',
|
|
||||||
backgroundColor: this.context.brandColor,
|
|
||||||
boxShadow: 'none',
|
|
||||||
userSelect: 'none',
|
|
||||||
width: '90px',
|
|
||||||
marginBottom: '12px'
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button name={name} onClick={(e) => {
|
|
||||||
this.handlePlanCheckout(e);
|
|
||||||
}} style={buttonStyle}>
|
|
||||||
Choose
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPlanBox({position, id, type, price, currency, name}) {
|
|
||||||
const boxStyle = (position) => {
|
|
||||||
const style = {
|
|
||||||
padding: '12px 24px',
|
|
||||||
flexBasis: '100%'
|
|
||||||
};
|
|
||||||
if (position !== 'last') {
|
|
||||||
style.borderRight = '1px solid black';
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
};
|
|
||||||
|
|
||||||
const nameStyle = {
|
|
||||||
fontSize: '14px',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center'
|
|
||||||
};
|
|
||||||
|
|
||||||
const priceStyle = {
|
|
||||||
fontSize: '12px',
|
|
||||||
fontWeight: 'bold',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
marginBottom: '9px'
|
|
||||||
};
|
|
||||||
const checkboxStyle = {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center'
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={boxStyle(position)}>
|
|
||||||
<div style={nameStyle}> {name} </div>
|
|
||||||
<div style={priceStyle}>
|
|
||||||
<strong style={{fontSize: '14px'}}> {currency} {price} </strong>
|
|
||||||
<span> {` / ${type}`}</span>
|
|
||||||
</div>
|
|
||||||
<div style={checkboxStyle}> {this.renderPlanSelectButton({name})} </div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPlans() {
|
|
||||||
const containerStyle = {
|
|
||||||
display: 'flex',
|
|
||||||
border: '1px solid black',
|
|
||||||
marginBottom: '12px'
|
|
||||||
};
|
|
||||||
const siteTitle = this.context.site.title;
|
|
||||||
const plans = this.context.site.plans;
|
|
||||||
return (
|
|
||||||
<div style={{padding: '12px 12px'}}>
|
|
||||||
<div style={{marginBottom: '12px', fontSize: '14px'}}>
|
|
||||||
{`Hey there! You are subscribed to free updates from ${siteTitle}, but don't have a paid subscription to unlock full access`}
|
|
||||||
</div>
|
|
||||||
<div style={{fontWeight: 'bold', marginBottom: '9px'}}> Choose a Plan </div>
|
|
||||||
<div style={containerStyle}>
|
|
||||||
{this.renderPlanBox({position: 'first', type: 'month', price: plans.monthly, currency: plans.currency_symbol, name: 'Monthly'})}
|
|
||||||
{this.renderPlanBox({position: 'last', type: 'year', price: plans.yearly, currency: plans.currency_symbol, name: 'Yearly'})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderHeader() {
|
renderHeader() {
|
||||||
const memberEmail = this.context.member.email;
|
const memberEmail = this.context.member.email;
|
||||||
|
|
||||||
@ -130,43 +30,65 @@ export default class AccountHomePage extends React.Component {
|
|||||||
renderUserAvatar() {
|
renderUserAvatar() {
|
||||||
const avatarImg = (this.context.member && this.context.member.avatar_image);
|
const avatarImg = (this.context.member && this.context.member.avatar_image);
|
||||||
|
|
||||||
const logoStyle = {
|
const avatarContainerStyle = {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'block',
|
display: 'flex',
|
||||||
width: '64px',
|
width: '64px',
|
||||||
height: '64px',
|
height: '64px',
|
||||||
marginBottom: '6px',
|
marginBottom: '6px',
|
||||||
backgroundPosition: '50%',
|
|
||||||
backgroundSize: 'cover',
|
|
||||||
borderRadius: '100%',
|
borderRadius: '100%',
|
||||||
boxShadow: '0 0 0 3px #fff',
|
boxShadow: '0 0 0 3px #fff',
|
||||||
border: '1px solid gray'
|
border: '1px solid gray',
|
||||||
|
overflow: 'hidden',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (avatarImg) {
|
|
||||||
logoStyle.backgroundImage = `url(${avatarImg})`;
|
|
||||||
return (
|
|
||||||
<span style={logoStyle}> </span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderUserHeader() {
|
|
||||||
const memberEmail = this.context.member.email;
|
|
||||||
const memberName = this.context.member.name;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: '24px'}}>
|
<div style={avatarContainerStyle}>
|
||||||
{this.renderUserAvatar()}
|
<MemberAvatar gravatar={avatarImg} style={{userIcon: {color: 'black', width: '45px', height: '45px'}}} />
|
||||||
{memberName ? <div style={{fontSize: '16px', fontWeight: '400'}}> {memberName}</div> : null}
|
|
||||||
<div style={{fontSize: '14px', fontWeight: '400', color: '#929292', lineHeight: '12px'}}> {memberEmail}</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAccountDetail(e) {
|
renderUserHeader() {
|
||||||
// No-op
|
return (
|
||||||
|
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: '12px'}}>
|
||||||
|
{this.renderUserAvatar()}
|
||||||
|
<div style={{fontSize: '21px', fontWeight: '500', marginTop: '6px'}}> Your Account </div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
openSettings(e) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAccountFooter() {
|
||||||
|
return (
|
||||||
|
<div style={{display: 'flex', padding: '0 24px', marginTop: '6px', color: this.context.brandColor, fontWeight: 'bold', fontSize: '13px'}}>
|
||||||
|
<div style={{cursor: 'pointer'}} role='button'> Contact support </div>
|
||||||
|
<div style={{display: 'flex', flexGrow: 1, justifyContent: 'flex-end'}}>
|
||||||
|
<div style={{marginRight: '16px', cursor: 'pointer'}} onClick={e => this.openSettings(e)} role='button'> Settings </div>
|
||||||
|
<div style={{cursor: 'pointer'}} onClick={e => this.handleSignout(e)} role='button'> Logout </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAccountDetail(e) {
|
||||||
|
const {name, firstname, email} = this.context.member;
|
||||||
|
const siteTitle = this.context.site.title;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{padding: '0 24px'}}>
|
||||||
|
<div style={{textAlign: 'center', marginBottom: '12px', fontSize: '14px'}}>
|
||||||
|
<span style={{fontWeight: 'bold'}}>Hey {firstname || name || email}! </span>
|
||||||
|
You are subscribed to free updates from <span style={{fontWeight: 'bold'}}>{siteTitle}</span>, but you don't have a paid subscription to unlock full access
|
||||||
|
</div>
|
||||||
|
<ActionButton label="Subscribe now" onClick={e => {}} brandColor={this.context.brandColor} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLogoutButton() {
|
renderLogoutButton() {
|
||||||
@ -184,10 +106,11 @@ export default class AccountHomePage extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div style={{display: 'flex', flexDirection: 'column', color: '#313131', paddingTop: '9px'}}>
|
<div style={{display: 'flex', flexDirection: 'column', color: '#313131'}}>
|
||||||
{this.renderUserHeader()}
|
{this.renderUserHeader()}
|
||||||
{this.renderLogoutButton()}
|
{this.renderAccountDetail()}
|
||||||
|
{this.renderAccountFooter()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,11 +6,13 @@ const setup = (overrides) => {
|
|||||||
const {mockOnActionFn, ...utils} = render(
|
const {mockOnActionFn, ...utils} = render(
|
||||||
<AccountHomePage />
|
<AccountHomePage />
|
||||||
);
|
);
|
||||||
const memberEmail = utils.getByText('member@example.com');
|
const logoutBtn = utils.queryByRole('button', {name: 'Logout'});
|
||||||
const logoutButton = utils.queryByRole('button', {name: 'Log out'});
|
const settingsBtn = utils.queryByRole('button', {name: 'Settings'});
|
||||||
|
const supportBtn = utils.queryByRole('button', {name: 'Contact support'});
|
||||||
return {
|
return {
|
||||||
memberEmail,
|
logoutBtn,
|
||||||
logoutButton,
|
settingsBtn,
|
||||||
|
supportBtn,
|
||||||
mockOnActionFn,
|
mockOnActionFn,
|
||||||
...utils
|
...utils
|
||||||
};
|
};
|
||||||
@ -18,16 +20,17 @@ const setup = (overrides) => {
|
|||||||
|
|
||||||
describe('Account Home Page', () => {
|
describe('Account Home Page', () => {
|
||||||
test('renders', () => {
|
test('renders', () => {
|
||||||
const {memberEmail, logoutButton} = setup();
|
const {settingsBtn, supportBtn, logoutBtn} = setup();
|
||||||
|
|
||||||
expect(memberEmail).toBeInTheDocument();
|
expect(settingsBtn).toBeInTheDocument();
|
||||||
expect(logoutButton).toBeInTheDocument();
|
expect(logoutBtn).toBeInTheDocument();
|
||||||
|
expect(supportBtn).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can call signout', () => {
|
test('can call signout', () => {
|
||||||
const {mockOnActionFn, logoutButton} = setup();
|
const {mockOnActionFn, logoutBtn} = setup();
|
||||||
|
|
||||||
fireEvent.click(logoutButton);
|
fireEvent.click(logoutBtn);
|
||||||
expect(mockOnActionFn).toHaveBeenCalledWith('signout');
|
expect(mockOnActionFn).toHaveBeenCalledWith('signout');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user