diff --git a/__tests__/Validation-spec.tsx b/__tests__/Validation-spec.tsx index f2617dbe..085a7744 100644 --- a/__tests__/Validation-spec.tsx +++ b/__tests__/Validation-spec.tsx @@ -3,21 +3,32 @@ import sinon from 'sinon'; import { mount } from 'enzyme'; import Formsy, { withFormsy } from '../src'; -import { InputFactory } from '../__test_utils__/TestInput'; import immediate from '../__test_utils__/immediate'; +import { InputFactory } from '../__test_utils__/TestInput'; +import { WrapperInstanceMethods, PassDownProps } from '../src/Wrapper'; -class MyTest extends React.Component { - static defaultProps = { type: 'text' }; +class MyTest extends React.Component<{ type?: string } & PassDownProps> { + public static defaultProps = { type: 'text' }; handleChange = event => { - this.props.setValue(event.target.value); + const { setValue } = this.props; + setValue(event.target.value); }; render() { - return ; + const { type, value } = this.props; + return ; } } -const FormsyTest = withFormsy(MyTest); +const FormsyTest = withFormsy<{ type?: string }, string>(MyTest); + +function getInputInstance(inputComponent) { + return inputComponent.instance() as WrapperInstanceMethods; +} + +function getFormInstance(formComponent) { + return formComponent.instance() as Formsy; +} describe('Validation', () => { it('should reset only changed form element when external error is passed', () => { @@ -31,34 +42,14 @@ describe('Validation', () => { const input = form.find('input').at(0); const inputComponents = form.find(FormsyTest); - form.instance().submit(); - expect( - inputComponents - .at(0) - .instance() - .isValid(), - ).toEqual(false); - expect( - inputComponents - .at(1) - .instance() - .isValid(), - ).toEqual(false); + getFormInstance(form).submit(); + expect(getInputInstance(inputComponents.at(0)).isValid()).toEqual(false); + expect(getInputInstance(inputComponents.at(1)).isValid()).toEqual(false); input.simulate('change', { target: { value: 'bar' } }); immediate(() => { - expect( - inputComponents - .at(0) - .instance() - .isValid(), - ).toEqual(true); - expect( - inputComponents - .at(1) - .instance() - .isValid(), - ).toEqual(false); + expect(getInputInstance(inputComponents.at(0)).isValid()).toEqual(true); + expect(getInputInstance(inputComponents).isValid()).toEqual(false); }); }); @@ -72,13 +63,13 @@ describe('Validation', () => { const input = form.find('input'); const inputComponent = form.find(FormsyTest); - form.instance().submit(); - expect(inputComponent.instance().isValid()).toEqual(false); + getFormInstance(form).submit(); + expect(getInputInstance(inputComponent).isValid()).toEqual(false); input.simulate('change', { target: { value: 'bar' } }); immediate(() => { - expect(inputComponent.getValue()).toEqual('bar'); - expect(inputComponent.instance().isValid()).toEqual(false); + expect(getInputInstance(inputComponent).getValue()).toEqual('bar'); + expect(getInputInstance(inputComponent).isValid()).toEqual(false); }); }); @@ -141,14 +132,12 @@ describe('Validation', () => { }); it('should provide invalidate callback on onValidSubmit', () => { - class TestForm extends React.Component { - render() { - return ( - invalidate({ foo: 'bar' })}> - - - ); - } + function TestForm() { + return ( + invalidate({ foo: 'bar' })}> + + + ); } const form = mount(); @@ -156,43 +145,39 @@ describe('Validation', () => { const formEl = form.find('form'); const input = form.find(FormsyTest); formEl.simulate('submit'); - expect(input.instance().isValid()).toEqual(false); + expect(getInputInstance(input).isValid()).toEqual(false); }); it('should provide invalidate callback on onInvalidSubmit', () => { - class TestForm extends React.Component { - render() { - return ( - invalidate({ foo: 'bar' })}> - - - ); - } + function TestForm() { + return ( + invalidate({ foo: 'bar' })}> + + + ); } const form = mount(); const formEl = form.find('form'); const input = form.find(FormsyTest); formEl.simulate('submit'); - expect(input.instance().getErrorMessage()).toEqual('bar'); + expect(getInputInstance(input).getErrorMessage()).toEqual('bar'); }); it('should not invalidate inputs on external errors with preventExternalInvalidation prop', () => { - class TestForm extends React.Component { - render() { - return ( - invalidate({ foo: 'bar' })}> - - - ); - } + function TestForm() { + return ( + invalidate({ foo: 'bar' })}> + + + ); } const form = mount(); const formEl = form.find('form'); const input = form.find(FormsyTest); formEl.simulate('submit'); - expect(input.instance().isValid()).toEqual(true); + expect(getInputInstance(input).isValid()).toEqual(true); }); it('should invalidate inputs on external errors without preventExternalInvalidation prop', () => { @@ -210,6 +195,6 @@ describe('Validation', () => { const formEl = form.find('form'); const input = form.find(FormsyTest); formEl.simulate('submit'); - expect(input.instance().isValid()).toEqual(false); + expect(getInputInstance(input).isValid()).toEqual(false); }); }); diff --git a/src/Wrapper.ts b/src/Wrapper.ts index 405dfb72..4bfe7c63 100644 --- a/src/Wrapper.ts +++ b/src/Wrapper.ts @@ -9,12 +9,8 @@ import { RequiredValidation, Validations, WrappedComponentClass } from './interf const convertValidationsToObject = (validations: false | Validations): Validations => { if (typeof validations === 'string') { return validations.split(/,(?![^{[]*[}\]])/g).reduce((validationsAccumulator, validation) => { - let args = validation.split(':'); - const validateMethod = args.shift(); - - if (typeof validateMethod !== 'string') { - throw new Error('Formsy encountered unexpected problem parsing validation string'); - } + let args: string[] = validation.split(':'); + const validateMethod: string = args.shift(); args = args.map(arg => { try { @@ -88,6 +84,12 @@ export interface InjectedProps { showRequired: boolean; } +export interface WrapperInstanceMethods { + isValid: () => boolean; + getValue: () => any; + getErrorMessage: () => any; +} + export type PassDownProps = WrapperProps & InjectedProps; export { propTypes }; @@ -103,7 +105,7 @@ function getDisplayName(component: WrappedComponentClass) { export default function( WrappedComponent: React.ComponentType>, ): React.ComponentType, keyof InjectedProps>> { - return class extends React.Component, WrapperState> { + return class extends React.Component, WrapperState> implements WrapperInstanceMethods { public validations?: Validations; public requiredValidations?: Validations; @@ -287,7 +289,7 @@ export default function( public render() { const { innerRef } = this.props; - const propsForElement: PassDownProps = { + const propsForElement: T & PassDownProps = { ...this.props, errorMessage: this.getErrorMessage(), errorMessages: this.getErrorMessages(), @@ -310,7 +312,7 @@ export default function( propsForElement.ref = innerRef; } - return React.createElement(WrappedComponent, propsForElement as any); + return React.createElement(WrappedComponent, propsForElement); } }; } diff --git a/src/index.ts b/src/index.ts index b77aba06..6275f0fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -370,7 +370,7 @@ class Formsy extends React.Component { public isChanged = () => !utils.isSame(this.getPristineValues(), this.getCurrentValues()); // Update model, submit to url prop and send the model - public submit = event => { + public submit = (event?: any) => { const { onSubmit, onValidSubmit, onInvalidSubmit } = this.props; const { isValid } = this.state;