Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@shopify/react-form-state

Package Overview
Dependencies
Maintainers
12
Versions
146
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shopify/react-form-state - npm Package Compare versions

Comparing version 0.7.4 to 0.8.0

11

dist/FormState.d.ts

@@ -30,2 +30,3 @@ import * as React from 'react';

initialValues: Fields;
children(form: FormDetails<Fields>): React.ReactNode;
validators?: Partial<ValidatorDictionary<Fields>>;

@@ -35,3 +36,3 @@ onSubmit?: SubmitHandler<Fields>;

onInitialValuesChange?: 'reset-all' | 'reset-where-changed' | 'ignore';
children(form: FormDetails<Fields>): React.ReactNode;
externalErrors?: RemoteError[];
}

@@ -43,2 +44,3 @@ interface State<Fields> {

errors: RemoteError[];
externalErrors: RemoteError[];
}

@@ -48,3 +50,6 @@ export default class FormState<Fields extends Object> extends React.PureComponent<Props<Fields>, State<Fields>> {

static Nested: typeof Nested;
static getDerivedStateFromProps<T>(newProps: Props<T>, oldState: State<T>): State<T> | null;
static getDerivedStateFromProps<T>(newProps: Props<T>, oldState: State<T>): {
externalErrors: RemoteError[];
fields: FieldStates<T>;
} | null;
state: State<Fields>;

@@ -56,5 +61,5 @@ private mounted;

render(): React.ReactNode;
private readonly formData;
validateForm(): Promise<{}>;
reset: () => Promise<{}>;
private readonly formData;
private readonly dirty;

@@ -61,0 +66,0 @@ private readonly valid;

@@ -12,3 +12,3 @@ "use strict";

var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = createFormState(_this.props.initialValues);
_this.state = createFormState(_this.props.initialValues, _this.props.externalErrors);
_this.mounted = false;

@@ -18,3 +18,5 @@ _this.fieldsWithHandlers = new WeakMap();

return new Promise(function (resolve) {
_this.setState(function (_state, props) { return createFormState(props.initialValues); }, function () { return resolve(); });
_this.setState(function (_state, props) {
return createFormState(props.initialValues, props.externalErrors);
}, function () { return resolve(); });
});

@@ -77,8 +79,16 @@ };

