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

@homebound/form-state

Package Overview
Dependencies
Maintainers
36
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.23.2 to 2.24.0

12

dist/FormStateApp.js

@@ -12,7 +12,9 @@ "use strict";

init: {
firstName: "a1",
books: [...Array(2)].map((_, i) => ({
title: `b${i}`,
classification: { number: `10${i + 1}`, category: `Test Category ${i}` },
})),
input: {
firstName: "a1",
books: [...Array(2)].map((_, i) => ({
title: `b${i}`,
classification: { number: `10${i + 1}`, category: `Test Category ${i}` },
})),
},
},

@@ -19,0 +21,0 @@ addRules(state) {

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

input: I;
map: (input: Exclude<I, null | undefined>) => T;
map?: (input: Exclude<I, null | undefined>) => T;
ifUndefined?: T;

@@ -19,2 +19,8 @@ };

};
/**
* The opts has for `useFormState`.
*
* @typeparam T the form type, which is usually as close as possible to your *GraphQL input*
* @typeparam I the *form input* type, which is usually the *GraphQL output* type, i.e. the type of the response from your GraphQL query
*/
export type UseFormStateOpts<T, I> = {

@@ -40,3 +46,3 @@ /** The form configuration, should be a module-level const or useMemo'd. */

*/
init?: T | InputAndMap<T, I> | QueryAndMap<T, I>;
init?: InputAndMap<T, I> | QueryAndMap<T, I>;
/**

@@ -43,0 +49,0 @@ * A hook to add custom, cross-field validation rules that can be difficult to setup directly in the config DSL.

@@ -86,24 +86,2 @@ "use strict";

});
it("uses init if set as a value", async () => {
const config = { firstName: { type: "value" } };
function TestComponent() {
const [, setTick] = (0, react_2.useState)(0);
const form = (0, useFormState_1.useFormState)({
config,
// That's using a raw init value
init: { firstName: "bob" },
});
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("button", { "data-testid": "change", onClick: () => {
// When that value changes
form.firstName.set("fred");
// And also we re-render the component
setTick(1);
} }), (0, jsx_runtime_1.jsx)("div", { "data-testid": "firstName", children: form.firstName.value })] }));
}
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");
});
it("doesn't required an init value", async () => {

@@ -158,3 +136,3 @@ function TestComponent() {

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

@@ -227,3 +205,3 @@ form.firstName.value = "local";

config,
init: { input: data, map: (d) => d },
init: { input: data },
// And the form is read only

@@ -324,3 +302,3 @@ readOnly: true,

config: authorWithBooksConfig,
init: { input: data, map: (d) => d, ifUndefined: { books: [] } },
init: { input: data, ifUndefined: { books: [] } },
autoSave,

@@ -356,3 +334,3 @@ });

config,
init: data,
init: { input: data },
// And there is reactive business logic in the `autoSave` method

@@ -380,3 +358,3 @@ async autoSave(state) {

config,
init: data,
init: { input: data },
autoSave: (form) => autoSave(form.changedValue),

@@ -462,3 +440,3 @@ });

autoSave: (fs) => autoSaveStub(fs.changedValue),
init: { id: "a:1" },
init: { input: { id: "a:1" } },
});

@@ -492,3 +470,3 @@ return (0, jsx_runtime_1.jsx)(FormStateApp_1.TextField, { field: fs.firstName });

function TestComponent({ data }) {
const form = (0, useFormState_1.useFormState)({ config, init: { input: data, map: (d) => d } });
const form = (0, useFormState_1.useFormState)({ config, init: { input: data } });
return (0, jsx_runtime_1.jsx)(mobx_react_1.Observer, { children: () => (0, jsx_runtime_1.jsx)("div", { "data-testid": "loading", children: String(form.loading) }) });

@@ -557,3 +535,3 @@ }

config,
init: { firstName: "f1", lastName: "f1" },
init: { input: { firstName: "f1", lastName: "f1" } },
autoSave: async (form) => {

@@ -560,0 +538,0 @@ autoSave(form.changedValue);

import { ObjectConfig } from "./config";
import { ObjectState } from "./fields/objectField";
export type ObjectStateCache<T, I> = Record<string, [ObjectState<T>, I]>;
/**
* The opts has for `useFormStates`.
*
* @typeparam T the form type, which is usually as close as possible to your *GraphQL input*
* @typeparam I the *form input* type, which is usually the *GraphQL output* type, i.e. the type of the response from your GraphQL query
*/
type UseFormStatesOpts<T, I> = {

@@ -48,3 +54,16 @@ /**

};
/**
* A hook to manage many "mini-forms" on a single page, typically one form per row
* in a table.
*
* This hook basically provides the page/table with a cache, so each table row naively ask "what's
* the form state for this given row's data?" and get back a new-or-existing `ObjectState` instance
* that, if already existing, still has any of the user's WIP changes.
*
* Each mini-form/row can have its own autoSave calls, independent of the other rows.
*
* @typeparam T the form type, which is usually as close as possible to your *GraphQL input*
* @typeparam I the *form input* type, which is usually the *GraphQL output* type, i.e. the type of the response from your GraphQL query
*/
export declare function useFormStates<T, I = T>(opts: UseFormStatesOpts<T, I>): UseFormStatesHook<T, I>;
export {};

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

const utils_1 = require("./utils");
/**
* A hook to manage many "mini-forms" on a single page, typically one form per row
* in a table.
*
* This hook basically provides the page/table with a cache, so each table row naively ask "what's
* the form state for this given row's data?" and get back a new-or-existing `ObjectState` instance
* that, if already existing, still has any of the user's WIP changes.
*
* Each mini-form/row can have its own autoSave calls, independent of the other rows.
*
* @typeparam T the form type, which is usually as close as possible to your *GraphQL input*
* @typeparam I the *form input* type, which is usually the *GraphQL output* type, i.e. the type of the response from your GraphQL query
*/
function useFormStates(opts) {

@@ -58,3 +71,3 @@ const { config, autoSave, getId, map, addRules, readOnly = false } = opts;

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

@@ -69,3 +82,3 @@ });

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

@@ -72,0 +85,0 @@ });

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

