@kenyaemr/esm-patient-registration-app 4.5.1 → 4.5.3

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 (77) hide show
  1. package/dist/130.js +2 -0
  2. package/dist/{858.js.LICENSE.txt → 130.js.LICENSE.txt} +2 -0
  3. package/dist/130.js.map +1 -0
  4. package/dist/196.js +1 -0
  5. package/dist/196.js.map +1 -0
  6. package/dist/208.js +2 -0
  7. package/dist/208.js.map +1 -0
  8. package/dist/317.js +1 -1
  9. package/dist/317.js.map +1 -1
  10. package/dist/319.js +1 -0
  11. package/dist/330.js +1 -1
  12. package/dist/537.js +1 -0
  13. package/dist/537.js.map +1 -0
  14. package/dist/59.js +1 -1
  15. package/dist/59.js.map +1 -1
  16. package/dist/591.js +1 -1
  17. package/dist/591.js.map +1 -1
  18. package/dist/62.js +1 -1
  19. package/dist/62.js.map +1 -1
  20. package/dist/635.js +1 -1
  21. package/dist/635.js.map +1 -1
  22. package/dist/68.js +1 -1
  23. package/dist/68.js.map +1 -1
  24. package/dist/735.js +1 -1
  25. package/dist/742.js +1 -0
  26. package/dist/742.js.map +1 -0
  27. package/dist/757.js +1 -1
  28. package/dist/784.js.map +1 -1
  29. package/dist/788.js +1 -0
  30. package/dist/807.js +1 -1
  31. package/dist/821.js +1 -1
  32. package/dist/821.js.map +1 -1
  33. package/dist/833.js +1 -1
  34. package/dist/857.js +1 -0
  35. package/dist/857.js.map +1 -0
  36. package/dist/9.js +1 -1
  37. package/dist/9.js.map +1 -1
  38. package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
  39. package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +154 -110
  40. package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
  41. package/dist/main.js +1 -1
  42. package/dist/main.js.map +1 -1
  43. package/dist/routes.json +1 -0
  44. package/package.json +2 -2
  45. package/src/add-patient-link.test.tsx +18 -0
  46. package/src/config-schema.ts +19 -3
  47. package/src/index.ts +36 -105
  48. package/src/nav-link.test.tsx +13 -0
  49. package/src/patient-registration/field/address/address-field.component.tsx +1 -1
  50. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +1 -1
  51. package/src/patient-registration/field/field.test.tsx +291 -0
  52. package/src/patient-registration/field/name/name-field.component.tsx +45 -28
  53. package/src/patient-registration/form-manager.ts +1 -0
  54. package/src/patient-registration/input/combo-input/combo-input.component.tsx +31 -28
  55. package/src/patient-registration/input/input.scss +5 -0
  56. package/src/patient-registration/section/death-info/death-info-section.test.tsx +9 -6
  57. package/src/root.component.tsx +11 -11
  58. package/src/routes.json +56 -0
  59. package/translations/am.json +91 -0
  60. package/translations/es.json +91 -0
  61. package/translations/fr.json +8 -6
  62. package/translations/he.json +8 -6
  63. package/translations/km.json +8 -6
  64. package/dist/144.js +0 -2
  65. package/dist/144.js.map +0 -1
  66. package/dist/207.js +0 -1
  67. package/dist/207.js.map +0 -1
  68. package/dist/805.js +0 -1
  69. package/dist/805.js.map +0 -1
  70. package/dist/858.js +0 -2
  71. package/dist/858.js.map +0 -1
  72. package/dist/876.js +0 -1
  73. package/dist/876.js.map +0 -1
  74. package/dist/887.js +0 -1
  75. package/dist/887.js.map +0 -1
  76. package/dist/kenyaemr-esm-patient-registration-app.old +0 -1
  77. /package/dist/{144.js.LICENSE.txt → 208.js.LICENSE.txt} +0 -0
@@ -41,6 +41,7 @@ export interface RegistrationConfig {
41
41
  defaultUnknownGivenName: string;
42
42
  defaultUnknownFamilyName: string;
43
43
  displayCapturePhoto: boolean;
44
+ displayReverseFieldOrder: boolean;
44
45
  };
