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

@homebound/form-state

Package Overview
Dependencies
Maintainers
34
Versions
91
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@homebound/form-state - npm Package Compare versions

Comparing version 2.19.0 to 2.20.0

dist/proxies.d.ts

12

dist/config.d.ts

@@ -10,14 +10,14 @@ import { Fragment, ObjectState } from "./fields/objectField";

*/
export declare type ObjectConfig<T> = {
export type ObjectConfig<T> = {
[P in keyof OmitIf<T, Function>]: T[P] extends Fragment<infer V> ? FragmentFieldConfig : T[P] extends Array<infer U> | null | undefined ? U extends Builtin ? ValueFieldConfig<T, T[P]> : ListFieldConfig<T, U> : ValueFieldConfig<T, T[P]> | ObjectFieldConfig<T[P]>;
};
declare type OmitIf<Base, Condition> = Pick<Base, {
type OmitIf<Base, Condition> = Pick<Base, {
[Key in keyof Base]: Base[Key] extends Condition ? never : Key;
}[keyof Base]>;
/** Field configuration for an opaque value that we don't actually want to include. */
export declare type FragmentFieldConfig = {
export type FragmentFieldConfig = {
type: "fragment";
};
/** Field configuration for primitive values, i.e. strings/numbers/Dates/user-defined types. */
export declare type ValueFieldConfig<T, V> = {
export type ValueFieldConfig<T, V> = {
type: "value";

@@ -49,3 +49,3 @@ rules?: Rule<V | null | undefined>[];

/** Field configuration for list values, i.e. `U` is `Book` in a form with `books: Book[]`. */
export declare type ListFieldConfig<T, U> = {
export type ListFieldConfig<T, U> = {
type: "list";

@@ -71,3 +71,3 @@ /** Rules that can run on the full list of children. */

};
export declare type ObjectFieldConfig<U> = {
export type ObjectFieldConfig<U> = {
type: "object";

@@ -74,0 +74,0 @@ /** Config for the child's form state, i.e. each book. */

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

// we want to pretend that it's observable, so use a tick to force it.
const _tick = mobx_1.observable({ value: 1 });
const _tick = (0, mobx_1.observable)({ value: 1 });
// We steal the fragment from our parent, so that it doesn't

@@ -17,3 +17,3 @@ // accidentally end up on the wire

get value() {
_tick.value > 0 || utils_1.fail();
_tick.value > 0 || (0, utils_1.fail)();
return value;

@@ -26,4 +26,4 @@ },

};
return mobx_1.makeAutoObservable(obj, { value: false });
return (0, mobx_1.makeAutoObservable)(obj, { value: false });
}
exports.newFragmentField = newFragmentField;

@@ -11,4 +11,4 @@ "use strict";

const rowMap = new Map();
const _tick = mobx_1.observable({ value: 1 });
const _childTick = mobx_1.observable({ value: 1 });
const _tick = (0, mobx_1.observable)({ value: 1 });
const _childTick = (0, mobx_1.observable)({ value: 1 });
// this is for dirty checking, not object identity

@@ -20,3 +20,3 @@ let originalCopy = [...(parentInstance[key] || [])];

get value() {
return _tick.value > 0 && _childTick.value > 0 ? parentInstance[key] : utils_1.fail();
return _tick.value > 0 && _childTick.value > 0 ? parentInstance[key] : (0, utils_1.fail)();
},

@@ -73,3 +73,3 @@ _focused: false,

if (_tick.value < 0)
utils_1.fail();
(0, utils_1.fail)();
// Avoid using `this.value` to avoid registering `_childTick` as a dependency

@@ -81,3 +81,3 @@ const value = parentInstance[key];

if (!childState) {
childState = objectField_1.newObjectState(config, parentState, list, child, undefined, maybeAutoSave);
childState = (0, objectField_1.newObjectState)(config, parentState, list, child, undefined, maybeAutoSave);
rowMap.set(child, childState);

@@ -106,3 +106,3 @@ }

if (_tick.value < 0)
utils_1.fail();
(0, utils_1.fail)();
const opts = { value: this.rows, key: key, originalValue: originalCopy || [], object: parentState() };

@@ -146,3 +146,3 @@ return this.rules.map((r) => r(opts)).filter(utils_1.isNotUndefined);

if (this.readOnly && !opts.resetting && !opts.refreshing) {
throw new Error(`${key} is currently readOnly`);
throw new Error(`${String(key)} is currently readOnly`);
}

@@ -162,3 +162,3 @@ // We should be passed values that are non-proxies.

// If we didn't have an existing child, just make a new object state
childState = objectField_1.createObjectState(config, value, { maybeAutoSave });
childState = (0, objectField_1.createObjectState)(config, value, { maybeAutoSave });
rowMap.set(value, childState);

@@ -177,3 +177,3 @@ }

// This is called by the user, so value should be a non-proxy value we should keep
const childState = objectField_1.createObjectState(config, value, { maybeAutoSave });
const childState = (0, objectField_1.createObjectState)(config, value, { maybeAutoSave });
rowMap.set(value, childState);

@@ -217,11 +217,11 @@ this.ensureSet();

};
const proxy = mobx_1.makeAutoObservable(list, {
const proxy = (0, mobx_1.makeAutoObservable)(list, {
// See other makeAutoObservable comment
value: mobx_1.computed({ equals: () => false }),
value: (0, mobx_1.computed)({ equals: () => false }),
});
// Any time a row's value changes, percolate that to our `.value` (so the callers to our
// `.value` will rerun given the value they saw has deeply changed.)
mobx_1.reaction(() => proxy.rows.map((r) => r.value), () => _childTick.value++);
(0, mobx_1.reaction)(() => proxy.rows.map((r) => r.value), () => _childTick.value++);
return proxy;
}
exports.newListFieldState = newListFieldState;

@@ -31,3 +31,3 @@ import { ObjectConfig } from "../config";

*/
export declare type ObjectState<T> = FieldStates<T> & FieldState<T> & {
export type ObjectState<T> = FieldStates<T> & FieldState<T> & {
/** Sets the state of fields in `state`. */

@@ -38,7 +38,7 @@ set(state: Partial<T>, opts?: SetOpts): void;

};
export declare type ObjectStateInternal<T> = ObjectState<T> & {
export type ObjectStateInternal<T> = ObjectState<T> & {
set(value: T, opts?: InternalSetOpts): void;
};
declare const fragmentSym: unique symbol;
export declare type Fragment<V> = V & {
export type Fragment<V> = V & {
[fragmentSym]: true;

@@ -48,3 +48,3 @@ };

/** For a given input type `T`, decorate each field into the "field state" type that holds our form-relevant state, i.e. valid/touched/etc. */
declare type FieldStates<T> = {
type FieldStates<T> = {
[K in keyof T]-?: T[K] extends Fragment<infer V> ? FragmentField<V> : T[K] extends Array<infer U> | null | undefined ? [U] extends [Builtin] ? FieldState<T[K]> : ListFieldState<U> : T[K] extends Builtin | null | undefined ? FieldState<T[K]> : ObjectState<T[K]>;

@@ -51,0 +51,0 @@ };

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

if (config.type === "value") {
field = valueField_1.newValueFieldState(instance, getObjectState, key, config.rules || [], config.isIdKey ||
field = (0, valueField_1.newValueFieldState)(instance, getObjectState, key, config.rules || [], config.isIdKey ||
// Default the id key to "id" unless some other field has isIdKey set

@@ -51,3 +51,3 @@ (key === "id" &&

else if (config.type === "list") {
field = listField_1.newListFieldState(instance, getObjectState, key, config.rules || [], config, config.config, (_b = config.strictOrder) !== null && _b !== void 0 ? _b : true, maybeAutoSave);
field = (0, listField_1.newListFieldState)(instance, getObjectState, key, config.rules || [], config, config.config, (_b = config.strictOrder) !== null && _b !== void 0 ? _b : true, maybeAutoSave);
}

@@ -61,3 +61,3 @@ else if (config.type === "object") {

else if (config.type === "fragment") {
field = fragmentField_1.newFragmentField(instance, key);
field = (0, fragmentField_1.newFragmentField)(instance, key);
}

@@ -71,3 +71,3 @@ else {

// we want to pretend that it's observable, so use a tick to force it.
const _tick = mobx_1.observable({ value: 1 });
const _tick = (0, mobx_1.observable)({ value: 1 });
const fieldNames = Object.keys(config);

@@ -81,3 +81,3 @@ function getFields(proxyThis) {

get value() {
_tick.value > 0 || utils_1.fail();
_tick.value > 0 || (0, utils_1.fail)();
return instance;

@@ -145,6 +145,6 @@ },

if (this.readOnly && !opts.resetting && !opts.refreshing) {
throw new Error(`${key || "formState"} is currently readOnly`);
throw new Error(`${String(key) || "formState"} is currently readOnly`);
}
getFields(this).forEach((field) => {
if (field.key in value) {
if (value && typeof value === "object" && field.key in value) {
field.set(value[field.key], opts);

@@ -196,3 +196,3 @@ }

};
proxy = mobx_1.makeAutoObservable(obj, {
proxy = (0, mobx_1.makeAutoObservable)(obj, {
// Use custom equality on `value` that is _never_ equals. This sounds weird, but

@@ -202,8 +202,8 @@ // because our `value` is always the same `instance` that was passed to `newObjectState`,

// even with our tick-based hacks.
value: mobx_1.computed({ equals: () => false }),
value: (0, mobx_1.computed)({ equals: () => false }),
});
// Any time a field changes, percolate that change up to us
mobx_1.reaction(() => getFields(proxy).map((f) => f.value), () => _tick.value++);
(0, mobx_1.reaction)(() => getFields(proxy).map((f) => f.value), () => _tick.value++);
return proxy;
}
exports.newObjectState = newObjectState;

@@ -39,3 +39,16 @@ import { ObjectState } from "./objectField";

commitChanges(): void;
/** Creates a new FieldState with a transformation of the value, i.e. string to int, or feet to inches. */
adapt<V2>(adapter: ValueAdapter<V, V2>): FieldState<V2>;
}
/**
* Allows changing a type in the formState (like a string) to a different type in the UI (like a number).
*
* Or doing unit of measure conversions within the same type, like from meters to feet.
*/
export interface ValueAdapter<V, V2 = V> {
/** Converts the original FieldState's value `V` into new `V2` type. */
toValue(value: V): V2;
/** Converts the adapted FieldState's value `V2` back into the original `V` type. */
fromValue(value: V2): V;
}
/** Public options for our `set` command. */

@@ -42,0 +55,0 @@ export interface SetOpts {

@@ -6,2 +6,3 @@ "use strict";

const mobx_1 = require("mobx");
const proxies_1 = require("../proxies");
const rules_1 = require("../rules");

@@ -12,9 +13,9 @@ const utils_1 = require("../utils");

const value = parentInstance[key];
let _originalValue = value === null ? undefined : is_plain_object_1.isPlainObject(value) ? mobx_1.toJS(value) : value;
let _originalValue = value === null ? undefined : (0, is_plain_object_1.isPlainObject)(value) ? (0, mobx_1.toJS)(value) : value;
// Because we read/write the value directly back into parentInstance[key],
// which itself is not a proxy, we use this as our "value changed" trigger.
const _tick = mobx_1.observable({ value: 1 });
const _originalValueTick = mobx_1.observable({ value: 1 });
const _tick = (0, mobx_1.observable)({ value: 1 });
const _originalValueTick = (0, mobx_1.observable)({ value: 1 });
const field = {
key,
key: key,
touched: false,

@@ -31,7 +32,7 @@ /** Current readOnly value. */

// If we're wrapping a mobx store, then we'll get reactivity from parentInstance[key]
const value = _tick.value > 0 ? parentInstance[key] : utils_1.fail();
const value = _tick.value > 0 ? parentInstance[key] : (0, utils_1.fail)();
// Re-create the `keepNull` logic on sets but for our initial read where our
// originalValue is null (empty) but we want to expose it as undefined for
// consistency of "empty-ness" to our UI components.
return value === null && utils_1.isEmpty(_originalValue) ? undefined : value;
return value === null && (0, utils_1.isEmpty)(_originalValue) ? undefined : value;
},

@@ -42,3 +43,3 @@ set value(v) {

get dirty() {
return !utils_1.areEqual(this.originalValue, this.value, strictOrder);
return !(0, utils_1.areEqual)(this.originalValue, this.value, strictOrder);
},

@@ -95,3 +96,3 @@ /** Returns whether this field is readOnly, although if our parent is readOnly then it trumps. */

if (this.readOnly && !opts.resetting && !opts.refreshing) {
throw new Error(`${key} is currently readOnly`);
throw new Error(`${String(key)} is currently readOnly`);
}

@@ -110,8 +111,8 @@ if (opts.refreshing && this.dirty && this.value !== value) {

// so that our partial update to the backend correctly unsets it.
const keepNull = !utils_1.isEmpty(this.originalValue) && utils_1.isEmpty(value) && !opts.refreshing;
const keepNull = !(0, utils_1.isEmpty)(this.originalValue) && (0, utils_1.isEmpty)(value) && !opts.refreshing;
// If a list of primitives was originally undefined, coerce `[]` to `undefined`
const coerceEmptyList = value && value instanceof Array && value.length === 0 && utils_1.isEmpty(this.originalValue);
const newValue = keepNull ? null : utils_1.isEmpty(value) || coerceEmptyList ? undefined : value;
const coerceEmptyList = value && value instanceof Array && value.length === 0 && (0, utils_1.isEmpty)(this.originalValue);
const newValue = keepNull ? null : (0, utils_1.isEmpty)(value) || coerceEmptyList ? undefined : value;
// Set the value on our parent object
const changed = !utils_1.areEqual(newValue, this.value, strictOrder);
const changed = !(0, utils_1.areEqual)(newValue, this.value, strictOrder);
parentInstance[key] = newValue;

@@ -128,2 +129,5 @@ _tick.value++;

},
adapt(adapter) {
return adapt(this, adapter);
},
revertChanges() {

@@ -136,4 +140,4 @@ if (!computed) {

commitChanges() {
if (is_plain_object_1.isPlainObject(this.originalValue)) {
this.originalValue = mobx_1.toJS(this.value);
if ((0, is_plain_object_1.isPlainObject)(this.originalValue)) {
this.originalValue = (0, mobx_1.toJS)(this.value);
}

@@ -170,1 +174,31 @@ else {

exports.newValueFieldState = newValueFieldState;
/**
* Returns a proxy that looks exactly like the original `field`, in terms of valid/touched/errors/etc., but
* has any methods that use `V` overridden to use be `V2`.
*
* Note that `V2` can be a new type, like string -> number, or just a transformation on the same
* type, i.e. feet -> inches where both are `number`s.
*/
function adapt(field, adapter) {
return (0, proxies_1.newDelegateProxy)(field, {
rules: [],
get value() {
return adapter.toValue(field.value);
},
set value(v) {
field.value = adapter.fromValue(v);
},
set: (v) => {
field.value = adapter.fromValue(v);
},
get changedValue() {
return this.value;
},
adapt(adapter) {
return adapt(this, adapter);
},
get originalValue() {
return adapter.toValue(field.originalValue);
},
});
}

@@ -1,5 +0,3 @@

/// <reference types="react" />
import { Meta } from "@storybook/react";
declare const _default: Meta<import("@storybook/react").Args>;
declare const _default: import("@storybook/types").ComponentAnnotations<import("@storybook/react/dist/types-0a347bb9").R, import("@storybook/types").Args>;
export default _default;
export declare function AppExample(): JSX.Element;
export declare function AppExample(): import("react/jsx-runtime").JSX.Element;

@@ -11,4 +11,4 @@ "use strict";

function AppExample() {
return jsx_runtime_1.jsx(FormStateApp_1.FormStateApp, {}, void 0);
return (0, jsx_runtime_1.jsx)(FormStateApp_1.FormStateApp, {});
}
exports.AppExample = AppExample;

@@ -1,6 +0,5 @@

/// <reference types="react" />
import { FieldState } from "./index";
export declare function FormStateApp(): JSX.Element;
export declare function FormStateApp(): import("react/jsx-runtime").JSX.Element;
export declare function TextField(props: {
field: FieldState<string | null | undefined>;
}): JSX.Element;
}): import("react/jsx-runtime").JSX.Element;

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

function FormStateApp() {
const formState = index_1.useFormState({
const formState = (0, index_1.useFormState)({
config: formConfig,

@@ -28,34 +28,8 @@ // Simulate getting the initial form state back from a server call

});
return (jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => {
return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => {
var _a;
return (jsx_runtime_1.jsx("div", Object.assign({ className: "App" }, { children: jsx_runtime_1.jsxs("header", Object.assign({ className: "App-header" }, { children: [jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("b", { children: "Author" }, void 0),
jsx_runtime_1.jsx(TextField, { field: formState.firstName }, void 0),
jsx_runtime_1.jsx(TextField, { field: formState.lastName }, void 0)] }, void 0),
jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsxs("strong", { children: ["Books ", jsx_runtime_1.jsx("button", Object.assign({ onClick: () => formState.books.add({}) }, { children: "Add book" }), void 0)] }, void 0),
(_a = formState.books.rows) === null || _a === void 0 ? void 0 : _a.map((row, i) => {
return (jsx_runtime_1.jsxs("div", { children: ["Book ", i,
jsx_runtime_1.jsx("button", Object.assign({ onClick: () => formState.books.remove(row.value) }, { children: "X" }), void 0),
jsx_runtime_1.jsx(TextField, { field: row.title }, void 0)] }, i));
})] }, void 0),
jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("strong", { children: "Rows" }, void 0),
jsx_runtime_1.jsxs("table", Object.assign({ cellPadding: "4px" }, { children: [jsx_runtime_1.jsx("thead", { children: jsx_runtime_1.jsxs("tr", { children: [jsx_runtime_1.jsx("th", { children: "touched" }, void 0),
jsx_runtime_1.jsx("th", { children: "valid" }, void 0),
jsx_runtime_1.jsx("th", { children: "dirty" }, void 0),
jsx_runtime_1.jsx("th", { children: "errors" }, void 0)] }, void 0) }, void 0),
jsx_runtime_1.jsx("tbody", { children: jsx_runtime_1.jsxs("tr", { children: [jsx_runtime_1.jsx("td", { children: formState.books.touched.toString() }, void 0),
jsx_runtime_1.jsx("td", { children: formState.books.valid.toString() }, void 0),
jsx_runtime_1.jsx("td", { children: formState.books.dirty.toString() }, void 0),
jsx_runtime_1.jsx("td", { children: formState.books.errors }, void 0)] }, void 0) }, void 0)] }), void 0)] }, void 0),
jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("strong", { children: "Form" }, void 0),
jsx_runtime_1.jsxs("table", Object.assign({ cellPadding: "4px" }, { children: [jsx_runtime_1.jsx("thead", { children: jsx_runtime_1.jsxs("tr", { children: [jsx_runtime_1.jsx("th", { children: "touched" }, void 0),
jsx_runtime_1.jsx("th", { children: "valid" }, void 0),
jsx_runtime_1.jsx("th", { children: "dirty" }, void 0)] }, void 0) }, void 0),
jsx_runtime_1.jsx("tbody", { children: jsx_runtime_1.jsxs("tr", { children: [jsx_runtime_1.jsx("td", { children: formState.touched.toString() }, void 0),
jsx_runtime_1.jsx("td", { children: formState.valid.toString() }, void 0),
jsx_runtime_1.jsx("td", { children: formState.dirty.toString() }, void 0)] }, void 0) }, void 0)] }), void 0),
jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("button", Object.assign({ "data-testid": "touch", onClick: () => (formState.touched = !formState.touched) }, { children: "touch" }), void 0),
jsx_runtime_1.jsx("button", Object.assign({ "data-testid": "revertChanges", onClick: () => formState.revertChanges() }, { children: "revert" }), void 0),
jsx_runtime_1.jsx("button", Object.assign({ "data-testid": "commitChanges", onClick: () => formState.commitChanges() }, { children: "commit" }), void 0),
jsx_runtime_1.jsx("button", Object.assign({ "data-testid": "set", onClick: () => formState.set({ firstName: "a2" }) }, { children: "set" }), void 0)] }, void 0)] }, void 0)] }), void 0) }), void 0));
} }, void 0));
return ((0, jsx_runtime_1.jsx)("div", { className: "App", children: (0, jsx_runtime_1.jsxs)("header", { className: "App-header", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("b", { children: "Author" }), (0, jsx_runtime_1.jsx)(TextField, { field: formState.firstName }), (0, jsx_runtime_1.jsx)(TextField, { field: formState.lastName })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("strong", { children: ["Books ", (0, jsx_runtime_1.jsx)("button", { onClick: () => formState.books.add({}), children: "Add book" })] }), (_a = formState.books.rows) === null || _a === void 0 ? void 0 : _a.map((row, i) => {
return ((0, jsx_runtime_1.jsxs)("div", { children: ["Book ", i, (0, jsx_runtime_1.jsx)("button", { onClick: () => formState.books.remove(row.value), children: "X" }), (0, jsx_runtime_1.jsx)(TextField, { field: row.title })] }, i));
})] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("strong", { children: "Rows" }), (0, jsx_runtime_1.jsxs)("table", { cellPadding: "4px", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { children: "touched" }), (0, jsx_runtime_1.jsx)("th", { children: "valid" }), (0, jsx_runtime_1.jsx)("th", { children: "dirty" }), (0, jsx_runtime_1.jsx)("th", { children: "errors" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: formState.books.touched.toString() }), (0, jsx_runtime_1.jsx)("td", { children: formState.books.valid.toString() }), (0, jsx_runtime_1.jsx)("td", { children: formState.books.dirty.toString() }), (0, jsx_runtime_1.jsx)("td", { children: formState.books.errors })] }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("strong", { children: "Form" }), (0, jsx_runtime_1.jsxs)("table", { cellPadding: "4px", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { children: "touched" }), (0, jsx_runtime_1.jsx)("th", { children: "valid" }), (0, jsx_runtime_1.jsx)("th", { children: "dirty" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { children: formState.touched.toString() }), (0, jsx_runtime_1.jsx)("td", { children: formState.valid.toString() }), (0, jsx_runtime_1.jsx)("td", { children: formState.dirty.toString() })] }) })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("button", { "data-testid": "touch", onClick: () => (formState.touched = !formState.touched), children: "touch" }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "revertChanges", onClick: () => formState.revertChanges(), children: "revert" }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "commitChanges", onClick: () => formState.commitChanges(), children: "commit" }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "set", onClick: () => formState.set({ firstName: "a2" }), children: "set" })] })] })] }) }));
} }));
}

