Skip to content
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

Merged
merged 52 commits into from
Apr 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
568d4c0
WIP
Mar 27, 2018
488f8e0
Migrate step three form to final-form
Mar 28, 2018
de0d86a
Custom GasPriceInput component
Mar 28, 2018
ec5445b
Merge remote-tracking branch 'origin/use-final-form-step-three' into …
fernandomg Mar 28, 2018
b883253
Add whitelist block
Mar 28, 2018
6837eb4
Merge remote-tracking branch 'origin/use-final-form-step-three' into …
fernandomg Mar 28, 2018
02bf232
Set initial value for wallet address
fernandomg Mar 29, 2018
c1f7cec
Set minCap default value to empty string
fernandomg Mar 29, 2018
f174929
Move add tier button to 'button-container' block
fernandomg Mar 29, 2018
8f719df
Use GasPriceStore values
fernandomg Mar 29, 2018
2ff0719
Use constant values for CUSTOM field
fernandomg Mar 29, 2018
050b458
Unified radios block
fernandomg Mar 29, 2018
3064fb7
Fix custom gas input styles
fernandomg Mar 29, 2018
cae8cea
Add loading overlay
fernandomg Mar 29, 2018
c302fc1
Fix Tier dates initialization
fernandomg Mar 29, 2018
5d2c38d
Add ability to display an array of error messages
fernandomg Mar 29, 2018
9a2aadf
Extend validations and tests
fernandomg Mar 29, 2018
827fb64
Add react-final-form-listener as a dependency
fernandomg Mar 29, 2018
48431ac
Create WhenFieldChanges component
fernandomg Mar 29, 2018
a912198
Add validations for minCap
fernandomg Mar 29, 2018
a718172
Add rate validations
fernandomg Mar 29, 2018
cc8093f
Set minCap to 0 (zero) when whitelist is enabled
fernandomg Apr 3, 2018
2ba546f
Remove 'instant' from collection of Gas Prices
fernandomg Apr 3, 2018
e75b002
Add gasPricesInGwei computed method
fernandomg Apr 3, 2018
501a58e
Create 'Error' component for final-form error display
fernandomg Apr 3, 2018
975db02
Modify InputField2 to use Error component
fernandomg Apr 3, 2018
dd4d2f2
Add Error component to GasPriceInput
fernandomg Apr 3, 2018
1996ca0
Modify customGasPrice initial value
fernandomg Apr 3, 2018
529364c
Add isGreaterOrEqualThan validation
fernandomg Apr 3, 2018
520be8e
Validate Custom GasPrice
fernandomg Apr 3, 2018
6d57d62
Cleanup gasPrice related code from stepThree
fernandomg Apr 3, 2018
fd4ef7d
Set minCap initial value to 0 (zero)
fernandomg Apr 3, 2018
6396fec
WIP (date validations)
fernandomg Apr 3, 2018
877cb82
Validate tiers' startTime
fernandomg Apr 3, 2018
39491d9
Validate tiers' endTime
fernandomg Apr 4, 2018
f4f6332
Update StepTwoFrom snapshot
fernandomg Apr 4, 2018
304c9f2
Add tests for validations
fernandomg Apr 4, 2018
03974ea
Create TierBlock component
fernandomg Apr 4, 2018
8a872c3
Create StepThreeForm component
fernandomg Apr 4, 2018
c3691cc
Refactor StepThree component
fernandomg Apr 4, 2018
c4689ef
Update validations tests
fernandomg Apr 4, 2018
d6bd92f
Fix whitelist block display
fernandomg Apr 4, 2018
2ace6bb
Merge branch 'master' into use-final-form-step-three
fernandomg Apr 4, 2018
fbcfe76
Update token-wizard-test-automation submodule
fernandomg Apr 5, 2018
7e5def7
Initialize walletAddress from web3Store
Apr 5, 2018
a3dcd37
Update index.js
Apr 5, 2018
f86453d
Fix hardcoded name attr
fernandomg Apr 5, 2018
d561382
Add className to errors
fernandomg Apr 5, 2018
704ee29
Update token-wizard-test-automation submodule
fernandomg Apr 6, 2018
42cdd02
Update StepTwoForm snapshot
fernandomg Apr 6, 2018
adfe883
Merge branch 'master' into use-final-form-step-three
fernandomg Apr 6, 2018
7ace876
Update e2e tests submodule (token-wizard-test-automation)
fernandomg Apr 6, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

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 whole meta object from props and use that?

If I'm understanding this correctly, this means that we'll have two Fields with the same name 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?

Copy link
Contributor Author

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.

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={{}}>
Copy link
Contributor

Choose a reason for hiding this comment

The 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 change function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change function is a Form's property, which will force us to transfer props from StepThreeForm to TierBlock.

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>
)
}
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