@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 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)(props.onSubmit);
42
- onSubmitRef.current = props.onSubmit;
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
@@ -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(props.onSubmit);
29
- onSubmitRef.current = props.onSubmit;
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(props.onSubmit);
32
- onSubmitRef.current = props.onSubmit;
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
@@ -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.1.1",
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.2.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
  }