@@ -79,19 +53,6 @@ exports.FormStateApp = FormStateApp;

// parent uses `<Observer>`
return (jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsxs("span", { children: [field.key, ":"] }, void 0),
jsx_runtime_1.jsx("div", { children: jsx_runtime_1.jsx("input", { "data-testid": field.key, value: field.value || "", onBlur: () => field.blur(), readOnly: field.readOnly, onChange: (e) => {
return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("span", { children: [field.key, ":"] }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("input", { "data-testid": field.key, value: field.value || "", onBlur: () => field.blur(), readOnly: field.readOnly, onChange: (e) => {
field.set(e.target.value);
} }, void 0) }, void 0),
jsx_runtime_1.jsxs("table", Object.assign({ cellPadding: "4px" }, { children: [jsx_runtime_1.jsx("thead", { children: jsx_runtime_1.jsxs("tr", { children: [jsx_runtime_1.jsx("th", { children: "touched" }, void 0),
jsx_runtime_1.jsx("th", { children: "valid" }, void 0),
jsx_runtime_1.jsx("th", { children: "dirty" }, void 0),
jsx_runtime_1.jsx("th", { children: "readOnly" }, void 0),
jsx_runtime_1.jsx("th", { children: "errors" }, void 0),
jsx_runtime_1.jsx("th", { children: "original value" }, void 0)] }, void 0) }, void 0),
jsx_runtime_1.jsx("tbody", { children: jsx_runtime_1.jsxs("tr", { children: [jsx_runtime_1.jsx("td", Object.assign({ "data-testid": `${field.key}_touched` }, { children: field.touched.toString() }), void 0),
jsx_runtime_1.jsx("td", Object.assign({ "data-testid": `${field.key}_valid` }, { children: field.valid.toString() }), void 0),
jsx_runtime_1.jsx("td", Object.assign({ "data-testid": `${field.key}_dirty` }, { children: field.dirty.toString() }), void 0),
jsx_runtime_1.jsx("td", Object.assign({ "data-testid": `${field.key}_readOnly` }, { children: field.readOnly.toString() }), void 0),
jsx_runtime_1.jsx("td", Object.assign({ "data-testid": `${field.key}_errors` }, { children: field.errors }), void 0),
jsx_runtime_1.jsx("td", Object.assign({ "data-testid": `${field.key}_original` }, { children: field.originalValue }), void 0)] }, void 0) }, void 0)] }), void 0)] }, void 0)) }, void 0));
} }) }), (0, jsx_runtime_1.jsxs)("table", { cellPadding: "4px", children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { children: "touched" }), (0, jsx_runtime_1.jsx)("th", { children: "valid" }), (0, jsx_runtime_1.jsx)("th", { children: "dirty" }), (0, jsx_runtime_1.jsx)("th", { children: "readOnly" }), (0, jsx_runtime_1.jsx)("th", { children: "errors" }), (0, jsx_runtime_1.jsx)("th", { children: "original value" })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { "data-testid": `${field.key}_touched`, children: field.touched.toString() }), (0, jsx_runtime_1.jsx)("td", { "data-testid": `${field.key}_valid`, children: field.valid.toString() }), (0, jsx_runtime_1.jsx)("td", { "data-testid": `${field.key}_dirty`, children: field.dirty.toString() }), (0, jsx_runtime_1.jsx)("td", { "data-testid": `${field.key}_readOnly`, children: field.readOnly.toString() }), (0, jsx_runtime_1.jsx)("td", { "data-testid": `${field.key}_errors`, children: field.errors }), (0, jsx_runtime_1.jsx)("td", { "data-testid": `${field.key}_original`, children: field.originalValue })] }) })] })] })) }));
}
exports.TextField = TextField;

