@evoke-platform/ui-components 1.10.0-dev.3 → 1.10.0-dev.31

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.
Files changed (71) hide show
  1. package/dist/published/components/core/Autocomplete/Autocomplete.js +4 -2
  2. package/dist/published/components/core/Autocomplete/Autocomplete.test.js +112 -3
  3. package/dist/published/components/core/TextField/TextField.js +1 -1
  4. package/dist/published/components/core/TextField/TextField.test.js +0 -2
  5. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +1 -1
  6. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.d.ts +1 -0
  7. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +428 -0
  8. package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +19 -6
  9. package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableField.js +1 -1
  10. package/dist/published/components/custom/Form/tests/Form.test.js +0 -2
  11. package/dist/published/components/custom/Form/utils.js +1 -0
  12. package/dist/published/components/custom/FormField/DatePickerSelect/DatePickerSelect.js +14 -1
  13. package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js +14 -1
  14. package/dist/published/components/custom/FormField/InputFieldComponent/InputFieldComponent.test.js +0 -2
  15. package/dist/published/components/custom/FormField/Select/Select.test.js +0 -2
  16. package/dist/published/components/custom/FormField/TimePickerSelect/TimePickerSelect.js +14 -1
  17. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +2 -1
  18. package/dist/published/components/custom/FormV2/FormRenderer.js +19 -7
  19. package/dist/published/components/custom/FormV2/FormRendererContainer.js +117 -74
  20. package/dist/published/components/custom/FormV2/components/AccordionSections.js +7 -2
  21. package/dist/published/components/custom/FormV2/components/Body.d.ts +1 -1
  22. package/dist/published/components/custom/FormV2/components/FieldWrapper.js +1 -1
  23. package/dist/published/components/custom/FormV2/components/Footer.d.ts +1 -0
  24. package/dist/published/components/custom/FormV2/components/Footer.js +8 -5
  25. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +3 -2
  26. package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.d.ts +9 -0
  27. package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.js +32 -15
  28. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.js +2 -2
  29. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.d.ts +0 -3
  30. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +36 -49
  31. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +16 -3
  32. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +16 -4
  33. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +2 -1
  34. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +16 -3
  35. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +31 -5
  36. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +15 -3
  37. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +109 -81
  38. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +38 -16
  39. package/dist/published/components/custom/FormV2/components/Header.d.ts +5 -3
  40. package/dist/published/components/custom/FormV2/components/Header.js +47 -9
  41. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +46 -35
  42. package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrors.js +1 -1
  43. package/dist/published/components/custom/FormV2/components/types.d.ts +1 -0
  44. package/dist/published/components/custom/FormV2/components/utils.d.ts +4 -4
  45. package/dist/published/components/custom/FormV2/components/utils.js +13 -16
  46. package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +289 -45
  47. package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +664 -16
  48. package/dist/published/components/custom/FormV2/tests/test-data.d.ts +1 -0
  49. package/dist/published/components/custom/FormV2/tests/test-data.js +140 -0
  50. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +3 -0
  51. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +155 -0
  52. package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.d.ts +13 -0
  53. package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.js +144 -0
  54. package/dist/published/components/custom/ViewDetailsV2/index.d.ts +3 -0
  55. package/dist/published/components/custom/ViewDetailsV2/index.js +2 -0
  56. package/dist/published/components/custom/index.d.ts +2 -0
  57. package/dist/published/components/custom/index.js +1 -0
  58. package/dist/published/index.d.ts +6 -6
  59. package/dist/published/index.js +1 -1
  60. package/dist/published/stories/FormRenderer.stories.d.ts +8 -4
  61. package/dist/published/stories/FormRendererContainer.stories.d.ts +26 -0
  62. package/dist/published/stories/FormRendererContainer.stories.js +5 -0
  63. package/dist/published/stories/FormRendererData.d.ts +12 -0
  64. package/dist/published/stories/FormRendererData.js +29 -44
  65. package/dist/published/stories/ViewDetailsV2Container.stories.d.ts +26 -0
  66. package/dist/published/stories/ViewDetailsV2Container.stories.js +37 -0
  67. package/dist/published/stories/ViewDetailsV2Data.d.ts +4 -0
  68. package/dist/published/stories/ViewDetailsV2Data.js +203 -0
  69. package/dist/published/stories/sharedMswHandlers.js +49 -10
  70. package/dist/published/theme/hooks.d.ts +4 -3
  71. package/package.json +12 -8
