A few changes to API for better Wrapped component support (#472)

* Add propTypes to API docs; move innerRef doc to match location in table of contents

* Change HOC to Wrapper (be consistent everywhere)

* Inject defaultProps.value from wrapped component into Formsy.Wrapper

This way users can provide a default value for their form field.
Previously, this was possible because mixins were on the same level as the actual component.

* Add optional parameter to setState to disable validation

* Update prepublish script to clean `lib`; rebuild release folder
This commit is contained in:
Tom B 2017-08-11 14:53:00 -04:00 committed by Aesop Wolf
parent 998e2be44f
commit b413f9db77
7 changed files with 82 additions and 32 deletions

47
API.md
View File

@ -37,6 +37,7 @@
- [isFormSubmitted()](#isformsubmitted)
- [validate](#validate)
- [formNoValidate](#formnovalidate)
- [Formsy.propTypes](#formsyproptypes)
- [Formsy.addValidationRule](#formsyaddvalidationrule)
- [Validators](#validators)
@ -238,6 +239,26 @@ export default Wrapper(MyInput);
```
The name is required to register the form input component in the form. You can also use dot notation. This will result in the "form model" being a nested object. `{email: 'value', address: {street: 'value'}}`.
#### <a name="innerRef">innerRef</a>
Use an `innerRef` prop to get a reference to your DOM node.
```jsx
class MyForm extends React.Component {
componentDidMount() {
this.searchInput.focus()
}
render() {
return (
<Formsy.Form>
<MyInput name="search" innerRef={(c) => { this.searchInput = c; }} />
</Formsy.Form>
);
}
}
```
#### <a name="value">value</a>
```jsx
<MyInputComponent name="email" value="My initial value"/>
@ -314,7 +335,7 @@ class MyInput extends React.Component {
```
Gets the current value of the form input component.
#### <a name="setvalue">setValue(value)</a>
#### <a name="setvalue">setValue(value[, validate = true])</a>
```jsx
class MyInput extends React.Component {
changeValue = (event) => {
@ -329,6 +350,8 @@ class MyInput extends React.Component {
```
Sets the value of your form input component. Notice that it does not have to be a text input. Anything can set a value on the component. Think calendars, checkboxes, autocomplete stuff etc. Running this method will trigger a **setState()** on the component and do a render.
You can also set the value without forcing an immediate validation by passing a second parameter of `false`. This is useful in cases where you want to only validate on blur / change / etc.
#### <a name="resetvalue">resetValue()</a>
```jsx
class MyInput extends React.Component {
@ -559,22 +582,16 @@ class MyInput extends React.Component {
}
```
#### <a name="innerRef">innerRef</a>
Use an `innerRef` prop to get a reference to your DOM node.
### <a name="formsyproptypes">Formsy.propTypes</a>
If you are using React's PropType typechecking, you can spread Formsy's propTypes into your local propTypes to avoid having to repeatedly add `Formsy.Wrapper`'s methods to your components.
```jsx
class MyForm extends React.Component {
componentDidMount() {
this.searchInput.focus()
}
render() {
return (
<Formsy.Form>
<MyInput name="search" innerRef={(c) => { this.searchInput = c; }} />
</Formsy.Form>
);
class MyInput extends React.Component {
static propTypes = {
firstProp: PropTypes.string,
secondProp: PropTypes.object,
...Formsy.propTypes
}
...
}
```

View File

@ -11,7 +11,7 @@
"build": "NODE_ENV=production webpack -p --config webpack.production.config.js",
"test": "babel-node testrunner",
"examples": "webpack-dev-server --config examples/webpack.config.js --content-base examples",
"prepublish": "babel ./src/ -d ./lib/"
"prepublish": "rm -Rf ./lib && babel ./src/ -d ./lib/"
},
"author": "Christian Alfoni",
"license": "MIT",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -35,11 +35,11 @@ module.exports = function (Component) {
static displayName = 'Formsy(' + getDisplayName(Component) + ')';
state = {
_value: this.props.value,
_value: typeof this.props.value !== 'undefined' ? this.props.value : Component.defaultProps ? Component.defaultProps.value : undefined,
_isRequired: false,
_isValid: true,
_isPristine: true,
_pristineValue: this.props.value,
_pristineValue: typeof this.props.value !== 'undefined' ? this.props.value : Component.defaultProps ? Component.defaultProps.value : undefined,
_validationError: [],
_externalError: null,
_formSubmitted: false
@ -100,15 +100,22 @@ module.exports = function (Component) {
this._requiredValidations = required === true ? {isDefaultRequiredValue: true} : convertValidationsToObject(required);
}
// We validate after the value has been set
setValue = (value) => {
this.setState({
_value: value,
_isPristine: false
}, () => {
this.context.formsy.validate(this);
//this.props._validate(this);
});
// By default, we validate after the value has been set.
// A user can override this and pass a second parameter of `false` to skip validation.
setValue = (value, validate = true) => {
if (!validate) {
this.setState({
_value: value
});
} else {
this.setState({
_value: value,
_isPristine: false
}, () => {
this.context.formsy.validate(this);
//this.props._validate(this);
});
}
}
resetValue = () => {

View File

@ -4,11 +4,11 @@ var Formsy = {};
var validationRules = require('./validationRules.js');
var formDataToObject = require('form-data-to-object');
var utils = require('./utils.js');
var HOC = require('./HOC.js');
var Wrapper = require('./Wrapper.js');
var options = {};
var emptyArray = [];
Formsy.Wrapper = HOC;
Formsy.Wrapper = Wrapper;
Formsy.propTypes = {
setValidations: PropTypes.func,
setValue: PropTypes.func,

View File

@ -26,6 +26,33 @@ export default {
},
'should only set the value and not validate when calling setValue(val, false)': function (test) {
const Input = Formsy.Wrapper(class TestInput extends React.Component {
updateValue = (event) => {
this.props.setValue(event.target.value, false);
}
render() {
return <input type="text" value={this.props.getValue()} onChange={this.updateValue}/>;
}
})
const form = TestUtils.renderIntoDocument(
<Formsy.Form>
<Input name="foo" value="foo" innerRef="comp" />
</Formsy.Form>
);
const inputComponent = TestUtils.findRenderedComponentWithType(form, Input);
const setStateSpy = sinon.spy(inputComponent, 'setState');
const inputElement = TestUtils.findRenderedDOMComponentWithTag(form, 'INPUT');
test.equal(setStateSpy.called, false);
TestUtils.Simulate.change(inputElement, {target: {value: 'foobar'}});
test.equal(setStateSpy.calledOnce, true);
test.equal(setStateSpy.calledWithExactly({ _value: 'foobar' }), true);
test.done();
},
'should set back to pristine value when running reset': function (test) {
let reset = null;
@ -317,7 +344,6 @@ export default {
},
'should override all error messages with error messages passed by form': function (test) {
class TestForm extends React.Component {