Skip to content

Commit

Permalink
Respect dot notation in reset
Browse files Browse the repository at this point in the history
  • Loading branch information
rkuykendall committed Feb 17, 2020
1 parent bebefbe commit 1124e4b
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 127 deletions.
28 changes: 18 additions & 10 deletions __tests__/Formsy-spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -675,26 +675,29 @@ describe('value === false', () => {
});

it('should be able to reset the form using custom data', () => {
class TestForm extends React.Component<{}, { value: boolean | string }> {
class TestForm extends React.Component<{}, { value: number; valueDeep: number }> {
constructor(props) {
super(props);
this.state = {
value: true,
value: 1,
valueDeep: 11,
};
}

changeValue() {
this.setState({
value: false,
value: 2,
valueDeep: 12,
});
}

render() {
const { value } = this.state;
const { value, valueDeep } = this.state;

return (
<Formsy>
<TestInput name="foo" value={value} />
<TestInput name="bar.foo" value={valueDeep} />
<button type="submit">Save</button>
</Formsy>
);
Expand All @@ -703,17 +706,22 @@ describe('value === false', () => {

const form = mount(<TestForm />);
const input = form.find(TestInput).at(0);
const inputDeep = form.find(TestInput).at(1);
const formsyForm = form.find(Formsy);

expect(getInputInstance(input).getValue()).toEqual(true);
expect(getInputInstance(input).getValue()).toEqual(1);
expect(getInputInstance(inputDeep).getValue()).toEqual(11);

((form.instance() as TestForm) as TestForm).changeValue();
expect(getInputInstance(input).getValue()).toEqual(false);
expect(getInputInstance(input).getValue()).toEqual(2);
expect(getInputInstance(inputDeep).getValue()).toEqual(12);

getFormInstance(formsyForm).reset({
foo: 'bar',
foo: 3,
bar: { foo: 13 },
});
expect(getInputInstance(input).getValue()).toEqual('bar');
expect(getInputInstance(input).getValue()).toEqual(3);
expect(getInputInstance(inputDeep).getValue()).toEqual(13);
});
});

Expand Down Expand Up @@ -871,7 +879,7 @@ describe('form valid state', () => {
it('should be false when validationErrors is not empty', () => {
let isValid = true;

class TestForm extends React.Component<{}, { validationErrors: { [key: string]: string } }> {
class TestForm extends React.Component<{}, { validationErrors: { [key: string]: ValidationError } }> {
constructor(props) {
super(props);
this.state = {
Expand Down Expand Up @@ -909,7 +917,7 @@ describe('form valid state', () => {

it('should be true when validationErrors is not empty and preventExternalInvalidation is true', () => {
let isValid = true;
class TestForm extends React.Component<{}, { validationErrors: { [key: string]: string } }> {
class TestForm extends React.Component<{}, { validationErrors: { [key: string]: ValidationError } }> {
constructor(props) {
super(props);
this.state = {
Expand Down
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@
"version": "npm run build && git add dist && npm run changelog && git add CHANGELOG.md"
},
"dependencies": {
"form-data-to-object": "^0.2.0",
"lodash.get": "^4.4.2",
"lodash.has": "^4.5.2",
"lodash.isplainobject": "^4.0.6",
"lodash.set": "^4.3.2",
"prop-types": "^15.7.2"
},
"devDependencies": {
Expand All @@ -78,10 +80,13 @@
"@rollup/plugin-node-resolve": "^7.1.1",
"@types/enzyme": "^3.10.3",
"@types/jest": "^25.1.1",
"@types/lodash.get": "^4.4.6",
"@types/lodash.has": "^4.5.6",
"@types/lodash.isplainobject": "^4.0.6",
"@types/lodash.set": "^4.3.6",
"@types/prop-types": "^15.7.1",
"@types/react": "^16.8.24",
"@types/react-dom": "^16.8.5",
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
"@typescript-eslint/eslint-plugin": "^2.14.0",
"@typescript-eslint/parser": "^2.14.0",
"auto-changelog": "^1.14.1",
Expand Down
58 changes: 31 additions & 27 deletions src/Wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ import PropTypes from 'prop-types';

import * as utils from './utils';
import FormsyContext from './FormsyContext';
import { ComponentWithStaticAttributes, RequiredValidation, Validations, WrappedComponentClass } from './interfaces';
import {
ComponentWithStaticAttributes,
RequiredValidation,
ValidationError,
Validations,
WrappedComponentClass,
} from './interfaces';
import { isString } from './utils';

/* eslint-disable react/default-props-match-prop-types */

const convertValidationsToObject = <V>(validations: false | Validations<V>): Validations<V> => {
if (typeof validations === 'string') {
if (isString(validations)) {
return validations.split(/,(?![^{[]*[}\]])/g).reduce((validationsAccumulator, validation) => {
let args: string[] = validation.split(':');
const validateMethod: string = args.shift();
Expand Down Expand Up @@ -49,27 +56,26 @@ export interface WrapperProps<V> {
innerRef?: (ref: React.Ref<any>) => void;
name: string;
required?: RequiredValidation<V>;
validationError?: string;
validationErrors?: { [key: string]: string };
validationError?: ValidationError;
validationErrors?: { [key: string]: ValidationError };
validations?: Validations<V>;
value?: V;
}

export interface WrapperState<V> {
[key: string]: unknown;
externalError: null;
formSubmitted: boolean;
isPristine: boolean;
isRequired: boolean;
isValid: boolean;
pristineValue: V;
validationError: string[];
validationError: ValidationError[];
value: V;
}

export interface InjectedProps<V> {
errorMessage: string;
errorMessages: string[];
errorMessage: ValidationError;
errorMessages: ValidationError[];
hasValue: boolean;
isFormDisabled: boolean;
isFormSubmitted: boolean;
Expand All @@ -85,10 +91,13 @@ export interface InjectedProps<V> {
showRequired: boolean;
}

export interface WrapperInstanceMethods {
export interface WrapperInstanceMethods<V> {
getErrorMessage: () => null | ValidationError;
getErrorMessages: () => ValidationError[];
getValue: () => V;
isFormDisabled: () => boolean;
isValid: () => boolean;
getValue: () => any;
getErrorMessage: () => null | string;
setValue: (value: V) => void;
}

export type PassDownProps<V> = WrapperProps<V> & InjectedProps<V>;
Expand All @@ -102,7 +111,7 @@ function getDisplayName(component: WrappedComponentClass) {
export default function<T, V>(
WrappedComponent: React.ComponentType<T & PassDownProps<V>>,
): React.ComponentType<Omit<T & WrapperProps<V>, keyof InjectedProps<V>>> {
return class extends React.Component<T & WrapperProps<V>, WrapperState<V>> implements WrapperInstanceMethods {
return class extends React.Component<T & WrapperProps<V>, WrapperState<V>> implements WrapperInstanceMethods<V> {
// eslint-disable-next-line react/sort-comp
public static contextType = FormsyContext;

Expand All @@ -128,7 +137,6 @@ export default function<T, V>(
public constructor(props) {
super(props);
this.state = {
externalError: null,
formSubmitted: false,
isPristine: true,
isRequired: false,
Expand All @@ -155,11 +163,10 @@ export default function<T, V>(

public shouldComponentUpdate(nextProps, nextState, nextContext) {
const { props, state, context } = this;
const isPropsChanged = Object.keys(props).some(k => props[k] !== nextProps[k]);

const isStateChanged = Object.keys(state).some(k => state[k] !== nextState[k]);

const isFormsyContextChanged = Object.keys(context).some(k => context[k] !== nextContext[k]);
const isChanged = (a: object, b: object): boolean => Object.keys(a).some(k => a[k] !== b[k]);
const isPropsChanged = isChanged(props, nextProps);
const isStateChanged = isChanged(state, nextState);
const isFormsyContextChanged = isChanged(context, nextContext);

return isPropsChanged || isStateChanged || isFormsyContextChanged;
}
Expand All @@ -182,22 +189,21 @@ export default function<T, V>(
}

// Detach it when component unmounts
// eslint-disable-next-line react/sort-comp
public componentWillUnmount() {
const { detachFromForm } = this.context;
detachFromForm(this);
}

public getErrorMessage = () => {
public getErrorMessage = (): ValidationError | null => {
const messages = this.getErrorMessages();
return messages.length ? messages[0] : null;
};

public getErrorMessages = () => {
const { externalError, validationError } = this.state;
public getErrorMessages = (): ValidationError[] => {
const { validationError } = this.state;

if (!this.isValid() || this.showRequired()) {
return externalError || validationError || [];
return validationError || [];
}
return [];
};
Expand All @@ -218,9 +224,7 @@ export default function<T, V>(
const { validate: validateForm } = this.context;

if (!validate) {
this.setState({
value,
});
this.setState({ value });
} else {
this.setState(
{
Expand All @@ -237,7 +241,7 @@ export default function<T, V>(
// eslint-disable-next-line react/destructuring-assignment
public hasValue = () => {
const { value } = this.state;
if (typeof value === 'string') {
if (isString(value)) {
return value !== '';
}
return value !== undefined;
Expand Down
1 change: 0 additions & 1 deletion src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
declare module 'form-data-to-object';
Loading

0 comments on commit 1124e4b

Please sign in to comment.