@kenyaemr/esm-patient-registration-app 7.0.3-pre.89 → 8.0.1-pre.95

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 (89) hide show
  1. package/.turbo/turbo-build.log +15 -14
  2. package/dist/130.js +1 -1
  3. package/dist/130.js.map +1 -1
  4. package/dist/271.js +1 -1
  5. package/dist/319.js +1 -1
  6. package/dist/460.js +1 -1
  7. package/dist/564.js +1 -0
  8. package/dist/564.js.map +1 -0
  9. package/dist/623.js +1 -0
  10. package/dist/623.js.map +1 -0
  11. package/dist/644.js +1 -1
  12. package/dist/757.js +1 -1
  13. package/dist/788.js +1 -1
  14. package/dist/807.js +1 -1
  15. package/dist/831.js +2 -0
  16. package/dist/831.js.map +1 -0
  17. package/dist/833.js +1 -1
  18. package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
  19. package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +99 -99
  20. package/dist/main.js +1 -1
  21. package/dist/main.js.map +1 -1
  22. package/dist/routes.json +1 -1
  23. package/package.json +2 -2
  24. package/src/add-patient-link.test.tsx +5 -9
  25. package/src/config-schema.ts +2 -0
  26. package/src/nav-link.test.tsx +3 -3
  27. package/src/patient-registration/field/address/address-field.component.tsx +2 -2
  28. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +16 -18
  29. package/src/patient-registration/field/address/address-search.scss +5 -5
  30. package/src/patient-registration/field/address/tests/address-hierarchy.test.tsx +165 -95
  31. package/src/patient-registration/field/address/tests/address-search-component.test.tsx +18 -17
  32. package/src/patient-registration/field/dob/dob.component.tsx +3 -6
  33. package/src/patient-registration/field/dob/dob.test.tsx +69 -53
  34. package/src/patient-registration/field/field.scss +30 -25
  35. package/src/patient-registration/field/field.test.tsx +50 -52
  36. package/src/patient-registration/field/gender/gender-field.component.tsx +2 -2
  37. package/src/patient-registration/field/gender/gender-field.test.tsx +45 -27
  38. package/src/patient-registration/field/id/id-field.component.tsx +11 -12
  39. package/src/patient-registration/field/id/id-field.test.tsx +64 -49
  40. package/src/patient-registration/field/id/identifier-selection.scss +12 -8
  41. package/src/patient-registration/field/name/name-field.component.tsx +1 -1
  42. package/src/patient-registration/field/obs/obs-field.component.tsx +7 -12
  43. package/src/patient-registration/field/obs/obs-field.test.tsx +98 -62
  44. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +9 -6
  45. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +49 -51
  46. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +2 -0
  47. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +1 -1
  48. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.scss +5 -5
  49. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +75 -33
  50. package/src/patient-registration/input/custom-input/identifier/identifier-input.component.tsx +3 -1
  51. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +9 -12
  52. package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +2 -11
  53. package/src/patient-registration/input/input.scss +12 -12
  54. package/src/patient-registration/patient-registration-context.ts +6 -6
  55. package/src/patient-registration/patient-registration-utils.ts +4 -5
  56. package/src/patient-registration/patient-registration.component.tsx +4 -14
  57. package/src/patient-registration/patient-registration.resource.test.tsx +0 -4
  58. package/src/patient-registration/patient-registration.scss +11 -25
  59. package/src/patient-registration/patient-registration.test.tsx +75 -85
  60. package/src/patient-registration/patient-registration.types.ts +18 -18
  61. package/src/patient-registration/section/death-info/death-info-section.test.tsx +1 -10
  62. package/src/patient-registration/section/demographics/demographics-section.test.tsx +32 -29
  63. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +16 -6
  64. package/src/patient-registration/section/patient-relationships/relationships.scss +4 -4
  65. package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
  66. package/src/patient-registration/section/section.scss +16 -1
  67. package/src/patient-registration/ui-components/overlay/overlay.scss +8 -8
  68. package/src/patient-registration/validation/patient-registration-validation.test.tsx +35 -10
  69. package/src/patient-verification/patient-verification-utils.ts +0 -1
  70. package/src/widgets/cancel-patient-edit.test.tsx +0 -4
  71. package/src/widgets/delete-identifier-confirmation.scss +8 -8
  72. package/src/widgets/delete-identifier-confirmation.test.tsx +0 -4
  73. package/src/widgets/edit-patient-details-button.test.tsx +2 -8
  74. package/translations/am.json +4 -0
  75. package/translations/ar.json +4 -0
  76. package/translations/es.json +4 -0
  77. package/translations/fr.json +4 -0
  78. package/translations/he.json +4 -0
  79. package/translations/km.json +4 -0
  80. package/translations/zh.json +4 -0
  81. package/translations/zh_CN.json +4 -0
  82. package/dist/220.js +0 -2
  83. package/dist/220.js.map +0 -1
  84. package/dist/453.js +0 -1
  85. package/dist/453.js.map +0 -1
  86. package/dist/975.js +0 -1
  87. package/dist/975.js.map +0 -1
  88. package/src/root.test.tsx +0 -32
  89. /package/dist/{220.js.LICENSE.txt → 831.js.LICENSE.txt} +0 -0
