Skip to content

Commit 6cdad9f

Browse files
authored
Merge pull request #759 from rvsia/afterBeforeSubmit
feat(manager): add after/before submit callbacks
2 parents 641b5ed + 9db8a6b commit 6cdad9f

File tree

6 files changed

+119
-6
lines changed

6 files changed

+119
-6
lines changed

packages/form-state-manager/src/files/use-field.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ const useField = ({
6363
format,
6464
parse = defaultParse,
6565
formatOnBlur,
66+
beforeSubmit,
67+
afterSubmit,
6668
...props
6769
}: UseField): UseFieldData => {
6870
const { registerField, unregisterField, change, getFieldValue, blur, focus, formOptions, ...rest } = useContext(FormManagerContext);
@@ -78,7 +80,9 @@ const useField = ({
7880
validate,
7981
subscription,
8082
internalId,
81-
defaultValue
83+
defaultValue,
84+
beforeSubmit,
85+
afterSubmit
8286
});
8387

8488
return internalId;

packages/form-state-manager/src/tests/utils/manager-api.test.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,4 +1473,82 @@ describe('managerApi', () => {
14731473
});
14741474
});
14751475
});
1476+
1477+
describe('after & before submit', () => {
1478+
it('calls after submit', () => {
1479+
const onSubmit = jest.fn();
1480+
1481+
const managerApi = createManagerApi({ onSubmit });
1482+
1483+
const afterSubmit1 = jest.fn();
1484+
const afterSubmit2 = jest.fn();
1485+
const afterSubmit3 = jest.fn();
1486+
1487+
managerApi().registerField({ name: 'field', internalId: '1', render: jest.fn(), afterSubmit: afterSubmit1 });
1488+
managerApi().registerField({ name: 'field', internalId: '2', render: jest.fn(), afterSubmit: afterSubmit2 });
1489+
managerApi().registerField({ name: 'field2', render: jest.fn(), afterSubmit: afterSubmit3 });
1490+
1491+
expect(afterSubmit1).not.toHaveBeenCalled();
1492+
expect(afterSubmit2).not.toHaveBeenCalled();
1493+
expect(afterSubmit3).not.toHaveBeenCalled();
1494+
1495+
managerApi().submit();
1496+
1497+
expect(afterSubmit1).toHaveBeenCalled();
1498+
expect(afterSubmit2).toHaveBeenCalled();
1499+
expect(afterSubmit3).toHaveBeenCalled();
1500+
});
1501+
1502+
it('calls before submit', () => {
1503+
const onSubmit = jest.fn();
1504+
1505+
const managerApi = createManagerApi({ onSubmit });
1506+
1507+
const beforeSubmit1 = jest.fn();
1508+
const beforeSubmit2 = jest.fn();
1509+
const beforeSubmit3 = jest.fn();
1510+
1511+
managerApi().registerField({ name: 'field', internalId: '1', render: jest.fn(), beforeSubmit: beforeSubmit1 });
1512+
managerApi().registerField({ name: 'field', internalId: '2', render: jest.fn(), beforeSubmit: beforeSubmit2 });
1513+
managerApi().registerField({ name: 'field2', render: jest.fn(), beforeSubmit: beforeSubmit3 });
1514+
1515+
expect(beforeSubmit1).not.toHaveBeenCalled();
1516+
expect(beforeSubmit2).not.toHaveBeenCalled();
1517+
expect(beforeSubmit3).not.toHaveBeenCalled();
1518+
1519+
managerApi().submit();
1520+
1521+
expect(beforeSubmit1).toHaveBeenCalled();
1522+
expect(beforeSubmit2).toHaveBeenCalled();
1523+
expect(beforeSubmit3).toHaveBeenCalled();
1524+
1525+
expect(onSubmit).toHaveBeenCalled();
1526+
});
1527+
1528+
it('bails out on before submit returning false', () => {
1529+
const onSubmit = jest.fn();
1530+
1531+
const managerApi = createManagerApi({ onSubmit });
1532+
1533+
const beforeSubmit1 = jest.fn();
1534+
const beforeSubmit2 = jest.fn().mockImplementation(() => false);
1535+
const beforeSubmit3 = jest.fn();
1536+
1537+
managerApi().registerField({ name: 'field', internalId: '1', render: jest.fn(), beforeSubmit: beforeSubmit1 });
1538+
managerApi().registerField({ name: 'field', internalId: '2', render: jest.fn(), beforeSubmit: beforeSubmit2 });
1539+
managerApi().registerField({ name: 'field2', render: jest.fn(), beforeSubmit: beforeSubmit3 });
1540+
1541+
expect(beforeSubmit1).not.toHaveBeenCalled();
1542+
expect(beforeSubmit2).not.toHaveBeenCalled();
1543+
expect(beforeSubmit3).not.toHaveBeenCalled();
1544+
1545+
managerApi().submit();
1546+
1547+
expect(beforeSubmit1).toHaveBeenCalled();
1548+
expect(beforeSubmit2).toHaveBeenCalled();
1549+
expect(beforeSubmit3).not.toHaveBeenCalled();
1550+
1551+
expect(onSubmit).not.toHaveBeenCalled();
1552+
});
1553+
});
14761554
});

