@evoke-platform/ui-components 1.10.0-testing.8 → 1.10.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/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 +25 -3
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.d.ts +1 -0
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +473 -0
- package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +19 -6
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/Document.js +2 -1
- 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/FormField/DatePickerSelect/DatePickerSelect.js +36 -7
- package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js +14 -1
- 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/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 +46 -8
- package/dist/published/components/custom/FormV2/FormRendererContainer.js +178 -153
- 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/DefaultValues.d.ts +2 -2
- package/dist/published/components/custom/FormV2/components/DefaultValues.js +36 -28
- 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.js +6 -23
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +16 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +22 -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 +115 -87
- 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 +43 -20
- 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/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 +47 -24
- package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrors.js +1 -1
- package/dist/published/components/custom/FormV2/components/types.d.ts +2 -0
- package/dist/published/components/custom/FormV2/components/utils.d.ts +6 -4
- package/dist/published/components/custom/FormV2/components/utils.js +83 -13
- package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +413 -46
- package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +983 -16
- package/dist/published/components/custom/FormV2/tests/test-data.d.ts +1 -0
- package/dist/published/components/custom/FormV2/tests/test-data.js +138 -0
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +3 -0
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +165 -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/CriteriaBuilder.stories.js +6 -0
- 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 +26 -1
- 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/dist/published/types.d.ts +3 -0
- 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('
|
|
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, {
|
|
60
|
+
else if (isEqual(whereFilter, { 'licenseType.id': 'rnLicenseType' }))
|
|
63
61
|
return HttpResponse.json([rnSpecialtyType1, rnSpecialtyType2]);
|
|
64
|
-
else if (isEqual(whereFilter, {
|
|
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)));
|
|
@@ -365,7 +363,7 @@ describe('Form component', () => {
|
|
|
365
363
|
});
|
|
366
364
|
});
|
|
367
365
|
});
|
|
368
|
-
describe('when passed a related object entry', () => {
|
|
366
|
+
describe('when passed a regular related object entry', () => {
|
|
369
367
|
const setupTestMocks = (object, form, instances) => {
|
|
370
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 || [])));
|
|
371
369
|
};
|
|
@@ -657,7 +655,12 @@ describe('Form component', () => {
|
|
|
657
655
|
actionId: '_update',
|
|
658
656
|
objectId: 'relatedObjectTestForm',
|
|
659
657
|
};
|
|
658
|
+
let scrollIntoViewMock;
|
|
659
|
+
let originalScrollIntoView;
|
|
660
660
|
beforeEach(async () => {
|
|
661
|
+
scrollIntoViewMock = vitest.fn();
|
|
662
|
+
originalScrollIntoView = Element.prototype.scrollIntoView;
|
|
663
|
+
Element.prototype.scrollIntoView = scrollIntoViewMock;
|
|
661
664
|
const relatedObjectTestFormObject = {
|
|
662
665
|
id: 'relatedObjectTestForm',
|
|
663
666
|
name: 'Related Object Test Form',
|
|
@@ -694,9 +697,20 @@ describe('Form component', () => {
|
|
|
694
697
|
type: 'content',
|
|
695
698
|
html: '<div>Specialty Type Form Content</div>',
|
|
696
699
|
},
|
|
700
|
+
{
|
|
701
|
+
type: 'input',
|
|
702
|
+
parameterId: 'requiredField',
|
|
703
|
+
display: {
|
|
704
|
+
label: 'Required Field',
|
|
705
|
+
required: true,
|
|
706
|
+
},
|
|
707
|
+
},
|
|
697
708
|
],
|
|
698
709
|
actionId: '_create',
|
|
699
710
|
objectId: 'specialtyType',
|
|
711
|
+
display: {
|
|
712
|
+
submitLabel: 'Create Specialty Type',
|
|
713
|
+
},
|
|
700
714
|
};
|
|
701
715
|
const specialtyTypeObject = {
|
|
702
716
|
id: 'specialtyType',
|
|
@@ -706,7 +720,14 @@ describe('Form component', () => {
|
|
|
706
720
|
id: '_create',
|
|
707
721
|
name: 'Create',
|
|
708
722
|
type: 'create',
|
|
709
|
-
parameters: [
|
|
723
|
+
parameters: [
|
|
724
|
+
{
|
|
725
|
+
id: 'requiredField',
|
|
726
|
+
name: 'Required Field',
|
|
727
|
+
type: 'string',
|
|
728
|
+
required: true,
|
|
729
|
+
},
|
|
730
|
+
],
|
|
710
731
|
outputEvent: 'created',
|
|
711
732
|
defaultFormId: 'specialtyTypeForm',
|
|
712
733
|
},
|
|
@@ -722,6 +743,9 @@ describe('Form component', () => {
|
|
|
722
743
|
};
|
|
723
744
|
setupTestMocks(specialtyTypeObject, specialtyTypeForm);
|
|
724
745
|
});
|
|
746
|
+
afterEach(() => {
|
|
747
|
+
Element.prototype.scrollIntoView = originalScrollIntoView;
|
|
748
|
+
});
|
|
725
749
|
it('displays an add button for related object fields', async () => {
|
|
726
750
|
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
727
751
|
await screen.findByRole('button', { name: 'Add' });
|
|
@@ -814,6 +838,63 @@ describe('Form component', () => {
|
|
|
814
838
|
await user.click(newRecordButton);
|
|
815
839
|
await screen.findByText(/not found/i);
|
|
816
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
|
+
});
|
|
817
898
|
});
|
|
818
899
|
});
|
|
819
900
|
describe('when in dropdown view', () => {
|
|
@@ -914,25 +995,25 @@ describe('Form component', () => {
|
|
|
914
995
|
});
|
|
915
996
|
describe('when mode is default (allows both new and existing)', () => {
|
|
916
997
|
const form = {
|
|
917
|
-
id: '
|
|
998
|
+
id: 'dropdownRelatedObjectTestForm',
|
|
918
999
|
name: 'Related Object Test Form',
|
|
919
1000
|
entries: [
|
|
920
1001
|
{
|
|
921
1002
|
type: 'input',
|
|
922
|
-
parameterId: '
|
|
1003
|
+
parameterId: 'licenseType',
|
|
923
1004
|
display: {
|
|
924
|
-
label: '
|
|
1005
|
+
label: 'License Type',
|
|
925
1006
|
relatedObjectDisplay: 'dropdown',
|
|
926
1007
|
createActionId: '_create',
|
|
927
1008
|
},
|
|
928
1009
|
},
|
|
929
1010
|
],
|
|
930
1011
|
actionId: '_update',
|
|
931
|
-
objectId: '
|
|
1012
|
+
objectId: 'dropdownRelatedTestObject',
|
|
932
1013
|
};
|
|
933
1014
|
beforeEach(() => {
|
|
934
|
-
const
|
|
935
|
-
id: '
|
|
1015
|
+
const dropdownRelatedTestObject = {
|
|
1016
|
+
id: 'dropdownRelatedTestObject',
|
|
936
1017
|
name: 'Related Object Test Form',
|
|
937
1018
|
actions: [
|
|
938
1019
|
{
|
|
@@ -941,10 +1022,10 @@ describe('Form component', () => {
|
|
|
941
1022
|
type: 'update',
|
|
942
1023
|
parameters: [
|
|
943
1024
|
{
|
|
944
|
-
id: '
|
|
1025
|
+
id: 'licenseType',
|
|
945
1026
|
name: 'Related Object',
|
|
946
1027
|
type: 'object',
|
|
947
|
-
objectId: '
|
|
1028
|
+
objectId: 'licenseType',
|
|
948
1029
|
},
|
|
949
1030
|
],
|
|
950
1031
|
outputEvent: 'updated',
|
|
@@ -952,45 +1033,61 @@ describe('Form component', () => {
|
|
|
952
1033
|
],
|
|
953
1034
|
properties: [
|
|
954
1035
|
{
|
|
955
|
-
id: '
|
|
1036
|
+
id: 'licenseType',
|
|
956
1037
|
name: 'Related Object',
|
|
957
1038
|
type: 'object',
|
|
1039
|
+
objectId: 'licenseType',
|
|
958
1040
|
},
|
|
959
1041
|
],
|
|
960
1042
|
};
|
|
961
|
-
setupTestMocks(
|
|
962
|
-
const
|
|
963
|
-
id: '
|
|
964
|
-
name: '
|
|
1043
|
+
setupTestMocks(dropdownRelatedTestObject, form);
|
|
1044
|
+
const licenseTypeObject = {
|
|
1045
|
+
id: 'licenseType',
|
|
1046
|
+
name: 'License Type',
|
|
965
1047
|
actions: [
|
|
966
1048
|
{
|
|
967
1049
|
id: '_create',
|
|
968
1050
|
name: 'Create',
|
|
969
1051
|
type: 'create',
|
|
970
|
-
parameters: [
|
|
1052
|
+
parameters: [
|
|
1053
|
+
{
|
|
1054
|
+
type: 'string',
|
|
1055
|
+
id: 'name',
|
|
1056
|
+
name: 'License Type Name',
|
|
1057
|
+
},
|
|
1058
|
+
],
|
|
971
1059
|
outputEvent: 'created',
|
|
972
|
-
defaultFormId: '
|
|
1060
|
+
defaultFormId: 'licenseTypeForm',
|
|
1061
|
+
},
|
|
1062
|
+
],
|
|
1063
|
+
properties: [
|
|
1064
|
+
{
|
|
1065
|
+
type: 'string',
|
|
1066
|
+
id: 'name',
|
|
1067
|
+
name: 'License Type Name',
|
|
973
1068
|
},
|
|
974
1069
|
],
|
|
975
|
-
properties: [],
|
|
976
1070
|
};
|
|
977
|
-
const
|
|
978
|
-
id: '
|
|
979
|
-
name: '
|
|
1071
|
+
const licenseTypeForm = {
|
|
1072
|
+
id: 'licenseTypeForm',
|
|
1073
|
+
name: 'License Type Form',
|
|
980
1074
|
entries: [
|
|
981
1075
|
{
|
|
982
|
-
type: '
|
|
983
|
-
|
|
1076
|
+
type: 'input',
|
|
1077
|
+
parameterId: 'name',
|
|
1078
|
+
display: {
|
|
1079
|
+
label: 'License Type Name',
|
|
1080
|
+
},
|
|
984
1081
|
},
|
|
985
1082
|
],
|
|
986
1083
|
actionId: '_create',
|
|
987
|
-
objectId: '
|
|
1084
|
+
objectId: 'licenseType',
|
|
988
1085
|
};
|
|
989
|
-
setupTestMocks(
|
|
1086
|
+
setupTestMocks(licenseTypeObject, licenseTypeForm, [
|
|
990
1087
|
{
|
|
991
|
-
id: '
|
|
992
|
-
name: '
|
|
993
|
-
objectId: '
|
|
1088
|
+
id: 'licenseType1',
|
|
1089
|
+
name: 'License Type #1',
|
|
1090
|
+
objectId: 'licenseType',
|
|
994
1091
|
},
|
|
995
1092
|
]);
|
|
996
1093
|
});
|
|
@@ -998,31 +1095,164 @@ describe('Form component', () => {
|
|
|
998
1095
|
const user = userEvent.setup();
|
|
999
1096
|
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1000
1097
|
// Navigate to and open dropdown
|
|
1001
|
-
const dropdown = await screen.findByRole('combobox', { name: '
|
|
1098
|
+
const dropdown = await screen.findByRole('combobox', { name: 'License Type' });
|
|
1002
1099
|
await user.click(dropdown);
|
|
1003
|
-
await
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
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' }));
|
|
1007
1111
|
await screen.findByRole('dialog');
|
|
1008
|
-
await screen.
|
|
1112
|
+
await screen.findByRole('textbox', { name: 'License Type Name' });
|
|
1009
1113
|
});
|
|
1010
1114
|
it('allows related object instance selection', async () => {
|
|
1011
1115
|
const user = userEvent.setup();
|
|
1012
1116
|
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1013
1117
|
// Navigate to and open dropdown
|
|
1014
|
-
const dropdown = await screen.findByRole('combobox', { name: '
|
|
1118
|
+
const dropdown = await screen.findByRole('combobox', { name: 'License Type' });
|
|
1015
1119
|
await user.click(dropdown);
|
|
1016
|
-
const option = await screen.findByRole('option', { name: '
|
|
1120
|
+
const option = await screen.findByRole('option', { name: 'License Type #1' });
|
|
1017
1121
|
await user.click(option);
|
|
1018
1122
|
// Verify option has been removed from the document
|
|
1019
1123
|
expect(option).not.toBeInTheDocument();
|
|
1020
1124
|
// Verify selection
|
|
1021
|
-
screen.getByText('
|
|
1125
|
+
screen.getByText('License Type #1');
|
|
1022
1126
|
});
|
|
1023
1127
|
});
|
|
1024
1128
|
});
|
|
1025
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
|
+
});
|
|
1026
1256
|
describe('when passed a one-to-many collection entry', () => {
|
|
1027
1257
|
const setupTestMocks = (object, form, instances) => {
|
|
1028
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({
|
|
@@ -1050,7 +1280,12 @@ describe('Form component', () => {
|
|
|
1050
1280
|
actionId: '_update',
|
|
1051
1281
|
objectId: 'testObjectForCollections',
|
|
1052
1282
|
};
|
|
1283
|
+
let scrollIntoViewMock;
|
|
1284
|
+
let originalScrollIntoView;
|
|
1053
1285
|
beforeEach(() => {
|
|
1286
|
+
scrollIntoViewMock = vitest.fn();
|
|
1287
|
+
originalScrollIntoView = Element.prototype.scrollIntoView;
|
|
1288
|
+
Element.prototype.scrollIntoView = scrollIntoViewMock;
|
|
1054
1289
|
const collectionFormObject = {
|
|
1055
1290
|
id: 'testObjectForCollections',
|
|
1056
1291
|
name: 'Object for one-to-many collections tests',
|
|
@@ -1108,6 +1343,7 @@ describe('Form component', () => {
|
|
|
1108
1343
|
objectId: 'testObjectForCollections',
|
|
1109
1344
|
},
|
|
1110
1345
|
],
|
|
1346
|
+
defaultFormId: 'collectionObjectForm',
|
|
1111
1347
|
},
|
|
1112
1348
|
],
|
|
1113
1349
|
};
|
|
@@ -1120,6 +1356,7 @@ describe('Form component', () => {
|
|
|
1120
1356
|
parameterId: 'name',
|
|
1121
1357
|
display: {
|
|
1122
1358
|
label: 'Name',
|
|
1359
|
+
required: true,
|
|
1123
1360
|
},
|
|
1124
1361
|
},
|
|
1125
1362
|
{
|
|
@@ -1133,9 +1370,15 @@ describe('Form component', () => {
|
|
|
1133
1370
|
],
|
|
1134
1371
|
actionId: '_create',
|
|
1135
1372
|
objectId: 'collectionObject',
|
|
1373
|
+
display: {
|
|
1374
|
+
submitLabel: 'Create Collection Item',
|
|
1375
|
+
},
|
|
1136
1376
|
};
|
|
1137
1377
|
setupTestMocks(collectionObject, collectionObjectForm);
|
|
1138
1378
|
});
|
|
1379
|
+
afterEach(() => {
|
|
1380
|
+
Element.prototype.scrollIntoView = originalScrollIntoView;
|
|
1381
|
+
});
|
|
1139
1382
|
it('should render collection field', async () => {
|
|
1140
1383
|
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1141
1384
|
await screen.findByText('Collection');
|
|
@@ -1194,7 +1437,7 @@ describe('Form component', () => {
|
|
|
1194
1437
|
const user = userEvent.setup();
|
|
1195
1438
|
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1196
1439
|
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
1197
|
-
await screen.findByRole('button', { name: '
|
|
1440
|
+
await screen.findByRole('button', { name: 'Create Collection Item' });
|
|
1198
1441
|
});
|
|
1199
1442
|
it('should hide related object field in collection item form', async () => {
|
|
1200
1443
|
const user = userEvent.setup();
|
|
@@ -1203,7 +1446,7 @@ describe('Form component', () => {
|
|
|
1203
1446
|
await user.click(addButton);
|
|
1204
1447
|
await screen.findByRole('dialog');
|
|
1205
1448
|
// Make sure other form entry is present
|
|
1206
|
-
await screen.findByRole('textbox', { name: 'Name' });
|
|
1449
|
+
await screen.findByRole('textbox', { name: 'Name *' });
|
|
1207
1450
|
const relatedObjectField = screen.queryByRole('textbox', { name: 'Related Object' });
|
|
1208
1451
|
expect(relatedObjectField).not.toBeInTheDocument();
|
|
1209
1452
|
});
|
|
@@ -1222,12 +1465,136 @@ describe('Form component', () => {
|
|
|
1222
1465
|
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1223
1466
|
await user.click(addButton);
|
|
1224
1467
|
await screen.findByRole('dialog');
|
|
1225
|
-
const nameField = screen.getByRole('textbox', { name: 'Name' });
|
|
1468
|
+
const nameField = screen.getByRole('textbox', { name: 'Name *' });
|
|
1226
1469
|
await user.type(nameField, 'New Collection Item');
|
|
1227
|
-
const submitButton = screen.getByRole('button', { name: '
|
|
1470
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1228
1471
|
await user.click(submitButton);
|
|
1229
1472
|
await screen.findByRole('columnheader', { name: 'Name' });
|
|
1230
1473
|
screen.getByRole('cell', { name: 'New Collection Item' });
|
|
1231
1474
|
});
|
|
1475
|
+
it('should show validation errors', async () => {
|
|
1476
|
+
const user = userEvent.setup();
|
|
1477
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1478
|
+
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1479
|
+
await user.click(addButton);
|
|
1480
|
+
await screen.findByRole('dialog');
|
|
1481
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1482
|
+
await user.click(submitButton);
|
|
1483
|
+
const errorMessage = await screen.findByRole('listitem');
|
|
1484
|
+
expect(errorMessage).toHaveTextContent('Name is required');
|
|
1485
|
+
});
|
|
1486
|
+
it('should hide validation errors after they have been resolved', async () => {
|
|
1487
|
+
const user = userEvent.setup();
|
|
1488
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1489
|
+
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1490
|
+
await user.click(addButton);
|
|
1491
|
+
await screen.findByRole('dialog');
|
|
1492
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1493
|
+
await user.click(submitButton);
|
|
1494
|
+
// Make sure error elements appear
|
|
1495
|
+
screen.getByRole('listitem');
|
|
1496
|
+
const requiredField = screen.getByRole('textbox', { name: /Name */i });
|
|
1497
|
+
await user.type(requiredField, 'Some content here...');
|
|
1498
|
+
expect(screen.queryByRole('listitem')).not.toBeInTheDocument();
|
|
1499
|
+
});
|
|
1500
|
+
it('should scroll to validation errors on submit', async () => {
|
|
1501
|
+
const user = userEvent.setup();
|
|
1502
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1503
|
+
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1504
|
+
await user.click(addButton);
|
|
1505
|
+
await screen.findByRole('dialog');
|
|
1506
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1507
|
+
await user.click(submitButton);
|
|
1508
|
+
expect(scrollIntoViewMock).toHaveBeenCalled();
|
|
1509
|
+
});
|
|
1510
|
+
it('should not scroll to validation errors if there are none', async () => async () => {
|
|
1511
|
+
const user = userEvent.setup();
|
|
1512
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1513
|
+
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1514
|
+
await user.click(addButton);
|
|
1515
|
+
await screen.findByRole('dialog');
|
|
1516
|
+
const requiredField = await screen.findByRole('textbox', { name: /Name */i });
|
|
1517
|
+
await user.type(requiredField, 'Some content here...');
|
|
1518
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1519
|
+
await user.click(submitButton);
|
|
1520
|
+
expect(scrollIntoViewMock).not.toHaveBeenCalled();
|
|
1521
|
+
});
|
|
1522
|
+
});
|
|
1523
|
+
describe('when passed a text field entry', () => {
|
|
1524
|
+
it('should render text field', async () => {
|
|
1525
|
+
const form = {
|
|
1526
|
+
id: 'textFieldTestForm',
|
|
1527
|
+
name: 'Text Field Test Form',
|
|
1528
|
+
entries: [
|
|
1529
|
+
{
|
|
1530
|
+
type: 'inputField',
|
|
1531
|
+
input: {
|
|
1532
|
+
id: 'textField',
|
|
1533
|
+
type: 'string',
|
|
1534
|
+
},
|
|
1535
|
+
display: {
|
|
1536
|
+
label: 'Text Field',
|
|
1537
|
+
},
|
|
1538
|
+
},
|
|
1539
|
+
],
|
|
1540
|
+
actionId: '_update',
|
|
1541
|
+
objectId: 'textFieldTestObject',
|
|
1542
|
+
};
|
|
1543
|
+
const textFieldTestObject = {
|
|
1544
|
+
id: 'textFieldTestObject',
|
|
1545
|
+
name: 'Text Field Test Object',
|
|
1546
|
+
actions: [
|
|
1547
|
+
{
|
|
1548
|
+
id: '_update',
|
|
1549
|
+
name: 'Update',
|
|
1550
|
+
type: 'update',
|
|
1551
|
+
outputEvent: 'updated',
|
|
1552
|
+
},
|
|
1553
|
+
],
|
|
1554
|
+
properties: [],
|
|
1555
|
+
};
|
|
1556
|
+
server.use(http.get(`/api/data/objects/${textFieldTestObject.id}/effective`, () => HttpResponse.json(textFieldTestObject)));
|
|
1557
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1558
|
+
await screen.findByRole('textbox', { name: 'Text Field' });
|
|
1559
|
+
});
|
|
1560
|
+
it('should allow text input in text field', async () => {
|
|
1561
|
+
const user = userEvent.setup();
|
|
1562
|
+
const form = {
|
|
1563
|
+
id: 'textFieldTestForm',
|
|
1564
|
+
name: 'Text Field Test Form',
|
|
1565
|
+
entries: [
|
|
1566
|
+
{
|
|
1567
|
+
type: 'inputField',
|
|
1568
|
+
input: {
|
|
1569
|
+
id: 'textField',
|
|
1570
|
+
type: 'string',
|
|
1571
|
+
},
|
|
1572
|
+
display: {
|
|
1573
|
+
label: 'Text Field',
|
|
1574
|
+
},
|
|
1575
|
+
},
|
|
1576
|
+
],
|
|
1577
|
+
actionId: '_update',
|
|
1578
|
+
objectId: 'textFieldTestObject',
|
|
1579
|
+
};
|
|
1580
|
+
const textFieldTestObject = {
|
|
1581
|
+
id: 'textFieldTestObject',
|
|
1582
|
+
name: 'Text Field Test Object',
|
|
1583
|
+
actions: [
|
|
1584
|
+
{
|
|
1585
|
+
id: '_update',
|
|
1586
|
+
name: 'Update',
|
|
1587
|
+
type: 'update',
|
|
1588
|
+
outputEvent: 'updated',
|
|
1589
|
+
},
|
|
1590
|
+
],
|
|
1591
|
+
properties: [],
|
|
1592
|
+
};
|
|
1593
|
+
server.use(http.get(`/api/data/objects/${textFieldTestObject.id}/effective`, () => HttpResponse.json(textFieldTestObject)));
|
|
1594
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1595
|
+
const textField = await screen.findByRole('textbox', { name: 'Text Field' });
|
|
1596
|
+
await user.type(textField, 'Test Input');
|
|
1597
|
+
expect(textField).toHaveValue('Test Input');
|
|
1598
|
+
});
|
|
1232
1599
|
});
|
|
1233
1600
|
});
|