@@ -9,19 +9,19 @@ "use strict";

it("save resets dirty reactively", async () => {
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(FormStateApp_1.FormStateApp, {}, void 0));
expect(r.firstName_dirty()).toHaveTextContent("false");
rtl_utils_1.type(r.firstName, "changed");
expect(r.firstName_dirty()).toHaveTextContent("true");
react_1.fireEvent.blur(r.firstName());
expect(r.firstName_touched()).toHaveTextContent("true");
rtl_utils_1.click(r.commitChanges);
expect(r.firstName_dirty()).toHaveTextContent("false");
expect(r.firstName_touched()).toHaveTextContent("false");
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(FormStateApp_1.FormStateApp, {}));
expect(r.firstName_dirty).toHaveTextContent("false");
(0, rtl_utils_1.type)(r.firstName, "changed");
expect(r.firstName_dirty).toHaveTextContent("true");
react_1.fireEvent.blur(r.firstName);
expect(r.firstName_touched).toHaveTextContent("true");
(0, rtl_utils_1.click)(r.commitChanges);
expect(r.firstName_dirty).toHaveTextContent("false");
expect(r.firstName_touched).toHaveTextContent("false");
});
it("originalValue is reactive", async () => {
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(FormStateApp_1.FormStateApp, {}, void 0));
expect(r.firstName_original()).toHaveTextContent("a1");
rtl_utils_1.click(r.set);
rtl_utils_1.click(r.commitChanges);
expect(r.firstName_original()).toHaveTextContent("a2");
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(FormStateApp_1.FormStateApp, {}));
expect(r.firstName_original).toHaveTextContent("a1");
(0, rtl_utils_1.click)(r.set);
(0, rtl_utils_1.click)(r.commitChanges);
expect(r.firstName_original).toHaveTextContent("a2");
});
});

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

