Skip to content

Commit c44ad1d

Browse files
rosskevinerikras
authored andcommitted
[typescript] add tests and implementation for FieldArrayProps (#18)
* [typescript] add tests and implementation for FieldArrayProps * fix typings extension * fix default import * Upgraded ff and ffarrays deps * Upgraded react-final-form * generate new lock * make runtime match the expected type signatures regarding fieldState and meta * fix resubscribe test - value is in different location * value is under fields * Fix validation after [email protected] breaking change
1 parent 4e128e2 commit c44ad1d

File tree

9 files changed

+959
-1527
lines changed

9 files changed

+959
-1527
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ lib
99
es
1010
npm-debug.log
1111
.DS_Store
12+
.idea
13+
yarn.lock

package-lock.json

Lines changed: 749 additions & 1501 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-scripts.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,14 @@ module.exports = {
6464
description: 'flow check the entire project',
6565
script: 'flow check'
6666
},
67+
typescript: {
68+
description: 'typescript check the entire project',
69+
script: 'tsc'
70+
},
6771
validate: {
6872
description:
6973
'This runs several scripts to make sure things look good before committing or on clean install',
70-
default: concurrent.nps('lint', 'flow', 'build.andTest', 'test')
74+
default: concurrent.nps('lint', 'flow', 'typescript', 'build.andTest', 'test')
7175
}
7276
},
7377
options: {

package.json

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
{
22
"name": "react-final-form-arrays",
33
"version": "1.0.2",
4-
"description":
5-
"A component for rendering and editing arrays 🏁 React Final Form",
4+
"description": "A component for rendering and editing arrays 🏁 React Final Form",
65
"main": "dist/react-final-form-arrays.cjs.js",
76
"jsnext:main": "dist/react-final-form-arrays.es.js",
87
"module": "dist/react-final-form-arrays.es.js",
9-
"typings": "dist/index.d.js",
10-
"files": ["dist"],
8+
"typings": "dist/index.d.ts",
9+
"files": [
10+
"dist"
11+
],
1112
"scripts": {
1213
"start": "nps",
1314
"test": "nps test",
1415
"precommit": "lint-staged && npm start validate"
1516
},
16-
"author":
17-
"Erik Rasmussen <[email protected]> (http://github.com/erikras)",
17+
"author": "Erik Rasmussen <[email protected]> (http://github.com/erikras)",
1818
"license": "MIT",
1919
"repository": {
2020
"type": "git",
@@ -42,9 +42,8 @@
4242
"eslint-plugin-import": "^2.8.0",
4343
"eslint-plugin-jsx-a11y": "^6.0.2",
4444
"eslint-plugin-react": "^7.4.0",
45-
"final-form": "^2.0.0",
46-
"final-form-arrays": "^1.0.3",
47-
"flow": "^0.2.3",
45+
"final-form": "^4.0.4",
46+
"final-form-arrays": "^1.0.4",
4847
"flow-bin": "^0.61.0",
4948
"husky": "^0.14.3",
5049
"jest": "^21.2.1",
@@ -57,14 +56,15 @@
5756
"raf": "^3.4.0",
5857
"react": "^16.1.0",
5958
"react-dom": "^16.1.0",
60-
"react-final-form": "^1.1.0",
59+
"react-final-form": "^3.0.5",
6160
"rollup": "^0.52.0",
6261
"rollup-plugin-babel": "^3.0.2",
6362
"rollup-plugin-commonjs": "^8.2.6",
6463
"rollup-plugin-flow": "^1.1.1",
6564
"rollup-plugin-node-resolve": "^3.0.0",
6665
"rollup-plugin-replace": "^2.0.0",
67-
"rollup-plugin-uglify": "^2.0.1"
66+
"rollup-plugin-uglify": "^2.0.1",
67+
"typescript": "^2.6.2"
6868
},
6969
"peerDependencies": {
7070
"final-form": ">=2.0.0",
@@ -73,10 +73,15 @@
7373
"react": "^15.3.0 || ^16.0.0-0"
7474
},
7575
"jest": {
76-
"setupFiles": ["raf/polyfill"]
76+
"setupFiles": [
77+
"raf/polyfill"
78+
]
7779
},
7880
"lint-staged": {
79-
"*.{js*,ts,json,md,css}": ["prettier --write", "git add"]
81+
"*.{js*,ts*,json,md,css}": [
82+
"prettier --write",
83+
"git add"
84+
]
8085
},
8186
"bundlesize": [
8287
{

src/FieldArray.d.test.tsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import * as React from 'react'
2+
import { Form, Field } from 'react-final-form'
3+
import arrayMutators from 'final-form-arrays'
4+
import { FieldArray } from './index'
5+
6+
const onSubmit = async (values: any) => {
7+
console.log(values)
8+
}
9+
10+
const basic = () => (
11+
<Form
12+
onSubmit={onSubmit}
13+
mutators={{
14+
...arrayMutators
15+
}}
16+
>
17+
{({
18+
handleSubmit,
19+
mutators: { push, pop }, // injected from final-form-arrays above
20+
pristine,
21+
reset,
22+
submitting,
23+
values
24+
}) => {
25+
return (
26+
<form onSubmit={handleSubmit}>
27+
<div>
28+
<label>Company</label>
29+
<Field name="company" component="input" />
30+
</div>
31+
<div className="buttons">
32+
<button type="button" onClick={() => push('customers', undefined)}>
33+
Add Customer
34+
</button>
35+
<button type="button" onClick={() => pop('customers')}>
36+
Remove Customer
37+
</button>
38+
</div>
39+
<FieldArray name="customers">
40+
{({ fields }) =>
41+
fields.map((name, index) => (
42+
<div key={name}>
43+
<label>Cust. #{index + 1}</label>
44+
<Field
45+
name={`${name}.firstName`}
46+
component="input"
47+
placeholder="First Name"
48+
/>
49+
<Field
50+
name={`${name}.lastName`}
51+
component="input"
52+
placeholder="Last Name"
53+
/>
54+
<span
55+
onClick={() => fields.remove(index)}
56+
style={{ cursor: 'pointer' }}
57+
>
58+
59+
</span>
60+
</div>
61+
))
62+
}
63+
</FieldArray>
64+
65+
<div className="buttons">
66+
<button type="submit" disabled={submitting || pristine}>
67+
Submit
68+
</button>
69+
<button
70+
type="button"
71+
onClick={reset}
72+
disabled={submitting || pristine}
73+
>
74+
Reset
75+
</button>
76+
</div>
77+
<pre>{JSON.stringify(values)}</pre>
78+
</form>
79+
)
80+
}}
81+
</Form>
82+
)

src/FieldArray.js

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ export default class FieldArray extends React.PureComponent<Props, State> {
5959
name,
6060
listener,
6161
subscription ? { ...subscription, length: true } : all,
62-
{ validate: this.validate }
62+
{
63+
getValidator: () => this.props.validate
64+
}
6365
)
6466
}
6567

@@ -80,9 +82,6 @@ export default class FieldArray extends React.PureComponent<Props, State> {
8082
}
8183
}
8284

83-
validate = (value: ?any, allValues: Object) =>
84-
this.props.validate && this.props.validate(value, allValues)
85-
8685
notify = (state: FieldState) => {
8786
setTimeout(() => this.setState({ state }))
8887
}
@@ -132,18 +131,51 @@ export default class FieldArray extends React.PureComponent<Props, State> {
132131

133132
render() {
134133
const { name, ...rest } = this.props
135-
let { value, length, ...meta } = this.state.state
134+
let {
135+
length,
136+
active,
137+
dirty,
138+
error,
139+
initial,
140+
invalid,
141+
pristine,
142+
submitError,
143+
submitFailed,
144+
submitSucceeded,
145+
touched,
146+
valid,
147+
visited,
148+
...fieldStateFunctions
149+
} = this.state.state
150+
const meta = {
151+
active,
152+
dirty,
153+
error,
154+
initial,
155+
invalid,
156+
pristine,
157+
submitError,
158+
submitFailed,
159+
submitSucceeded,
160+
touched,
161+
valid,
162+
visited
163+
}
164+
const fieldState = {
165+
...meta,
166+
...fieldStateFunctions
167+
}
136168
return renderComponent(
137169
{
138170
fields: {
139171
name,
140172
forEach: this.forEach,
141173
length,
142174
map: this.map,
143-
...this.mutators
175+
...this.mutators,
176+
...fieldState
144177
},
145178
meta,
146-
value,
147179
...rest
148180
},
149181
`FieldArray(${name})`

src/FieldArray.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ describe('FieldArray', () => {
7474
const dom = TestUtils.renderIntoDocument(<Container />)
7575
expect(renderArray).toHaveBeenCalled()
7676
expect(renderArray).toHaveBeenCalledTimes(1)
77-
expect(renderArray.mock.calls[0][0].value).toEqual(['Odie'])
77+
expect(renderArray.mock.calls[0][0].fields.value).toEqual(['Odie'])
7878

7979
const button = TestUtils.findRenderedDOMComponentWithTag(dom, 'button')
8080
TestUtils.Simulate.click(button)
8181
await sleep(2)
8282

8383
expect(renderArray).toHaveBeenCalledTimes(4)
84-
expect(renderArray.mock.calls[3][0].value).toEqual(['Garfield'])
84+
expect(renderArray.mock.calls[3][0].fields.value).toEqual(['Garfield'])
8585
})
8686

8787
it('should not resubscribe if name changes when not inside a <Form> (duh)', () => {

src/index.d.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,49 @@
11
import * as React from 'react'
2-
import { FieldArrayProps } from 'final-form'
2+
import { FieldSubscription, FieldState } from 'final-form'
3+
export const version: string
34

4-
export var FieldArray: React.ComponentType<FieldArrayProps>
5-
export var version: string
5+
export const FieldArray: React.ComponentType<FieldArrayProps>
6+
7+
export interface FieldArrayRenderProps {
8+
fields: {
9+
forEach: (iterator: (name: string, index: number) => void) => void
10+
insert: (index: number, value: any) => void
11+
map: (iterator: (name: string, index: number) => any) => any[]
12+
move: (from: number, to: number) => void
13+
name: string
14+
pop: () => any
15+
push: (value: any) => void
16+
remove: (index: number) => any
17+
shift: () => any
18+
swap: (indexA: number, indexB: number) => void
19+
unshift: (value: any) => void
20+
} & FieldState
21+
meta: Partial<{
22+
// TODO: Make a diff of `FieldState` without all the functions
23+
active: boolean
24+
dirty: boolean
25+
error: boolean
26+
initial: boolean
27+
invalid: boolean
28+
pristine: boolean
29+
submitError: boolean
30+
submitFailed: boolean
31+
submitSucceeded: boolean
32+
touched: boolean
33+
valid: boolean
34+
visited: boolean
35+
}>
36+
}
37+
38+
export interface RenderableProps<T> {
39+
children?: ((props: T) => React.ReactNode) | React.ReactNode
40+
component?: React.ComponentType<T> | string
41+
render?: (props: T) => React.ReactNode
42+
}
43+
44+
export interface FieldArrayProps
45+
extends RenderableProps<FieldArrayRenderProps> {
46+
name: string
47+
subscription?: FieldSubscription
48+
validate?: (value: any, allValues: object) => any
49+
}

tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"compilerOptions": {
3+
"lib": [
4+
"es2015",
5+
"dom"
6+
],
7+
"jsx": "react",
8+
"baseUrl": ".",
9+
"noEmit": true,
10+
"strict": true
11+
},
12+
"include": [
13+
"./src/**/*"
14+
]
15+
}

0 commit comments

Comments
 (0)