@evoke-platform/ui-components 1.1.0-testing.9 → 1.1.0
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/custom/CriteriaBuilder/CriteriaBuilder.js +1 -2
- package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +67 -12
- package/dist/published/components/custom/Form/Common/Form.d.ts +57 -20
- package/dist/published/components/custom/Form/Common/Form.js +17 -15
- package/dist/published/components/custom/Form/FormComponents/ButtonComponent.d.ts +1 -2
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentComponent.d.ts +1 -2
- package/dist/published/components/custom/Form/FormComponents/FormFieldComponent.d.ts +0 -1
- package/dist/published/components/custom/Form/FormComponents/ImageComponent/ImageComponent.d.ts +0 -3
- package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectComponent.d.ts +0 -1
- package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectPropertyInput.js +11 -5
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/ActionDialog.d.ts +2 -2
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/ManyToMany/DropdownRepeatableField.js +16 -8
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableField.d.ts +2 -1
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableFieldComponent.d.ts +0 -1
- package/dist/published/components/custom/Form/FormComponents/UserComponent/UserComponent.d.ts +0 -3
- package/dist/published/components/custom/Form/utils.d.ts +3 -3
- package/dist/published/stories/CriteriaBuilder.stories.js +1 -1
- package/dist/published/stories/Form.stories.d.ts +6 -0
- package/dist/published/stories/Form.stories.js +82 -0
- package/package.json +1 -1
@@ -3,6 +3,7 @@ import { ClearRounded } from '@mui/icons-material';
|
|
3
3
|
import { Box, darken, lighten, styled } from '@mui/material';
|
4
4
|
import { TimePicker } from '@mui/x-date-pickers';
|
5
5
|
import React, { useEffect, useRef, useState } from 'react';
|
6
|
+
import { InvalidDate } from '../../../util';
|
6
7
|
import { Autocomplete, Chip, DatePicker, DateTimePicker, LocalizationProvider, Menu, MenuItem, TextField, Typography, } from '../../core';
|
7
8
|
import { NumericFormat } from '../FormField/InputFieldComponent';
|
8
9
|
const GroupHeader = styled('div')(({ theme }) => ({
|
@@ -32,6 +33,7 @@ const ValueEditor = (props) => {
|
|
32
33
|
}));
|
33
34
|
}
|
34
35
|
}
|
36
|
+
const [invalidDateTime, setInvalidDateTime] = useState(false);
|
35
37
|
const [anchorEl, setAnchorEl] = useState(null);
|
36
38
|
const inputRef = useRef(null);
|
37
39
|
const [openPresetValues, setOpenPresetValues] = useState(false);
|
@@ -82,12 +84,47 @@ const ValueEditor = (props) => {
|
|
82
84
|
const groupRenderGroup = (params) => (React.createElement("li", { key: params.key },
|
83
85
|
React.createElement(GroupHeader, null, params.group),
|
84
86
|
React.createElement(GroupItems, null, params.children)));
|
85
|
-
function
|
87
|
+
function validateDateTime(value) {
|
86
88
|
if (!value) {
|
87
89
|
return null;
|
88
90
|
}
|
89
|
-
|
90
|
-
|
91
|
+
// check if it's an ISO string
|
92
|
+
const isoRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/;
|
93
|
+
if (isoRegex.test(value)) {
|
94
|
+
try {
|
95
|
+
const instant = Instant.parse(value);
|
96
|
+
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
97
|
+
}
|
98
|
+
catch (error) {
|
99
|
+
console.error('Error parsing ISO string:', error);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
return new InvalidDate(value);
|
103
|
+
}
|
104
|
+
// Prevents the RangeError issue on keyboard input by returning an empty string for invalid dates
|
105
|
+
// lets users type partial dates without crashing the component during the input process.
|
106
|
+
function convertToISOString(date) {
|
107
|
+
if (!date) {
|
108
|
+
return '';
|
109
|
+
}
|
110
|
+
try {
|
111
|
+
if (date instanceof InvalidDate) {
|
112
|
+
return '';
|
113
|
+
}
|
114
|
+
if (date instanceof LocalDateTime) {
|
115
|
+
return new Date(date.toString()).toISOString();
|
116
|
+
}
|
117
|
+
if (date instanceof LocalDate) {
|
118
|
+
// Convert LocalDate to LocalDateTime to avoid HourOfDay error
|
119
|
+
const dateTime = LocalDateTime.of(date, LocalTime.of(0));
|
120
|
+
return new Date(dateTime.toString()).toISOString();
|
121
|
+
}
|
122
|
+
return '';
|
123
|
+
}
|
124
|
+
catch (error) {
|
125
|
+
console.error('Error converting date to ISO string:', error);
|
126
|
+
return '';
|
127
|
+
}
|
91
128
|
}
|
92
129
|
const getEditor = () => {
|
93
130
|
if (isPresetValueSelected) {
|
@@ -106,26 +143,44 @@ const ValueEditor = (props) => {
|
|
106
143
|
React.createElement(TimePicker, { inputRef: inputRef, disabled: disabled, value: disabled || !value ? null : value, onChange: handleOnChange, renderInput: (params) => (React.createElement(TextField, { ...params, onClick: onClick, placeholder: "Value", size: "small", sx: styles.input })), readOnly: readOnly })));
|
107
144
|
}
|
108
145
|
else if (inputType === 'date-time') {
|
109
|
-
const dateTimeValue =
|
146
|
+
const dateTimeValue = validateDateTime(value);
|
110
147
|
return (React.createElement(LocalizationProvider, null,
|
111
148
|
React.createElement(DateTimePicker, { inputRef: inputRef, value: dateTimeValue, onChange: (date) => {
|
112
149
|
if (!date) {
|
113
150
|
handleOnChange('');
|
151
|
+
setInvalidDateTime(false);
|
114
152
|
return;
|
115
153
|
}
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
localDateTime = LocalDateTime.of(date, LocalTime.of(0));
|
154
|
+
if (date instanceof InvalidDate) {
|
155
|
+
setInvalidDateTime(true);
|
156
|
+
return;
|
120
157
|
}
|
121
|
-
|
122
|
-
localDateTime
|
158
|
+
try {
|
159
|
+
let localDateTime;
|
160
|
+
if (date instanceof LocalDate && !(date instanceof LocalDateTime)) {
|
161
|
+
// onChange initially returns a LocalDate after date is selected
|
162
|
+
localDateTime = LocalDateTime.of(date, LocalTime.of(0));
|
163
|
+
}
|
164
|
+
else {
|
165
|
+
localDateTime = date;
|
166
|
+
}
|
167
|
+
const isoString = convertToISOString(localDateTime);
|
168
|
+
if (isoString) {
|
169
|
+
setInvalidDateTime(false);
|
170
|
+
handleOnChange(isoString);
|
171
|
+
}
|
172
|
+
else {
|
173
|
+
setInvalidDateTime(true);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
catch (error) {
|
177
|
+
console.error('Error processing date value:', error);
|
178
|
+
setInvalidDateTime(true);
|
123
179
|
}
|
124
|
-
handleOnChange(new Date(localDateTime.toString()).toISOString());
|
125
180
|
}, onClose: onClose, PopperProps: {
|
126
181
|
anchorEl,
|
127
182
|
}, renderInput: (params) => (React.createElement(Box, { sx: styles.input, ref: setAnchorEl },
|
128
|
-
React.createElement(TextField, { ...params, disabled: disabled, onClick: onClick, placeholder: "Value", size: "small", inputRef: inputRef }))), readOnly: readOnly })));
|
183
|
+
React.createElement(TextField, { ...params, disabled: disabled, onClick: onClick, placeholder: "Value", size: "small", inputRef: inputRef, error: invalidDateTime }))), readOnly: readOnly })));
|
129
184
|
}
|
130
185
|
else if (inputType === 'number' || inputType === 'integer') {
|
131
186
|
const isMultiple = ['in', 'notIn'].includes(operator);
|
@@ -2,40 +2,77 @@ import { ApiServices, Obj, ObjectInstance, UserAccount } from '@evoke-platform/c
|
|
2
2
|
import { ReactComponent } from '@formio/react';
|
3
3
|
import React from 'react';
|
4
4
|
import '../../../../styles/form-component.css';
|
5
|
-
import { Document, ObjectPropertyInputProps } from '../types';
|
5
|
+
import { Address, Document, ObjectPropertyInputProps } from '../types';
|
6
6
|
type OnSaveResponse = {
|
7
7
|
isSuccessful: boolean;
|
8
8
|
error?: Record<string, unknown>;
|
9
9
|
};
|
10
10
|
export type FormProps = {
|
11
|
-
|
12
|
-
object?: Obj;
|
13
|
-
instance?: ObjectInstance;
|
14
|
-
document?: Document;
|
15
|
-
user?: UserAccount;
|
16
|
-
onSave?: (data: Record<string, unknown>, setSubmitting?: (value: boolean) => void) => Promise<OnSaveResponse>;
|
17
|
-
submitButtonLabel?: string;
|
18
|
-
isReadOnly?: boolean;
|
19
|
-
apiServices?: ApiServices | undefined;
|
20
|
-
navigateTo?: (path: string) => void;
|
21
|
-
closeModal?: () => void;
|
22
|
-
clearable?: boolean;
|
23
|
-
objectInputCommonProps?: ObjectPropertyInputProps;
|
11
|
+
/** An optional identifier for the action associated with the form. */
|
24
12
|
actionId?: string;
|
25
|
-
|
26
|
-
isSuccessful: boolean;
|
27
|
-
error?: Record<string, unknown>;
|
28
|
-
}>;
|
13
|
+
/** The type of action associated with the form. */
|
29
14
|
actionType?: string;
|
15
|
+
/** Optional API services for the form. */
|
16
|
+
apiServices?: ApiServices | undefined;
|
17
|
+
/** An object representing an associated object with optional `instanceId` and `propertyId` properties. */
|
30
18
|
associatedObject?: {
|
31
19
|
instanceId?: string;
|
32
20
|
propertyId?: string;
|
33
21
|
};
|
34
|
-
|
22
|
+
/**
|
23
|
+
* Whether the form can be cleared.
|
24
|
+
* @default false
|
25
|
+
* */
|
26
|
+
clearable?: boolean;
|
27
|
+
/** An optional document related to the form. */
|
28
|
+
document?: Document;
|
29
|
+
/** Defines the height of the fields in the form. */
|
35
30
|
fieldHeight?: 'small' | 'medium';
|
31
|
+
/** An optional unique identifier for the form instance. */
|
32
|
+
formKey?: number;
|
33
|
+
/**
|
34
|
+
* A reference to the form for direct manipulation.
|
35
|
+
* i.e: formRef.current.submit({data: formRef.current.data}, 'submit', (error?: Record<string, unknown>) => {...}, (value: boolean) => {...})
|
36
|
+
* */
|
37
|
+
formRef?: React.MutableRefObject<FormRef | undefined>;
|
38
|
+
/** A React component used as a rich text editor. */
|
36
39
|
richTextEditor?: typeof ReactComponent;
|
40
|
+
/**
|
41
|
+
* Whether to hide the buttons in the form.
|
42
|
+
* @default false
|
43
|
+
* */
|
37
44
|
hideButtons?: boolean;
|
38
|
-
|
45
|
+
/** An optional instance of an object associated with the form. */
|
46
|
+
instance?: ObjectInstance;
|
47
|
+
/**
|
48
|
+
* Whether the form is in read-only mode.
|
49
|
+
* @default false
|
50
|
+
* */
|
51
|
+
isReadOnly?: boolean;
|
52
|
+
/** An optional object associated with the form. */
|
53
|
+
object?: Obj;
|
54
|
+
/** Common properties for object input fields. */
|
55
|
+
objectInputCommonProps?: ObjectPropertyInputProps;
|
56
|
+
/** An optional user account associated with the form. */
|
57
|
+
user?: UserAccount;
|
58
|
+
/**
|
59
|
+
* The label for the submit button.
|
60
|
+
* @default Submit
|
61
|
+
* */
|
62
|
+
submitButtonLabel?: string;
|
63
|
+
/** A function to close a modal if the form is in one. */
|
64
|
+
closeModal?: () => void;
|
65
|
+
/** A function to navigate to a different path. */
|
66
|
+
navigateTo?: (path: string) => void;
|
67
|
+
/** A callback function to save draft the form data. */
|
68
|
+
onAutoSave?: (data: Record<string, unknown>) => Promise<{
|
69
|
+
isSuccessful: boolean;
|
70
|
+
error?: Record<string, unknown>;
|
71
|
+
}>;
|
72
|
+
/** A callback function to save the form data. It returns a promise with a response. */
|
73
|
+
onSave?: (data: Record<string, unknown>, setSubmitting?: (value: boolean) => void) => Promise<OnSaveResponse>;
|
74
|
+
/** A function to query addresses related to the form. */
|
75
|
+
queryAddresses?: (query: string) => Promise<Address[]>;
|
39
76
|
};
|
40
77
|
export type FormRef = {
|
41
78
|
submit: (submission: Record<string, unknown>, type: 'submit' | 'draft', setError: (error?: Record<string, unknown>) => void, setSubmitting?: (value: boolean) => void) => void;
|
@@ -231,23 +231,25 @@ export function Form(props) {
|
|
231
231
|
// If form.io form is not configured and no inputProperties are
|
232
232
|
// set, use object properties to build form.
|
233
233
|
const propertiesInForm = object.properties.filter((prop) => prop.id !== associatedObject?.propertyId && (action?.type !== 'create' || prop.type !== 'document'));
|
234
|
-
|
235
|
-
...
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
234
|
+
const components = buildComponentPropsFromObjectProperties(propertiesInForm, object.id, instance, {
|
235
|
+
...objectInputCommonProps,
|
236
|
+
defaultPages: allDefaultPages,
|
237
|
+
navigateTo,
|
238
|
+
apiServices,
|
239
|
+
user,
|
240
|
+
}, !!action, undefined, isReadOnly, queryAddresses, !!closeModal, fieldHeight, richTextEditor);
|
241
|
+
if (!isReadOnly && !hideButtons) {
|
242
|
+
components.push(BottomButtons);
|
243
|
+
}
|
244
|
+
setComponentProps(components);
|
244
245
|
}
|
245
246
|
else if (document) {
|
246
247
|
const documentProperties = toPairs(flatten(document ?? {}));
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
248
|
+
const components = buildComponentPropsFromDocumentProperties(documentProperties, isReadOnly, undefined, fieldHeight);
|
249
|
+
if (!isReadOnly && !hideButtons) {
|
250
|
+
components.push(BottomButtons);
|
251
|
+
}
|
252
|
+
setComponentProps(components);
|
251
253
|
}
|
252
254
|
};
|
253
255
|
const uploadDocuments = async (files, metadata) => {
|
@@ -443,7 +445,7 @@ export function Form(props) {
|
|
443
445
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
444
446
|
, {
|
445
447
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
446
|
-
onChange: (e) => setFormData(e.data), key: closeModal ? undefined : formKey, form: {
|
448
|
+
onChange: (e) => !isEqual(e.data, formData) && setFormData(e.data), key: closeModal ? undefined : formKey, form: {
|
447
449
|
display: 'form',
|
448
450
|
components: componentProps,
|
449
451
|
}, formReady: handleFormReady })) : (React.createElement(Box, null,
|
@@ -21,11 +21,10 @@ type ButtonProps = {
|
|
21
21
|
};
|
22
22
|
export declare class ButtonComponent extends ReactComponent {
|
23
23
|
[x: string]: any;
|
24
|
-
component: ButtonComponentProps;
|
25
24
|
componentRoot?: Root;
|
26
25
|
constructor(component: ButtonComponentProps, options: any, data: any);
|
27
26
|
setError(error?: Record<string, unknown>): void;
|
28
|
-
getButton: (buttonAction: 'cancel' | 'submit' | 'save-draft') =>
|
27
|
+
getButton: (buttonAction: 'cancel' | 'submit' | 'save-draft') => any;
|
29
28
|
handleCancel: (event: any) => void;
|
30
29
|
handleSaveDraft: (setSubmitting: (value: boolean) => void) => void;
|
31
30
|
handleSubmit: (setSubmitting: (value: boolean) => void) => void;
|
@@ -1,10 +1,9 @@
|
|
1
1
|
import { ReactComponent } from '@formio/react';
|
2
2
|
import { Root } from 'react-dom/client';
|
3
|
-
import {
|
3
|
+
import { SavedDocumentReference } from '../../types';
|
4
4
|
export declare class DocumentComponent extends ReactComponent {
|
5
5
|
[x: string]: any;
|
6
6
|
static schema: any;
|
7
|
-
component: BaseFormComponentProps;
|
8
7
|
errorDetails: any;
|
9
8
|
componentRoot?: Root;
|
10
9
|
constructor(component: any, options: any, data: any);
|
@@ -15,7 +15,6 @@ type FormFieldComponentProps = Omit<BaseFormComponentProps, 'property'> & {
|
|
15
15
|
export declare class FormFieldComponent extends ReactComponent {
|
16
16
|
[x: string]: any;
|
17
17
|
static schema: any;
|
18
|
-
component: FormFieldComponentProps;
|
19
18
|
errorDetails: any;
|
20
19
|
/**
|
21
20
|
* Called when the component has been instantiated. This is useful to define
|
package/dist/published/components/custom/Form/FormComponents/ImageComponent/ImageComponent.d.ts
CHANGED
@@ -4,9 +4,6 @@ import { BaseFormComponentProps } from '../../types';
|
|
4
4
|
export declare class ImageComponent extends ReactComponent {
|
5
5
|
[x: string]: any;
|
6
6
|
static schema: any;
|
7
|
-
component: BaseFormComponentProps & {
|
8
|
-
initialValue?: string;
|
9
|
-
};
|
10
7
|
errorDetails: any;
|
11
8
|
componentRoot?: Root;
|
12
9
|
constructor(component: BaseFormComponentProps, options: any, data: any);
|
package/dist/published/components/custom/Form/FormComponents/ObjectComponent/ObjectPropertyInput.js
CHANGED
@@ -17,8 +17,10 @@ export const ObjectPropertyInput = (props) => {
|
|
17
17
|
const [options, setOptions] = useState([]);
|
18
18
|
const [openOptions, setOpenOptions] = useState(false);
|
19
19
|
const [layout, setLayout] = useState();
|
20
|
+
const [layoutLoaded, setLayoutLoaded] = useState(false);
|
20
21
|
const DEFAULT_CREATE_ACTION = '_create';
|
21
22
|
useEffect(() => {
|
23
|
+
setLayoutLoaded(false);
|
22
24
|
if (relatedObject) {
|
23
25
|
let defaultViewLayout;
|
24
26
|
if (relatedObject?.viewLayout?.dropdown && displayOption === 'dropdown') {
|
@@ -41,10 +43,12 @@ export const ObjectPropertyInput = (props) => {
|
|
41
43
|
apiServices
|
42
44
|
.get(getPrefixedUrl(`/objects/${viewLayout.objectId}/${displayOption === 'dropdown' ? 'dropdown' : 'table'}Layouts/${viewLayout.id}`))
|
43
45
|
.then(setLayout)
|
44
|
-
.catch((
|
46
|
+
.catch(() => setLayout(defaultViewLayout))
|
47
|
+
.finally(() => setLayoutLoaded(true));
|
45
48
|
}
|
46
49
|
else {
|
47
50
|
setLayout(defaultViewLayout);
|
51
|
+
setLayoutLoaded(true);
|
48
52
|
}
|
49
53
|
}
|
50
54
|
}, [displayOption, relatedObject, viewLayout]);
|
@@ -109,19 +113,21 @@ export const ObjectPropertyInput = (props) => {
|
|
109
113
|
});
|
110
114
|
}, [relatedObject, setLoadingOptions, setOptions, property, filter, layout]);
|
111
115
|
useEffect(() => {
|
112
|
-
if (displayOption === 'dropdown') {
|
116
|
+
if (displayOption === 'dropdown' && layoutLoaded) {
|
113
117
|
getDropdownOptions();
|
114
118
|
}
|
115
|
-
}, [property, filter, displayOption, getDropdownOptions]);
|
119
|
+
}, [property, filter, displayOption, getDropdownOptions, layoutLoaded]);
|
116
120
|
const debouncedGetDropdownOptions = useCallback(debounce(getDropdownOptions, 500), [
|
117
121
|
filter,
|
118
122
|
property,
|
119
123
|
getDropdownOptions,
|
120
124
|
]);
|
121
125
|
useEffect(() => {
|
122
|
-
|
126
|
+
if (dropdownInput !== undefined && layoutLoaded) {
|
127
|
+
debouncedGetDropdownOptions(dropdownInput);
|
128
|
+
}
|
123
129
|
return () => debouncedGetDropdownOptions.cancel();
|
124
|
-
}, [dropdownInput, debouncedGetDropdownOptions]);
|
130
|
+
}, [dropdownInput, debouncedGetDropdownOptions, layoutLoaded]);
|
125
131
|
useEffect(() => {
|
126
132
|
apiServices.get(getPrefixedUrl(`/objects/${property.objectId}/effective?sanitizedVersion=true`), (error, object) => {
|
127
133
|
if (error) {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/// <reference types="react" />
|
2
2
|
import { Action, ApiServices, Obj, Property, UserAccount } from '@evoke-platform/context';
|
3
|
-
import { ObjectPropertyInputProps } from '../../types';
|
3
|
+
import { Address, ObjectPropertyInputProps } from '../../types';
|
4
4
|
export type ActionDialogProps = {
|
5
5
|
open: boolean;
|
6
6
|
onClose: () => void;
|
@@ -15,7 +15,7 @@ export type ActionDialogProps = {
|
|
15
15
|
instanceId?: string;
|
16
16
|
relatedProperty?: Property;
|
17
17
|
apiServices: ApiServices;
|
18
|
-
queryAddresses?:
|
18
|
+
queryAddresses?: (query: string) => Promise<Address[]>;
|
19
19
|
user?: UserAccount;
|
20
20
|
};
|
21
21
|
export declare const ActionDialog: (props: ActionDialogProps) => JSX.Element;
|
@@ -17,7 +17,8 @@ export const DropdownRepeatableField = (props) => {
|
|
17
17
|
showAlert: false,
|
18
18
|
isError: true,
|
19
19
|
});
|
20
|
-
const [searchValue, setSearchValue] = useState(
|
20
|
+
const [searchValue, setSearchValue] = useState();
|
21
|
+
const [layoutLoaded, setLayoutLoaded] = useState(false);
|
21
22
|
const { instanceChanges } = useNotification();
|
22
23
|
const fetchMiddleObjectInstances = async () => {
|
23
24
|
const newInstances = await getMiddleObjectInstances();
|
@@ -36,6 +37,7 @@ export const DropdownRepeatableField = (props) => {
|
|
36
37
|
useEffect(() => {
|
37
38
|
const endObjectProperty = middleObject?.properties?.find((currProperty) => property.manyToManyPropertyId === currProperty.id);
|
38
39
|
if (endObjectProperty && endObjectProperty.objectId) {
|
40
|
+
setLayoutLoaded(false);
|
39
41
|
apiServices.get(getPrefixedUrl(`/objects/${endObjectProperty.objectId}/effective`), { params: { filter: { fields: ['id', 'name', 'properties', 'viewLayout'] } } }, (error, effectiveObject) => {
|
40
42
|
if (error) {
|
41
43
|
console.error(error);
|
@@ -58,10 +60,12 @@ export const DropdownRepeatableField = (props) => {
|
|
58
60
|
apiServices
|
59
61
|
.get(getPrefixedUrl(`/objects/${viewLayout.objectId}/dropdownLayouts/${viewLayout.id}`))
|
60
62
|
.then(setLayout)
|
61
|
-
.catch((
|
63
|
+
.catch(() => setLayout(defaultLayout))
|
64
|
+
.finally(() => setLayoutLoaded(true));
|
62
65
|
}
|
63
66
|
else {
|
64
67
|
setLayout(defaultLayout);
|
68
|
+
setLayoutLoaded(true);
|
65
69
|
}
|
66
70
|
}
|
67
71
|
});
|
@@ -110,15 +114,19 @@ export const DropdownRepeatableField = (props) => {
|
|
110
114
|
});
|
111
115
|
}
|
112
116
|
}
|
113
|
-
}, [property.objectId, property.manyToManyPropertyId, apiServices, middleObject, layout]);
|
117
|
+
}, [property.objectId, property.manyToManyPropertyId, apiServices, middleObject, layout, criteria]);
|
114
118
|
const debouncedEndObjectSearch = useCallback(debounce(fetchEndObjectInstances, 500), [fetchEndObjectInstances]);
|
115
119
|
useEffect(() => {
|
116
|
-
|
120
|
+
if (searchValue !== undefined && layoutLoaded) {
|
121
|
+
debouncedEndObjectSearch(searchValue);
|
122
|
+
}
|
117
123
|
return () => debouncedEndObjectSearch.cancel();
|
118
|
-
}, [searchValue, debouncedEndObjectSearch]);
|
124
|
+
}, [searchValue, debouncedEndObjectSearch, layoutLoaded]);
|
119
125
|
useEffect(() => {
|
120
|
-
|
121
|
-
|
126
|
+
if (layoutLoaded) {
|
127
|
+
fetchEndObjectInstances();
|
128
|
+
}
|
129
|
+
}, [fetchEndObjectInstances, layoutLoaded]);
|
122
130
|
const saveMiddleInstance = (endObjectId, endObjectName) => {
|
123
131
|
if (property.objectId) {
|
124
132
|
const middleObject = getMiddleObject(instance, property, endObjectId, endObjectName);
|
@@ -157,5 +165,5 @@ export const DropdownRepeatableField = (props) => {
|
|
157
165
|
}
|
158
166
|
});
|
159
167
|
};
|
160
|
-
return initialLoading ? (React.createElement(Skeleton, null)) : (React.createElement(React.Fragment, null, middleObjectInstances && endObject && (React.createElement(DropdownRepeatableFieldInput, { id: id, property: property, readOnly: readOnly || !middleObject.actions?.some((action) => action.id === '_create'), layout: layout, middleObjectInstances: middleObjectInstances, endObjectInstances: endObjectInstances ?? [], endObject: endObject, searchValue: searchValue, loading: loading, handleSaveMiddleInstance: saveMiddleInstance, handleRemoveMiddleInstance: removeMiddleInstance, setSearchValue: setSearchValue, setSnackbarError: setSnackbarError, snackbarError: snackbarError, selectedOptions: selectedOptions, setSelectedOptions: setSelectedOptions, setDropdownSelections: setDropDownSelections, fieldHeight: fieldHeight }))));
|
168
|
+
return initialLoading ? (React.createElement(Skeleton, null)) : (React.createElement(React.Fragment, null, middleObjectInstances && endObject && (React.createElement(DropdownRepeatableFieldInput, { id: id, property: property, readOnly: readOnly || !middleObject.actions?.some((action) => action.id === '_create'), layout: layout, middleObjectInstances: middleObjectInstances, endObjectInstances: endObjectInstances ?? [], endObject: endObject, searchValue: searchValue ?? '', loading: loading, handleSaveMiddleInstance: saveMiddleInstance, handleRemoveMiddleInstance: removeMiddleInstance, setSearchValue: setSearchValue, setSnackbarError: setSnackbarError, snackbarError: snackbarError, selectedOptions: selectedOptions, setSelectedOptions: setSelectedOptions, setDropdownSelections: setDropDownSelections, fieldHeight: fieldHeight }))));
|
161
169
|
};
|
@@ -1,11 +1,12 @@
|
|
1
1
|
/// <reference types="react" />
|
2
2
|
import { ApiServices, ObjectInstance, Property, UserAccount, ViewLayoutEntityReference } from '@evoke-platform/context';
|
3
|
+
import { Address } from '../../types';
|
3
4
|
export type ObjectPropertyInputProps = {
|
4
5
|
property: Property;
|
5
6
|
instance: ObjectInstance;
|
6
7
|
canUpdateProperty: boolean;
|
7
8
|
apiServices: ApiServices;
|
8
|
-
queryAddresses?:
|
9
|
+
queryAddresses?: (query: string) => Promise<Address[]>;
|
9
10
|
user?: UserAccount;
|
10
11
|
viewLayout?: ViewLayoutEntityReference;
|
11
12
|
};
|
@@ -10,7 +10,6 @@ type RepeatableFieldComponentProps = BaseFormComponentProps & {
|
|
10
10
|
export declare class RepeatableFieldComponent extends ReactComponent {
|
11
11
|
[x: string]: any;
|
12
12
|
static schema: any;
|
13
|
-
component: RepeatableFieldComponentProps;
|
14
13
|
criteria: Record<string, any> | undefined;
|
15
14
|
updatedCriteria: Record<string, any>;
|
16
15
|
constructor(component: RepeatableFieldComponentProps, options: any, data: any);
|
package/dist/published/components/custom/Form/FormComponents/UserComponent/UserComponent.d.ts
CHANGED
@@ -6,9 +6,6 @@ export declare class UserComponent extends ReactComponent {
|
|
6
6
|
[x: string]: any;
|
7
7
|
static schema: any;
|
8
8
|
errorDetails: any;
|
9
|
-
component: BaseFormComponentProps & {
|
10
|
-
initialValue?: string;
|
11
|
-
};
|
12
9
|
componentRoot?: Root;
|
13
10
|
criteria: Record<string, unknown> | undefined;
|
14
11
|
updatedCriteria: Record<string, unknown>;
|
@@ -2,7 +2,7 @@ import { ActionInput, ActionInputType, ApiServices, FormEntry, InputParameter, I
|
|
2
2
|
import { ReactComponent } from '@formio/react';
|
3
3
|
import { LocalDateTime } from '@js-joda/core';
|
4
4
|
import { AutocompleteOption } from '../../core';
|
5
|
-
import { ObjectPropertyInputProps } from './types';
|
5
|
+
import { Address, ObjectPropertyInputProps } from './types';
|
6
6
|
export declare function determineComponentType(properties: Property[], parameter?: InputParameter): ActionInputType | undefined;
|
7
7
|
export declare function determineParameterType(componentType: string): PropertyType;
|
8
8
|
export declare function getFlattenEntries(entries: FormEntry[]): InputParameterReference[];
|
@@ -21,9 +21,9 @@ export declare function getMiddleObject(instance: ObjectInstance, property: Prop
|
|
21
21
|
} | undefined;
|
22
22
|
export declare function getMiddleInstance(instanceId: string, property: Property, middleObjectInstances: ObjectInstance[]): ObjectInstance | undefined;
|
23
23
|
export declare function getPrefixedUrl(url: string): string;
|
24
|
-
export declare function addObjectPropertiesToComponentProps(properties: Property[], formComponents: any[], instance?: ObjectInstance, objectPropertyInputProps?: ObjectPropertyInputProps, autoSave?: (data: Record<string, unknown>) => void, readOnly?: boolean, defaultPages?: Record<string, string>, navigateTo?: (path: string) => void, queryAddresses?:
|
24
|
+
export declare function addObjectPropertiesToComponentProps(properties: Property[], formComponents: any[], instance?: ObjectInstance, objectPropertyInputProps?: ObjectPropertyInputProps, autoSave?: (data: Record<string, unknown>) => void, readOnly?: boolean, defaultPages?: Record<string, string>, navigateTo?: (path: string) => void, queryAddresses?: (query: string) => Promise<Address[]>, apiServices?: ApiServices, isModal?: boolean, fieldHeight?: 'small' | 'medium', richTextEditor?: typeof ReactComponent): Promise<ActionInput[]>;
|
25
25
|
export declare function getDefaultValue(initialValue: unknown, selectOptions?: AutocompleteOption[]): unknown;
|
26
|
-
export declare const buildComponentPropsFromObjectProperties: (properties: Property[], objectId: string, instance?: ObjectInstance, objectPropertyInputProps?: ObjectPropertyInputProps, hasActionPermissions?: boolean, autoSave?: ((data: Record<string, unknown>) => void) | undefined, readOnly?: boolean, queryAddresses?:
|
26
|
+
export declare const buildComponentPropsFromObjectProperties: (properties: Property[], objectId: string, instance?: ObjectInstance, objectPropertyInputProps?: ObjectPropertyInputProps, hasActionPermissions?: boolean, autoSave?: ((data: Record<string, unknown>) => void) | undefined, readOnly?: boolean, queryAddresses?: ((query: string) => Promise<Address[]>) | undefined, isModal?: boolean, fieldHeight?: 'small' | 'medium', richTextEditor?: typeof ReactComponent) => unknown[];
|
27
27
|
export declare const buildComponentPropsFromDocumentProperties: (documentProperties: [string, unknown][], readOnly?: boolean, autoSave?: ((data: Record<string, unknown>) => void) | undefined, fieldHeight?: 'small' | 'medium') => {
|
28
28
|
type: string;
|
29
29
|
key: string;
|
@@ -0,0 +1,6 @@
|
|
1
|
+
import { ComponentMeta, ComponentStory } from '@storybook/react';
|
2
|
+
import { Form } from '../index';
|
3
|
+
declare const _default: ComponentMeta<typeof Form>;
|
4
|
+
export default _default;
|
5
|
+
export declare const FormWithButtons: ComponentStory<typeof Form>;
|
6
|
+
export declare const FormWithNoButtons: ComponentStory<typeof Form>;
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { Button, Form } from '../index';
|
3
|
+
export default {
|
4
|
+
title: 'Custom/Form',
|
5
|
+
component: Form,
|
6
|
+
};
|
7
|
+
const object = {
|
8
|
+
id: 'test',
|
9
|
+
name: 'Test',
|
10
|
+
properties: [
|
11
|
+
{
|
12
|
+
id: 'name',
|
13
|
+
name: 'Name',
|
14
|
+
type: 'string',
|
15
|
+
required: true,
|
16
|
+
},
|
17
|
+
{
|
18
|
+
id: 'decimal',
|
19
|
+
name: 'Decimal',
|
20
|
+
type: 'number',
|
21
|
+
},
|
22
|
+
{
|
23
|
+
id: 'integer',
|
24
|
+
name: 'Integer',
|
25
|
+
type: 'integer',
|
26
|
+
},
|
27
|
+
],
|
28
|
+
actions: [
|
29
|
+
{
|
30
|
+
id: '_create',
|
31
|
+
name: 'Create Test',
|
32
|
+
type: 'create',
|
33
|
+
outputEvent: 'Test Created',
|
34
|
+
},
|
35
|
+
{
|
36
|
+
id: '_update',
|
37
|
+
name: 'Update Test',
|
38
|
+
type: 'update',
|
39
|
+
outputEvent: 'Test Updated',
|
40
|
+
},
|
41
|
+
{
|
42
|
+
id: '_delete',
|
43
|
+
name: 'Delete Test',
|
44
|
+
type: 'delete',
|
45
|
+
inputProperties: [],
|
46
|
+
outputEvent: 'Test Deleted',
|
47
|
+
},
|
48
|
+
],
|
49
|
+
};
|
50
|
+
const FormWithButtonsTemplate = (args) => {
|
51
|
+
return React.createElement(Form, { ...args });
|
52
|
+
};
|
53
|
+
export const FormWithButtons = FormWithButtonsTemplate.bind({});
|
54
|
+
FormWithButtons.args = {
|
55
|
+
actionId: '_update',
|
56
|
+
actionType: 'update',
|
57
|
+
object: object,
|
58
|
+
instance: { id: 'id', objectId: 'test', name: 'test', integer: 12, decimal: 12.5 },
|
59
|
+
};
|
60
|
+
const FormWithNoButtonsTemplate = (args) => {
|
61
|
+
return (React.createElement("div", null,
|
62
|
+
React.createElement(Form, { ...args }),
|
63
|
+
React.createElement(Button, { onClick: () => {
|
64
|
+
console.log(args.formRef?.current?.data);
|
65
|
+
args.formRef?.current?.data &&
|
66
|
+
args.formRef.current.submit({ data: args.formRef.current.data }, 'submit', (error) => {
|
67
|
+
console.log(error);
|
68
|
+
});
|
69
|
+
} },
|
70
|
+
' ',
|
71
|
+
"Submit",
|
72
|
+
' ')));
|
73
|
+
};
|
74
|
+
export const FormWithNoButtons = FormWithNoButtonsTemplate.bind({});
|
75
|
+
FormWithNoButtons.args = {
|
76
|
+
actionId: '_update',
|
77
|
+
actionType: 'update',
|
78
|
+
object: object,
|
79
|
+
instance: { id: 'id', objectId: 'test', name: 'test', integer: 12, decimal: 12.5 },
|
80
|
+
hideButtons: true,
|
81
|
+
formRef: { current: {} },
|
82
|
+
};
|