Color["Green"] = "GREEN";
})(Color = exports.Color || (exports.Color = {}));
})(Color || (exports.Color = Color = {}));
class DateOnly {

@@ -19,0 +19,0 @@ constructor(date) {

export { ObjectConfig } from "./config";
export { ListFieldState } from "./fields/listField";
export { createObjectState, ObjectState } from "./fields/objectField";
export { FieldState } from "./fields/valueField";
export { required, Rule } from "./rules";
export { ObjectState, createObjectState } from "./fields/objectField";
export { FieldState, ValueAdapter } from "./fields/valueField";
export { Rule, required } from "./rules";
export { useFormState } from "./useFormState";
export { useFormStates } from "./useFormStates";
/** A validation rule, given the value and name, return the error string if valid, or undefined if valid. */
export declare type Rule<V> = (opts: {
export type Rule<V> = (opts: {
value: V;

@@ -4,0 +4,0 @@ key: string;

@@ -8,5 +8,5 @@ "use strict";

// and breaks our ability to do `rules.some(r => r === required)`.
exports.required = mobx_1.action(({ value: v }) => {
exports.required = (0, mobx_1.action)(({ value: v }) => {
const isEmptyString = typeof v === "string" ? v.trim() === "" : false;
return v !== undefined && v !== null && !isEmptyString ? undefined : "Required";
});

@@ -6,5 +6,5 @@ "use strict";

// formState doesn't use actions
mobx_1.configure({ enforceActions: "never" });
(0, mobx_1.configure)({ enforceActions: "never" });
beforeEach(() => {
jest.useFakeTimers("modern");
jest.useFakeTimers();
});

@@ -11,0 +11,0 @@ afterEach(() => {

import { ObjectConfig } from "./config";
import { ObjectState } from "./fields/objectField";
export declare type Query<I> = {
export type Query<I> = {
data: I;

@@ -8,3 +8,3 @@ loading: boolean;

};
export declare type InputAndMap<T, I> = {
export type InputAndMap<T, I> = {
input: I;

@@ -14,3 +14,3 @@ map: (input: Exclude<I, null | undefined>) => T;

};
export declare type QueryAndMap<T, I> = {
export type QueryAndMap<T, I> = {
query: Query<I>;

@@ -20,3 +20,3 @@ map: (input: Exclude<I, null | undefined>) => T;

};
export declare type UseFormStateOpts<T, I> = {
export type UseFormStateOpts<T, I> = {
/** The form configuration, should be a module-level const or useMemo'd. */

@@ -23,0 +23,0 @@ config: ObjectConfig<T>;

@@ -21,11 +21,11 @@ "use strict";

// Use a ref so our memo'ized `onBlur` always see the latest value
const autoSaveRef = react_1.useRef(autoSave);
const autoSaveRef = (0, react_1.useRef)(autoSave);
autoSaveRef.current = autoSave;
const firstRunRef = react_1.useRef(true);
const firstRunRef = (0, react_1.useRef)(true);
// This is a little weird, but we need to know ahead of time, before the form useMemo, if we're working with classes/mobx proxies
const [firstInitValue] = react_1.useState(() => utils_1.initValue(config, init));
const isWrappingMobxProxy = !is_plain_object_1.isPlainObject(firstInitValue);
const [firstInitValue] = (0, react_1.useState)(() => (0, utils_1.initValue)(config, init));
const isWrappingMobxProxy = !(0, is_plain_object_1.isPlainObject)(firstInitValue);
// If they're using init.input, useMemo on it (and it might be an array), otherwise allow the identity of init be unstable
const dep = utils_1.isInput(init) ? makeArray(init.input) : utils_1.isQuery(init) ? [init.query.data, init.query.loading] : [];
const form = react_1.useMemo(() => {
const dep = (0, utils_1.isInput)(init) ? makeArray(init.input) : (0, utils_1.isQuery)(init) ? [init.query.data, init.query.loading] : [];
const form = (0, react_1.useMemo)(() => {
function maybeAutoSave() {

@@ -68,4 +68,4 @@ if (isAutoSaving === "in-flight") {

}
const value = firstRunRef.current ? firstInitValue : utils_1.initValue(config, init);
const form = objectField_1.createObjectState(config, value, { maybeAutoSave });
const value = firstRunRef.current ? firstInitValue : (0, utils_1.initValue)(config, init);
const form = (0, objectField_1.createObjectState)(config, value, { maybeAutoSave });
form.readOnly = readOnly;

@@ -83,3 +83,3 @@ setLoading(form, opts);

// queue their components' render), don't happen during our render, per https://fb.me/setstate-in-render.
react_1.useEffect(() => {
(0, react_1.useEffect)(() => {
// Ignore the 1st run b/c our 1st useMemo already initialized `form` with the current `init` value.

@@ -99,6 +99,6 @@ // Also for mobx proxies, we recreate a new form-state every time init changes, so that our

// component, but it's unlikely the user will always remember to do this).
form.set(utils_1.initValue(config, init), { refreshing: true });
form.set((0, utils_1.initValue)(config, init), { refreshing: true });
}, [form, ...dep]);
// Use useEffect so that we don't touch the form.init proxy during a render
react_1.useEffect(() => {
(0, react_1.useEffect)(() => {
form.readOnly = readOnly;

@@ -118,7 +118,7 @@ if (loading !== undefined) {

}
else if (utils_1.isInput(init) && !init.ifUndefined) {
else if ((0, utils_1.isInput)(init) && !init.ifUndefined) {
// Otherwise, check for `init.input`
form.loading = init.input === undefined;
}
else if (utils_1.isQuery(init)) {
else if ((0, utils_1.isQuery)(init)) {
// Or `query.loading`

@@ -125,0 +125,0 @@ form.loading = init.query.loading;

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

const data = { firstName: "bob" };
const form = useFormState_1.useFormState({
const form = (0, useFormState_1.useFormState)({
config,

@@ -25,5 +25,5 @@ // Then the lambda is passed the "de-undefined" data, i.e. `d.firstName` is not a compile error

});
return jsx_runtime_1.jsx("div", { children: form.firstName.value }, void 0);
return (0, jsx_runtime_1.jsx)("div", { children: form.firstName.value });
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
expect(r.baseElement).toHaveTextContent("bob");

@@ -34,4 +34,4 @@ });

function TestComponent() {
const [, setTick] = react_2.useState(0);
const form = useFormState_1.useFormState({
const [, setTick] = (0, react_2.useState)(0);
const form = (0, useFormState_1.useFormState)({
config,

@@ -41,9 +41,8 @@ init: { input: ["a", "b"], map: ([a, b]) => ({ firstName: a + b }) },

const onClick = () => [form.firstName.set("fred"), setTick(1)];
return (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("button", { "data-testid": "change", onClick: onClick }, void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName" }, { children: form.firstName.value }), void 0)] }, void 0));
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("button", { "data-testid": "change", onClick: onClick }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: form.firstName.value })] }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
expect(r.firstName()).toHaveTextContent("ab");
rtl_utils_1.click(r.change);
expect(r.firstName()).toHaveTextContent("fred");
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
expect(r.firstName).toHaveTextContent("ab");
(0, rtl_utils_1.click)(r.change);
expect(r.firstName).toHaveTextContent("fred");
});

@@ -56,9 +55,9 @@ it("uses default if init.input is undefined", async () => {

const data = Math.random() >= 0 ? undefined : { firstName: "bob" };
const form = useFormState_1.useFormState({
const form = (0, useFormState_1.useFormState)({
config,
init: { input: data, map: (d) => ({ firstName: d.firstName }) },
});
return jsx_runtime_1.jsx("div", { children: form.firstName.value }, void 0);
return (0, jsx_runtime_1.jsx)("div", { children: form.firstName.value });
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// Then we init.map wasn't called, and we used {} instead

@@ -76,3 +75,3 @@ expect(r.baseElement.textContent).toEqual("");

const data = Math.random() >= 0 ? undefined : { firstName: "bob" };
const form = useFormState_1.useFormState({
const form = (0, useFormState_1.useFormState)({
config,

@@ -86,6 +85,5 @@ // And we pass `ifUndefined`

});
return (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName" }, { children: form.firstName.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "changedValue" }, { children: JSON.stringify(form.changedValue) }), void 0)] }, void 0));
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: form.firstName.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "changedValue", children: JSON.stringify(form.changedValue) })] }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// Then we use the ifUndefined value

@@ -98,4 +96,4 @@ expect(r.firstName.textContent).toEqual("default");

function TestComponent() {
const [, setTick] = react_2.useState(0);
const form = useFormState_1.useFormState({
const [, setTick] = (0, react_2.useState)(0);
const form = (0, useFormState_1.useFormState)({
config,

@@ -105,3 +103,3 @@ // That's using a raw init value

});
return (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("button", { "data-testid": "change", onClick: () => {
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("button", { "data-testid": "change", onClick: () => {
// When that value changes

@@ -111,10 +109,9 @@ form.firstName.set("fred");

setTick(1);
} }, void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName" }, { children: form.firstName.value }), void 0)] }, void 0));
} }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: form.firstName.value })] }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
expect(r.firstName()).toHaveTextContent("bob");
rtl_utils_1.click(r.change);
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
expect(r.firstName).toHaveTextContent("bob");
(0, rtl_utils_1.click)(r.change);
// Then the change didn't get dropped due to init being unstable
expect(r.firstName()).toHaveTextContent("fred");
expect(r.firstName).toHaveTextContent("fred");
});

@@ -124,6 +121,6 @@ it("doesn't required an init value", async () => {

const config = { firstName: { type: "value" } };
const form = useFormState_1.useFormState({ config });
return jsx_runtime_1.jsx("div", { children: form.firstName.value }, void 0);
const form = (0, useFormState_1.useFormState)({ config });
return (0, jsx_runtime_1.jsx)("div", { children: form.firstName.value });
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
expect(r.baseElement.textContent).toEqual("");

@@ -170,4 +167,4 @@ });

// And we start out with data1
const [data, setData] = react_2.useState(data1);
const form = useFormState_1.useFormState({ config, init: { input: data, map: (d) => d } });
const [data, setData] = (0, react_2.useState)(data1);
const form = (0, useFormState_1.useFormState)({ config, init: { input: data, map: (d) => d } });
function makeLocalChanges() {

@@ -178,26 +175,16 @@ form.firstName.value = "local";

}
return (jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName" }, { children: form.firstName.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "lastName" }, { children: form.lastName.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "street" }, { children: form.address.street.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "city" }, { children: form.address.city.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "title1" }, { children: form.books.rows[0].title.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "title2" }, { children: form.books.rows[1].title.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "booksLength" }, { children: form.books.rows.length }), void 0),
jsx_runtime_1.jsx("button", { "data-testid": "makeLocalChanges", onClick: makeLocalChanges }, void 0),
jsx_runtime_1.jsx("button", { "data-testid": "refreshData", onClick: () => setData(data2) }, void 0),
jsx_runtime_1.jsx("button", { "data-testid": "saveData", onClick: () => setData(data3) }, void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "changedValue" }, { children: JSON.stringify(form.changedValue) }), void 0)] }, void 0)) }, void 0));
return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: form.firstName.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "lastName", children: form.lastName.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "street", children: form.address.street.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "city", children: form.address.city.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "title1", children: form.books.rows[0].title.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "title2", children: form.books.rows[1].title.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "booksLength", children: form.books.rows.length }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "makeLocalChanges", onClick: makeLocalChanges }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "refreshData", onClick: () => setData(data2) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "saveData", onClick: () => setData(data3) }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "changedValue", children: JSON.stringify(form.changedValue) })] })) }));
}
// And we start out with the initial query data
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
expect(r.firstName().textContent).toEqual("f1");
expect(r.street().textContent).toEqual("s1");
expect(r.title1().textContent).toEqual("a1");
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
expect(r.firstName.textContent).toEqual("f1");
expect(r.street.textContent).toEqual("s1");
expect(r.title1.textContent).toEqual("a1");
// When we make some local changes
rtl_utils_1.click(r.makeLocalChanges);
(0, rtl_utils_1.click)(r.makeLocalChanges);
// Then we see them
expect(r.firstName().textContent).toEqual("local");
expect(r.street().textContent).toEqual("local");
expect(r.title1().textContent).toEqual("local");
expect(JSON.parse(r.changedValue().textContent)).toEqual({
expect(r.firstName.textContent).toEqual("local");
expect(r.street.textContent).toEqual("local");
expect(r.title1.textContent).toEqual("local");
expect(JSON.parse(r.changedValue.textContent)).toEqual({
id: "a:1",

@@ -209,13 +196,13 @@ address: { id: "address:1", street: "local" },

// And when the new query is ran i.e. due to a cache refresh
rtl_utils_1.click(r.refreshData);
(0, rtl_utils_1.click)(r.refreshData);
// Then we kept our local changes
expect(r.firstName().textContent).toEqual("local");
expect(r.street().textContent).toEqual("local");
expect(r.title1().textContent).toEqual("local");
expect(r.firstName.textContent).toEqual("local");
expect(r.street.textContent).toEqual("local");
expect(r.title1.textContent).toEqual("local");
// But we also see the new data for fields we have not changed
expect(r.lastName().textContent).toEqual("l2");
expect(r.city().textContent).toEqual("c2");
expect(r.title2().textContent).toEqual("b2");
expect(r.booksLength().textContent).toEqual("3");
expect(JSON.parse(r.changedValue().textContent)).toEqual({
expect(r.lastName.textContent).toEqual("l2");
expect(r.city.textContent).toEqual("c2");
expect(r.title2.textContent).toEqual("b2");
expect(r.booksLength.textContent).toEqual("3");
expect(JSON.parse(r.changedValue.textContent)).toEqual({
id: "a:1",

@@ -227,5 +214,5 @@ address: { id: "address:1", street: "local" },

// And then when our mutation results come back
rtl_utils_1.click(r.saveData);
(0, rtl_utils_1.click)(r.saveData);
// Then changedValue doesn't show our local changes anymore
expect(JSON.parse(r.changedValue().textContent)).toEqual({
expect(JSON.parse(r.changedValue.textContent)).toEqual({
id: "a:1",

@@ -250,4 +237,4 @@ });

// And we start out with data1
const [data, setData] = react_2.useState(data1);
const form = useFormState_1.useFormState({
const [data, setData] = (0, react_2.useState)(data1);
const form = (0, useFormState_1.useFormState)({
config,

@@ -258,18 +245,15 @@ init: { input: data, map: (d) => d },

});
return (jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName" }, { children: form.firstName.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "street" }, { children: form.address.street.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "title1" }, { children: form.books.rows[0].title.value }), void 0),
jsx_runtime_1.jsx("button", { "data-testid": "refreshData", onClick: () => setData(data2) }, void 0)] }, void 0)) }, void 0));
return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: form.firstName.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "street", children: form.address.street.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "title1", children: form.books.rows[0].title.value }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "refreshData", onClick: () => setData(data2) })] })) }));
}
// And we start out with the initial query data
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
expect(r.firstName().textContent).toEqual("f1");
expect(r.street().textContent).toEqual("s1");
expect(r.title1().textContent).toEqual("a1");
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
expect(r.firstName.textContent).toEqual("f1");
expect(r.street.textContent).toEqual("s1");
expect(r.title1.textContent).toEqual("a1");
// When the new query is ran i.e. due to a cache refresh
rtl_utils_1.click(r.refreshData);
(0, rtl_utils_1.click)(r.refreshData);
// Then we see the latest data
expect(r.firstName().textContent).toEqual("f2");
expect(r.street().textContent).toEqual("s2");
expect(r.title1().textContent).toEqual("a2");
expect(r.firstName.textContent).toEqual("f2");
expect(r.street.textContent).toEqual("s2");
expect(r.title1.textContent).toEqual("a2");
});

@@ -293,5 +277,5 @@ it("useFormState can accept new data with computed fields", async () => {

// And we start out with data1
const [data, setData] = react_2.useState(data1);
const author = react_2.useMemo(() => new AuthorRow(data.firstName, data.lastName), [data]);
const config = react_2.useMemo(() => ({
const [data, setData] = (0, react_2.useState)(data1);
const author = (0, react_2.useMemo)(() => new AuthorRow(data.firstName, data.lastName), [data]);
const config = (0, react_2.useMemo)(() => ({
firstName: { type: "value" },

@@ -301,15 +285,13 @@ lastName: { type: "value" },

}), []);
const form = useFormState_1.useFormState({ config, init: { input: author, map: (a) => a } });
return (jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName" }, { children: form.firstName.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "fullName" }, { children: form.fullName.value }), void 0),
jsx_runtime_1.jsx("button", { "data-testid": "refreshData", onClick: () => setData(data2) }, void 0)] }, void 0)) }, void 0));
const form = (0, useFormState_1.useFormState)({ config, init: { input: author, map: (a) => a } });
return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: form.firstName.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "fullName", children: form.fullName.value }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "refreshData", onClick: () => setData(data2) })] })) }));
}
// And we start out with the initial query data
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
expect(r.firstName().textContent).toEqual("f1");
expect(r.fullName().textContent).toEqual("f1 l1");
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
expect(r.firstName.textContent).toEqual("f1");
expect(r.fullName.textContent).toEqual("f1 l1");
// When the new query is ran i.e. due to a cache refresh
rtl_utils_1.click(r.refreshData);
expect(r.firstName().textContent).toEqual("f2");
expect(r.fullName().textContent).toEqual("f2 l2");
(0, rtl_utils_1.click)(r.refreshData);
expect(r.firstName.textContent).toEqual("f2");
expect(r.fullName.textContent).toEqual("f2 l2");
});

