Skip to content

Commit

Permalink
Merge pull request #767 from poanetwork/use-final-form-step-three
Browse files Browse the repository at this point in the history
(Refactor) Use final form step three
  • Loading branch information
vbaranov authored Apr 9, 2018
2 parents 66b2ce5 + 7ace876 commit 1c41784
Show file tree
Hide file tree
Showing 19 changed files with 1,323 additions and 397 deletions.
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"extract-text-webpack-plugin": "2.1.2",
"file-loader": "0.11.2",
"final-form": "^4.3.1",
"final-form-arrays": "^1.0.4",
"font-awesome": "^4.7.0",
"fs-extra": "3.0.1",
"html-webpack-plugin": "2.29.0",
Expand Down Expand Up @@ -95,6 +96,8 @@
"react-dropzone": "^4.2.8",
"react-error-overlay": "^1.0.9",
"react-final-form": "^3.1.4",
"react-final-form-arrays": "^1.0.4",
"react-final-form-listeners": "^1.0.0",
"react-router-dom": "^4.1.2",
"react-tooltip": "^3.4.0",
"solc": "^0.4.14",
Expand Down
2 changes: 1 addition & 1 deletion src/assets/stylesheets/application.css

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion src/assets/stylesheets/application/controls.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
color: #9c9c9c;

&:hover {
background-color: #e6e6e6;
color: #9c9c9c;
cursor: default !important;
}
}
Expand Down Expand Up @@ -98,6 +100,6 @@
}

&:disabled {
background: #f2f2f2;
background: #f2f2f2;
}
}
32 changes: 32 additions & 0 deletions src/components/Common/Error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react'
import { Field } from 'react-final-form'

const defaultErrorStyles = {
color: 'red',
fontWeight: 'bold',
fontSize: '12px',
width: '100%',
height: '20px',
}

export const Error = ({ name, errorStyle }) => (
<Field
name={name}
subscription={{ touched: true, pristine: true, error: true }}
render={({ meta: { touched, pristine, error } }) => {
const errors = [].concat(error)

return (
<span>
{
errors.length
? errors.map((error, index) => (
<p className="error" key={index} style={errorStyle || defaultErrorStyles}>{(!pristine || touched) && error}</p>
))
: null
}
</span>
)
}}
/>
)
33 changes: 16 additions & 17 deletions src/components/Common/InputField2.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import React from 'react'
import { Error } from './Error'

export const InputField2 = ({ input, meta, side, label, type, description, disabled, errorStyle }) => {
return (
<div className={side}>
<label htmlFor={input.name} className="label">{label}</label>
<input
autoComplete="off"
className="input"
type={type}
disabled={disabled}
id={input.name}
{...input}
/>
<p className="description">{description}</p>
<p style={errorStyle}>{(!meta.pristine || meta.touched) && meta.error}</p>
</div>
)
}
export const InputField2 = (props) => (
<div className={props.side}>
<label htmlFor={props.input.name} className="label">{props.label}</label>
<input
autoComplete="off"
className="input"
type={props.type}
disabled={props.disabled}
id={props.input.name}
{...props.input}
/>
<p className="description">{props.description}</p>
<Error name={props.input.name} errorStyle={props.errorStyle}/>
</div>
)

197 changes: 197 additions & 0 deletions src/components/Common/TierBlock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import React from 'react'
import { Field } from 'react-final-form'
import { OnChange } from 'react-final-form-listeners'
import { InputField2 } from './InputField2'
import { WhitelistInputBlock } from './WhitelistInputBlock'
import {
composeValidators,
isDateInFuture,
isDateLaterThan,
isDatePreviousThan,
isDateSameOrLaterThan,
isDateSameOrPreviousThan,
isInteger,
isLessOrEqualThan,
isPositive,
isRequired,
validateTokenName,
} from '../../utils/validations'
import { DESCRIPTION, TEXT_FIELDS } from '../../utils/constants'

const {
ALLOWMODIFYING,
CROWDSALE_SETUP_NAME,
START_TIME,
END_TIME,
RATE,
SUPPLY
} = TEXT_FIELDS

const inputErrorStyle = {
color: 'red',
fontWeight: 'bold',
fontSize: '12px',
width: '100%',
height: '20px',
}

