@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
@@ -1,4 +1,3 @@
1
- import * as matchers from '@testing-library/jest-dom/matchers';
2
1
  import { render as baseRender, screen, waitFor, within } from '@testing-library/react';
3
2
  import userEvent from '@testing-library/user-event';
4
3
  import { isEmpty, isEqual, set } from 'lodash';
@@ -9,7 +8,6 @@ import { MemoryRouter } from 'react-router-dom';
9
8
  import { expect, it } from 'vitest';
10
9
  import FormRenderer from '../FormRenderer';
11
10
  import { accessibility508Object, createSpecialtyForm, jsonLogicDisplayTestSpecialtyForm, licenseObject, npLicense, npSpecialtyType1, npSpecialtyType2, rnLicense, rnSpecialtyType1, rnSpecialtyType2, simpleConditionDisplayTestSpecialtyForm, specialtyObject, specialtyTypeObject, UpdateAccessibilityFormOne, UpdateAccessibilityFormTwo, users, } from './test-data';
12
- expect.extend(matchers);
13
11
  // Mock ResizeObserver
14
12
  global.ResizeObserver = class ResizeObserver {
15
13
  observe() { }
@@ -20,7 +18,7 @@ const WithProviders = ({ children }) => {
20
18
  return React.createElement(MemoryRouter, null, children);
21
19
  };
22
20
  const render = (ui, options) => baseRender(ui, { wrapper: WithProviders, ...options });
23
- describe('Form component', () => {
21
+ describe('FormRenderer', () => {
24
22
  let server;
25
23
  beforeAll(() => {
26
24
  server = setupServer(http.get('/api/data/objects/specialtyType/effective', () => HttpResponse.json(specialtyTypeObject)), http.get('/api/data/objects/specialtyType/effective', (req) => {
@@ -59,9 +57,9 @@ describe('Form component', () => {
59
57
  npSpecialtyType1,
60
58
  npSpecialtyType2,
61
59
  ]);
62
- else if (isEqual(whereFilter, { and: [{ 'licenseType.id': 'rnLicenseType' }, {}] }))
60
+ else if (isEqual(whereFilter, { 'licenseType.id': 'rnLicenseType' }))
63
61
  return HttpResponse.json([rnSpecialtyType1, rnSpecialtyType2]);
64
- else if (isEqual(whereFilter, { and: [{ 'licenseType.id': 'npLicenseType' }, {}] }))
62
+ else if (isEqual(whereFilter, { 'licenseType.id': 'npLicenseType' }))
65
63
  return HttpResponse.json([npSpecialtyType1, npSpecialtyType2]);
66
64
  }
67
65
  }), http.get('/api/accessManagement/users', () => HttpResponse.json(users)));
@@ -650,13 +648,19 @@ describe('Form component', () => {
650
648
  parameterId: 'specialtyType',
651
649
  display: {
652
650
  label: 'Speciality Type',
651
+ createActionId: '_create',
653
652
  },
654
653
  },
655
654
  ],
656
655
  actionId: '_update',
657
656
  objectId: 'relatedObjectTestForm',
658
657
  };
658
+ let scrollIntoViewMock;
659
+ let originalScrollIntoView;
659
660
  beforeEach(async () => {
661
+ scrollIntoViewMock = vitest.fn();
662
+ originalScrollIntoView = Element.prototype.scrollIntoView;
663
+ Element.prototype.scrollIntoView = scrollIntoViewMock;
660
664
  const relatedObjectTestFormObject = {
661
665
  id: 'relatedObjectTestForm',
662
666
  name: 'Related Object Test Form',
@@ -693,9 +697,20 @@ describe('Form component', () => {
693
697
  type: 'content',
694
698
  html: '<div>Specialty Type Form Content</div>',
695
699
  },
700
+ {
701
+ type: 'input',
702
+ parameterId: 'requiredField',
703
+ display: {
704
+ label: 'Required Field',
705
+ required: true,
706
+ },
707
+ },
696
708
  ],
697
709
  actionId: '_create',
698
710
  objectId: 'specialtyType',
711
+ display: {
712
+ submitLabel: 'Create Specialty Type',
713
+ },
699
714
  };
700
715
  const specialtyTypeObject = {
701
716
  id: 'specialtyType',
@@ -705,7 +720,14 @@ describe('Form component', () => {
705
720
  id: '_create',
706
721
  name: 'Create',
707
722
  type: 'create',
708
- parameters: [],
723
+ parameters: [
724
+ {
725
+ id: 'requiredField',
726
+ name: 'Required Field',
727
+ type: 'string',
728
+ required: true,
729
+ },
730
+ ],
709
731
  outputEvent: 'created',
710
732
  defaultFormId: 'specialtyTypeForm',
711
733
  },
@@ -721,6 +743,9 @@ describe('Form component', () => {
721
743
  };
722
744
  setupTestMocks(specialtyTypeObject, specialtyTypeForm);
723
745
  });
746
+ afterEach(() => {
747
+ Element.prototype.scrollIntoView = originalScrollIntoView;
748
+ });
724
749
  it('displays an add button for related object fields', async () => {
725
750
  render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
726
751
  await screen.findByRole('button', { name: 'Add' });
@@ -813,6 +838,63 @@ describe('Form component', () => {
813
838
  await user.click(newRecordButton);
814
839
  await screen.findByText(/not found/i);
815
840
  });
841
+ it('should show validation errors in record creation mode', async () => {
842
+ const user = userEvent.setup();
843
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
844
+ await user.click(await screen.findByRole('button', { name: 'Add' }));
845
+ const newRecordButton = await screen.findByRole('radio', { name: /new/i });
846
+ await user.click(newRecordButton);
847
+ const createSpecialtyTypeButton = await screen.findByRole('button', {
848
+ name: /create specialty type/i,
849
+ });
850
+ await user.click(createSpecialtyTypeButton);
851
+ const errorMessage = await screen.findByRole('listitem');
852
+ expect(errorMessage).toHaveTextContent('Required Field is required');
853
+ });
854
+ it('should clear validation errors after they have been resolved', async () => {
855
+ const user = userEvent.setup();
856
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
857
+ await user.click(await screen.findByRole('button', { name: 'Add' }));
858
+ const newRecordButton = await screen.findByRole('radio', { name: /new/i });
859
+ await user.click(newRecordButton);
860
+ const createSpecialtyTypeButton = await screen.findByRole('button', {
861
+ name: /create specialty type/i,
862
+ });
863
+ await user.click(createSpecialtyTypeButton);
864
+ // Make sure error elements appear
865
+ screen.getByRole('listitem');
866
+ const requiredField = screen.getByRole('textbox', { name: /Required Field */i });
867
+ await user.type(requiredField, 'Some content here...');
868
+ expect(screen.queryByRole('listitem')).not.toBeInTheDocument();
869
+ });
870
+ it('should scroll to validation errors after submission', async () => {
871
+ const user = userEvent.setup();
872
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
873
+ await user.click(await screen.findByRole('button', { name: 'Add' }));
874
+ const newRecordButton = await screen.findByRole('radio', { name: /new/i });
875
+ await user.click(newRecordButton);
876
+ const createSpecialtyTypeButton = await screen.findByRole('button', {
877
+ name: /create specialty type/i,
878
+ });
879
+ await user.click(createSpecialtyTypeButton);
880
+ expect(scrollIntoViewMock).toHaveBeenCalled();
881
+ });
882
+ it('should not scroll to validation errors if there are none', async () => async () => {
883
+ const user = userEvent.setup();
884
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
885
+ await user.click(await screen.findByRole('button', { name: 'Add' }));
886
+ const newRecordButton = await screen.findByRole('radio', { name: /new/i });
887
+ await user.click(newRecordButton);
888
+ // Make sure error elements appear
889
+ screen.getByRole('listitem');
890
+ const requiredField = await screen.findByRole('textbox', { name: /Required Field */i });
891
+ await user.type(requiredField, 'Some content here...');
892
+ const createSpecialtyTypeButton = await screen.findByRole('button', {
893
+ name: /create specialty type/i,
894
+ });
895
+ await user.click(createSpecialtyTypeButton);
896
+ expect(scrollIntoViewMock).not.toHaveBeenCalled();
897
+ });
816
898
  });
817
899
  });
818
900
  describe('when in dropdown view', () => {
@@ -913,24 +995,25 @@ describe('Form component', () => {
913
995
  });
914
996
  describe('when mode is default (allows both new and existing)', () => {
915
997
  const form = {
916
- id: 'relatedObjectTestForm',
998
+ id: 'dropdownRelatedObjectTestForm',
917
999
  name: 'Related Object Test Form',
918
1000
  entries: [
919
1001
  {
920
1002
  type: 'input',
921
- parameterId: 'specialtyType',
1003
+ parameterId: 'licenseType',
922
1004
  display: {
923
- label: 'Speciality Type',
1005
+ label: 'License Type',
924
1006
  relatedObjectDisplay: 'dropdown',
1007
+ createActionId: '_create',
925
1008
  },
926
1009
  },
927
1010
  ],
928
1011
  actionId: '_update',
929
- objectId: 'relatedObjectTestForm',
1012
+ objectId: 'dropdownRelatedTestObject',
930
1013
  };
931
1014
  beforeEach(() => {
932
- const relatedObjectTestFormObject = {
933
- id: 'relatedObjectTestForm',
1015
+ const dropdownRelatedTestObject = {
1016
+ id: 'dropdownRelatedTestObject',
934
1017
  name: 'Related Object Test Form',
935
1018
  actions: [
936
1019
  {
@@ -939,10 +1022,10 @@ describe('Form component', () => {
939
1022
  type: 'update',
940
1023
  parameters: [
941
1024
  {
942
- id: 'specialtyType',
1025
+ id: 'licenseType',
943
1026
  name: 'Related Object',
944
1027
  type: 'object',
945
- objectId: 'specialtyType',
1028
+ objectId: 'licenseType',
946
1029
  },
947
1030
  ],
948
1031
  outputEvent: 'updated',
@@ -950,45 +1033,61 @@ describe('Form component', () => {
950
1033
  ],
951
1034
  properties: [
952
1035
  {
953
- id: 'specialtyType',
1036
+ id: 'licenseType',
954
1037
  name: 'Related Object',
955
1038
  type: 'object',
1039
+ objectId: 'licenseType',
956
1040
  },
957
1041
  ],
958
1042
  };
959
- setupTestMocks(relatedObjectTestFormObject, form);
960
- const specialtyTypeObject = {
961
- id: 'specialtyType',
962
- name: 'Specialty Type',
1043
+ setupTestMocks(dropdownRelatedTestObject, form);
1044
+ const licenseTypeObject = {
1045
+ id: 'licenseType',
1046
+ name: 'License Type',
963
1047
  actions: [
964
1048
  {
965
1049
  id: '_create',
966
1050
  name: 'Create',
967
1051
  type: 'create',
968
- parameters: [],
1052
+ parameters: [
1053
+ {
1054
+ type: 'string',
1055
+ id: 'name',
1056
+ name: 'License Type Name',
1057
+ },
1058
+ ],
969
1059
  outputEvent: 'created',
970
- defaultFormId: 'specialtyTypeForm',
1060
+ defaultFormId: 'licenseTypeForm',
1061
+ },
1062
+ ],
1063
+ properties: [
1064
+ {
1065
+ type: 'string',
1066
+ id: 'name',
1067
+ name: 'License Type Name',
971
1068
  },
972
1069
  ],
973
- properties: [],
974
1070
  };
975
- const specialtyTypeForm = {
976
- id: 'specialtyTypeForm',
977
- name: 'Specialty Type Form',
1071
+ const licenseTypeForm = {
1072
+ id: 'licenseTypeForm',
1073
+ name: 'License Type Form',
978
1074
  entries: [
979
1075
  {
980
- type: 'content',
981
- html: '<div>Specialty Type Form Content</div>',
1076
+ type: 'input',
1077
+ parameterId: 'name',
1078
+ display: {
1079
+ label: 'License Type Name',
1080
+ },
982
1081
  },
983
1082
  ],
984
1083
  actionId: '_create',
985
- objectId: 'specialtyType',
1084
+ objectId: 'licenseType',
986
1085
  };
987
- setupTestMocks(specialtyTypeObject, specialtyTypeForm, [
1086
+ setupTestMocks(licenseTypeObject, licenseTypeForm, [
988
1087
  {
989
- id: 'rnSpecialtyType1',
990
- name: 'RN Specialty Type #1',
991
- objectId: 'specialtyType',
1088
+ id: 'licenseType1',
1089
+ name: 'License Type #1',
1090
+ objectId: 'licenseType',
992
1091
  },
993
1092
  ]);
994
1093
  });
@@ -996,27 +1095,34 @@ describe('Form component', () => {
996
1095
  const user = userEvent.setup();
997
1096
  render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
998
1097
  // Navigate to and open dropdown
999
- const dropdown = await screen.findByRole('combobox', { name: 'Speciality Type' });
1098
+ const dropdown = await screen.findByRole('combobox', { name: 'License Type' });
1000
1099
  await user.click(dropdown);
1001
- await waitFor(() => {
1002
- expect(screen.getByText('+ Add New')).toBeInTheDocument();
1003
- });
1004
- await user.click(screen.getByText('+ Add New'));
1100
+ await screen.findByRole('listbox');
1101
+ await user.click(await screen.findByRole('option', { name: '+ Add New' }));
1102
+ });
1103
+ it('opens create related object instance form in dialog when add new is clicked', async () => {
1104
+ const user = userEvent.setup();
1105
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1106
+ // Navigate to and open dropdown
1107
+ const dropdown = await screen.findByRole('combobox', { name: 'License Type' });
1108
+ await user.click(dropdown);
1109
+ await screen.findByRole('listbox');
1110
+ await user.click(await screen.findByRole('option', { name: '+ Add New' }));
1005
1111
  await screen.findByRole('dialog');
1006
- await screen.findByText('Specialty Type Form Content');
1112
+ await screen.findByRole('textbox', { name: 'License Type Name' });
1007
1113
  });
1008
1114
  it('allows related object instance selection', async () => {
1009
1115
  const user = userEvent.setup();
1010
1116
  render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1011
1117
  // Navigate to and open dropdown
1012
- const dropdown = await screen.findByRole('combobox', { name: 'Speciality Type' });
1118
+ const dropdown = await screen.findByRole('combobox', { name: 'License Type' });
1013
1119
  await user.click(dropdown);
1014
- const option = await screen.findByRole('option', { name: 'RN Specialty Type #1' });
1120
+ const option = await screen.findByRole('option', { name: 'License Type #1' });
1015
1121
  await user.click(option);
1016
1122
  // Verify option has been removed from the document
1017
1123
  expect(option).not.toBeInTheDocument();
1018
1124
  // Verify selection
1019
- screen.getByText('RN Specialty Type #1');
1125
+ screen.getByText('License Type #1');
1020
1126
  });
1021
1127
  });
1022
1128
  });
@@ -1041,13 +1147,19 @@ describe('Form component', () => {
1041
1147
  },
1042
1148
  display: {
1043
1149
  label: 'Collection',
1150
+ createActionId: '_create',
1044
1151
  },
1045
1152
  },
1046
1153
  ],
1047
1154
  actionId: '_update',
1048
1155
  objectId: 'testObjectForCollections',
1049
1156
  };
1157
+ let scrollIntoViewMock;
1158
+ let originalScrollIntoView;
1050
1159
  beforeEach(() => {
1160
+ scrollIntoViewMock = vitest.fn();
1161
+ originalScrollIntoView = Element.prototype.scrollIntoView;
1162
+ Element.prototype.scrollIntoView = scrollIntoViewMock;
1051
1163
  const collectionFormObject = {
1052
1164
  id: 'testObjectForCollections',
1053
1165
  name: 'Object for one-to-many collections tests',
@@ -1105,6 +1217,7 @@ describe('Form component', () => {
1105
1217
  objectId: 'testObjectForCollections',
1106
1218
  },
1107
1219
  ],
1220
+ defaultFormId: 'collectionObjectForm',
1108
1221
  },
1109
1222
  ],
1110
1223
  };
@@ -1117,6 +1230,7 @@ describe('Form component', () => {
1117
1230
  parameterId: 'name',
1118
1231
  display: {
1119
1232
  label: 'Name',
1233
+ required: true,
1120
1234
  },
1121
1235
  },
1122
1236
  {
@@ -1130,9 +1244,15 @@ describe('Form component', () => {
1130
1244
  ],
1131
1245
  actionId: '_create',
1132
1246
  objectId: 'collectionObject',
1247
+ display: {
1248
+ submitLabel: 'Create Collection Item',
1249
+ },
1133
1250
  };
1134
1251
  setupTestMocks(collectionObject, collectionObjectForm);
1135
1252
  });
1253
+ afterEach(() => {
1254
+ Element.prototype.scrollIntoView = originalScrollIntoView;
1255
+ });
1136
1256
  it('should render collection field', async () => {
1137
1257
  render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1138
1258
  await screen.findByText('Collection');
@@ -1191,7 +1311,7 @@ describe('Form component', () => {
1191
1311
  const user = userEvent.setup();
1192
1312
  render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1193
1313
  await user.click(await screen.findByRole('button', { name: 'Add' }));
1194
- await screen.findByRole('button', { name: 'Submit' });
1314
+ await screen.findByRole('button', { name: 'Create Collection Item' });
1195
1315
  });
1196
1316
  it('should hide related object field in collection item form', async () => {
1197
1317
  const user = userEvent.setup();
@@ -1200,7 +1320,7 @@ describe('Form component', () => {
1200
1320
  await user.click(addButton);
1201
1321
  await screen.findByRole('dialog');
1202
1322
  // Make sure other form entry is present
1203
- await screen.findByRole('textbox', { name: 'Name' });
1323
+ await screen.findByRole('textbox', { name: 'Name *' });
1204
1324
  const relatedObjectField = screen.queryByRole('textbox', { name: 'Related Object' });
1205
1325
  expect(relatedObjectField).not.toBeInTheDocument();
1206
1326
  });
@@ -1219,12 +1339,136 @@ describe('Form component', () => {
1219
1339
  const addButton = await screen.findByRole('button', { name: /add/i });
1220
1340
  await user.click(addButton);
1221
1341
  await screen.findByRole('dialog');
1222
- const nameField = screen.getByRole('textbox', { name: 'Name' });
1342
+ const nameField = screen.getByRole('textbox', { name: 'Name *' });
1223
1343
  await user.type(nameField, 'New Collection Item');
1224
- const submitButton = screen.getByRole('button', { name: 'Submit' });
1344
+ const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
1225
1345
  await user.click(submitButton);
1226
1346
  await screen.findByRole('columnheader', { name: 'Name' });
1227
1347
  screen.getByRole('cell', { name: 'New Collection Item' });
1228
1348
  });
1349
+ it('should show validation errors', async () => {
1350
+ const user = userEvent.setup();
1351
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1352
+ const addButton = await screen.findByRole('button', { name: /add/i });
1353
+ await user.click(addButton);
1354
+ await screen.findByRole('dialog');
1355
+ const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
1356
+ await user.click(submitButton);
1357
+ const errorMessage = await screen.findByRole('listitem');
1358
+ expect(errorMessage).toHaveTextContent('Name is required');
1359
+ });
1360
+ it('should hide validation errors after they have been resolved', async () => {
1361
+ const user = userEvent.setup();
1362
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1363
+ const addButton = await screen.findByRole('button', { name: /add/i });
1364
+ await user.click(addButton);
1365
+ await screen.findByRole('dialog');
1366
+ const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
1367
+ await user.click(submitButton);
1368
+ // Make sure error elements appear
1369
+ screen.getByRole('listitem');
1370
+ const requiredField = screen.getByRole('textbox', { name: /Name */i });
1371
+ await user.type(requiredField, 'Some content here...');
1372
+ expect(screen.queryByRole('listitem')).not.toBeInTheDocument();
1373
+ });
1374
+ it('should scroll to validation errors on submit', async () => {
1375
+ const user = userEvent.setup();
1376
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1377
+ const addButton = await screen.findByRole('button', { name: /add/i });
1378
+ await user.click(addButton);
1379
+ await screen.findByRole('dialog');
1380
+ const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
1381
+ await user.click(submitButton);
1382
+ expect(scrollIntoViewMock).toHaveBeenCalled();
1383
+ });
1384
+ it('should not scroll to validation errors if there are none', async () => async () => {
1385
+ const user = userEvent.setup();
1386
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1387
+ const addButton = await screen.findByRole('button', { name: /add/i });
1388
+ await user.click(addButton);
1389
+ await screen.findByRole('dialog');
1390
+ const requiredField = await screen.findByRole('textbox', { name: /Name */i });
1391
+ await user.type(requiredField, 'Some content here...');
1392
+ const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
1393
+ await user.click(submitButton);
1394
+ expect(scrollIntoViewMock).not.toHaveBeenCalled();
1395
+ });
1396
+ });
1397
+ describe('when passed a text field entry', () => {
1398
+ it('should render text field', async () => {
1399
+ const form = {
1400
+ id: 'textFieldTestForm',
1401
+ name: 'Text Field Test Form',
1402
+ entries: [
1403
+ {
1404
+ type: 'inputField',
1405
+ input: {
1406
+ id: 'textField',
1407
+ type: 'string',
1408
+ },
1409
+ display: {
1410
+ label: 'Text Field',
1411
+ },
1412
+ },
1413
+ ],
1414
+ actionId: '_update',
1415
+ objectId: 'textFieldTestObject',
1416
+ };
1417
+ const textFieldTestObject = {
1418
+ id: 'textFieldTestObject',
1419
+ name: 'Text Field Test Object',
1420
+ actions: [
1421
+ {
1422
+ id: '_update',
1423
+ name: 'Update',
1424
+ type: 'update',
1425
+ outputEvent: 'updated',
1426
+ },
1427
+ ],
1428
+ properties: [],
1429
+ };
1430
+ server.use(http.get(`/api/data/objects/${textFieldTestObject.id}/effective`, () => HttpResponse.json(textFieldTestObject)));
1431
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1432
+ await screen.findByRole('textbox', { name: 'Text Field' });
1433
+ });
1434
+ it('should allow text input in text field', async () => {
1435
+ const user = userEvent.setup();
1436
+ const form = {
1437
+ id: 'textFieldTestForm',
1438
+ name: 'Text Field Test Form',
1439
+ entries: [
1440
+ {
1441
+ type: 'inputField',
1442
+ input: {
1443
+ id: 'textField',
1444
+ type: 'string',
1445
+ },
1446
+ display: {
1447
+ label: 'Text Field',
1448
+ },
1449
+ },
1450
+ ],
1451
+ actionId: '_update',
1452
+ objectId: 'textFieldTestObject',
1453
+ };
1454
+ const textFieldTestObject = {
1455
+ id: 'textFieldTestObject',
1456
+ name: 'Text Field Test Object',
1457
+ actions: [
1458
+ {
1459
+ id: '_update',
1460
+ name: 'Update',
1461
+ type: 'update',
1462
+ outputEvent: 'updated',
1463
+ },
1464
+ ],
1465
+ properties: [],
1466
+ };
1467
+ server.use(http.get(`/api/data/objects/${textFieldTestObject.id}/effective`, () => HttpResponse.json(textFieldTestObject)));
1468
+ render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
1469
+ const textField = await screen.findByRole('textbox', { name: 'Text Field' });
1470
+ await user.type(textField, 'Test Input');
1471
+ expect(textField).toHaveValue('Test Input');
1472
+ });
1229
1473
  });
1230
1474
  });