@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.
Files changed (24) hide show
  1. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +24 -2
  2. package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +45 -0
  3. package/dist/published/components/custom/FormField/DatePickerSelect/DatePickerSelect.js +22 -6
  4. package/dist/published/components/custom/FormField/FormField.d.ts +3 -1
  5. package/dist/published/components/custom/FormField/FormField.js +17 -5
  6. package/dist/published/components/custom/FormField/InputFieldComponent/InputFieldComponent.js +6 -4
  7. package/dist/published/components/custom/FormV2/FormRenderer.js +17 -0
  8. package/dist/published/components/custom/FormV2/FormRendererContainer.js +66 -83
  9. package/dist/published/components/custom/FormV2/components/DefaultValues.d.ts +2 -2
  10. package/dist/published/components/custom/FormV2/components/DefaultValues.js +36 -28
  11. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +13 -13
  12. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +2 -3
  13. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +5 -4
  14. package/dist/published/components/custom/FormV2/components/PropertyProtection.d.ts +16 -0
  15. package/dist/published/components/custom/FormV2/components/PropertyProtection.js +113 -0
  16. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +3 -2
  17. package/dist/published/components/custom/FormV2/components/types.d.ts +1 -0
  18. package/dist/published/components/custom/FormV2/components/utils.d.ts +2 -0
  19. package/dist/published/components/custom/FormV2/components/utils.js +72 -4
  20. package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +127 -1
  21. package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +13 -3
  22. package/dist/published/stories/CriteriaBuilder.stories.js +6 -0
  23. package/dist/published/types.d.ts +3 -0
  24. 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 = fieldValue && DateTime.fromISO(fieldValue).toFormat('MM/dd/yyyy');
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(Typography, { variant: "body1", key: entryId, sx: { height: '24px' } }, fieldValue))));
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
  }
@@ -57,6 +57,12 @@ const defaultProperties = [
57
57
  required: false,
58
58
  objectId: 'application',
59
59
  },
60
+ {
61
+ id: 'dynamicObject',
62
+ name: 'Dynamic Object',
63
+ type: 'object',
64
+ required: false,
65
+ },
60
66
  {
61
67
  id: 'applicantName',
62
68
  name: 'Application Info',
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evoke-platform/ui-components",
3
- "version": "1.10.0-dev.33",
3
+ "version": "1.10.0-dev.35",
4
4
  "description": "",
5
5
  "main": "dist/published/index.js",
6
6
  "module": "dist/published/index.js",