1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-23 01:39:36 +01:00

form validation first cut

This commit is contained in:
Tim Daubenschütz 2015-08-18 17:57:14 +02:00
parent e88612704f
commit 8a814c287c
5 changed files with 131 additions and 41 deletions

View File

@ -23,8 +23,8 @@ let Form = React.createClass({
handleSuccess: React.PropTypes.func,
getFormData: React.PropTypes.func,
children: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.array
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]),
className: React.PropTypes.string,
spinner: React.PropTypes.element,
@ -165,14 +165,43 @@ let Form = React.createClass({
this.setState({errors: []});
},
checkFormValidity() {
let requiredProps = [];
Object
.keys(this.refs)
.forEach((refName) => {
if(this.refs[refName].props.required) {
requiredProps.push(this.refs[refName]);
}
});
let validProps = requiredProps.filter((property) => property.state.isValid);
if(requiredProps.length === validProps.length) {
return true;
} else {
return false;
}
},
getButtons() {
if (this.state.submitted){
let buttons = null;
let isFormValid = this.checkFormValidity();
if(this.state.submitted) {
return this.props.spinner;
}
if (this.props.buttons){
return this.props.buttons;
if(this.props.buttons) {
buttons = React.cloneElement(this.props.buttons, {
disabled: !isFormValid
});
return buttons;
}
let buttons = null;
if (this.state.edited){
buttons = (

View File

@ -27,7 +27,10 @@ let RegisterPieceForm = React.createClass({
isFineUploaderActive: React.PropTypes.bool,
isFineUploaderEditable: React.PropTypes.bool,
enableLocalHashing: React.PropTypes.bool,
children: React.PropTypes.element,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]),
onLoggedOut: React.PropTypes.func
},
@ -74,16 +77,11 @@ let RegisterPieceForm = React.createClass({
});
},
setIsUploadReady(isReady) {
this.setState({
isUploadReady: isReady
});
},
render() {
let currentUser = this.state.currentUser;
let enableLocalHashing = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false;
enableLocalHashing = enableLocalHashing && this.props.enableLocalHashing;
return (
<Form
className="ascribe-form-bordered"
@ -106,11 +104,11 @@ let RegisterPieceForm = React.createClass({
<h3>{this.props.headerMessage}</h3>
</div>
<Property
ignoreFocus={true}>
name="digital_work_key"
ignoreFocus={true}
required={true}>
<FileUploader
submitKey={this.submitKey}
setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={isReadyForFormSubmission}
isFineUploaderActive={this.props.isFineUploaderActive}
onLoggedOut={this.props.onLoggedOut}
editable={this.props.isFineUploaderEditable}
@ -118,7 +116,8 @@ let RegisterPieceForm = React.createClass({
</Property>
<Property
name='artist_name'
label={getLangText('Artist Name')}>
label={getLangText('Artist Name')}
required={true}>
<input
type="text"
placeholder="(e.g. Andy Warhol)"
@ -126,7 +125,8 @@ let RegisterPieceForm = React.createClass({
</Property>
<Property
name='title'
label={getLangText('Title')}>
label={getLangText('Title')}
required={true}>
<input
type="text"
placeholder="(e.g. 32 Campbell's Soup Cans)"
@ -134,7 +134,8 @@ let RegisterPieceForm = React.createClass({
</Property>
<Property
name='date_created'
label={getLangText('Year Created')}>
label={getLangText('Year Created')}
required={true}>
<input
type="number"
placeholder="(e.g. 1962)"
@ -149,7 +150,6 @@ let RegisterPieceForm = React.createClass({
let FileUploader = React.createClass({
propTypes: {
setIsUploadReady: React.PropTypes.func,
submitKey: React.PropTypes.func,
isReadyForFormSubmission: React.PropTypes.func,
onClick: React.PropTypes.func,
@ -160,7 +160,31 @@ let FileUploader = React.createClass({
isFineUploaderActive: React.PropTypes.bool,
onLoggedOut: React.PropTypes.func,
editable: React.PropTypes.bool,
enableLocalHashing: React.PropTypes.bool
enableLocalHashing: React.PropTypes.bool,
// is provided by Property
onChange: React.PropTypes.func
},
getInitialState() {
return {
value: false
};
},
setIsUploadReady(isReady) {
this.setState({
value: isReady
});
// Property is listening to this.state.value to determine
// if the form is ready. As it will access this.state.value directly
// we need to call onChange after the state submission has been done.
this.props.onChange({
target: {
value: isReady
}
});
},
render() {
@ -179,8 +203,8 @@ let FileUploader = React.createClass({
itemLimit: 100000,
sizeLimit: '25000000000'
}}
setIsUploadReady={this.props.setIsUploadReady}
isReadyForFormSubmission={this.props.isReadyForFormSubmission}
setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={isReadyForFormSubmission}
areAssetsDownloadable={false}
areAssetsEditable={this.props.isFineUploaderActive}
signature={{

View File

@ -21,7 +21,8 @@ let InputCheckbox = React.createClass({
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
])
]),
onChange: React.PropTypes.bool
},
// As HTML inputs, we're setting the default value for an input to checked === false
@ -55,6 +56,21 @@ let InputCheckbox = React.createClass({
}
},
// after the component was updated and the state value changed,
// we need to call onChange from property to refresh the state.value of property
componentDidUpdate(prevProps, prevState) {
if(prevState.value !== this.state.value) {
// and also call Property's onChange method
// (in this case we're mocking event.target.value, since we can not use the event
// coming from onChange. Its coming from the span (user is clicking on the span) and not the input)
this.props.onChange({
target: {
value: this.state.value
}
});
}
},
onChange() {
// On every change, we're inversing the input's value
let inverseValue = !this.refs.checkbox.getDOMNode().checked;
@ -62,15 +78,6 @@ let InputCheckbox = React.createClass({
// pass it to the state
this.setState({value: inverseValue});
// and also call Property's onChange method
// (in this case we're mocking event.target.value, since we can not use the event
// coming from onChange. Its coming from the span (user is clicking on the span) and not the input)
this.props.onChange({
target: {
value: inverseValue
}
});
},
render() {

View File

@ -26,14 +26,19 @@ let Property = React.createClass({
React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.element
]),
style: React.PropTypes.object
style: React.PropTypes.object,
required: React.PropTypes.bool,
// is a std reg exp as seen from HTML inputs
pattern: React.PropTypes.string
},
getDefaultProps() {
return {
editable: true,
hidden: false,
className: ''
className: '',
required: false
};
},
@ -45,7 +50,8 @@ let Property = React.createClass({
initialValue: null,
value: null,
isFocused: false,
errors: null
errors: null,
isValid: false
};
},
@ -56,8 +62,10 @@ let Property = React.createClass({
// the state of value should only be set if its not undefined and
// actually references something
if(typeof childInput.getDOMNode().value !== 'undefined') {
this.setState({
value: childInput.getDOMNode().value
value: childInput.getDOMNode().value,
isValid: this.checkValidity(childInput.getDOMNode().value)
});
// When implementing custom input components, their value isn't exposed like the one
@ -65,8 +73,10 @@ let Property = React.createClass({
// To enable developers to create input elements, they can expose a property called value
// in their state that will be picked up by property.js
} else if(childInput.state && typeof childInput.state.value !== 'undefined') {
this.setState({
value: childInput.state.value
value: childInput.state.value,
isValid: this.checkValidity(childInput.state.value)
});
}
@ -75,6 +85,24 @@ let Property = React.createClass({
initialValue: childInput.props.defaultValue
});
}
},
checkValidity(value) {
if(this.props.required && value) {
if(this.props.pattern && value && value.match(this.props.pattern)) {
// if the value is matching the provided pattern
return true;
} else if(!this.props.pattern) {
// if no pattern was provided, everything is evaluated to true
return true;
} else {
// if the pattern was provided but the input didn't match it
return false;
}
} else {
return false;
}
},
reset() {
@ -90,13 +118,14 @@ let Property = React.createClass({
},
handleChange(event) {
this.props.handleChange(event);
if ('onChange' in this.props) {
this.props.onChange(event);
}
this.setState({value: event.target.value});
this.setState({
value: event.target.value
});
},
handleFocus() {

View File

@ -153,7 +153,8 @@ let CylandRegisterPiece = React.createClass({
<Property
name="terms"
className="ascribe-settings-property-collapsible-toggle"
style={{paddingBottom: 0}}>
style={{paddingBottom: 0}}
required={true}>
<InputCheckbox>
<span>
{' ' + getLangText('I agree to the Terms of Service of Cyland Archive') + ' '}