@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.
- package/dist/published/components/core/Autocomplete/Autocomplete.js +4 -2
- package/dist/published/components/core/Autocomplete/Autocomplete.test.js +112 -3
- package/dist/published/components/core/TextField/TextField.js +1 -1
- package/dist/published/components/core/TextField/TextField.test.js +0 -2
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +1 -1
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.d.ts +1 -0
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +428 -0
- package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +19 -6
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableField.js +1 -1
- package/dist/published/components/custom/Form/tests/Form.test.js +0 -2
- package/dist/published/components/custom/Form/utils.js +1 -0
- package/dist/published/components/custom/FormField/DatePickerSelect/DatePickerSelect.js +14 -1
- package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js +14 -1
- package/dist/published/components/custom/FormField/InputFieldComponent/InputFieldComponent.test.js +0 -2
- package/dist/published/components/custom/FormField/Select/Select.test.js +0 -2
- package/dist/published/components/custom/FormField/TimePickerSelect/TimePickerSelect.js +14 -1
- package/dist/published/components/custom/FormV2/FormRenderer.d.ts +2 -1
- package/dist/published/components/custom/FormV2/FormRenderer.js +19 -7
- package/dist/published/components/custom/FormV2/FormRendererContainer.js +117 -74
- package/dist/published/components/custom/FormV2/components/AccordionSections.js +7 -2
- package/dist/published/components/custom/FormV2/components/Body.d.ts +1 -1
- package/dist/published/components/custom/FormV2/components/FieldWrapper.js +1 -1
- package/dist/published/components/custom/FormV2/components/Footer.d.ts +1 -0
- package/dist/published/components/custom/FormV2/components/Footer.js +8 -5
- package/dist/published/components/custom/FormV2/components/FormContext.d.ts +3 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.d.ts +9 -0
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.js +32 -15
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.js +2 -2
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.d.ts +0 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +36 -49
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +16 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +16 -4
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +2 -1
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +16 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +31 -5
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +15 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +109 -81
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +38 -16
- package/dist/published/components/custom/FormV2/components/Header.d.ts +5 -3
- package/dist/published/components/custom/FormV2/components/Header.js +47 -9
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +46 -35
- package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrors.js +1 -1
- package/dist/published/components/custom/FormV2/components/types.d.ts +1 -0
- package/dist/published/components/custom/FormV2/components/utils.d.ts +4 -4
- package/dist/published/components/custom/FormV2/components/utils.js +13 -16
- package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +289 -45
- package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +664 -16
- package/dist/published/components/custom/FormV2/tests/test-data.d.ts +1 -0
- package/dist/published/components/custom/FormV2/tests/test-data.js +140 -0
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +3 -0
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +155 -0
- package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.d.ts +13 -0
- package/dist/published/components/custom/ViewDetailsV2/ViewDetailsV2Container.js +144 -0
- package/dist/published/components/custom/ViewDetailsV2/index.d.ts +3 -0
- package/dist/published/components/custom/ViewDetailsV2/index.js +2 -0
- package/dist/published/components/custom/index.d.ts +2 -0
- package/dist/published/components/custom/index.js +1 -0
- package/dist/published/index.d.ts +6 -6
- package/dist/published/index.js +1 -1
- package/dist/published/stories/FormRenderer.stories.d.ts +8 -4
- package/dist/published/stories/FormRendererContainer.stories.d.ts +26 -0
- package/dist/published/stories/FormRendererContainer.stories.js +5 -0
- package/dist/published/stories/FormRendererData.d.ts +12 -0
- package/dist/published/stories/FormRendererData.js +29 -44
- package/dist/published/stories/ViewDetailsV2Container.stories.d.ts +26 -0
- package/dist/published/stories/ViewDetailsV2Container.stories.js +37 -0
- package/dist/published/stories/ViewDetailsV2Data.d.ts +4 -0
- package/dist/published/stories/ViewDetailsV2Data.js +203 -0
- package/dist/published/stories/sharedMswHandlers.js +49 -10
- package/dist/published/theme/hooks.d.ts +4 -3
- 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, {
|
|
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,
|
|
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
|
|
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'
|
|
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,
|
|
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,
|
package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js
CHANGED
|
@@ -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,
|
package/dist/published/components/custom/FormField/InputFieldComponent/InputFieldComponent.test.js
CHANGED
|
@@ -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()
|
|
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
|
|
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
|
-
(
|
|
215
|
+
(action || form.id === 'documentForm') &&
|
|
204
216
|
onSubmit &&
|
|
205
217
|
(renderFooter ? renderFooter(footerProps) : React.createElement(Footer, { ...footerProps }))))));
|
|
206
218
|
};
|