@@ -321,5 +303,5 @@ it("can trigger auto save for fields in list that were initially undefined", async () => {

// When the data is initially undefined
const [data, setData] = react_2.useState();
const [data, setData] = (0, react_2.useState)();
const data2 = { books: [{ title: "Title 1" }] };
const form = useFormState_1.useFormState({
const form = (0, useFormState_1.useFormState)({
config: authorWithBooksConfig,

@@ -329,17 +311,14 @@ init: { input: data, map: (d) => d, ifUndefined: { books: [] } },

});
return (jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("button", { "data-testid": "refreshData", onClick: () => setData(data2) }, void 0),
jsx_runtime_1.jsx("button", { "data-testid": "add", onClick: () => form.books.add({ title: "New Book" }) }, void 0),
jsx_runtime_1.jsx("button", { "data-testid": "blurBookOne", onClick: () => focusAndBlur(form.books.rows[0].title) }, void 0),
jsx_runtime_1.jsx("button", { "data-testid": "blurBookTwo", onClick: () => focusAndBlur(form.books.rows[1].title) }, void 0)] }, void 0)) }, void 0));
return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("button", { "data-testid": "refreshData", onClick: () => setData(data2) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "add", onClick: () => form.books.add({ title: "New Book" }) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "blurBookOne", onClick: () => focusAndBlur(form.books.rows[0].title) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "blurBookTwo", onClick: () => focusAndBlur(form.books.rows[1].title) })] })) }));
}
// Given a formState with `onBlur` set
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// When the data/child is now available
rtl_utils_1.click(r.refreshData);
(0, rtl_utils_1.click)(r.refreshData);
// And the field is blurred
await rtl_utils_1.clickAndWait(r.blurBookOne);
await (0, rtl_utils_1.clickAndWait)(r.blurBookOne);
// Then we don't auto-save because nothing has changed
expect(autoSave).toBeCalledTimes(0);
// And when adding a new book
await rtl_utils_1.clickAndWait(r.add);
await (0, rtl_utils_1.clickAndWait)(r.add);
// We autoSave the new row right away (because we don't have any validation rules

@@ -349,3 +328,3 @@ // that say the new row can't be empty)

// And the new book is blurred
await rtl_utils_1.clickAndWait(r.blurBookTwo);
await (0, rtl_utils_1.clickAndWait)(r.blurBookTwo);
// Then we auto save again

@@ -360,3 +339,3 @@ expect(autoSave).toBeCalledTimes(2);

const data = { firstName: "f1", lastName: "f1" };
const form = useFormState_1.useFormState({
const form = (0, useFormState_1.useFormState)({
config,

@@ -370,7 +349,7 @@ init: data,

});
return (jsx_runtime_1.jsx("div", { children: jsx_runtime_1.jsx("button", { "data-testid": "setFirst", onClick: () => form.firstName.set("f2") }, void 0) }, void 0));
return ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("button", { "data-testid": "setFirst", onClick: () => form.firstName.set("f2") }) }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// When we change firstName
await rtl_utils_1.clickAndWait(r.setFirst);
await (0, rtl_utils_1.clickAndWait)(r.setFirst);
// When autoSave didn't infinite loop

@@ -385,3 +364,3 @@ expect(autoSaves).toEqual(1);

const data = { firstName: "f1", lastName: "f1" };
const form = useFormState_1.useFormState({
const form = (0, useFormState_1.useFormState)({
config,

@@ -391,11 +370,11 @@ init: data,

});
return (jsx_runtime_1.jsx("div", { children: jsx_runtime_1.jsx("button", { "data-testid": "set", onClick: () => {
return ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("button", { "data-testid": "set", onClick: () => {
// And it sets two fields in a single callback
form.firstName.set("f2");
form.lastName.set("l2");
} }, void 0) }, void 0));
} }) }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// When we change both
await rtl_utils_1.clickAndWait(r.set);
await (0, rtl_utils_1.clickAndWait)(r.set);
// Then we only call autoSave once

@@ -414,4 +393,4 @@ expect(autoSave).toBeCalledTimes(1);

function TestComponent() {
const [apiData, setApiData] = react_2.useState({ id: "a:1", firstName: "Brandon", lastName: "Dow" });
const state = useFormState_1.useFormState({ config, autoSave, init: { input: apiData, map: (v) => v } });
const [apiData, setApiData] = (0, react_2.useState)({ id: "a:1", firstName: "Brandon", lastName: "Dow" });
const state = (0, useFormState_1.useFormState)({ config, autoSave, init: { input: apiData, map: (v) => v } });
async function autoSave(form) {

@@ -424,4 +403,3 @@ autoSaveStub(form.changedValue);

}
return (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsxs("div", Object.assign({ "data-testid": "name" }, { children: [apiData.firstName, " ", apiData.lastName] }), void 0),
jsx_runtime_1.jsx("button", { "data-testid": "focusSetAndSaveField", onClick: () => {
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { "data-testid": "name", children: [apiData.firstName, " ", apiData.lastName] }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "focusSetAndSaveField", onClick: () => {
state.firstName.focus();

@@ -431,24 +409,23 @@ state.firstName.set("Foo");

state.firstName.blur();
} }, void 0),
jsx_runtime_1.jsx("button", { "data-testid": "focusSetAndSaveFieldLastName", onClick: () => {
} }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "focusSetAndSaveFieldLastName", onClick: () => {
state.lastName.focus();
state.lastName.set("Bar");
state.lastName.maybeAutoSave();
} }, void 0)] }, void 0));
} })] }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// And triggering the auto save behavior before awaiting the initial promise to
// resolve so we have pending changes.
rtl_utils_1.click(r.focusSetAndSaveField(), { allowAsync: true });
(0, rtl_utils_1.click)(r.focusSetAndSaveField, { allowAsync: true });
// Let the initial autoSave be called
react_1.act(() => {
(0, react_1.act)(() => {
jest.runOnlyPendingTimers();
});
expect(r.name()).toHaveTextContent("Brandon Dow");
expect(r.name).toHaveTextContent("Brandon Dow");
expect(autoSaveStub).toBeCalledTimes(1);
expect(autoSaveStub).toBeCalledWith({ id: "a:1", firstName: "Foo" });
// And while that is in flight, trigger another user action
rtl_utils_1.click(r.focusSetAndSaveFieldLastName(), { allowAsync: true });
(0, rtl_utils_1.click)(r.focusSetAndSaveFieldLastName, { allowAsync: true });
// (Use `wait` so that our timer flushes before the Promise.resolve(1) is ran)
await rtl_utils_1.wait();
await (0, rtl_utils_1.wait)();
// Then expect the auto save to only have been called twice. Once with each changedValues.

@@ -468,7 +445,7 @@ expect(autoSaveStub).toBeCalledTimes(2);

function TestComponent() {
const fs = useFormState_1.useFormState({
const fs = (0, useFormState_1.useFormState)({
config,
addRules(fs) {
// And also has calculated values
mobx_1.reaction(() => fs.firstName.value, (curr) => (fs.lastName.value = curr));
(0, mobx_1.reaction)(() => fs.firstName.value, (curr) => (fs.lastName.value = curr));
},

@@ -478,7 +455,7 @@ autoSave: (fs) => autoSaveStub(fs.changedValue),

});
return jsx_runtime_1.jsx(FormStateApp_1.TextField, { field: fs.firstName }, void 0);
return (0, jsx_runtime_1.jsx)(FormStateApp_1.TextField, { field: fs.firstName });
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// When the user sets one field
await rtl_utils_1.typeAndWait(r.firstName, "first");
await (0, rtl_utils_1.typeAndWait)(r.firstName, "first");
// Then we only called autoSave once

@@ -491,13 +468,13 @@ expect(autoSaveStub).toBeCalledTimes(1);

function TestComponent({ loading }) {
const form = useFormState_1.useFormState({ config, loading });
return jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "loading" }, { children: String(form.loading) }), void 0) }, void 0);
const form = (0, useFormState_1.useFormState)({ config, loading });
return (0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => (0, jsx_runtime_1.jsx)("div", { "data-testid": "loading", children: String(form.loading) }) });
}
// And we initially pass in `init.query.loading: true`
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, { loading: true }, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { loading: true }));
// Then the form is marked as loading
expect(r.loading()).toHaveTextContent("true");
expect(r.loading).toHaveTextContent("true");
// And when the query is not loading
await r.rerender(jsx_runtime_1.jsx(TestComponent, { loading: false }, void 0));
await r.rerender((0, jsx_runtime_1.jsx)(TestComponent, { loading: false }));
// Then the form is marked as not loading
expect(r.loading()).toHaveTextContent("false");
expect(r.loading).toHaveTextContent("false");
});

