Skip to content

Commit 64fa5d4

Browse files
erikras-dinesh-agentwesflynnDinesh Chugtai
authored
Merge PR #45: Add updateOnPristine option (#62)
* added updateOnPristine option * Address CodeRabbit review comments 1. src/types.ts: Reorder Calculation properties to match index.d.ts (field, updates, updateOnPristine, isEqual) 2. src/decorator.ts: Replace bitwise NOT (~) with clearer indexOf check (field.indexOf(name) !== -1) 3. src/decorator.test.ts: Add regex test case for updateOnPristine=false with regex field matcher --------- Co-authored-by: Wesley <flynn.wes@gmail.com> Co-authored-by: Dinesh Chugtai <dinesh@erikras.com>
1 parent 0365a69 commit 64fa5d4

4 files changed

Lines changed: 173 additions & 18 deletions

File tree

src/decorator.test.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,147 @@ import createDecorator from './decorator'
33

44
const onSubmitMock = () => { }
55
describe('decorator', () => {
6+
it('should not trigger update on initialValues if updateOnPristine is false', () => {
7+
const form = createForm({
8+
initialValues: { foo: 'test', bar: 'test' },
9+
onSubmit: onSubmitMock
10+
})
11+
const spy = jest.fn()
12+
const foo = jest.fn()
13+
const bar = jest.fn()
14+
form.subscribe(spy, { values: true })
15+
form.registerField('foo', foo, { value: true })
16+
form.registerField('bar', bar, { value: true })
17+
const decorator = createDecorator({
18+
field: 'foo',
19+
updateOnPristine: false,
20+
updates: {
21+
bar: (fooValue) => `${fooValue}bar`
22+
}
23+
})
24+
const unsubscribe = decorator(form)
25+
expect(typeof unsubscribe).toBe('function')
26+
27+
expect(spy).toHaveBeenCalled()
28+
expect(spy).toHaveBeenCalledTimes(1)
29+
expect(spy.mock.calls[0][0].values).toEqual({ foo: 'test', bar: 'test' })
30+
31+
expect(foo).toHaveBeenCalled()
32+
expect(foo).toHaveBeenCalledTimes(1)
33+
expect(foo.mock.calls[0][0].value).toBe('test')
34+
35+
expect(bar).toHaveBeenCalled()
36+
expect(bar).toHaveBeenCalledTimes(1)
37+
expect(bar.mock.calls[0][0].value).toBe('test')
38+
39+
// change foo (should trigger calculation on bar)
40+
form.change('foo', 'baz')
41+
42+
expect(spy).toHaveBeenCalledTimes(3)
43+
expect(spy.mock.calls[1][0].values).toEqual({ foo: 'baz', bar: 'test' })
44+
expect(spy.mock.calls[2][0].values).toEqual({ foo: 'baz', bar: 'bazbar' })
45+
46+
expect(foo).toHaveBeenCalledTimes(2)
47+
expect(foo.mock.calls[1][0].value).toBe('baz')
48+
49+
expect(bar).toHaveBeenCalledTimes(2)
50+
expect(bar.mock.calls[1][0].value).toBe('bazbar')
51+
})
52+
53+
it('should not trigger update on initialValues if updateOnPristine is false, using array of field names', () => {
54+
const form = createForm({
55+
initialValues: { foo: 'test', bar: 'test' },
56+
onSubmit: onSubmitMock
57+
})
58+
const spy = jest.fn()
59+
const foo = jest.fn()
60+
const bar = jest.fn()
61+
form.subscribe(spy, { values: true })
62+
form.registerField('foo', foo, { value: true })
63+
form.registerField('bar', bar, { value: true })
64+
const decorator = createDecorator({
65+
field: ['cat', 'dog', 'rat', 'foo', 'hog'],
66+
updateOnPristine: false,
67+
updates: {
68+
bar: (fooValue) => `${fooValue}bar`
69+
}
70+
})
71+
const unsubscribe = decorator(form)
72+
expect(typeof unsubscribe).toBe('function')
73+
74+
expect(spy).toHaveBeenCalled()
75+
expect(spy).toHaveBeenCalledTimes(1)
76+
expect(spy.mock.calls[0][0].values).toEqual({ foo: 'test', bar: 'test' })
77+
78+
expect(foo).toHaveBeenCalled()
79+
expect(foo).toHaveBeenCalledTimes(1)
80+
expect(foo.mock.calls[0][0].value).toBe('test')
81+
82+
expect(bar).toHaveBeenCalled()
83+
expect(bar).toHaveBeenCalledTimes(1)
84+
expect(bar.mock.calls[0][0].value).toBe('test')
85+
86+
// change foo (should trigger calculation on bar)
87+
form.change('foo', 'baz')
88+
89+
expect(spy).toHaveBeenCalledTimes(3)
90+
expect(spy.mock.calls[1][0].values).toEqual({ foo: 'baz', bar: 'test' })
91+
expect(spy.mock.calls[2][0].values).toEqual({ foo: 'baz', bar: 'bazbar' })
92+
93+
expect(foo).toHaveBeenCalledTimes(2)
94+
expect(foo.mock.calls[1][0].value).toBe('baz')
95+
96+
expect(bar).toHaveBeenCalledTimes(2)
97+
expect(bar.mock.calls[1][0].value).toBe('bazbar')
98+
})
99+
100+
it('should not trigger update on initialValues if updateOnPristine is false, using regex for field matcher', () => {
101+
const form = createForm({
102+
initialValues: { foo: 'test', bar: 'test' },
103+
onSubmit: onSubmitMock
104+
})
105+
const spy = jest.fn()
106+
const foo = jest.fn()
107+
const bar = jest.fn()
108+
form.subscribe(spy, { values: true })
109+
form.registerField('foo', foo, { value: true })
110+
form.registerField('bar', bar, { value: true })
111+
const decorator = createDecorator({
112+
field: /^foo$/,
113+
updateOnPristine: false,
114+
updates: {
115+
bar: (fooValue) => `${fooValue}bar`
116+
}
117+
})
118+
const unsubscribe = decorator(form)
119+
expect(typeof unsubscribe).toBe('function')
120+
121+
expect(spy).toHaveBeenCalled()
122+
expect(spy).toHaveBeenCalledTimes(1)
123+
expect(spy.mock.calls[0][0].values).toEqual({ foo: 'test', bar: 'test' })
124+
125+
expect(foo).toHaveBeenCalled()
126+
expect(foo).toHaveBeenCalledTimes(1)
127+
expect(foo.mock.calls[0][0].value).toBe('test')
128+
129+
expect(bar).toHaveBeenCalled()
130+
expect(bar).toHaveBeenCalledTimes(1)
131+
expect(bar.mock.calls[0][0].value).toBe('test')
132+
133+
// change foo (should trigger calculation on bar)
134+
form.change('foo', 'baz')
135+
136+
expect(spy).toHaveBeenCalledTimes(3)
137+
expect(spy.mock.calls[1][0].values).toEqual({ foo: 'baz', bar: 'test' })
138+
expect(spy.mock.calls[2][0].values).toEqual({ foo: 'baz', bar: 'bazbar' })
139+
140+
expect(foo).toHaveBeenCalledTimes(2)
141+
expect(foo.mock.calls[1][0].value).toBe('baz')
142+
143+
expect(bar).toHaveBeenCalledTimes(2)
144+
expect(bar.mock.calls[1][0].value).toBe('bazbar')
145+
})
146+
6147
it('should update one field when another changes', () => {
7148
const form = createForm({ onSubmit: onSubmitMock })
8149
const spy = jest.fn()

src/decorator.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,37 @@ const createDecorator = <FormValues extends Record<string, any> = Record<string,
4848
}
4949
}
5050
const fields = form.getRegisteredFields()
51-
calculations.forEach(({ field, isEqual, updates }) => {
52-
if (typeof field === 'string') {
53-
runUpdates(field, isEqual || tripleEquals, updates)
54-
} else {
55-
// field is a either array or regex
56-
const matches = Array.isArray(field)
57-
? (name: string) =>
58-
~field.indexOf(name) ||
59-
field.findIndex(
60-
f => f instanceof RegExp && (f as RegExp).test(name)
61-
) !== -1
62-
: (name: string) => (field as RegExp).test(name)
63-
fields.forEach(fieldName => {
64-
if (matches(fieldName)) {
65-
runUpdates(fieldName, isEqual || tripleEquals, updates)
51+
calculations.forEach(
52+
({ field, isEqual, updates, updateOnPristine = true }) => {
53+
if (typeof field === 'string') {
54+
if (
55+
updateOnPristine ||
56+
!form.getFieldState(field)?.pristine
57+
) {
58+
runUpdates(field, isEqual || tripleEquals, updates)
6659
}
67-
})
60+
} else {
61+
// field is a either array or regex
62+
const matches = Array.isArray(field)
63+
? (name: string) =>
64+
field.indexOf(name) !== -1 ||
65+
field.findIndex(
66+
f => f instanceof RegExp && (f as RegExp).test(name)
67+
) !== -1
68+
: (name: string) => (field as RegExp).test(name)
69+
fields.forEach(fieldName => {
70+
if (matches(fieldName)) {
71+
if (
72+
updateOnPristine ||
73+
!form.getFieldState(fieldName)?.pristine
74+
) {
75+
runUpdates(fieldName, isEqual || tripleEquals, updates)
76+
}
77+
}
78+
})
79+
}
6880
}
69-
})
81+
)
7082
previousValues = values
7183
})
7284
},

src/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type Updates = UpdatesByName | UpdatesForAll
2020
export type Calculation = {
2121
field: FieldPattern,
2222
updates: Updates,
23+
updateOnPristine?: boolean,
2324
isEqual?: (_a: any, _b: any) => boolean,
2425
}
2526

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export type Updates = UpdatesByName | UpdatesForAll
1313

1414
export type Calculation = {
1515
field: FieldPattern,
16+
updates: Updates,
17+
updateOnPristine?: boolean,
1618
isEqual?: (_a: any, _b: any) => boolean,
17-
updates: Updates
1819
}

0 commit comments

Comments
 (0)