@conform-to/react 0.5.0-pre.0 → 0.5.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/hooks.js CHANGED
@@ -11,7 +11,7 @@ var helpers = require('./helpers.js');
11
11
  * Returns properties required to hook into form events.
12
12
  * Applied custom validation and define when error should be reported.
13
13
  *
14
- * @see https://github.com/edmundhung/conform/tree/v0.4.1/packages/conform-react/README.md#useform
14
+ * @see https://conform.guide/api/react#useform
15
15
  */
16
16
  function useForm() {
17
17
  var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -25,17 +25,26 @@ function useForm() {
25
25
  })) !== null && _config$state$error$f !== void 0 ? _config$state$error$f : [];
26
26
  return message !== null && message !== void 0 ? message : '';
27
27
  });
28
- var [fieldsetConfig, setFieldsetConfig] = react.useState(() => {
29
- var _config$state$error2, _config$state2, _config$state$value, _config$state3;
30
- var error = (_config$state$error2 = (_config$state2 = config.state) === null || _config$state2 === void 0 ? void 0 : _config$state2.error) !== null && _config$state$error2 !== void 0 ? _config$state$error2 : [];
28
+ var [uncontrolledState, setUncontrolledState] = react.useState(() => {
29
+ var submission = config.state;
30
+ if (!submission) {
31
+ return {
32
+ defaultValue: config.defaultValue
33
+ };
34
+ }
31
35
  return {
32
- defaultValue: (_config$state$value = (_config$state3 = config.state) === null || _config$state3 === void 0 ? void 0 : _config$state3.value) !== null && _config$state$value !== void 0 ? _config$state$value : config.defaultValue,
33
- initialError: error.filter(_ref2 => {
36
+ defaultValue: submission.value,
37
+ initialError: submission.error.filter(_ref2 => {
34
38
  var [name] = _ref2;
35
- return name !== '' && dom.getSubmissionType(name) === null;
39
+ return name !== '' && dom.shouldValidate(submission, name);
36
40
  })
37
41
  };
38
42
  });
43
+ var fieldsetConfig = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, uncontrolledState), {}, {
44
+ constraint: config.constraint,
45
+ form: config.id
46
+ });
47
+ var fieldset = useFieldset(ref, fieldsetConfig);
39
48
  var [noValidate, setNoValidate] = react.useState(config.noValidate || !config.fallbackNative);
40
49
  react.useEffect(() => {
41
50
  configRef.current = config;
@@ -59,11 +68,8 @@ function useForm() {
59
68
  if (!form || !dom.isFieldElement(field) || field.form !== form) {
60
69
  return;
61
70
  }
62
- if (formConfig.initialReport === 'onChange') {
63
- field.dataset.conformTouched = 'true';
64
- }
65
- if (field.dataset.conformTouched) {
66
- dom.requestValidate(form, field.name);
71
+ if (field.dataset.conformTouched || formConfig.initialReport === 'onChange') {
72
+ dom.requestCommand(form, dom.validate(field.name));
67
73
  }
68
74
  };
69
75
  var handleBlur = event => {
@@ -74,8 +80,7 @@ function useForm() {
74
80
  return;
75
81
  }
76
82
  if (formConfig.initialReport === 'onBlur' && !field.dataset.conformTouched) {
77
- field.dataset.conformTouched = 'true';
78
- dom.requestValidate(form, field.name);
83
+ dom.requestCommand(form, dom.validate(field.name));
79
84
  }
80
85
  };
81
86
  var handleInvalid = event => {
@@ -100,11 +105,12 @@ function useForm() {
100
105
  for (var field of form.elements) {
101
106
  if (dom.isFieldElement(field)) {
102
107
  delete field.dataset.conformTouched;
108
+ field.setAttribute('aria-invalid', 'false');
103
109
  field.setCustomValidity('');
104
110
  }
105
111
  }
106
112
  setError('');
107
- setFieldsetConfig({
113
+ setUncontrolledState({
108
114
  defaultValue: formConfig.defaultValue,
109
115
  initialError: []
110
116
  });
@@ -127,11 +133,13 @@ function useForm() {
127
133
  document.removeEventListener('reset', handleReset);
128
134
  };
129
135
  }, []);
130
- return {
136
+ var form = {
137
+ id: config.id,
131
138
  ref,
132
139
  error,
133
140
  props: {
134
141
  ref,
142
+ id: config.id,
135
143
  noValidate,
136
144
  onSubmit(event) {
137
145
  var form = event.currentTarget;
@@ -173,16 +181,6 @@ function useForm() {
173
181
  }
174
182
  }
175
183
  }
176
-
177
- // Touch all fields only if the submitter is not a command button
178
- if (submission.type === 'submit') {
179
- for (var field of form.elements) {
180
- if (dom.isFieldElement(field)) {
181
- // Mark the field as touched
182
- field.dataset.conformTouched = 'true';
183
- }
184
- }
185
- }
186
184
  if (!config.noValidate && !(submitter !== null && submitter !== void 0 && submitter.formNoValidate) && dom.hasError(submission.error) || submission.type === 'validate' && config.mode !== 'server-validation') {
187
185
  event.preventDefault();
188
186
  } else {
@@ -202,6 +200,7 @@ function useForm() {
202
200
  },
203
201
  config: fieldsetConfig
204
202
  };
203
+ return [form, fieldset];
205
204
  }
206
205
 
207
206
  /**
@@ -262,6 +261,10 @@ function useFieldset(ref, config) {
262
261
  // Update the error only if the field belongs to the fieldset
263
262
  if (typeof key === 'string' && paths.length === 0) {
264
263
  if (field.dataset.conformTouched) {
264
+ // Update the aria attribute only if it is set
265
+ if (field.getAttribute('aria-invalid')) {
266
+ field.setAttribute('aria-invalid', field.validationMessage !== '' ? 'true' : 'false');
267
+ }
265
268
  setError(prev => {
266
269
  var _prev$key;
267
270
  var prevMessage = (_prev$key = prev === null || prev === void 0 ? void 0 : prev[key]) !== null && _prev$key !== void 0 ? _prev$key : '';
@@ -316,21 +319,26 @@ function useFieldset(ref, config) {
316
319
  var field = {
317
320
  config: _rollupPluginBabelHelpers.objectSpread2({
318
321
  name: fieldsetConfig.name ? "".concat(fieldsetConfig.name, ".").concat(key) : key,
319
- form: fieldsetConfig.form,
320
322
  defaultValue: uncontrolledState.defaultValue[key],
321
323
  initialError: uncontrolledState.initialError[key]
322
324
  }, constraint),
323
325
  error: (_error$key = error === null || error === void 0 ? void 0 : error[key]) !== null && _error$key !== void 0 ? _error$key : ''
324
326
  };
327
+ if (fieldsetConfig.form) {
328
+ field.config.form = fieldsetConfig.form;
329
+ field.config.id = "".concat(fieldsetConfig.form, "-").concat(field.config.name);
330
+ field.config.errorId = "".concat(field.config.id, "-error");
331
+ }
325
332
  return field;
326
333
  }
327
334
  });
328
335
  }
336
+
329
337
  /**
330
338
  * Returns a list of key and config, with a group of helpers
331
339
  * configuring buttons for list manipulation
332
340
  *
333
- * @see https://github.com/edmundhung/conform/tree/v0.4.1/packages/conform-react/README.md#usefieldlist
341
+ * @see https://conform.guide/api/react#usefieldlist
334
342
  */
335
343
  function useFieldList(ref, config) {
336
344
  var configRef = react.useRef(config);
@@ -361,41 +369,6 @@ function useFieldList(ref, config) {
361
369
  var _config$defaultValue3;
362
370
  return Object.entries((_config$defaultValue3 = config.defaultValue) !== null && _config$defaultValue3 !== void 0 ? _config$defaultValue3 : [undefined]);
363
371
  });
364
- var list = entries.map((_ref3, index) => {
365
- var [key, defaultValue] = _ref3;
366
- return {
367
- key,
368
- error: error[index],
369
- config: {
370
- name: "".concat(config.name, "[").concat(index, "]"),
371
- form: config.form,
372
- defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : uncontrolledState.defaultValue[index],
373
- initialError: uncontrolledState.initialError[index]
374
- }
375
- };
376
- });
377
-
378
- /***
379
- * This use proxy to capture all information about the command and
380
- * have it encoded in the value.
381
- */
382
- var command = new Proxy({}, {
383
- get(_target, type) {
384
- return function () {
385
- var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
386
- return {
387
- name: 'conform/list',
388
- value: JSON.stringify({
389
- type,
390
- scope: config.name,
391
- payload
392
- }),
393
- form: config.form,
394
- formNoValidate: true
395
- };
396
- };
397
- }
398
- });
399
372
  react.useEffect(() => {
400
373
  configRef.current = config;
401
374
  });
@@ -492,16 +465,32 @@ function useFieldList(ref, config) {
492
465
  document.removeEventListener('reset', resetHandler);
493
466
  };
494
467
  }, [ref]);
495
- return [list,
496
- // @ts-expect-error proxy type
497
- command];
468
+ return entries.map((_ref3, index) => {
469
+ var [key, defaultValue] = _ref3;
470
+ var fieldConfig = {
471
+ name: "".concat(config.name, "[").concat(index, "]"),
472
+ defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : uncontrolledState.defaultValue[index],
473
+ initialError: uncontrolledState.initialError[index]
474
+ };
475
+ if (config.form) {
476
+ fieldConfig.form = config.form;
477
+ fieldConfig.id = "".concat(config.form, "-").concat(config.name);
478
+ fieldConfig.errorId = "".concat(fieldConfig.id, "-error");
479
+ }
480
+ return {
481
+ key,
482
+ error: error[index],
483
+ config: fieldConfig
484
+ };
485
+ });
498
486
  }
499
487
  /**
500
488
  * Returns the properties required to configure a shadow input for validation.
501
489
  * This is particular useful when integrating dropdown and datepicker whichs
502
490
  * introduces custom input mode.
503
491
  *
504
- * @see https://github.com/edmundhung/conform/tree/v0.4.1/packages/conform-react/README.md#usecontrolledinput
492
+ * @deprecated Please use the `useInputEvent` hook instead
493
+ * @see https://conform.guide/api/react#usecontrolledinput
505
494
  */
506
495
  function useControlledInput(config) {
507
496
  var _config$defaultValue4;
@@ -556,24 +545,13 @@ function useControlledInput(config) {
556
545
  }, []);
557
546
  return [_rollupPluginBabelHelpers.objectSpread2({
558
547
  ref,
559
- style: {
560
- position: 'absolute',
561
- width: '1px',
562
- height: '1px',
563
- padding: 0,
564
- margin: '-1px',
565
- overflow: 'hidden',
566
- clip: 'rect(0,0,0,0)',
567
- whiteSpace: 'nowrap',
568
- borderWidth: 0
569
- },
570
- tabIndex: -1,
571
- 'aria-hidden': true,
572
548
  onFocus() {
573
549
  var _inputRef$current;
574
550
  (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
575
551
  }
576
- }, helpers.input(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), uncontrolledState))), {
552
+ }, helpers.input(_rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, config), uncontrolledState), {
553
+ hidden: true
554
+ })), {
577
555
  ref: inputRef,
578
556
  value,
579
557
  onChange: handleChange,
@@ -582,7 +560,183 @@ function useControlledInput(config) {
582
560
  }];
583
561
  }
584
562
 
563
+ /**
564
+ * Triggering react custom change event
565
+ * Solution based on dom-testing-library
566
+ * @see https://github.com/facebook/react/issues/10135#issuecomment-401496776
567
+ * @see https://github.com/testing-library/dom-testing-library/blob/main/src/events.js#L104-L123
568
+ */
569
+ function setNativeValue(element, value) {
570
+ if (element.value === value) {
571
+ // It will not trigger a change event if `element.value` is the same as the set value
572
+ return;
573
+ }
574
+ var {
575
+ set: valueSetter
576
+ } = Object.getOwnPropertyDescriptor(element, 'value') || {};
577
+ var prototype = Object.getPrototypeOf(element);
578
+ var {
579
+ set: prototypeValueSetter
580
+ } = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
581
+ if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
582
+ prototypeValueSetter.call(element, value);
583
+ } else {
584
+ if (valueSetter) {
585
+ valueSetter.call(element, value);
586
+ } else {
587
+ throw new Error('The given element does not have a value setter');
588
+ }
589
+ }
590
+ }
591
+
592
+ /**
593
+ * useLayoutEffect is client-only.
594
+ * This basically makes it a no-op on server
595
+ */
596
+ var useSafeLayoutEffect = typeof document === 'undefined' ? react.useEffect : react.useLayoutEffect;
597
+ function useInputEvent(options) {
598
+ var ref = react.useRef(null);
599
+ var optionsRef = react.useRef(options);
600
+ var changeDispatched = react.useRef(false);
601
+ var focusDispatched = react.useRef(false);
602
+ var blurDispatched = react.useRef(false);
603
+ useSafeLayoutEffect(() => {
604
+ optionsRef.current = options;
605
+ });
606
+ useSafeLayoutEffect(() => {
607
+ var getInputElement = () => {
608
+ var _optionsRef$current$g, _optionsRef$current, _optionsRef$current$g2;
609
+ return (_optionsRef$current$g = (_optionsRef$current = optionsRef.current) === null || _optionsRef$current === void 0 ? void 0 : (_optionsRef$current$g2 = _optionsRef$current.getElement) === null || _optionsRef$current$g2 === void 0 ? void 0 : _optionsRef$current$g2.call(_optionsRef$current, ref.current)) !== null && _optionsRef$current$g !== void 0 ? _optionsRef$current$g : ref.current;
610
+ };
611
+ var inputHandler = event => {
612
+ var input = getInputElement();
613
+ if (input && event.target === input) {
614
+ changeDispatched.current = true;
615
+ }
616
+ };
617
+ var focusHandler = event => {
618
+ var input = getInputElement();
619
+ if (input && event.target === input) {
620
+ focusDispatched.current = true;
621
+ }
622
+ };
623
+ var blurHandler = event => {
624
+ var input = getInputElement();
625
+ if (input && event.target === input) {
626
+ blurDispatched.current = true;
627
+ }
628
+ };
629
+ var submitHandler = event => {
630
+ var input = getInputElement();
631
+ if (input !== null && input !== void 0 && input.form && event.target === input.form) {
632
+ var _optionsRef$current2, _optionsRef$current2$;
633
+ (_optionsRef$current2 = optionsRef.current) === null || _optionsRef$current2 === void 0 ? void 0 : (_optionsRef$current2$ = _optionsRef$current2.onSubmit) === null || _optionsRef$current2$ === void 0 ? void 0 : _optionsRef$current2$.call(_optionsRef$current2, event);
634
+ }
635
+ };
636
+ var resetHandler = event => {
637
+ var input = getInputElement();
638
+ if (input !== null && input !== void 0 && input.form && event.target === input.form) {
639
+ var _optionsRef$current3, _optionsRef$current3$;
640
+ (_optionsRef$current3 = optionsRef.current) === null || _optionsRef$current3 === void 0 ? void 0 : (_optionsRef$current3$ = _optionsRef$current3.onReset) === null || _optionsRef$current3$ === void 0 ? void 0 : _optionsRef$current3$.call(_optionsRef$current3, event);
641
+ }
642
+ };
643
+ document.addEventListener('input', inputHandler, true);
644
+ document.addEventListener('focus', focusHandler, true);
645
+ document.addEventListener('blur', blurHandler, true);
646
+ document.addEventListener('submit', submitHandler);
647
+ document.addEventListener('reset', resetHandler);
648
+ return () => {
649
+ document.removeEventListener('input', inputHandler, true);
650
+ document.removeEventListener('focus', focusHandler, true);
651
+ document.removeEventListener('blur', blurHandler, true);
652
+ document.removeEventListener('submit', submitHandler);
653
+ document.removeEventListener('reset', resetHandler);
654
+ };
655
+ }, []);
656
+ var control = react.useMemo(() => {
657
+ var getInputElement = () => {
658
+ var _optionsRef$current$g3, _optionsRef$current4, _optionsRef$current4$;
659
+ return (_optionsRef$current$g3 = (_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 ? void 0 : (_optionsRef$current4$ = _optionsRef$current4.getElement) === null || _optionsRef$current4$ === void 0 ? void 0 : _optionsRef$current4$.call(_optionsRef$current4, ref.current)) !== null && _optionsRef$current$g3 !== void 0 ? _optionsRef$current$g3 : ref.current;
660
+ };
661
+ return {
662
+ change(eventOrValue) {
663
+ var input = getInputElement();
664
+ if (!input) {
665
+ console.warn('Missing input ref; No change-related events will be dispatched');
666
+ return;
667
+ }
668
+ if (changeDispatched.current) {
669
+ changeDispatched.current = false;
670
+ return;
671
+ }
672
+ var previousValue = input.value;
673
+ var nextValue = typeof eventOrValue === 'string' ? eventOrValue : eventOrValue.target.value;
674
+
675
+ // This make sure no event is dispatched on the first effect run
676
+ if (nextValue === previousValue) {
677
+ return;
678
+ }
679
+
680
+ // Dispatch beforeinput event before updating the input value
681
+ input.dispatchEvent(new Event('beforeinput', {
682
+ bubbles: true
683
+ }));
684
+ // Update the input value to trigger a change event
685
+ setNativeValue(input, nextValue);
686
+ // Dispatch input event with the updated input value
687
+ input.dispatchEvent(new InputEvent('input', {
688
+ bubbles: true
689
+ }));
690
+ // Reset the dispatched flag
691
+ changeDispatched.current = false;
692
+ },
693
+ focus() {
694
+ var input = getInputElement();
695
+ if (!input) {
696
+ console.warn('Missing input ref; No focus-related events will be dispatched');
697
+ return;
698
+ }
699
+ if (focusDispatched.current) {
700
+ focusDispatched.current = false;
701
+ return;
702
+ }
703
+ var focusinEvent = new FocusEvent('focusin', {
704
+ bubbles: true
705
+ });
706
+ var focusEvent = new FocusEvent('focus');
707
+ input.dispatchEvent(focusinEvent);
708
+ input.dispatchEvent(focusEvent);
709
+
710
+ // Reset the dispatched flag
711
+ focusDispatched.current = false;
712
+ },
713
+ blur() {
714
+ var input = getInputElement();
715
+ if (!input) {
716
+ console.warn('Missing input ref; No blur-related events will be dispatched');
717
+ return;
718
+ }
719
+ if (blurDispatched.current) {
720
+ blurDispatched.current = false;
721
+ return;
722
+ }
723
+ var focusoutEvent = new FocusEvent('focusout', {
724
+ bubbles: true
725
+ });
726
+ var blurEvent = new FocusEvent('blur');
727
+ input.dispatchEvent(focusoutEvent);
728
+ input.dispatchEvent(blurEvent);
729
+
730
+ // Reset the dispatched flag
731
+ blurDispatched.current = false;
732
+ }
733
+ };
734
+ }, []);
735
+ return [ref, control];
736
+ }
737
+
585
738
  exports.useControlledInput = useControlledInput;
586
739
  exports.useFieldList = useFieldList;
587
740
  exports.useFieldset = useFieldset;
588
741
  exports.useForm = useForm;
742
+ exports.useInputEvent = useInputEvent;
package/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { type FieldConfig, type FieldsetConstraint, type Submission, getFormElements, hasError, parse, shouldValidate, } from '@conform-to/dom';
1
+ export { type FieldConfig, type FieldsetConstraint, type Submission, getFormElements, hasError, list, validate, requestCommand, requestSubmit, parse, shouldValidate, } from '@conform-to/dom';
2
2
  export * from './hooks';
3
3
  export * as conform from './helpers';
package/index.js CHANGED
@@ -16,16 +16,33 @@ Object.defineProperty(exports, 'hasError', {
16
16
  enumerable: true,
17
17
  get: function () { return dom.hasError; }
18
18
  });
19
+ Object.defineProperty(exports, 'list', {
20
+ enumerable: true,
21
+ get: function () { return dom.list; }
22
+ });
19
23
  Object.defineProperty(exports, 'parse', {
20
24
  enumerable: true,
21
25
  get: function () { return dom.parse; }
22
26
  });
27
+ Object.defineProperty(exports, 'requestCommand', {
28
+ enumerable: true,
29
+ get: function () { return dom.requestCommand; }
30
+ });
31
+ Object.defineProperty(exports, 'requestSubmit', {
32
+ enumerable: true,
33
+ get: function () { return dom.requestSubmit; }
34
+ });
23
35
  Object.defineProperty(exports, 'shouldValidate', {
24
36
  enumerable: true,
25
37
  get: function () { return dom.shouldValidate; }
26
38
  });
39
+ Object.defineProperty(exports, 'validate', {
40
+ enumerable: true,
41
+ get: function () { return dom.validate; }
42
+ });
27
43
  exports.useControlledInput = hooks.useControlledInput;
28
44
  exports.useFieldList = hooks.useFieldList;
29
45
  exports.useFieldset = hooks.useFieldset;
30
46
  exports.useForm = hooks.useForm;
47
+ exports.useInputEvent = hooks.useInputEvent;
31
48
  exports.conform = helpers;
package/module/helpers.js CHANGED
@@ -1,6 +1,23 @@
1
+ /**
2
+ * Style to make the input element visually hidden
3
+ * Based on the `sr-only` class from tailwindcss
4
+ */
5
+ var hiddenStyle = {
6
+ position: 'absolute',
7
+ width: '1px',
8
+ height: '1px',
9
+ padding: 0,
10
+ margin: '-1px',
11
+ overflow: 'hidden',
12
+ clip: 'rect(0,0,0,0)',
13
+ whiteSpace: 'nowrap',
14
+ border: 0
15
+ };
1
16
  function input(config) {
17
+ var _config$initialError;
2
18
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3
19
  var attributes = {
20
+ id: config.id,
4
21
  type: options.type,
5
22
  name: config.name,
6
23
  form: config.form,
@@ -11,8 +28,15 @@ function input(config) {
11
28
  max: config.max,
12
29
  step: config.step,
13
30
  pattern: config.pattern,
14
- multiple: config.multiple
31
+ multiple: config.multiple,
32
+ 'aria-invalid': Boolean((_config$initialError = config.initialError) === null || _config$initialError === void 0 ? void 0 : _config$initialError.length),
33
+ 'aria-describedby': config.errorId
15
34
  };
35
+ if (options !== null && options !== void 0 && options.hidden) {
36
+ attributes.style = hiddenStyle;
37
+ attributes.tabIndex = -1;
38
+ attributes['aria-hidden'] = true;
39
+ }
16
40
  if (config.initialError && config.initialError.length > 0) {
17
41
  attributes.autoFocus = true;
18
42
  }
@@ -25,31 +49,47 @@ function input(config) {
25
49
  }
26
50
  return attributes;
27
51
  }
28
- function select(config) {
29
- var _config$defaultValue;
52
+ function select(config, options) {
53
+ var _config$defaultValue, _config$initialError2;
30
54
  var attributes = {
55
+ id: config.id,
31
56
  name: config.name,
32
57
  form: config.form,
33
58
  defaultValue: config.multiple ? Array.isArray(config.defaultValue) ? config.defaultValue : [] : "".concat((_config$defaultValue = config.defaultValue) !== null && _config$defaultValue !== void 0 ? _config$defaultValue : ''),
34
59
  required: config.required,
35
- multiple: config.multiple
60
+ multiple: config.multiple,
61
+ 'aria-invalid': Boolean((_config$initialError2 = config.initialError) === null || _config$initialError2 === void 0 ? void 0 : _config$initialError2.length),
62
+ 'aria-describedby': config.errorId
36
63
  };
64
+ if (options !== null && options !== void 0 && options.hidden) {
65
+ attributes.style = hiddenStyle;
66
+ attributes.tabIndex = -1;
67
+ attributes['aria-hidden'] = true;
68
+ }
37
69
  if (config.initialError && config.initialError.length > 0) {
38
70
  attributes.autoFocus = true;
39
71
  }
40
72
  return attributes;
41
73
  }
42
- function textarea(config) {
43
- var _config$defaultValue2;
74
+ function textarea(config, options) {
75
+ var _config$defaultValue2, _config$initialError3;
44
76
  var attributes = {
77
+ id: config.id,
45
78
  name: config.name,
46
79
  form: config.form,
47
80
  defaultValue: "".concat((_config$defaultValue2 = config.defaultValue) !== null && _config$defaultValue2 !== void 0 ? _config$defaultValue2 : ''),
48
81
  required: config.required,
49
82
  minLength: config.minLength,
50
83
  maxLength: config.maxLength,
51
- autoFocus: Boolean(config.initialError)
84
+ autoFocus: Boolean(config.initialError),
85
+ 'aria-invalid': Boolean((_config$initialError3 = config.initialError) === null || _config$initialError3 === void 0 ? void 0 : _config$initialError3.length),
86
+ 'aria-describedby': config.errorId
52
87
  };
88
+ if (options !== null && options !== void 0 && options.hidden) {
89
+ attributes.style = hiddenStyle;
90
+ attributes.tabIndex = -1;
91
+ attributes['aria-hidden'] = true;
92
+ }
53
93
  if (config.initialError && config.initialError.length > 0) {
54
94
  attributes.autoFocus = true;
55
95
  }