mayoiga 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
+ import React from 'react';
1
2
  import type { SliceProps, FieldProps, StateRestriction, Controller } from './index';
2
- declare type ControlledComponentProps<State, Props> = Props & {
3
+ type ControlledComponentProps<State extends StateRestriction, Props> = Props & {
3
4
  controller: Controller<State>;
4
5
  };
5
- export declare function Slicer<State extends StateRestriction, R extends ReadonlyArray<unknown>>(props: ControlledComponentProps<State, SliceProps<State, R>>): JSX.Element;
6
- export declare function Field<State extends StateRestriction, Name extends keyof State>(props: ControlledComponentProps<State, FieldProps<State, Name>>): JSX.Element;
6
+ export declare function Slicer<State extends StateRestriction, R extends ReadonlyArray<unknown>>(props: ControlledComponentProps<State, SliceProps<State, R>>): React.JSX.Element;
7
+ export declare function Field<State extends StateRestriction, Name extends keyof State>(props: ControlledComponentProps<State, FieldProps<State, Name>>): React.JSX.Element;
7
8
  export {};
package/dist/index.d.ts CHANGED
@@ -1,20 +1,20 @@
1
1
  import type { Subscriber } from 'nozuchi';
2
2
  import type { BaseSyntheticEvent, ChangeEvent, ReactNode, ReactElement } from 'react';
3
3
  import type { infer as zodInfer, ZodIssue, ZodType } from 'zod';
4
- export declare type StateRestriction = Record<string, any>;
5
- declare type FormStatus = {
4
+ export type StateRestriction = Record<string, any>;
5
+ type FormStatus = {
6
6
  isDirty: boolean;
7
7
  isValid: boolean;
8
8
  };
9
- declare type FormErrors<State extends StateRestriction> = {
9
+ type FormErrors<State extends StateRestriction> = {
10
10
  [k in keyof State]: string | null;
11
11
  };
12
- export declare type FullFormState<State extends StateRestriction> = FormStatus & {
12
+ export type FullFormState<State extends StateRestriction> = FormStatus & {
13
13
  value: State;
14
14
  errors: FormErrors<State>;
15
15
  };
16
- export declare type HandleChangeAction<State extends StateRestriction, R = void> = <Name extends keyof State>(name: Name, value: State[Name]) => R;
17
- export declare type FieldProps<State extends StateRestriction, Name extends keyof State> = {
16
+ export type HandleChangeAction<State extends StateRestriction, R = void> = <Name extends keyof State>(name: Name, value: State[Name]) => R;
17
+ export type FieldProps<State extends StateRestriction, Name extends keyof State> = {
18
18
  name: Name;
19
19
  children: (tool: {
20
20
  name: Name;
@@ -28,7 +28,7 @@ export declare type FieldProps<State extends StateRestriction, Name extends keyo
28
28
  */
29
29
  deps?: ReadonlyArray<unknown>;
30
30
  };
31
- export declare type SliceProps<State extends StateRestriction, Selected extends ReadonlyArray<unknown>> = {
31
+ export type SliceProps<State extends StateRestriction, Selected extends ReadonlyArray<unknown>> = {
32
32
  selector: (s: FullFormState<State>) => Selected;
33
33
  children: (tool: {
34
34
  handleChange: HandleChangeAction<State>;
@@ -43,14 +43,19 @@ export declare type SliceProps<State extends StateRestriction, Selected extends
43
43
  */
44
44
  deps?: ReadonlyArray<unknown>;
45
45
  };
46
- declare type FormControllerBehavior<StateBeforeValidation> = {
46
+ type FormControllerBehavior<StateBeforeValidation extends StateRestriction> = {
47
47
  /** For testing */
48
48
  peek: (eyeball: (prev: FullFormState<StateBeforeValidation>) => void) => (prev: FullFormState<StateBeforeValidation>) => FullFormState<StateBeforeValidation>;
49
49
  reset: () => () => FullFormState<StateBeforeValidation>;
50
50
  /**
51
51
  * Initialize form state without validation.
52
+ *
53
+ * @param initialVal value to initialize form state
54
+ * @param opts.cleanup if true, form state is become pristine (which means, `form.isDirty` is going to be false) .
52
55
  */
53
- initializeForm: (initialVal: Partial<StateBeforeValidation>) => (prev: FullFormState<StateBeforeValidation>) => FullFormState<StateBeforeValidation>;
56
+ initializeForm: (initialVal: Partial<StateBeforeValidation>, opts?: {
57
+ cleanup?: true;
58
+ }) => (prev: FullFormState<StateBeforeValidation>) => FullFormState<StateBeforeValidation>;
54
59
  /**
55
60
  * Handle issue that zod reported
56
61
  */
@@ -66,9 +71,17 @@ declare type FormControllerBehavior<StateBeforeValidation> = {
66
71
  reduceFormState: (reducer: (prevValue: StateBeforeValidation) => StateBeforeValidation) => (prev: FullFormState<StateBeforeValidation>) => FullFormState<StateBeforeValidation>;
67
72
  handleBulkChange: (setter: (prev: StateBeforeValidation) => Partial<StateBeforeValidation>) => (prev: FullFormState<StateBeforeValidation>) => FullFormState<StateBeforeValidation>;
68
73
  };
69
- declare type ActionsCanBePublic<State extends StateRestriction> = {
74
+ type ActionsCanBePublic<State extends StateRestriction> = {
70
75
  reset: VoidFunction;
71
- initializeForm: (initial: Partial<State>) => void;
76
+ /**
77
+ * Initialize form state without validation.
78
+ *
79
+ * @param initialVal value to initialize form state
80
+ * @param opts.cleanup if true, form state is become pristine (which means, `form.isDirty` is going to be false) .
81
+ */
82
+ initializeForm: (initial: Partial<State>, opts?: {
83
+ cleanup?: true;
84
+ }) => void;
72
85
  pushFormErrors: (validator: (state: State) => Partial<FormErrors<State>>) => void;
73
86
  handleChange: HandleChangeAction<State>;
74
87
  handleBulkChange: (setter: (prev: State) => Partial<State>) => void;
@@ -77,7 +90,7 @@ declare type ActionsCanBePublic<State extends StateRestriction> = {
77
90
  * Restricted type interface of the form store
78
91
  * TODO: more restriction
79
92
  */
80
- export declare type Controller<State extends StateRestriction> = {
93
+ export type Controller<State extends StateRestriction> = {
81
94
  useInitialize: (initialValue: Partial<State>) => void;
82
95
  useSelector: <R>(selector: (state: FullFormState<State>) => R, isEqual?: (prev: R, current: R) => boolean) => R;
83
96
  actions: ActionsCanBePublic<State>;
@@ -86,7 +99,7 @@ export declare type Controller<State extends StateRestriction> = {
86
99
  Slicer: <Selected extends ReadonlyArray<unknown>>(props: SliceProps<State, Selected>) => ReactElement<any, any> | null;
87
100
  };
88
101
  };
89
- declare type FormHook<State extends StateRestriction, Schema extends ZodType<any, any, any>> = {
102
+ type FormHook<State extends StateRestriction, Schema extends ZodType<any, any, any>> = {
90
103
  controller: Controller<State>;
91
104
  handleSubmit: (handler: (e: BaseSyntheticEvent) => (val: {
92
105
  success: true;
@@ -101,6 +114,6 @@ declare type FormHook<State extends StateRestriction, Schema extends ZodType<any
101
114
  /**
102
115
  * @typeParam InitialState should be shallow
103
116
  */
104
- export declare function createFormHook<StateBeforeValidation, Schema extends ZodType<any, any, any>>(initialState: StateBeforeValidation, schema: Schema): FormHook<StateBeforeValidation, Schema>;
117
+ export declare function createFormHook<StateBeforeValidation extends StateRestriction, Schema extends ZodType<any, any, any>>(initialState: StateBeforeValidation, schema: Schema): FormHook<StateBeforeValidation, Schema>;
105
118
  export declare function useFormSlice<State extends StateRestriction, Sliced>(formController: Controller<State>, selector: (s: FullFormState<State>) => Sliced): readonly [Sliced, ActionsCanBePublic<State>];
106
119
  export {};
package/dist/index.js CHANGED
@@ -34,10 +34,11 @@ function createFormStore(initialState, schema) {
34
34
  return prev;
35
35
  },
36
36
  reset: () => () => fullInitialState,
37
- initializeForm: (initialVal) => (prev) => ({
37
+ initializeForm: (initialVal, opts) => (prev) => ({
38
38
  ...prev,
39
39
  value: { ...prev.value, ...initialVal },
40
40
  errors: { ...initialErrors },
41
+ isDirty: opts?.cleanup === true ? false : prev.isDirty,
41
42
  }),
42
43
  handleIssues: (issues) => (prev) => {
43
44
  // FIXME: handle duplication
@@ -13,7 +13,8 @@ const testSchema = zod.object({
13
13
  .refine((val) => (Number.isNaN(val) ? 0 : val)),
14
14
  marked: zod.boolean(),
15
15
  });
16
- const createTestHook = () => createFormHook({ name: '', description: '', age: '', marked: false }, testSchema);
16
+ const initialState = { name: '', description: '', age: '', marked: false };
17
+ const createTestHook = () => createFormHook(initialState, testSchema);
17
18
  const AgeInputComponent = (props) => {
18
19
  const { controller } = props;
19
20
  const [[age, errorMessage], { handleChange }] = useFormSlice(controller, (s) => [s.value.age, s.errors.age]);
@@ -75,3 +76,16 @@ test('zod parse value before submit', async () => {
75
76
  });
76
77
  });
77
78
  });
79
+ test('User is able to re-initialize the form state', async () => {
80
+ const TestFormHook = createTestHook();
81
+ const Top = () => {
82
+ return (React.createElement("div", null,
83
+ React.createElement(AgeInputComponent, { controller: TestFormHook.controller })));
84
+ };
85
+ render(React.createElement(Top, null));
86
+ const ageInput = screen.getByTestId('age-input');
87
+ await userEvent.type(ageInput, '9999');
88
+ expect(TestFormHook.api.getState().isDirty).toBeTruthy();
89
+ TestFormHook.actions.initializeForm({}, { cleanup: true });
90
+ expect(TestFormHook.api.getState().isDirty).toBeFalsy();
91
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mayoiga",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Type safe hook based form library for React",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -40,30 +40,30 @@
40
40
  },
41
41
  "homepage": "https://github.com/hachibeeDI/mayoiga#readme",
42
42
  "peerDependencies": {
43
- "zod": "^3.14.4"
43
+ "zod": "^3.20.2"
44
44
  },
45
45
  "dependencies": {
46
- "nozuchi": "^0.0.3"
46
+ "nozuchi": "^0.0.4"
47
47
  },
48
48
  "devDependencies": {
49
- "@testing-library/react": "^13.0.0",
50
- "@testing-library/user-event": "^14.1.1",
49
+ "@testing-library/react": "^14.0.0",
50
+ "@testing-library/user-event": "^14.4.3",
51
51
  "@types/node": "^17.0.23",
52
- "@types/react": "^18.0.1",
52
+ "@types/react": "^18.2.7",
53
53
  "@types/use-sync-external-store": "^0.0.3",
54
- "@typescript-eslint/eslint-plugin": "^5.20.0",
55
- "@typescript-eslint/parser": "^5.20.0",
56
- "eslint": "^8.13.0",
57
- "eslint-plugin-import": "^2.26.0",
58
- "eslint-plugin-testing-library": "^5.3.1",
59
- "jsdom": "^19.0.0",
60
- "prettier": "^2.6.2",
61
- "react": "^18.0.0",
62
- "react-dom": "^18.0.0",
63
- "typedoc": "^0.22.15",
64
- "typescript": "^4.6.3",
65
- "use-sync-external-store": "^1.0.0",
66
- "vitest": "^0.9.3",
67
- "zod": "^3.14.4"
54
+ "@typescript-eslint/eslint-plugin": "^5.59.8",
55
+ "@typescript-eslint/parser": "^5.59.8",
56
+ "eslint": "^8.41.0",
57
+ "eslint-plugin-import": "^2.27.5",
58
+ "eslint-plugin-testing-library": "^5.11.0",
59
+ "jsdom": "^22.1.0",
60
+ "prettier": "^2.8.8",
61
+ "react": "^18.2.0",
62
+ "react-dom": "^18.2.0",
63
+ "typedoc": "^0.24.7",
64
+ "typescript": "^5.0.4",
65
+ "use-sync-external-store": "^1.2.0",
66
+ "vitest": "^0.31.1",
67
+ "zod": "^3.20.2"
68
68
  }
69
69
  }
package/src/component.tsx CHANGED
@@ -2,7 +2,7 @@ import React from 'react';
2
2
 
3
3
  import type {SliceProps, FieldProps, StateRestriction, Controller} from './index';
4
4
 
5
- type ControlledComponentProps<State, Props> = Props & {
5
+ type ControlledComponentProps<State extends StateRestriction, Props> = Props & {
6
6
  controller: Controller<State>;
7
7
  };
8
8
 
@@ -28,7 +28,8 @@ type FormStateBeforeValidation = {
28
28
  marked: boolean;
29
29
  };
30
30
 
31
- const createTestHook = () => createFormHook({name: '', description: '', age: '', marked: false} as FormStateBeforeValidation, testSchema);
31
+ const initialState = {name: '', description: '', age: '', marked: false};
32
+ const createTestHook = () => createFormHook(initialState as FormStateBeforeValidation, testSchema);
32
33
 
33
34
  const AgeInputComponent = (props: {controller: Controller<FormStateBeforeValidation>}) => {
34
35
  const {controller} = props;
@@ -120,3 +121,24 @@ test('zod parse value before submit', async () => {
120
121
  });
121
122
  });
122
123
  });
124
+
125
+ test('User is able to re-initialize the form state', async () => {
126
+ const TestFormHook = createTestHook();
127
+
128
+ const Top = () => {
129
+ return (
130
+ <div>
131
+ <AgeInputComponent controller={TestFormHook.controller} />
132
+ </div>
133
+ );
134
+ };
135
+
136
+ render(<Top />);
137
+
138
+ const ageInput: HTMLInputElement = screen.getByTestId('age-input');
139
+ await userEvent.type(ageInput, '9999');
140
+
141
+ expect(TestFormHook.api.getState().isDirty).toBeTruthy();
142
+ TestFormHook.actions.initializeForm({}, {cleanup: true});
143
+ expect(TestFormHook.api.getState().isDirty).toBeFalsy();
144
+ });
package/src/index.ts CHANGED
@@ -57,7 +57,7 @@ export type SliceProps<State extends StateRestriction, Selected extends Readonly
57
57
  deps?: ReadonlyArray<unknown>;
58
58
  };
59
59
 
60
- type FormControllerBehavior<StateBeforeValidation> = {
60
+ type FormControllerBehavior<StateBeforeValidation extends StateRestriction> = {
61
61
  /** For testing */
62
62
  peek: (
63
63
  eyeball: (prev: FullFormState<StateBeforeValidation>) => void,
@@ -67,9 +67,15 @@ type FormControllerBehavior<StateBeforeValidation> = {
67
67
 
68
68
  /**
69
69
  * Initialize form state without validation.
70
+ *
71
+ * @param initialVal value to initialize form state
72
+ * @param opts.cleanup if true, form state is become pristine (which means, `form.isDirty` is going to be false) .
70
73
  */
71
74
  initializeForm: (
72
75
  initialVal: Partial<StateBeforeValidation>,
76
+ opts?: {
77
+ cleanup?: true;
78
+ },
73
79
  ) => (prev: FullFormState<StateBeforeValidation>) => FullFormState<StateBeforeValidation>;
74
80
 
75
81
  /**
@@ -151,10 +157,11 @@ function createFormStore<StateBeforeValidation extends StateRestriction, Schema
151
157
  },
152
158
 
153
159
  reset: () => () => fullInitialState,
154
- initializeForm: (initialVal) => (prev) => ({
160
+ initializeForm: (initialVal, opts) => (prev) => ({
155
161
  ...prev,
156
162
  value: {...prev.value, ...initialVal},
157
163
  errors: {...initialErrors},
164
+ isDirty: opts?.cleanup === true ? false : prev.isDirty,
158
165
  }),
159
166
  handleIssues: (issues) => (prev) => {
160
167
  // FIXME: handle duplication
@@ -196,7 +203,19 @@ function createFormStore<StateBeforeValidation extends StateRestriction, Schema
196
203
 
197
204
  type ActionsCanBePublic<State extends StateRestriction> = {
198
205
  reset: VoidFunction;
199
- initializeForm: (initial: Partial<State>) => void;
206
+
207
+ /**
208
+ * Initialize form state without validation.
209
+ *
210
+ * @param initialVal value to initialize form state
211
+ * @param opts.cleanup if true, form state is become pristine (which means, `form.isDirty` is going to be false) .
212
+ */
213
+ initializeForm: (
214
+ initial: Partial<State>,
215
+ opts?: {
216
+ cleanup?: true;
217
+ },
218
+ ) => void;
200
219
  pushFormErrors: (validator: (state: State) => Partial<FormErrors<State>>) => void;
201
220
  handleChange: HandleChangeAction<State>;
202
221
  handleBulkChange: (setter: (prev: State) => Partial<State>) => void;
@@ -234,7 +253,7 @@ type FormHook<State extends StateRestriction, Schema extends ZodType<any, any, a
234
253
  /**
235
254
  * @typeParam InitialState should be shallow
236
255
  */
237
- export function createFormHook<StateBeforeValidation, Schema extends ZodType<any, any, any>>(
256
+ export function createFormHook<StateBeforeValidation extends StateRestriction, Schema extends ZodType<any, any, any>>(
238
257
  initialState: StateBeforeValidation,
239
258
  schema: Schema,
240
259
  ): FormHook<StateBeforeValidation, Schema> {