Integrate Storybook (#98)
* add missing static alias + include ./stories in tsconfig * add storybook dep + script + babel-core bridge dep. * storybook setup * expose unconnected Component for storybook * fix discovered styling issue * dummy props (ProposalWithCrowdFund) * Basic stories.
This commit is contained in:
parent
a8266eb4ac
commit
8be518fff7
|
@ -0,0 +1,11 @@
|
||||||
|
import { configure } from '@storybook/react';
|
||||||
|
import '@babel/polyfill'; // fix regeneratorruntime undefined
|
||||||
|
|
||||||
|
// automatically import all files ending in *.stories.tsx
|
||||||
|
const req = require.context('../stories', true, /.stories.tsx$/);
|
||||||
|
|
||||||
|
function loadStories() {
|
||||||
|
req.keys().forEach(filename => req(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(loadStories, module);
|
|
@ -0,0 +1,11 @@
|
||||||
|
const paths = require('../config/paths');
|
||||||
|
const { client: clientLoaders } = require('../config/webpack.config.js/loaders');
|
||||||
|
const { alias } = require('../config/webpack.config.js/resolvers');
|
||||||
|
|
||||||
|
module.exports = (baseConfig, env, defaultConfig) => {
|
||||||
|
const rules = [...baseConfig.module.rules, ...clientLoaders];
|
||||||
|
baseConfig.module.rules = rules;
|
||||||
|
baseConfig.resolve.extensions.push('.ts', '.tsx', '.json');
|
||||||
|
baseConfig.resolve.alias = alias;
|
||||||
|
return baseConfig;
|
||||||
|
};
|
|
@ -40,7 +40,7 @@ interface State {
|
||||||
amountError: string | null;
|
amountError: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProposalCampaignBlock extends React.Component<Props, State> {
|
export class ProposalCampaignBlock extends React.Component<Props, State> {
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
|
@ -29,7 +29,7 @@ interface ActionProps {
|
||||||
|
|
||||||
type Props = OwnProps & Web3Props & StateProps & ActionProps;
|
type Props = OwnProps & Web3Props & StateProps & ActionProps;
|
||||||
|
|
||||||
class Milestones extends React.Component<Props> {
|
export class Milestones extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
proposal,
|
proposal,
|
||||||
|
|
|
@ -31,10 +31,6 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 0 2rem 0.5rem 0;
|
margin: 0 2rem 0.5rem 0;
|
||||||
|
|
||||||
.ant-progress-circle-path {
|
|
||||||
stroke: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-starting {
|
&.is-starting {
|
||||||
.ant-progress-circle-path {
|
.ant-progress-circle-path {
|
||||||
stroke: #1890ff;
|
stroke: #1890ff;
|
||||||
|
|
|
@ -18,7 +18,7 @@ interface Props extends ProposalWithCrowdFund {
|
||||||
web3: AppState['web3']['web3'];
|
web3: AppState['web3']['web3'];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProposalCard extends React.Component<Props> {
|
export class ProposalCard extends React.Component<Props> {
|
||||||
state = { redirect: '' };
|
state = { redirect: '' };
|
||||||
render() {
|
render() {
|
||||||
if (this.state.redirect) {
|
if (this.state.redirect) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ module.exports = {
|
||||||
lib: `${paths.srcClient}/lib`,
|
lib: `${paths.srcClient}/lib`,
|
||||||
modules: `${paths.srcClient}/modules`,
|
modules: `${paths.srcClient}/modules`,
|
||||||
pages: `${paths.srcClient}/pages`,
|
pages: `${paths.srcClient}/pages`,
|
||||||
|
static: `${paths.srcClient}/static`,
|
||||||
store: `${paths.srcClient}/store`,
|
store: `${paths.srcClient}/store`,
|
||||||
styles: `${paths.srcClient}/styles`,
|
styles: `${paths.srcClient}/styles`,
|
||||||
typings: `${paths.srcClient}/typings`,
|
typings: `${paths.srcClient}/typings`,
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
"tsc": "tsc",
|
"tsc": "tsc",
|
||||||
"link-contracts": "cd client/lib && ln -s ../../build/contracts contracts",
|
"link-contracts": "cd client/lib && ln -s ../../build/contracts contracts",
|
||||||
"ganache": "ganache-cli -b 5",
|
"ganache": "ganache-cli -b 5",
|
||||||
"truffle": "truffle exec ./bin/init-truffle.js && cd client/lib/contracts && truffle console"
|
"truffle": "truffle exec ./bin/init-truffle.js && cd client/lib/contracts && truffle console",
|
||||||
|
"storybook": "start-storybook -p 9001 -c .storybook"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
@ -64,6 +65,7 @@
|
||||||
"ant-design-pro": "^2.0.0-beta.2",
|
"ant-design-pro": "^2.0.0-beta.2",
|
||||||
"antd": "^3.7.1",
|
"antd": "^3.7.1",
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"babel-loader": "^8.0.2",
|
"babel-loader": "^8.0.2",
|
||||||
"babel-plugin-dynamic-import-node": "^2.1.0",
|
"babel-plugin-dynamic-import-node": "^2.1.0",
|
||||||
"babel-plugin-dynamic-import-webpack": "^1.0.2",
|
"babel-plugin-dynamic-import-webpack": "^1.0.2",
|
||||||
|
@ -147,10 +149,12 @@
|
||||||
"xss": "1.0.3"
|
"xss": "1.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@storybook/react": "4.0.0-alpha.22",
|
||||||
"@types/bn.js": "4.11.1",
|
"@types/bn.js": "4.11.1",
|
||||||
"@types/ethereumjs-util": "5.2.0",
|
"@types/ethereumjs-util": "5.2.0",
|
||||||
"@types/query-string": "6.1.0",
|
"@types/query-string": "6.1.0",
|
||||||
"@types/showdown": "1.7.5",
|
"@types/showdown": "1.7.5",
|
||||||
|
"@types/storybook__react": "^3.0.9",
|
||||||
"@types/web3": "1.0.3",
|
"@types/web3": "1.0.3",
|
||||||
"rimraf": "2.6.2"
|
"rimraf": "2.6.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
|
||||||
|
import MarkdownEditor from 'components/MarkdownEditor';
|
||||||
|
import { MARKDOWN_TYPE } from 'utils/markdown';
|
||||||
|
|
||||||
|
const initialMarkdown = `
|
||||||
|
### Initial Markdown
|
||||||
|
Ut enim ad **minima** veniam, quis nostrum _exercitationem_ ullam
|
||||||
|
corporis suscipit ~~laboriosam~~, nisi ut aliquid ex ea commodi
|
||||||
|
consequatur?
|
||||||
|
1. Dog
|
||||||
|
1. Cat
|
||||||
|
1. Stomatopoda
|
||||||
|
|
||||||
|
- Orange
|
||||||
|
- Apple
|
||||||
|
- Durian
|
||||||
|
`;
|
||||||
|
|
||||||
|
storiesOf('MarkdownEditor', module)
|
||||||
|
.add('basic', () => (
|
||||||
|
<div style={{ padding: '2em' }}>
|
||||||
|
<MarkdownEditor onChange={_ => null} />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
.add('initial markdown', () => (
|
||||||
|
<div style={{ padding: '2em' }}>
|
||||||
|
<MarkdownEditor initialMarkdown={initialMarkdown} onChange={_ => null} />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
.add('type - reduced', () => (
|
||||||
|
<div style={{ padding: '2em' }}>
|
||||||
|
<MarkdownEditor
|
||||||
|
type={MARKDOWN_TYPE.REDUCED}
|
||||||
|
initialMarkdown={initialMarkdown}
|
||||||
|
onChange={_ => null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
));
|
|
@ -0,0 +1,23 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
|
||||||
|
import Placeholder from '../client/components/Placeholder';
|
||||||
|
|
||||||
|
storiesOf('Placeholder', module)
|
||||||
|
.add('basic', () => (
|
||||||
|
<div style={{ padding: '2em' }}>
|
||||||
|
<Placeholder
|
||||||
|
title="This Is a Placeholder Title"
|
||||||
|
subtitle="Subtitle. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
.add('styled', () => (
|
||||||
|
<div style={{ padding: '2em' }}>
|
||||||
|
<Placeholder
|
||||||
|
style={{ borderColor: 'green', width: '35em' }}
|
||||||
|
title="Styled Placeholder Title"
|
||||||
|
subtitle="Subtitle. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
));
|
|
@ -0,0 +1,262 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
|
||||||
|
import { ProposalCampaignBlock } from 'components/Proposal/CampaignBlock';
|
||||||
|
import Contributors from 'components/Proposal/Contributors';
|
||||||
|
import { Milestones as GovernanceMilestones } from 'components/Proposal/Governance/Milestones';
|
||||||
|
import Milestones from 'components/Proposal/Milestones';
|
||||||
|
import { MILESTONE_STATE } from 'modules/proposals/reducers';
|
||||||
|
const { WAITING, ACTIVE, PAID, REJECTED } = MILESTONE_STATE;
|
||||||
|
|
||||||
|
import 'styles/style.less';
|
||||||
|
import 'components/Proposal/style.less';
|
||||||
|
import 'components/Proposal/Governance/style.less';
|
||||||
|
import { getProposalWithCrowdFund, getGovernanceMilestonesProps } from './props';
|
||||||
|
|
||||||
|
const propsNoFunding = getProposalWithCrowdFund({
|
||||||
|
amount: 5,
|
||||||
|
funded: 0,
|
||||||
|
});
|
||||||
|
const propsHalfFunded = getProposalWithCrowdFund({
|
||||||
|
amount: 5,
|
||||||
|
funded: 2.5,
|
||||||
|
});
|
||||||
|
const propsFunded = getProposalWithCrowdFund({
|
||||||
|
amount: 5,
|
||||||
|
funded: 5,
|
||||||
|
});
|
||||||
|
const propsNotFundedExpired = getProposalWithCrowdFund({
|
||||||
|
created: Date.now() - 10,
|
||||||
|
deadline: Date.now() - 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const msWaiting = { state: WAITING, isPaid: false };
|
||||||
|
const msPaid = { state: PAID, isPaid: true };
|
||||||
|
const msActive = { state: ACTIVE, isPaid: false };
|
||||||
|
const msRejected = { state: REJECTED, isPaid: false };
|
||||||
|
|
||||||
|
const propsMilestoneActive = getProposalWithCrowdFund({
|
||||||
|
milestoneOverrides: [msPaid, msActive, msWaiting],
|
||||||
|
});
|
||||||
|
const propsMilestoneActiveOneVote = getProposalWithCrowdFund({
|
||||||
|
milestoneOverrides: [
|
||||||
|
msPaid,
|
||||||
|
{ state: ACTIVE, isPaid: false, percentAgainstPayout: 33 },
|
||||||
|
msWaiting,
|
||||||
|
],
|
||||||
|
contributorOverrides: [{ milestoneNoVotes: [false, true, false] }],
|
||||||
|
});
|
||||||
|
const propsMilestoneRejected = getProposalWithCrowdFund({
|
||||||
|
milestoneOverrides: [msPaid, msPaid, msRejected],
|
||||||
|
});
|
||||||
|
const propsMilestoneFirstPaid = getProposalWithCrowdFund({
|
||||||
|
milestoneOverrides: [msPaid, msWaiting, msWaiting],
|
||||||
|
});
|
||||||
|
const propsMilestoneSecondUncollected = getProposalWithCrowdFund({
|
||||||
|
milestoneOverrides: [msPaid, { state: PAID, isPaid: false }, msWaiting],
|
||||||
|
});
|
||||||
|
const propsMilestoneInitialSuccessNotPaid = getProposalWithCrowdFund({
|
||||||
|
milestoneOverrides: [
|
||||||
|
{ state: ACTIVE, isPaid: false, payoutRequestVoteDeadline: Date.now() },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const propsMilestoneAllPaid = getProposalWithCrowdFund({
|
||||||
|
milestoneOverrides: [msPaid, msPaid, msPaid],
|
||||||
|
});
|
||||||
|
|
||||||
|
const trusteeInactiveFirstImmediateGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({ isContributor: false }),
|
||||||
|
propsFunded,
|
||||||
|
);
|
||||||
|
const trusteeActiveNotPaidFirstImmediateGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({ isContributor: false }),
|
||||||
|
propsMilestoneInitialSuccessNotPaid,
|
||||||
|
);
|
||||||
|
const trusteeInactiveFirstPaidGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({ isContributor: false }),
|
||||||
|
propsMilestoneFirstPaid,
|
||||||
|
);
|
||||||
|
const trusteeUncollectedSecondGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({ isContributor: false }),
|
||||||
|
propsMilestoneSecondUncollected,
|
||||||
|
);
|
||||||
|
const trusteeActiveFirstPaidGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({ isContributor: false }),
|
||||||
|
propsMilestoneActive,
|
||||||
|
);
|
||||||
|
const trusteeAllPaidGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({ isContributor: false }),
|
||||||
|
propsMilestoneAllPaid,
|
||||||
|
);
|
||||||
|
|
||||||
|
const contributorInactiveGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({}),
|
||||||
|
propsFunded,
|
||||||
|
);
|
||||||
|
const contributorActiveGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({}),
|
||||||
|
propsMilestoneActive,
|
||||||
|
);
|
||||||
|
const contributorActiveOneVoteGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({}),
|
||||||
|
propsMilestoneActiveOneVote,
|
||||||
|
);
|
||||||
|
const contributorAllPaidGovernanceMilestoneProps = Object.assign(
|
||||||
|
getGovernanceMilestonesProps({}),
|
||||||
|
propsMilestoneAllPaid,
|
||||||
|
);
|
||||||
|
|
||||||
|
const CampaignBlocks = ({ style }: { style: any }) => (
|
||||||
|
<React.Fragment>
|
||||||
|
<div style={style}>
|
||||||
|
<ProposalCampaignBlock {...propsNoFunding} />
|
||||||
|
</div>
|
||||||
|
<div style={style}>
|
||||||
|
<ProposalCampaignBlock {...propsHalfFunded} />
|
||||||
|
</div>
|
||||||
|
<div style={style}>
|
||||||
|
<ProposalCampaignBlock {...propsFunded} />
|
||||||
|
</div>
|
||||||
|
<div style={style}>
|
||||||
|
<ProposalCampaignBlock {...propsNotFundedExpired} />
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
|
||||||
|
storiesOf('Proposal', module)
|
||||||
|
.add('CampaignBlock - narrow', () => (
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
|
<CampaignBlocks style={{ width: '19rem', margin: '0 12px' }} />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
.add('CampaignBlock - wide', () => (
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||||
|
<CampaignBlocks style={{ margin: '0 12px' }} />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
.add('Contributors', () => (
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||||
|
<Contributors crowdFund={propsFunded.proposal.crowdFund} />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
.add('Governance/Milestones trustee', () => {
|
||||||
|
const style = {
|
||||||
|
maxWidth: '500px',
|
||||||
|
margin: '0 0 1.5em',
|
||||||
|
padding: '0.5em',
|
||||||
|
border: '1px solid #cccccc',
|
||||||
|
flex: '1 1 0%',
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="ProposalGovernance"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flex: '1 1 0%',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '2em',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
Trustee - immediate initial payout
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones
|
||||||
|
{...trusteeInactiveFirstImmediateGovernanceMilestoneProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Trustee - immediate initial payout, accepted not paid
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones
|
||||||
|
{...trusteeActiveNotPaidFirstImmediateGovernanceMilestoneProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Trustee - first milestone paid
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones {...trusteeInactiveFirstPaidGovernanceMilestoneProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Trustee - second milestone active
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones {...trusteeActiveFirstPaidGovernanceMilestoneProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Trustee - second milestone uncollected
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones {...trusteeUncollectedSecondGovernanceMilestoneProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Trustee - all paid
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones {...trusteeAllPaidGovernanceMilestoneProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.add('Governance/Milestones contributor', () => {
|
||||||
|
const style = {
|
||||||
|
maxWidth: '500px',
|
||||||
|
margin: '0 0 1.5em',
|
||||||
|
padding: '0.5em',
|
||||||
|
border: '1px solid #cccccc',
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="ProposalGovernance"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '2em',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
Contributor - innactive milestones
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones {...contributorInactiveGovernanceMilestoneProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Contributor - active milestone
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones {...contributorActiveGovernanceMilestoneProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Contributor - active milestone - voted against
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones {...contributorActiveOneVoteGovernanceMilestoneProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Contributor - all paid
|
||||||
|
<div style={style}>
|
||||||
|
<GovernanceMilestones {...contributorAllPaidGovernanceMilestoneProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.add('Milestones - waiting', () => (
|
||||||
|
<div style={{ paddingTop: '2em', display: 'flex', justifyContent: 'center' }}>
|
||||||
|
<Milestones {...propsFunded} />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
.add('Milestones - active', () => (
|
||||||
|
<div style={{ paddingTop: '2em', display: 'flex', justifyContent: 'center' }}>
|
||||||
|
<Milestones {...propsMilestoneActive} />
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
.add('Milestones - rejected', () => (
|
||||||
|
<div style={{ paddingTop: '2em', display: 'flex', justifyContent: 'center' }}>
|
||||||
|
<Milestones {...propsMilestoneRejected} />
|
||||||
|
</div>
|
||||||
|
));
|
|
@ -0,0 +1,95 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
|
||||||
|
import { ProposalCard } from 'components/Proposals/ProposalCard';
|
||||||
|
|
||||||
|
import 'styles/style.less';
|
||||||
|
import 'components/Proposal/style.less';
|
||||||
|
import 'components/Proposal/Governance/style.less';
|
||||||
|
import { getProposalWithCrowdFund } from './props';
|
||||||
|
|
||||||
|
const propsNoFunding = getProposalWithCrowdFund({
|
||||||
|
amount: 5,
|
||||||
|
funded: 0,
|
||||||
|
});
|
||||||
|
const propsHalfFunded = getProposalWithCrowdFund({
|
||||||
|
amount: 5,
|
||||||
|
funded: 2.5,
|
||||||
|
});
|
||||||
|
const propsFunded = getProposalWithCrowdFund({
|
||||||
|
amount: 5,
|
||||||
|
funded: 5,
|
||||||
|
});
|
||||||
|
const propsNotFundedExpired = getProposalWithCrowdFund({
|
||||||
|
created: Date.now() - 1000 * 60 * 60 * 10,
|
||||||
|
deadline: Date.now() - 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
storiesOf('Proposals', module)
|
||||||
|
.add('ProposalCard - narrow', () => {
|
||||||
|
const style = {
|
||||||
|
width: '300px',
|
||||||
|
padding: '0 0.5em 0.5em 0',
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="ProposalGovernance"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
padding: '2em',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={style}>
|
||||||
|
Started - no funding
|
||||||
|
<ProposalCard {...propsNoFunding} />
|
||||||
|
</div>
|
||||||
|
<div style={style}>
|
||||||
|
Started - half funded
|
||||||
|
<ProposalCard {...propsHalfFunded} />
|
||||||
|
</div>
|
||||||
|
<div style={style}>
|
||||||
|
Started - fully funded
|
||||||
|
<ProposalCard {...propsFunded} />
|
||||||
|
</div>
|
||||||
|
<div style={style}>
|
||||||
|
Started - expired
|
||||||
|
<ProposalCard {...propsNotFundedExpired} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.add('ProposalCard - wide', () => {
|
||||||
|
const style = {
|
||||||
|
padding: '0 0.5em 0.5em 0',
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="ProposalGovernance"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
padding: '2em',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={style}>
|
||||||
|
Started - no funding
|
||||||
|
<ProposalCard {...propsNoFunding} />
|
||||||
|
</div>
|
||||||
|
<div style={style}>
|
||||||
|
Started - half funded
|
||||||
|
<ProposalCard {...propsHalfFunded} />
|
||||||
|
</div>
|
||||||
|
<div style={style}>
|
||||||
|
Started - fully funded
|
||||||
|
<ProposalCard {...propsFunded} />
|
||||||
|
</div>
|
||||||
|
<div style={style}>
|
||||||
|
Started - expired
|
||||||
|
<ProposalCard {...propsNotFundedExpired} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,19 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
|
||||||
|
import ShortAddress from '../client/components/ShortAddress';
|
||||||
|
|
||||||
|
const containerWidths = ['100px', '150px', '200px', '300px', '400px'];
|
||||||
|
|
||||||
|
storiesOf('ShortAddress', module).add('widths', () => (
|
||||||
|
<div style={{ padding: '2em' }}>
|
||||||
|
{containerWidths.map(width => (
|
||||||
|
<div key={width} style={{ padding: '5px' }}>
|
||||||
|
<div style={{ fontSize: '0.8em', color: 'gray' }}>{width}</div>
|
||||||
|
<div style={{ width, border: '1px solid gray' }}>
|
||||||
|
<ShortAddress address="0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
));
|
|
@ -0,0 +1,83 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import BN from 'bn.js';
|
||||||
|
|
||||||
|
import UnitDisplay from '../client/components/UnitDisplay';
|
||||||
|
|
||||||
|
const oneEth = new BN('1000000000000000000');
|
||||||
|
|
||||||
|
const cases = [
|
||||||
|
{
|
||||||
|
disp: 'basic',
|
||||||
|
props: { value: oneEth.mul(new BN(25)), symbol: 'ETH' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disp: 'fraction',
|
||||||
|
props: { value: oneEth.div(new BN(3)), symbol: 'ETH' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disp: 'fraction - displayShortBalance: true',
|
||||||
|
props: { value: oneEth.div(new BN(3)), symbol: 'ETH', displayShortBalance: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disp: 'fraction - displayShortBalance: 2',
|
||||||
|
props: { value: oneEth.div(new BN(3)), symbol: 'ETH', displayShortBalance: 2 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disp: 'fraction - displayShortBalance: 4, displayTrailingZeros: false',
|
||||||
|
props: {
|
||||||
|
value: oneEth.div(new BN(2)),
|
||||||
|
symbol: 'ETH',
|
||||||
|
displayShortBalance: 4,
|
||||||
|
displayTrailingZeroes: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disp: 'fraction - displayShortBalance: 4, displayTrailingZeros: true',
|
||||||
|
props: {
|
||||||
|
value: oneEth.div(new BN(2)),
|
||||||
|
symbol: 'ETH',
|
||||||
|
displayShortBalance: 4,
|
||||||
|
displayTrailingZeroes: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disp: 'tiny',
|
||||||
|
props: { value: new BN(1), symbol: 'ETH' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disp: 'tiny - displayShortBalance: true',
|
||||||
|
props: {
|
||||||
|
value: new BN(1),
|
||||||
|
symbol: 'ETH',
|
||||||
|
displayShortBalance: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
disp: 'tiny - displayShortBalance: 2',
|
||||||
|
props: {
|
||||||
|
value: new BN(1),
|
||||||
|
symbol: 'ETH',
|
||||||
|
displayShortBalance: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
storiesOf('UnitDisplay', module).add('all', () => (
|
||||||
|
<div style={{ padding: '2em' }}>
|
||||||
|
{cases.map(c => (
|
||||||
|
<div key={c.disp}>
|
||||||
|
<div style={{ fontSize: '0.9em', paddingTop: '0.6em' }}>{`${c.disp}`}</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '0 0.5em',
|
||||||
|
border: '1px solid gray',
|
||||||
|
display: 'inline-block',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<UnitDisplay {...c.props} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
));
|
|
@ -0,0 +1,2 @@
|
||||||
|
const req = require.context('./', true, /.story.tsx$/);
|
||||||
|
req.keys().forEach(filename => req(filename));
|
|
@ -0,0 +1,221 @@
|
||||||
|
import { Contributor, Milestone, MILESTONE_STATE } from 'modules/proposals/reducers';
|
||||||
|
import { PROPOSAL_CATEGORY } from 'api/constants';
|
||||||
|
import {
|
||||||
|
fundCrowdFund,
|
||||||
|
requestMilestonePayout,
|
||||||
|
payMilestonePayout,
|
||||||
|
voteMilestonePayout,
|
||||||
|
} from 'modules/web3/actions';
|
||||||
|
import Web3 from 'web3';
|
||||||
|
import BN from 'bn.js';
|
||||||
|
|
||||||
|
const oneEth = new BN('1000000000000000000');
|
||||||
|
|
||||||
|
export function getGovernanceMilestonesProps({
|
||||||
|
isContributor = true,
|
||||||
|
}: {
|
||||||
|
isContributor?: boolean;
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
accounts: [
|
||||||
|
isContributor
|
||||||
|
? '0xAAA91bde2303f2f43325b2108d26f1eaba1e32b'
|
||||||
|
: '0x0c7C6178AD0618Bf289eFd5E1Ff9Ada25fC3bDE7',
|
||||||
|
],
|
||||||
|
isMilestoneActionPending: false,
|
||||||
|
milestoneActionError: '',
|
||||||
|
requestMilestonePayout,
|
||||||
|
payMilestonePayout,
|
||||||
|
voteMilestonePayout,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProposalWithCrowdFund({
|
||||||
|
amount = 10,
|
||||||
|
funded = 5,
|
||||||
|
created = Date.now(),
|
||||||
|
deadline = Date.now() + 1000 * 60 * 60 * 10,
|
||||||
|
milestoneOverrides = [],
|
||||||
|
contributorOverrides = [],
|
||||||
|
}: {
|
||||||
|
amount?: number;
|
||||||
|
funded?: number;
|
||||||
|
created?: number;
|
||||||
|
deadline?: number;
|
||||||
|
milestoneOverrides?: Array<Partial<Milestone>>;
|
||||||
|
contributorOverrides?: Array<Partial<Contributor>>;
|
||||||
|
}) {
|
||||||
|
const amountBn = oneEth.mul(new BN(amount));
|
||||||
|
const fundedBn = oneEth.mul(new BN(funded));
|
||||||
|
|
||||||
|
const contributors = [
|
||||||
|
{
|
||||||
|
address: '0xAAA91bde2303f2f43325b2108d26f1eaba1e32b',
|
||||||
|
contributionAmount: new BN(0),
|
||||||
|
refundVote: false,
|
||||||
|
refunded: false,
|
||||||
|
proportionalContribution: '',
|
||||||
|
milestoneNoVotes: [false],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
address: '0xBBB491bde2303f2f43325b2108d26f1eaba1e32b',
|
||||||
|
contributionAmount: new BN(0),
|
||||||
|
refundVote: false,
|
||||||
|
refunded: false,
|
||||||
|
proportionalContribution: '',
|
||||||
|
milestoneNoVotes: [false],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
address: '0xCCC491bde2303f2f43325b2108d26f1eaba1e32b',
|
||||||
|
contributionAmount: new BN(0),
|
||||||
|
refundVote: false,
|
||||||
|
refunded: false,
|
||||||
|
proportionalContribution: '',
|
||||||
|
milestoneNoVotes: [false],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
address: '0xDDD491bde2303f2f43325b2108d26f1eaba1e32b',
|
||||||
|
contributionAmount: new BN(0),
|
||||||
|
refundVote: false,
|
||||||
|
refunded: false,
|
||||||
|
proportionalContribution: '',
|
||||||
|
milestoneNoVotes: [false],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const eachContributorAmount = fundedBn.div(new BN(contributors.length));
|
||||||
|
contributors.forEach(c => (c.contributionAmount = eachContributorAmount));
|
||||||
|
contributorOverrides.forEach((co, idx) => {
|
||||||
|
Object.assign(contributors[idx], co);
|
||||||
|
});
|
||||||
|
|
||||||
|
const milestones = [
|
||||||
|
{
|
||||||
|
title: 'Milestone A',
|
||||||
|
body: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||||
|
tempor incididunt ut labore et dolore magna aliqua.`,
|
||||||
|
content: '',
|
||||||
|
dateCreated: '2018-09-23T19:06:15.844399+00:00',
|
||||||
|
dateEstimated: '2018-10-01T00:00:00+00:00',
|
||||||
|
immediatePayout: true,
|
||||||
|
index: 0,
|
||||||
|
state: MILESTONE_STATE.WAITING,
|
||||||
|
amount: amountBn,
|
||||||
|
amountAgainstPayout: new BN(0),
|
||||||
|
percentAgainstPayout: 0,
|
||||||
|
payoutRequestVoteDeadline: 0,
|
||||||
|
isPaid: false,
|
||||||
|
isImmediatePayout: true,
|
||||||
|
payoutPercent: '33',
|
||||||
|
stage: 'NOT_REQUESTED',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Milestone B',
|
||||||
|
body: `Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
|
||||||
|
nisi ut aliquip ex ea commodo consequat.`,
|
||||||
|
content: '',
|
||||||
|
dateCreated: '2018-09-23T19:06:15.844399+00:00',
|
||||||
|
dateEstimated: '2018-11-01T00:00:00+00:00',
|
||||||
|
immediatePayout: false,
|
||||||
|
index: 1,
|
||||||
|
state: MILESTONE_STATE.WAITING,
|
||||||
|
amount: amountBn,
|
||||||
|
amountAgainstPayout: new BN(0),
|
||||||
|
percentAgainstPayout: 0,
|
||||||
|
payoutRequestVoteDeadline: Date.now(),
|
||||||
|
isPaid: false,
|
||||||
|
isImmediatePayout: false,
|
||||||
|
payoutPercent: '33',
|
||||||
|
stage: 'NOT_REQUESTED',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Milestone C',
|
||||||
|
body: `Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
|
||||||
|
nisi ut aliquip ex ea commodo consequat.`,
|
||||||
|
content: '',
|
||||||
|
dateCreated: '2018-09-23T19:06:15.844399+00:00',
|
||||||
|
dateEstimated: '2018-12-01T00:00:00+00:00',
|
||||||
|
immediatePayout: false,
|
||||||
|
index: 2,
|
||||||
|
state: MILESTONE_STATE.WAITING,
|
||||||
|
amount: amountBn,
|
||||||
|
amountAgainstPayout: new BN(0),
|
||||||
|
percentAgainstPayout: 0,
|
||||||
|
payoutRequestVoteDeadline: Date.now(),
|
||||||
|
isPaid: false,
|
||||||
|
isImmediatePayout: false,
|
||||||
|
payoutPercent: '33',
|
||||||
|
stage: 'NOT_REQUESTED',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const eachMilestoneAmount = fundedBn.div(new BN(milestones.length));
|
||||||
|
milestones.forEach(ms => (ms.amount = eachMilestoneAmount));
|
||||||
|
milestoneOverrides.forEach((mso, idx) => {
|
||||||
|
Object.assign(milestones[idx], mso);
|
||||||
|
});
|
||||||
|
|
||||||
|
const proposal = {
|
||||||
|
proposalId: '0x033fDc6C01DC2385118C7bAAB88093e22B8F0710',
|
||||||
|
dateCreated: created / 1000,
|
||||||
|
title: 'Crowdfund Title',
|
||||||
|
body: 'body',
|
||||||
|
stage: 'FUNDING_REQUIRED',
|
||||||
|
category: PROPOSAL_CATEGORY.COMMUNITY,
|
||||||
|
team: [
|
||||||
|
{
|
||||||
|
accountAddress: '0x0c7C6178AD0618Bf289eFd5E1Ff9Ada25fC3bDE7',
|
||||||
|
title: '',
|
||||||
|
userid: 1,
|
||||||
|
username: '',
|
||||||
|
avatar: { '120x120': '' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accountAddress: '0x0c7C6178AD0618Bf289eFd5E1Ff9Ada25fC3bDE7',
|
||||||
|
title: '',
|
||||||
|
userid: 2,
|
||||||
|
username: '',
|
||||||
|
avatar: { '120x120': '' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accountAddress: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
|
||||||
|
title: '',
|
||||||
|
userid: 3,
|
||||||
|
username: '',
|
||||||
|
avatar: { '120x120': '' },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
milestones,
|
||||||
|
crowdFund: {
|
||||||
|
beneficiary: '0x0c7C6178AD0618Bf289eFd5E1Ff9Ada25fC3bDE7',
|
||||||
|
trustees: [
|
||||||
|
'0x0c7C6178AD0618Bf289eFd5E1Ff9Ada25fC3bDE7',
|
||||||
|
'0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
|
||||||
|
],
|
||||||
|
contributors,
|
||||||
|
milestones,
|
||||||
|
deadline,
|
||||||
|
milestoneVotingPeriod: 3600000,
|
||||||
|
isFrozen: false,
|
||||||
|
isRaiseGoalReached: amount <= funded,
|
||||||
|
immediateFirstMilestonePayout: true,
|
||||||
|
balance: new BN(0),
|
||||||
|
funded: fundedBn,
|
||||||
|
percentFunded: (funded / amount) * 100,
|
||||||
|
target: amountBn,
|
||||||
|
amountVotingForRefund: new BN(0),
|
||||||
|
percentVotingForRefund: 0,
|
||||||
|
},
|
||||||
|
crowdFundContract: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
sendLoading: false,
|
||||||
|
fundCrowdFund,
|
||||||
|
web3: new Web3(),
|
||||||
|
proposal,
|
||||||
|
...proposal, // yeah...
|
||||||
|
};
|
||||||
|
|
||||||
|
return props;
|
||||||
|
}
|
|
@ -24,6 +24,7 @@
|
||||||
"lib/*": ["./client/lib/*"],
|
"lib/*": ["./client/lib/*"],
|
||||||
"modules/*": ["./client/modules/*"],
|
"modules/*": ["./client/modules/*"],
|
||||||
"pages/*": ["./client/pages/*"],
|
"pages/*": ["./client/pages/*"],
|
||||||
|
"static/*": ["./client/static/*"],
|
||||||
"store/*": ["./client/store/*"],
|
"store/*": ["./client/store/*"],
|
||||||
"styles/*": ["./client/styles/*"],
|
"styles/*": ["./client/styles/*"],
|
||||||
"typings/*": ["./client/typings/*"],
|
"typings/*": ["./client/typings/*"],
|
||||||
|
@ -31,6 +32,6 @@
|
||||||
"web3interact/*": ["./client/web3interact/*"]
|
"web3interact/*": ["./client/web3interact/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["./client/**/*", "./server/**/*"],
|
"include": ["./client/**/*", "./server/**/*", "./stories/**/*"],
|
||||||
"exclude": ["./client/static"]
|
"exclude": ["./client/static"]
|
||||||
}
|
}
|
||||||
|
|
1592
frontend/yarn.lock
1592
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue