@atlaskit/form 12.1.1 → 12.2.0
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.
- package/CHANGELOG.md +10 -0
- package/codemods/__tests__/not-yet-migrate-to-simplified-form.test.tsx +613 -0
- package/codemods/not-yet-migrate-to-simplified-form.tsx +173 -0
- package/codemods/utils/helpers.tsx +327 -0
- package/dist/cjs/form.js +16 -8
- package/dist/es2019/form.js +18 -8
- package/dist/esm/form.js +16 -8
- package/dist/types/form.d.ts +13 -0
- package/dist/types-ts4.5/form.d.ts +13 -0
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @atlaskit/form
|
|
2
2
|
|
|
3
|
+
## 12.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`07de46497864a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/07de46497864a) -
|
|
8
|
+
We are testing a new way to render the Form component behind a feature flag. Rendering a `Form`
|
|
9
|
+
component with direct JSX elements instead of a function as `children` will render an HTML `form`
|
|
10
|
+
element internally, reducing the boilerplate required for most use cases. If this fix is
|
|
11
|
+
successful it will be available in a later release.
|
|
12
|
+
|
|
3
13
|
## 12.1.1
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
jest.autoMockOff();
|
|
2
|
+
|
|
3
|
+
import transformer from '../not-yet-migrate-to-simplified-form';
|
|
4
|
+
|
|
5
|
+
const defineInlineTest = require('jscodeshift/dist/testUtils').defineInlineTest;
|
|
6
|
+
|
|
7
|
+
defineInlineTest(
|
|
8
|
+
{ default: transformer, parser: 'tsx' },
|
|
9
|
+
{},
|
|
10
|
+
`import React from 'react';`,
|
|
11
|
+
`import React from 'react';`,
|
|
12
|
+
'should not transform if imports are not present',
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
defineInlineTest(
|
|
16
|
+
{ default: transformer, parser: 'tsx' },
|
|
17
|
+
{},
|
|
18
|
+
`
|
|
19
|
+
import React from 'react';
|
|
20
|
+
import { Field } from '@atlaskit/form';
|
|
21
|
+
|
|
22
|
+
const Component1 = () => <Field />;
|
|
23
|
+
|
|
24
|
+
const Component2 = () => <><Field /></>;
|
|
25
|
+
|
|
26
|
+
class Component3 extends React.Component { render() { return <Field />; } }
|
|
27
|
+
|
|
28
|
+
const element = <Field />;
|
|
29
|
+
`,
|
|
30
|
+
`
|
|
31
|
+
import React from 'react';
|
|
32
|
+
import { Field } from '@atlaskit/form';
|
|
33
|
+
|
|
34
|
+
const Component1 = () => <Field />;
|
|
35
|
+
|
|
36
|
+
const Component2 = () => <><Field /></>;
|
|
37
|
+
|
|
38
|
+
class Component3 extends React.Component { render() { return <Field />; } }
|
|
39
|
+
|
|
40
|
+
const element = <Field />;
|
|
41
|
+
`,
|
|
42
|
+
'should not transform if default import is not preset',
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
defineInlineTest(
|
|
46
|
+
{ default: transformer, parser: 'tsx' },
|
|
47
|
+
{},
|
|
48
|
+
`
|
|
49
|
+
import React from 'react';
|
|
50
|
+
import Form from '@atlaskit/form';
|
|
51
|
+
|
|
52
|
+
const Component1 = () => <Form />;
|
|
53
|
+
|
|
54
|
+
const Component2 = () => <><Form /></>;
|
|
55
|
+
|
|
56
|
+
class Component3 extends React.Component { render() { return <Form />; } }
|
|
57
|
+
|
|
58
|
+
const element = <Form />;
|
|
59
|
+
`,
|
|
60
|
+
`
|
|
61
|
+
import React from 'react';
|
|
62
|
+
import Form from '@atlaskit/form';
|
|
63
|
+
|
|
64
|
+
const Component1 = () => <Form />;
|
|
65
|
+
|
|
66
|
+
const Component2 = () => <><Form /></>;
|
|
67
|
+
|
|
68
|
+
class Component3 extends React.Component { render() { return <Form />; } }
|
|
69
|
+
|
|
70
|
+
const element = <Form />;
|
|
71
|
+
`,
|
|
72
|
+
'should not transform if children are not preset',
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
defineInlineTest(
|
|
76
|
+
{ default: transformer, parser: 'tsx' },
|
|
77
|
+
{},
|
|
78
|
+
`
|
|
79
|
+
import React from 'react';
|
|
80
|
+
import Form from '@atlaskit/code';
|
|
81
|
+
|
|
82
|
+
const Component1 = () => (
|
|
83
|
+
<Form onSubmit={() => {}}>
|
|
84
|
+
<p>Test</p>
|
|
85
|
+
</Form>
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const Component2 = () => (
|
|
89
|
+
<Form onSubmit={() => {}}>
|
|
90
|
+
<p>Test</p>
|
|
91
|
+
</Form>
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
class Component3 extends React.Component {
|
|
95
|
+
render() {
|
|
96
|
+
return (
|
|
97
|
+
<Form onSubmit={() => {}}>
|
|
98
|
+
<p>Test</p>
|
|
99
|
+
</Form>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const element = (
|
|
105
|
+
<Form onSubmit={() => {}}>
|
|
106
|
+
<p>Test</p>
|
|
107
|
+
</Form>
|
|
108
|
+
);
|
|
109
|
+
`,
|
|
110
|
+
`
|
|
111
|
+
import React from 'react';
|
|
112
|
+
import Form from '@atlaskit/code';
|
|
113
|
+
|
|
114
|
+
const Component1 = () => (
|
|
115
|
+
<Form onSubmit={() => {}}>
|
|
116
|
+
<p>Test</p>
|
|
117
|
+
</Form>
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const Component2 = () => (
|
|
121
|
+
<Form onSubmit={() => {}}>
|
|
122
|
+
<p>Test</p>
|
|
123
|
+
</Form>
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
class Component3 extends React.Component {
|
|
127
|
+
render() {
|
|
128
|
+
return (
|
|
129
|
+
<Form onSubmit={() => {}}>
|
|
130
|
+
<p>Test</p>
|
|
131
|
+
</Form>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const element = (
|
|
137
|
+
<Form onSubmit={() => {}}>
|
|
138
|
+
<p>Test</p>
|
|
139
|
+
</Form>
|
|
140
|
+
);
|
|
141
|
+
`,
|
|
142
|
+
'should not transform if children is not a function',
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
describe('Migrate <Form> to simplified version if only `formProps` in child arg ', () => {
|
|
146
|
+
defineInlineTest(
|
|
147
|
+
{ default: transformer, parser: 'tsx' },
|
|
148
|
+
{},
|
|
149
|
+
`
|
|
150
|
+
import React from 'react';
|
|
151
|
+
import Form from '@atlaskit/form';
|
|
152
|
+
|
|
153
|
+
const FormComponent1 = () => (
|
|
154
|
+
<Form onSubmit={() => {}}>
|
|
155
|
+
{({ formProps }) => (
|
|
156
|
+
<form {...formProps}>
|
|
157
|
+
<input />
|
|
158
|
+
</form>
|
|
159
|
+
)}
|
|
160
|
+
</Form>
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const FormComponent2 = () => (
|
|
164
|
+
<>
|
|
165
|
+
<Form onSubmit={() => {}}>
|
|
166
|
+
{({ formProps }) => (
|
|
167
|
+
<form {...formProps}>
|
|
168
|
+
<input />
|
|
169
|
+
</form>
|
|
170
|
+
)}
|
|
171
|
+
</Form>
|
|
172
|
+
</>
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
class FormComponent3 extends React.Component {
|
|
176
|
+
render() {
|
|
177
|
+
return (
|
|
178
|
+
<Form onSubmit={() => {}}>
|
|
179
|
+
{({ formProps }) => (
|
|
180
|
+
<form {...formProps}>
|
|
181
|
+
<input />
|
|
182
|
+
</form>
|
|
183
|
+
)}
|
|
184
|
+
</Form>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const formElement = (
|
|
190
|
+
<Form onSubmit={() => {}}>
|
|
191
|
+
{({ formProps }) => (
|
|
192
|
+
<form {...formProps}>
|
|
193
|
+
<input />
|
|
194
|
+
</form>
|
|
195
|
+
)}
|
|
196
|
+
</Form>
|
|
197
|
+
);
|
|
198
|
+
`,
|
|
199
|
+
`
|
|
200
|
+
import React from 'react';
|
|
201
|
+
import Form from '@atlaskit/form';
|
|
202
|
+
|
|
203
|
+
const FormComponent1 = () => (
|
|
204
|
+
<Form onSubmit={() => {}}><input /></Form>
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const FormComponent2 = () => (
|
|
208
|
+
<>
|
|
209
|
+
<Form onSubmit={() => {}}><input /></Form>
|
|
210
|
+
</>
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
class FormComponent3 extends React.Component {
|
|
214
|
+
render() {
|
|
215
|
+
return (<Form onSubmit={() => {}}><input /></Form>);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const formElement = (
|
|
220
|
+
<Form onSubmit={() => {}}><input /></Form>
|
|
221
|
+
);
|
|
222
|
+
`,
|
|
223
|
+
'should convert from function with no props on form',
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
defineInlineTest(
|
|
227
|
+
{ default: transformer, parser: 'tsx' },
|
|
228
|
+
{},
|
|
229
|
+
`
|
|
230
|
+
import React from 'react';
|
|
231
|
+
import Form from '@atlaskit/form';
|
|
232
|
+
|
|
233
|
+
const FormComponent1 = () => (
|
|
234
|
+
<Form onSubmit={() => {}}>
|
|
235
|
+
{({ formProps: renamed }) => (
|
|
236
|
+
<form {...renamed}>
|
|
237
|
+
<input />
|
|
238
|
+
</form>
|
|
239
|
+
)}
|
|
240
|
+
</Form>
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const FormComponent2 = () => (
|
|
244
|
+
<>
|
|
245
|
+
<Form onSubmit={() => {}}>
|
|
246
|
+
{({ formProps: renamed }) => (
|
|
247
|
+
<form {...renamed}>
|
|
248
|
+
<input />
|
|
249
|
+
</form>
|
|
250
|
+
)}
|
|
251
|
+
</Form>
|
|
252
|
+
</>
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
class FormComponent3 extends React.Component {
|
|
256
|
+
render() {
|
|
257
|
+
return (
|
|
258
|
+
<Form onSubmit={() => {}}>
|
|
259
|
+
{({ formProps: renamed }) => (
|
|
260
|
+
<form {...renamed}>
|
|
261
|
+
<input />
|
|
262
|
+
</form>
|
|
263
|
+
)}
|
|
264
|
+
</Form>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const formElement = (
|
|
270
|
+
<Form onSubmit={() => {}}>
|
|
271
|
+
{({ formProps: renamed }) => (
|
|
272
|
+
<form {...renamed}>
|
|
273
|
+
<input />
|
|
274
|
+
</form>
|
|
275
|
+
)}
|
|
276
|
+
</Form>
|
|
277
|
+
);
|
|
278
|
+
`,
|
|
279
|
+
`
|
|
280
|
+
import React from 'react';
|
|
281
|
+
import Form from '@atlaskit/form';
|
|
282
|
+
|
|
283
|
+
const FormComponent1 = () => (
|
|
284
|
+
<Form onSubmit={() => {}}><input /></Form>
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const FormComponent2 = () => (
|
|
288
|
+
<>
|
|
289
|
+
<Form onSubmit={() => {}}><input /></Form>
|
|
290
|
+
</>
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
class FormComponent3 extends React.Component {
|
|
294
|
+
render() {
|
|
295
|
+
return (<Form onSubmit={() => {}}><input /></Form>);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const formElement = (
|
|
300
|
+
<Form onSubmit={() => {}}><input /></Form>
|
|
301
|
+
);
|
|
302
|
+
`,
|
|
303
|
+
'should convert from function with no props on form',
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
defineInlineTest(
|
|
307
|
+
{ default: transformer, parser: 'tsx' },
|
|
308
|
+
{},
|
|
309
|
+
`
|
|
310
|
+
import React from 'react';
|
|
311
|
+
import Form from '@atlaskit/form';
|
|
312
|
+
|
|
313
|
+
const FormComponent1 = () => (
|
|
314
|
+
<Form onSubmit={() => {}}>
|
|
315
|
+
{({ formProps }) => (
|
|
316
|
+
<form {...formProps} foo="bar">
|
|
317
|
+
<input />
|
|
318
|
+
</form>
|
|
319
|
+
)}
|
|
320
|
+
</Form>
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const FormComponent2 = () => (
|
|
324
|
+
<>
|
|
325
|
+
<Form onSubmit={() => {}}>
|
|
326
|
+
{({ formProps }) => (
|
|
327
|
+
<form {...formProps} foo="bar">
|
|
328
|
+
<input />
|
|
329
|
+
</form>
|
|
330
|
+
)}
|
|
331
|
+
</Form>
|
|
332
|
+
</>
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
class FormComponent3 extends React.Component {
|
|
336
|
+
render() {
|
|
337
|
+
return (
|
|
338
|
+
<Form onSubmit={() => {}}>
|
|
339
|
+
{({ formProps }) => (
|
|
340
|
+
<form {...formProps} foo="bar">
|
|
341
|
+
<input />
|
|
342
|
+
</form>
|
|
343
|
+
)}
|
|
344
|
+
</Form>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const formElement = (
|
|
350
|
+
<Form onSubmit={() => {}}>
|
|
351
|
+
{({ formProps }) => (
|
|
352
|
+
<form {...formProps} foo="bar">
|
|
353
|
+
<input />
|
|
354
|
+
</form>
|
|
355
|
+
)}
|
|
356
|
+
</Form>
|
|
357
|
+
);
|
|
358
|
+
`,
|
|
359
|
+
`
|
|
360
|
+
import React from 'react';
|
|
361
|
+
import Form from '@atlaskit/form';
|
|
362
|
+
|
|
363
|
+
const FormComponent1 = () => (
|
|
364
|
+
<Form
|
|
365
|
+
onSubmit={() => {}}
|
|
366
|
+
formProps={{
|
|
367
|
+
foo: "bar"
|
|
368
|
+
}}><input /></Form>
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
const FormComponent2 = () => (
|
|
372
|
+
<>
|
|
373
|
+
<Form
|
|
374
|
+
onSubmit={() => {}}
|
|
375
|
+
formProps={{
|
|
376
|
+
foo: "bar"
|
|
377
|
+
}}><input /></Form>
|
|
378
|
+
</>
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
class FormComponent3 extends React.Component {
|
|
382
|
+
render() {
|
|
383
|
+
return (
|
|
384
|
+
<Form
|
|
385
|
+
onSubmit={() => {}}
|
|
386
|
+
formProps={{
|
|
387
|
+
foo: "bar"
|
|
388
|
+
}}><input /></Form>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const formElement = (
|
|
394
|
+
<Form
|
|
395
|
+
onSubmit={() => {}}
|
|
396
|
+
formProps={{
|
|
397
|
+
foo: "bar"
|
|
398
|
+
}}><input /></Form>
|
|
399
|
+
);
|
|
400
|
+
`,
|
|
401
|
+
'should convert from function with single prop on form',
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
defineInlineTest(
|
|
405
|
+
{ default: transformer, parser: 'tsx' },
|
|
406
|
+
{},
|
|
407
|
+
`
|
|
408
|
+
import React from 'react';
|
|
409
|
+
import Form from '@atlaskit/form';
|
|
410
|
+
|
|
411
|
+
const FormComponent1 = () => (
|
|
412
|
+
<Form onSubmit={() => {}}>
|
|
413
|
+
{({ formProps }) => (
|
|
414
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
415
|
+
<input />
|
|
416
|
+
</form>
|
|
417
|
+
)}
|
|
418
|
+
</Form>
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
const FormComponent2 = () => (
|
|
422
|
+
<>
|
|
423
|
+
<Form onSubmit={() => {}}>
|
|
424
|
+
{({ formProps }) => (
|
|
425
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
426
|
+
<input />
|
|
427
|
+
</form>
|
|
428
|
+
)}
|
|
429
|
+
</Form>
|
|
430
|
+
</>
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
class FormComponent3 extends React.Component {
|
|
434
|
+
render() {
|
|
435
|
+
return (
|
|
436
|
+
<Form onSubmit={() => {}}>
|
|
437
|
+
{({ formProps }) => (
|
|
438
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
439
|
+
<input />
|
|
440
|
+
</form>
|
|
441
|
+
)}
|
|
442
|
+
</Form>
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const formElement = (
|
|
448
|
+
<Form onSubmit={() => {}}>
|
|
449
|
+
{({ formProps }) => (
|
|
450
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
451
|
+
<input />
|
|
452
|
+
</form>
|
|
453
|
+
)}
|
|
454
|
+
</Form>
|
|
455
|
+
);
|
|
456
|
+
`,
|
|
457
|
+
`
|
|
458
|
+
import React from 'react';
|
|
459
|
+
import Form from '@atlaskit/form';
|
|
460
|
+
|
|
461
|
+
const FormComponent1 = () => (
|
|
462
|
+
<Form
|
|
463
|
+
onSubmit={() => {}}
|
|
464
|
+
formProps={{
|
|
465
|
+
foo: "bar",
|
|
466
|
+
baz: "qux"
|
|
467
|
+
}}><input /></Form>
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
const FormComponent2 = () => (
|
|
471
|
+
<>
|
|
472
|
+
<Form
|
|
473
|
+
onSubmit={() => {}}
|
|
474
|
+
formProps={{
|
|
475
|
+
foo: "bar",
|
|
476
|
+
baz: "qux"
|
|
477
|
+
}}><input /></Form>
|
|
478
|
+
</>
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
class FormComponent3 extends React.Component {
|
|
482
|
+
render() {
|
|
483
|
+
return (
|
|
484
|
+
<Form
|
|
485
|
+
onSubmit={() => {}}
|
|
486
|
+
formProps={{
|
|
487
|
+
foo: "bar",
|
|
488
|
+
baz: "qux"
|
|
489
|
+
}}><input /></Form>
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const formElement = (
|
|
495
|
+
<Form
|
|
496
|
+
onSubmit={() => {}}
|
|
497
|
+
formProps={{
|
|
498
|
+
foo: "bar",
|
|
499
|
+
baz: "qux"
|
|
500
|
+
}}><input /></Form>
|
|
501
|
+
);
|
|
502
|
+
`,
|
|
503
|
+
'should convert from function with multiple props on form',
|
|
504
|
+
);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
describe('Do not migrate Form when anything more than `formProps` in child arg', () => {
|
|
508
|
+
defineInlineTest(
|
|
509
|
+
{ default: transformer, parser: 'tsx' },
|
|
510
|
+
{},
|
|
511
|
+
`
|
|
512
|
+
import React from 'react';
|
|
513
|
+
import Form from '@atlaskit/form';
|
|
514
|
+
|
|
515
|
+
const FormComponent1 = () => (
|
|
516
|
+
<Form onSubmit={() => {}}>
|
|
517
|
+
{({ formProps, submitting }) => (
|
|
518
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
519
|
+
<input />
|
|
520
|
+
</form>
|
|
521
|
+
)}
|
|
522
|
+
</Form>
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
const FormComponent2 = () => (
|
|
526
|
+
<>
|
|
527
|
+
<Form onSubmit={() => {}}>
|
|
528
|
+
{({ formProps, submitting }) => (
|
|
529
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
530
|
+
<input />
|
|
531
|
+
</form>
|
|
532
|
+
)}
|
|
533
|
+
</Form>
|
|
534
|
+
</>
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
class FormComponent3 extends React.Component {
|
|
538
|
+
render() {
|
|
539
|
+
return (
|
|
540
|
+
<Form onSubmit={() => {}}>
|
|
541
|
+
{({ formProps, submitting }) => (
|
|
542
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
543
|
+
<input />
|
|
544
|
+
</form>
|
|
545
|
+
)}
|
|
546
|
+
</Form>
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const formElement = (
|
|
552
|
+
<Form onSubmit={() => {}}>
|
|
553
|
+
{({ formProps, submitting }) => (
|
|
554
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
555
|
+
<input />
|
|
556
|
+
</form>
|
|
557
|
+
)}
|
|
558
|
+
</Form>
|
|
559
|
+
);
|
|
560
|
+
`,
|
|
561
|
+
`
|
|
562
|
+
import React from 'react';
|
|
563
|
+
import Form from '@atlaskit/form';
|
|
564
|
+
|
|
565
|
+
const FormComponent1 = () => (
|
|
566
|
+
<Form onSubmit={() => {}}>
|
|
567
|
+
{({ formProps, submitting }) => (
|
|
568
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
569
|
+
<input />
|
|
570
|
+
</form>
|
|
571
|
+
)}
|
|
572
|
+
</Form>
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
const FormComponent2 = () => (
|
|
576
|
+
<>
|
|
577
|
+
<Form onSubmit={() => {}}>
|
|
578
|
+
{({ formProps, submitting }) => (
|
|
579
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
580
|
+
<input />
|
|
581
|
+
</form>
|
|
582
|
+
)}
|
|
583
|
+
</Form>
|
|
584
|
+
</>
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
class FormComponent3 extends React.Component {
|
|
588
|
+
render() {
|
|
589
|
+
return (
|
|
590
|
+
<Form onSubmit={() => {}}>
|
|
591
|
+
{({ formProps, submitting }) => (
|
|
592
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
593
|
+
<input />
|
|
594
|
+
</form>
|
|
595
|
+
)}
|
|
596
|
+
</Form>
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const formElement = (
|
|
602
|
+
<Form onSubmit={() => {}}>
|
|
603
|
+
{({ formProps, submitting }) => (
|
|
604
|
+
<form {...formProps} foo="bar" baz="qux">
|
|
605
|
+
<input />
|
|
606
|
+
</form>
|
|
607
|
+
)}
|
|
608
|
+
</Form>
|
|
609
|
+
);
|
|
610
|
+
`,
|
|
611
|
+
'should not migrate when anything other than `formProps` is an arg in child function',
|
|
612
|
+
);
|
|
613
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type API,
|
|
3
|
+
type FileInfo,
|
|
4
|
+
type JSCodeshift,
|
|
5
|
+
type JSXElement,
|
|
6
|
+
type Options,
|
|
7
|
+
type Property,
|
|
8
|
+
} from 'jscodeshift';
|
|
9
|
+
import { type Collection } from 'jscodeshift/src/Collection';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
addJSXAttributeToJSXElement,
|
|
13
|
+
getImportDeclarationCollection,
|
|
14
|
+
getImportDefaultSpecifierCollection,
|
|
15
|
+
getImportDefaultSpecifierName,
|
|
16
|
+
hasImportDeclaration,
|
|
17
|
+
} from './utils/helpers';
|
|
18
|
+
|
|
19
|
+
const importPath = '@atlaskit/form';
|
|
20
|
+
|
|
21
|
+
const convertToSimpleForm = (j: JSCodeshift, collection: Collection<any>) => {
|
|
22
|
+
const importDeclarationCollection = getImportDeclarationCollection(j, collection, importPath);
|
|
23
|
+
const defaultImport = getImportDefaultSpecifierCollection(j, importDeclarationCollection);
|
|
24
|
+
const defaultImportName = getImportDefaultSpecifierName(defaultImport);
|
|
25
|
+
|
|
26
|
+
// if no default import is present, exit early
|
|
27
|
+
if (defaultImportName === null) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
collection.findJSXElements(defaultImportName).forEach((jsxElementPath) => {
|
|
32
|
+
const node = jsxElementPath.node;
|
|
33
|
+
// If no children, exit early
|
|
34
|
+
if (!node.children || node.children.length === 0) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// if component but child is not a function, exit early
|
|
39
|
+
const children = node.children.filter((child) => child.type === 'JSXExpressionContainer');
|
|
40
|
+
if (children.length === 0 || children.length > 1) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// If expression child is not a function, exit early
|
|
45
|
+
const childFunction = children[0];
|
|
46
|
+
if (
|
|
47
|
+
childFunction.type !== 'JSXExpressionContainer' ||
|
|
48
|
+
childFunction.expression.type !== 'ArrowFunctionExpression'
|
|
49
|
+
) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// if function child but more than just `fieldProps`, exit early
|
|
54
|
+
const args = childFunction.expression.params
|
|
55
|
+
.filter((arg) => arg.type === 'ObjectPattern' && 'properties' in arg)
|
|
56
|
+
.flatMap((arg) => arg.properties)
|
|
57
|
+
.filter((property) => property.type === 'ObjectProperty');
|
|
58
|
+
if (
|
|
59
|
+
args.length !== 1 ||
|
|
60
|
+
args[0].key.type !== 'Identifier' ||
|
|
61
|
+
args[0].value.type !== 'Identifier'
|
|
62
|
+
) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (args[0].key.name !== 'formProps') {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// This is the name that is used within, e.g. if something like
|
|
70
|
+
// `{({ formProps: fooBar }) => ()}` is used, this will be `fooBar`
|
|
71
|
+
const argName = args[0].value.name;
|
|
72
|
+
|
|
73
|
+
// get HTML form inside the child function
|
|
74
|
+
const body = childFunction.expression.body;
|
|
75
|
+
let htmlForm: JSXElement | null = null;
|
|
76
|
+
const q: any[] = [body];
|
|
77
|
+
while (q.length > 0) {
|
|
78
|
+
// pop child from end of q
|
|
79
|
+
const child = q.pop();
|
|
80
|
+
// if children, add children to q
|
|
81
|
+
if (!child || child.type !== 'JSXElement') {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
child.children?.forEach((el: any) => q.push(el));
|
|
85
|
+
// if child is a JSXElement with an openingElement of `form`, save to htmlForm and exit loop
|
|
86
|
+
if (child.type === 'JSXElement' && child.openingElement.name.name === 'form') {
|
|
87
|
+
htmlForm = child;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// if no HTML form, exit early
|
|
93
|
+
if (htmlForm === null) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// make new object and add each attribute on HTML `form` that is not a spread of `formProps` to new object
|
|
98
|
+
let otherSpreadPropsSeen = false;
|
|
99
|
+
// We are required to do it this way instead of a map to make the types work correctly.
|
|
100
|
+
const nonFormPropsAttributes: Property[] = [];
|
|
101
|
+
htmlForm?.openingElement?.attributes?.forEach((attr) => {
|
|
102
|
+
if (otherSpreadPropsSeen) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (attr.type === 'JSXSpreadAttribute') {
|
|
107
|
+
if (attr.argument.type === 'Identifier' && attr.argument.name === argName) {
|
|
108
|
+
return;
|
|
109
|
+
} else {
|
|
110
|
+
otherSpreadPropsSeen = true;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (attr.name.type !== 'JSXIdentifier') {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (attr.value === undefined) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let value;
|
|
124
|
+
if (attr.value === null) {
|
|
125
|
+
value = j.nullLiteral();
|
|
126
|
+
} else {
|
|
127
|
+
value = attr.value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
nonFormPropsAttributes.push(j.property('init', attr.name, value));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// We don't know how to handle other spread props in the formProps object, so ignore it
|
|
134
|
+
if (otherSpreadPropsSeen) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (nonFormPropsAttributes.length !== 0) {
|
|
139
|
+
// make new attribute for parent `Form` default import called `formProps` and set new attribute to new object
|
|
140
|
+
const formPropsAttr = j.jsxAttribute(
|
|
141
|
+
j.jsxIdentifier('formProps'),
|
|
142
|
+
j.jsxExpressionContainer(j.objectExpression(nonFormPropsAttributes.filter(Boolean))),
|
|
143
|
+
);
|
|
144
|
+
addJSXAttributeToJSXElement(j, jsxElementPath, formPropsAttr, 1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// replace functional child with inner (all children of HTML `form`)
|
|
148
|
+
const htmlFormChildren = htmlForm.children?.filter((child) => child.type !== 'JSXText');
|
|
149
|
+
if (!htmlFormChildren) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
node.children.splice(0);
|
|
154
|
+
node.children.splice(0, 0, ...htmlFormChildren);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export default function transformer(fileInfo: FileInfo, { jscodeshift: j }: API, options: Options) {
|
|
161
|
+
const { source } = fileInfo;
|
|
162
|
+
const collection = j(source);
|
|
163
|
+
|
|
164
|
+
// If our component is not here, skip the file
|
|
165
|
+
if (!hasImportDeclaration(j, collection, importPath)) {
|
|
166
|
+
return source;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Convert form if possible
|
|
170
|
+
convertToSimpleForm(j, collection);
|
|
171
|
+
|
|
172
|
+
return collection.toSource(options.printOptions || { quote: 'single' });
|
|
173
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ASTPath,
|
|
3
|
+
CallExpression,
|
|
4
|
+
default as core,
|
|
5
|
+
Identifier,
|
|
6
|
+
ImportDeclaration,
|
|
7
|
+
ImportDefaultSpecifier,
|
|
8
|
+
ImportSpecifier,
|
|
9
|
+
JSCodeshift,
|
|
10
|
+
JSXAttribute,
|
|
11
|
+
JSXElement,
|
|
12
|
+
ObjectProperty,
|
|
13
|
+
StringLiteral,
|
|
14
|
+
} from 'jscodeshift';
|
|
15
|
+
import { type Collection } from 'jscodeshift/src/Collection';
|
|
16
|
+
|
|
17
|
+
import { addCommentToStartOfFile, getNamedSpecifier } from '@atlaskit/codemod-utils';
|
|
18
|
+
|
|
19
|
+
export function hasImportDeclaration(
|
|
20
|
+
j: JSCodeshift,
|
|
21
|
+
collection: Collection<any>,
|
|
22
|
+
importPath: string,
|
|
23
|
+
) {
|
|
24
|
+
return getImportDeclarationCollection(j, collection, importPath).length > 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getImportDeclarationCollection(
|
|
28
|
+
j: JSCodeshift,
|
|
29
|
+
collection: Collection<any>,
|
|
30
|
+
importPath: string,
|
|
31
|
+
) {
|
|
32
|
+
return collection
|
|
33
|
+
.find(j.ImportDeclaration)
|
|
34
|
+
.filter((importDeclarationPath) => importDeclarationPath.node.source.value === importPath);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function hasDynamicImport(j: JSCodeshift, collection: Collection<any>, importPath: string) {
|
|
38
|
+
return getDynamicImportCollection(j, collection, importPath).length > 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getDynamicImportCollection(
|
|
42
|
+
j: JSCodeshift,
|
|
43
|
+
collection: Collection<any>,
|
|
44
|
+
importPath: string,
|
|
45
|
+
) {
|
|
46
|
+
return collection.find(j.CallExpression).filter((callExpressionPath) => {
|
|
47
|
+
const { callee, arguments: callExpressionArguments } = callExpressionPath.node;
|
|
48
|
+
|
|
49
|
+
return !!(
|
|
50
|
+
isCallExpressionCalleeImportType(callee) &&
|
|
51
|
+
isCallExpressionArgumentStringLiteralType(callExpressionArguments) &&
|
|
52
|
+
isCallExpressionArgumentValueMatches(callExpressionArguments[0], j, importPath)
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function isCallExpressionCalleeImportType(callee: CallExpression['callee']) {
|
|
57
|
+
return callee && callee.type === 'Import';
|
|
58
|
+
}
|
|
59
|
+
function isCallExpressionArgumentStringLiteralType(
|
|
60
|
+
callExpressionArguments: CallExpression['arguments'],
|
|
61
|
+
) {
|
|
62
|
+
return (
|
|
63
|
+
callExpressionArguments &&
|
|
64
|
+
callExpressionArguments.length &&
|
|
65
|
+
callExpressionArguments[0].type === 'StringLiteral'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
function isCallExpressionArgumentValueMatches(
|
|
69
|
+
callExpressionArgument: CallExpression['arguments'][0],
|
|
70
|
+
j: JSCodeshift,
|
|
71
|
+
value: string,
|
|
72
|
+
) {
|
|
73
|
+
return j(callExpressionArgument).some((path) => path.node.value === value);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getImportDefaultSpecifierCollection(
|
|
77
|
+
j: JSCodeshift,
|
|
78
|
+
importDeclarationCollection: Collection<ImportDeclaration>,
|
|
79
|
+
) {
|
|
80
|
+
return importDeclarationCollection.find(j.ImportDefaultSpecifier);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getImportSpecifierCollection(
|
|
84
|
+
j: JSCodeshift,
|
|
85
|
+
importDeclarationCollection: Collection<ImportDeclaration>,
|
|
86
|
+
importName: string,
|
|
87
|
+
) {
|
|
88
|
+
return importDeclarationCollection
|
|
89
|
+
.find(j.ImportSpecifier)
|
|
90
|
+
.filter((importSpecifierPath) => importSpecifierPath.node.imported.name === importName);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function getImportDefaultSpecifierName(
|
|
94
|
+
importSpecifierCollection: Collection<ImportDefaultSpecifier>,
|
|
95
|
+
) {
|
|
96
|
+
if (importSpecifierCollection.length === 0) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return importSpecifierCollection.nodes()[0]!.local!.name;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getImportSpecifierName(importSpecifierCollection: Collection<ImportSpecifier>) {
|
|
104
|
+
if (importSpecifierCollection.length === 0) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return importSpecifierCollection.nodes()[0]!.local!.name;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function isVariableDeclaratorIdentifierPresent(
|
|
112
|
+
j: JSCodeshift,
|
|
113
|
+
collection: Collection<any>,
|
|
114
|
+
variableName: string,
|
|
115
|
+
) {
|
|
116
|
+
return collection
|
|
117
|
+
.find(j.VariableDeclaration)
|
|
118
|
+
.find(j.VariableDeclarator)
|
|
119
|
+
.some((variableDeclaratorPath) => {
|
|
120
|
+
const { id } = variableDeclaratorPath.node;
|
|
121
|
+
|
|
122
|
+
return !!(id && id.type === 'Identifier' && (id as Identifier).name === variableName);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function isFunctionDeclarationIdentifierPresent(
|
|
127
|
+
j: JSCodeshift,
|
|
128
|
+
collection: Collection<any>,
|
|
129
|
+
variableName: string,
|
|
130
|
+
) {
|
|
131
|
+
return collection.find(j.FunctionDeclaration).some((functionDeclarationPath) => {
|
|
132
|
+
const { id } = functionDeclarationPath.node;
|
|
133
|
+
|
|
134
|
+
return !!(id && id.type === 'Identifier' && (id as Identifier).name === variableName);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function isClassDeclarationIdentifierPresent(
|
|
139
|
+
j: JSCodeshift,
|
|
140
|
+
collection: Collection<any>,
|
|
141
|
+
variableName: string,
|
|
142
|
+
) {
|
|
143
|
+
return collection.find(j.ClassDeclaration).some((classDeclarationPath) => {
|
|
144
|
+
const { id } = classDeclarationPath.node;
|
|
145
|
+
|
|
146
|
+
return !!(id && id.type === 'Identifier' && (id as Identifier).name === variableName);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function isImportDeclarationIdentifierPresent(
|
|
151
|
+
j: JSCodeshift,
|
|
152
|
+
collection: Collection<any>,
|
|
153
|
+
variableName: string,
|
|
154
|
+
) {
|
|
155
|
+
return collection
|
|
156
|
+
.find(j.ImportDeclaration)
|
|
157
|
+
.find(j.Identifier)
|
|
158
|
+
.some((identifierPath) => identifierPath.node.name === variableName);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function getJSXAttributesByName(
|
|
162
|
+
j: JSCodeshift,
|
|
163
|
+
jsxElementPath: ASTPath<JSXElement>,
|
|
164
|
+
attributeName: string,
|
|
165
|
+
): Collection<JSXAttribute> {
|
|
166
|
+
return j(jsxElementPath)
|
|
167
|
+
.find(j.JSXOpeningElement)
|
|
168
|
+
.find(j.JSXAttribute)
|
|
169
|
+
.filter((jsxAttributePath) =>
|
|
170
|
+
j(jsxAttributePath)
|
|
171
|
+
.find(j.JSXIdentifier)
|
|
172
|
+
.some((jsxIdentifierPath) => jsxIdentifierPath.node.name === attributeName),
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function getJSXSpreadIdentifierAttributesByName(
|
|
177
|
+
j: JSCodeshift,
|
|
178
|
+
collection: Collection<any>,
|
|
179
|
+
jsxElementPath: ASTPath<JSXElement>,
|
|
180
|
+
attributeName: string,
|
|
181
|
+
): Collection<ObjectProperty> | null {
|
|
182
|
+
const identifierCollection = j(jsxElementPath)
|
|
183
|
+
.find(j.JSXOpeningElement)
|
|
184
|
+
.find(j.JSXSpreadAttribute)
|
|
185
|
+
.filter((jsxSpreadAttributePath) => jsxSpreadAttributePath.node.argument.type === 'Identifier')
|
|
186
|
+
.find(j.Identifier);
|
|
187
|
+
|
|
188
|
+
if (identifierCollection.length === 0) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return collection
|
|
193
|
+
.find(j.VariableDeclarator)
|
|
194
|
+
.filter((variableDeclaratorPath) => {
|
|
195
|
+
const { id } = variableDeclaratorPath.node;
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
id.type === 'Identifier' &&
|
|
199
|
+
identifierCollection.some((identifierPath) => identifierPath.node.name === id.name)
|
|
200
|
+
);
|
|
201
|
+
})
|
|
202
|
+
.find(j.ObjectExpression)
|
|
203
|
+
.find(j.ObjectProperty)
|
|
204
|
+
.filter((objectPropertyPath) =>
|
|
205
|
+
j(objectPropertyPath)
|
|
206
|
+
.find(j.Identifier)
|
|
207
|
+
.some((identifierPath) => identifierPath.node.name === attributeName),
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function getJSXSpreadObjectExpressionAttributesByName(
|
|
212
|
+
j: JSCodeshift,
|
|
213
|
+
jsxElementPath: ASTPath<JSXElement>,
|
|
214
|
+
attributeName: string,
|
|
215
|
+
) {
|
|
216
|
+
return j(jsxElementPath)
|
|
217
|
+
.find(j.JSXOpeningElement)
|
|
218
|
+
.find(j.JSXSpreadAttribute)
|
|
219
|
+
.find(j.ObjectExpression)
|
|
220
|
+
.find(j.ObjectProperty)
|
|
221
|
+
.filter((objectPropertyPath) =>
|
|
222
|
+
j(objectPropertyPath)
|
|
223
|
+
.find(j.Identifier)
|
|
224
|
+
.some((identifierPath) => identifierPath.node.name === attributeName),
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export const createRemoveFuncFor =
|
|
229
|
+
(component: string, importName: string, prop: string, comment?: string) =>
|
|
230
|
+
(j: core.JSCodeshift, source: Collection<Node>) => {
|
|
231
|
+
const specifier = getNamedSpecifier(j, source, component, importName);
|
|
232
|
+
|
|
233
|
+
if (!specifier) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
source.findJSXElements(specifier).forEach((element) => {
|
|
238
|
+
getJSXAttributesByName(j, element, prop).forEach((attribute: any) => {
|
|
239
|
+
j(attribute).remove();
|
|
240
|
+
if (comment) {
|
|
241
|
+
addCommentToStartOfFile({ j, base: source, message: comment });
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export const getJSXAttributeByName = (
|
|
248
|
+
j: JSCodeshift,
|
|
249
|
+
jsxElementPath: ASTPath<JSXElement>,
|
|
250
|
+
attributeName: string,
|
|
251
|
+
): JSXAttribute | undefined => {
|
|
252
|
+
const attributes: JSXAttribute[] = j(jsxElementPath).find(j.JSXAttribute).nodes();
|
|
253
|
+
|
|
254
|
+
return attributes?.find((attr) => attr.name && attr.name.name === attributeName);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export const addJSXAttributeToJSXElement = (
|
|
258
|
+
j: JSCodeshift,
|
|
259
|
+
jsxElementPath: ASTPath<JSXElement>,
|
|
260
|
+
jsxAttribute: JSXAttribute,
|
|
261
|
+
limit?: number,
|
|
262
|
+
) => {
|
|
263
|
+
j(jsxElementPath)
|
|
264
|
+
.find(j.JSXOpeningElement)
|
|
265
|
+
.forEach((openingElement, i) => {
|
|
266
|
+
if (!limit || i < limit) {
|
|
267
|
+
openingElement.node.attributes?.push(jsxAttribute);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
export const removeJSXAttributeByName = (
|
|
273
|
+
j: JSCodeshift,
|
|
274
|
+
jsxElementPath: ASTPath<JSXElement>,
|
|
275
|
+
attrName: string,
|
|
276
|
+
) => {
|
|
277
|
+
const attributes = getJSXAttributes(jsxElementPath);
|
|
278
|
+
const attr = getJSXAttributeByName(j, jsxElementPath, attrName);
|
|
279
|
+
if (attr) {
|
|
280
|
+
attributes?.splice(attributes.indexOf(attr), 1);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export const getJSXAttributes = (jsxElementPath: ASTPath<JSXElement>) =>
|
|
285
|
+
jsxElementPath.node.openingElement.attributes as JSXAttribute[];
|
|
286
|
+
|
|
287
|
+
export const removeJSXAttributeObjectPropertyByName = (
|
|
288
|
+
j: JSCodeshift,
|
|
289
|
+
jsxElementPath: ASTPath<JSXElement>,
|
|
290
|
+
attrName: string,
|
|
291
|
+
propertyToRemove: string,
|
|
292
|
+
) => {
|
|
293
|
+
const attr = getJSXAttributeByName(j, jsxElementPath, attrName);
|
|
294
|
+
|
|
295
|
+
if (!attr) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const attrCollection = getJSXAttributesByName(j, jsxElementPath, attrName);
|
|
300
|
+
|
|
301
|
+
const removeMatchingNodes = (p: ASTPath<Identifier | StringLiteral>) => {
|
|
302
|
+
const name = p.node.type === 'Identifier' ? p.node?.name : p.node?.value;
|
|
303
|
+
// Need to account for quoted properties
|
|
304
|
+
const nameMatches = propertyToRemove.match(new RegExp(`['"]?${name}['"]?`));
|
|
305
|
+
// This will otherwise try to remove values of properties since they are literals
|
|
306
|
+
const isKey = p.parent.value?.type === 'ObjectProperty';
|
|
307
|
+
// Sorry about all the parents. This is the easiest way to get the name
|
|
308
|
+
// of the attribute name. And I always know the depth of the object
|
|
309
|
+
// property here.
|
|
310
|
+
const parentNameMatches = attrName === p.parent.parent.parent.parent.node?.name?.name;
|
|
311
|
+
if (isKey && nameMatches && parentNameMatches) {
|
|
312
|
+
j(p.parent).remove();
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// Remove all the now migrated object properties
|
|
317
|
+
const objectProperties = attrCollection.find(j.ObjectProperty);
|
|
318
|
+
objectProperties.find(j.Identifier).forEach(removeMatchingNodes);
|
|
319
|
+
objectProperties.find(j.StringLiteral).forEach(removeMatchingNodes);
|
|
320
|
+
|
|
321
|
+
// @ts-ignore -- Property 'expression' does not exist on type 'LiteralKind | JSXElement | JSXExpressionContainer | JSXFragment'. Property 'expression' does not exist on type 'Literal'.
|
|
322
|
+
const attrProperties = attr.value?.expression.properties;
|
|
323
|
+
|
|
324
|
+
if (attrProperties && attrProperties?.length === 0) {
|
|
325
|
+
removeJSXAttributeByName(j, jsxElementPath, attrName);
|
|
326
|
+
}
|
|
327
|
+
};
|
package/dist/cjs/form.js
CHANGED
|
@@ -7,11 +7,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
});
|
|
8
8
|
exports.IsDisabledContext = exports.FormContext = void 0;
|
|
9
9
|
exports.default = Form;
|
|
10
|
+
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
10
11
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
11
12
|
var _react = _interopRequireWildcard(require("react"));
|
|
12
13
|
var _finalForm = require("final-form");
|
|
13
14
|
var _finalFormFocus = _interopRequireDefault(require("final-form-focus"));
|
|
14
15
|
var _set = _interopRequireDefault(require("lodash/set"));
|
|
16
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
15
17
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
16
18
|
/**
|
|
17
19
|
* __Form context__
|
|
@@ -37,9 +39,11 @@ var FormContext = exports.FormContext = /*#__PURE__*/(0, _react.createContext)({
|
|
|
37
39
|
*/
|
|
38
40
|
var IsDisabledContext = exports.IsDisabledContext = /*#__PURE__*/(0, _react.createContext)(false);
|
|
39
41
|
function Form(props) {
|
|
42
|
+
var userProvidedFormProps = props.formProps,
|
|
43
|
+
onSubmit = props.onSubmit;
|
|
40
44
|
var formRef = (0, _react.useRef)(null);
|
|
41
|
-
var onSubmitRef = (0, _react.useRef)(
|
|
42
|
-
onSubmitRef.current =
|
|
45
|
+
var onSubmitRef = (0, _react.useRef)(onSubmit);
|
|
46
|
+
onSubmitRef.current = onSubmit;
|
|
43
47
|
var _useState = (0, _react.useState)(function () {
|
|
44
48
|
// Types here would break the existing API
|
|
45
49
|
var finalForm = (0, _finalForm.createForm)({
|
|
@@ -137,14 +141,17 @@ function Form(props) {
|
|
|
137
141
|
subscribe: form.subscribe
|
|
138
142
|
};
|
|
139
143
|
}, [registerField, getCurrentValue, form.subscribe]);
|
|
144
|
+
|
|
145
|
+
// Abstracting so we can use the same for both rendering patterns
|
|
146
|
+
var formProps = {
|
|
147
|
+
onKeyDown: handleKeyDown,
|
|
148
|
+
onSubmit: handleSubmit,
|
|
149
|
+
ref: formRef
|
|
150
|
+
};
|
|
140
151
|
var childrenContent = function () {
|
|
141
152
|
if (typeof children === 'function') {
|
|
142
153
|
var result = children.length > 0 ? children({
|
|
143
|
-
formProps:
|
|
144
|
-
onSubmit: handleSubmit,
|
|
145
|
-
ref: formRef,
|
|
146
|
-
onKeyDown: handleKeyDown
|
|
147
|
-
},
|
|
154
|
+
formProps: formProps,
|
|
148
155
|
dirty: dirty,
|
|
149
156
|
reset: handleReset,
|
|
150
157
|
submitting: submitting,
|
|
@@ -158,8 +165,9 @@ function Form(props) {
|
|
|
158
165
|
setFieldValue: form.change
|
|
159
166
|
}) : children();
|
|
160
167
|
return result === undefined ? null : result;
|
|
168
|
+
} else {
|
|
169
|
+
return (0, _platformFeatureFlags.fg)('platform_design-system-team_form-upgrade') ? /*#__PURE__*/_react.default.createElement("form", (0, _extends2.default)({}, formProps, userProvidedFormProps), children) : children;
|
|
161
170
|
}
|
|
162
|
-
return children;
|
|
163
171
|
}();
|
|
164
172
|
return /*#__PURE__*/_react.default.createElement(FormContext.Provider, {
|
|
165
173
|
value: FormContextValue
|
package/dist/es2019/form.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
1
2
|
import React, { createContext, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
3
|
import { createForm } from 'final-form';
|
|
3
4
|
import createDecorator from 'final-form-focus';
|
|
4
5
|
import set from 'lodash/set';
|
|
6
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
7
|
/**
|
|
6
8
|
* __Form context__
|
|
7
9
|
*
|
|
@@ -24,9 +26,13 @@ export const FormContext = /*#__PURE__*/createContext({
|
|
|
24
26
|
*/
|
|
25
27
|
export const IsDisabledContext = /*#__PURE__*/createContext(false);
|
|
26
28
|
export default function Form(props) {
|
|
29
|
+
const {
|
|
30
|
+
formProps: userProvidedFormProps,
|
|
31
|
+
onSubmit
|
|
32
|
+
} = props;
|
|
27
33
|
const formRef = useRef(null);
|
|
28
|
-
const onSubmitRef = useRef(
|
|
29
|
-
onSubmitRef.current =
|
|
34
|
+
const onSubmitRef = useRef(onSubmit);
|
|
35
|
+
onSubmitRef.current = onSubmit;
|
|
30
36
|
const [form] = useState(() => {
|
|
31
37
|
// Types here would break the existing API
|
|
32
38
|
const finalForm = createForm({
|
|
@@ -116,14 +122,17 @@ export default function Form(props) {
|
|
|
116
122
|
subscribe: form.subscribe
|
|
117
123
|
};
|
|
118
124
|
}, [registerField, getCurrentValue, form.subscribe]);
|
|
125
|
+
|
|
126
|
+
// Abstracting so we can use the same for both rendering patterns
|
|
127
|
+
const formProps = {
|
|
128
|
+
onKeyDown: handleKeyDown,
|
|
129
|
+
onSubmit: handleSubmit,
|
|
130
|
+
ref: formRef
|
|
131
|
+
};
|
|
119
132
|
const childrenContent = (() => {
|
|
120
133
|
if (typeof children === 'function') {
|
|
121
134
|
const result = children.length > 0 ? children({
|
|
122
|
-
formProps
|
|
123
|
-
onSubmit: handleSubmit,
|
|
124
|
-
ref: formRef,
|
|
125
|
-
onKeyDown: handleKeyDown
|
|
126
|
-
},
|
|
135
|
+
formProps,
|
|
127
136
|
dirty,
|
|
128
137
|
reset: handleReset,
|
|
129
138
|
submitting,
|
|
@@ -133,8 +142,9 @@ export default function Form(props) {
|
|
|
133
142
|
setFieldValue: form.change
|
|
134
143
|
}) : children();
|
|
135
144
|
return result === undefined ? null : result;
|
|
145
|
+
} else {
|
|
146
|
+
return fg('platform_design-system-team_form-upgrade') ? /*#__PURE__*/React.createElement("form", _extends({}, formProps, userProvidedFormProps), children) : children;
|
|
136
147
|
}
|
|
137
|
-
return children;
|
|
138
148
|
})();
|
|
139
149
|
return /*#__PURE__*/React.createElement(FormContext.Provider, {
|
|
140
150
|
value: FormContextValue
|
package/dist/esm/form.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
1
2
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
3
|
import React, { createContext, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
4
|
import { createForm } from 'final-form';
|
|
4
5
|
import createDecorator from 'final-form-focus';
|
|
5
6
|
import set from 'lodash/set';
|
|
7
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
6
8
|
/**
|
|
7
9
|
* __Form context__
|
|
8
10
|
*
|
|
@@ -27,9 +29,11 @@ export var FormContext = /*#__PURE__*/createContext({
|
|
|
27
29
|
*/
|
|
28
30
|
export var IsDisabledContext = /*#__PURE__*/createContext(false);
|
|
29
31
|
export default function Form(props) {
|
|
32
|
+
var userProvidedFormProps = props.formProps,
|
|
33
|
+
onSubmit = props.onSubmit;
|
|
30
34
|
var formRef = useRef(null);
|
|
31
|
-
var onSubmitRef = useRef(
|
|
32
|
-
onSubmitRef.current =
|
|
35
|
+
var onSubmitRef = useRef(onSubmit);
|
|
36
|
+
onSubmitRef.current = onSubmit;
|
|
33
37
|
var _useState = useState(function () {
|
|
34
38
|
// Types here would break the existing API
|
|
35
39
|
var finalForm = createForm({
|
|
@@ -127,14 +131,17 @@ export default function Form(props) {
|
|
|
127
131
|
subscribe: form.subscribe
|
|
128
132
|
};
|
|
129
133
|
}, [registerField, getCurrentValue, form.subscribe]);
|
|
134
|
+
|
|
135
|
+
// Abstracting so we can use the same for both rendering patterns
|
|
136
|
+
var formProps = {
|
|
137
|
+
onKeyDown: handleKeyDown,
|
|
138
|
+
onSubmit: handleSubmit,
|
|
139
|
+
ref: formRef
|
|
140
|
+
};
|
|
130
141
|
var childrenContent = function () {
|
|
131
142
|
if (typeof children === 'function') {
|
|
132
143
|
var result = children.length > 0 ? children({
|
|
133
|
-
formProps:
|
|
134
|
-
onSubmit: handleSubmit,
|
|
135
|
-
ref: formRef,
|
|
136
|
-
onKeyDown: handleKeyDown
|
|
137
|
-
},
|
|
144
|
+
formProps: formProps,
|
|
138
145
|
dirty: dirty,
|
|
139
146
|
reset: handleReset,
|
|
140
147
|
submitting: submitting,
|
|
@@ -148,8 +155,9 @@ export default function Form(props) {
|
|
|
148
155
|
setFieldValue: form.change
|
|
149
156
|
}) : children();
|
|
150
157
|
return result === undefined ? null : result;
|
|
158
|
+
} else {
|
|
159
|
+
return fg('platform_design-system-team_form-upgrade') ? /*#__PURE__*/React.createElement("form", _extends({}, formProps, userProvidedFormProps), children) : children;
|
|
151
160
|
}
|
|
152
|
-
return children;
|
|
153
161
|
}();
|
|
154
162
|
return /*#__PURE__*/React.createElement(FormContext.Provider, {
|
|
155
163
|
value: FormContextValue
|
package/dist/types/form.d.ts
CHANGED
|
@@ -35,12 +35,25 @@ type FormChildrenArgs<FormValues> = {
|
|
|
35
35
|
setFieldValue: (name: string, value: any) => void;
|
|
36
36
|
reset: (initialValues?: FormValues) => void;
|
|
37
37
|
};
|
|
38
|
+
type ExcludeReservedFormProps = {
|
|
39
|
+
onKeyDown?: never;
|
|
40
|
+
onSubmit?: never;
|
|
41
|
+
ref?: never;
|
|
42
|
+
};
|
|
38
43
|
export interface FormProps<FormValues> {
|
|
39
44
|
/**
|
|
40
45
|
* The contents rendered inside of the form. This is a function where the props will be passed from the form. The function props you can access are `dirty`, `submitting` and `disabled`.
|
|
41
46
|
* You can read more about these props in [react-final form documentation](https://final-form.org/docs/final-form/types/FormState).
|
|
42
47
|
*/
|
|
43
48
|
children: ((args: FormChildrenArgs<FormValues>) => ReactNode) | (() => void) | ReactNode;
|
|
49
|
+
/**
|
|
50
|
+
* When `Form` renders JSX children directly and not using a function to
|
|
51
|
+
* spread `formProps` manually, the properties in this `formProps` prop will
|
|
52
|
+
* be spread on an internally rendered HTML `form` element.
|
|
53
|
+
*/
|
|
54
|
+
formProps?: {
|
|
55
|
+
[x: string]: any;
|
|
56
|
+
} & ExcludeReservedFormProps;
|
|
44
57
|
/**
|
|
45
58
|
* Event handler called when the form is submitted. Fields must be free of validation errors.
|
|
46
59
|
*/
|
|
@@ -35,12 +35,25 @@ type FormChildrenArgs<FormValues> = {
|
|
|
35
35
|
setFieldValue: (name: string, value: any) => void;
|
|
36
36
|
reset: (initialValues?: FormValues) => void;
|
|
37
37
|
};
|
|
38
|
+
type ExcludeReservedFormProps = {
|
|
39
|
+
onKeyDown?: never;
|
|
40
|
+
onSubmit?: never;
|
|
41
|
+
ref?: never;
|
|
42
|
+
};
|
|
38
43
|
export interface FormProps<FormValues> {
|
|
39
44
|
/**
|
|
40
45
|
* The contents rendered inside of the form. This is a function where the props will be passed from the form. The function props you can access are `dirty`, `submitting` and `disabled`.
|
|
41
46
|
* You can read more about these props in [react-final form documentation](https://final-form.org/docs/final-form/types/FormState).
|
|
42
47
|
*/
|
|
43
48
|
children: ((args: FormChildrenArgs<FormValues>) => ReactNode) | (() => void) | ReactNode;
|
|
49
|
+
/**
|
|
50
|
+
* When `Form` renders JSX children directly and not using a function to
|
|
51
|
+
* spread `formProps` manually, the properties in this `formProps` prop will
|
|
52
|
+
* be spread on an internally rendered HTML `form` element.
|
|
53
|
+
*/
|
|
54
|
+
formProps?: {
|
|
55
|
+
[x: string]: any;
|
|
56
|
+
} & ExcludeReservedFormProps;
|
|
44
57
|
/**
|
|
45
58
|
* Event handler called when the form is submitted. Fields must be free of validation errors.
|
|
46
59
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/form",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.2.0",
|
|
4
4
|
"description": "A form allows users to input information.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"@atlaskit/banner": "^14.0.0",
|
|
50
50
|
"@atlaskit/button": "^23.4.0",
|
|
51
51
|
"@atlaskit/checkbox": "^17.1.0",
|
|
52
|
+
"@atlaskit/codemod-utils": "^4.2.0",
|
|
52
53
|
"@atlaskit/datetime-picker": "^17.0.0",
|
|
53
54
|
"@atlaskit/docs": "^11.0.0",
|
|
54
55
|
"@atlaskit/link": "^3.2.0",
|
|
@@ -63,12 +64,13 @@
|
|
|
63
64
|
"@atlaskit/toggle": "^15.1.0",
|
|
64
65
|
"@atlaskit/visual-regression": "workspace:^",
|
|
65
66
|
"@atlassian/feature-flags-test-utils": "^0.3.0",
|
|
66
|
-
"@atlassian/ssr-tests": "^0.
|
|
67
|
+
"@atlassian/ssr-tests": "^0.3.0",
|
|
67
68
|
"@testing-library/react": "^13.4.0",
|
|
68
69
|
"@testing-library/react-hooks": "^8.0.1",
|
|
69
70
|
"@testing-library/user-event": "^14.4.3",
|
|
70
71
|
"@types/final-form-focus": "^1.1.1",
|
|
71
72
|
"jest-in-case": "^1.0.2",
|
|
73
|
+
"jscodeshift": "^17.0.0",
|
|
72
74
|
"react-dom": "^18.2.0"
|
|
73
75
|
},
|
|
74
76
|
"keywords": [
|
|
@@ -123,6 +125,9 @@
|
|
|
123
125
|
"platform-feature-flags": {
|
|
124
126
|
"platform_dst_form_screenreader_message_fix": {
|
|
125
127
|
"type": "boolean"
|
|
128
|
+
},
|
|
129
|
+
"platform_design-system-team_form-upgrade": {
|
|
130
|
+
"type": "boolean"
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
}
|