Added new component for membership plans

refs https://github.com/TryGhost/members.js/issues/10

Create common plans section UI component as its used in multiple places in app
This commit is contained in:
Rish 2020-04-30 00:17:16 +05:30
parent 9edb3d2e58
commit 886ee7e9ec
2 changed files with 188 additions and 0 deletions

View File

@ -0,0 +1,148 @@
import React from 'react';
const Styles = ({style = {}}) => {
return {
input: {
display: 'block',
padding: '0 .6em',
width: '100%',
height: '44px',
outline: '0',
border: '1px solid #c5d2d9',
color: 'inherit',
textDecoration: 'none',
background: '#fff',
borderRadius: '9px',
fontSize: '14px',
marginBottom: '12px',
boxSizing: 'border-box',
...(style.input || {}) // Override any custom style
},
label: {
marginBottom: '3px',
fontSize: '12px',
fontWeight: '700',
...(style.label || {}) // Override any custom style
}
};
};
function Checkbox({name, onPlanSelect, isChecked}) {
const style = {
width: '20px',
height: '20px',
border: 'solid 1px #cccccc'
};
return (
<input
name={name}
key={name}
type="checkbox"
checked={isChecked}
aria-label={name}
style={style}
onChange={e => onPlanSelect(e, name)}
/>
);
}
function PriceLabel({name, currency, price}) {
if (name === 'Free') {
return (
<strong style={{
fontSize: '11px',
textAlign: 'center',
lineHeight: '18px',
color: '#929292',
fontWeight: 'normal'
}}> Access free members-only posts </strong>
);
}
const type = name === 'Monthly' ? 'month' : 'year';
return (
<div style={{
display: 'inline',
verticalAlign: 'baseline'
}}>
<span style={{fontSize: '14px', color: '#929292', fontWeight: 'normal'}}> {currency} </span>
<strong style={{fontSize: '21px'}}> {price} </strong>
<span style={{fontSize: '12px', color: '#929292', fontWeight: 'normal'}}> {` / ${type}`}</span>
</div>
);
}
function PlanOptions({plans, selectedPlan, onPlanSelect}) {
const nameStyle = {
fontSize: '13px',
fontWeight: '500',
display: 'flex',
color: '#343F44',
justifyContent: 'center'
};
const priceStyle = {
fontSize: '12px',
fontWeight: 'bold',
display: 'flex',
justifyContent: 'center',
marginBottom: '9px'
};
const checkboxStyle = {
display: 'flex',
justifyContent: 'center'
};
const boxStyle = ({isLast = false}) => {
const style = {
padding: '12px 12px',
flexBasis: '100%'
};
if (!isLast) {
style.borderRight = '1px solid #c5d2d9';
}
return style;
};
return plans.map(({name, currency, price}, i) => {
const isLast = i === plans.length - 1;
const isChecked = selectedPlan === name;
return (
<div style={boxStyle({isLast})} key={name} onClick={e => onPlanSelect(e, name)}>
<div style={checkboxStyle}>
<Checkbox name={name} isChecked={isChecked} onPlanSelect={onPlanSelect} />
</div>
<div style={nameStyle}> {name.toUpperCase()} </div>
<div style={priceStyle}>
<PriceLabel name={name} currency={currency} price={price} />
</div>
</div>
);
});
}
function PlansSection({plans, selectedPlan, onPlanSelect, style}) {
const containerStyle = {
display: 'flex',
border: '1px solid #c5d2d9',
borderRadius: '9px',
marginBottom: '12px'
};
if (!plans || plans.length === 0) {
return null;
}
return (
<div style={{width: '100%'}}>
<label style={{marginBottom: '3px', fontSize: '12px', fontWeight: '700'}}> Plan </label>
<div style={containerStyle}>
<PlanOptions plans={[
{type: 'free', price: 'Decide later', name: 'Free'},
{type: 'month', price: plans.monthly, currency: plans.currency_symbol, name: 'Monthly'},
{type: 'year', price: plans.yearly, currency: plans.currency_symbol, name: 'Yearly'}
]} onPlanSelect={onPlanSelect} selectedPlan={selectedPlan} />
</div>
</div>
);
}
export default PlansSection;

View File

@ -0,0 +1,40 @@
import React from 'react';
import {render, fireEvent} from '@testing-library/react';
import PlansSection from './PlansSection';
const setup = (overrides = {}) => {
const mockOnPlanSelectFn = jest.fn();
const props = {
plans: {
monthly: 12,
yearly: 110,
currency: 'USD',
currency_symbol: '$'
},
selectedPlan: 'Monthly',
onPlanSelect: mockOnPlanSelectFn
};
const utils = render(
<PlansSection {...props} />
);
const freeCheckboxEl = utils.getByLabelText('Free');
const monthlyCheckboxEl = utils.getByLabelText('Monthly');
const yearlyCheckboxEl = utils.getByLabelText('Yearly');
return {
freeCheckboxEl,
monthlyCheckboxEl,
yearlyCheckboxEl,
mockOnPlanSelectFn,
...utils
};
};
describe('InputField', () => {
test('renders', () => {
const {freeCheckboxEl, monthlyCheckboxEl, yearlyCheckboxEl} = setup();
expect(freeCheckboxEl).toBeInTheDocument();
expect(monthlyCheckboxEl).toBeInTheDocument();
expect(yearlyCheckboxEl).toBeInTheDocument();
});
});