@atlaskit/form 14.1.0 → 14.2.1

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,18 @@
1
1
  # @atlaskit/form
2
2
 
3
+ ## 14.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 14.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`10985771cb1e5`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/10985771cb1e5) -
14
+ Add props to form component for more transparent prop application to underlying HTML form element.
15
+
3
16
  ## 14.1.0
4
17
 
5
18
  ### Minor Changes
@@ -401,6 +401,282 @@ const formElement = (
401
401
  'should convert from function with single prop on form',
402
402
  );
403
403
 
404
+ describe('Migrate existing props', () => {
405
+ defineInlineTest(
406
+ { default: transformer, parser: 'tsx' },
407
+ {},
408
+ `
409
+ import React from 'react';
410
+ import Form from '@atlaskit/form';
411
+
412
+ const FormComponent1 = () => (
413
+ <Form onSubmit={() => {}}>
414
+ {({ formProps }) => (
415
+ <form {...formProps} autocomplete="off" id="foo" name="bar" noValidate>
416
+ <input />
417
+ </form>
418
+ )}
419
+ </Form>
420
+ );
421
+
422
+ const FormComponent2 = () => (
423
+ <>
424
+ <Form onSubmit={() => {}}>
425
+ {({ formProps }) => (
426
+ <form {...formProps} autocomplete="off" id="foo" name="bar" noValidate>
427
+ <input />
428
+ </form>
429
+ )}
430
+ </Form>
431
+ </>
432
+ );
433
+
434
+ class FormComponent3 extends React.Component {
435
+ render() {
436
+ return (
437
+ <Form onSubmit={() => {}}>
438
+ {({ formProps }) => (
439
+ <form {...formProps} autocomplete="off" id="foo" name="bar" noValidate>
440
+ <input />
441
+ </form>
442
+ )}
443
+ </Form>
444
+ );
445
+ }
446
+ }
447
+
448
+ const formElement = (
449
+ <Form onSubmit={() => {}}>
450
+ {({ formProps }) => (
451
+ <form {...formProps} autocomplete="off" id="foo" name="bar" noValidate>
452
+ <input />
453
+ </form>
454
+ )}
455
+ </Form>
456
+ );
457
+ `,
458
+ `
459
+ import React from 'react';
460
+ import Form from '@atlaskit/form';
461
+
462
+ const FormComponent1 = () => (
463
+ <Form onSubmit={() => {}} autocomplete="off" id="foo" name="bar" noValidate><input /></Form>
464
+ );
465
+
466
+ const FormComponent2 = () => (
467
+ <>
468
+ <Form onSubmit={() => {}} autocomplete="off" id="foo" name="bar" noValidate><input /></Form>
469
+ </>
470
+ );
471
+
472
+ class FormComponent3 extends React.Component {
473
+ render() {
474
+ return (<Form onSubmit={() => {}} autocomplete="off" id="foo" name="bar" noValidate><input /></Form>);
475
+ }
476
+ }
477
+
478
+ const formElement = (
479
+ <Form onSubmit={() => {}} autocomplete="off" id="foo" name="bar" noValidate><input /></Form>
480
+ );
481
+ `,
482
+ 'should migrate existing props on `form` into their respective props on `Form`',
483
+ );
484
+
485
+ defineInlineTest(
486
+ { default: transformer, parser: 'tsx' },
487
+ {},
488
+ `
489
+ import React from 'react';
490
+ import Form from '@atlaskit/form';
491
+
492
+ const FormComponent1 = () => (
493
+ <Form onSubmit={() => {}}>
494
+ {({ formProps }) => (
495
+ <form {...formProps} aria-label="foo" aria-labelledby="bar">
496
+ <input />
497
+ </form>
498
+ )}
499
+ </Form>
500
+ );
501
+
502
+ const FormComponent2 = () => (
503
+ <>
504
+ <Form onSubmit={() => {}}>
505
+ {({ formProps }) => (
506
+ <form {...formProps} aria-label="foo" aria-labelledby="bar">
507
+ <input />
508
+ </form>
509
+ )}
510
+ </Form>
511
+ </>
512
+ );
513
+
514
+ class FormComponent3 extends React.Component {
515
+ render() {
516
+ return (
517
+ <Form onSubmit={() => {}}>
518
+ {({ formProps }) => (
519
+ <form {...formProps} aria-label="foo" aria-labelledby="bar">
520
+ <input />
521
+ </form>
522
+ )}
523
+ </Form>
524
+ );
525
+ }
526
+ }
527
+
528
+ const formElement = (
529
+ <Form onSubmit={() => {}}>
530
+ {({ formProps }) => (
531
+ <form {...formProps} aria-label="foo" aria-labelledby="bar">
532
+ <input />
533
+ </form>
534
+ )}
535
+ </Form>
536
+ );
537
+ `,
538
+ `
539
+ import React from 'react';
540
+ import Form from '@atlaskit/form';
541
+
542
+ const FormComponent1 = () => (
543
+ <Form onSubmit={() => {}} label="foo" labelId="bar"><input /></Form>
544
+ );
545
+
546
+ const FormComponent2 = () => (
547
+ <>
548
+ <Form onSubmit={() => {}} label="foo" labelId="bar"><input /></Form>
549
+ </>
550
+ );
551
+
552
+ class FormComponent3 extends React.Component {
553
+ render() {
554
+ return (<Form onSubmit={() => {}} label="foo" labelId="bar"><input /></Form>);
555
+ }
556
+ }
557
+
558
+ const formElement = (
559
+ <Form onSubmit={() => {}} label="foo" labelId="bar"><input /></Form>
560
+ );
561
+ `,
562
+ 'should migrate existing props on `form` into different names',
563
+ );
564
+
565
+ defineInlineTest(
566
+ { default: transformer, parser: 'tsx' },
567
+ {},
568
+ `
569
+ import React from 'react';
570
+ import Form from '@atlaskit/form';
571
+
572
+ const FormComponent1 = () => (
573
+ <Form onSubmit={() => {}}>
574
+ {({ formProps }) => (
575
+ <form {...formProps} autocomplete="off" id="foo" name="bar" noValidate quu="qux">
576
+ <input />
577
+ </form>
578
+ )}
579
+ </Form>
580
+ );
581
+
582
+ const FormComponent2 = () => (
583
+ <>
584
+ <Form onSubmit={() => {}}>
585
+ {({ formProps }) => (
586
+ <form {...formProps} autocomplete="off" id="foo" name="bar" noValidate quu="qux">
587
+ <input />
588
+ </form>
589
+ )}
590
+ </Form>
591
+ </>
592
+ );
593
+
594
+ class FormComponent3 extends React.Component {
595
+ render() {
596
+ return (
597
+ <Form onSubmit={() => {}}>
598
+ {({ formProps }) => (
599
+ <form {...formProps} autocomplete="off" id="foo" name="bar" noValidate quu="qux">
600
+ <input />
601
+ </form>
602
+ )}
603
+ </Form>
604
+ );
605
+ }
606
+ }
607
+
608
+ const formElement = (
609
+ <Form onSubmit={() => {}}>
610
+ {({ formProps }) => (
611
+ <form {...formProps} autocomplete="off" id="foo" name="bar" noValidate quu="qux">
612
+ <input />
613
+ </form>
614
+ )}
615
+ </Form>
616
+ );
617
+ `,
618
+ `
619
+ import React from 'react';
620
+ import Form from '@atlaskit/form';
621
+
622
+ const FormComponent1 = () => (
623
+ <Form
624
+ onSubmit={() => {}}
625
+ formProps={{
626
+ quu: "qux"
627
+ }}
628
+ autocomplete="off"
629
+ id="foo"
630
+ name="bar"
631
+ noValidate><input /></Form>
632
+ );
633
+
634
+ const FormComponent2 = () => (
635
+ <>
636
+ <Form
637
+ onSubmit={() => {}}
638
+ formProps={{
639
+ quu: "qux"
640
+ }}
641
+ autocomplete="off"
642
+ id="foo"
643
+ name="bar"
644
+ noValidate><input /></Form>
645
+ </>
646
+ );
647
+
648
+ class FormComponent3 extends React.Component {
649
+ render() {
650
+ return (
651
+ <Form
652
+ onSubmit={() => {}}
653
+ formProps={{
654
+ quu: "qux"
655
+ }}
656
+ autocomplete="off"
657
+ id="foo"
658
+ name="bar"
659
+ noValidate><input /></Form>
660
+ );
661
+ }
662
+ }
663
+
664
+ const formElement = (
665
+ <Form
666
+ onSubmit={() => {}}
667
+ formProps={{
668
+ quu: "qux"
669
+ }}
670
+ autocomplete="off"
671
+ id="foo"
672
+ name="bar"
673
+ noValidate><input /></Form>
674
+ );
675
+ `,
676
+ 'should migrate existing props on `form` into their respective props on `Form` and also use `formProps` if needed',
677
+ );
678
+ });
679
+
404
680
  defineInlineTest(
405
681
  { default: transformer, parser: 'tsx' },
406
682
  {},
@@ -2,6 +2,7 @@ import {
2
2
  type API,
3
3
  type FileInfo,
4
4
  type JSCodeshift,
5
+ type JSXAttribute,
5
6
  type JSXElement,
6
7
  type Options,
7
8
  } from 'jscodeshift';
@@ -16,6 +17,14 @@ import {
16
17
  } from './utils/helpers';
17
18
 
18
19
  const importPath = '@atlaskit/form';
20
+ const EXISTING_FORM_ATTRIBUTES: Record<string, any> = {
21
+ autocomplete: 'autocomplete',
22
+ id: 'id',
23
+ 'aria-label': 'label',
24
+ 'aria-labelledby': 'labelId',
25
+ name: 'name',
26
+ noValidate: 'noValidate',
27
+ };
19
28
 
20
29
  const convertToSimpleForm = (j: JSCodeshift, collection: Collection<any>) => {
21
30
  const importDeclarationCollection = getImportDeclarationCollection(j, collection, importPath);
@@ -98,6 +107,8 @@ const convertToSimpleForm = (j: JSCodeshift, collection: Collection<any>) => {
98
107
  // We are required to do it this way instead of a map to make the types work correctly.
99
108
  // We also have to use `any` here and below because typing in this SUCKS.
100
109
  const nonFormPropsAttributes: any[] = [];
110
+ // These are the attributes that exist on `Form` that can migrate directly over.
111
+ const existingFormPropsAttributes: JSXAttribute[] = [];
101
112
  htmlForm?.openingElement?.attributes?.forEach((attr) => {
102
113
  if (otherSpreadPropsSeen) {
103
114
  return;
@@ -112,7 +123,9 @@ const convertToSimpleForm = (j: JSCodeshift, collection: Collection<any>) => {
112
123
  }
113
124
  }
114
125
 
115
- if (attr.name.type !== 'JSXIdentifier') {
126
+ const attrName = attr.name;
127
+
128
+ if (attrName.type !== 'JSXIdentifier') {
116
129
  return;
117
130
  }
118
131
 
@@ -120,6 +133,11 @@ const convertToSimpleForm = (j: JSCodeshift, collection: Collection<any>) => {
120
133
  return;
121
134
  }
122
135
 
136
+ if (Object.keys(EXISTING_FORM_ATTRIBUTES).includes(attrName.name)) {
137
+ existingFormPropsAttributes.push(attr);
138
+ return;
139
+ }
140
+
123
141
  let value: any;
124
142
  if (attr.value === null) {
125
143
  value = j.booleanLiteral(true) as any;
@@ -129,7 +147,7 @@ const convertToSimpleForm = (j: JSCodeshift, collection: Collection<any>) => {
129
147
  value = attr.value as any;
130
148
  }
131
149
 
132
- nonFormPropsAttributes.push(j.property('init', attr.name, value));
150
+ nonFormPropsAttributes.push(j.property('init', attrName, value));
133
151
  });
134
152
 
135
153
  // We don't know how to handle other spread props in the formProps object, so ignore it
@@ -145,6 +163,16 @@ const convertToSimpleForm = (j: JSCodeshift, collection: Collection<any>) => {
145
163
  );
146
164
  addJSXAttributeToJSXElement(j, jsxElementPath, formPropsAttr, 1);
147
165
  }
166
+ existingFormPropsAttributes.forEach((attr) => {
167
+ // add existing props to new `Form`
168
+ const fromName = attr.name.name;
169
+ if (typeof fromName !== 'string') {
170
+ return;
171
+ }
172
+ const toName = EXISTING_FORM_ATTRIBUTES[fromName];
173
+ attr.name.name = toName;
174
+ addJSXAttributeToJSXElement(j, jsxElementPath, attr, 1);
175
+ });
148
176
 
149
177
  // replace functional child with inner (all children of HTML `form`)
150
178
  const htmlFormChildren = htmlForm.children?.filter((child) => child.type !== 'JSXText');
package/dist/cjs/form.js CHANGED
@@ -5,8 +5,7 @@ var _typeof = require("@babel/runtime/helpers/typeof");
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.IsDisabledContext = exports.FormContext = void 0;
9
- exports.default = Form;
8
+ exports.default = exports.IsDisabledContext = exports.FormContext = void 0;
10
9
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
12
11
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
@@ -14,6 +13,8 @@ var _react = _interopRequireWildcard(require("react"));
14
13
  var _finalForm = require("final-form");
15
14
  var _finalFormFocus = _interopRequireDefault(require("final-form-focus"));
16
15
  var _set = _interopRequireDefault(require("lodash/set"));
16
+ var _forwardRefWithGeneric = _interopRequireDefault(require("@atlaskit/ds-lib/forward-ref-with-generic"));
17
+ var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
17
18
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
18
19
  var _utils = require("./utils");
19
20
  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); }
@@ -42,10 +43,17 @@ var FormContext = exports.FormContext = /*#__PURE__*/(0, _react.createContext)({
42
43
  * An is disabled context creates the context for when a value is disabled.
43
44
  */
44
45
  var IsDisabledContext = exports.IsDisabledContext = /*#__PURE__*/(0, _react.createContext)(false);
45
- function Form(props) {
46
- var userProvidedFormProps = props.formProps,
46
+ var FormBase = function FormBase(props, ref) {
47
+ var autocomplete = props.autocomplete,
48
+ userProvidedFormProps = props.formProps,
49
+ id = props.id,
50
+ label = props.label,
51
+ labelId = props.labelId,
52
+ name = props.name,
53
+ noValidate = props.noValidate,
47
54
  onSubmit = props.onSubmit,
48
- testId = props.testId;
55
+ testId = props.testId,
56
+ xcss = props.xcss;
49
57
  var formRef = (0, _react.useRef)(null);
50
58
  var onSubmitRef = (0, _react.useRef)(onSubmit);
51
59
  onSubmitRef.current = onSubmit;
@@ -146,14 +154,29 @@ function Form(props) {
146
154
  subscribe: form.subscribe
147
155
  };
148
156
  }, [registerField, getCurrentValue, form.subscribe]);
157
+ var conditionalFormProps = {
158
+ autocomplete: autocomplete,
159
+ className: xcss,
160
+ id: id,
161
+ 'aria-label': label,
162
+ 'aria-labelledby': labelId,
163
+ name: name,
164
+ noValidate: noValidate,
165
+ 'data-testid': testId
166
+ };
149
167
 
150
168
  // Abstracting so we can use the same for both rendering patterns
151
- var formProps = _objectSpread({
169
+ var formProps = {
152
170
  onKeyDown: handleKeyDown,
153
171
  onSubmit: handleSubmit,
154
- ref: formRef
155
- }, testId && {
156
- 'data-testid': testId
172
+ ref: ref ? (0, _mergeRefs.default)([ref, formRef]) : formRef
173
+ };
174
+
175
+ // We don't want to add undefined values to the component
176
+ Object.keys(conditionalFormProps).forEach(function (attr) {
177
+ if (conditionalFormProps[attr] !== undefined) {
178
+ formProps[attr] = conditionalFormProps[attr];
179
+ }
157
180
  });
158
181
  var childrenContent = function () {
159
182
  if (typeof children === 'function') {
@@ -183,4 +206,16 @@ function Form(props) {
183
206
  }, /*#__PURE__*/_react.default.createElement(IsDisabledContext.Provider, {
184
207
  value: isDisabled
185
208
  }, childrenContent));
186
- }
209
+ };
210
+
211
+ /**
212
+ * __Form__
213
+ *
214
+ * A form allows users to input information.
215
+ *
216
+ * - [Examples](https://atlassian.design/components/form/examples)
217
+ * - [Code](https://atlassian.design/components/form/code)
218
+ * - [Usage](https://atlassian.design/components/form/usage)
219
+ */
220
+ var Form = (0, _forwardRefWithGeneric.default)(FormBase);
221
+ var _default = exports.default = Form;
@@ -3,6 +3,8 @@ import React, { createContext, useCallback, useEffect, useMemo, useRef, useState
3
3
  import { createForm } from 'final-form';
4
4
  import createDecorator from 'final-form-focus';
5
5
  import set from 'lodash/set';
6
+ import forwardRefWithGeneric from '@atlaskit/ds-lib/forward-ref-with-generic';
7
+ import mergeRefs from '@atlaskit/ds-lib/merge-refs';
6
8
  import { fg } from '@atlaskit/platform-feature-flags';
7
9
  import { getFirstErrorField } from './utils';
8
10
  /**
@@ -26,11 +28,18 @@ export const FormContext = /*#__PURE__*/createContext({
26
28
  * An is disabled context creates the context for when a value is disabled.
27
29
  */
28
30
  export const IsDisabledContext = /*#__PURE__*/createContext(false);
29
- export default function Form(props) {
31
+ const FormBase = (props, ref) => {
30
32
  const {
33
+ autocomplete,
31
34
  formProps: userProvidedFormProps,
35
+ id,
36
+ label,
37
+ labelId,
38
+ name,
39
+ noValidate,
32
40
  onSubmit,
33
- testId
41
+ testId,
42
+ xcss
34
43
  } = props;
35
44
  const formRef = useRef(null);
36
45
  const onSubmitRef = useRef(onSubmit);
@@ -124,16 +133,30 @@ export default function Form(props) {
124
133
  subscribe: form.subscribe
125
134
  };
126
135
  }, [registerField, getCurrentValue, form.subscribe]);
136
+ const conditionalFormProps = {
137
+ autocomplete,
138
+ className: xcss,
139
+ id,
140
+ 'aria-label': label,
141
+ 'aria-labelledby': labelId,
142
+ name,
143
+ noValidate,
144
+ 'data-testid': testId
145
+ };
127
146
 
128
147
  // Abstracting so we can use the same for both rendering patterns
129
148
  const formProps = {
130
149
  onKeyDown: handleKeyDown,
131
150
  onSubmit: handleSubmit,
132
- ref: formRef,
133
- ...(testId && {
134
- 'data-testid': testId
135
- })
151
+ ref: ref ? mergeRefs([ref, formRef]) : formRef
136
152
  };
153
+
154
+ // We don't want to add undefined values to the component
155
+ Object.keys(conditionalFormProps).forEach(attr => {
156
+ if (conditionalFormProps[attr] !== undefined) {
157
+ formProps[attr] = conditionalFormProps[attr];
158
+ }
159
+ });
137
160
  const childrenContent = (() => {
138
161
  if (typeof children === 'function') {
139
162
  const result = children.length > 0 ? children({
@@ -159,4 +182,16 @@ export default function Form(props) {
159
182
  }, /*#__PURE__*/React.createElement(IsDisabledContext.Provider, {
160
183
  value: isDisabled
161
184
  }, childrenContent));
162
- }
185
+ };
186
+
187
+ /**
188
+ * __Form__
189
+ *
190
+ * A form allows users to input information.
191
+ *
192
+ * - [Examples](https://atlassian.design/components/form/examples)
193
+ * - [Code](https://atlassian.design/components/form/code)
194
+ * - [Usage](https://atlassian.design/components/form/usage)
195
+ */
196
+ const Form = forwardRefWithGeneric(FormBase);
197
+ export default Form;
package/dist/esm/form.js CHANGED
@@ -7,6 +7,8 @@ import React, { createContext, useCallback, useEffect, useMemo, useRef, useState
7
7
  import { createForm } from 'final-form';
8
8
  import createDecorator from 'final-form-focus';
9
9
  import set from 'lodash/set';
10
+ import forwardRefWithGeneric from '@atlaskit/ds-lib/forward-ref-with-generic';
11
+ import mergeRefs from '@atlaskit/ds-lib/merge-refs';
10
12
  import { fg } from '@atlaskit/platform-feature-flags';
11
13
  import { getFirstErrorField } from './utils';
12
14
  /**
@@ -32,10 +34,17 @@ export var FormContext = /*#__PURE__*/createContext({
32
34
  * An is disabled context creates the context for when a value is disabled.
33
35
  */
34
36
  export var IsDisabledContext = /*#__PURE__*/createContext(false);
35
- export default function Form(props) {
36
- var userProvidedFormProps = props.formProps,
37
+ var FormBase = function FormBase(props, ref) {
38
+ var autocomplete = props.autocomplete,
39
+ userProvidedFormProps = props.formProps,
40
+ id = props.id,
41
+ label = props.label,
42
+ labelId = props.labelId,
43
+ name = props.name,
44
+ noValidate = props.noValidate,
37
45
  onSubmit = props.onSubmit,
38
- testId = props.testId;
46
+ testId = props.testId,
47
+ xcss = props.xcss;
39
48
  var formRef = useRef(null);
40
49
  var onSubmitRef = useRef(onSubmit);
41
50
  onSubmitRef.current = onSubmit;
@@ -136,14 +145,29 @@ export default function Form(props) {
136
145
  subscribe: form.subscribe
137
146
  };
138
147
  }, [registerField, getCurrentValue, form.subscribe]);
148
+ var conditionalFormProps = {
149
+ autocomplete: autocomplete,
150
+ className: xcss,
151
+ id: id,
152
+ 'aria-label': label,
153
+ 'aria-labelledby': labelId,
154
+ name: name,
155
+ noValidate: noValidate,
156
+ 'data-testid': testId
157
+ };
139
158
 
140
159
  // Abstracting so we can use the same for both rendering patterns
141
- var formProps = _objectSpread({
160
+ var formProps = {
142
161
  onKeyDown: handleKeyDown,
143
162
  onSubmit: handleSubmit,
144
- ref: formRef
145
- }, testId && {
146
- 'data-testid': testId
163
+ ref: ref ? mergeRefs([ref, formRef]) : formRef
164
+ };
165
+
166
+ // We don't want to add undefined values to the component
167
+ Object.keys(conditionalFormProps).forEach(function (attr) {
168
+ if (conditionalFormProps[attr] !== undefined) {
169
+ formProps[attr] = conditionalFormProps[attr];
170
+ }
147
171
  });
148
172
  var childrenContent = function () {
149
173
  if (typeof children === 'function') {
@@ -173,4 +197,16 @@ export default function Form(props) {
173
197
  }, /*#__PURE__*/React.createElement(IsDisabledContext.Provider, {
174
198
  value: isDisabled
175
199
  }, childrenContent));
176
- }
200
+ };
201
+
202
+ /**
203
+ * __Form__
204
+ *
205
+ * A form allows users to input information.
206
+ *
207
+ * - [Examples](https://atlassian.design/components/form/examples)
208
+ * - [Code](https://atlassian.design/components/form/code)
209
+ * - [Usage](https://atlassian.design/components/form/usage)
210
+ */
211
+ var Form = forwardRefWithGeneric(FormBase);
212
+ export default Form;
@@ -1,5 +1,6 @@
1
1
  import React, { type ReactNode } from 'react';
2
2
  import { type FieldConfig, type FieldSubscriber, type FieldSubscription, type FormApi, type FormState, type Unsubscribe } from 'final-form';
3
+ import type { StrictXCSSProp, XCSSAllProperties, XCSSAllPseudos } from '@atlaskit/css';
3
4
  import { type OnSubmitHandler } from './types';
4
5
  type DefaultValue<FieldValue> = (value?: FieldValue) => FieldValue;
5
6
  type RegisterField = <FieldValue>(name: string, defaultValue: FieldValue | DefaultValue<FieldValue>, subscriber: FieldSubscriber<FieldValue>, subscription: FieldSubscription, config: FieldConfig<FieldValue>) => Unsubscribe;
@@ -12,7 +13,7 @@ type GetCurrentValue = <FormValues>(name: string) => FormValues[keyof FormValues
12
13
  export declare const FormContext: React.Context<{
13
14
  registerField: RegisterField;
14
15
  getCurrentValue: GetCurrentValue;
15
- subscribe: FormApi["subscribe"];
16
+ subscribe: FormApi['subscribe'];
16
17
  }>;
17
18
  /**
18
19
  * __Is disabled context__
@@ -21,7 +22,7 @@ export declare const FormContext: React.Context<{
21
22
  */
22
23
  export declare const IsDisabledContext: React.Context<boolean>;
23
24
  interface FormChildrenProps {
24
- ref: React.RefObject<HTMLFormElement>;
25
+ ref: React.RefObject<HTMLFormElement> | ((value: HTMLFormElement | null) => void);
25
26
  onSubmit: (event?: React.FormEvent<HTMLFormElement> | React.SyntheticEvent<HTMLElement>) => void;
26
27
  onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
27
28
  }
@@ -37,11 +38,22 @@ type FormChildrenArgs<FormValues> = {
37
38
  reset: (initialValues?: FormValues) => void;
38
39
  };
39
40
  type ExcludeReservedFormProps = {
41
+ autocomplete?: never;
42
+ id?: never;
43
+ label?: never;
44
+ labelId?: never;
40
45
  onKeyDown?: never;
41
46
  onSubmit?: never;
47
+ name?: never;
48
+ noValidate?: never;
42
49
  ref?: never;
50
+ xcss?: never;
43
51
  };
44
52
  export interface FormProps<FormValues> {
53
+ /**
54
+ * Indicates whether the value of the form's controls can be automatically completed by the browser. It is `on` by default.
55
+ */
56
+ autocomplete?: 'off' | 'on';
45
57
  /**
46
58
  * 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`.
47
59
  * You can read more about these props in [react-final form documentation](https://final-form.org/docs/final-form/types/FormState).
@@ -56,17 +68,53 @@ export interface FormProps<FormValues> {
56
68
  [x: string]: any;
57
69
  } & ExcludeReservedFormProps;
58
70
  /**
59
- * Event handler called when the form is submitted. Fields must be free of validation errors.
71
+ * `id` attribute applied to the `form` element.
72
+ */
73
+ id?: string;
74
+ /**
75
+ * Accessible name to be applied to the form element. Maps to the `aria-label` attribute.
76
+ */
77
+ label?: string;
78
+ /**
79
+ * ID of the element that has the accessible name to be applied to the form element. Maps to the `aria-labelledby` attribute.
80
+ */
81
+ labelId?: string;
82
+ /**
83
+ * Event handler called when the form is submitted. Fields must be free of validation errors.
60
84
  */
61
85
  onSubmit: OnSubmitHandler<FormValues>;
62
86
  /**
63
- * Sets the form and its fields as disabled. Users cannot edit or focus on the fields.
87
+ * Sets the form and its fields as disabled. Users cannot edit or focus on the fields.
64
88
  */
65
89
  isDisabled?: boolean;
90
+ /**
91
+ * `name` attribute applied to the `form` element.
92
+ */
93
+ name?: string;
94
+ /**
95
+ * Indicates if the inputs within the form will bypass HTML5 constraint
96
+ * validation when submitted. This is not recommended to be used because it
97
+ * can cause experiences to be inaccessible. It is `false` by default but will
98
+ * be set to `true` in the future to increase accessibility, so it is **not recommended**.
99
+ */
100
+ noValidate?: boolean;
66
101
  /**
67
102
  * A test identifier for the form element. This will be applied as `data-testid` attribute.
68
103
  */
69
104
  testId?: string;
105
+ /**
106
+ * Apply a subset of permitted styles powered by Atlassian Design System design tokens.
107
+ */
108
+ xcss?: StrictXCSSProp<XCSSAllProperties, XCSSAllPseudos>;
70
109
  }
71
- export default function Form<FormValues extends Record<string, any> = {}>(props: FormProps<FormValues>): React.JSX.Element;
72
- export {};
110
+ /**
111
+ * __Form__
112
+ *
113
+ * A form allows users to input information.
114
+ *
115
+ * - [Examples](https://atlassian.design/components/form/examples)
116
+ * - [Code](https://atlassian.design/components/form/code)
117
+ * - [Usage](https://atlassian.design/components/form/usage)
118
+ */
119
+ declare const Form: <FormValues extends Record<string, any>>(props: FormProps<FormValues> & React.RefAttributes<HTMLFormElement>) => React.ReactElement | null;
120
+ export default Form;
@@ -1,5 +1,6 @@
1
1
  import React, { type ReactNode } from 'react';
2
2
  import { type FieldConfig, type FieldSubscriber, type FieldSubscription, type FormApi, type FormState, type Unsubscribe } from 'final-form';
3
+ import type { StrictXCSSProp, XCSSAllProperties, XCSSAllPseudos } from '@atlaskit/css';
3
4
  import { type OnSubmitHandler } from './types';
4
5
  type DefaultValue<FieldValue> = (value?: FieldValue) => FieldValue;
5
6
  type RegisterField = <FieldValue>(name: string, defaultValue: FieldValue | DefaultValue<FieldValue>, subscriber: FieldSubscriber<FieldValue>, subscription: FieldSubscription, config: FieldConfig<FieldValue>) => Unsubscribe;
@@ -12,7 +13,7 @@ type GetCurrentValue = <FormValues>(name: string) => FormValues[keyof FormValues
12
13
  export declare const FormContext: React.Context<{
13
14
  registerField: RegisterField;
14
15
  getCurrentValue: GetCurrentValue;
15
- subscribe: FormApi["subscribe"];
16
+ subscribe: FormApi['subscribe'];
16
17
  }>;
17
18
  /**
18
19
  * __Is disabled context__
@@ -21,7 +22,7 @@ export declare const FormContext: React.Context<{
21
22
  */
22
23
  export declare const IsDisabledContext: React.Context<boolean>;
23
24
  interface FormChildrenProps {
24
- ref: React.RefObject<HTMLFormElement>;
25
+ ref: React.RefObject<HTMLFormElement> | ((value: HTMLFormElement | null) => void);
25
26
  onSubmit: (event?: React.FormEvent<HTMLFormElement> | React.SyntheticEvent<HTMLElement>) => void;
26
27
  onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
27
28
  }
@@ -37,11 +38,22 @@ type FormChildrenArgs<FormValues> = {
37
38
  reset: (initialValues?: FormValues) => void;
38
39
  };
39
40
  type ExcludeReservedFormProps = {
41
+ autocomplete?: never;
42
+ id?: never;
43
+ label?: never;
44
+ labelId?: never;
40
45
  onKeyDown?: never;
41
46
  onSubmit?: never;
47
+ name?: never;
48
+ noValidate?: never;
42
49
  ref?: never;
50
+ xcss?: never;
43
51
  };
44
52
  export interface FormProps<FormValues> {
53
+ /**
54
+ * Indicates whether the value of the form's controls can be automatically completed by the browser. It is `on` by default.
55
+ */
56
+ autocomplete?: 'off' | 'on';
45
57
  /**
46
58
  * 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`.
47
59
  * You can read more about these props in [react-final form documentation](https://final-form.org/docs/final-form/types/FormState).
@@ -56,17 +68,53 @@ export interface FormProps<FormValues> {
56
68
  [x: string]: any;
57
69
  } & ExcludeReservedFormProps;
58
70
  /**
59
- * Event handler called when the form is submitted. Fields must be free of validation errors.
71
+ * `id` attribute applied to the `form` element.
72
+ */
73
+ id?: string;
74
+ /**
75
+ * Accessible name to be applied to the form element. Maps to the `aria-label` attribute.
76
+ */
77
+ label?: string;
78
+ /**
79
+ * ID of the element that has the accessible name to be applied to the form element. Maps to the `aria-labelledby` attribute.
80
+ */
81
+ labelId?: string;
82
+ /**
83
+ * Event handler called when the form is submitted. Fields must be free of validation errors.
60
84
  */
61
85
  onSubmit: OnSubmitHandler<FormValues>;
62
86
  /**
63
- * Sets the form and its fields as disabled. Users cannot edit or focus on the fields.
87
+ * Sets the form and its fields as disabled. Users cannot edit or focus on the fields.
64
88
  */
65
89
  isDisabled?: boolean;
90
+ /**
91
+ * `name` attribute applied to the `form` element.
92
+ */
93
+ name?: string;
94
+ /**
95
+ * Indicates if the inputs within the form will bypass HTML5 constraint
96
+ * validation when submitted. This is not recommended to be used because it
97
+ * can cause experiences to be inaccessible. It is `false` by default but will
98
+ * be set to `true` in the future to increase accessibility, so it is **not recommended**.
99
+ */
100
+ noValidate?: boolean;
66
101
  /**
67
102
  * A test identifier for the form element. This will be applied as `data-testid` attribute.
68
103
  */
69
104
  testId?: string;
105
+ /**
106
+ * Apply a subset of permitted styles powered by Atlassian Design System design tokens.
107
+ */
108
+ xcss?: StrictXCSSProp<XCSSAllProperties, XCSSAllPseudos>;
70
109
  }
71
- export default function Form<FormValues extends Record<string, any> = {}>(props: FormProps<FormValues>): React.JSX.Element;
72
- export {};
110
+ /**
111
+ * __Form__
112
+ *
113
+ * A form allows users to input information.
114
+ *
115
+ * - [Examples](https://atlassian.design/components/form/examples)
116
+ * - [Code](https://atlassian.design/components/form/code)
117
+ * - [Usage](https://atlassian.design/components/form/usage)
118
+ */
119
+ declare const Form: <FormValues extends Record<string, any>>(props: FormProps<FormValues> & React.RefAttributes<HTMLFormElement>) => React.ReactElement | null;
120
+ export default Form;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/form",
3
- "version": "14.1.0",
3
+ "version": "14.2.1",
4
4
  "description": "A form allows users to input information.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -25,13 +25,13 @@
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
- "@atlaskit/css": "^0.14.0",
28
+ "@atlaskit/css": "^0.15.0",
29
29
  "@atlaskit/ds-lib": "^5.1.0",
30
30
  "@atlaskit/heading": "^5.2.0",
31
- "@atlaskit/icon": "^28.3.0",
31
+ "@atlaskit/icon": "^28.5.0",
32
32
  "@atlaskit/platform-feature-flags": "^1.1.0",
33
- "@atlaskit/primitives": "^14.15.0",
34
- "@atlaskit/tokens": "^6.3.0",
33
+ "@atlaskit/primitives": "^15.0.0",
34
+ "@atlaskit/tokens": "^6.5.0",
35
35
  "@babel/runtime": "^7.0.0",
36
36
  "final-form": "^4.20.3",
37
37
  "final-form-focus": "^1.1.2",
@@ -45,14 +45,14 @@
45
45
  "@af/integration-testing": "workspace:^",
46
46
  "@af/visual-regression": "workspace:^",
47
47
  "@atlaskit/banner": "^14.0.0",
48
- "@atlaskit/button": "^23.4.0",
48
+ "@atlaskit/button": "^23.5.0",
49
49
  "@atlaskit/checkbox": "^17.1.0",
50
50
  "@atlaskit/codemod-utils": "^4.2.0",
51
- "@atlaskit/datetime-picker": "^17.0.0",
51
+ "@atlaskit/datetime-picker": "^17.1.0",
52
52
  "@atlaskit/docs": "^11.1.0",
53
53
  "@atlaskit/link": "^3.2.0",
54
54
  "@atlaskit/lozenge": "^13.0.0",
55
- "@atlaskit/modal-dialog": "^14.3.0",
55
+ "@atlaskit/modal-dialog": "^14.5.0",
56
56
  "@atlaskit/radio": "^8.3.0",
57
57
  "@atlaskit/range": "^9.2.0",
58
58
  "@atlaskit/section-message": "^8.7.0",