@@ -507,13 +484,13 @@ it("sets loading if input.data is undefined", async () => {

function TestComponent({ data }) {
const form = useFormState_1.useFormState({ config, init: { input: data, map: (d) => d } });
return jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "loading" }, { children: String(form.loading) }), void 0) }, void 0);
const form = (0, useFormState_1.useFormState)({ config, init: { input: data, map: (d) => d } });
return (0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => (0, jsx_runtime_1.jsx)("div", { "data-testid": "loading", children: String(form.loading) }) });
}
// And we initially pass in `init.input: undefined`
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, { data: undefined }, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { data: undefined }));
// Then the form is marked as loading
expect(r.loading()).toHaveTextContent("true");
expect(r.loading).toHaveTextContent("true");
// And when the data is no longer undefined
await r.rerender(jsx_runtime_1.jsx(TestComponent, { data: { firstName: "first" } }, void 0));
await r.rerender((0, jsx_runtime_1.jsx)(TestComponent, { data: { firstName: "first" } }));
// Then the form is marked as not loading
expect(r.loading()).toHaveTextContent("false");
expect(r.loading).toHaveTextContent("false");
});

@@ -523,13 +500,13 @@ it("sets loading if query.loading is true", async () => {

function TestComponent({ loading, data }) {
const form = useFormState_1.useFormState({ config, init: { query: { data, loading, error: null }, map: (d) => d } });
return jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "loading" }, { children: String(form.loading) }), void 0) }, void 0);
const form = (0, useFormState_1.useFormState)({ config, init: { query: { data, loading, error: null }, map: (d) => d } });
return (0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => (0, jsx_runtime_1.jsx)("div", { "data-testid": "loading", children: String(form.loading) }) });
}
// And we initially pass in `init.query.loading: true`
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, { loading: true, data: undefined }, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { loading: true, data: undefined }));
// Then the form is marked as loading
expect(r.loading()).toHaveTextContent("true");
expect(r.loading).toHaveTextContent("true");
// And when the query is not loading
await r.rerender(jsx_runtime_1.jsx(TestComponent, { loading: false, data: { firstName: "first" } }, void 0));
await r.rerender((0, jsx_runtime_1.jsx)(TestComponent, { loading: false, data: { firstName: "first" } }));
// Then the form is marked as not loading
expect(r.loading()).toHaveTextContent("false");
expect(r.loading).toHaveTextContent("false");
});

@@ -540,32 +517,27 @@ it("treats the id changing as a whole new entity instead of a delete", async () => {

// Given an initial author a1
const [author, setAuthor] = react_2.useState({ id: "a:1", firstName: "a1" });
const form = useFormState_1.useFormState({ config, init: { input: author, map: (a) => a } });
return (jsx_runtime_1.jsx(mobx_react_1.Observer, { children: () => (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "value" }, { children: String(form.firstName.value) }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "dirty" }, { children: String(form.firstName.dirty) }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "originalValue" }, { children: String(form.firstName.originalValue) }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "objectValue" }, { children: String(form.value.firstName) }), void 0),
jsx_runtime_1.jsx("div", { "data-testid": "a1", onClick: () => setAuthor({ id: "a:1", firstName: "a1" }) }, void 0),
jsx_runtime_1.jsx("div", { "data-testid": "a2", onClick: () => setAuthor({ id: "a:2", firstName: undefined }) }, void 0)] }, void 0)) }, void 0));
const [author, setAuthor] = (0, react_2.useState)({ id: "a:1", firstName: "a1" });
const form = (0, useFormState_1.useFormState)({ config, init: { input: author, map: (a) => a } });
return ((0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "value", children: String(form.firstName.value) }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "dirty", children: String(form.firstName.dirty) }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "originalValue", children: String(form.firstName.originalValue) }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "objectValue", children: String(form.value.firstName) }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "a1", onClick: () => setAuthor({ id: "a:1", firstName: "a1" }) }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "a2", onClick: () => setAuthor({ id: "a:2", firstName: undefined }) })] })) }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// And the value is initially a1/and not dirty
expect(r.value()).toHaveTextContent("a1");
expect(r.dirty()).toHaveTextContent("false");
expect(r.originalValue()).toHaveTextContent("a1");
expect(r.objectValue()).toHaveTextContent("a1");
expect(r.value).toHaveTextContent("a1");
expect(r.dirty).toHaveTextContent("false");
expect(r.originalValue).toHaveTextContent("a1");
expect(r.objectValue).toHaveTextContent("a1");
// When we switch to a completely separate author
rtl_utils_1.click(r.a2);
(0, rtl_utils_1.click)(r.a2);
// Then it switches to the next author
expect(r.value()).toHaveTextContent("undefined");
expect(r.value).toHaveTextContent("undefined");
// And the field is not dirty (which had been the case before this bug fix)
expect(r.dirty()).toHaveTextContent("false");
expect(r.originalValue()).toHaveTextContent("undefined");
expect(r.objectValue()).toHaveTextContent("undefined");
expect(r.dirty).toHaveTextContent("false");
expect(r.originalValue).toHaveTextContent("undefined");
expect(r.objectValue).toHaveTextContent("undefined");
// And when we switch back to the original author
rtl_utils_1.click(r.a1);
(0, rtl_utils_1.click)(r.a1);
// It again restores the value and does not think an edit was WIP
expect(r.value()).toHaveTextContent("a1");
expect(r.dirty()).toHaveTextContent("false");
expect(r.originalValue()).toHaveTextContent("a1");
expect(r.objectValue()).toHaveTextContent("a1");
expect(r.value).toHaveTextContent("a1");
expect(r.dirty).toHaveTextContent("false");
expect(r.originalValue).toHaveTextContent("a1");
expect(r.objectValue).toHaveTextContent("a1");
});

@@ -572,0 +544,0 @@ });