45
46
  gender: Array<Gender>;
46
47
  address: {
@@ -78,12 +79,12 @@ export const builtInSections: Array<SectionDefinition> = [
78
79
  name: 'Basic Info',
79
80
  fields: ['name', 'gender', 'dob', 'id'],
80
81
  },
81
- { id: 'contact', name: 'Contact Details', fields: ['address'] },
82
+ { id: 'contact', name: 'Contact Details', fields: ['address', 'phone'] },
82
83
  { id: 'death', name: 'Death Info', fields: [] },
83
84
  { id: 'relationships', name: 'Relationships', fields: [] },
84
85
  ];
85
86
 
86
- export const builtInFields = ['name', 'gender', 'dob', 'address', 'id'] as const;
87
+ export const builtInFields = ['name', 'gender', 'dob', 'address', 'id', 'phone & email'] as const;
87
88
 
88
89
  export const esmPatientRegistrationSchema = {
89
90
  sections: {
@@ -183,7 +184,17 @@ export const esmPatientRegistrationSchema = {
183
184
  _description: 'For coded questions only. Provide ability to add custom concept answers.',
184
185
  },
185
186
  },
186
- _default: [],
187
+ _default: [
188
+ {
189
+ id: 'phone',
190
+ type: 'person attribute',
191
+ uuid: '14d4f066-15f5-102d-96e4-000c29c2a5d7',
192
+ showHeading: false,
193
+ validation: {
194
+ matches: '',
195
+ },
196
+ },
197
+ ],
187
198
  _description:
188
199
  'Definitions for custom fields that can be used in sectionDefinitions. Can also be used to override built-in fields.',
189
200
  },
@@ -210,6 +221,11 @@ export const esmPatientRegistrationSchema = {
210
221
  _default: true,
211
222
  _description: 'Whether to display capture patient photo slot on name field',
212
223
  },
224
+ displayReverseFieldOrder: {
225
+ _type: Type.Boolean,
226
+ _default: false,
227
+ _description: "Whether to display the name fields in the order 'Family name' -> 'Middle name' -> 'First name'",
228
+ },
213
229
  },
