@evoke-platform/ui-components 1.10.0-dev.3 → 1.10.0-dev.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.js +1 -1
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.d.ts +1 -0
- package/dist/published/components/custom/CriteriaBuilder/CriteriaBuilder.test.js +430 -0
- package/dist/published/components/custom/CriteriaBuilder/ValueEditor.js +19 -6
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableField.js +1 -1
- package/dist/published/components/custom/Form/utils.js +1 -0
- package/dist/published/components/custom/FormField/DatePickerSelect/DatePickerSelect.js +14 -1
- package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js +14 -1
- 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 +19 -7
- package/dist/published/components/custom/FormV2/FormRendererContainer.js +117 -74
- 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/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.d.ts +0 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +36 -49
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +16 -3
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +16 -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 +109 -81
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +38 -16
- 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/RecursiveEntryRenderer.js +46 -35
- package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrors.js +1 -1
- package/dist/published/components/custom/FormV2/components/types.d.ts +1 -0
- package/dist/published/components/custom/FormV2/components/utils.d.ts +4 -4
- package/dist/published/components/custom/FormV2/components/utils.js +13 -16
- package/dist/published/components/custom/FormV2/tests/FormRenderer.test.js +231 -8
- package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +662 -13
- package/dist/published/components/custom/FormV2/tests/test-data.d.ts +1 -0
- package/dist/published/components/custom/FormV2/tests/test-data.js +140 -0
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.d.ts +3 -0
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +155 -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/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 +29 -44
- 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/package.json +4 -2
|
@@ -10,7 +10,7 @@ export declare function isAddressProperty(key: string): boolean;
|
|
|
10
10
|
/**
|
|
11
11
|
* Determine if a form entry is visible or not.
|
|
12
12
|
*/
|
|
13
|
-
export declare const entryIsVisible: (entry: FormEntry,
|
|
13
|
+
export declare const entryIsVisible: (entry: FormEntry, instance?: FieldValues, formValues?: FieldValues) => boolean;
|
|
14
14
|
/**
|
|
15
15
|
* Recursively retrieves all parameter IDs from a given entry of type Sections or Columns.
|
|
16
16
|
*
|
|
@@ -86,8 +86,8 @@ export declare function formatSubmission(submission: FieldValues, apiServices?:
|
|
|
86
86
|
message?: string;
|
|
87
87
|
isError: boolean;
|
|
88
88
|
}>>): Promise<FieldValues>;
|
|
89
|
-
export declare function filterEmptySections(entry: Sections | Columns,
|
|
90
|
-
export declare function assignIdsToSectionsAndRichText(entries: FormEntry[], object
|
|
89
|
+
export declare function filterEmptySections(entry: Sections | Columns, instance?: FieldValues, formData?: FieldValues): Sections | Columns | null;
|
|
90
|
+
export declare function assignIdsToSectionsAndRichText(entries: FormEntry[], object: Obj, parameters?: InputParameter[]): FormEntry[];
|
|
91
91
|
/**
|
|
92
92
|
* Converts a plain text string to RTF format suitable for a RichTextEditor.
|
|
93
93
|
*
|
|
@@ -101,4 +101,4 @@ export declare function assignIdsToSectionsAndRichText(entries: FormEntry[], obj
|
|
|
101
101
|
* This ensures that any plain text input will be safely represented in RTF without losing formatting or characters.
|
|
102
102
|
*/
|
|
103
103
|
export declare function plainTextToRtf(plainText: string): string;
|
|
104
|
-
export declare function getFieldDefinition(entry: FormEntry, object
|
|
104
|
+
export declare function getFieldDefinition(entry: FormEntry, object: Obj, parameters?: InputParameter[], isDocument?: boolean): InputParameter | Property | undefined;
|
|
@@ -68,7 +68,7 @@ export function isAddressProperty(key) {
|
|
|
68
68
|
/**
|
|
69
69
|
* Determine if a form entry is visible or not.
|
|
70
70
|
*/
|
|
71
|
-
export const entryIsVisible = (entry,
|
|
71
|
+
export const entryIsVisible = (entry, instance, formValues) => {
|
|
72
72
|
const display = 'display' in entry ? entry.display : undefined;
|
|
73
73
|
const { visibility } = display ?? ('visibility' in entry ? entry : {});
|
|
74
74
|
if (isObject(visibility) && 'conditions' in visibility && isArray(visibility.conditions)) {
|
|
@@ -482,11 +482,6 @@ export function convertDocToEntries(document) {
|
|
|
482
482
|
sortBy: 'ASC',
|
|
483
483
|
},
|
|
484
484
|
},
|
|
485
|
-
enumWithLabels: [
|
|
486
|
-
{ label: 'Public', value: 'Public' },
|
|
487
|
-
{ label: 'Private', value: 'Private' },
|
|
488
|
-
{ label: 'Portal', value: 'Portal' },
|
|
489
|
-
],
|
|
490
485
|
});
|
|
491
486
|
}
|
|
492
487
|
entries.push({
|
|
@@ -507,8 +502,8 @@ export function formatDataToDoc(data) {
|
|
|
507
502
|
uploadedDate: data.uploadedDate,
|
|
508
503
|
versionId: data.versionId,
|
|
509
504
|
metadata: {
|
|
510
|
-
type: data.type,
|
|
511
|
-
view_permission: data.view_permission,
|
|
505
|
+
type: data.type ?? '',
|
|
506
|
+
view_permission: data.view_permission ?? '',
|
|
512
507
|
},
|
|
513
508
|
};
|
|
514
509
|
}
|
|
@@ -567,6 +562,7 @@ export const uploadDocuments = async (files, metadata, apiServices, instanceId,
|
|
|
567
562
|
const allDocuments = [];
|
|
568
563
|
const formData = new FormData();
|
|
569
564
|
for (const [index, file] of files.entries()) {
|
|
565
|
+
// Only upload File instances; SavedDocumentReference objects are already uploaded
|
|
570
566
|
if ('size' in file) {
|
|
571
567
|
formData.append(`files[${index}]`, file);
|
|
572
568
|
}
|
|
@@ -628,6 +624,7 @@ export const deleteDocuments = async (submittedFields, requestSuccess, apiServic
|
|
|
628
624
|
export async function formatSubmission(submission, apiServices, objectId, instanceId, form, setSnackbarError) {
|
|
629
625
|
for (const [key, value] of Object.entries(submission)) {
|
|
630
626
|
if (isArray(value)) {
|
|
627
|
+
// Only upload if array contains File instances (not SavedDocumentReference)
|
|
631
628
|
const fileInArray = value.some((item) => item instanceof File);
|
|
632
629
|
if (fileInArray && instanceId && apiServices && objectId) {
|
|
633
630
|
try {
|
|
@@ -672,21 +669,21 @@ export async function formatSubmission(submission, apiServices, objectId, instan
|
|
|
672
669
|
}
|
|
673
670
|
return submission;
|
|
674
671
|
}
|
|
675
|
-
export function filterEmptySections(entry,
|
|
672
|
+
export function filterEmptySections(entry, instance, formData) {
|
|
676
673
|
if (entry.type === 'sections' && isArray(entry.sections)) {
|
|
677
674
|
const visibleSections = entry.sections.filter((section) => {
|
|
678
675
|
if (!section.entries || section.entries.length === 0)
|
|
679
676
|
return false;
|
|
680
677
|
for (const sectionEntry of section.entries) {
|
|
681
678
|
if (sectionEntry.type === 'sections' || sectionEntry.type === 'columns') {
|
|
682
|
-
if (sectionEntry.visibility && !entryIsVisible(sectionEntry,
|
|
679
|
+
if (sectionEntry.visibility && !entryIsVisible(sectionEntry, instance, formData)) {
|
|
683
680
|
return false;
|
|
684
681
|
}
|
|
685
|
-
else if (filterEmptySections(sectionEntry,
|
|
682
|
+
else if (filterEmptySections(sectionEntry, instance, formData)) {
|
|
686
683
|
return true;
|
|
687
684
|
}
|
|
688
685
|
}
|
|
689
|
-
else if (entryIsVisible(sectionEntry,
|
|
686
|
+
else if (entryIsVisible(sectionEntry, instance, formData)) {
|
|
690
687
|
return true;
|
|
691
688
|
}
|
|
692
689
|
}
|
|
@@ -706,13 +703,13 @@ export function filterEmptySections(entry, formData, instance) {
|
|
|
706
703
|
let hasVisibleEntry = false;
|
|
707
704
|
for (const columnEntry of column.entries) {
|
|
708
705
|
if (columnEntry.type === 'sections' || columnEntry.type === 'columns') {
|
|
709
|
-
if (filterEmptySections(columnEntry,
|
|
706
|
+
if (filterEmptySections(columnEntry, instance, formData)) {
|
|
710
707
|
hasVisibleEntry = true;
|
|
711
708
|
break;
|
|
712
709
|
}
|
|
713
710
|
}
|
|
714
711
|
else {
|
|
715
|
-
if (entryIsVisible(columnEntry,
|
|
712
|
+
if (entryIsVisible(columnEntry, instance, formData)) {
|
|
716
713
|
hasVisibleEntry = true;
|
|
717
714
|
break;
|
|
718
715
|
}
|
|
@@ -813,8 +810,8 @@ export function getFieldDefinition(entry, object, parameters, isDocument) {
|
|
|
813
810
|
def = isDocument
|
|
814
811
|
? docProperties.find((prop) => prop.id === entry.propertyId)
|
|
815
812
|
: isAddressProperty(entry.propertyId)
|
|
816
|
-
? object
|
|
817
|
-
: object
|
|
813
|
+
? object.properties?.find((prop) => prop.id === entry.propertyId.split('.')[0])
|
|
814
|
+
: object.properties?.find((prop) => prop.id === entry.propertyId);
|
|
818
815
|
}
|
|
819
816
|
else if (entry.type === 'inputField') {
|
|
820
817
|
def = entry.input;
|
|
@@ -20,7 +20,7 @@ const WithProviders = ({ children }) => {
|
|
|
20
20
|
return React.createElement(MemoryRouter, null, children);
|
|
21
21
|
};
|
|
22
22
|
const render = (ui, options) => baseRender(ui, { wrapper: WithProviders, ...options });
|
|
23
|
-
describe('
|
|
23
|
+
describe('FormRenderer', () => {
|
|
24
24
|
let server;
|
|
25
25
|
beforeAll(() => {
|
|
26
26
|
server = setupServer(http.get('/api/data/objects/specialtyType/effective', () => HttpResponse.json(specialtyTypeObject)), http.get('/api/data/objects/specialtyType/effective', (req) => {
|
|
@@ -59,9 +59,9 @@ describe('Form component', () => {
|
|
|
59
59
|
npSpecialtyType1,
|
|
60
60
|
npSpecialtyType2,
|
|
61
61
|
]);
|
|
62
|
-
else if (isEqual(whereFilter, {
|
|
62
|
+
else if (isEqual(whereFilter, { 'licenseType.id': 'rnLicenseType' }))
|
|
63
63
|
return HttpResponse.json([rnSpecialtyType1, rnSpecialtyType2]);
|
|
64
|
-
else if (isEqual(whereFilter, {
|
|
64
|
+
else if (isEqual(whereFilter, { 'licenseType.id': 'npLicenseType' }))
|
|
65
65
|
return HttpResponse.json([npSpecialtyType1, npSpecialtyType2]);
|
|
66
66
|
}
|
|
67
67
|
}), http.get('/api/accessManagement/users', () => HttpResponse.json(users)));
|
|
@@ -650,13 +650,19 @@ describe('Form component', () => {
|
|
|
650
650
|
parameterId: 'specialtyType',
|
|
651
651
|
display: {
|
|
652
652
|
label: 'Speciality Type',
|
|
653
|
+
createActionId: '_create',
|
|
653
654
|
},
|
|
654
655
|
},
|
|
655
656
|
],
|
|
656
657
|
actionId: '_update',
|
|
657
658
|
objectId: 'relatedObjectTestForm',
|
|
658
659
|
};
|
|
660
|
+
let scrollIntoViewMock;
|
|
661
|
+
let originalScrollIntoView;
|
|
659
662
|
beforeEach(async () => {
|
|
663
|
+
scrollIntoViewMock = vitest.fn();
|
|
664
|
+
originalScrollIntoView = Element.prototype.scrollIntoView;
|
|
665
|
+
Element.prototype.scrollIntoView = scrollIntoViewMock;
|
|
660
666
|
const relatedObjectTestFormObject = {
|
|
661
667
|
id: 'relatedObjectTestForm',
|
|
662
668
|
name: 'Related Object Test Form',
|
|
@@ -693,9 +699,20 @@ describe('Form component', () => {
|
|
|
693
699
|
type: 'content',
|
|
694
700
|
html: '<div>Specialty Type Form Content</div>',
|
|
695
701
|
},
|
|
702
|
+
{
|
|
703
|
+
type: 'input',
|
|
704
|
+
parameterId: 'requiredField',
|
|
705
|
+
display: {
|
|
706
|
+
label: 'Required Field',
|
|
707
|
+
required: true,
|
|
708
|
+
},
|
|
709
|
+
},
|
|
696
710
|
],
|
|
697
711
|
actionId: '_create',
|
|
698
712
|
objectId: 'specialtyType',
|
|
713
|
+
display: {
|
|
714
|
+
submitLabel: 'Create Specialty Type',
|
|
715
|
+
},
|
|
699
716
|
};
|
|
700
717
|
const specialtyTypeObject = {
|
|
701
718
|
id: 'specialtyType',
|
|
@@ -705,7 +722,14 @@ describe('Form component', () => {
|
|
|
705
722
|
id: '_create',
|
|
706
723
|
name: 'Create',
|
|
707
724
|
type: 'create',
|
|
708
|
-
parameters: [
|
|
725
|
+
parameters: [
|
|
726
|
+
{
|
|
727
|
+
id: 'requiredField',
|
|
728
|
+
name: 'Required Field',
|
|
729
|
+
type: 'string',
|
|
730
|
+
required: true,
|
|
731
|
+
},
|
|
732
|
+
],
|
|
709
733
|
outputEvent: 'created',
|
|
710
734
|
defaultFormId: 'specialtyTypeForm',
|
|
711
735
|
},
|
|
@@ -721,6 +745,9 @@ describe('Form component', () => {
|
|
|
721
745
|
};
|
|
722
746
|
setupTestMocks(specialtyTypeObject, specialtyTypeForm);
|
|
723
747
|
});
|
|
748
|
+
afterEach(() => {
|
|
749
|
+
Element.prototype.scrollIntoView = originalScrollIntoView;
|
|
750
|
+
});
|
|
724
751
|
it('displays an add button for related object fields', async () => {
|
|
725
752
|
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
726
753
|
await screen.findByRole('button', { name: 'Add' });
|
|
@@ -813,6 +840,63 @@ describe('Form component', () => {
|
|
|
813
840
|
await user.click(newRecordButton);
|
|
814
841
|
await screen.findByText(/not found/i);
|
|
815
842
|
});
|
|
843
|
+
it('should show validation errors in record creation mode', async () => {
|
|
844
|
+
const user = userEvent.setup();
|
|
845
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
846
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
847
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
848
|
+
await user.click(newRecordButton);
|
|
849
|
+
const createSpecialtyTypeButton = await screen.findByRole('button', {
|
|
850
|
+
name: /create specialty type/i,
|
|
851
|
+
});
|
|
852
|
+
await user.click(createSpecialtyTypeButton);
|
|
853
|
+
const errorMessage = await screen.findByRole('listitem');
|
|
854
|
+
expect(errorMessage).toHaveTextContent('Required Field is required');
|
|
855
|
+
});
|
|
856
|
+
it('should clear validation errors after they have been resolved', async () => {
|
|
857
|
+
const user = userEvent.setup();
|
|
858
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
859
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
860
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
861
|
+
await user.click(newRecordButton);
|
|
862
|
+
const createSpecialtyTypeButton = await screen.findByRole('button', {
|
|
863
|
+
name: /create specialty type/i,
|
|
864
|
+
});
|
|
865
|
+
await user.click(createSpecialtyTypeButton);
|
|
866
|
+
// Make sure error elements appear
|
|
867
|
+
screen.getByRole('listitem');
|
|
868
|
+
const requiredField = screen.getByRole('textbox', { name: /Required Field */i });
|
|
869
|
+
await user.type(requiredField, 'Some content here...');
|
|
870
|
+
expect(screen.queryByRole('listitem')).not.toBeInTheDocument();
|
|
871
|
+
});
|
|
872
|
+
it('should scroll to validation errors after submission', async () => {
|
|
873
|
+
const user = userEvent.setup();
|
|
874
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
875
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
876
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
877
|
+
await user.click(newRecordButton);
|
|
878
|
+
const createSpecialtyTypeButton = await screen.findByRole('button', {
|
|
879
|
+
name: /create specialty type/i,
|
|
880
|
+
});
|
|
881
|
+
await user.click(createSpecialtyTypeButton);
|
|
882
|
+
expect(scrollIntoViewMock).toHaveBeenCalled();
|
|
883
|
+
});
|
|
884
|
+
it('should not scroll to validation errors if there are none', async () => async () => {
|
|
885
|
+
const user = userEvent.setup();
|
|
886
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
887
|
+
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
888
|
+
const newRecordButton = await screen.findByRole('radio', { name: /new/i });
|
|
889
|
+
await user.click(newRecordButton);
|
|
890
|
+
// Make sure error elements appear
|
|
891
|
+
screen.getByRole('listitem');
|
|
892
|
+
const requiredField = await screen.findByRole('textbox', { name: /Required Field */i });
|
|
893
|
+
await user.type(requiredField, 'Some content here...');
|
|
894
|
+
const createSpecialtyTypeButton = await screen.findByRole('button', {
|
|
895
|
+
name: /create specialty type/i,
|
|
896
|
+
});
|
|
897
|
+
await user.click(createSpecialtyTypeButton);
|
|
898
|
+
expect(scrollIntoViewMock).not.toHaveBeenCalled();
|
|
899
|
+
});
|
|
816
900
|
});
|
|
817
901
|
});
|
|
818
902
|
describe('when in dropdown view', () => {
|
|
@@ -922,6 +1006,7 @@ describe('Form component', () => {
|
|
|
922
1006
|
display: {
|
|
923
1007
|
label: 'Speciality Type',
|
|
924
1008
|
relatedObjectDisplay: 'dropdown',
|
|
1009
|
+
createActionId: '_create',
|
|
925
1010
|
},
|
|
926
1011
|
},
|
|
927
1012
|
],
|
|
@@ -1041,13 +1126,19 @@ describe('Form component', () => {
|
|
|
1041
1126
|
},
|
|
1042
1127
|
display: {
|
|
1043
1128
|
label: 'Collection',
|
|
1129
|
+
createActionId: '_create',
|
|
1044
1130
|
},
|
|
1045
1131
|
},
|
|
1046
1132
|
],
|
|
1047
1133
|
actionId: '_update',
|
|
1048
1134
|
objectId: 'testObjectForCollections',
|
|
1049
1135
|
};
|
|
1136
|
+
let scrollIntoViewMock;
|
|
1137
|
+
let originalScrollIntoView;
|
|
1050
1138
|
beforeEach(() => {
|
|
1139
|
+
scrollIntoViewMock = vitest.fn();
|
|
1140
|
+
originalScrollIntoView = Element.prototype.scrollIntoView;
|
|
1141
|
+
Element.prototype.scrollIntoView = scrollIntoViewMock;
|
|
1051
1142
|
const collectionFormObject = {
|
|
1052
1143
|
id: 'testObjectForCollections',
|
|
1053
1144
|
name: 'Object for one-to-many collections tests',
|
|
@@ -1105,6 +1196,7 @@ describe('Form component', () => {
|
|
|
1105
1196
|
objectId: 'testObjectForCollections',
|
|
1106
1197
|
},
|
|
1107
1198
|
],
|
|
1199
|
+
defaultFormId: 'collectionObjectForm',
|
|
1108
1200
|
},
|
|
1109
1201
|
],
|
|
1110
1202
|
};
|
|
@@ -1117,6 +1209,7 @@ describe('Form component', () => {
|
|
|
1117
1209
|
parameterId: 'name',
|
|
1118
1210
|
display: {
|
|
1119
1211
|
label: 'Name',
|
|
1212
|
+
required: true,
|
|
1120
1213
|
},
|
|
1121
1214
|
},
|
|
1122
1215
|
{
|
|
@@ -1130,9 +1223,15 @@ describe('Form component', () => {
|
|
|
1130
1223
|
],
|
|
1131
1224
|
actionId: '_create',
|
|
1132
1225
|
objectId: 'collectionObject',
|
|
1226
|
+
display: {
|
|
1227
|
+
submitLabel: 'Create Collection Item',
|
|
1228
|
+
},
|
|
1133
1229
|
};
|
|
1134
1230
|
setupTestMocks(collectionObject, collectionObjectForm);
|
|
1135
1231
|
});
|
|
1232
|
+
afterEach(() => {
|
|
1233
|
+
Element.prototype.scrollIntoView = originalScrollIntoView;
|
|
1234
|
+
});
|
|
1136
1235
|
it('should render collection field', async () => {
|
|
1137
1236
|
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1138
1237
|
await screen.findByText('Collection');
|
|
@@ -1191,7 +1290,7 @@ describe('Form component', () => {
|
|
|
1191
1290
|
const user = userEvent.setup();
|
|
1192
1291
|
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1193
1292
|
await user.click(await screen.findByRole('button', { name: 'Add' }));
|
|
1194
|
-
await screen.findByRole('button', { name: '
|
|
1293
|
+
await screen.findByRole('button', { name: 'Create Collection Item' });
|
|
1195
1294
|
});
|
|
1196
1295
|
it('should hide related object field in collection item form', async () => {
|
|
1197
1296
|
const user = userEvent.setup();
|
|
@@ -1200,7 +1299,7 @@ describe('Form component', () => {
|
|
|
1200
1299
|
await user.click(addButton);
|
|
1201
1300
|
await screen.findByRole('dialog');
|
|
1202
1301
|
// Make sure other form entry is present
|
|
1203
|
-
await screen.findByRole('textbox', { name: 'Name' });
|
|
1302
|
+
await screen.findByRole('textbox', { name: 'Name *' });
|
|
1204
1303
|
const relatedObjectField = screen.queryByRole('textbox', { name: 'Related Object' });
|
|
1205
1304
|
expect(relatedObjectField).not.toBeInTheDocument();
|
|
1206
1305
|
});
|
|
@@ -1219,12 +1318,136 @@ describe('Form component', () => {
|
|
|
1219
1318
|
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1220
1319
|
await user.click(addButton);
|
|
1221
1320
|
await screen.findByRole('dialog');
|
|
1222
|
-
const nameField = screen.getByRole('textbox', { name: 'Name' });
|
|
1321
|
+
const nameField = screen.getByRole('textbox', { name: 'Name *' });
|
|
1223
1322
|
await user.type(nameField, 'New Collection Item');
|
|
1224
|
-
const submitButton = screen.getByRole('button', { name: '
|
|
1323
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1225
1324
|
await user.click(submitButton);
|
|
1226
1325
|
await screen.findByRole('columnheader', { name: 'Name' });
|
|
1227
1326
|
screen.getByRole('cell', { name: 'New Collection Item' });
|
|
1228
1327
|
});
|
|
1328
|
+
it('should show validation errors', async () => {
|
|
1329
|
+
const user = userEvent.setup();
|
|
1330
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1331
|
+
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1332
|
+
await user.click(addButton);
|
|
1333
|
+
await screen.findByRole('dialog');
|
|
1334
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1335
|
+
await user.click(submitButton);
|
|
1336
|
+
const errorMessage = await screen.findByRole('listitem');
|
|
1337
|
+
expect(errorMessage).toHaveTextContent('Name is required');
|
|
1338
|
+
});
|
|
1339
|
+
it('should hide validation errors after they have been resolved', async () => {
|
|
1340
|
+
const user = userEvent.setup();
|
|
1341
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1342
|
+
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1343
|
+
await user.click(addButton);
|
|
1344
|
+
await screen.findByRole('dialog');
|
|
1345
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1346
|
+
await user.click(submitButton);
|
|
1347
|
+
// Make sure error elements appear
|
|
1348
|
+
screen.getByRole('listitem');
|
|
1349
|
+
const requiredField = screen.getByRole('textbox', { name: /Name */i });
|
|
1350
|
+
await user.type(requiredField, 'Some content here...');
|
|
1351
|
+
expect(screen.queryByRole('listitem')).not.toBeInTheDocument();
|
|
1352
|
+
});
|
|
1353
|
+
it('should scroll to validation errors on submit', async () => {
|
|
1354
|
+
const user = userEvent.setup();
|
|
1355
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1356
|
+
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1357
|
+
await user.click(addButton);
|
|
1358
|
+
await screen.findByRole('dialog');
|
|
1359
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1360
|
+
await user.click(submitButton);
|
|
1361
|
+
expect(scrollIntoViewMock).toHaveBeenCalled();
|
|
1362
|
+
});
|
|
1363
|
+
it('should not scroll to validation errors if there are none', async () => async () => {
|
|
1364
|
+
const user = userEvent.setup();
|
|
1365
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1366
|
+
const addButton = await screen.findByRole('button', { name: /add/i });
|
|
1367
|
+
await user.click(addButton);
|
|
1368
|
+
await screen.findByRole('dialog');
|
|
1369
|
+
const requiredField = await screen.findByRole('textbox', { name: /Name */i });
|
|
1370
|
+
await user.type(requiredField, 'Some content here...');
|
|
1371
|
+
const submitButton = screen.getByRole('button', { name: 'Create Collection Item' });
|
|
1372
|
+
await user.click(submitButton);
|
|
1373
|
+
expect(scrollIntoViewMock).not.toHaveBeenCalled();
|
|
1374
|
+
});
|
|
1375
|
+
});
|
|
1376
|
+
describe('when passed a text field entry', () => {
|
|
1377
|
+
it('should render text field', async () => {
|
|
1378
|
+
const form = {
|
|
1379
|
+
id: 'textFieldTestForm',
|
|
1380
|
+
name: 'Text Field Test Form',
|
|
1381
|
+
entries: [
|
|
1382
|
+
{
|
|
1383
|
+
type: 'inputField',
|
|
1384
|
+
input: {
|
|
1385
|
+
id: 'textField',
|
|
1386
|
+
type: 'string',
|
|
1387
|
+
},
|
|
1388
|
+
display: {
|
|
1389
|
+
label: 'Text Field',
|
|
1390
|
+
},
|
|
1391
|
+
},
|
|
1392
|
+
],
|
|
1393
|
+
actionId: '_update',
|
|
1394
|
+
objectId: 'textFieldTestObject',
|
|
1395
|
+
};
|
|
1396
|
+
const textFieldTestObject = {
|
|
1397
|
+
id: 'textFieldTestObject',
|
|
1398
|
+
name: 'Text Field Test Object',
|
|
1399
|
+
actions: [
|
|
1400
|
+
{
|
|
1401
|
+
id: '_update',
|
|
1402
|
+
name: 'Update',
|
|
1403
|
+
type: 'update',
|
|
1404
|
+
outputEvent: 'updated',
|
|
1405
|
+
},
|
|
1406
|
+
],
|
|
1407
|
+
properties: [],
|
|
1408
|
+
};
|
|
1409
|
+
server.use(http.get(`/api/data/objects/${textFieldTestObject.id}/effective`, () => HttpResponse.json(textFieldTestObject)));
|
|
1410
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1411
|
+
await screen.findByRole('textbox', { name: 'Text Field' });
|
|
1412
|
+
});
|
|
1413
|
+
it('should allow text input in text field', async () => {
|
|
1414
|
+
const user = userEvent.setup();
|
|
1415
|
+
const form = {
|
|
1416
|
+
id: 'textFieldTestForm',
|
|
1417
|
+
name: 'Text Field Test Form',
|
|
1418
|
+
entries: [
|
|
1419
|
+
{
|
|
1420
|
+
type: 'inputField',
|
|
1421
|
+
input: {
|
|
1422
|
+
id: 'textField',
|
|
1423
|
+
type: 'string',
|
|
1424
|
+
},
|
|
1425
|
+
display: {
|
|
1426
|
+
label: 'Text Field',
|
|
1427
|
+
},
|
|
1428
|
+
},
|
|
1429
|
+
],
|
|
1430
|
+
actionId: '_update',
|
|
1431
|
+
objectId: 'textFieldTestObject',
|
|
1432
|
+
};
|
|
1433
|
+
const textFieldTestObject = {
|
|
1434
|
+
id: 'textFieldTestObject',
|
|
1435
|
+
name: 'Text Field Test Object',
|
|
1436
|
+
actions: [
|
|
1437
|
+
{
|
|
1438
|
+
id: '_update',
|
|
1439
|
+
name: 'Update',
|
|
1440
|
+
type: 'update',
|
|
1441
|
+
outputEvent: 'updated',
|
|
1442
|
+
},
|
|
1443
|
+
],
|
|
1444
|
+
properties: [],
|
|
1445
|
+
};
|
|
1446
|
+
server.use(http.get(`/api/data/objects/${textFieldTestObject.id}/effective`, () => HttpResponse.json(textFieldTestObject)));
|
|
1447
|
+
render(React.createElement(FormRenderer, { form: form, onChange: () => { } }));
|
|
1448
|
+
const textField = await screen.findByRole('textbox', { name: 'Text Field' });
|
|
1449
|
+
await user.type(textField, 'Test Input');
|
|
1450
|
+
expect(textField).toHaveValue('Test Input');
|
|
1451
|
+
});
|
|
1229
1452
|
});
|
|
1230
1453
|
});
|