packages/form-state-manager/src/types/field-config.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { FieldRender } from './manager-api';
44
import { Validator } from './validate';
55

66
export type IsEqual = (a: any, b: any) => boolean;
7+
export type AfterSubmit = () => void;
8+
export type BeforeSubmit = () => void | false;
79

810
export interface FieldConfig extends AnyObject {
911
name: string;
@@ -21,6 +23,8 @@ export interface FieldConfig extends AnyObject {
2123
initialValue?: any;
2224
defaultValue?: any;
2325
data?: AnyObject;
26+
afterSubmit?: AfterSubmit;
27+
beforeSubmit?: BeforeSubmit;
2428
}
2529

2630
export default FieldConfig;

packages/form-state-manager/src/types/manager-api.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import AnyObject, { AnyBooleanObject } from './any-object';
22
import { FormEvent } from 'react';
3-
import FieldConfig, { IsEqual } from './field-config';
3+
import FieldConfig, { IsEqual, AfterSubmit, BeforeSubmit } from './field-config';
44
import { FormValidator, Validator } from './validate';
55
import { Subscription, Meta } from './use-field';
66

@@ -20,7 +20,7 @@ export type UpdateFieldState = (name: string, mutateState: (prevState: FieldStat
2020

2121
export type Callback = () => void;
2222
export type Change = (name: string, value?: any) => void;
23-
export type HandleSubmit = (event: FormEvent) => void;
23+
export type HandleSubmit = (event?: FormEvent) => void;
2424
export type RegisterField = (field: FieldConfig) => void;
2525
export type UnregisterField = (field: Omit<FieldConfig, 'render'>) => void;
2626
export type GetState = () => ManagerState;
@@ -62,6 +62,8 @@ export type FieldRender = () => void;
6262
export interface ListenerField {
6363
render: FieldRender;
6464
subscription?: Subscription;
65+
afterSubmit?: AfterSubmit;
66+
beforeSubmit?: BeforeSubmit;
6567
}
6668

6769
export interface FieldListenerFields {
@@ -178,6 +180,8 @@ export interface SubscriberConfig extends AnyObject {
178180
internalId?: number | string;
179181
isEqual?: IsEqual;
180182
validateFields?: Array<string>;
183+
afterSubmit?: AfterSubmit;
184+
beforeSubmit?: BeforeSubmit;
181185
}
182186

183187
export interface CreateManagerApiConfig {

packages/form-state-manager/src/types/use-field.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Validator } from './validate';
22
import { DataType } from './data-types';
33
import AnyObject from './any-object';
4+
import { AfterSubmit, BeforeSubmit } from './field-config';
45

56
export interface Subscription {
67
active?: boolean;
@@ -77,6 +78,8 @@ export interface UseField extends AnyObject {
7778
format?: Format;
7879
parse?: Parse;
7980
formatOnBlur?: boolean;
81+
afterSubmit?: AfterSubmit;
82+
beforeSubmit?: BeforeSubmit;
8083
}
8184

8285
export default UseField;

packages/form-state-manager/src/utils/manager-api.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,9 +463,27 @@ const createManagerApi: CreateManagerApi = ({
463463
setFieldState(name, (prevState) => ({ ...prevState, meta: { ...prevState.meta, active: false } }));
464464
}
465465

466-
function handleSubmit(event: FormEvent): void {
467-
event.preventDefault();
466+
function handleSubmit(event?: FormEvent): void {
467+
event && event.preventDefault && event.preventDefault();
468+
469+
let error = false;
470+
state.registeredFields.forEach((name) =>
471+
traverseObject(state.fieldListeners[name].fields, (field) => {
472+
error = error || (field.beforeSubmit && field.beforeSubmit() === false);
473+
})
474+
);
475+
476+
if (error) {
477+
return;
478+
}
479+
468480
config.onSubmit(state.values);
481+
482+
state.registeredFields.forEach((name) =>
483+
traverseObject(state.fieldListeners[name].fields, (field) => {
484+
field.afterSubmit && field.afterSubmit();
485+
})
486+
);
469487
}
470488

471489
function isInitialized(name: string): boolean {
@@ -649,7 +667,9 @@ const createManagerApi: CreateManagerApi = ({
649667
...state.fieldListeners[subscriberConfig.name]?.fields,
650668
[subscriberConfig.internalId || subscriberConfig.name]: {
651669
render: subscriberConfig.render,
652-
subscription: subscriberConfig.subscription
670+
subscription: subscriberConfig.subscription,
671+
afterSubmit: subscriberConfig.afterSubmit,
672+
beforeSubmit: subscriberConfig.beforeSubmit
653673
}
654674
}
655675
};

0 commit comments

Comments
 (0)