214
230
  gender: {
215
231
  _type: Type.Array,
package/src/index.ts CHANGED
@@ -1,25 +1,16 @@
1
1
  import { registerBreadcrumbs, defineConfigSchema, getAsyncLifecycle } from '@openmrs/esm-framework';
2
- import { FormManager } from './patient-registration/form-manager';
3
2
  import { esmPatientRegistrationSchema } from './config-schema';
4
3
  import { moduleName, patientRegistration } from './constants';
5
4
  import { setupOffline } from './offline';
6
5
 
7
- declare var __VERSION__: string;
8
- // __VERSION__ is replaced by Webpack with the version from package.json
9
- const version = __VERSION__;
6
+ export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
10
7
 
11
- const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
12
-
13
- const backendDependencies = {
14
- 'webservices.rest': '^2.24.0',
8
+ const options = {
9
+ featureName: 'Patient Registration',
10
+ moduleName,
15
11
  };
16
12
 
17
- function setupOpenMRS() {
18
- const options = {
19
- featureName: 'Patient Registration',
20
- moduleName,
21
- };
22
-
13
+ export function startupApp() {
23
14
  defineConfigSchema(moduleName, esmPatientRegistrationSchema);
24
15
 
25
16
  registerBreadcrumbs([
@@ -36,96 +27,36 @@ function setupOpenMRS() {
36
27
  ]);
37
28
 
38
29
  setupOffline();
39
-
40
- return {
41
- pages: [
42
- {
43
- load: getAsyncLifecycle(() => import('./root.component'), options),
44
- route: 'patient-registration',
45
- online: {
46
- savePatientForm: FormManager.savePatientFormOnline,
47
- isOffline: false,
48
- },
49
- offline: {
50
- savePatientForm: FormManager.savePatientFormOffline,
51
- isOffline: true,
52
- },
53
- },
54
- {
55
- load: getAsyncLifecycle(() => import('./root.component'), {
56
- featureName: 'edit-patient-details-form',
57
- moduleName,
58
- }),
59
- route: /patient\/([a-zA-Z0-9\-]+)\/edit/,
60
- online: {
61
- savePatientForm: FormManager.savePatientFormOnline,
62
- },
63
- offline: {
64
- savePatientForm: FormManager.savePatientFormOffline,
65
- },
66
- },
67
- ],
68
- extensions: [
69
- {
70
- id: 'add-patient-action',
71
- slot: 'top-nav-actions-slot',
72
- load: getAsyncLifecycle(() => import('./add-patient-link'), options),
73
- online: true,
74
- offline: true,
75
- },
76
- {
77
- id: 'cancel-patient-edit-modal',
78
- load: getAsyncLifecycle(() => import('./widgets/cancel-patient-edit.component'), options),
79
- online: true,
80
- offline: true,
81
- },
82
- {
83
- id: 'patient-photo-widget',
84
- slot: 'patient-photo-slot',
85
- load: getAsyncLifecycle(() => import('./widgets/display-photo.component'), options),
86
- online: true,
87
- offline: true,
88
- },
89
- {
90
- id: 'edit-patient-details-button',
91
- slot: 'patient-actions-slot',
92
- load: getAsyncLifecycle(() => import('./widgets/edit-patient-details-button.component'), options),
93
- online: true,
94
- offline: true,
95
- },
96
- {
97
- id: 'edit-patient-details-button',
98
- slot: 'patient-search-actions-slot',
99
- load: getAsyncLifecycle(() => import('./widgets/edit-patient-details-button.component'), options),
100
- online: true,
101
- offline: true,
102
- },
103
- {
104
- id: 'delete-identifier-confirmation-modal',
105
- load: getAsyncLifecycle(() => import('./widgets/delete-identifier-confirmation-modal'), options),
106
- online: true,
107
- offline: true,
108
- },
109
- {
110
- id: 'empty-client-registry-modal',
111
- load: getAsyncLifecycle(
112
- () => import('./patient-verification/verification-modal/empty-prompt.component'),
113
- options,
114
- ),
115
- online: true,
116
- offline: true,
117
- },
118
- {
119
- id: 'confirm-client-registry-modal',
120
- load: getAsyncLifecycle(
121
- () => import('./patient-verification/verification-modal/confirm-prompt.component'),
122
- options,
123
- ),
124
- online: true,
125
- offline: true,
126
- },
127
- ],
128
- };
129
30
  }
130
31
 
131
- export { backendDependencies, importTranslation, setupOpenMRS, version };
32
+ export const root = getAsyncLifecycle(() => import('./root.component'), options);
33
+
34
+ export const editPatient = getAsyncLifecycle(() => import('./root.component'), {
35
+ featureName: 'edit-patient-details-form',
36
+ moduleName,
37
+ });
38
+
39
+ export const addPatientLink = getAsyncLifecycle(() => import('./add-patient-link'), options);
40
+
41
+ export const cancelPatientEditModal = getAsyncLifecycle(
42
+ () => import('./widgets/cancel-patient-edit.component'),
43
+ options,
44
+ );
45
+
46
+ export const patientPhoto = getAsyncLifecycle(() => import('./widgets/display-photo.component'), options);
47
+
48
+ export const editPatientDetailsButton = getAsyncLifecycle(
49
+ () => import('./widgets/edit-patient-details-button.component'),
50
+ {
51
+ featureName: 'edit-patient-details',
52
+ moduleName,
53
+ },
54
+ );
55
+
56
+ export const deleteIdentifierConfirmationModal = getAsyncLifecycle(
57
+ () => import('./widgets/delete-identifier-confirmation-modal'),
58
+ options,
59
+ );
60
+
61
+ export const emptyVerificationModal = getAsyncLifecycle(() => import('./patient-verification/verification-modal/empty-prompt.component'), options);
62
+ export const confirmationVerificationModal = getAsyncLifecycle(() => import('./patient-verification/verification-modal/confirm-prompt.component'), options);
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import Root from './nav-link';
4
+
5
+ describe('Nav link component', () => {
6
+ it('renders a link to the patient registration page', () => {
7
+ const { getByText } = render(<Root />);
8
+ const linkElement = getByText('Patient Registration');
9
+
10
+ expect(linkElement).toBeInTheDocument();
11
+ expect(linkElement).toHaveAttribute('href', '/openmrs/spa/patient-registration');
12
+ });
13
+ });
@@ -188,4 +188,4 @@ const AddressComponentContainer = ({ children }) => {
188
188
  </div>
189
189
  </div>
190
190
  );
191
- };
191
+ };
@@ -37,7 +37,7 @@ const AddressComboBox: React.FC<AddressComboBoxProps> = ({ attribute }) => {
37
37
  const [field, meta, helpers] = useField(`address.${attribute.name}`);
38
38
  const { fetchEntriesForField, searchString, updateChildElements } = useAddressEntryFetchConfig(attribute.name);
39
39
  const { entries } = useAddressEntries(fetchEntriesForField, searchString);
40
- console.log(searchString);
40
+
41
41
  const handleInputChange = useCallback((newValue) => {
42
42
  helpers.setValue(newValue);
43
43
  }, []);
@@ -0,0 +1,291 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { Field } from './field.component';
4
+ import { useConfig } from '@openmrs/esm-framework';
5
+ import { PatientRegistrationContext } from '../patient-registration-context';
6
+ import { Resources, ResourcesContext } from '../../offline.resources';
7
+ import { Form, Formik } from 'formik';
8
+
9
+ jest.mock('@openmrs/esm-framework', () => ({
10
+ ...jest.requireActual('@openmrs/esm-framework'),
11
+ useConfig: jest.fn(),
12
+ }));
13
+ const predefinedAddressTemplate = {
14
+ results: [
15
+ {
16
+ value:
17
+ '<org.openmrs.layout.address.AddressTemplate>\r\n <nameMappings class="properties">\r\n <property name="postalCode" value="Location.postalCode"/>\r\n <property name="address2" value="Location.address2"/>\r\n <property name="address1" value="Location.address1"/>\r\n <property name="country" value="Location.country"/>\r\n <property name="stateProvince" value="Location.stateProvince"/>\r\n <property name="cityVillage" value="Location.cityVillage"/>\r\n </nameMappings>\r\n <sizeMappings class="properties">\r\n <property name="postalCode" value="4"/>\r\n <property name="address1" value="40"/>\r\n <property name="address2" value="40"/>\r\n <property name="country" value="10"/>\r\n <property name="stateProvince" value="10"/>\r\n <property name="cityVillage" value="10"/>\r\n <asset name="cityVillage" value="10"/>\r\n </sizeMappings>\r\n <lineByLineFormat>\r\n <string>address1 address2</string>\r\n <string>cityVillage stateProvince postalCode</string>\r\n <string>country</string>\r\n </lineByLineFormat>\r\n <elementDefaults class="properties">\r\n <property name="country" value=""/>\r\n </elementDefaults>\r\n <elementRegex class="properties">\r\n <property name="address1" value="[a-zA-Z]+$"/>\r\n </elementRegex>\r\n <elementRegexFormats class="properties">\r\n <property name="address1" value="Countries can only be letters"/>\r\n </elementRegexFormats>\r\n </org.openmrs.layout.address.AddressTemplate>',
18
+ },
19
+ ],
20
+ };
21
+
22
+ const mockedIdentifierTypes = [
23
+ {
24
+ fieldName: 'openMrsId',
25
+ format: null,
26
+ identifierSources: [
27
+ {
28
+ uuid: '8549f706-7e85-4c1d-9424-217d50a2988b',
29
+ name: 'Generator for OpenMRS ID',
30
+ description: 'Generator for OpenMRS ID',
31
+ baseCharacterSet: '0123456789ACDEFGHJKLMNPRTUVWXY',
32
+ prefix: '',
33
+ },
34
+ ],
35
+ isPrimary: true,
36
+ name: 'OpenMRS ID',
37
+ required: true,
38
+ uniquenessBehavior: 'UNIQUE',
39
+ uuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
40
+ },
41
+ {
42
+ fieldName: 'idCard',
43
+ format: null,
44
+ identifierSources: [],
45
+ isPrimary: false,
46
+ name: 'ID Card',
47
+ required: false,
48
+ uniquenessBehavior: 'UNIQUE',
49
+ uuid: 'b4143563-16cd-4439-b288-f83d61670fc8',
50
+ },
51
+ {
52
+ fieldName: 'legacyId',
53
+ format: null,
54
+ identifierSources: [],
55
+ isPrimary: false,
56
+ name: 'Legacy ID',
57
+ required: false,
58
+ uniquenessBehavior: null,
59
+ uuid: '22348099-3873-459e-a32e-d93b17eda533',
60
+ },
61
+ {
62
+ fieldName: 'oldIdentificationNumber',
63
+ format: '',
64
+ identifierSources: [],
65
+ isPrimary: false,
66
+ name: 'Old Identification Number',
67
+ required: false,
68
+ uniquenessBehavior: null,
69
+ uuid: '8d79403a-c2cc-11de-8d13-0010c6dffd0f',
70
+ },
71
+ {
72
+ fieldName: 'openMrsIdentificationNumber',
73
+ format: '',
74
+ identifierSources: [],
75
+ isPrimary: false,
76
+ name: 'OpenMRS Identification Number',
77
+ required: false,
78
+ uniquenessBehavior: null,
79
+ uuid: '8d793bee-c2cc-11de-8d13-0010c6dffd0f',
80
+ },
81
+ ];
82
+ const mockResourcesContextValue = {
83
+ addressTemplate: predefinedAddressTemplate,
84
+ currentSession: {
85
+ authenticated: true,
86
+ sessionId: 'JSESSION',
87
+ currentProvider: { uuid: 'provider-uuid', identifier: 'PRO-123' },
88
+ },
89
+ relationshipTypes: [],
90
+ identifierTypes: [...mockedIdentifierTypes],
91
+ } as Resources;
92
+
93
+ describe('Field', () => {
94
+ beforeEach(() => {
95
+ jest.clearAllMocks();
96
+ });
97
+
98
+ it('should render NameField component when name prop is "name"', () => {
99
+ (useConfig as jest.Mock).mockImplementation(() => ({
100
+ fieldConfigurations: {
101
+ name: {
102
+ displayMiddleName: true,
103
+ unidentifiedPatient: true,
104
+ defaultUnknownGivenName: 'UNKNOWN',
105
+ defaultUnknownFamilyName: 'UNKNOWN',
106
+ },
107
+ },
108
+ }));
109
+ render(
110
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
111
+ <Formik initialValues={{}} onSubmit={null}>
112
+ <Form>
113
+ <PatientRegistrationContext.Provider
114
+ value={{
115
+ setFieldValue: jest.fn(),
116
+ setInitialFormValues: jest.fn(),
117
+ values: {},
118
+ }}>
119
+ <Field name="name" />
120
+ </PatientRegistrationContext.Provider>
121
+ </Form>
122
+ </Formik>
123
+ </ResourcesContext.Provider>,
124
+ );
125
+ expect(screen.getByText('Full Name')).toBeInTheDocument();
126
+ });
127
+
128
+ it('should render GenderField component when name prop is "gender"', () => {
129
+ (useConfig as jest.Mock).mockImplementation(() => ({
130
+ fieldConfigurations: {
131
+ gender: [
132
+ {
133
+ value: 'Male',
134
+ label: 'Male',
135
+ id: 'male',
136
+ },
137
+ ],
138
+ },
139
+ }));
140
+ render(
141
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
142
+ <Formik initialValues={{}} onSubmit={null}>
143
+ <Form>
144
+ <PatientRegistrationContext.Provider
145
+ value={{
146
+ setFieldValue: jest.fn(),
147
+ setInitialFormValues: jest.fn(),
148
+ values: {},
149
+ }}>
150
+ <Field name="gender" />
151
+ </PatientRegistrationContext.Provider>
152
+ </Form>
153
+ </Formik>
154
+ </ResourcesContext.Provider>,
155
+ );
156
+ expect(screen.getByLabelText('Male')).toBeInTheDocument();
157
+ });
158
+
159
+ it('should render DobField component when name prop is "dob"', () => {
160
+ (useConfig as jest.Mock).mockImplementation(() => ({
161
+ fieldConfigurations: {
162
+ dob: {
163
+ minAgeLimit: 0,
164
+ maxAgeLimit: 120,
165
+ },
166
+ },
167
+ }));
168
+ render(
169
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
170
+ <Formik initialValues={{}} onSubmit={null}>
171
+ <Form>
172
+ <PatientRegistrationContext.Provider
173
+ value={{
174
+ setFieldValue: jest.fn(),
175
+ setInitialFormValues: jest.fn(),
176
+ values: {},
177
+ }}>
178
+ <Field name="dob" />
179
+ </PatientRegistrationContext.Provider>
180
+ </Form>
181
+ </Formik>
182
+ </ResourcesContext.Provider>,
183
+ );
184
+ expect(screen.getByText('Birth')).toBeInTheDocument();
185
+ });
186
+
187
+ it('should render AddressComponent component when name prop is "address"', () => {
188
+ jest.mock('./address/address-hierarchy.resource', () => ({
189
+ ...(jest.requireActual('../address-hierarchy.resource') as jest.Mock),
190
+ useOrderedAddressHierarchyLevels: jest.fn(),
191
+ }));
192
+ (useConfig as jest.Mock).mockImplementation(() => ({
193
+ fieldConfigurations: {
194
+ address: {
195
+ useAddressHierarchy: {
196
+ enabled: false,
197
+ useQuickSearch: false,
198
+ searchAddressByLevel: false,
199
+ },
200
+ },
201
+ },
202
+ }));
203
+ render(
204
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
205
+ <Formik initialValues={{}} onSubmit={null}>
206
+ <Form>
207
+ <PatientRegistrationContext.Provider
208
+ value={{
209
+ setFieldValue: jest.fn(),
210
+ setInitialFormValues: jest.fn(),
211
+ values: {},
212
+ }}>
213
+ <Field name="address" />
214
+ </PatientRegistrationContext.Provider>
215
+ </Form>
216
+ </Formik>
217
+ </ResourcesContext.Provider>,
218
+ );
219
+ expect(screen.getByText('Address')).toBeInTheDocument();
220
+ });
221
+
222
+ it('should render Identifiers component when name prop is "id"', () => {
223
+ (useConfig as jest.Mock).mockImplementation(() => ({
224
+ defaultPatientIdentifierTypes: ['OpenMRS ID'],
225
+ }));
226
+ // initial value for the identifiers field
227
+ const openmrsID = {
228
+ name: 'OpenMRS ID',
229
+ fieldName: 'openMrsId',
230
+ required: true,
231
+ uuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
232
+ format: null,
233
+ isPrimary: true,
234
+ identifierSources: [
235
+ {
236
+ uuid: '691eed12-c0f1-11e2-94be-8c13b969e334',
237
+ name: 'Generator 1 for OpenMRS ID',
238
+ autoGenerationOption: {
239
+ manualEntryEnabled: false,
240
+ automaticGenerationEnabled: true,
241
+ },
242
+ },
243
+ {
244
+ uuid: '01af8526-cea4-4175-aa90-340acb411771',
245
+ name: 'Generator 2 for OpenMRS ID',
246
+ autoGenerationOption: {
247
+ manualEntryEnabled: true,
248
+ automaticGenerationEnabled: true,
249
+ },
250
+ },
251
+ ],
252
+ autoGenerationSource: null,
253
+ };
254
+ render(
255
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
256
+ <Formik initialValues={{}} onSubmit={null}>
257
+ <Form>
258
+ <PatientRegistrationContext.Provider
259
+ value={{
260
+ setFieldValue: jest.fn(),
261
+ initialFormValues: { identifiers: { openmrsID } },
262
+ setInitialFormValues: jest.fn(),
263
+ values: {
264
+ identifiers: { openmrsID },
265
+ },
266
+ }}>
267
+ <Field name="id" />
268
+ </PatientRegistrationContext.Provider>
269
+ </Form>
270
+ </Formik>
271
+ </ResourcesContext.Provider>,
272
+ );
273
+ expect(screen.getByText('Identifiers')).toBeInTheDocument();
274
+ });
275
+
276
+ it('should return null and report an error for an invalid field name', () => {
277
+ (useConfig as jest.Mock).mockImplementation(() => ({
278
+ fieldDefinitions: [{ id: 'weight' }],
279
+ }));
280
+ let error = null;
281
+ try {
282
+ render(<Field name="invalidField" />);
283
+ } catch (err) {
284
+ error = err;
285
+ }
286
+ expect(error).toBe(
287
+ "Invalid field name 'invalidField'. Valid options are 'weight', 'name', 'gender', 'dob', 'address', 'id', 'phone & email'.",
288
+ );
289
+ expect(screen.queryByTestId('invalid-field')).not.toBeInTheDocument();
290
+ });
291
+ });
@@ -21,7 +21,7 @@ function checkNumber(value: string) {
21
21
  export const NameField = () => {
22
22
  const {
23
23
  fieldConfigurations: {
24
- name: { displayCapturePhoto },
24
+ name: { displayCapturePhoto, displayReverseFieldOrder },
25
25
  },
26
26
  } = useConfig() as RegistrationConfig;
27
27
  const { t } = useTranslation();
@@ -55,6 +55,36 @@ export const NameField = () => {
55
55
  }
56
56
  };
57
57
 
58
+ const firstNameField = (
59
+ <Input
60
+ id="givenName"
61
+ name="givenName"
62
+ labelText={t('givenNameLabelText', 'First Name')}
63
+ checkWarning={checkNumber}
64
+ required
65
+ />
66
+ );
67
+
68
+ const middleNameField = fieldConfigs.displayMiddleName && (
69
+ <Input
70
+ id="middleName"
71
+ name="middleName"
72
+ labelText={t('middleNameLabelText', 'Middle Name')}
73
+ light
74
+ checkWarning={checkNumber}
75
+ />
76
+ );
77
+
78
+ const familyNameField = (
79
+ <Input
80
+ id="familyName"
81
+ name="familyName"
82
+ labelText={t('familyNameLabelText', 'Family Name')}
83
+ checkWarning={checkNumber}
84
+ required
85
+ />
86
+ );
87
+
58
88
  return (
59
89
  <div>
60
90
  <h4 className={styles.productiveHeading02Light}>{t('fullNameLabelText', 'Full Name')}</h4>
@@ -75,33 +105,20 @@ export const NameField = () => {
75
105
  <Switch name="known" text={t('yes', 'Yes')} />
76
106
  <Switch name="unknown" text={t('no', 'No')} />
77
107
  </ContentSwitcher>
78
- {nameKnown && (
79
- <>
80
- <Input
81
- id="givenName"
82
- name="givenName"
83
- labelText={t('givenNameLabelText', 'First Name')}
84
- checkWarning={checkNumber}
85
- required
86
- />
87
- {fieldConfigs.displayMiddleName && (
88
- <Input
89
- id="middleName"
90
- name="middleName"
91
- labelText={t('middleNameLabelText', 'Middle Name')}
92
- light
93
- checkWarning={checkNumber}
94
- />
95
- )}
96
- <Input
97
- id="familyName"
98
- name="familyName"
99
- labelText={t('familyNameLabelText', 'Family Name')}
100
- checkWarning={checkNumber}
101
- required
102
- />
103
- </>
104
- )}
108
+ {nameKnown &&
109
+ (!displayReverseFieldOrder ? (
110
+ <>
111
+ {firstNameField}
112
+ {middleNameField}
113
+ {familyNameField}
114
+ </>
115
+ ) : (
116
+ <>
117
+ {familyNameField}
118
+ {middleNameField}
119
+ {firstNameField}
120
+ </>
121
+ ))}
105
122
  </div>
106
123
  </div>
107
124
  </div>
@@ -39,6 +39,7 @@ export type SavePatientForm = (
39
39
  savePatientTransactionManager: SavePatientTransactionManager,
40
40
  abortController?: AbortController,
41
41
  ) => Promise<string | void>;
42
+
42
43
  export class FormManager {
43
44
  static savePatientFormOffline: SavePatientForm = async (
44
45
  isNewPatient,