@itcase/forms 1.0.64 → 1.0.65

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.
@@ -2477,116 +2477,6 @@ ChipsField.propTypes = {
2477
2477
  onChange: PropTypes__default.default.func
2478
2478
  };
2479
2479
 
2480
- const focusOnError = (formElementsList, errors) => {
2481
- const selectsIds = Object.keys(errors).map(fieldName => {
2482
- if (fieldName === finalForm.FORM_ERROR) {
2483
- // TODO: get from somewhere
2484
- return 'notification__item_status_error';
2485
- }
2486
- return `react-select-id_${fieldName}-input`;
2487
- });
2488
- const errorFieldElement = formElementsList.find(element => {
2489
- if (element.name) {
2490
- return finalForm.getIn(errors, element.name);
2491
- } else {
2492
- return selectsIds.includes(element.id);
2493
- }
2494
- });
2495
- const errorsList = Object.keys(errors);
2496
- if (!errorFieldElement && errorsList.length) {
2497
- let errorElement;
2498
- try {
2499
- const fieldName = errorsList[0];
2500
- if (fieldName === finalForm.FORM_ERROR) {
2501
- errorElement = document.querySelector('notification__item_status_error');
2502
- } else {
2503
- errorElement = document.querySelector(`#${fieldName}-error`);
2504
- if (!errorElement) {
2505
- errorElement = document.querySelector(`#id_${fieldName}`);
2506
- }
2507
- }
2508
- } catch (err) {
2509
- console.warn(err);
2510
- }
2511
- if (errorElement) {
2512
- errorElement.scrollIntoView({
2513
- block: 'center'
2514
- }); // , behavior: 'smooth'
2515
- }
2516
- }
2517
-
2518
- // The field is covered by the header because header is "sticky",
2519
- // that's we scroll manually so that the field falls into the center of the visible area
2520
- if (errorFieldElement) {
2521
- errorFieldElement.scrollIntoView({
2522
- block: 'center'
2523
- });
2524
- }
2525
- return null;
2526
- };
2527
- const focusOnErrorDecorator = createDecorator__default.default(null, focusOnError);
2528
- const setErrorsMutator = (args, state) => {
2529
- const [fieldName, data] = args;
2530
- const submitError = data.submitError;
2531
- const fieldError = data.error;
2532
- if (fieldName === 'non_field_errors') {
2533
- // state.formState.invalid = true
2534
- // state.formState.valid = false
2535
- state.formState.error = fieldError;
2536
- state.formState.submitError = submitError;
2537
- } else if (fieldName in state.fields) {
2538
- if (fieldError) {
2539
- const errorsState = Object.assign({}, state.formState.errors, {
2540
- [fieldName]: fieldError
2541
- });
2542
- state.fields[fieldName].touched = true;
2543
- state.fields[fieldName].error = fieldError;
2544
- state.formState.errors = errorsState;
2545
- }
2546
- if (submitError) {
2547
- const submitErrorsState = Object.assign({}, state.formState.submitErrors, {
2548
- [fieldName]: submitError
2549
- });
2550
-
2551
- // state.fields[fieldName].submitFailed = true
2552
- // state.fields[fieldName].submitSucceeded = false
2553
- state.fields[fieldName].submitError = submitError;
2554
- state.formState.submitErrors = submitErrorsState;
2555
- state.formState.submitFailed = true;
2556
- state.formState.submitSucceeded = false;
2557
- state.formState.lastSubmittedValues = state.formState.values;
2558
- }
2559
- }
2560
- };
2561
- const sendFormDataToServer = async (url, data) => {
2562
- try {
2563
- const response = await axios__default.default({
2564
- url: url,
2565
- method: 'POST',
2566
- data: data
2567
- });
2568
- return {
2569
- success: true,
2570
- response
2571
- };
2572
- } catch (error) {
2573
- const formErrors = {};
2574
- if (typeof error.response?.data === 'string') {
2575
- formErrors[finalForm.FORM_ERROR] = 'Something went wrong';
2576
- }
2577
- if (typeof error.response?.data === 'object') {
2578
- Object.entries(error.response.data).forEach(([fieldName, errorsList]) => {
2579
- formErrors[fieldName] = errorsList[0];
2580
- });
2581
- }
2582
- return {
2583
- success: false,
2584
- formErrors,
2585
- error
2586
- };
2587
- }
2588
- };
2589
-
2590
2480
  const formTypes = {
2591
2481
  checkbox: 'checkbox',
2592
2482
  custom: 'custom',
@@ -2709,8 +2599,119 @@ function generateField(field, config, props) {
2709
2599
  }
2710
2600
  }
2711
2601
 