import { ObjectConfig } from "./config";
import { ObjectState } from "./fields/objectField";
export declare type ObjectStateCache<T, I> = Record<string, [ObjectState<T>, I]>;
declare type UseFormStatesOpts<T, I> = {
export type ObjectStateCache<T, I> = Record<string, [ObjectState<T>, I]>;
type UseFormStatesOpts<T, I> = {
/**

@@ -43,3 +43,3 @@ * The config to use for each form state.

};
declare type UseFormStatesHook<T, I> = {
type UseFormStatesHook<T, I> = {
getFormState: (input: I, opts?: {

@@ -46,0 +46,0 @@ readOnly?: boolean;

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

const { config, autoSave, getId, map, addRules, readOnly = false } = opts;
const objectStateCache = react_1.useMemo(() => ({}),
const objectStateCache = (0, react_1.useMemo)(() => ({}),
// Force resetting the cache if config changes

@@ -15,10 +15,10 @@ // eslint-disable-next-line react-hooks/exhaustive-deps

// Keep track of ObjectStates that triggered auto-save when a save was already in progress.
const pendingAutoSaves = react_1.useRef(new Set());
const pendingAutoSaves = (0, react_1.useRef)(new Set());
// Use a ref so our memo'ized `autoSave` always see the latest value
const autoSaveRef = react_1.useRef(autoSave);
const autoSaveRef = (0, react_1.useRef)(autoSave);
autoSaveRef.current = autoSave;
// Use a ref b/c we're memod
const readOnlyRef = react_1.useRef(readOnly);
const readOnlyRef = (0, react_1.useRef)(readOnly);
readOnlyRef.current = readOnly;
const getFormState = react_1.useCallback((input, opts = {}) => {
const getFormState = (0, react_1.useCallback)((input, opts = {}) => {
const existing = objectStateCache[getId(input)];

@@ -59,3 +59,3 @@ let form = existing === null || existing === void 0 ? void 0 : existing[0];

if (!form) {
form = objectField_1.createObjectState(config, utils_1.initValue(config, map ? { map, input } : input), {
form = (0, objectField_1.createObjectState)(config, (0, utils_1.initValue)(config, map ? { map, input } : input), {
maybeAutoSave: () => maybeAutoSave(form),

@@ -70,3 +70,3 @@ });

if (existing && existing[1] !== input) {
form.set(utils_1.initValue(config, map ? { map, input } : input), { refreshing: true });
form.set((0, utils_1.initValue)(config, map ? { map, input } : input), { refreshing: true });
existing[1] = input;

@@ -73,0 +73,0 @@ }

@@ -15,7 +15,7 @@ "use strict";

function ChildComponent({ os }) {
return jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName" }, { children: os.firstName.value }), void 0);
return (0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: os.firstName.value });
}
function TestComponent() {
const config = { id: { type: "value" }, firstName: { type: "value" } };
const { getFormState } = useFormStates_1.useFormStates({
const { getFormState } = (0, useFormStates_1.useFormStates)({
config,

@@ -25,7 +25,7 @@ autoSave,

});
return (jsx_runtime_1.jsx("div", { children: jsx_runtime_1.jsx(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }) }, void 0) }, void 0));
return ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }) }) }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// And the child component has defined state
expect(r.firstName()).toHaveTextContent("Brandon");
expect(r.firstName).toHaveTextContent("Brandon");
});

@@ -37,21 +37,19 @@ it("can update existing object state from cache with new values", async () => {

function TestComponent() {
const [apiData, setApiData] = react_1.useState({ id: "a:1", firstName: "Brandon" });
const { getFormState } = useFormStates_1.useFormStates({ config, autoSave, getId: (o) => o.id });
const [apiData, setApiData] = (0, react_1.useState)({ id: "a:1", firstName: "Brandon" });
const { getFormState } = (0, useFormStates_1.useFormStates)({ config, autoSave, getId: (o) => o.id });
// Memoize an original for comparing the update against.
const originalState = react_1.useMemo(() => getFormState(apiData), [getFormState]);
const originalState = (0, react_1.useMemo)(() => getFormState(apiData), [getFormState]);
const state = getFormState(apiData);
return (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName" }, { children: state.firstName.value }), void 0),
jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "statesEqual" }, { children: JSON.stringify(state === originalState) }), void 0),
jsx_runtime_1.jsx("button", { "data-testid": "updateApiData", onClick: () => setApiData((prevState) => ({ ...prevState, firstName: "Bob" })) }, void 0)] }, void 0));
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: state.firstName.value }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "statesEqual", children: JSON.stringify(state === originalState) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "updateApiData", onClick: () => setApiData((prevState) => ({ ...prevState, firstName: "Bob" })) })] }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// And the initial values for the form state display
expect(r.firstName()).toHaveTextContent("Brandon");
expect(r.statesEqual()).toHaveTextContent("true");
expect(r.firstName).toHaveTextContent("Brandon");
expect(r.statesEqual).toHaveTextContent("true");
// When updating the API data
await rtl_utils_1.clickAndWait(r.updateApiData());
await (0, rtl_utils_1.clickAndWait)(r.updateApiData);
// Then the new value is shown in the component
expect(r.firstName()).toHaveTextContent("Bob");
expect(r.firstName).toHaveTextContent("Bob");
// And the two states are using the same reference
expect(r.statesEqual()).toHaveTextContent("true");
expect(r.statesEqual).toHaveTextContent("true");
});

@@ -67,5 +65,5 @@ it("can queue up changes for auto save if a save is already in progress - works across multiple states", async () => {

function TestComponent() {
const [apiData, setApiData] = react_1.useState({ id: "a:1", firstName: "Tony", lastName: "Stark" });
const [apiData2, setApiData2] = react_1.useState({ id: "a:2", firstName: "Steve", lastName: "Rogers" });
const { getFormState } = useFormStates_1.useFormStates({ config, autoSave, getId: (o) => o.id });
const [apiData, setApiData] = (0, react_1.useState)({ id: "a:1", firstName: "Tony", lastName: "Stark" });
const [apiData2, setApiData2] = (0, react_1.useState)({ id: "a:2", firstName: "Steve", lastName: "Rogers" });
const { getFormState } = (0, useFormStates_1.useFormStates)({ config, autoSave, getId: (o) => o.id });
const state = getFormState(apiData);

@@ -84,26 +82,23 @@ const state2 = getFormState(apiData2);

}
return (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName" }, { children: state.firstName.value }), void 0),
jsx_runtime_1.jsx("button", { "data-testid": "focusSetAndSaveField", onClick: () => {
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: state.firstName.value }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "focusSetAndSaveField", onClick: () => {
state.firstName.focus();
state.firstName.set("Foo");
state.firstName.maybeAutoSave();
} }, void 0),
jsx_runtime_1.jsx("button", { "data-testid": "focusSetAndSaveFieldLastName", onClick: () => {
} }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "focusSetAndSaveFieldLastName", onClick: () => {
state.lastName.focus();
state.lastName.set("Bar");
state.lastName.maybeAutoSave();
} }, void 0),
jsx_runtime_1.jsx("button", { "data-testid": "focusSetAndSaveField2", onClick: () => {
} }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "focusSetAndSaveField2", onClick: () => {
state2.lastName.focus();
state2.lastName.set("Bar");
state2.lastName.maybeAutoSave();
} }, void 0)] }, void 0));
} })] }));
}
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// And triggering the auto save behavior before awaiting the initial promise to resolve so we have pending changes.
rtl_utils_1.click(r.focusSetAndSaveField());
rtl_utils_1.click(r.focusSetAndSaveFieldLastName());
rtl_utils_1.click(r.focusSetAndSaveField2());
(0, rtl_utils_1.click)(r.focusSetAndSaveField);
(0, rtl_utils_1.click)(r.focusSetAndSaveFieldLastName);
(0, rtl_utils_1.click)(r.focusSetAndSaveField2);
// Awaits the promises for all methods triggered above
await rtl_utils_1.wait();
await (0, rtl_utils_1.wait)();
// Then expect the auto save to only have been called two times. Once with each set of changedValues.

@@ -125,19 +120,18 @@ expect(autoSaveStub).toBeCalledTimes(2);

function TestComponent() {
const [config, setConfig] = react_1.useState(originalConfig);
const { getFormState } = useFormStates_1.useFormStates({ config, autoSave, getId: (o) => o.id });
const [config, setConfig] = (0, react_1.useState)(originalConfig);
const { getFormState } = (0, useFormStates_1.useFormStates)({ config, autoSave, getId: (o) => o.id });
// Memoize an original for comparing the update against.
// eslint-disable-next-line react-hooks/exhaustive-deps
const originalState = react_1.useMemo(() => getFormState(apiData), []);
const originalState = (0, react_1.useMemo)(() => getFormState(apiData), []);
const state = getFormState(apiData);
return (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "statesEqual" }, { children: JSON.stringify(state === originalState) }), void 0),
jsx_runtime_1.jsx("button", { "data-testid": "updateConfig", onClick: () => setConfig(updatedConfig) }, void 0)] }, void 0));
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { "data-testid": "statesEqual", children: JSON.stringify(state === originalState) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": "updateConfig", onClick: () => setConfig(updatedConfig) })] }));
}
// When rendered with the original configuration
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// Then the two form-states generated are equal
expect(r.statesEqual()).toHaveTextContent("true");
expect(r.statesEqual).toHaveTextContent("true");
// When updating the configuration object
rtl_utils_1.click(r.updateConfig);
(0, rtl_utils_1.click)(r.updateConfig);
// Then the two form-states are no longer equal.
expect(r.statesEqual()).toHaveTextContent("false");
expect(r.statesEqual).toHaveTextContent("false");
});

@@ -149,3 +143,3 @@ it("calls addRules once per form state", async () => {

const config = { id: { type: "value" }, firstName: { type: "value" } };
const { getFormState } = useFormStates_1.useFormStates({
const { getFormState } = (0, useFormStates_1.useFormStates)({
config,

@@ -155,7 +149,6 @@ addRules,

});
return (jsx_runtime_1.jsxs("div", { children: [jsx_runtime_1.jsx(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }) }, void 0),
jsx_runtime_1.jsx(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }) }, void 0)] }, void 0));
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }) }), (0, jsx_runtime_1.jsx)(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }) })] }));
}
// When we render
await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// Then addRules was only called once

@@ -168,3 +161,3 @@ expect(addRules).toHaveBeenCalledTimes(1);

function TestComponent() {
const { getFormState } = useFormStates_1.useFormStates({
const { getFormState } = (0, useFormStates_1.useFormStates)({
config,

@@ -174,3 +167,3 @@ getId: (o) => o.id,

// And they have a reactive true that calculates last name
mobx_1.reaction(() => fs.firstName.value, (curr) => {
(0, mobx_1.reaction)(() => fs.firstName.value, (curr) => {
fs.lastName.set(curr);

@@ -183,8 +176,8 @@ });

});
return jsx_runtime_1.jsx(FormStateApp_1.TextField, { field: getFormState({ id: "a:1", firstName: "Brandon" }).firstName }, void 0);
return (0, jsx_runtime_1.jsx)(FormStateApp_1.TextField, { field: getFormState({ id: "a:1", firstName: "Brandon" }).firstName });
}
// When we render
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, {}, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, {}));
// And update the firstName
await rtl_utils_1.typeAndWait(r.firstName(), "first");
await (0, rtl_utils_1.typeAndWait)(r.firstName, "first");
// Then autoSave was called once with both input+calc'd values

@@ -197,3 +190,3 @@ expect(autoSave).toHaveBeenCalledTimes(1);

function TestComponent({ readOnly }) {
const { getFormState } = useFormStates_1.useFormStates({
const { getFormState } = (0, useFormStates_1.useFormStates)({
config,

@@ -204,12 +197,12 @@ getId: (o) => o.id,

});
return jsx_runtime_1.jsx(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }) }, void 0);
return (0, jsx_runtime_1.jsx)(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }) });
}
// When we render
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, { readOnly: true }, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { readOnly: true }));
// Then it's read only
expect(r.firstName()).toHaveAttribute("data-readonly", "true");
expect(r.firstName).toHaveAttribute("data-readonly", "true");
// And when we rerender
await r.rerender(jsx_runtime_1.jsx(TestComponent, { readOnly: false }, void 0));
await r.rerender((0, jsx_runtime_1.jsx)(TestComponent, { readOnly: false }));
// Then it's not read only
expect(r.firstName()).toHaveAttribute("data-readonly", "false");
expect(r.firstName).toHaveAttribute("data-readonly", "false");
});

@@ -219,3 +212,3 @@ it("can set readOnly via the getFormState function", async () => {

function TestComponent({ readOnly }) {
const { getFormState } = useFormStates_1.useFormStates({
const { getFormState } = (0, useFormStates_1.useFormStates)({
config,

@@ -225,12 +218,12 @@ getId: (o) => o.id,

// And it passes readOnly directly to getFormState
return jsx_runtime_1.jsx(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }, { readOnly }) }, void 0);
return (0, jsx_runtime_1.jsx)(ChildComponent, { os: getFormState({ id: "a:1", firstName: "Brandon" }, { readOnly }) });
}
// When we render
const r = await rtl_utils_1.render(jsx_runtime_1.jsx(TestComponent, { readOnly: true }, void 0));
const r = await (0, rtl_utils_1.render)((0, jsx_runtime_1.jsx)(TestComponent, { readOnly: true }));
// Then it's read only
expect(r.firstName()).toHaveAttribute("data-readonly", "true");
expect(r.firstName).toHaveAttribute("data-readonly", "true");
// And when we rerender
await r.rerender(jsx_runtime_1.jsx(TestComponent, { readOnly: false }, void 0));
await r.rerender((0, jsx_runtime_1.jsx)(TestComponent, { readOnly: false }));
// Then it's not read only
expect(r.firstName()).toHaveAttribute("data-readonly", "false");
expect(r.firstName).toHaveAttribute("data-readonly", "false");
});

@@ -244,3 +237,3 @@ });

function ChildComponent({ os }) {
return (jsx_runtime_1.jsx("div", Object.assign({ "data-testid": "firstName", "data-readonly": os.firstName.readOnly }, { children: os.firstName.value }), void 0));
return ((0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", "data-readonly": os.firstName.readOnly, children: os.firstName.value }));
}
import { ObjectConfig } from "./config";
import { InputAndMap, QueryAndMap, UseFormStateOpts } from "./useFormState";
export declare type Builtin = Date | Function | Uint8Array | string | number | boolean;
export declare type Primitive = undefined | null | boolean | string | number | Function | Date | {
export type Builtin = Date | Function | Uint8Array | string | number | boolean;
export type Primitive = undefined | null | boolean | string | number | Function | Date | {
toJSON(): any;
};
/** Makes the keys in `T` required while keeping the values undefined. */
export declare type DeepRequired<T> = T extends Primitive ? T : {
export type DeepRequired<T> = T extends Primitive ? T : {
[P in keyof Required<T>]: T[P] extends Array<infer U> ? Array<DeepRequired<U>> : T[P] extends ReadonlyArray<infer U2> ? ReadonlyArray<DeepRequired<U2>> : DeepRequired<T[P]>;

@@ -10,0 +10,0 @@ };

@@ -34,7 +34,7 @@ "use strict";

function isInput(init) {
return !!init && "input" in init && "map" in init;
return !!init && typeof init === "object" && "input" in init && "map" in init;
}
exports.isInput = isInput;
function isQuery(init) {
return !!init && "query" in init && "map" in init;
return !!init && typeof init === "object" && "query" in init && "map" in init;
}

@@ -53,3 +53,3 @@ exports.isQuery = isQuery;

// If the caller is using classes, i.e. with their own custom observable behavior, then just use as-is
if (!is_plain_object_1.isPlainObject(instance)) {
if (!(0, is_plain_object_1.isPlainObject)(instance)) {
return instance;

@@ -69,3 +69,3 @@ }

else if (keyConfig.type === "list") {
if (mobx_1.isObservable(value)) {
if ((0, mobx_1.isObservable)(value)) {
// If we hit an observable array, leave it as the existing proxy so the our

@@ -109,4 +109,4 @@ // ListFieldState will react to changes in the original array.

function areEqual(a, b, strictOrder) {
if (is_plain_object_1.isPlainObject(a)) {
return fast_deep_equal_1.default(mobx_1.toJS(a), mobx_1.toJS(b));
if ((0, is_plain_object_1.isPlainObject)(a)) {
return (0, fast_deep_equal_1.default)((0, mobx_1.toJS)(a), (0, mobx_1.toJS)(b));
}

@@ -116,7 +116,7 @@ if (hasToJSON(a) || hasToJSON(b)) {

const b1 = hasToJSON(b) ? b.toJSON() : b;
return fast_deep_equal_1.default(a1, b1);
return (0, fast_deep_equal_1.default)(a1, b1);
}
if (a && b && a instanceof Array && b instanceof Array) {
if (strictOrder !== false) {
return fast_deep_equal_1.default(a, b);
return (0, fast_deep_equal_1.default)(a, b);
}

@@ -123,0 +123,0 @@ if (a.length !== b.length)

@@ -9,68 +9,68 @@ "use strict";

it("can pick a value field", () => {
const a = utils_1.pickFields(
const a = (0, utils_1.pickFields)(
//
{ firstName: { type: "value" } }, { firstName: "a", b: "ignored" });
expect(a).toMatchInlineSnapshot(`
Object {
"firstName": "a",
}
`);
{
"firstName": "a",
}
`);
});
it("can pick an unset object fields", () => {
const a = utils_1.pickFields(authorWithAddressConfig, { firstName: "a", b: "ignored" });
const a = (0, utils_1.pickFields)(authorWithAddressConfig, { firstName: "a", b: "ignored" });
expect(a).toMatchInlineSnapshot(`
Object {
"address": undefined,
"firstName": "a",
"id": undefined,
"lastName": undefined,
}
`);
{
"address": undefined,
"firstName": "a",
"id": undefined,
"lastName": undefined,
}
`);
});
it("can pick a set object field", () => {
const a = utils_1.pickFields(authorWithAddressConfig, { firstName: "a", b: "ignored", address: {} });
const a = (0, utils_1.pickFields)(authorWithAddressConfig, { firstName: "a", b: "ignored", address: {} });
expect(a).toMatchInlineSnapshot(`
Object {
"address": Object {
"city": undefined,
"street": undefined,
},
"firstName": "a",
"id": undefined,
"lastName": undefined,
}
`);
{
"address": {
"city": undefined,
"street": undefined,
},
"firstName": "a",
"id": undefined,
"lastName": undefined,
}
`);
});
it("can pick an unset list field", () => {
const a = utils_1.pickFields(authorWithBooksConfig, { firstName: "a", b: "ignored" });
const a = (0, utils_1.pickFields)(authorWithBooksConfig, { firstName: "a", b: "ignored" });
expect(a).toMatchInlineSnapshot(`
Object {
"books": undefined,
"firstName": "a",
"id": undefined,
"lastName": undefined,
}
`);
{
"books": undefined,
"firstName": "a",
"id": undefined,
"lastName": undefined,
}
`);
});
it("can pick a set list field", () => {
const a = utils_1.pickFields(authorWithBooksConfig, { firstName: "a", b: "ignored", books: [{}] });
const a = (0, utils_1.pickFields)(authorWithBooksConfig, { firstName: "a", b: "ignored", books: [{}] });
expect(a).toMatchInlineSnapshot(`
Object {
"books": Array [
Object {
"classification": undefined,
"id": undefined,
"title": undefined,
},
],
"firstName": "a",
"id": undefined,
"lastName": undefined,
}
`);
{
"books": [
{
"classification": undefined,
"id": undefined,
"title": undefined,
},
],
"firstName": "a",
"id": undefined,
"lastName": undefined,
}
`);
});
it("can pick a set observable list field", () => {
const books = mobx_1.observable([]);
const a = utils_1.pickFields(authorWithBooksConfig, { firstName: "a", b: "ignored", books });
expect(mobx_1.isObservable(a.books)).toEqual(true);
const books = (0, mobx_1.observable)([]);
const a = (0, utils_1.pickFields)(authorWithBooksConfig, { firstName: "a", b: "ignored", books });
expect((0, mobx_1.isObservable)(a.books)).toEqual(true);
});

@@ -77,0 +77,0 @@ });

{
"name": "@homebound/form-state",
"version": "2.19.0",
"version": "2.20.0",
"author": "Homebound",

@@ -16,7 +16,7 @@ "license": "MIT",

"scripts": {
"build": "ttsc",
"build:storybook": "build-storybook",
"build": "tspc",
"build:storybook": "storybook build",
"test": "jest",
"lint": "eslint . --cache --fix --ext .js,.ts,.tsx",
"storybook": "start-storybook -p 9000",
"storybook": "storybook dev -p 9000",
"format": "prettier --loglevel warn --write \"**/*.{ts,tsx,css,md}\"",

@@ -36,4 +36,4 @@ "semantic-release": "semantic-release"

"is-plain-object": "^5.0.0",
"mobx": "^6.3.2",
"mobx-react": "^7.2.0"
"mobx": "^6.10.2",
"mobx-react": "^9.0.1"
},

@@ -45,18 +45,20 @@ "peerDependencies": {

"@babel/core": "^7.13.1",
"@homebound/rtl-utils": "^2.59.2",
"@semantic-release/exec": "^5.0.0",
"@semantic-release/git": "^9.0.0",
"@storybook/addon-essentials": "^6.2.9",
"@storybook/addon-info": "^5.3.21",
"@storybook/addon-links": "^6.2.9",
"@storybook/addons": "^6.2.9",
"@storybook/react": "^6.2.9",
"@testing-library/dom": "^8.11.3",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.2",
"@testing-library/react-hooks": "^8.0.0",
"@tsconfig/recommended": "^1.0.1",
"@types/jest": "^26.0.20",
"@types/react": "^16.14.5",
"@types/react-dom": "^16.9.11",
"@babel/preset-env": "^7.22.15",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.22.15",
"@homebound/rtl-utils": "2.65.0",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@storybook/addon-essentials": "^7.4.0",
"@storybook/addon-links": "^7.4.0",
"@storybook/addons": "^7.4.0",
"@storybook/react": "^7.4.0",
"@storybook/react-webpack5": "^7.4.0",
"@testing-library/dom": "^9.3.1",
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^14.0.0",
"@tsconfig/recommended": "^1.0.2",
"@types/jest": "^29.5.4",
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^4.17.0",

@@ -75,19 +77,22 @@ "@typescript-eslint/parser": "^4.17.0",

"eslint-plugin-react-hooks": "^4.2.0",
"husky": "^3.0.9",
"jest": "^27.0.2",
"lint-staged": "^10.1.2",
"mobx": "^6.3.2",
"mobx-react": "^7.2.0",
"prettier": "^2.2.1",
"prettier-plugin-organize-imports": "^1.1.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"semantic-release": "^17.4.0",
"ts-jest": "^27.0.1",
"ts-node": "^9.1.1",
"tslib": "^2.1.0",
"ttypescript": "^1.5.12",
"typescript": "^4.3.2",
"typescript-transform-paths": "^2.0.1"
}
"husky": "^3.1.0",
"jest": "^29.6.4",
"jest-environment-jsdom": "^29.6.4",
"lint-staged": "^14.0.1",
"mobx": "^6.10.2",
"mobx-react": "^9.0.1",
"prettier": "2.8.8",
"prettier-plugin-organize-imports": "^3.2.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"semantic-release": "^21.1.1",
"storybook": "^7.4.0",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"ts-patch": "^3.0.2",
"tslib": "^2.6.2",
"typescript": "^5.2.2",
"typescript-transform-paths": "^3.4.6"
},
"packageManager": "yarn@3.6.3"
}

Sorry, the diff of this file is too big to display

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