@@ -221,7 +221,10 @@ const ValueEditor = (props) => {
221
221
  })
222
222
  .filter((item) => item !== '');
223
223
  handleOnChange(uniqueSelections.length ? Array.from(new Set(uniqueSelections)) : '');
224
- }, isOptionEqualToValue: (option, value) => option.value === value.value, renderInput: (params) => (React.createElement(TextField, { label: params.label, ...params, size: "small" })), groupBy: (option) => isPresetValue(option.value) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sx: styles.input, readOnly: readOnly }));
224
+ }, isOptionEqualToValue: (option, value) => option.value === value.value, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, ...params, size: "small", inputProps: {
225
+ ...params.inputProps,
226
+ 'aria-label': 'Select or enter a value',
227
+ } })), groupBy: (option) => isPresetValue(option.value) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sx: styles.input, readOnly: readOnly }));
225
228
  }
226
229
  else {
227
230
  return (React.createElement(TextField, { inputRef: inputRef, value: ['null', 'notNull'].includes(operator) ? '' : value, disabled: disabled || ['null', 'notNull'].includes(operator), onChange: (e) => {
@@ -240,11 +243,14 @@ const ValueEditor = (props) => {
240
243
  const options = [{ label: 'True', value: true }, { label: 'False', value: false }, ...presetValues];
241
244
  return (React.createElement(Autocomplete, { options: options, value: options.find((opt) => opt.value === value) ?? value, onChange: (event, newValue) => {
242
245
  handleOnChange(newValue ? newValue.value : '');
243
- }, isOptionEqualToValue: (option, value) => option.value === value.value, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, label: params.label, ...params, size: "small" })), groupBy: (option) => isPresetValue(option.value) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sortBy: "NONE", sx: styles.input, readOnly: readOnly }));
246
+ }, isOptionEqualToValue: (option, value) => option.value === value.value, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, size: "small", ...params, inputProps: {
247
+ 'aria-label': 'Select or enter a value',
248
+ ...params.inputProps,
249
+ } })), groupBy: (option) => isPresetValue(option.value) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sortBy: "NONE", sx: styles.input, readOnly: readOnly }));
244
250
  }
245
251
  else {
246
- const isMultiple = inputType === 'array' || isMultipleOperator || values?.length;
247
- if (isMultiple) {
252
+ const isMultiple = inputType === 'array' || isMultipleOperator;
253
+ if (isMultiple || values?.length) {
248
254
  const options = [...values, ...presetValues];
249
255
  return (React.createElement(Autocomplete, { freeSolo: inputType !== 'array' && fieldData.valueEditorType !== 'select', multiple: isMultiple, options: options, value: isMultiple
250
256
  ? Array.isArray(value)
@@ -263,7 +269,11 @@ const ValueEditor = (props) => {
263
269
  }
264
270
  else {
265
271
  value =
266
- typeof newValue === 'string' ? newValue : newValue.value;
272
+ typeof newValue === 'string'
273
+ ? newValue
274
+ : !newValue
275
+ ? newValue
276
+ : newValue.value;
267
277
  }
268
278
  handleOnChange(value);
269
279
  }, onBlur: () => {
@@ -285,7 +295,10 @@ const ValueEditor = (props) => {
285
295
  }
286
296
  }, onInputChange: (event, newInputValue) => {
287
297
  setInputValue(newInputValue);
288
- }, inputValue: inputValue, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, label: params.label, ...params, size: "small" })), isOptionEqualToValue: (option, value) => option?.value === value.value, groupBy: (option) => isPresetValue(option.value) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sortBy: "NONE", sx: styles.input, readOnly: readOnly }));
298
+ }, inputValue: inputValue, renderInput: (params) => (React.createElement(TextField, { inputRef: inputRef, ...params, size: "small", inputProps: {
299
+ ...params.inputProps,
300
+ 'aria-label': 'Select or enter a value',
301
+ } })), isOptionEqualToValue: (option, value) => typeof value === 'string' ? option?.value === value : option?.value === value.value, groupBy: (option) => isPresetValue(option.value) ? context.presetGroupLabel || 'Preset Values' : 'Options', renderGroup: groupRenderGroup, sortBy: "NONE", sx: styles.input, readOnly: readOnly }));
289
302
  }