FormState.getDerivedStateFromProps = function (newProps, oldState) {
var initialValues = newProps.initialValues, onInitialValuesChange = newProps.onInitialValuesChange;
var initialValues = newProps.initialValues, onInitialValuesChange = newProps.onInitialValuesChange, _a = newProps.externalErrors, externalErrors = _a === void 0 ? [] : _a;
var externalErrorsChanged = externalErrors !== oldState.externalErrors ||
externalErrors.length !== oldState.externalErrors.length;
var updatedExternalErrors = externalErrorsChanged
? {
externalErrors: externalErrors,
fields: fieldsWithErrors(oldState.fields, tslib_1.__spread(externalErrors, oldState.errors)),
}
: null;
switch (onInitialValuesChange) {
case 'ignore':
return null;
return updatedExternalErrors;
case 'reset-where-changed':
return reconcileFormState(initialValues, oldState);
return reconcileFormState(initialValues, oldState, externalErrors);
case 'reset-all':

@@ -89,5 +99,5 @@ default:

if (valuesMatch) {
return null;
return updatedExternalErrors;
}
return createFormState(initialValues);
return createFormState(initialValues, externalErrors);
}

@@ -109,10 +119,17 @@ };

};
FormState.prototype.validateForm = function () {
var _this = this;
return new Promise(function (resolve) {
_this.setState(runAllValidators, function () { return resolve(); });
});
};
Object.defineProperty(FormState.prototype, "formData", {
get: function () {
var errors = this.state.errors;
var _a = this, fields = _a.fields, dirty = _a.dirty, valid = _a.valid;
var _a = this.props.externalErrors, externalErrors = _a === void 0 ? [] : _a;
var _b = this, fields = _b.fields, dirty = _b.dirty, valid = _b.valid;
return {
dirty: dirty,
valid: valid,
errors: errors,
errors: tslib_1.__spread(errors, externalErrors),
fields: fields,

@@ -124,8 +141,2 @@ };

});
FormState.prototype.validateForm = function () {
var _this = this;
return new Promise(function (resolve) {
_this.setState(runAllValidators, function () { return resolve(); });
});
};
Object.defineProperty(FormState.prototype, "dirty", {

@@ -140,4 +151,6 @@ get: function () {

get: function () {
var errors = this.state.errors;
return !this.hasClientErrors && errors.length === 0;
var _a = this.state, errors = _a.errors, externalErrors = _a.externalErrors;
return (!this.hasClientErrors &&
errors.length === 0 &&
externalErrors.length === 0);
},

@@ -250,16 +263,7 @@ enumerable: true,

this.setState(function (_a) {
var fields = _a.fields;
var errorDictionary = errors.reduce(function (accumulator, _a) {
var field = _a.field, message = _a.message;
if (field == null) {
return accumulator;
}
return utilities_1.set(accumulator, field, message);
}, {});
return {
var fields = _a.fields, externalErrors = _a.externalErrors;
return ({
errors: errors,
fields: utilities_1.mapObject(fields, function (field, path) {
return tslib_1.__assign({}, field, { error: errorDictionary[path] });
}),
};
fields: fieldsWithErrors(fields, tslib_1.__spread(errors, externalErrors)),
});
});

@@ -272,3 +276,16 @@ };

exports.default = FormState;
function reconcileFormState(values, oldState) {
function fieldsWithErrors(fields, errors) {
var errorDictionary = errors.reduce(function (accumulator, _a) {
var field = _a.field, message = _a.message;
if (field == null) {
return accumulator;
}
return utilities_1.set(accumulator, field, message);
}, {});
return utilities_1.mapObject(fields, function (field, path) {
return tslib_1.__assign({}, field, { error: errorDictionary[path] });
});
}
function reconcileFormState(values, oldState, externalErrors) {
if (externalErrors === void 0) { externalErrors = []; }
var oldFields = oldState.fields;

@@ -288,5 +305,6 @@ var dirtyFields = new Set(oldState.dirtyFields);

});
return tslib_1.__assign({}, oldState, { dirtyFields: Array.from(dirtyFields), fields: fields });
return tslib_1.__assign({}, oldState, { dirtyFields: Array.from(dirtyFields), fields: fieldsWithErrors(fields, externalErrors) });
}
function createFormState(values) {
function createFormState(values, externalErrors) {
if (externalErrors === void 0) { externalErrors = []; }
var fields = utilities_1.mapObject(values, function (value) {

@@ -303,3 +321,4 @@ return {

submitting: false,
fields: fields,
externalErrors: externalErrors,
fields: fieldsWithErrors(fields, externalErrors),
};

@@ -306,0 +325,0 @@ }

@@ -401,2 +401,43 @@ # Building forms with FormState

## External errors
You can use the `externalErrors` prop to supply `<FormState />` with external errors. This is useful for displaying errors that occur outside of the normal form submit flow. These errors will be available alongside regular errors via the `errors` array.
```typescript
class MyComponent extends React.Component {
state = {
externalErrors: [{message: 'Example error', field: 'foo'}]
}
render() {
const {externalErrors} = this.state;
return (
<FormState
initialValues={{foo: ''}}
externalErrors={externalErrors}
onSubmit={async (formDetails) => {
const response = await submitFormMutation(formDetails);
if (response.errors) {
// we still need to clear the errors ourselves because formstate
// doesn't know what they represent or when to clear them
this.setState({externalErrors: null});
return response.errors;
}
}}
>
{({fields, errors}) => {
return (
<Banner>
{/* the externalErrors get seamlessly added to the errors array */}
{errors.map(error => <li>{error.message}</li>)}
</Banner>
{/* and passed down to matching fields */}
<TextField {...fields.foo} />
)
}}
</FormState>
)
}
}
```
## Compound fields

@@ -403,0 +444,0 @@

{
"name": "@shopify/react-form-state",
"version": "0.7.4",
"version": "0.8.0",
"license": "MIT",

@@ -5,0 +5,0 @@ "description": "Manage react forms tersely and type-safe with no magic.",

@@ -49,2 +49,3 @@ /* eslint-disable no-case-declarations */

initialValues: Fields;
children(form: FormDetails<Fields>): React.ReactNode;
validators?: Partial<ValidatorDictionary<Fields>>;

@@ -54,3 +55,3 @@ onSubmit?: SubmitHandler<Fields>;

onInitialValuesChange?: 'reset-all' | 'reset-where-changed' | 'ignore';
children(form: FormDetails<Fields>): React.ReactNode;
externalErrors?: RemoteError[];
}

@@ -63,2 +64,3 @@

errors: RemoteError[];
externalErrors: RemoteError[];
}

@@ -73,9 +75,27 @@

static getDerivedStateFromProps<T>(newProps: Props<T>, oldState: State<T>) {
const {initialValues, onInitialValuesChange} = newProps;
const {
initialValues,
onInitialValuesChange,
externalErrors = [],
} = newProps;
const externalErrorsChanged =
externalErrors !== oldState.externalErrors ||
externalErrors.length !== oldState.externalErrors.length;
const updatedExternalErrors = externalErrorsChanged
? {
externalErrors,
fields: fieldsWithErrors(oldState.fields, [
...externalErrors,
...oldState.errors,
]),
}
: null;
switch (onInitialValuesChange) {
case 'ignore':
return null;
return updatedExternalErrors;
case 'reset-where-changed':
return reconcileFormState(initialValues, oldState);
return reconcileFormState(initialValues, oldState, externalErrors);
case 'reset-all':

@@ -87,10 +107,10 @@ default:

if (valuesMatch) {
return null;
return updatedExternalErrors;
}
return createFormState(initialValues);
return createFormState(initialValues, externalErrors);
}
}
state = createFormState(this.props.initialValues);
state = createFormState(this.props.initialValues, this.props.externalErrors);
private mounted = false;

@@ -120,14 +140,2 @@ private fieldsWithHandlers = new WeakMap();

private get formData() {
const {errors} = this.state;
const {fields, dirty, valid} = this;
return {
dirty,
valid,
errors,
fields,
};
}
public validateForm() {

@@ -142,3 +150,4 @@ return new Promise(resolve => {

this.setState(
(_state, props) => createFormState(props.initialValues),
(_state, props) =>
createFormState(props.initialValues, props.externalErrors),
() => resolve(),

@@ -149,2 +158,15 @@ );

private get formData() {
const {errors} = this.state;
const {externalErrors = []} = this.props;
const {fields, dirty, valid} = this;
return {
dirty,
valid,
errors: [...errors, ...externalErrors],
fields,
};
}
private get dirty() {

@@ -155,5 +177,9 @@ return this.state.dirtyFields.length > 0;

private get valid() {
const {errors} = this.state;
const {errors, externalErrors} = this.state;
return !this.hasClientErrors && errors.length === 0;
return (
!this.hasClientErrors &&
errors.length === 0 &&
externalErrors.length === 0
);
}

@@ -372,25 +398,30 @@

private updateRemoteErrors(errors: RemoteError[]) {
this.setState(({fields}: State<Fields>) => {
const errorDictionary = errors.reduce(
(accumulator: any, {field, message}) => {
if (field == null) {
return accumulator;
}
this.setState(({fields, externalErrors}) => ({
errors,
fields: fieldsWithErrors(fields, [...errors, ...externalErrors]),
}));
}
}
return set(accumulator, field, message);
},
{},
);
function fieldsWithErrors<Fields>(
fields: Fields,
errors: RemoteError[],
): Fields {
const errorDictionary = errors.reduce(
(accumulator: any, {field, message}) => {
if (field == null) {
return accumulator;
}
return {
errors,
fields: mapObject(fields, (field, path) => {
return {
...field,
error: errorDictionary[path],
};
}),
};
});
}
return set(accumulator, field, message);
},
{},
);
return mapObject(fields, (field, path) => {
return {
...field,
error: errorDictionary[path],
};
});
}

@@ -401,2 +432,3 @@

oldState: State<Fields>,
externalErrors: RemoteError[] = [],
): State<Fields> {

@@ -425,7 +457,10 @@ const {fields: oldFields} = oldState;

dirtyFields: Array.from(dirtyFields),
fields,
fields: fieldsWithErrors(fields, externalErrors),
};
}
function createFormState<Fields>(values: Fields): State<Fields> {
function createFormState<Fields>(
values: Fields,
externalErrors: RemoteError[] = [],
): State<Fields> {
const fields: FieldStates<Fields> = mapObject(values, value => {

@@ -443,3 +478,4 @@ return {

submitting: false,
fields,
externalErrors,
fields: fieldsWithErrors(fields, externalErrors),
};

@@ -446,0 +482,0 @@ }

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc