Skip to content

Commit

Permalink
Prevent usage of optional properties (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
zoontek authored Feb 23, 2024
1 parent b7b4248 commit c098afc
Show file tree
Hide file tree
Showing 6 changed files with 16 additions and 18 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ Submit your form. Each callback could return a `Promise` to keep `formStatus` in

```tsx
type submitForm = (options?: {
onSuccess?: (values: OptionalRecord<Values>) => Future<unknown> | Promise<unknown> | void;
onSuccess?: (values: OptionRecord<Values>) => Future<unknown> | Promise<unknown> | void;
onFailure?: (errors: Partial<Record<keyof Values, ErrorMessage>>) => void;
// by default, it will try to focus the first errored field (which is a good practice)
focusOnFirstError?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@swan-io/use-form",
"version": "2.0.0-rc.0",
"version": "2.0.0-rc.1",
"license": "MIT",
"description": "A simple, fast and opinionated form library for React & React Native focusing on UX.",
"author": "Mathieu Acthernoene <mathieu.acthernoene@swan.io>",
Expand Down
13 changes: 5 additions & 8 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { Future, Option } from "@swan-io/boxed";
import { MutableRefObject, ReactElement } from "react";

export type AnyRecord = Record<string, unknown>;
export type EmptyRecord = Record<PropertyKey, never>;

export type OptionalRecord<T extends AnyRecord> = {
[K in keyof T]: Option<T[K]>;
export type OptionRecord<T> = {
[K in keyof T]-?: Option<T[K]>;
};

export type ValidatorResult<ErrorMessage = string> = ErrorMessage | void;
Expand All @@ -30,7 +27,7 @@ export type FieldState<Value, ErrorMessage = string> = {
error: ErrorMessage | undefined;
};

export type FormConfig<Values extends AnyRecord, ErrorMessage = string> = {
export type FormConfig<Values extends Required<Values>, ErrorMessage = string> = {
[N in keyof Values]: {
initialValue: Values[N];
strategy?: Strategy;
Expand All @@ -49,7 +46,7 @@ export type FormConfig<Values extends AnyRecord, ErrorMessage = string> = {
};
};

export type Form<Values extends AnyRecord, ErrorMessage = string> = {
export type Form<Values extends Required<Values>, ErrorMessage = string> = {
formStatus: FormStatus;

Field: (<N extends keyof Values>(props: {
Expand Down Expand Up @@ -97,7 +94,7 @@ export type Form<Values extends AnyRecord, ErrorMessage = string> = {

resetForm: () => void;
submitForm: (options?: {
onSuccess?: (values: OptionalRecord<Values>) => Future<unknown> | Promise<unknown> | void;
onSuccess?: (values: OptionRecord<Values>) => Future<unknown> | Promise<unknown> | void;
onFailure?: (errors: Partial<Record<keyof Values, ErrorMessage>>) => void;
focusOnFirstError?: boolean;
}) => void;
Expand Down
13 changes: 6 additions & 7 deletions src/useForm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Future, Option } from "@swan-io/boxed";
import { Dict, Future, Option } from "@swan-io/boxed";
import {
MutableRefObject,
SetStateAction,
Expand All @@ -11,12 +11,11 @@ import {
} from "react";
import { identity, isPromise, noop } from "./helpers";
import {
AnyRecord,
FieldState,
Form,
FormConfig,
FormStatus,
OptionalRecord,
OptionRecord,
Strategy,
ValidatorResult,
Validity,
Expand All @@ -25,7 +24,7 @@ import {
// For server-side rendering / react-native
const useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;

export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
export const useForm = <Values extends Required<Values>, ErrorMessage = string>(
config: FormConfig<Values, ErrorMessage>,
): Form<Values, ErrorMessage> => {
type Contract = Form<Values, ErrorMessage>;
Expand Down Expand Up @@ -268,7 +267,7 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
};

const resetForm: Contract["resetForm"] = () => {
Object.keys(arg.current).forEach((name) => resetField(name));
Dict.keys(arg.current).forEach((name) => resetField(name));
formStatus.current = "untouched";

forceUpdate();
Expand Down Expand Up @@ -306,9 +305,9 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(

formStatus.current = "submitting";

const keys: Name[] = Object.keys(fields.current);
const keys: Name[] = Dict.keys(fields.current);
const names = keys.filter((name) => fields.current[name].mounted);
const values = {} as OptionalRecord<Values>;
const values = {} as OptionRecord<Values>;
const errors: Partial<Record<Name, ErrorMessage>> = {};
const results: ValidatorResult<ErrorMessage>[] = [];

Expand Down
1 change: 1 addition & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@chakra-ui/system": "^2.6.2",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@swan-io/boxed": "^2.0.0",
"@swan-io/chicane": "^1.4.1",
"@swan-io/use-form": "link:../",
"card-validator": "^9.1.0",
Expand Down
3 changes: 2 additions & 1 deletion website/src/utils/router.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Dict } from "@swan-io/boxed";
import { createRouter } from "@swan-io/chicane";

const routesObject = {
Expand All @@ -11,7 +12,7 @@ const routesObject = {
InputMasking: "/input-masking",
} as const;

export const routes = Object.keys(routesObject) as (keyof typeof routesObject)[];
export const routes = Dict.keys(routesObject);

export const Router = createRouter(routesObject, {
basePath: "/use-form",
Expand Down

0 comments on commit c098afc

Please sign in to comment.