-
Notifications
You must be signed in to change notification settings - Fork 215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(Refactor) Use final form step three #767
Changes from 47 commits
568d4c0
488f8e0
de0d86a
ec5445b
b883253
6837eb4
02bf232
c1f7cec
f174929
8f719df
2ff0719
050b458
3064fb7
cae8cea
c302fc1
5d2c38d
9a2aadf
827fb64
48431ac
a912198
a718172
cc8093f
2ba546f
e75b002
501a58e
975db02
dd4d2f2
1996ca0
529364c
520be8e
6d57d62
fd4ef7d
6396fec
877cb82
39491d9
f4f6332
304c9f2
03974ea
8a872c3
c3691cc
c4689ef
d6bd92f
2ace6bb
fbcfe76
7e5def7
a3dcd37
f86453d
d561382
704ee29
42cdd02
adfe883
7ace876
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Large diffs are not rendered by default.
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 key={index} style={errorStyle || defaultErrorStyles}>{(!pristine || touched) && error}</p> | ||
)) | ||
: null | ||
} | ||
</span> | ||
) | ||
}} | ||
/> | ||
) |
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> | ||
) | ||
|
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={{}}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here thing (as in the Error component). Is the Field necessary? Can't we use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Whereas keeping the current approach makes easier for us to extend this sole component, without needing to modify any parent component. |
||
{({ 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> | ||
) | ||
} |
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> | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand this. Is the
Field
component really necessary? Couldn't we just receive the wholemeta
object from props and use that?If I'm understanding this correctly, this means that we'll have two
Field
s with the samename
attribute, but one of them is used only to show errors. It doesn't sound right to me, is there a good reason for this approach?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a way to make the
Error
component react only to a subset of items' changes (subscription
) for the specified field.