2602
+ const focusOnError = (formElementsList, errors) => {
2603
+ const selectsIds = Object.keys(errors).map(fieldName => {
2604
+ if (fieldName === finalForm.FORM_ERROR) {
2605
+ // TODO: get from somewhere
2606
+ return 'notification__item_status_error';
2607
+ }
2608
+ return `react-select-id_${fieldName}-input`;
2609
+ });
2610
+ const errorFieldElement = formElementsList.find(element => {
2611
+ if (element.name) {
2612
+ return finalForm.getIn(errors, element.name);
2613
+ } else {
2614
+ return selectsIds.includes(element.id);
2615
+ }
2616
+ });
2617
+ const errorsList = Object.keys(errors);
2618
+ if (!errorFieldElement && errorsList.length) {
2619
+ let errorElement;
2620
+ try {
2621
+ const fieldName = errorsList[0];
2622
+ if (fieldName === finalForm.FORM_ERROR) {
2623
+ errorElement = document.querySelector('notification__item_status_error');
2624
+ } else {
2625
+ errorElement = document.querySelector(`#${fieldName}-error`);
2626
+ if (!errorElement) {
2627
+ errorElement = document.querySelector(`#id_${fieldName}`);
2628
+ }
2629
+ }
2630
+ } catch (err) {
2631
+ console.warn(err);
2632
+ }
2633
+ if (errorElement) {
2634
+ errorElement.scrollIntoView({
2635
+ block: 'center'
2636
+ }); // , behavior: 'smooth'
2637
+ }
2638
+ }
2639
+
2640
+ // The field is covered by the header because header is "sticky",
2641
+ // that's we scroll manually so that the field falls into the center of the visible area
2642
+ if (errorFieldElement) {
2643
+ errorFieldElement.scrollIntoView({
2644
+ block: 'center'
2645
+ });
2646
+ }
2647
+ return null;
2648
+ };
2649
+ const focusOnErrorDecorator = createDecorator__default.default(null, focusOnError);
2650
+ const setErrorsMutator = (args, state) => {
2651
+ const [fieldName, data] = args;
2652
+ const submitError = data.submitError;
2653
+ const fieldError = data.error;
2654
+ if (fieldName === 'non_field_errors') {
2655
+ // state.formState.invalid = true
2656
+ // state.formState.valid = false
2657
+ state.formState.error = fieldError;
2658
+ state.formState.submitError = submitError;
2659
+ } else if (fieldName in state.fields) {
2660
+ if (fieldError) {
2661
+ const errorsState = Object.assign({}, state.formState.errors, {
2662
+ [fieldName]: fieldError
2663
+ });
2664
+ state.fields[fieldName].touched = true;
2665
+ state.fields[fieldName].error = fieldError;
2666
+ state.formState.errors = errorsState;
2667
+ }
2668
+ if (submitError) {
2669
+ const submitErrorsState = Object.assign({}, state.formState.submitErrors, {
2670
+ [fieldName]: submitError
2671
+ });
2672
+
2673
+ // state.fields[fieldName].submitFailed = true
2674
+ // state.fields[fieldName].submitSucceeded = false
2675
+ state.fields[fieldName].submitError = submitError;
2676
+ state.formState.submitErrors = submitErrorsState;
2677
+ state.formState.submitFailed = true;
2678
+ state.formState.submitSucceeded = false;
2679
+ state.formState.lastSubmittedValues = state.formState.values;
2680
+ }
2681
+ }
2682
+ };
2683
+ const sendFormDataToServer = async (url, data) => {
2684
+ try {
2685
+ const response = await axios__default.default({
2686
+ url: url,
2687
+ method: 'POST',
2688
+ data: data
2689
+ });
2690
+ return {
2691
+ success: true,
2692
+ response
2693
+ };
2694
+ } catch (error) {
2695
+ const formErrors = {};
2696
+ if (typeof error.response?.data === 'string') {
2697
+ formErrors[finalForm.FORM_ERROR] = 'Something went wrong';
2698
+ }
2699
+ if (typeof error.response?.data === 'object') {
2700
+ Object.entries(error.response.data).forEach(([fieldName, errorsList]) => {
2701
+ formErrors[fieldName] = errorsList[0];
2702
+ });
2703
+ }
2704
+ return {
2705
+ success: false,
2706
+ formErrors,
2707
+ error
2708
+ };
2709
+ }
2710
+ };
2711
+
2712
2712
  const FinalForm = /*#__PURE__*/React__default.default.forwardRef(function FinalForm(props, ref) {
2713
2713
  const {
2714
+ isLoading,
2714
2715
  additionalProps,
2715
2716
  after,
2716
2717
  before,
@@ -2721,13 +2722,13 @@ const FinalForm = /*#__PURE__*/React__default.default.forwardRef(function FinalF
2721
2722
  buttonPadding,
2722
2723
  className,
2723
2724
  config,
2724
- dataTour,
2725
2725
  dataTestId,
2726
- dataTourButtons,
2727
2726
  dataTestIdButtons,
2728
2727
  dataTestIdPrimaryButton,
2729
2728
  dataTestIdSecondaryButton,
2730
2729
  dataTestIdTertiaryButton,
2730
+ dataTour,
2731
+ dataTourButtons,
2731
2732
  dataTourPrimaryButton,
2732
2733
  dataTourSecondaryButton,
2733
2734
  dataTourTertiaryButton,
@@ -2741,7 +2742,6 @@ const FinalForm = /*#__PURE__*/React__default.default.forwardRef(function FinalF
2741
2742
  groupGap,
2742
2743
  initialValues,
2743
2744
  initialValuesEqual,
2744
- isLoading,
2745
2745
  language,
2746
2746
  loader,
2747
2747
  loaderFill,
@@ -2751,10 +2751,6 @@ const FinalForm = /*#__PURE__*/React__default.default.forwardRef(function FinalF
2751
2751
  mutators,
2752
2752
  notificationCloseButton,
2753
2753
  notificationType,
2754
- onChangeFormValues,
2755
- onClickSecondaryButton,
2756
- onClickTertiaryButton,
2757
- onSubmit,
2758
2754
  primaryButton,
2759
2755
  primaryButtonFill,
2760
2756
  primaryButtonFillHover,
@@ -2763,6 +2759,7 @@ const FinalForm = /*#__PURE__*/React__default.default.forwardRef(function FinalF
2763
2759
  primaryButtonLabelTextColor,
2764
2760
  primaryButtonLabelTextWeight,
2765
2761
  primaryButtonSize,
2762
+ renderFieldsWrapper = wrapperChildren => wrapperChildren,
2766
2763
  secondaryButton,
2767
2764
  secondaryButtonFill,
2768
2765
  secondaryButtonFillHover,
@@ -2785,7 +2782,11 @@ const FinalForm = /*#__PURE__*/React__default.default.forwardRef(function FinalF
2785
2782
  titleTextSize,
2786
2783
  titleTextWeight,
2787
2784
  type,
2788
- validationSchema
2785
+ validationSchema,
2786
+ onChangeFormValues,
2787
+ onClickSecondaryButton,
2788
+ onClickTertiaryButton,
2789
+ onSubmit
2789
2790
  } = props;
2790
2791
  const validate = useYupValidationSchema(validationSchema, language);
2791
2792
  const onRefFormInstance = React.useCallback(formInstance => {
@@ -2817,26 +2818,27 @@ const FinalForm = /*#__PURE__*/React__default.default.forwardRef(function FinalF
2817
2818
  initialValues: initialValues,
2818
2819
  initialValuesEqual: initialValuesEqual,
2819
2820
  mutators: mutators,
2821
+ validate: validate,
2820
2822
  render: ({
2823
+ form,
2821
2824
  handleSubmit,
2822
- submitError,
2823
2825
  modifiedSinceLastSubmit,
2824
- form
2826
+ submitError
2825
2827
  }) => {
2826
2828
  return /*#__PURE__*/React__default.default.createElement("form", {
2827
- style: formStyles,
2828
- className: clsx__default.default(className, 'form', set && `form_set_${set}`, type && `form_type_${type}`, directionClass, fillClass, shapeClass, elevationClass),
2829
- name: formName,
2830
- "data-tour": dataTour,
2831
- "data-test-id": dataTestId
2829
+ className: clsx__default.default(className, 'form', set && `form_set_${set}`, type && `form_type_${type}`, directionClass, fillClass, shapeClass, elevationClass)
2832
2830
  // We no need set reference to html element, we need reference to "final-form" instance
2833
2831
  ,
2834
2832
  ref: () => onRefFormInstance(form),
2835
- onSubmit: handleSubmit,
2833
+ name: formName,
2834
+ autoCapitalize: disableFieldsAutoComplete ? 'off' : undefined,
2836
2835
  autoComplete: disableFieldsAutoComplete ? 'off' : undefined,
2837
2836
  autoCorrect: disableFieldsAutoComplete ? 'off' : undefined,
2838
- autoCapitalize: disableFieldsAutoComplete ? 'off' : undefined,
2839
- spellCheck: disableFieldsAutoComplete ? 'false' : undefined
2837
+ "data-test-id": dataTestId,
2838
+ "data-tour": dataTour,
2839
+ spellCheck: disableFieldsAutoComplete ? 'false' : undefined,
2840
+ style: formStyles,
2841
+ onSubmit: handleSubmit
2840
2842
  }, before, title && /*#__PURE__*/React__default.default.createElement(Title.Title, {
2841
2843
  className: "form__title",
2842
2844
  size: titleTextSize,
@@ -2851,88 +2853,88 @@ const FinalForm = /*#__PURE__*/React__default.default.forwardRef(function FinalF
2851
2853
  className: clsx__default.default('notification', 'form-notification', notificationType ? `form-notification_type_${notificationType}` : 'form-notification_type_global')
2852
2854
  }, /*#__PURE__*/React__default.default.createElement(Notification.NotificationItem, {
2853
2855
  className: "form-notification__item",
2854
- titleTextSize: "h6",
2855
2856
  closeButton: notificationCloseButton,
2857
+ status: "error",
2856
2858
  title: form.getState().submitError,
2857
- set: "form",
2858
- status: "error"
2859
+ titleTextSize: "h6",
2860
+ set: "form"
2859
2861
  })), onChangeFormValues && /*#__PURE__*/React__default.default.createElement(reactFinalForm.FormSpy, {
2860
2862
  subscription: {
2861
2863
  values: true
2862
2864
  },
2863
2865
  onChange: onChangeFormValues
2864
- }), Boolean(Object.keys(config).length) && /*#__PURE__*/React__default.default.createElement(Group$1.Group, {
2866
+ }), Boolean(Object.keys(config).length) && /*#__PURE__*/React__default.default.createElement(React__default.default.Fragment, null, renderFieldsWrapper( /*#__PURE__*/React__default.default.createElement(Group$1.Group, {
2867
+ className: "form__wrapper",
2865
2868
  direction: "vertical",
2866
- gap: fieldsGap || groupGap,
2867
- className: "form__wrapper"
2869
+ gap: fieldsGap || groupGap
2868
2870
  }, Object.keys(config).map(key => generateField(config[key], {
2869
2871
  key
2870
2872
  }, additionalProps[config[key].name])), isLoading && (loader || /*#__PURE__*/React__default.default.createElement(Loader.Loader, {
2871
2873
  className: "form__loader",
2872
- set: loaderSet,
2873
2874
  fill: loaderFill,
2874
2875
  itemFill: loaderItemFill,
2875
- text: loaderText
2876
- }))), (primaryButtonLabel || primaryButton || secondaryButtonLabel || secondaryButton || tertiaryButton || tertiaryButtonLabel) && /*#__PURE__*/React__default.default.createElement(Group$1.Group, {
2876
+ text: loaderText,
2877
+ set: loaderSet
2878
+ }))))), (primaryButtonLabel || primaryButton || secondaryButtonLabel || secondaryButton || tertiaryButton || tertiaryButtonLabel) && /*#__PURE__*/React__default.default.createElement(Group$1.Group, {
2879
+ className: "form__button",
2877
2880
  fill: buttonFill,
2878
- justifyContent: buttonJustifyContent,
2879
- direction: buttonDirection,
2880
2881
  padding: buttonPadding,
2881
- gap: buttonGap,
2882
- className: "form__button",
2882
+ dataTestId: dataTestIdButtons,
2883
2883
  dataTour: dataTourButtons,
2884
- dataTestId: dataTestIdButtons
2884
+ direction: buttonDirection,
2885
+ gap: buttonGap,
2886
+ justifyContent: buttonJustifyContent
2885
2887
  }, primaryButtonLabel ? /*#__PURE__*/React__default.default.createElement(Button.Button, {
2886
- width: "fill",
2887
2888
  className: "form__button-item",
2889
+ width: "fill",
2890
+ labelTextColor: primaryButtonLabelTextColor,
2891
+ labelTextSize: primaryButtonLabelSize,
2888
2892
  fill: primaryButtonFill,
2889
2893
  fillHover: primaryButtonFillHover,
2890
- size: primaryButtonSize,
2891
- labelTextColor: primaryButtonLabelTextColor,
2892
- labelSize: primaryButtonLabelSize,
2893
- labelTextWeight: primaryButtonLabelTextWeight,
2894
- label: primaryButtonLabel,
2894
+ dataTestId: dataTestIdPrimaryButton,
2895
2895
  dataTour: dataTourPrimaryButton,
2896
- dataTestId: dataTestIdPrimaryButton
2896
+ label: primaryButtonLabel,
2897
+ labelTextWeight: primaryButtonLabelTextWeight,
2898
+ size: primaryButtonSize
2897
2899
  }) : primaryButton, secondaryButtonLabel ? /*#__PURE__*/React__default.default.createElement(Button.Button, {
2898
- width: "fill",
2899
2900
  className: "form__button-item",
2901
+ width: "fill",
2902
+ labelTextColor: secondaryButtonLabelTextColor,
2903
+ labelTextSize: secondaryButtonLabelSize,
2900
2904
  fill: secondaryButtonFill,
2901
2905
  fillHover: secondaryButtonFillHover,
2902
- size: secondaryButtonSize,
2903
- labelTextColor: secondaryButtonLabelTextColor,
2904
- labelSize: secondaryButtonLabelSize,
2905
- labelTextWeight: secondaryButtonLabelTextWeight,
2906
- label: secondaryButtonLabel,
2907
- onClick: onClickSecondaryButton,
2906
+ dataTestId: dataTestIdSecondaryButton,
2908
2907
  dataTour: dataTourSecondaryButton,
2909
- dataTestId: dataTestIdSecondaryButton
2908
+ label: secondaryButtonLabel,
2909
+ labelTextWeight: secondaryButtonLabelTextWeight,
2910
+ size: secondaryButtonSize,
2911
+ onClick: onClickSecondaryButton
2910
2912
  }) : secondaryButton, tertiaryButtonLabel ? /*#__PURE__*/React__default.default.createElement(Button.Button, {
2911
- width: "fill",
2912
2913
  className: "form__button-item",
2914
+ width: "fill",
2915
+ labelTextColor: tertiaryButtonLabelTextColor,
2916
+ labelTextSize: tertiaryButtonLabelSize,
2913
2917
  fill: tertiaryButtonFill,
2914
2918
  fillHover: tertiaryButtonFillHover,
2915
- size: tertiaryButtonSize,
2916
- labelTextColor: tertiaryButtonLabelTextColor,
2917
- labelSize: tertiaryButtonLabelSize,
2918
- labelTextWeight: tertiaryButtonLabelTextWeight,
2919
- label: tertiaryButtonLabel,
2920
- onClick: onClickTertiaryButton,
2919
+ dataTestId: dataTestIdTertiaryButton,
2921
2920
  dataTour: dataTourTertiaryButton,
2922
- dataTestId: dataTestIdTertiaryButton
2921
+ label: tertiaryButtonLabel,
2922
+ labelTextWeight: tertiaryButtonLabelTextWeight,
2923
+ size: tertiaryButtonSize,
2924
+ onClick: onClickTertiaryButton
2923
2925
  }) : tertiaryButton), after);
2924
2926
  },
2925
2927
  subscription: {
2926
- submitting: true,
2927
- pristine: true,
2928
2928
  modifiedSinceLastSubmit: true,
2929
- submitError: true
2929
+ pristine: true,
2930
+ submitError: true,
2931
+ submitting: true
2930
2932
  },
2931
- validate: validate,
2932
2933
  onSubmit: onSubmit
2933
2934
  });
2934
2935
  });
2935
2936
  FinalForm.propTypes = {
2937
+ isLoading: PropTypes__default.default.bool,
2936
2938
  additionalProps: PropTypes__default.default.object,
2937
2939
  after: PropTypes__default.default.any,
2938
2940
  before: PropTypes__default.default.any,
@@ -2958,7 +2960,6 @@ FinalForm.propTypes = {
2958
2960
  groupGap: PropTypes__default.default.string,
2959
2961
  initialValues: PropTypes__default.default.any,
2960
2962
  initialValuesEqual: PropTypes__default.default.any,
2961
- isLoading: PropTypes__default.default.bool,
2962
2963
  language: PropTypes__default.default.string,
2963
2964
  loader: PropTypes__default.default.element,
2964
2965
  loaderFill: PropTypes__default.default.string,
@@ -3006,16 +3007,16 @@ FinalForm.propTypes = {
3006
3007
  onSubmit: PropTypes__default.default.func
3007
3008
  };
3008
3009
  FinalForm.defaultProps = {
3009
- additionalProps: {},
3010
+ direction: 'vertical',
3010
3011
  isLoading: false,
3011
- loaderSet: 'simple',
3012
+ additionalProps: {},
3013
+ buttonDirection: 'vertical',
3014
+ disableFieldsAutoComplete: false,
3015
+ language: 'en',
3012
3016
  loaderFill: 'surfacePrimary',
3013
3017
  loaderItemFill: 'surfaceItemAccent',
3014
- language: 'en',
3015
- titleSize: 'h1',
3016
- buttonDirection: 'vertical',
3017
- direction: 'vertical',
3018
- disableFieldsAutoComplete: false
3018
+ loaderSet: 'simple',
3019
+ titleSize: 'h1'
3019
3020
  };
3020
3021
 
3021
3022
  const DEFAULT_MESSAGES_FIELDS = {
@@ -2463,116 +2463,6 @@ ChipsField.propTypes = {
2463
2463
  onChange: PropTypes.func
2464
2464
  };
2465
2465
 
2466
- const focusOnError = (formElementsList, errors) => {
2467
- const selectsIds = Object.keys(errors).map(fieldName => {
2468
- if (fieldName === FORM_ERROR) {
2469
- // TODO: get from somewhere
2470
- return 'notification__item_status_error';
2471
- }
2472
- return `react-select-id_${fieldName}-input`;
2473
- });
2474
- const errorFieldElement = formElementsList.find(element => {
2475
- if (element.name) {
2476
- return getIn(errors, element.name);
2477
- } else {
2478
- return selectsIds.includes(element.id);
2479
- }
2480
- });
2481
- const errorsList = Object.keys(errors);
2482
- if (!errorFieldElement && errorsList.length) {
2483
- let errorElement;
2484
- try {
2485
- const fieldName = errorsList[0];
2486
- if (fieldName === FORM_ERROR) {
2487
- errorElement = document.querySelector('notification__item_status_error');
2488
- } else {
2489
- errorElement = document.querySelector(`#${fieldName}-error`);
2490
- if (!errorElement) {
2491
- errorElement = document.querySelector(`#id_${fieldName}`);
2492
- }
2493
- }
2494
- } catch (err) {
2495
- console.warn(err);
2496
- }
2497
- if (errorElement) {
2498
- errorElement.scrollIntoView({
2499
- block: 'center'
2500
- }); // , behavior: 'smooth'
2501
- }
2502
- }
2503
-
2504
- // The field is covered by the header because header is "sticky",
2505
- // that's we scroll manually so that the field falls into the center of the visible area
2506
- if (errorFieldElement) {
2507
- errorFieldElement.scrollIntoView({
2508
- block: 'center'
2509
- });
2510
- }
2511
- return null;
2512
- };
2513
- const focusOnErrorDecorator = createDecorator(null, focusOnError);
2514
- const setErrorsMutator = (args, state) => {
2515
- const [fieldName, data] = args;
2516
- const submitError = data.submitError;
2517
- const fieldError = data.error;
2518
- if (fieldName === 'non_field_errors') {
2519
- // state.formState.invalid = true
2520
- // state.formState.valid = false
2521
- state.formState.error = fieldError;
2522
- state.formState.submitError = submitError;
2523
- } else if (fieldName in state.fields) {
2524
- if (fieldError) {
2525
- const errorsState = Object.assign({}, state.formState.errors, {
2526
- [fieldName]: fieldError
2527
- });
2528
- state.fields[fieldName].touched = true;
2529
- state.fields[fieldName].error = fieldError;
2530
- state.formState.errors = errorsState;
2531
- }
2532
- if (submitError) {
2533
- const submitErrorsState = Object.assign({}, state.formState.submitErrors, {
2534
- [fieldName]: submitError
2535
- });
2536
-
2537
- // state.fields[fieldName].submitFailed = true
2538
- // state.fields[fieldName].submitSucceeded = false
2539
- state.fields[fieldName].submitError = submitError;
2540
- state.formState.submitErrors = submitErrorsState;
2541
- state.formState.submitFailed = true;
2542
- state.formState.submitSucceeded = false;
2543
- state.formState.lastSubmittedValues = state.formState.values;
2544
- }
2545
- }
2546
- };
2547
- const sendFormDataToServer = async (url, data) => {
2548
- try {
2549
- const response = await axios({
2550
- url: url,
2551
- method: 'POST',
2552
- data: data
2553
- });
2554
- return {
2555
- success: true,
2556
- response
2557
- };
2558
- } catch (error) {
2559
- const formErrors = {};
2560
- if (typeof error.response?.data === 'string') {
2561
- formErrors[FORM_ERROR] = 'Something went wrong';
2562
- }
2563
- if (typeof error.response?.data === 'object') {
2564
- Object.entries(error.response.data).forEach(([fieldName, errorsList]) => {
2565
- formErrors[fieldName] = errorsList[0];
2566
- });
2567
- }
2568
- return {
2569
- success: false,
2570
- formErrors,
2571
- error
2572
- };
2573
- }
2574
- };
2575
-
2576
2466
  const formTypes = {
2577
2467
  checkbox: 'checkbox',
2578
2468
  custom: 'custom',
@@ -2695,8 +2585,119 @@ function generateField(field, config, props) {
2695
2585
  }
2696
2586
  }
2697
2587
 
2588
+ const focusOnError = (formElementsList, errors) => {
2589
+ const selectsIds = Object.keys(errors).map(fieldName => {
2590
+ if (fieldName === FORM_ERROR) {
2591
+ // TODO: get from somewhere
2592
+ return 'notification__item_status_error';
2593
+ }
2594
+ return `react-select-id_${fieldName}-input`;
2595
+ });
2596
+ const errorFieldElement = formElementsList.find(element => {
2597
+ if (element.name) {
2598
+ return getIn(errors, element.name);
2599
+ } else {
2600
+ return selectsIds.includes(element.id);
2601
+ }
2602
+ });
2603
+ const errorsList = Object.keys(errors);
2604
+ if (!errorFieldElement && errorsList.length) {
2605
+ let errorElement;
2606
+ try {
2607
+ const fieldName = errorsList[0];
2608
+ if (fieldName === FORM_ERROR) {
2609
+ errorElement = document.querySelector('notification__item_status_error');
2610
+ } else {
2611
+ errorElement = document.querySelector(`#${fieldName}-error`);
2612
+ if (!errorElement) {
2613
+ errorElement = document.querySelector(`#id_${fieldName}`);
2614
+ }
2615
+ }
2616
+ } catch (err) {
2617
+ console.warn(err);
2618
+ }
2619
+ if (errorElement) {
2620
+ errorElement.scrollIntoView({
2621
+ block: 'center'
2622
+ }); // , behavior: 'smooth'
2623
+ }
2624
+ }
2625
+
2626
+ // The field is covered by the header because header is "sticky",
2627
+ // that's we scroll manually so that the field falls into the center of the visible area
2628
+ if (errorFieldElement) {
2629
+ errorFieldElement.scrollIntoView({
2630
+ block: 'center'
2631
+ });
2632
+ }
2633
+ return null;
2634
+ };
2635
+ const focusOnErrorDecorator = createDecorator(null, focusOnError);
2636
+ const setErrorsMutator = (args, state) => {
2637
+ const [fieldName, data] = args;
2638
+ const submitError = data.submitError;
2639
+ const fieldError = data.error;
2640
+ if (fieldName === 'non_field_errors') {
2641
+ // state.formState.invalid = true
2642
+ // state.formState.valid = false
2643
+ state.formState.error = fieldError;
2644
+ state.formState.submitError = submitError;
2645
+ } else if (fieldName in state.fields) {
2646
+ if (fieldError) {
2647
+ const errorsState = Object.assign({}, state.formState.errors, {
2648
+ [fieldName]: fieldError
2649
+ });
2650
+ state.fields[fieldName].touched = true;
2651
+ state.fields[fieldName].error = fieldError;
2652
+ state.formState.errors = errorsState;
2653
+ }
2654
+ if (submitError) {
2655
+ const submitErrorsState = Object.assign({}, state.formState.submitErrors, {
2656
+ [fieldName]: submitError
2657
+ });
2658
+
2659
+ // state.fields[fieldName].submitFailed = true
2660
+ // state.fields[fieldName].submitSucceeded = false
2661
+ state.fields[fieldName].submitError = submitError;
2662
+ state.formState.submitErrors = submitErrorsState;
2663
+ state.formState.submitFailed = true;
2664
+ state.formState.submitSucceeded = false;
2665
+ state.formState.lastSubmittedValues = state.formState.values;
2666
+ }
2667
+ }
2668
+ };
2669
+ const sendFormDataToServer = async (url, data) => {
2670
+ try {
2671
+ const response = await axios({
2672
+ url: url,
2673
+ method: 'POST',
2674
+ data: data
2675
+ });
2676
+ return {
2677
+ success: true,
2678
+ response
2679
+ };
2680
+ } catch (error) {
2681
+ const formErrors = {};
2682
+ if (typeof error.response?.data === 'string') {
2683
+ formErrors[FORM_ERROR] = 'Something went wrong';
2684
+ }
2685
+ if (typeof error.response?.data === 'object') {
2686
+ Object.entries(error.response.data).forEach(([fieldName, errorsList]) => {
2687
+ formErrors[fieldName] = errorsList[0];
2688
+ });
2689
+ }
2690
+ return {
2691
+ success: false,
2692
+ formErrors,
2693
+ error
2694
+ };
2695
+ }
2696
+ };
2697
+
2698
2698
  const FinalForm = /*#__PURE__*/React.forwardRef(function FinalForm(props, ref) {
2699
2699
  const {
2700
+ isLoading,
2700
2701
  additionalProps,
2701
2702
  after,
2702
2703
  before,
@@ -2707,13 +2708,13 @@ const FinalForm = /*#__PURE__*/React.forwardRef(function FinalForm(props, ref) {
2707
2708
  buttonPadding,
2708
2709
  className,
2709
2710
  config,
2710
- dataTour,
2711
2711
  dataTestId,
2712
- dataTourButtons,
2713
2712
  dataTestIdButtons,
2714
2713
  dataTestIdPrimaryButton,
2715
2714
  dataTestIdSecondaryButton,
2716
2715
  dataTestIdTertiaryButton,
2716
+ dataTour,
2717
+ dataTourButtons,
2717
2718
  dataTourPrimaryButton,
2718
2719
  dataTourSecondaryButton,
2719
2720
  dataTourTertiaryButton,
@@ -2727,7 +2728,6 @@ const FinalForm = /*#__PURE__*/React.forwardRef(function FinalForm(props, ref) {
2727
2728
  groupGap,
2728
2729
  initialValues,
2729
2730
  initialValuesEqual,
2730
- isLoading,
2731
2731
  language,
2732
2732
  loader,
2733
2733
  loaderFill,
@@ -2737,10 +2737,6 @@ const FinalForm = /*#__PURE__*/React.forwardRef(function FinalForm(props, ref) {
2737
2737
  mutators,
2738
2738
  notificationCloseButton,
2739
2739
  notificationType,
2740
- onChangeFormValues,
2741
- onClickSecondaryButton,
2742
- onClickTertiaryButton,
2743
- onSubmit,
2744
2740
  primaryButton,
2745
2741
  primaryButtonFill,
2746
2742
  primaryButtonFillHover,
@@ -2749,6 +2745,7 @@ const FinalForm = /*#__PURE__*/React.forwardRef(function FinalForm(props, ref) {
2749
2745
  primaryButtonLabelTextColor,
2750
2746
  primaryButtonLabelTextWeight,
2751
2747
  primaryButtonSize,
2748
+ renderFieldsWrapper = wrapperChildren => wrapperChildren,
2752
2749
  secondaryButton,
2753
2750
  secondaryButtonFill,
2754
2751
  secondaryButtonFillHover,
@@ -2771,7 +2768,11 @@ const FinalForm = /*#__PURE__*/React.forwardRef(function FinalForm(props, ref) {
2771
2768
  titleTextSize,
2772
2769
  titleTextWeight,
2773
2770
  type,
2774
- validationSchema
2771
+ validationSchema,
2772
+ onChangeFormValues,
2773
+ onClickSecondaryButton,
2774
+ onClickTertiaryButton,
2775
+ onSubmit
2775
2776
  } = props;
2776
2777
  const validate = useYupValidationSchema(validationSchema, language);
2777
2778
  const onRefFormInstance = useCallback(formInstance => {
@@ -2803,26 +2804,27 @@ const FinalForm = /*#__PURE__*/React.forwardRef(function FinalForm(props, ref) {
2803
2804
  initialValues: initialValues,
2804
2805
  initialValuesEqual: initialValuesEqual,
2805
2806
  mutators: mutators,
2807
+ validate: validate,
2806
2808
  render: ({
2809
+ form,
2807
2810
  handleSubmit,
2808
- submitError,
2809
2811
  modifiedSinceLastSubmit,
2810
- form
2812
+ submitError
2811
2813
  }) => {
2812
2814
  return /*#__PURE__*/React.createElement("form", {
2813
- style: formStyles,
2814
- className: clsx(className, 'form', set && `form_set_${set}`, type && `form_type_${type}`, directionClass, fillClass, shapeClass, elevationClass),
2815
- name: formName,
2816
- "data-tour": dataTour,
2817
- "data-test-id": dataTestId
2815
+ className: clsx(className, 'form', set && `form_set_${set}`, type && `form_type_${type}`, directionClass, fillClass, shapeClass, elevationClass)
2818
2816
  // We no need set reference to html element, we need reference to "final-form" instance
2819
2817
  ,
2820
2818
  ref: () => onRefFormInstance(form),
2821
- onSubmit: handleSubmit,
2819
+ name: formName,
2820
+ autoCapitalize: disableFieldsAutoComplete ? 'off' : undefined,
2822
2821
  autoComplete: disableFieldsAutoComplete ? 'off' : undefined,
2823
2822
  autoCorrect: disableFieldsAutoComplete ? 'off' : undefined,
2824
- autoCapitalize: disableFieldsAutoComplete ? 'off' : undefined,
2825
- spellCheck: disableFieldsAutoComplete ? 'false' : undefined
2823
+ "data-test-id": dataTestId,
2824
+ "data-tour": dataTour,
2825
+ spellCheck: disableFieldsAutoComplete ? 'false' : undefined,
2826
+ style: formStyles,
2827
+ onSubmit: handleSubmit
2826
2828
  }, before, title && /*#__PURE__*/React.createElement(Title, {
2827
2829
  className: "form__title",
2828
2830
  size: titleTextSize,
@@ -2837,88 +2839,88 @@ const FinalForm = /*#__PURE__*/React.forwardRef(function FinalForm(props, ref) {
2837
2839
  className: clsx('notification', 'form-notification', notificationType ? `form-notification_type_${notificationType}` : 'form-notification_type_global')
2838
2840
  }, /*#__PURE__*/React.createElement(NotificationItem, {
2839
2841
  className: "form-notification__item",
2840
- titleTextSize: "h6",
2841
2842
  closeButton: notificationCloseButton,
2843
+ status: "error",
2842
2844
  title: form.getState().submitError,
2843
- set: "form",
2844
- status: "error"
2845
+ titleTextSize: "h6",
2846
+ set: "form"
2845
2847
  })), onChangeFormValues && /*#__PURE__*/React.createElement(FormSpy, {
2846
2848
  subscription: {
2847
2849
  values: true
2848
2850
  },
2849
2851
  onChange: onChangeFormValues
2850
- }), Boolean(Object.keys(config).length) && /*#__PURE__*/React.createElement(Group$1, {
2852
+ }), Boolean(Object.keys(config).length) && /*#__PURE__*/React.createElement(React.Fragment, null, renderFieldsWrapper( /*#__PURE__*/React.createElement(Group$1, {
2853
+ className: "form__wrapper",
2851
2854
  direction: "vertical",
2852
- gap: fieldsGap || groupGap,
2853
- className: "form__wrapper"
2855
+ gap: fieldsGap || groupGap
2854
2856
  }, Object.keys(config).map(key => generateField(config[key], {
2855
2857
  key
2856
2858
  }, additionalProps[config[key].name])), isLoading && (loader || /*#__PURE__*/React.createElement(Loader, {
2857
2859
  className: "form__loader",
2858
- set: loaderSet,
2859
2860
  fill: loaderFill,
2860
2861
  itemFill: loaderItemFill,
2861
- text: loaderText
2862
- }))), (primaryButtonLabel || primaryButton || secondaryButtonLabel || secondaryButton || tertiaryButton || tertiaryButtonLabel) && /*#__PURE__*/React.createElement(Group$1, {
2862
+ text: loaderText,
2863
+ set: loaderSet
2864
+ }))))), (primaryButtonLabel || primaryButton || secondaryButtonLabel || secondaryButton || tertiaryButton || tertiaryButtonLabel) && /*#__PURE__*/React.createElement(Group$1, {
2865
+ className: "form__button",
2863
2866
  fill: buttonFill,
2864
- justifyContent: buttonJustifyContent,
2865
- direction: buttonDirection,
2866
2867
  padding: buttonPadding,
2867
- gap: buttonGap,
2868
- className: "form__button",
2868
+ dataTestId: dataTestIdButtons,
2869
2869
  dataTour: dataTourButtons,
2870
- dataTestId: dataTestIdButtons
2870
+ direction: buttonDirection,
2871
+ gap: buttonGap,
2872
+ justifyContent: buttonJustifyContent
2871
2873
  }, primaryButtonLabel ? /*#__PURE__*/React.createElement(Button, {
2872
- width: "fill",
2873
2874
  className: "form__button-item",
2875
+ width: "fill",
2876
+ labelTextColor: primaryButtonLabelTextColor,
2877
+ labelTextSize: primaryButtonLabelSize,
2874
2878
  fill: primaryButtonFill,
2875
2879
  fillHover: primaryButtonFillHover,
2876
- size: primaryButtonSize,
2877
- labelTextColor: primaryButtonLabelTextColor,
2878
- labelSize: primaryButtonLabelSize,
2879
- labelTextWeight: primaryButtonLabelTextWeight,
2880
- label: primaryButtonLabel,
2880
+ dataTestId: dataTestIdPrimaryButton,
2881
2881
  dataTour: dataTourPrimaryButton,
2882
- dataTestId: dataTestIdPrimaryButton
2882
+ label: primaryButtonLabel,
2883
+ labelTextWeight: primaryButtonLabelTextWeight,
2884
+ size: primaryButtonSize
2883
2885
  }) : primaryButton, secondaryButtonLabel ? /*#__PURE__*/React.createElement(Button, {
2884
- width: "fill",
2885
2886
  className: "form__button-item",
2887
+ width: "fill",
2888
+ labelTextColor: secondaryButtonLabelTextColor,
2889
+ labelTextSize: secondaryButtonLabelSize,
2886
2890
  fill: secondaryButtonFill,
2887
2891
  fillHover: secondaryButtonFillHover,
2888
- size: secondaryButtonSize,
2889
- labelTextColor: secondaryButtonLabelTextColor,
2890
- labelSize: secondaryButtonLabelSize,
2891
- labelTextWeight: secondaryButtonLabelTextWeight,
2892
- label: secondaryButtonLabel,
2893
- onClick: onClickSecondaryButton,
2892
+ dataTestId: dataTestIdSecondaryButton,
2894
2893
  dataTour: dataTourSecondaryButton,
2895
- dataTestId: dataTestIdSecondaryButton
2894
+ label: secondaryButtonLabel,
2895
+ labelTextWeight: secondaryButtonLabelTextWeight,
2896
+ size: secondaryButtonSize,
2897
+ onClick: onClickSecondaryButton
2896
2898
  }) : secondaryButton, tertiaryButtonLabel ? /*#__PURE__*/React.createElement(Button, {
2897
- width: "fill",
2898
2899
  className: "form__button-item",
2900
+ width: "fill",
2901
+ labelTextColor: tertiaryButtonLabelTextColor,
2902
+ labelTextSize: tertiaryButtonLabelSize,
2899
2903
  fill: tertiaryButtonFill,
2900
2904
  fillHover: tertiaryButtonFillHover,
2901
- size: tertiaryButtonSize,
2902
- labelTextColor: tertiaryButtonLabelTextColor,
2903
- labelSize: tertiaryButtonLabelSize,
2904
- labelTextWeight: tertiaryButtonLabelTextWeight,
2905
- label: tertiaryButtonLabel,
2906
- onClick: onClickTertiaryButton,
2905
+ dataTestId: dataTestIdTertiaryButton,
2907
2906
  dataTour: dataTourTertiaryButton,
2908
- dataTestId: dataTestIdTertiaryButton
2907
+ label: tertiaryButtonLabel,
2908
+ labelTextWeight: tertiaryButtonLabelTextWeight,
2909
+ size: tertiaryButtonSize,
2910
+ onClick: onClickTertiaryButton
2909
2911
  }) : tertiaryButton), after);
2910
2912
  },
2911
2913
  subscription: {
2912
- submitting: true,
2913
- pristine: true,
2914
2914
  modifiedSinceLastSubmit: true,
2915
- submitError: true
2915
+ pristine: true,
2916
+ submitError: true,
2917
+ submitting: true
2916
2918
  },
2917
- validate: validate,
2918
2919
  onSubmit: onSubmit
2919
2920
  });
2920
2921
  });
2921
2922
  FinalForm.propTypes = {
2923
+ isLoading: PropTypes.bool,
2922
2924
  additionalProps: PropTypes.object,
2923
2925
  after: PropTypes.any,
2924
2926
  before: PropTypes.any,
@@ -2944,7 +2946,6 @@ FinalForm.propTypes = {
2944
2946
  groupGap: PropTypes.string,
2945
2947
  initialValues: PropTypes.any,
2946
2948
  initialValuesEqual: PropTypes.any,
2947
- isLoading: PropTypes.bool,
2948
2949
  language: PropTypes.string,
2949
2950
  loader: PropTypes.element,
2950
2951
  loaderFill: PropTypes.string,
@@ -2992,16 +2993,16 @@ FinalForm.propTypes = {
2992
2993
  onSubmit: PropTypes.func
2993
2994
  };
2994
2995
  FinalForm.defaultProps = {
2995
- additionalProps: {},
2996
+ direction: 'vertical',
2996
2997
  isLoading: false,
2997
- loaderSet: 'simple',
2998
+ additionalProps: {},
2999
+ buttonDirection: 'vertical',
3000
+ disableFieldsAutoComplete: false,
3001
+ language: 'en',
2998
3002
  loaderFill: 'surfacePrimary',
2999
3003
  loaderItemFill: 'surfaceItemAccent',
3000
- language: 'en',
3001
- titleSize: 'h1',
3002
- buttonDirection: 'vertical',
3003
- direction: 'vertical',
3004
- disableFieldsAutoComplete: false
3004
+ loaderSet: 'simple',
3005
+ titleSize: 'h1'
3005
3006
  };
3006
3007
 
3007
3008
  const DEFAULT_MESSAGES_FIELDS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itcase/forms",
3
- "version": "1.0.64",
3
+ "version": "1.0.65",
4
4
  "description": "Forms fields, inputs, etc.",
5
5
  "keywords": [],
6
6
  "license": "MIT",