export const TierBlock = ({ fields, ...props }) => {
const validateTierName = (value) => validateTokenName(value)

const validateTierStartDate = (index) => (value, values) => {
const listOfValidations = [
isRequired(),
isDateInFuture(),
isDatePreviousThan("Should be previous than same tier's End Time")(values.tiers[index].endTime),
]

if (index > 0) {
listOfValidations.push(isDateSameOrLaterThan("Should be same or later than previous tier's End Time")(values.tiers[index - 1].endTime))
}

return composeValidators(...listOfValidations)(value)
}

const validateTierEndDate = (index) => (value, values) => {
const listOfValidations = [
isRequired(),
isDateInFuture(),
isDateLaterThan("Should be later than same tier's Start Time")(values.tiers[index].startTime),
]

if (index < values.tiers.length - 1) {
listOfValidations.push(isDateSameOrPreviousThan("Should be same or previous than next tier's Start Time")(values.tiers[index + 1].startTime))
}

return composeValidators(...listOfValidations)(value)
}

return (
<div>
{fields.map((name, index) => (
<div style={{ marginTop: '40px' }} className='steps-content container' key={index}>
<div className="hidden">
<div className="input-block-container">
<Field
name={`${name}.tier`}
validate={validateTierName}
errorStyle={inputErrorStyle}
component={InputField2}
type="text"
side="left"
label={CROWDSALE_SETUP_NAME}
description={DESCRIPTION.CROWDSALE_SETUP_NAME}
/>

<Field
name={`${name}.updatable`}
render={({ input }) => (
<div className='right'>
<label className="label">{ALLOWMODIFYING}</label>
<div className='radios-inline'>
<label className='radio-inline'>
<input
type='radio'
checked={input.value === 'on'}
onChange={() => input.onChange('on')}
value='on'
/>
<span className='title'>on</span>
</label>
<label className='radio-inline'>
<input
type='radio'
checked={input.value === 'off'}
value='off'
onChange={() => input.onChange('off')}
/>
<span className='title'>off</span>
</label>
</div>
<p className='description'>{DESCRIPTION.ALLOW_MODIFYING}</p>
</div>
)}
/>
</div>

<div className="input-block-container">
<Field
name={`${name}.startTime`}
component={InputField2}
validate={validateTierStartDate(index)}
errorStyle={inputErrorStyle}
type="datetime-local"
side="left"
label={START_TIME}
description={DESCRIPTION.START_TIME}
/>
<Field
name={`${name}.endTime`}
component={InputField2}
validate={validateTierEndDate(index)}
errorStyle={inputErrorStyle}
type="datetime-local"
side="right"
label={END_TIME}
description={DESCRIPTION.END_TIME}
/>
</div>

<div className="input-block-container">
<Field
name={`${name}.rate`}
component={InputField2}
validate={composeValidators(
isPositive(),
isInteger(),
isLessOrEqualThan('Should not be greater than 1 quintillion (10^18)')('1e18')
)}
errorStyle={inputErrorStyle}
type="text"
side="left"
label={RATE}
description={DESCRIPTION.RATE}
/>
<Field
name={`${name}.supply`}
component={InputField2}
validate={isPositive()}
errorStyle={inputErrorStyle}
type="text"
side="right"
label={SUPPLY}
description={DESCRIPTION.SUPPLY}
/>
{
/*
* TODO: REVIEW. I'm not sure about this approach.
* But it worked for me to keep the error messages properly updated for the minCap field.
*/
}
<Field name="minCap" subscription={{}}>
{({ input: { onChange } }) => (
<OnChange name={`${name}.supply`}>
{() => {
onChange(0)
onChange(props.minCap)
}}
</OnChange>
)}
</Field>
</div>
</div>
{
props.tierStore.tiers[0].whitelistEnabled === 'yes' ? (
<div>
<div className="section-title">
<p className="title">Whitelist</p>
</div>
<WhitelistInputBlock num={index}/>
</div>
) : null
}
</div>
))}
</div>
)
}
21 changes: 21 additions & 0 deletions src/components/Common/WhenFieldChanges.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import { Field } from 'react-final-form'
import { OnChange } from 'react-final-form-listeners'

export const WhenFieldChanges = ({ field, becomes, set, to }) => (
<Field name={set} subscription={{}}>
{(
// No subscription. We only use Field to get to the change function
{ input: { onChange } }
) => (
<OnChange name={field}>
{value => {
if (value === becomes) {
onChange(to)
}
}}
</OnChange>
)}
</Field>
)

Loading

0 comments on commit 1c41784

Please sign in to comment.