export declare function assertNever(x: never): never;
/** Introspects the `init` prop to see if has a `map` function/etc. and returns the form value. */
/** Introspects the `init` prop to see if it has a `map` function/etc. and returns the form value. */
export declare function initValue<T>(config: ObjectConfig<T>, init: any): T;

@@ -19,0 +19,0 @@ export declare function isInput<T, I>(init: UseFormStateOpts<T, I>["init"]): init is InputAndMap<T, I>;

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

exports.assertNever = assertNever;
/** Introspects the `init` prop to see if has a `map` function/etc. and returns the form value. */
/** Introspects the `init` prop to see if it has a `map` function/etc. and returns the form value. */
function initValue(config, init) {
let value;
if (isInput(init)) {
value = init.input ? init.map(init.input) : init.ifUndefined;
value = init.input ? (init.map ? init.map(init.input) : init.input) : init.ifUndefined;
}

@@ -28,5 +28,9 @@ else if (isQuery(init)) {

}
else if (init === undefined) {
// allow completely undefined init
}
else {
value = init;
throw new Error("init must have an input or query key");
}
// Given our form config, pick out only the subset of fields out of `value` (unless it's a mobx class)
return pickFields(config, value !== null && value !== void 0 ? value : {});

@@ -36,3 +40,3 @@ }

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

@@ -39,0 +43,0 @@ exports.isInput = isInput;

{
"name": "@homebound/form-state",
"version": "2.23.2",
"version": "2.24.0",
"author": "Homebound",

@@ -5,0 +5,0 @@ "license": "MIT",

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