formula-one
Advanced tools
Comparing version 0.5.0 to 0.6.0
# Changelog | ||
### v0.6.0 | ||
- Rework the `feedbackStrategy` prop. Several primitive strategies and algebraic combinators are exported under `FeedbackStrategies`, a new top-level export. See the documentation for more info. | ||
### v0.5.0 | ||
@@ -4,0 +8,0 @@ |
@@ -26,2 +26,4 @@ "use strict"; | ||
require("./feedbackStrategies"); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
@@ -91,38 +93,2 @@ | ||
// FEATURE(zach): Change these to be mix-and-matchable (i.e. OnSubmit & OnChange, OnSuccess | OnTouch) | ||
function getShouldShowError(strategy) { | ||
switch (strategy) { | ||
case "Always": | ||
return function () { | ||
return true; | ||
}; | ||
case "OnFirstTouch": | ||
return function (_metaForm, meta) { | ||
return meta.touched; | ||
}; | ||
case "OnFirstChange": | ||
return function (_metaForm, meta) { | ||
return meta.changed; | ||
}; | ||
case "OnFirstSuccess": | ||
return function (_metaForm, meta) { | ||
return meta.succeeded; | ||
}; | ||
case "OnFirstSuccessOrFirstTouch": | ||
return function (_metaForm, meta) { | ||
return meta.succeeded || meta.touched; | ||
}; | ||
case "OnSubmit": | ||
return function (metaForm) { | ||
return metaForm.submitted; | ||
}; | ||
default: | ||
// eslint-disable-next-line no-unused-expressions | ||
strategy; | ||
throw new Error("Unimplemented feedback strategy: " + strategy); | ||
} | ||
} | ||
var Form = function (_React$Component) { | ||
@@ -230,3 +196,3 @@ _inherits(Form, _React$Component); | ||
value: _extends({ | ||
shouldShowError: getShouldShowError(this.props.feedbackStrategy).bind(null, metaForm) | ||
shouldShowError: this.props.feedbackStrategy.bind(null, metaForm) | ||
}, metaForm) | ||
@@ -242,3 +208,3 @@ }, | ||
changed: (0, _formState2.getExtras)(formState).meta.changed, | ||
shouldShowErrors: getShouldShowError(this.props.feedbackStrategy)(metaForm, (0, _formState2.getExtras)(formState).meta), | ||
shouldShowErrors: this.props.feedbackStrategy(metaForm, (0, _formState2.getExtras)(formState).meta), | ||
unfilteredErrors: (0, _formState2.flatRootErrors)(formState), | ||
@@ -245,0 +211,0 @@ asyncValidationInFlight: false, // no validations on Form |
@@ -6,4 +6,7 @@ "use strict"; | ||
}); | ||
exports.TestUtils = exports.Field = exports.ErrorsHelper = exports.ArrayField = exports.ObjectField = exports.Form = undefined; | ||
exports.TestUtils = exports.mergedStrategies = exports.Field = exports.ErrorsHelper = exports.ArrayField = exports.ObjectField = exports.Form = undefined; | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; // strict | ||
// Just re-exports and some masssaging | ||
var _Form = require("./Form"); | ||
@@ -54,2 +57,6 @@ | ||
var _FeedbackStrategies = require("./FeedbackStrategies"); | ||
var _FeedbackStrategies2 = _interopRequireDefault(_FeedbackStrategies); | ||
var _TestUtils2 = require("./TestUtils"); | ||
@@ -63,2 +70,8 @@ | ||
var mergedStrategies = _extends({}, _FeedbackStrategies2.default, { | ||
and: _FeedbackStrategies.and, | ||
or: _FeedbackStrategies.or, | ||
not: _FeedbackStrategies.not | ||
}); | ||
exports.mergedStrategies = mergedStrategies; | ||
exports.TestUtils = _TestUtils; |
@@ -15,2 +15,6 @@ "use strict"; | ||
var _feedbackStrategies = require("../feedbackStrategies"); | ||
var _feedbackStrategies2 = _interopRequireDefault(_feedbackStrategies); | ||
var _Form = require("../Form"); | ||
@@ -100,3 +104,3 @@ | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -145,3 +149,3 @@ serverErrors: { "/": ["Server error", "Another server error"] } | ||
}, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -192,3 +196,3 @@ serverErrors: { | ||
}, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -221,3 +225,3 @@ serverErrors: { | ||
}, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -263,3 +267,3 @@ serverErrors: { | ||
}, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -343,3 +347,3 @@ serverErrors: null | ||
}, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -400,3 +404,3 @@ serverErrors: null | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -425,3 +429,3 @@ serverErrors: null | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -463,3 +467,3 @@ serverErrors: null | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -499,3 +503,3 @@ serverErrors: { "/": ["Server error", "Another server error"] } | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: jest.fn(), | ||
@@ -535,3 +539,3 @@ serverErrors: { "/": ["Server error", "Another server error"] } | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -557,3 +561,3 @@ serverErrors: { "/": ["Server error", "Another server error"] } | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Touched, | ||
onSubmit: jest.fn(), | ||
@@ -594,3 +598,3 @@ serverErrors: { "/": ["Server error", "Another server error"] } | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -618,3 +622,3 @@ serverErrors: { "/": ["Server error", "Another server error"] } | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -641,3 +645,3 @@ serverErrors: { "/": ["Server error", "Another server error"] } | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onSubmit: onSubmit, | ||
@@ -669,3 +673,3 @@ serverErrors: { "/": ["Server error", "Another server error"] } | ||
initialValue: 1, | ||
feedbackStrategy: "OnFirstTouch", | ||
feedbackStrategy: _feedbackStrategies2.default.Always, | ||
onChange: onChange, | ||
@@ -672,0 +676,0 @@ serverErrors: { "/": ["Server error", "Another server error"] } |
{ | ||
"name": "formula-one", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "Strongly-typed React form state management", | ||
@@ -5,0 +5,0 @@ "author": "Zach Gotsch", |
393
README.md
@@ -1,1 +0,392 @@ | ||
todo | ||
# formula-one | ||
**formula-one** is a library which makes it easier to write type-safe forms with validations and complex inputs. | ||
## A simple example | ||
```jsx | ||
type Person = { | ||
name: string, | ||
age: string, | ||
side: "Empire" | "Rebels", | ||
}; | ||
const emptyPerson: Person = { | ||
name: "", | ||
age: null, | ||
side: "Empire", | ||
}; | ||
<Form | ||
feedbackStrategy="Always" | ||
initialValue={emptyPerson} | ||
onSubmit={savePerson} | ||
> | ||
{(link, onSubmit) => ( | ||
<ObjectField link={link}> | ||
{links => ( | ||
<> | ||
<Field link={links.name}> | ||
{(value, errors, onChange, onBlur) => ( | ||
<> | ||
<label>Name:</label> | ||
<input type="text" onChange={onChange} onBlur={onBlur} /> | ||
</> | ||
)} | ||
</Field> | ||
<Field link={links.age}> | ||
{(value, errors, onChange, onBlur) => ( | ||
<> | ||
<label>Age:</label> | ||
<input type="text" onChange={onChange} onBlur={onBlur} /> | ||
</> | ||
)} | ||
</Field> | ||
<Field link={links.side}> | ||
{(value, errors, onChange, onBlur) => ( | ||
<> | ||
<label>Side:</label> | ||
<select onChange={onChange} onBlur={onBlur} value={value}> | ||
<option value="Empire">Empire</option> | ||
<option value="Rebels">Rebels</option> | ||
</select> | ||
</> | ||
)} | ||
</Field> | ||
<div> | ||
<button onClick={onSubmit}>Submit</button> | ||
</div> | ||
</> | ||
)} | ||
</ObjectField> | ||
)} | ||
</Form>; | ||
``` | ||
## Philosophy | ||
**formula-one** helps you write forms in React by managing the state of your form and ensuring your inputs are the right type. It does this by introducing a new abstraction, called a _Field_. A _Field_ wraps some value and provides a way to render and edit that value. A simple _Field_ might wrap a `string`, which displays and edits its value through an `<input type="text">`. A more complex value, such as a date and time might be displayed as an ISO 8601 string and be edited through a calendar input. | ||
*Field*s are specified using the `<Field>` component, which wraps your input using a render prop. It provides the value, errors, onChange, and onBlur handlers, which should be hooked up to your input. | ||
Individual *Field*s are aggregated into objects and arrays using the `<ObjectField>` and `<ArrayField>` components. These components enable you to build forms with multiple fields. | ||
In **formula-one**, all of the form's state is held in the `<Form>` component, and communicated to its internal *Field*s via opaque props called *link*s. These links contain all of the data and metadata used to render an input and its associated errors. | ||
<!-- something about tree here? --> | ||
## Validations | ||
### Simple Validation | ||
**formula-one** provides an api for specifying validations on *Field*s. Each _Field_ exposes a `validation` props, which has the type `T => Array<string>` for a `Field` of type `T`. Each string represents an error message, and the empty array indicates no errors. | ||
An example of a `Field<string>` which doesn't allow empty strings: | ||
```jsx | ||
function noEmptyStrings(s: string): Array<string> { | ||
if (s === "") { | ||
return ["Cannot be empty"]; | ||
} | ||
} | ||
<Field link={link} validation={noEmptyStrings}> | ||
{(value, errors, onChange, onBlur)} => ( | ||
<> | ||
<input type="text" value={value} /> | ||
<ul class="input_errors"> | ||
{errors.map(error => ( | ||
<li>{error}</li> | ||
))} | ||
</ul> | ||
</> | ||
)} | ||
</Field>; | ||
``` | ||
### When to show errors | ||
In addition to tracking errors and validating when inputs change, **formula-one** tracks metadata to help you decide whether you should show errors to your user. `<Form>` allows you to specify a strategy for when to show errors. | ||
Some base strategies are exported as fields on the `FeedbackStrategies` object. Here is a table of the strategies and their behavior. | ||
| Strategy identifier | Strategy Behavior | | ||
| ---------------------------------------------- | ------------------------------------------------------------------------------------ | | ||
| `FeedbackStrategies.Always` | Always show errors | | ||
| `FeedbackStrategies.Touched` | Show errors for fields which have been touched (changed or blurred) | | ||
| `FeedbackStrategies.Changed` | Show errors for fields which have been changed | | ||
| `FeedbackStrategies.ClientValidationSucceeded` | Show errors for fields which have had their validations pass at any time in the past | | ||
| `FeedbackStrategies.Pristine` | Show errors when the form has not been modified | | ||
| `FeedbackStrategies.Submitted` | Show errors after the form has been submitted | | ||
These simple strategies can be combined by using the `and`, `or`, and `not` functions also on the `FeedbackStrategies` object, as follows: | ||
```js | ||
import {FeedbackStrategies} from "formula-one"; | ||
const {Changed, Submitted, or} = FeedbackStrategies; | ||
const myStrategy = or(Changed, Submitted); | ||
``` | ||
### Multiple validations for a single `<Field>` | ||
To specify multiple validations for a single field, simply run the validations in sequence and serialize their errors into a single array. | ||
```jsx | ||
function validate(s: string): Array<string> { | ||
return [noShortStrings, mustHaveLettersAndNumbers, noLongStrings].flatMap( | ||
validation => validation(s) | ||
); | ||
} | ||
``` | ||
### Validations on aggregations of *Field*s | ||
Both `<ObjectField>` and `<ArrayField>` allow a validation to be specified. You can use the `<ErrorHelper>` component to extract the errors from the link. | ||
## Arrays in forms | ||
Often, you may want to edit a list of items in a form. **formula-one** exposes an aggregator called `<ArrayField>`, which allows you to manipulate a list of *Field*s. | ||
For example, imagine you have a form for a person, who has a name, but also some number of pets, who each have their own name. | ||
```jsx | ||
type Person = { | ||
name: string, | ||
pets: Array<{ | ||
name: string, | ||
}>, | ||
}; | ||
const emptyPerson = { | ||
name: "", | ||
pets: [], | ||
}; | ||
<Form> | ||
{(link, onSubmit) => ( | ||
<ObjectField link={link}> | ||
{links => ( | ||
<> | ||
<Field link={links.name}> | ||
{(value, errors, onChange, onBlur) => ( | ||
<> | ||
<label>Name:</label> | ||
<input type="text" onChange={onChange} onBlur={onBlur} /> | ||
</> | ||
)} | ||
</Field> | ||
<ArrayField link={links.pets}> | ||
{(links, {addField}) => ( | ||
<ul> | ||
{links.map((link, i) => ( | ||
<ObjectField link={link}> | ||
{link => ( | ||
<Field link={link}> | ||
{(value, errors, onChange, onBlur) => ( | ||
<li> | ||
Pet #{i + 1} | ||
<input | ||
type="text" | ||
value={value} | ||
onChange={onChange} | ||
onBlur={onBlur} | ||
/> | ||
</li> | ||
)} | ||
</Field> | ||
)} | ||
</ObjectField> | ||
))} | ||
{links.length === 0 ? "No pets :(" : null} | ||
<button onClick={addField(links.length, {name: ""})}> | ||
Add pet | ||
</button> | ||
</ul> | ||
)} | ||
</ArrayField> | ||
<div> | ||
<button onClick={onSubmit}>Submit</button> | ||
</div> | ||
</> | ||
)} | ||
</ObjectField> | ||
)} | ||
</Form>; | ||
``` | ||
`<ArrayField>` exposes both an array of links to the array elements, but also an object containing mutators for the array: | ||
- `addField(index: number, value: T)`: Add a field at a position in the array | ||
- `removeField(index: number)`: Remove a field at a position in array | ||
- `moveField(fromIndex: number, toIndex: number)`: Move a field in an array (preserves metadata for the field) | ||
## Complex inputs | ||
Even inputs which are complex can be wrapped in a `<Field>` wrapper, but validations are tracked at the field level, so you won't be able to use **formula-one** to track changes and validations below the field level. | ||
<!-- ### Form state vs actual model --> | ||
## Common use cases | ||
### Form in a modal | ||
Oftentimes, when you need to wrap a component which has a button you will use for submission, you can simply wrap that component with your `<Form>` element. The `<Form>` does not render any elements, so it will not affect your DOM hierarchy. | ||
Example: | ||
```jsx | ||
<Form> | ||
{(link, handleSubmit) => ( | ||
<Modal buttons={[<button onClick={handleSubmit}>Submit</button>]}> | ||
<MyField link={link} /> | ||
</Modal> | ||
)} | ||
</Form> | ||
``` | ||
### External validation | ||
Oftentimes, you will want to show errors from an external source (such as the server) in your form alongside any client-side validation errors. These can be passed into your `<Form>` component using the `serverErrors` (TODO(zach): change to `externalErrors`?) prop. | ||
These errors must be in an object with keys representing the path to the field they should be associated with. For example, the errors: | ||
```js | ||
const serverErrors = { | ||
"/": "User failed to save!", | ||
"/email": "A user with this email already exists!", | ||
}; | ||
``` | ||
could be used in this form: | ||
```jsx | ||
<Form serverErrors={serverErrors}> | ||
({(link, handleSubmit)}) => ( | ||
<> | ||
<ObjectField link={link}> | ||
{links => ( | ||
<> | ||
<StringField link={links.name} /> | ||
<StringField link={links.email} /> | ||
</> | ||
)} | ||
</ObjectField> | ||
<button onClick={handleSubmit}>Submit</button> | ||
</> | ||
)} | ||
</Form> | ||
``` | ||
## Advanced usage | ||
### Additional information in render prop | ||
Additional information is available in an object which is the last argument to the `<Form>`, `<ObjectField>`, `<ArrayField>`, and `<Field>` components' render props. This object contains the following information: | ||
| key | type | description | | ||
| ----------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ||
| touched | `boolean` | Whether the field has been touched (blurred or changed) | | ||
| ch}anged | `boolean` | Whether the field has been changed | | ||
| shouldShowErrors | `boolean` | Whether errors should be shown according to the current feedback strategy | | ||
| unfilteredErrors | `$ReadOnlyArray<string>` | All validation errors for the current field. (This differs from the `errors` argument in `<Field>`, since the `errors` argument in `<Field>` will be empty if `shouldShowErrors` is false) | | ||
| valid | `boolean` | Whether the field (and its children) pass their validations (NOTE: only client errors are considered!) | | ||
| asyncValidationInFlight | `boolean` | Whether there is an asynchronous validation in progress for this field | | ||
| value | `T` | The current value for this field. (This will always match the `value` argument to `<Field>`) | | ||
An example of how these data could be used: | ||
```jsx | ||
<Form onSubmit={handleSubmit}> | ||
({(link, handleSubmit, {valid})}) => ( | ||
<> | ||
<Field link={link}> | ||
{(value, errors, onChange, onBlur, {changed}) => ( | ||
<label> | ||
Name | ||
<input | ||
type="text" | ||
value={value} | ||
onChange={onChange} | ||
onBlur={onBlur} | ||
/> | ||
{changed ? "(Modified)" : null} | ||
</label> | ||
)} | ||
</Field> | ||
<button disabled={!valid} onClick={() => handleSubmit()}> | ||
Submit | ||
</button> | ||
</> | ||
)} | ||
</Form> | ||
``` | ||
### Multiple submission buttons | ||
Sometimes, you need to have multiple submission buttons and need to know which button was clicked in your `onSubmit` prop callback. This can be achieved by passing additional information as an argument to the `handleSubmit` argument to your `<Form>`'s render prop. This argument will be passed to your `onSubmit` prop callback as a second argument. If your `onSubmit` prop callback is typed to make this extra data mandatory, they inner `handleSubmit` callback will require that data. | ||
Example: | ||
```jsx | ||
function handleSubmit(value: User, saveOrSubmit: "save" | "submit") { | ||
if (saveOrSubmit === "save") { | ||
// ... | ||
} else if (saveOrSubmit === "submit") { | ||
// ... | ||
} | ||
} | ||
<Form onSubmit={handleSubmit}> | ||
({(link, handleSubmit)}) => ( | ||
<> | ||
<UserField link={link} /> | ||
<div> | ||
<button onClick={() => handleSubmit("save")}>Save</button> | ||
<button onClick={() => handleSubmit("submit")}>Submit</button> | ||
</div> | ||
</> | ||
)} | ||
</Form>; | ||
``` | ||
### Submitting forms externally | ||
It is easy to sumbit a **formula-one** form using the `handleSubmit` argument provided to `<Form>`'s render prop, but sometimes you need to submit a `<Form>` from outside. This is possible using the `submit()` method available on `<Form>` along with a React ref to that `<Form>` element. This `submit()` method can also receive additional user-specified information, as stated above. | ||
```jsx | ||
class MyExternalButtonExample extends React.Component<Props> { | ||
form: null | React.Element<typeof Form>; | ||
constructor(props: Props) { | ||
super(props); | ||
this.form = null; | ||
this.handleSubmitClick = this.handleSubmitClick.bind(this); | ||
} | ||
handleSubmitClick() { | ||
if (this.form != null) { | ||
this.form.submit(); | ||
} | ||
} | ||
render() { | ||
<div> | ||
<Form | ||
ref={f => { | ||
this.form = f; | ||
}} | ||
onSubmit={handleSubmit} | ||
> | ||
({(link, handleSubmit)}) => (<UserField link={link} /> | ||
)} | ||
</Form> | ||
<button onClick={this.handleSubmitClick}>Submit</button> | ||
</div>; | ||
} | ||
} | ||
``` | ||
<!-- #### Nested forms --> | ||
<!-- #### Disjoint union --> |
@@ -26,2 +26,3 @@ // @flow strict | ||
import {pathFromPathString} from "./tree"; | ||
import {type FeedbackStrategy} from "./feedbackStrategies"; | ||
@@ -94,34 +95,2 @@ export type FormContextPayload = { | ||
// FEATURE(zach): Change these to be mix-and-matchable (i.e. OnSubmit & OnChange, OnSuccess | OnTouch) | ||
export type FeedbackStrategy = | ||
| "Always" | ||
| "OnFirstTouch" // A touch is a blur or a change | ||
| "OnFirstChange" | ||
| "OnFirstSuccess" | ||
| "OnFirstSuccessOrFirstTouch" | ||
| "OnSubmit"; | ||
function getShouldShowError( | ||
strategy: FeedbackStrategy | ||
): (MetaForm, MetaField) => boolean { | ||
switch (strategy) { | ||
case "Always": | ||
return () => true; | ||
case "OnFirstTouch": | ||
return (_metaForm, meta: MetaField) => meta.touched; | ||
case "OnFirstChange": | ||
return (_metaForm, meta: MetaField) => meta.changed; | ||
case "OnFirstSuccess": | ||
return (_metaForm, meta: MetaField) => meta.succeeded; | ||
case "OnFirstSuccessOrFirstTouch": | ||
return (_metaForm, meta: MetaField) => meta.succeeded || meta.touched; | ||
case "OnSubmit": | ||
return (metaForm: MetaForm) => metaForm.submitted; | ||
default: | ||
// eslint-disable-next-line no-unused-expressions | ||
(strategy: empty); | ||
throw new Error("Unimplemented feedback strategy: " + strategy); | ||
} | ||
} | ||
type Props<T, ExtraSubmitData> = { | ||
@@ -244,6 +213,3 @@ // This is *only* used to intialize the form. Further changes will be ignored | ||
value={{ | ||
shouldShowError: getShouldShowError(this.props.feedbackStrategy).bind( | ||
null, | ||
metaForm | ||
), | ||
shouldShowError: this.props.feedbackStrategy.bind(null, metaForm), | ||
...metaForm, | ||
@@ -263,3 +229,3 @@ }} | ||
changed: getExtras(formState).meta.changed, | ||
shouldShowErrors: getShouldShowError(this.props.feedbackStrategy)( | ||
shouldShowErrors: this.props.feedbackStrategy( | ||
metaForm, | ||
@@ -266,0 +232,0 @@ getExtras(formState).meta |
// @flow strict | ||
// Just re-exports | ||
// Just re-exports and some masssaging | ||
import FeedbackStrategies, {and, or, not} from "./FeedbackStrategies"; | ||
export {default as Form} from "./Form"; | ||
@@ -10,5 +12,13 @@ export {default as ObjectField} from "./ObjectField"; | ||
export type {FeedbackStrategy} from "./Form"; | ||
const mergedStrategies = { | ||
...FeedbackStrategies, | ||
and, | ||
or, | ||
not, | ||
}; | ||
export {mergedStrategies}; | ||
export type {FeedbackStrategy} from "./FeedbackStrategies"; | ||
export type {Validation, FieldLink} from "./types"; | ||
export * as TestUtils from "./TestUtils"; |
@@ -5,2 +5,3 @@ // @flow | ||
import TestRenderer from "react-test-renderer"; | ||
import FeedbackStrategies from "../feedbackStrategies"; | ||
import Form, {FormContext} from "../Form"; | ||
@@ -51,3 +52,3 @@ import ObjectField from "../ObjectField"; | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -91,3 +92,3 @@ serverErrors={{"/": ["Server error", "Another server error"]}} | ||
}} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -132,3 +133,3 @@ serverErrors={{ | ||
}} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -155,3 +156,3 @@ serverErrors={{ | ||
}} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -193,3 +194,3 @@ serverErrors={{ | ||
}} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -261,3 +262,3 @@ serverErrors={null} | ||
}} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -306,3 +307,3 @@ serverErrors={null} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -329,3 +330,3 @@ serverErrors={null} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -357,3 +358,3 @@ serverErrors={null} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -391,3 +392,3 @@ serverErrors={{"/": ["Server error", "Another server error"]}} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={jest.fn()} | ||
@@ -425,3 +426,3 @@ serverErrors={{"/": ["Server error", "Another server error"]}} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -447,3 +448,3 @@ serverErrors={{"/": ["Server error", "Another server error"]}} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Touched} | ||
onSubmit={jest.fn()} | ||
@@ -485,3 +486,3 @@ serverErrors={{"/": ["Server error", "Another server error"]}} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -509,3 +510,3 @@ serverErrors={{"/": ["Server error", "Another server error"]}} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -532,3 +533,3 @@ serverErrors={{"/": ["Server error", "Another server error"]}} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onSubmit={onSubmit} | ||
@@ -558,3 +559,3 @@ serverErrors={{"/": ["Server error", "Another server error"]}} | ||
initialValue={1} | ||
feedbackStrategy="OnFirstTouch" | ||
feedbackStrategy={FeedbackStrategies.Always} | ||
onChange={onChange} | ||
@@ -561,0 +562,0 @@ serverErrors={{"/": ["Server error", "Another server error"]}} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
257907
63
6255
393