290
303
  else {
291
304
  return (React.createElement(TextField, { inputRef: inputRef, value: ['null', 'notNull'].includes(operator) ? '' : value, disabled: ['null', 'notNull'].includes(operator), onChange: (e) => handleOnChange(e.target.value), onClick: onClick, placeholder: "Value", size: "small", sx: styles.input, readOnly: readOnly }));
@@ -430,7 +430,7 @@ const RepeatableField = (props) => {
430
430
  hasCreateAction && (React.createElement(Button, { variant: "contained", sx: styles.addButton, onClick: addRow }, "Add"))),
431
431
  relatedObject && openDialog && (React.createElement(ActionDialog, { object: relatedObject, open: openDialog, apiServices: apiServices, onClose: () => setOpenDialog(false), instanceInput: dialogType === 'update' ? (relatedInstances.find((i) => i.id === selectedRow) ?? {}) : {}, handleSubmit: save,
432
432
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
433
- objectInputCommonProps: { apiServices }, action: relatedObject?.actions?.find((a) => a.id ===
433
+ objectInputCommonProps: { apiServices, setSnackbarError }, action: relatedObject?.actions?.find((a) => a.id ===
434
434
  (dialogType === 'create' ? '_create' : dialogType === 'update' ? '_update' : '_delete')), instanceId: selectedRow, queryAddresses: queryAddresses, user: user, associatedObject: instance.id && property.relatedPropertyId
435
435
  ? { instanceId: instance.id, propertyId: property.relatedPropertyId }
436
436
  : undefined, richTextEditor: richTextEditor })),
@@ -1,5 +1,4 @@
1
1
  import { ApiServices } from '@evoke-platform/context';
2
- import * as matchers from '@testing-library/jest-dom/matchers';
3
2
  import { render, screen, waitFor, within } from '@testing-library/react';
4
3
  import userEvent from '@testing-library/user-event';
5
4
  import axios from 'axios';
@@ -10,7 +9,6 @@ import React from 'react';
10
9
  import { expect, it } from 'vitest';
11
10
  import Form from '../Common/Form';
12
11
  import { accessibility508Object, licenseObject, npLicense, npSpecialtyType1, npSpecialtyType2, rnLicense, rnSpecialtyType1, rnSpecialtyType2, specialtyObject, specialtyTypeObject, users, } from './test-data';
13
- expect.extend(matchers);
14
12
  const removePoppers = () => {
15
13
  const portalSelectors = ['.MuiAutocomplete-popper'];
16
14
  portalSelectors.forEach((selector) => {
@@ -784,6 +784,7 @@ formComponents, allCriteriaInputs, instance, objectPropertyInputProps, associate
784
784
  item.autoSave = autoSave;
785
785
  item.apiServices = objectPropertyInputProps?.apiServices;
786
786
  item.user = objectPropertyInputProps?.user;
787
+ item.setSnackbarError = objectPropertyInputProps?.setSnackbarError;
787
788
  item.defaultPages = defaultPages;
788
789
  item.navigateTo = navigateTo;
789
790
  item.allCriteriaInputs = allCriteriaInputs;
@@ -1,6 +1,7 @@
1
1
  import { DateTimeFormatter } from '@js-joda/core';
2
2
  import { omit } from 'lodash';
3
3
  import React, { useEffect, useState } from 'react';
4
+ import { useFormContext } from '../../../../theme/hooks';
4
5
  import { InvalidDate, LocalDate, nativeJs } from '../../../../util';
5
6
  import { DatePicker, LocalizationProvider, TextField } from '../../../core';
6
7
  import InputFieldComponent from '../InputFieldComponent/InputFieldComponent';
@@ -29,6 +30,7 @@ const asMonthDayYearFormat = (date) => {
29
30
  const DatePickerSelect = (props) => {
30
31
  const { id, property, defaultValue, error, errorMessage, readOnly, required, size, onBlur, onChange, additionalProps, } = props;
31
32
  const [value, setValue] = useState(asCalendarDate(defaultValue));
33
+ const { onAutosave } = useFormContext();
32
34
  useEffect(() => {
33
35
  setValue(asCalendarDate(defaultValue));
34
36
  }, [defaultValue]);
@@ -36,8 +38,19 @@ const DatePickerSelect = (props) => {
36
38
  setValue(date);
37
39
  onChange && onChange(property.id, date, property);
38
40
  };
41
+ const handleAccept = async () => {
42
+ // Trigger autosave when date is accepted (picker closes after selection)
43
+ if (onAutosave) {
44
+ try {
45
+ await onAutosave(id);
46
+ }
47
+ catch (error) {
48
+ console.error('Autosave failed:', error);
49
+ }
50
+ }
51
+ };
39
52
  return readOnly ? (React.createElement(InputFieldComponent, { ...{ ...props, defaultValue: asMonthDayYearFormat(value) } })) : (React.createElement(LocalizationProvider, null,
40
- React.createElement(DatePicker, { value: value, onChange: handleChange, inputFormat: "MM/dd/yyyy", renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium',
53
+ React.createElement(DatePicker, { value: value, onChange: handleChange, onAccept: handleAccept, inputFormat: "MM/dd/yyyy", renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium',
41
54
  // merges MUI inputProps with additionalProps.inputProps in a way that still shows the value
42
55
  inputProps: {
43
56
  ...params.inputProps,
@@ -1,6 +1,7 @@
1
1
  import { LocalDate, LocalDateTime, LocalTime, nativeJs } from '@js-joda/core';
2
2
  import { omit } from 'lodash';
3
3
  import React, { useEffect, useState } from 'react';
4
+ import { useFormContext } from '../../../../theme/hooks';
4
5
  import { InvalidDate } from '../../../../util';
5
6
  import { DateTimePicker, LocalizationProvider, TextField } from '../../../core';
6
7
  import InputFieldComponent from '../InputFieldComponent/InputFieldComponent';
@@ -30,6 +31,7 @@ const formatDateTime = (date) => {
30
31
  const DateTimePickerSelect = (props) => {
31
32
  const { id, property, defaultValue, error, errorMessage, readOnly, required, size, onBlur, additionalProps } = props;
32
33
  const [value, setValue] = useState(asCalendarDate(defaultValue));
34
+ const { onAutosave } = useFormContext();
33
35
  useEffect(() => {
34
36
  setValue(asCalendarDate(defaultValue));
35
37
  }, [defaultValue]);
@@ -43,8 +45,19 @@ const DateTimePickerSelect = (props) => {
43
45
  setValue(date);
44
46
  props.onChange && props.onChange(property.id, date, property);
45
47
  };
48
+ const handleAccept = async () => {
49
+ // Trigger autosave when date/time is accepted (picker closes after selection)
50
+ if (onAutosave) {
51
+ try {
52
+ await onAutosave(id);
53
+ }
54
+ catch (error) {
55
+ console.error('Autosave failed:', error);
56
+ }
57
+ }
58
+ };
46
59
  return readOnly ? (React.createElement(InputFieldComponent, { ...{ ...props, defaultValue: formatDateTime(value) } })) : (React.createElement(LocalizationProvider, null,
47
- React.createElement(DateTimePicker, { value: value, onChange: handleChange, renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium',
60
+ React.createElement(DateTimePicker, { value: value, onChange: handleChange, onAccept: handleAccept, renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium',
48
61
  // merges MUI inputProps with additionalProps.inputProps in a way that still shows the value
49
62
  inputProps: {
50
63
  ...params.inputProps,
@@ -1,10 +1,8 @@
1
- import * as matchers from '@testing-library/jest-dom/matchers';
2
1
  import { render, screen } from '@testing-library/react';
3
2
  import { userEvent } from '@testing-library/user-event';
4
3
  import React from 'react';
5
4
  import { describe, expect, it, vi } from 'vitest';
6
5
  import InputField from './InputFieldComponent';
7
- expect.extend(matchers);
8
6
  describe('Free-text input', () => {
9
7
  // Right now an object property is required for this to function, but eventually this should go
10
8
  // away.
@@ -1,10 +1,8 @@
1
- import * as matchers from '@testing-library/jest-dom/matchers';
2
1
  import { render, screen } from '@testing-library/react';
3
2
  import { userEvent } from '@testing-library/user-event';
4
3
  import React from 'react';
5
4
  import { describe, expect, it, vi } from 'vitest';
6
5
  import Select from './Select';
7
- expect.extend(matchers);
8
6
  describe('Single select', () => {
9
7
  // Right now an object property is required for this to function, but eventually this should go
10
8
  // away.
@@ -3,11 +3,13 @@ import { TimePicker } from '@mui/x-date-pickers';
3
3
  import { isUndefined, omit, padStart } from 'lodash';
4
4
  import { DateTime } from 'luxon';
5
5
  import React, { useEffect, useState } from 'react';
6
+ import { useFormContext } from '../../../../theme/hooks';
6
7
  import { InvalidDate } from '../../../../util';
7
8
  import { LocalizationProvider, TextField } from '../../../core';
8
9
  import InputFieldComponent from '../InputFieldComponent/InputFieldComponent';
9
10
  const TimePickerSelect = (props) => {
10
11
  const { id, property, defaultValue, error, errorMessage, readOnly, required, size, onBlur, placeholder, additionalProps, } = props;
12
+ const { onAutosave } = useFormContext();
11
13
  const values = defaultValue ? defaultValue.split(':') : undefined;
12
14
  const hour = values ? parseInt(values[0]) : undefined;
13
15
  const minute = values ? parseInt(values[1]) : undefined;
@@ -41,11 +43,22 @@ const TimePickerSelect = (props) => {
41
43
  props.onChange && props.onChange(property.id, date, property);
42
44
  }
43
45
  };
46
+ const handleAccept = async () => {
47
+ // Trigger autosave when time is accepted (picker closes after selection)
48
+ if (onAutosave) {
49
+ try {
50
+ await onAutosave(id);
51
+ }
52
+ catch (error) {
53
+ console.error('Autosave failed:', error);
54
+ }
55
+ }
56
+ };
44
57
  return readOnly ? (React.createElement(InputFieldComponent, { ...{
45
58
  ...props,
46
59
  defaultValue: value instanceof LocalDateTime ? DateTime.fromISO(value.toString()).toFormat('hh:mm a') : '',
47
60
  } })) : (React.createElement(LocalizationProvider, null,
48
- React.createElement(TimePicker, { value: value, onChange: handleChange, renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium', placeholder: placeholder,
61
+ React.createElement(TimePicker, { value: value, onChange: handleChange, onAccept: handleAccept, renderInput: (params) => (React.createElement(TextField, { ...params, id: id, error: error, errorMessage: errorMessage, onBlur: onBlur, fullWidth: true, required: required, sx: { background: 'white', borderRadius: '8px' }, size: size ?? 'medium', placeholder: placeholder,
49
62
  // merges MUI inputProps with additionalProps.inputProps in a way that still shows the value
50
63
  inputProps: {
51
64
  ...params.inputProps,
@@ -16,7 +16,8 @@ export type FormRendererProps = BaseProps & {
16
16
  form: EvokeForm;
17
17
  title?: string | React.ReactNode;
18
18
  instance?: ObjectInstance | Document;
19
- onChange: (id: string, value: unknown) => void;
19
+ onChange: (id: string, value: unknown) => void | Promise<void>;
20
+ onAutosave?: (fieldId: string) => void | Promise<void>;
20
21
  associatedObject?: {
21
22
  instanceId?: string;
22
23
  propertyId?: string;
@@ -1,6 +1,6 @@
1
1
  import { useObject } from '@evoke-platform/context';
2
2
  import { isEmpty, isEqual, omit } from 'lodash';
3
- import React, { useEffect, useMemo, useState } from 'react';
3
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { useForm } from 'react-hook-form';
5
5
  import { useWidgetSize } from '../../../theme';
6
6
  import { Box } from '../../layout';
@@ -12,7 +12,7 @@ import { assignIdsToSectionsAndRichText, convertDocToParameters, convertProperti
12
12
  import { handleValidation } from './components/ValidationFiles/Validation';
13
13
  import ValidationErrors from './components/ValidationFiles/ValidationErrors';
14
14
  const FormRendererInternal = (props) => {
15
- const { onSubmit, onDiscardChanges, onSubmitError, value, fieldHeight, richTextEditor, form, instance, onChange, associatedObject, renderHeader, renderBody, renderFooter, } = props;
15
+ const { onSubmit, onDiscardChanges, onSubmitError: onSubmitErrorOverride, value, fieldHeight, richTextEditor, form, instance, onChange, onAutosave, associatedObject, renderHeader, renderBody, renderFooter, } = props;
16
16
  const { entries, name: title, objectId, actionId, display } = form;
17
17
  const { register, unregister, setValue, reset, handleSubmit, formState: { errors, isSubmitted }, getValues, } = useForm({
18
18
  defaultValues: value,
@@ -32,6 +32,7 @@ const FormRendererInternal = (props) => {
32
32
  const [isInitializing, setIsInitializing] = useState(true);
33
33
  const [parameters, setParameters] = useState();
34
34
  const objectStore = useObject(objectId);
35
+ const validationContainerRef = useRef(null);
35
36
  const updateFetchedOptions = (newData) => {
36
37
  setFetchedOptions((prev) => ({
37
38
  ...prev,
@@ -45,7 +46,7 @@ const FormRendererInternal = (props) => {
45
46
  setExpandAll(false);
46
47
  }
47
48
  const updatedEntries = useMemo(() => {
48
- return assignIdsToSectionsAndRichText(entries, object, parameters);
49
+ return object ? assignIdsToSectionsAndRichText(entries, object, parameters) : [];
49
50
  }, [entries, object, parameters]);
50
51
  useEffect(() => {
51
52
  (async () => {
@@ -108,7 +109,7 @@ const FormRendererInternal = (props) => {
108
109
  }
109
110
  });
110
111
  }
111
- if (!entryIsVisible(entry, getValues(), instance)) {
112
+ if (!entryIsVisible(entry, instance, getValues())) {
112
113
  if (entry.type === 'sections' || entry.type === 'columns') {
113
114
  const fieldsToUnregister = getNestedParameterIds(entry);
114
115
  fieldsToUnregister.forEach(processFieldUnregister);
@@ -134,9 +135,17 @@ const FormRendererInternal = (props) => {
134
135
  unregister(fieldId);
135
136
  }
136
137
  };
138
+ const onSubmitError = (errors) => {
139
+ if (onSubmitErrorOverride) {
140
+ onSubmitErrorOverride(errors);
141
+ }
142
+ else if (validationContainerRef.current) {
143
+ validationContainerRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
144
+ }
145
+ };
137
146
  async function unregisterHiddenFieldsAndSubmit() {
138
147
  unregisterHiddenFields(entries ?? []);
139
- await handleSubmit((data) => onSubmit && onSubmit(action?.type === 'delete' ? {} : data), (errors) => onSubmitError?.(errors))();
148
+ await handleSubmit((data) => onSubmit && onSubmit(action?.type === 'delete' ? {} : data), (errors) => onSubmitError(errors))();
140
149
  }
141
150
  const headerProps = {
142
151
  title,
@@ -146,8 +155,9 @@ const FormRendererInternal = (props) => {
146
155
  errors,
147
156
  hasAccordions: hasSections && isSmallerThanMd,
148
157
  shouldShowValidationErrors: isSubmitted,
149
- form,
150
158
  action,
159
+ validationContainerRef: validationContainerRef,
160
+ autosaveEnabled: !!form.autosaveActionId,
151
161
  };
152
162
  const footerProps = {
153
163
  onSubmit: unregisterHiddenFieldsAndSubmit,
@@ -155,6 +165,7 @@ const FormRendererInternal = (props) => {
155
165
  action,
156
166
  discardChangesButtonLabel: 'Discard Changes',
157
167
  submitButtonLabel: display?.submitLabel ?? 'Submit',
168
+ disableDiscardChanges: !!form?.autosaveActionId,
158
169
  };
159
170
  return (React.createElement(Box, { ref: containerRef },
160
171
  React.createElement(FormContext.Provider, { value: {
@@ -172,6 +183,7 @@ const FormRendererInternal = (props) => {
172
183
  parameters,
173
184
  fieldHeight,
174
185
  handleChange: onChange,
186
+ onAutosave,
175
187
  triggerFieldReset,
176
188
  showSubmitError: isSubmitted,
177
189
  associatedObject,
@@ -200,7 +212,7 @@ const FormRendererInternal = (props) => {
200
212
  expandedSections,
201
213
  hasAccordions: hasSections && isSmallerThanMd,
202
214
  } })),
203
- (actionId || form.id === 'documentForm') &&
215
+ (action || form.id === 'documentForm') &&
204
216
  onSubmit &&
205
217
  (renderFooter ? renderFooter(footerProps) : React.createElement(Footer, { ...footerProps }))))));
206
218
  };