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, handleSuccess: React.PropTypes.func,
getFormData: React.PropTypes.func, getFormData: React.PropTypes.func,
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.object, React.PropTypes.arrayOf(React.PropTypes.element),
React.PropTypes.array React.PropTypes.element
]), ]),
className: React.PropTypes.string, className: React.PropTypes.string,
spinner: React.PropTypes.element, spinner: React.PropTypes.element,
@ -165,14 +165,43 @@ let Form = React.createClass({
this.setState({errors: []}); 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() { getButtons() {
if (this.state.submitted){ let buttons = null;
let isFormValid = this.checkFormValidity();
if(this.state.submitted) {
return this.props.spinner; return this.props.spinner;
} }
if (this.props.buttons){ if(this.props.buttons) {
return this.props.buttons;
buttons = React.cloneElement(this.props.buttons, {
disabled: !isFormValid
});
return buttons;
} }
let buttons = null;
if (this.state.edited){ if (this.state.edited){
buttons = ( buttons = (

View File

@ -27,7 +27,10 @@ let RegisterPieceForm = React.createClass({
isFineUploaderActive: React.PropTypes.bool, isFineUploaderActive: React.PropTypes.bool,
isFineUploaderEditable: React.PropTypes.bool, isFineUploaderEditable: React.PropTypes.bool,
enableLocalHashing: 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 onLoggedOut: React.PropTypes.func
}, },
@ -74,16 +77,11 @@ let RegisterPieceForm = React.createClass({
}); });
}, },
setIsUploadReady(isReady) {
this.setState({
isUploadReady: isReady
});
},
render() { render() {
let currentUser = this.state.currentUser; let currentUser = this.state.currentUser;
let enableLocalHashing = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false; let enableLocalHashing = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false;
enableLocalHashing = enableLocalHashing && this.props.enableLocalHashing; enableLocalHashing = enableLocalHashing && this.props.enableLocalHashing;
return ( return (
<Form <Form
className="ascribe-form-bordered" className="ascribe-form-bordered"
@ -106,11 +104,11 @@ let RegisterPieceForm = React.createClass({
<h3>{this.props.headerMessage}</h3> <h3>{this.props.headerMessage}</h3>
</div> </div>
<Property <Property
ignoreFocus={true}> name="digital_work_key"
ignoreFocus={true}
required={true}>
<FileUploader <FileUploader
submitKey={this.submitKey} submitKey={this.submitKey}
setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={isReadyForFormSubmission}
isFineUploaderActive={this.props.isFineUploaderActive} isFineUploaderActive={this.props.isFineUploaderActive}
onLoggedOut={this.props.onLoggedOut} onLoggedOut={this.props.onLoggedOut}
editable={this.props.isFineUploaderEditable} editable={this.props.isFineUploaderEditable}
@ -118,7 +116,8 @@ let RegisterPieceForm = React.createClass({
</Property> </Property>
<Property <Property
name='artist_name' name='artist_name'
label={getLangText('Artist Name')}> label={getLangText('Artist Name')}
required={true}>
<input <input
type="text" type="text"
placeholder="(e.g. Andy Warhol)" placeholder="(e.g. Andy Warhol)"
@ -126,7 +125,8 @@ let RegisterPieceForm = React.createClass({
</Property> </Property>
<Property <Property
name='title' name='title'
label={getLangText('Title')}> label={getLangText('Title')}
required={true}>
<input <input
type="text" type="text"
placeholder="(e.g. 32 Campbell's Soup Cans)" placeholder="(e.g. 32 Campbell's Soup Cans)"
@ -134,7 +134,8 @@ let RegisterPieceForm = React.createClass({
</Property> </Property>
<Property <Property
name='date_created' name='date_created'
label={getLangText('Year Created')}> label={getLangText('Year Created')}
required={true}>
<input <input
type="number" type="number"
placeholder="(e.g. 1962)" placeholder="(e.g. 1962)"
@ -149,7 +150,6 @@ let RegisterPieceForm = React.createClass({
let FileUploader = React.createClass({ let FileUploader = React.createClass({
propTypes: { propTypes: {
setIsUploadReady: React.PropTypes.func,
submitKey: React.PropTypes.func, submitKey: React.PropTypes.func,
isReadyForFormSubmission: React.PropTypes.func, isReadyForFormSubmission: React.PropTypes.func,
onClick: React.PropTypes.func, onClick: React.PropTypes.func,
@ -160,7 +160,31 @@ let FileUploader = React.createClass({
isFineUploaderActive: React.PropTypes.bool, isFineUploaderActive: React.PropTypes.bool,
onLoggedOut: React.PropTypes.func, onLoggedOut: React.PropTypes.func,
editable: React.PropTypes.bool, 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() { render() {
@ -179,8 +203,8 @@ let FileUploader = React.createClass({
itemLimit: 100000, itemLimit: 100000,
sizeLimit: '25000000000' sizeLimit: '25000000000'
}} }}
setIsUploadReady={this.props.setIsUploadReady} setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={this.props.isReadyForFormSubmission} isReadyForFormSubmission={isReadyForFormSubmission}
areAssetsDownloadable={false} areAssetsDownloadable={false}
areAssetsEditable={this.props.isFineUploaderActive} areAssetsEditable={this.props.isFineUploaderActive}
signature={{ signature={{

View File

@ -21,7 +21,8 @@ let InputCheckbox = React.createClass({
children: React.PropTypes.oneOfType([ children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
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 // 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() { onChange() {
// On every change, we're inversing the input's value // On every change, we're inversing the input's value
let inverseValue = !this.refs.checkbox.getDOMNode().checked; let inverseValue = !this.refs.checkbox.getDOMNode().checked;
@ -62,15 +78,6 @@ let InputCheckbox = React.createClass({
// pass it to the state // pass it to the state
this.setState({value: inverseValue}); 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() { render() {

View File

@ -26,14 +26,19 @@ let Property = React.createClass({
React.PropTypes.arrayOf(React.PropTypes.element), React.PropTypes.arrayOf(React.PropTypes.element),
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() { getDefaultProps() {
return { return {
editable: true, editable: true,
hidden: false, hidden: false,
className: '' className: '',
required: false
}; };
}, },
@ -45,7 +50,8 @@ let Property = React.createClass({
initialValue: null, initialValue: null,
value: null, value: null,
isFocused: false, 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 // the state of value should only be set if its not undefined and
// actually references something // actually references something
if(typeof childInput.getDOMNode().value !== 'undefined') { if(typeof childInput.getDOMNode().value !== 'undefined') {
this.setState({ 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 // 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 // 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 // in their state that will be picked up by property.js
} else if(childInput.state && typeof childInput.state.value !== 'undefined') { } else if(childInput.state && typeof childInput.state.value !== 'undefined') {
this.setState({ 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 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() { reset() {
@ -90,13 +118,14 @@ let Property = React.createClass({
}, },
handleChange(event) { handleChange(event) {
this.props.handleChange(event); this.props.handleChange(event);
if ('onChange' in this.props) { if ('onChange' in this.props) {
this.props.onChange(event); this.props.onChange(event);
} }
this.setState({value: event.target.value}); this.setState({
value: event.target.value
});
}, },
handleFocus() { handleFocus() {

View File

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