@@ -3,42 +3,65 @@ import dayjs from 'dayjs';
3
3
  import { Formik, Form } from 'formik';
4
4
  import { render, screen } from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
- import { DobField } from './dob.component';
6
+ import { getDefaultsFromConfigSchema, OpenmrsDatePicker, useConfig } from '@openmrs/esm-framework';
7
+ import { type RegistrationConfig, esmPatientRegistrationSchema } from '../../../config-schema';
7
8
  import { PatientRegistrationContext } from '../../patient-registration-context';
8
9
  import { initialFormValues } from '../../patient-registration.component';
9
- import { type FormValues } from '../../patient-registration.types';
10
+ import { DobField } from './dob.component';
10
11
 
11
- jest.mock('@openmrs/esm-framework', () => {
12
- const originalModule = jest.requireActual('@openmrs/esm-framework');
13
- return {
14
- ...originalModule,
15
- useConfig: jest.fn().mockImplementation(() => ({
12
+ const mockOpenmrsDatePicker = jest.mocked(OpenmrsDatePicker);
13
+ const mockUseConfig = jest.mocked(useConfig<RegistrationConfig>);
14
+
15
+ mockOpenmrsDatePicker.mockImplementation(({ id, labelText, value, onChange }) => {
16
+ return (
17
+ <>
18
+ <label htmlFor={id}>{labelText}</label>
19
+ <input
20
+ id={id}
21
+ // @ts-ignore
22
+ value={value ? dayjs(value).format('DD/MM/YYYY') : ''}
23
+ onChange={(evt) => {
24
+ onChange(dayjs(evt.target.value).toDate());
25
+ }}
26
+ />
27
+ </>
28
+ );
29
+ });
30
+
31
+ describe('Dob', () => {
32
+ beforeEach(() => {
33
+ mockUseConfig.mockReturnValue({
34
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
16
35
  fieldConfigurations: {
17
36
  dateOfBirth: {
18
37
  allowEstimatedDateOfBirth: true,
19
38
  useEstimatedDateOfBirth: { enabled: true, dayOfMonth: 0, month: 0 },
20
39
  },
21
- },
22
- })),
23
- getLocale: jest.fn().mockReturnValue('en'),
24
- OpenmrsDatePicker: jest.fn().mockImplementation(({ id, labelText, value, onChange }) => {
25
- return (
26
- <>
27
- <label htmlFor={id}>{labelText}</label>
28
- <input
29
- id={id}
30
- value={value ? dayjs(value).format('DD/MM/YYYY') : undefined}
31
- onChange={(evt) => onChange(dayjs(evt.target.value).toDate())}
32
- />
33
- </>
34
- );
35
- }),
36
- };
37
- });
40
+ } as RegistrationConfig['fieldConfigurations'],
41
+ });
42
+ });
38
43
 
39
- describe('Dob', () => {
40
44
  it('renders the fields in the birth section of the registration form', async () => {
41
- renderDob();
45
+ render(
46
+ <Formik initialValues={{ birthdate: '' }} onSubmit={() => {}}>
47
+ <Form>
48
+ <PatientRegistrationContext.Provider
49
+ value={{
50
+ identifierTypes: [],
51
+ values: initialFormValues,
52
+ validationSchema: null,
53
+ inEditMode: false,
54
+ setFieldValue: () => {},
55
+ setCapturePhotoProps: (value) => {},
56
+ currentPhoto: '',
57
+ isOffline: false,
58
+ initialFormValues: initialFormValues,
59
+ }}>
60
+ <DobField />
61
+ </PatientRegistrationContext.Provider>
62
+ </Form>
63
+ </Formik>,
64
+ );
42
65
 
43
66
  expect(screen.getByRole('heading', { name: /birth/i })).toBeInTheDocument();
44
67
  expect(screen.getByText(/date of birth known?/i)).toBeInTheDocument();
@@ -54,38 +77,31 @@ describe('Dob', () => {
54
77
  it.skip('typing in the date picker input sets the date of birth', async () => {
55
78
  const user = userEvent.setup();
56
79
 
57
- renderDob();
80
+ render(
81
+ <Formik initialValues={{ birthdate: '' }} onSubmit={() => {}}>
82
+ <Form>
83
+ <PatientRegistrationContext.Provider
84
+ value={{
85
+ identifierTypes: [],
86
+ values: initialFormValues,
87
+ validationSchema: null,
88
+ inEditMode: false,
89
+ setFieldValue: () => {},
90
+ setCapturePhotoProps: (value) => {},
91
+ currentPhoto: '',
92
+ isOffline: false,
93
+ initialFormValues: initialFormValues,
94
+ }}>
95
+ <DobField />
96
+ </PatientRegistrationContext.Provider>
97
+ </Form>
98
+ </Formik>,
99
+ );
58
100
 
59
101
  const dateInput = screen.getByLabelText(/Date of birth/i);
60
102
  expect(dateInput).toBeInTheDocument();
61
103
 
62
104
  await user.type(dateInput, '10/10/2022');
63
-
64
105
  expect(screen.getByPlaceholderText('dd/mm/YYYY')).toHaveValue('10/10/2022');
65
106
  });
66
107
  });
67
-
68
- function renderDob() {
69
- let formValues: FormValues = initialFormValues;
70
-
71
- render(
72
- <Formik initialValues={{ birthdate: '' }} onSubmit={() => {}}>
73
- <Form>
74
- <PatientRegistrationContext.Provider
75
- value={{
76
- identifierTypes: [],
77
- values: formValues,
78
- validationSchema: null,
79
- inEditMode: false,
80
- setFieldValue: () => {},
81
- setCapturePhotoProps: (value) => {},
82
- currentPhoto: '',
83
- isOffline: false,
84
- initialFormValues: formValues,
85
- }}>
86
- <DobField />
87
- </PatientRegistrationContext.Provider>
88
- </Form>
89
- </Formik>,
90
- );
91
- }
@@ -1,16 +1,17 @@
1
- @use '@carbon/styles/scss/spacing';
2
- @use '@carbon/styles/scss/type';
3
- @import '~@openmrs/esm-styleguide/src/vars';
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+ @use '@openmrs/esm-styleguide/src/vars' as *;
4
5
 
5
6
  .productiveHeading02 {
6
7
  @include type.type-style('heading-compact-02');
7
- margin-bottom: 1rem;
8
+ margin-bottom: layout.$spacing-05;
8
9
  }
9
10
 
10
11
  .productiveHeading02Light {
11
12
  @include type.type-style('heading-compact-02');
12
- margin-bottom: 1rem;
13
- color: #525252;
13
+ margin-bottom: layout.$spacing-05;
14
+ color: colors.$gray-70;
14
15
  }
15
16
 
16
17
  .label01 {
@@ -20,11 +21,11 @@
20
21
  .grid {
21
22
  display: grid;
22
23
  grid-template-columns: 1fr 1fr;
23
- column-gap: spacing.$spacing-05;
24
+ column-gap: layout.$spacing-05;
24
25
  }
25
26
 
26
27
  .halfWidthInDesktopView {
27
- width: calc(50% - spacing.$spacing-05);
28
+ width: calc(50% - layout.$spacing-05);
28
29
  }
29
30
 
30
31
  .patientPhoto {
@@ -45,7 +46,7 @@
45
46
  }
46
47
 
47
48
  .contentSwitcher {
48
- margin-bottom: 1rem;
49
+ margin-bottom: layout.$spacing-05;
49
50
  }
50
51
 
51
52
  .dobField > :global(.cds--content-switcher) {
@@ -56,7 +57,7 @@
56
57
  }
57
58
 
58
59
  .photoExtension {
59
- margin-bottom: 1rem;
60
+ margin-bottom: layout.$spacing-05;
60
61
  grid-row: 1;
61
62
  grid-column: 2;
62
63
  justify-self: center;
@@ -64,11 +65,11 @@
64
65
 
65
66
  .sexField,
66
67
  .dobField {
67
- margin-bottom: spacing.$spacing-05;
68
+ margin-bottom: layout.$spacing-05;
68
69
  }
69
70
 
70
71
  .dobContentSwitcherLabel {
71
- margin-bottom: spacing.$spacing-03;
72
+ margin-bottom: layout.$spacing-03;
72
73
  }
73
74
 
74
75
  .identifierLabelText {
@@ -76,20 +77,20 @@
76
77
  align-items: center;
77
78
  }
78
79
 
79
- .setIDNumberButton {
80
- margin-bottom: spacing.$spacing-05;
81
- }
80
+ .configureIdentifiersButton {
81
+ margin: 0 0 layout.$spacing-05 layout.$spacing-05;
82
82
 
83
- .setIDNumberButton > svg {
84
- margin-left: spacing.$spacing-03;
85
- }
83
+ svg {
84
+ margin-left: layout.$spacing-03;
85
+ }
86
86
 
87
- .customField {
88
- margin-bottom: spacing.$spacing-05;
87
+ .customField {
88
+ margin-bottom: layout.$spacing-05;
89
+ }
89
90
  }
90
91
 
91
92
  .attributeField {
92
- margin-bottom: spacing.$spacing-05;
93
+ margin-bottom: layout.$spacing-05;
93
94
  }
94
95
 
95
96
  :global(.omrs-breakpoint-lt-desktop) {
@@ -97,31 +98,35 @@
97
98
  grid-template-columns: 1fr;
98
99
  grid-template-rows: auto auto;
99
100
  }
101
+
100
102
  .nameField {
101
103
  grid-row: 2;
102
104
  grid-column: 1;
103
105
  }
106
+
104
107
  .photoExtension {
105
108
  grid-column: 1;
106
109
  grid-row: 1;
107
110
  justify-self: start;
108
111
  }
112
+
109
113
  .radioButton label {
110
- height: spacing.$spacing-09 !important;
114
+ height: layout.$spacing-09 !important;
111
115
  }
116
+
112
117
  .halfWidthInDesktopView {
113
118
  width: 100%;
114
119
  }
115
120
  }
116
121
 
117
122
  .radioFieldError {
118
- color: #da1e28;
123
+ color: colors.$red-60;
119
124
  display: block;
120
125
  font-weight: 400;
121
126
  max-height: 12.5rem;
122
127
  overflow: visible;
123
- font-size: 0.75rem;
128
+ font-size: layout.$spacing-04;
124
129
  letter-spacing: 0.32px;
125
130
  line-height: 1.34;
126
- margin: 0.25rem 0 0;
131
+ margin: layout.$spacing-02 0 0;
127
132
  }
@@ -1,30 +1,32 @@
1
1
  import React from 'react';
2
+ import dayjs from 'dayjs';
2
3
  import { Form, Formik } from 'formik';
3
4
  import { render, screen } from '@testing-library/react';
4
- import { useConfig } from '@openmrs/esm-framework';
5
+ import { getDefaultsFromConfigSchema, OpenmrsDatePicker, useConfig } from '@openmrs/esm-framework';
5
6
  import { Field } from './field.component';
6
- import type { AddressTemplate, FormValues } from '../patient-registration.types';
7
+ import { esmPatientRegistrationSchema, type RegistrationConfig } from '../../config-schema';
7
8
  import { type Resources, ResourcesContext } from '../../offline.resources';
9
+ import type { AddressTemplate, FormValues } from '../patient-registration.types';
8
10
  import { PatientRegistrationContext } from '../patient-registration-context';
9
- import dayjs from 'dayjs';
10
11
 
11
- jest.mock('@openmrs/esm-framework', () => ({
12
- ...jest.requireActual('@openmrs/esm-framework'),
13
- useConfig: jest.fn(),
14
- getLocale: jest.fn().mockReturnValue('en'),
15
- OpenmrsDatePicker: jest.fn().mockImplementation(({ id, labelText, value, onChange }) => {
16
- return (
17
- <>
18
- <label htmlFor={id}>{labelText}</label>
19
- <input
20
- id={id}
21
- value={value ? dayjs(value).format('DD/MM/YYYY') : undefined}
22
- onChange={(evt) => onChange(dayjs(evt.target.value).toDate())}
23
- />
24
- </>
25
- );
26
- }),
27
- }));
12
+ const mockOpenmrsDatePicker = jest.mocked(OpenmrsDatePicker);
13
+ const mockUseConfig = jest.mocked(useConfig<RegistrationConfig>);
14
+
15
+ mockOpenmrsDatePicker.mockImplementation(({ id, labelText, value, onChange }) => {
16
+ return (
17
+ <>
18
+ <label htmlFor={id}>{labelText}</label>
19
+ <input
20
+ id={id}
21
+ // @ts-ignore
22
+ value={value ? dayjs(value).format('DD/MM/YYYY') : ''}
23
+ onChange={(evt) => {
24
+ onChange(dayjs(evt.target.value).toDate());
25
+ }}
26
+ />
27
+ </>
28
+ );
29
+ });
28
30
 
29
31
  const predefinedAddressTemplate = {
30
32
  uuid: 'test-address-template-uuid',
@@ -36,7 +38,7 @@ const predefinedAddressTemplate = {
36
38
  '<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>',
37
39
  };
38
40
 
39
- const mockedIdentifierTypes = [
41
+ const mockIdentifierTypes = [
40
42
  {
41
43
  fieldName: 'openMrsId',
42
44
  format: '',
@@ -105,7 +107,7 @@ const mockResourcesContextValue: Resources = {
105
107
  currentProvider: { uuid: 'provider-uuid', identifier: 'PRO-123' },
106
108
  },
107
109
  relationshipTypes: [],
108
- identifierTypes: [...mockedIdentifierTypes],
110
+ identifierTypes: [...mockIdentifierTypes],
109
111
  };
110
112
 
111
113
  const initialContextValues = {
@@ -136,31 +138,32 @@ describe('Field', () => {
136
138
  </Formik>
137
139
  </ResourcesContext.Provider>
138
140
  );
139
- });
140
141
 
141
- afterEach(() => {
142
- jest.clearAllMocks();
142
+ mockUseConfig.mockReturnValue({
143
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
144
+ });
143
145
  });
144
146
 
145
147
  it('should render NameField component when name prop is "name"', () => {
146
- (useConfig as jest.Mock).mockImplementation(() => ({
148
+ mockUseConfig.mockReturnValue({
149
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
147
150
  fieldConfigurations: {
148
151
  name: {
152
+ allowUnidentifiedPatients: true,
149
153
  displayMiddleName: true,
150
- unidentifiedPatient: true,
151
154
  defaultUnknownGivenName: 'UNKNOWN',
152
155
  defaultUnknownFamilyName: 'UNKNOWN',
153
156
  },
154
- },
155
- }));
157
+ } as RegistrationConfig['fieldConfigurations'],
158
+ });
156
159
 
157
160
  render(<Field name="name" />, { wrapper: ContextWrapper });
158
-
159
161
  expect(screen.getByText('Full Name')).toBeInTheDocument();
160
162
  });
161
163
 
162
164
  it('should render GenderField component when name prop is "gender"', () => {
163
- (useConfig as jest.Mock).mockImplementation(() => ({
165
+ mockUseConfig.mockReturnValue({
166
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
164
167
  fieldConfigurations: {
165
168
  gender: [
166
169
  {
@@ -169,33 +172,26 @@ describe('Field', () => {
169
172
  id: 'male',
170
173
  },
171
174
  ],
172
- },
173
- }));
175
+ } as unknown as RegistrationConfig['fieldConfigurations'],
176
+ });
174
177
 
175
178
  render(<Field name="gender" />, { wrapper: ContextWrapper });
176
-
177
179
  expect(screen.getByLabelText('Male')).toBeInTheDocument();
178
180
  });
179
181
 
180
182
  it('should render DobField component when name prop is "dob"', () => {
181
- (useConfig as jest.Mock).mockImplementation(() => ({
182
- fieldConfigurations: {
183
- dob: {
184
- minAgeLimit: 0,
185
- maxAgeLimit: 120,
186
- },
187
- },
188
- }));
189
183
  render(<Field name="dob" />, { wrapper: ContextWrapper });
190
184
  expect(screen.getByText('Birth')).toBeInTheDocument();
191
185
  });
192
186
 
193
187
  it('should render AddressComponent component when name prop is "address"', () => {
194
188
  jest.mock('./address/address-hierarchy.resource', () => ({
195
- ...(jest.requireActual('../address-hierarchy.resource') as jest.Mock),
189
+ ...jest.requireActual('../address-hierarchy.resource'),
196
190
  useOrderedAddressHierarchyLevels: jest.fn(),
197
191
  }));
198
- (useConfig as jest.Mock).mockImplementation(() => ({
192
+
193
+ mockUseConfig.mockReturnValue({
194
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
199
195
  fieldConfigurations: {
200
196
  address: {
201
197
  useAddressHierarchy: {
@@ -204,18 +200,18 @@ describe('Field', () => {
204
200
  searchAddressByLevel: false,
205
201
  },
206
202
  },
207
- },
208
- }));
203
+ } as RegistrationConfig['fieldConfigurations'],
204
+ });
209
205
 
210
206
  render(<Field name="address" />, { wrapper: ContextWrapper });
211
-
212
207
  expect(screen.getByText('Address')).toBeInTheDocument();
213
208
  });
214
209
 
215
210
  it('should render Identifiers component when name prop is "id"', () => {
216
- (useConfig as jest.Mock).mockImplementation(() => ({
211
+ mockUseConfig.mockReturnValue({
212
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
217
213
  defaultPatientIdentifierTypes: ['OpenMRS ID'],
218
- }));
214
+ });
219
215
 
220
216
  const openmrsID = {
221
217
  name: 'OpenMRS ID',
@@ -289,9 +285,11 @@ describe('Field', () => {
289
285
  it('should return null and report an error for an invalid field name', () => {
290
286
  const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});
291
287
 
292
- (useConfig as jest.Mock).mockImplementation(() => ({
293
- fieldDefinitions: [{ id: 'weight' }],
294
- }));
288
+ mockUseConfig.mockReturnValue({
289
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
290
+ fieldDefinitions: [{ id: 'weight' }] as RegistrationConfig['fieldDefinitions'],
291
+ });
292
+
295
293
  let error = null;
296
294
 
297
295
  try {
@@ -1,14 +1,14 @@
1
1
  import React, { useContext } from 'react';
2
2
  import { RadioButton, RadioButtonGroup } from '@carbon/react';
3
- import styles from '../field.scss';
4
3
  import { useTranslation } from 'react-i18next';
5
4
  import { PatientRegistrationContext } from '../../patient-registration-context';
6
5
  import { useField } from 'formik';
7
6
  import { type RegistrationConfig } from '../../../config-schema';
8
7
  import { useConfig } from '@openmrs/esm-framework';
8
+ import styles from '../field.scss';
9
9
 
10
10
  export const GenderField: React.FC = () => {
11
- const { fieldConfigurations } = useConfig() as RegistrationConfig;
11
+ const { fieldConfigurations } = useConfig<RegistrationConfig>();
12
12
  const { t } = useTranslation();
13
13
  const [field, meta] = useField('gender');
14
14
  const { setFieldValue } = useContext(PatientRegistrationContext);
@@ -1,28 +1,12 @@
1
1
  import React from 'react';
2
2
  import userEvent from '@testing-library/user-event';
3
3
  import { Formik, Form } from 'formik';
4
- import { render } from '@testing-library/react';
4
+ import { render, screen } from '@testing-library/react';
5
+ import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
6
+ import { type RegistrationConfig, esmPatientRegistrationSchema } from '../../../config-schema';
5
7
  import { GenderField } from './gender-field.component';
6
8
 
7
- jest.mock('@openmrs/esm-framework', () => ({
8
- ...(jest.requireActual('@openmrs/esm-framework') as any),
9
- useConfig: jest.fn(() => ({
10
- fieldConfigurations: {
11
- gender: [
12
- {
13
- value: 'male',
14
- label: 'Male',
15
- },
16
- ],
17
- name: {
18
- displayMiddleName: false,
19
- unidentifiedPatient: false,
20
- defaultUnknownGivenName: '',
21
- defaultUnknownFamilyName: '',
22
- },
23
- },
24
- })),
25
- }));
9
+ const mockUseConfig = jest.mocked(useConfig<RegistrationConfig>);
26
10
 
27
11
  jest.mock('react', () => ({
28
12
  ...(jest.requireActual('react') as any),
@@ -37,23 +21,57 @@ jest.mock('formik', () => ({
37
21
  }));
38
22
 
39
23
  describe('GenderField', () => {
40
- const renderComponent = () => {
41
- return render(
24
+ beforeEach(() => {
25
+ mockUseConfig.mockReturnValue({
26
+ ...getDefaultsFromConfigSchema(esmPatientRegistrationSchema),
27
+ fieldConfigurations: {
28
+ gender: [
29
+ {
30
+ value: 'male',
31
+ label: 'Male',
32
+ },
33
+ {
34
+ value: 'female',
35
+ label: 'Female',
36
+ },
37
+ ],
38
+ name: {
39
+ displayMiddleName: false,
40
+ allowUnidentifiedPatients: false,
41
+ defaultUnknownGivenName: '',
42
+ defaultUnknownFamilyName: '',
43
+ displayCapturePhoto: false,
44
+ displayReverseFieldOrder: false,
45
+ },
46
+ } as RegistrationConfig['fieldConfigurations'],
47
+ });
48
+ });
49
+ it('has a label', () => {
50
+ render(
42
51
  <Formik initialValues={{}} onSubmit={null}>
43
52
  <Form>
44
53
  <GenderField />
45
54
  </Form>
46
55
  </Formik>,
47
56
  );
48
- };
49
57
 
50
- it('has a label', () => {
51
- expect(renderComponent().getAllByText('Sex')).toBeTruthy();
58
+ expect(screen.getByRole('heading', { name: /sex/i })).toBeInTheDocument();
59
+ expect(screen.getByLabelText(/^male/i)).toBeInTheDocument();
60
+ expect(screen.getByLabelText(/female/i)).toBeInTheDocument();
52
61
  });
53
62
 
54
63
  it('checks an option', async () => {
55
64
  const user = userEvent.setup();
56
- const component = renderComponent();
57
- expect(component.getByLabelText('Male').getAttribute('value')).toBe('male');
65
+ render(
66
+ <Formik initialValues={{}} onSubmit={null}>
67
+ <Form>
68
+ <GenderField />
69
+ </Form>
70
+ </Formik>,
71
+ );
72
+
73
+ await user.click(screen.getByText(/female/i));
74
+ expect(screen.getByLabelText(/female/i)).toBeChecked();
75
+ expect(screen.getByLabelText(/^male/i)).not.toBeChecked();
58
76
  });
59
77
  });
@@ -4,13 +4,13 @@ import { Button, SkeletonText } from '@carbon/react';
4
4
  import { ArrowRight } from '@carbon/react/icons';
5
5
  import { useLayoutType, useConfig, isDesktop, UserHasAccess } from '@openmrs/esm-framework';
6
6
  import IdentifierSelectionOverlay from './identifier-selection-overlay.component';
7
- import { IdentifierInput } from '../../input/custom-input/identifier/identifier-input.component';
7
+ import IdentifierInput from '../../input/custom-input/identifier/identifier-input.component';
8
8
  import { PatientRegistrationContext } from '../../patient-registration-context';
9
- import {
10
- type FormValues,
11
- type IdentifierSource,
12
- type PatientIdentifierType,
13
- type PatientIdentifierValue,
9
+ import type {
10
+ FormValues,
11
+ IdentifierSource,
12
+ PatientIdentifierType,
13
+ PatientIdentifierValue,
14
14
  } from '../../patient-registration.types';
15
15
  import { ResourcesContext } from '../../../offline.resources';
16
16
  import styles from '../field.scss';
@@ -58,7 +58,7 @@ export function deleteIdentifierType(identifiers: FormValues['identifiers'], ide
58
58
 
59
59
  export const Identifiers: React.FC = () => {
60
60
  const { identifierTypes } = useContext(ResourcesContext);
61
- const isLoading = !identifierTypes;
61
+ const isLoading = !identifierTypes?.length;
62
62
  const { values, setFieldValue, initialFormValues, isOffline } = useContext(PatientRegistrationContext);
63
63
  const { t } = useTranslation();
64
64
  const layout = useLayoutType();
@@ -67,7 +67,6 @@ export const Identifiers: React.FC = () => {
67
67
  const { defaultPatientIdentifierTypes } = config;
68
68
 
69
69
  useEffect(() => {
70
- // Initialization
71
70
  if (identifierTypes) {
72
71
  const identifiers = {};
73
72
  identifierTypes
@@ -108,23 +107,23 @@ export const Identifiers: React.FC = () => {
108
107
 
109
108
  if (isLoading && !isOffline) {
110
109
  return (
111
- <div data-testid="loading-skeleton" className={styles.halfWidthInDesktopView}>
110
+ <div className={styles.halfWidthInDesktopView}>
112
111
  <div className={styles.identifierLabelText}>
113
112
  <h4 className={styles.productiveHeading02Light}>{t('idFieldLabelText', 'Identifiers')}</h4>
114
113
  </div>
115
- <SkeletonText />
114
+ <SkeletonText role="progressbar" />
116
115
  </div>
117
116
  );
118
117
  }
119
118
 
120
119
  return (
121
120
  <div className={styles.halfWidthInDesktopView}>
122
- <UserHasAccess privilege={['Get Identifier Types', 'Add Patient Identifiers']}>
121
+ <UserHasAccess privilege={['Get Identifier Types', 'Add patient identifiers']}>
123
122
  <div className={styles.identifierLabelText}>
124
123
  <h4 className={styles.productiveHeading02Light}>{t('idFieldLabelText', 'Identifiers')}</h4>
125
124
  <Button
126
125
  kind="ghost"
127
- className={styles.setIDNumberButton}
126
+ className={styles.configureIdentifiersButton}
128
127
  onClick={() => setShowIdentifierOverlay(true)}
129
128
  size={isDesktop(layout) ? 'sm' : 'md'}>
130
129
  {t('configure', 'Configure')} <ArrowRight size={16} />