Skip to content

Commit

Permalink
Merge pull request #43 from ArrayKnight/feat/context-dirty
Browse files Browse the repository at this point in the history
feat(form/validation context): add dirty to context
  • Loading branch information
selbekk authored Apr 24, 2019
2 parents 6194e77 + 8480569 commit bda731c
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 39 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ The `children` function is called with an object with the following props:
```js
{
dirty: object, // Object with all fields isDirty state, keyed per field
errors: object, // object with the same keys as `fields`, but with error messages
fields: object, // object with the form field values, to make controlled components
resetAll: func, // call this to programmatically trigger a full state reset
Expand Down Expand Up @@ -501,6 +502,7 @@ The `children` function is called with an object with the following props:
```js
{
dirty: object, // Object with all fields isDirty state, keyed per field
errors: object, // object with the same keys as `fields`, but with error messages
fields: object, // object with the form field values, to make controlled components
resetAll: func, // call this to programmatically trigger a full state reset
Expand Down
16 changes: 6 additions & 10 deletions src/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { func, shape } from 'prop-types';
import invariant from 'invariant';
import { withValidators } from './ValidatorsContext';
import { FormProvider } from './FormContext';
import { getFirstDefinedValue, removeFrom } from './utilities';
import { areDirty, getFirstDefinedValue, removeFrom } from './utilities';

const propTypes = {
onChange: func,
Expand Down Expand Up @@ -90,13 +90,7 @@ class Form extends Component {
this.setState({ submitted: true });

this.props.onSubmit({
dirty: Object.keys(fields).reduce(
(obj, key) => ({
...obj,
[key]: fields[key] !== this.initialValues[key],
}),
{},
),
dirty: areDirty(this.initialValues, fields),
errors,
fields,
isValid: Object.values(errors).every(error => error === null),
Expand Down Expand Up @@ -207,8 +201,8 @@ class Form extends Component {
});
};

unregisterSubComponent = fieldsToRemove => {
const keys = Object.keys(fieldsToRemove);
unregisterSubComponent = subComponentConfig => {
const keys = Object.keys(subComponentConfig);

this.initialValues = removeFrom(this.initialValues)(keys);

Expand All @@ -228,6 +222,7 @@ class Form extends Component {
const { children, onSubmit, ...rest } = this.props;
const { errors, fields, submitted } = this.state;
const formContext = {
dirty: areDirty(this.initialValues, fields),
errors,
fields,
register: this.registerSubComponent,
Expand All @@ -238,6 +233,7 @@ class Form extends Component {
submitted,
unregister: this.unregisterSubComponent,
};

return (
<form
{...rest}
Expand Down
37 changes: 22 additions & 15 deletions src/Validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Validation extends Component {
// OwnProps
...propTypes,
// FormContext
dirty: shape({}),
errors: shape({}),
fields: shape({}),
register: func.isRequired,
Expand All @@ -31,31 +32,37 @@ class Validation extends Component {
unregister: func.isRequired,
};

getFields = source => {
const { config } = this.props;

return Object.keys(config).reduce(
(allFields, field) => ({
...allFields,
[field]: getFirstDefinedValue(source[field], ''),
}),
{},
);
state = {
isRegistered: false,
};

componentDidMount() {
const { register, initialValues, config } = this.props;

register(config, this.getFields(initialValues));
register(
config,
Object.keys(config).reduce(
(allFields, field) => ({
...allFields,
[field]: getFirstDefinedValue(initialValues[field], ''),
}),
{},
),
);

this.setState({ isRegistered: true });
}

componentWillUnmount() {
this.props.unregister(this.props.config);

this.setState({ isRegistered: false });
}

render() {
const {
children,
dirty,
errors,
fields,
resetAll,
Expand All @@ -64,18 +71,18 @@ class Validation extends Component {
submit,
submitted,
} = this.props;

const childrenArgs = {
const validationContext = {
dirty,
errors,
fields: this.getFields(fields),
fields,
resetAll,
setError,
setField,
submit,
submitted,
};

return children(childrenArgs);
return this.state.isRegistered ? children(validationContext) : null;
}
}

Expand Down
57 changes: 43 additions & 14 deletions src/__tests__/utilities.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,39 @@
import { getFirstDefinedValue, removeFrom } from '../utilities';
import { areDirty, getFirstDefinedValue, removeFrom, uuid } from '../utilities';

const foo = 'foo';
const bar = 'bar';
const original = {
foo,
bar,
};

describe('areDirty', () => {
it('should compare objects', () => {
expect(areDirty(original, {})).toEqual({
foo: true,
bar: true,
});
expect(areDirty(original, { foo, bar })).toEqual({
foo: false,
bar: false,
});
expect(areDirty(original, { foo, bar: 'BAR' })).toEqual({
foo: false,
bar: true,
});
expect(areDirty(original, { foo: null, bar: 'BAR' })).toEqual({
foo: true,
bar: true,
});
expect(
areDirty(original, { foo: null, bar: 'BAR', wux: false }),
).toEqual({
foo: true,
bar: true,
wux: true,
});
});
});

describe('getFirstDefinedValue', () => {
it('should find and return first defined value', () => {
Expand All @@ -12,20 +44,17 @@ describe('getFirstDefinedValue', () => {
});

describe('removeFrom', () => {
const original = {
foo,
bar,
};

expect(removeFrom(original)).toEqual(expect.any(Function));
it('should remove keys from object', () => {
expect(removeFrom(original)).toEqual(expect.any(Function));

const removeKeys = removeFrom(original);
const removeKeys = removeFrom(original);

expect(removeKeys([])).toEqual(original);
expect(removeKeys([foo])).toEqual({
bar,
expect(removeKeys([])).toEqual(original);
expect(removeKeys([foo])).toEqual({
bar,
});
expect(removeKeys([foo, bar])).toEqual({});
expect(removeKeys(['wux'])).toEqual(original);
expect(removeKeys([foo, bar, 'wux'])).toEqual({});
});
expect(removeKeys([foo, bar])).toEqual({});
expect(removeKeys(['wux'])).toEqual(original);
expect(removeKeys([foo, bar, 'wux'])).toEqual({});
});
14 changes: 14 additions & 0 deletions src/utilities.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
export const areDirty = (original, current) => {
const keys = [
...new Set([...Object.keys(original), ...Object.keys(current)]),
];

return keys.reduce(
(obj, key) => ({
...obj,
[key]: original[key] !== current[key],
}),
{},
);
};

export const getFirstDefinedValue = (...values) =>
values.find(value => value !== undefined);

Expand Down

0 comments on commit bda731c

Please sign in to comment.