@evoke-platform/ui-components 1.10.0-dev.33 → 1.10.0-dev.35
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 +24 -2
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +45 -0
- package/dist/published/components/custom/FormField/DatePickerSelect/DatePickerSelect.js +22 -6
- package/dist/published/components/custom/FormField/FormField.d.ts +3 -1
- package/dist/published/components/custom/FormField/FormField.js +17 -5
- package/dist/published/components/custom/FormField/InputFieldComponent/InputFieldComponent.js +6 -4
- package/dist/published/components/custom/FormV2/FormRenderer.js +17 -0
- package/dist/published/components/custom/FormV2/FormRendererContainer.js +66 -83
- package/dist/published/components/custom/FormV2/components/DefaultValues.d.ts +2 -2
- package/dist/published/components/custom/FormV2/components/DefaultValues.js +36 -28
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +13 -13
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +2 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +5 -4
- package/dist/published/components/custom/FormV2/components/PropertyProtection.d.ts +16 -0
- package/dist/published/components/custom/FormV2/components/PropertyProtection.js +113 -0
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +3 -2
- package/dist/published/components/custom/FormV2/components/types.d.ts +1 -0
- package/dist/published/components/custom/FormV2/components/utils.d.ts +2 -0
- package/dist/published/components/custom/FormV2/components/utils.js +72 -4
- package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +127 -1
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +13 -3
- package/dist/published/stories/CriteriaBuilder.stories.js +6 -0
- package/dist/published/types.d.ts +3 -0
- package/package.json +1 -1
|
@@ -363,7 +363,7 @@ describe('FormRenderer', () => {
|
|
|
363
363
|
});
|
|
364
364
|
});
|
|
365
365
|
});
|
|
366
|
-
describe('when passed a related object entry', () => {
|
|
366
|
+
describe('when passed a regular related object entry', () => {
|
|
367
367
|
const setupTestMocks = (object, form, instances) => {
|
|
368
368
|
server.use(http.get(`/api/data/objects/${object.id}/effective`, () => HttpResponse.json(object)), http.get(`/api/data/forms/${form.id}`, () => HttpResponse.json(form)), http.get(`/api/data/forms?filter={"where":{"actionId":"${form.actionId}","objectId":"${object.id}"}}`, () => HttpResponse.json([form])), http.get(`/api/data/objects/${object.id}/instances`, () => HttpResponse.json(instances || [])));
|
|
369
369
|
};
|
|
@@ -1127,6 +1127,132 @@ describe('FormRenderer', () => {
|
|
|
1127
1127
|
});
|
|
1128
1128
|
});
|
|
1129
1129
|
});
|
|
1130
|
+
describe('when passed a dynamic related object entry', () => {
|
|
1131
|
+
const setupTestMocks = (object, form, instances) => {
|
|
1132
|
+
server.use(http.get(`/api/data/objects/${object.id}/effective`, () => HttpResponse.json(object)), http.get(`/api/data/forms/${form.id}`, () => HttpResponse.json(form)), http.get(`/api/data/forms?filter={"where":{"actionId":"${form.actionId}","objectId":"${object.id}"}}`, () => HttpResponse.json([form])), http.get(`/api/data/objects/${object.id}/instances`, () => HttpResponse.json(instances || [])));
|
|
1133
|
+
};
|
|
1134
|
+
const form = {
|
|
1135
|
+
id: 'relatedObjectTestForm',
|
|
1136
|
+
name: 'Related Object Test Form',
|
|
1137
|
+
entries: [
|
|
1138
|
+
{
|
|
1139
|
+
type: 'input',
|
|
1140
|
+
parameterId: 'specialtyType',
|
|
1141
|
+
display: {
|
|
1142
|
+
label: 'Speciality Type',
|
|
1143
|
+
createActionId: '_create',
|
|
1144
|
+
relatedObjectId: 'specialtyType',
|
|
1145
|
+
},
|
|
1146
|
+
},
|
|
1147
|
+
],
|
|
1148
|
+
actionId: '_update',
|
|
1149
|
+
objectId: 'relatedObjectTestForm',
|
|
1150
|
+
};
|
|
1151
|
+
beforeEach(async () => {
|
|
1152
|
+
const relatedObjectTestFormObject = {
|
|
1153
|
+
id: 'relatedObjectTestForm',
|
|
1154
|
+
name: 'Related Object Test Form',
|
|
1155
|
+
actions: [
|
|
1156
|
+
{
|
|
1157
|
+
id: '_update',
|
|
1158
|
+
name: 'Update',
|
|
1159
|
+
type: 'update',
|
|
1160
|
+
parameters: [
|
|
1161
|
+
{
|
|
1162
|
+
id: 'specialtyType',
|
|
1163
|
+
name: 'Related Object',
|
|
1164
|
+
type: 'object',
|
|
1165
|
+
},
|
|
1166
|
+
],
|
|
1167
|
+
outputEvent: 'updated',
|
|
1168
|
+
},
|
|
1169
|
+
],
|
|
1170
|
+
properties: [
|
|
1171
|
+
{
|
|
1172
|
+
id: 'specialtyType',
|
|
1173
|
+
name: 'Related Object',
|
|
1174
|
+
type: 'object',
|
|
1175
|
+
},
|
|
1176
|
+
],
|
|
1177
|
+
};
|
|
1178
|
+
setupTestMocks(relatedObjectTestFormObject, form);
|
|
1179
|
+
const specialtyTypeForm = {
|
|
1180
|
+
id: 'specialtyTypeForm',
|
|
1181
|
+
name: 'Specialty Type Form',
|
|
1182
|
+
entries: [
|
|
1183
|
+
{
|
|
1184
|
+
type: 'content',
|
|
1185
|
+
html: '<div>Specialty Type Form Content</div>',
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
type: 'input',
|
|
1189
|
+
parameterId: 'requiredField',
|
|
1190
|
+
display: {
|
|
1191
|
+
label: 'Required Field',
|
|
1192
|
+
required: true,
|
|
1193
|
+
},
|
|
1194
|
+
},
|
|
1195
|
+
],
|
|
1196
|
+
actionId: '_create',
|
|
1197
|
+
objectId: 'specialtyType',
|
|
1198
|
+
display: {
|
|
1199
|
+
submitLabel: 'Create Specialty Type',
|
|
1200
|
+
},
|
|
1201
|
+
};
|
|
1202
|
+
const specialtyTypeObject = {
|
|
1203
|
+
id: 'specialtyType',
|
|
1204
|
+
name: 'Specialty Type',
|
|
1205
|
+
actions: [
|
|
1206
|
+
{
|
|
1207
|
+
id: '_create',
|
|
1208
|
+
name: 'Create',
|
|
1209
|
+
type: 'create',
|
|
1210
|
+
parameters: [
|
|
1211
|
+
{
|
|
1212
|
+
id: 'requiredField',
|
|
1213
|
+
name: 'Required Field',
|
|
1214
|
+
type: 'string',
|
|
1215
|
+
required: true,
|
|
1216
|
+
},
|
|
1217
|
+
],
|
|
1218
|
+
outputEvent: 'created',
|
|
1219
|
+
defaultFormId: 'specialtyTypeForm',
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
id: '_update',
|
|
1223
|
+
name: 'Update',
|
|
1224
|
+
type: 'update',
|
|
1225
|
+
parameters: [],
|
|
1226
|
+
outputEvent: 'updated',
|
|
1227
|
+
},
|
|
1228
|
+
],
|
|
1229
|
+
properties: [],
|
|
1230
|
+
};
|
|
1231
|
+
setupTestMocks(specialtyTypeObject, specialtyTypeForm);
|
|
1232
|
+
});
|
|
1233
|
+
it('displays form if user switches to creating a new record', async () => {
|
|
1234
|
+
const user = userEvent.setup();
|
|
1235
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1236
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
1237
|
+
await screen.findByRole('radiogroup', { name: 'Relation Type' });
|
|
1238
|
+
const existingRecordButton = await screen.findByRole('radio', { name: /existing/i });
|
|
1239
|
+
expect(existingRecordButton).toBeChecked();
|
|
1240
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
1241
|
+
await user.click(newRecordButton);
|
|
1242
|
+
await screen.findByText('Specialty Type Form Content');
|
|
1243
|
+
});
|
|
1244
|
+
it('displays a not found error in record creation mode if a form could not be found', async () => {
|
|
1245
|
+
const user = userEvent.setup();
|
|
1246
|
+
server.use(http.get('/api/data/forms/specialtyTypeForm', () => {
|
|
1247
|
+
return HttpResponse.json({ error: 'Not found' }, { status: 404 });
|
|
1248
|
+
}));
|
|
1249
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1250
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
1251
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
1252
|
+
await user.click(newRecordButton);
|
|
1253
|
+
await screen.findByText(/not found/i);
|
|
1254
|
+
});
|
|
1255
|
+
});
|
|
1130
1256
|
describe('when passed a one-to-many collection entry', () => {
|
|
1131
1257
|
const setupTestMocks = (object, form, instances) => {
|
|
1132
1258
|
server.use(http.get(`/api/data/objects/${object.id}/effective`, () => HttpResponse.json(object)), http.get(`/api/data/forms/${form.id}`, () => HttpResponse.json(form)), http.get(`/api/data/forms?filter={"where":{"actionId":"${form.actionId}","objectId":"${object.id}"}}`, () => HttpResponse.json([form])), http.get(`/api/data/objects/${object.id}/instances`, () => HttpResponse.json(instances || [])), http.get(`/api/data/objects/${object.id}/instances/checkAccess`, () => HttpResponse.json({
|
|
@@ -15,6 +15,7 @@ import RepeatableField from '../FormV2/components/FormFieldTypes/CollectionFiles
|
|
|
15
15
|
import Criteria from '../FormV2/components/FormFieldTypes/Criteria';
|
|
16
16
|
import { Document } from '../FormV2/components/FormFieldTypes/DocumentFiles/Document';
|
|
17
17
|
import { Image } from '../FormV2/components/FormFieldTypes/Image';
|
|
18
|
+
import PropertyProtection from '../FormV2/components/PropertyProtection';
|
|
18
19
|
import { entryIsVisible, fetchCollectionData, filterEmptySections, getDefaultPages, isAddressProperty, } from '../FormV2/components/utils';
|
|
19
20
|
function ViewOnlyEntryRenderer(props) {
|
|
20
21
|
const { entry } = props;
|
|
@@ -23,6 +24,8 @@ function ViewOnlyEntryRenderer(props) {
|
|
|
23
24
|
const apiServices = useApiServices();
|
|
24
25
|
const { defaultPages, findDefaultPageSlugFor } = useApp();
|
|
25
26
|
const [navigationSlug, setNavigationSlug] = useState(fetchedOptions[`${entryId}NavigationSlug`]);
|
|
27
|
+
const [currentDisplayValue, setCurrentDisplayValue] = useState(instance?.[entryId]);
|
|
28
|
+
const [protectionMode, setProtectionMode] = useState('mask');
|
|
26
29
|
const initialMiddleObjectInstances = fetchedOptions[`${entryId}InitialMiddleObjectInstances`];
|
|
27
30
|
const middleObject = fetchedOptions[`${entryId}MiddleObject`];
|
|
28
31
|
const display = 'display' in entry ? entry.display : undefined;
|
|
@@ -40,6 +43,8 @@ function ViewOnlyEntryRenderer(props) {
|
|
|
40
43
|
}
|
|
41
44
|
return def;
|
|
42
45
|
}, [entry, object]);
|
|
46
|
+
const isProtectedProperty = fieldDefinition?.protection?.maskChar;
|
|
47
|
+
const protectionComponent = isProtectedProperty ? (React.createElement(PropertyProtection, { parameter: fieldDefinition, protection: fieldDefinition?.protection, mask: fieldDefinition?.mask, value: currentDisplayValue, canEdit: false, setCurrentDisplayValue: setCurrentDisplayValue, mode: protectionMode, setMode: setProtectionMode })) : null;
|
|
43
48
|
useEffect(() => {
|
|
44
49
|
if (fieldDefinition?.type === 'collection' && fieldDefinition?.manyToManyPropertyId && instance) {
|
|
45
50
|
fetchCollectionData(apiServices, fieldDefinition, setFetchedOptions, instance.id, fetchedOptions, initialMiddleObjectInstances);
|
|
@@ -72,7 +77,7 @@ function ViewOnlyEntryRenderer(props) {
|
|
|
72
77
|
return (React.createElement(AddressFields, { entry: entry, viewOnly: true, entryId: entryId, fieldDefinition: fieldDefinition }));
|
|
73
78
|
}
|
|
74
79
|
else {
|
|
75
|
-
let fieldValue = instance?.[entryId] || '';
|
|
80
|
+
let fieldValue = currentDisplayValue ?? (instance?.[entryId] || '');
|
|
76
81
|
switch (fieldDefinition?.type) {
|
|
77
82
|
case 'object':
|
|
78
83
|
if (navigationSlug && fieldDefinition?.objectId) {
|
|
@@ -93,7 +98,10 @@ function ViewOnlyEntryRenderer(props) {
|
|
|
93
98
|
fieldValue = fieldValue && fieldValue.name;
|
|
94
99
|
break;
|
|
95
100
|
case 'date':
|
|
96
|
-
fieldValue =
|
|
101
|
+
fieldValue =
|
|
102
|
+
isProtectedProperty && protectionMode === 'mask'
|
|
103
|
+
? fieldValue
|
|
104
|
+
: fieldValue && DateTime.fromISO(fieldValue).toFormat('MM/dd/yyyy');
|
|
97
105
|
break;
|
|
98
106
|
case 'date-time':
|
|
99
107
|
fieldValue =
|
|
@@ -136,7 +144,9 @@ function ViewOnlyEntryRenderer(props) {
|
|
|
136
144
|
React.createElement(Criteria, { value: fieldValue, fieldDefinition: fieldDefinition, canUpdateProperty: false })));
|
|
137
145
|
}
|
|
138
146
|
else {
|
|
139
|
-
return (React.createElement(FieldWrapper, { inputId: entryId, inputType: fieldDefinition?.type || 'string', label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, prefix: display?.prefix, suffix: display?.suffix, viewOnly: true }, fieldDefinition?.type === 'boolean' && fieldValue ? (React.createElement(CheckCircleRounded, { "aria-label": "Checked", color: "success" })) : fieldDefinition?.type === 'boolean' ? (React.createElement(CancelRounded, { "aria-label": "Unchecked", color: "error" })) : (React.createElement(
|
|
147
|
+
return (React.createElement(FieldWrapper, { inputId: entryId, inputType: fieldDefinition?.type || 'string', label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, prefix: display?.prefix, suffix: display?.suffix, viewOnly: true }, fieldDefinition?.type === 'boolean' && fieldValue ? (React.createElement(CheckCircleRounded, { "aria-label": "Checked", color: "success" })) : fieldDefinition?.type === 'boolean' ? (React.createElement(CancelRounded, { "aria-label": "Unchecked", color: "error" })) : (React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
|
|
148
|
+
React.createElement(Typography, { variant: "body1", key: entryId, sx: { height: '24px' } }, fieldValue),
|
|
149
|
+
fieldDefinition?.protection?.maskChar && protectionComponent))));
|
|
140
150
|
}
|
|
141
151
|
}
|
|
142
152
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PropertyProtection } from '@evoke-platform/context';
|
|
1
2
|
export type EvokeObject = {
|
|
2
3
|
id: string;
|
|
3
4
|
name: string;
|
|
@@ -13,6 +14,8 @@ export type ObjectProperty = {
|
|
|
13
14
|
searchable?: boolean;
|
|
14
15
|
objectId?: string;
|
|
15
16
|
formula?: string;
|
|
17
|
+
mask?: string;
|
|
18
|
+
protection?: PropertyProtection;
|
|
16
19
|
};
|
|
17
20
|
export type Obj = {
|
|
18
21
|
id: string;
|