@openmrs/esm-patient-programs-app 3.0.3 → 3.1.1-pre.1090

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 (83) hide show
  1. package/dist/119.js +2 -0
  2. package/dist/119.js.map +1 -0
  3. package/dist/169.js +2 -0
  4. package/dist/169.js.map +1 -0
  5. package/dist/206.js +3 -0
  6. package/dist/{314.openmrs-esm-patient-programs-app.js.LICENSE.txt → 206.js.LICENSE.txt} +0 -0
  7. package/dist/206.js.map +1 -0
  8. package/dist/313.js +2 -0
  9. package/dist/313.js.map +1 -0
  10. package/dist/339.js +3 -0
  11. package/dist/339.js.LICENSE.txt +1 -0
  12. package/dist/339.js.map +1 -0
  13. package/dist/363.js +2 -0
  14. package/dist/363.js.map +1 -0
  15. package/dist/364.js +3 -0
  16. package/dist/{545.openmrs-esm-patient-programs-app.js.LICENSE.txt → 364.js.LICENSE.txt} +0 -0
  17. package/dist/364.js.map +1 -0
  18. package/dist/373.js +2 -0
  19. package/dist/373.js.map +1 -0
  20. package/dist/402.js +2 -0
  21. package/dist/402.js.map +1 -0
  22. package/dist/440.js +2 -0
  23. package/dist/440.js.map +1 -0
  24. package/dist/47.js +3 -0
  25. package/dist/47.js.LICENSE.txt +20 -0
  26. package/dist/47.js.map +1 -0
  27. package/dist/539.js +2 -0
  28. package/dist/539.js.map +1 -0
  29. package/dist/574.js +1 -0
  30. package/dist/600.js +2 -0
  31. package/dist/600.js.map +1 -0
  32. package/dist/635.js +3 -0
  33. package/dist/635.js.LICENSE.txt +16 -0
  34. package/dist/635.js.map +1 -0
  35. package/dist/678.js +2 -0
  36. package/dist/678.js.map +1 -0
  37. package/dist/754.js +2 -0
  38. package/dist/754.js.map +1 -0
  39. package/dist/{757.openmrs-esm-patient-programs-app.js → 757.js} +0 -0
  40. package/dist/78.js +3 -0
  41. package/dist/{946.openmrs-esm-patient-programs-app.js.LICENSE.txt → 78.js.LICENSE.txt} +0 -0
  42. package/dist/78.js.map +1 -0
  43. package/dist/784.js +3 -0
  44. package/dist/784.js.LICENSE.txt +14 -0
  45. package/dist/784.js.map +1 -0
  46. package/dist/879.js +2 -0
  47. package/dist/879.js.map +1 -0
  48. package/dist/932.js +2 -0
  49. package/dist/932.js.map +1 -0
  50. package/dist/openmrs-esm-patient-programs-app.js +1 -2
  51. package/dist/openmrs-esm-patient-programs-app.js.buildmanifest.json +461 -54
  52. package/dist/openmrs-esm-patient-programs-app.js.map +1 -1
  53. package/package.json +4 -3
  54. package/src/index.ts +3 -5
  55. package/src/programs/programs-detailed-summary.component.tsx +37 -8
  56. package/src/programs/programs-detailed-summary.scss +0 -16
  57. package/src/programs/programs-detailed-summary.test.tsx +28 -1
  58. package/src/programs/programs-form.component.tsx +79 -89
  59. package/src/programs/programs-form.scss +42 -6
  60. package/src/programs/programs-form.test.tsx +5 -15
  61. package/src/programs/programs-overview.component.tsx +49 -15
  62. package/src/programs/programs-overview.scss +0 -16
  63. package/src/programs/programs-overview.test.tsx +2 -0
  64. package/src/programs/programs.component.tsx +157 -17
  65. package/src/programs/programs.resource.tsx +4 -2
  66. package/translations/en.json +4 -6
  67. package/dist/314.openmrs-esm-patient-programs-app.js +0 -3
  68. package/dist/314.openmrs-esm-patient-programs-app.js.map +0 -1
  69. package/dist/545.openmrs-esm-patient-programs-app.js +0 -3
  70. package/dist/545.openmrs-esm-patient-programs-app.js.map +0 -1
  71. package/dist/574.openmrs-esm-patient-programs-app.js +0 -1
  72. package/dist/639.openmrs-esm-patient-programs-app.js +0 -2
  73. package/dist/639.openmrs-esm-patient-programs-app.js.map +0 -1
  74. package/dist/839.openmrs-esm-patient-programs-app.js +0 -2
  75. package/dist/839.openmrs-esm-patient-programs-app.js.map +0 -1
  76. package/dist/943.openmrs-esm-patient-programs-app.js +0 -2
  77. package/dist/943.openmrs-esm-patient-programs-app.js.map +0 -1
  78. package/dist/946.openmrs-esm-patient-programs-app.js +0 -3
  79. package/dist/946.openmrs-esm-patient-programs-app.js.map +0 -1
  80. package/dist/openmrs-esm-patient-programs-app.js.LICENSE.txt +0 -5
  81. package/src/programs/program-record.component.tsx +0 -93
  82. package/src/programs/program-record.css +0 -42
  83. package/src/programs/program-record.test.outdated.tsx +0 -57
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-patient-programs-app",
3
- "version": "3.0.3",
3
+ "version": "3.1.1-pre.1090",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Patient programs microfrontend for the OpenMRS SPA",
6
6
  "browser": "dist/openmrs-esm-patient-programs-app.js",
@@ -36,13 +36,14 @@
36
36
  "dependencies": {
37
37
  "@carbon/charts-react": "^0.49.4",
38
38
  "@carbon/icons-react": "^10.18.0",
39
- "@openmrs/esm-patient-common-lib": "^3.0.3",
39
+ "@openmrs/esm-patient-common-lib": "^3.1.1-pre.1090",
40
40
  "carbon-components-react": "^7.25.0",
41
41
  "d3": "^7.0.3",
42
42
  "lodash-es": "^4.17.15"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@openmrs/esm-framework": "3.x",
46
+ "@openmrs/esm-patient-common-lib": "3.x",
46
47
  "carbon-components": "10.x",
47
48
  "carbon-icons": "7.x",
48
49
  "dayjs": "1.x",
@@ -51,5 +52,5 @@
51
52
  "react-router-dom": "5.x",
52
53
  "rxjs": "6.x"
53
54
  },
54
- "gitHead": "0613587d69c851115180b05633ff41c5db771a87"
55
+ "gitHead": "0e2864a46bd5ccf97dbb833c862eb38e6cdb5140"
55
56
  }
package/src/index.ts CHANGED
@@ -8,10 +8,6 @@ const backendDependencies = {
8
8
  'webservices.rest': '^2.2.0',
9
9
  };
10
10
 
11
- const frontendDependencies = {
12
- '@openmrs/esm-framework': process.env.FRAMEWORK_VERSION,
13
- };
14
-
15
11
  function setupOpenMRS() {
16
12
  const moduleName = '@openmrs/esm-patient-programs-app';
17
13
 
@@ -27,6 +23,7 @@ function setupOpenMRS() {
27
23
  {
28
24
  id: 'programs-overview-widget',
29
25
  slot: 'patient-chart-summary-dashboard-slot',
26
+ order: 0,
30
27
  load: getAsyncLifecycle(() => import('./programs/programs-overview.component'), options),
31
28
  meta: {
32
29
  columnSpan: 4,
@@ -43,6 +40,7 @@ function setupOpenMRS() {
43
40
  {
44
41
  id: 'programs-summary-dashboard',
45
42
  slot: 'patient-chart-dashboard-slot',
43
+ order: 10,
46
44
  load: getSyncLifecycle(createDashboardLink(dashboardMeta), options),
47
45
  meta: dashboardMeta,
48
46
  },
@@ -57,4 +55,4 @@ function setupOpenMRS() {
57
55
  };
58
56
  }
59
57
 
60
- export { backendDependencies, frontendDependencies, importTranslation, setupOpenMRS };
58
+ export { backendDependencies, importTranslation, setupOpenMRS };
@@ -1,10 +1,13 @@
1
1
  import React from 'react';
2
2
  import dayjs from 'dayjs';
3
3
  import Add16 from '@carbon/icons-react/es/add/16';
4
+ import filter from 'lodash-es/filter';
5
+ import includes from 'lodash-es/includes';
6
+ import map from 'lodash-es/map';
4
7
  import styles from './programs-detailed-summary.scss';
5
- import { EmptyState, ErrorState, launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
8
+ import { CardHeader, EmptyState, ErrorState, launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
6
9
  import { useTranslation } from 'react-i18next';
7
- import { useEnrollments } from './programs.resource';
10
+ import { useAvailablePrograms, useEnrollments } from './programs.resource';
8
11
  import { useProgramsContext } from './programs.context';
9
12
  import {
10
13
  Button,
@@ -20,7 +23,9 @@ import {
20
23
  TableRow,
21
24
  DataTableHeader,
22
25
  DataTableRow,
26
+ InlineNotification,
23
27
  } from 'carbon-components-react';
28
+ import { formatDate, formatDatetime } from '@openmrs/esm-framework';
24
29
 
25
30
  interface ProgramsDetailedSummaryProps {}
26
31
 
@@ -31,6 +36,11 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = () => {
31
36
  const headerTitle = t('carePrograms', 'Care Programs');
32
37
 
33
38
  const { data: enrolledPrograms, isError, isLoading, isValidating } = useEnrollments(patientUuid);
39
+ const { data: availablePrograms } = useAvailablePrograms();
40
+ const eligiblePrograms = filter(
41
+ availablePrograms,
42
+ (program) => !includes(map(enrolledPrograms, 'program.uuid'), program.uuid),
43
+ );
34
44
 
35
45
  const tableHeaders: Array<DataTableHeader> = React.useMemo(
36
46
  () => [
@@ -38,6 +48,10 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = () => {
38
48
  key: 'display',
39
49
  header: t('activePrograms', 'Active programs'),
40
50
  },
51
+ {
52
+ key: 'location',
53
+ header: t('location', 'Location'),
54
+ },
41
55
  {
42
56
  key: 'dateEnrolled',
43
57
  header: t('dateEnrolled', 'Date enrolled'),
@@ -55,9 +69,10 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = () => {
55
69
  return {
56
70
  id: program.uuid,
57
71
  display: program.display,
58
- dateEnrolled: dayjs(program.dateEnrolled).format('MMM-YYYY'),
72
+ location: program.location?.display,
73
+ dateEnrolled: formatDatetime(new Date(program.dateEnrolled)),
59
74
  status: program.dateCompleted
60
- ? `${t('completedOn', 'Completed On')} ${dayjs(program.dateCompleted).format('MMM-YYYY')}`
75
+ ? `${t('completedOn', 'Completed On')} ${formatDate(new Date(program.dateCompleted))}`
61
76
  : t('active', 'Active'),
62
77
  };
63
78
  });
@@ -70,14 +85,28 @@ const ProgramsDetailedSummary: React.FC<ProgramsDetailedSummaryProps> = () => {
70
85
  if (enrolledPrograms?.length) {
71
86
  return (
72
87
  <div className={styles.widgetCard}>
73
- <div className={styles.programsHeader}>
74
- <h4 className={`${styles.productiveHeading03} ${styles.text02}`}>{headerTitle}</h4>
88
+ <CardHeader title={headerTitle}>
75
89
  <span>{isValidating ? <InlineLoading /> : null}</span>
76
- <Button kind="ghost" renderIcon={Add16} iconDescription="Add programs" onClick={launchProgramsForm}>
90
+ <Button
91
+ kind="ghost"
92
+ renderIcon={Add16}
93
+ iconDescription="Add programs"
94
+ onClick={launchProgramsForm}
95
+ disabled={availablePrograms?.length && eligiblePrograms?.length === 0}
96
+ >
77
97
  {t('add', 'Add')}
78
98
  </Button>
79
- </div>
99
+ </CardHeader>
80
100
  <TableContainer>
101
+ {availablePrograms?.length && eligiblePrograms?.length === 0 && (
102
+ <InlineNotification
103
+ style={{ minWidth: '100%', margin: '0rem', padding: '0rem' }}
104
+ kind={'info'}
105
+ lowContrast={true}
106
+ subtitle={t('noEligibleEnrollments', 'There are no more programs left to enroll this patient in')}
107
+ title={t('fullyEnrolled', 'Enrolled in all programs')}
108
+ />
109
+ )}
81
110
  <DataTable rows={tableRows} headers={tableHeaders} isSortable={true} size="short">
82
111
  {({ rows, headers, getHeaderProps, getTableProps }) => (
83
112
  <Table {...getTableProps()} useZebraStyles>
@@ -6,19 +6,3 @@
6
6
  .widgetCard {
7
7
  border: 1px solid $ui-03;
8
8
  }
9
-
10
- .programsHeader {
11
- display: flex;
12
- justify-content: space-between;
13
- align-items: center;
14
- padding: $spacing-04 0 $spacing-04 $spacing-05;
15
- background-color: $ui-background;
16
- }
17
-
18
- .programsHeader > h4:after {
19
- content: "";
20
- display: block;
21
- width: 2rem;
22
- padding-top: 0.188rem;
23
- border-bottom: 0.375rem solid $brand-teal-01;
24
- }
@@ -1,13 +1,33 @@
1
1
  import React from 'react';
2
2
  import { screen } from '@testing-library/react';
3
3
  import userEvent from '@testing-library/user-event';
4
- import { openmrsFetch } from '@openmrs/esm-framework';
4
+ import { formatDate, formatDatetime, openmrsFetch, usePagination } from '@openmrs/esm-framework';
5
5
  import { mockEnrolledProgramsResponse } from '../../../../__mocks__/programs.mock';
6
6
  import { swrRender, waitForLoadingToFinish } from '../../../../tools/test-helpers';
7
7
  import { launchPatientWorkspace } from '@openmrs/esm-patient-common-lib';
8
8
  import ProgramsDetailedSummary from './programs-detailed-summary.component';
9
+ import dayjs from 'dayjs';
9
10
 
10
11
  const mockOpenmrsFetch = openmrsFetch as jest.Mock;
12
+ const mockUsePagination = usePagination as jest.Mock;
13
+ const mockFormatDate = formatDate as jest.Mock;
14
+ const mockFormatDateTime = formatDatetime as jest.Mock;
15
+
16
+ jest.mock('@openmrs/esm-framework', () => {
17
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
18
+
19
+ return {
20
+ ...originalModule,
21
+ formatDate: jest.fn(),
22
+ formatDatetime: jest.fn(),
23
+ openmrsFetch: jest.fn(),
24
+ usePagination: jest.fn().mockImplementation(() => ({
25
+ currentPage: 1,
26
+ goTo: () => {},
27
+ results: [],
28
+ })),
29
+ };
30
+ });
11
31
 
12
32
  jest.mock('@openmrs/esm-patient-common-lib', () => {
13
33
  const originalModule = jest.requireActual('@openmrs/esm-patient-common-lib');
@@ -56,6 +76,13 @@ describe('ProgramsDetailedSummary ', () => {
56
76
 
57
77
  it("renders a detailed tabular summary of the patient's program enrollments", async () => {
58
78
  mockOpenmrsFetch.mockReturnValueOnce({ data: { results: mockEnrolledProgramsResponse } });
79
+ mockUsePagination.mockImplementation(() => ({
80
+ currentPage: 1,
81
+ goTo: () => {},
82
+ results: mockEnrolledProgramsResponse.slice(0, 5),
83
+ }));
84
+ mockFormatDate.mockImplementation((dateTime) => dayjs(dateTime).format('MMM-YYYY'));
85
+ mockFormatDateTime.mockImplementation((dateTime) => dayjs(dateTime).format('DD-MMM-YYYY HH:mm'));
59
86
 
60
87
  renderProgramsOverview();
61
88
 
@@ -6,23 +6,21 @@ import includes from 'lodash-es/includes';
6
6
  import map from 'lodash-es/map';
7
7
  import styles from './programs-form.scss';
8
8
  import { useTranslation } from 'react-i18next';
9
+ import { createErrorHandler, showNotification, showToast, useSessionUser, useLocations } from '@openmrs/esm-framework';
9
10
  import {
10
- createErrorHandler,
11
- detach,
12
- showNotification,
13
- showToast,
14
- useSessionUser,
15
- useLocations,
16
- } from '@openmrs/esm-framework';
17
- import { Button, DatePicker, DatePickerInput, Select, SelectItem, Form, FormGroup } from 'carbon-components-react';
11
+ Button,
12
+ DatePicker,
13
+ DatePickerInput,
14
+ Select,
15
+ SelectItem,
16
+ Form,
17
+ FormGroup,
18
+ ButtonSet,
19
+ } from 'carbon-components-react';
18
20
  import { createProgramEnrollment, useAvailablePrograms, useEnrollments } from './programs.resource';
21
+ import { DefaultWorkspaceProps } from '@openmrs/esm-patient-common-lib';
19
22
 
20
- interface ProgramsFormProps {
21
- patientUuid: string;
22
- isTablet: boolean;
23
- }
24
-
25
- const ProgramsForm: React.FC<ProgramsFormProps> = ({ patientUuid, isTablet }) => {
23
+ const ProgramsForm: React.FC<DefaultWorkspaceProps> = ({ closeWorkspace, patientUuid, isTablet }) => {
26
24
  const { t } = useTranslation();
27
25
  const session = useSessionUser();
28
26
  const availableLocations = useLocations();
@@ -44,8 +42,6 @@ const ProgramsForm: React.FC<ProgramsFormProps> = ({ patientUuid, isTablet }) =>
44
42
  setUserLocation(session?.sessionLocation?.uuid);
45
43
  }
46
44
 
47
- const closeWorkspace = React.useCallback(() => detach('patient-chart-workspace-slot', 'programs-form-workspace'), []);
48
-
49
45
  const handleSubmit = React.useCallback(
50
46
  (event: SyntheticEvent<HTMLFormElement>) => {
51
47
  event.preventDefault();
@@ -95,89 +91,83 @@ const ProgramsForm: React.FC<ProgramsFormProps> = ({ patientUuid, isTablet }) =>
95
91
  );
96
92
 
97
93
  return (
98
- <Form style={{ margin: '2rem' }} onSubmit={handleSubmit}>
99
- <FormGroup style={{ width: '50%' }} legendText={t('program', 'Program')}>
100
- <div className={styles.selectContainer}>
94
+ <Form className={styles.form} onSubmit={handleSubmit}>
95
+ <div className={styles.formContainer}>
96
+ <FormGroup style={{ width: '50%' }} legendText={t('program', 'Program')}>
97
+ <div className={styles.selectContainer}>
98
+ <Select
99
+ id="program"
100
+ invalidText={t('required', 'Required')}
101
+ labelText=""
102
+ light={isTablet}
103
+ onChange={(event) => setSelectedProgram(event.target.value)}
104
+ >
105
+ {!selectedProgram ? <SelectItem text={t('chooseProgram', 'Choose a program')} value="" /> : null}
106
+ {eligiblePrograms?.length > 0 &&
107
+ eligiblePrograms.map((program) => (
108
+ <SelectItem key={program.uuid} text={program.display} value={program.uuid}>
109
+ {program.display}
110
+ </SelectItem>
111
+ ))}
112
+ </Select>
113
+ </div>
114
+ </FormGroup>
115
+ <FormGroup legendText={t('dateEnrolled', 'Date enrolled')}>
116
+ <DatePicker
117
+ id="enrollmentDate"
118
+ datePickerType="single"
119
+ dateFormat="d/m/Y"
120
+ light={isTablet}
121
+ maxDate={new Date().toISOString()}
122
+ placeholder="dd/mm/yyyy"
123
+ onChange={([date]) => setEnrollmentDate(date)}
124
+ value={enrollmentDate}
125
+ >
126
+ <DatePickerInput id="enrollmentDateInput" labelText="" />
127
+ </DatePicker>
128
+ </FormGroup>
129
+ <FormGroup legendText={t('dateCompleted', 'Date completed')}>
130
+ <DatePicker
131
+ id="completionDate"
132
+ datePickerType="single"
133
+ dateFormat="d/m/Y"
134
+ light={isTablet}
135
+ minDate={new Date(enrollmentDate).toISOString()}
136
+ maxDate={new Date().toISOString()}
137
+ placeholder="dd/mm/yyyy"
138
+ onChange={([date]) => setCompletionDate(date)}
139
+ value={completionDate}
140
+ >
141
+ <DatePickerInput id="completionDateInput" labelText="" />
142
+ </DatePicker>
143
+ </FormGroup>
144
+ <FormGroup style={{ width: '50%' }} legendText={t('enrollmentLocation', 'Enrollment location')}>
101
145
  <Select
102
- id="program"
103
- invalidText={t('required', 'Required')}
146
+ id="location"
147
+ invalidText="Required"
104
148
  labelText=""
105
149
  light={isTablet}
106
- onChange={(event) => setSelectedProgram(event.target.value)}
150
+ onChange={(event) => setUserLocation(event.target.value)}
151
+ value={userLocation}
107
152
  >
108
- {!selectedProgram ? <SelectItem text={t('chooseProgram', 'Choose a program')} value="" /> : null}
109
- {eligiblePrograms?.length > 0 &&
110
- eligiblePrograms.map((program) => (
111
- <SelectItem key={program.uuid} text={program.display} value={program.uuid}>
112
- {program.display}
153
+ {!userLocation ? <SelectItem text={t('chooseLocation', 'Choose a location')} value="" /> : null}
154
+ {availableLocations?.length > 0 &&
155
+ availableLocations.map((location) => (
156
+ <SelectItem key={location.uuid} text={location.display} value={location.uuid}>
157
+ {location.display}
113
158
  </SelectItem>
114
159
  ))}
115
160
  </Select>
116
- </div>
117
- {availablePrograms?.length &&
118
- eligiblePrograms?.length === 0 &&
119
- showNotification({
120
- title: t('error', 'Error'),
121
- kind: 'error',
122
- critical: true,
123
- description: t('alreadyEnrolledText', 'This patient is already enrolled in all of the available programs'),
124
- })}
125
- </FormGroup>
126
- <FormGroup legendText={t('dateEnrolled', 'Date enrolled')}>
127
- <DatePicker
128
- id="enrollmentDate"
129
- datePickerType="single"
130
- dateFormat="d/m/Y"
131
- light={isTablet}
132
- maxDate={new Date().toISOString()}
133
- placeholder="dd/mm/yyyy"
134
- onChange={([date]) => setEnrollmentDate(date)}
135
- value={enrollmentDate}
136
- >
137
- <DatePickerInput id="enrollmentDateInput" labelText="" />
138
- </DatePicker>
139
- </FormGroup>
140
- <FormGroup legendText={t('dateCompleted', 'Date completed')}>
141
- <DatePicker
142
- id="completionDate"
143
- datePickerType="single"
144
- dateFormat="d/m/Y"
145
- light={isTablet}
146
- minDate={new Date(enrollmentDate).toISOString()}
147
- maxDate={new Date().toISOString()}
148
- placeholder="dd/mm/yyyy"
149
- onChange={([date]) => setCompletionDate(date)}
150
- value={completionDate}
151
- >
152
- <DatePickerInput id="completionDateInput" labelText="" />
153
- </DatePicker>
154
- </FormGroup>
155
- <FormGroup style={{ width: '50%' }} legendText={t('enrollmentLocation', 'Enrollment location')}>
156
- <Select
157
- id="location"
158
- invalidText="Required"
159
- labelText=""
160
- light={isTablet}
161
- onChange={(event) => setUserLocation(event.target.value)}
162
- value={userLocation}
163
- >
164
- {!userLocation ? <SelectItem text={t('chooseLocation', 'Choose a location')} value="" /> : null}
165
- {availableLocations?.length > 0 &&
166
- availableLocations.map((location) => (
167
- <SelectItem key={location.uuid} text={location.display} value={location.uuid}>
168
- {location.display}
169
- </SelectItem>
170
- ))}
171
- </Select>
172
- </FormGroup>
173
- <div style={{ marginTop: '1.625rem' }}>
174
- <Button style={{ width: '50%' }} kind="secondary" type="button" onClick={closeWorkspace}>
161
+ </FormGroup>
162
+ </div>
163
+ <ButtonSet className={isTablet ? styles.tablet : styles.desktop}>
164
+ <Button className={styles.button} kind="secondary" onClick={closeWorkspace}>
175
165
  {t('cancel', 'Cancel')}
176
166
  </Button>
177
- <Button style={{ width: '50%' }} kind="primary" type="submit" disabled={!selectedProgram}>
178
- {t('enroll', 'Enroll')}
167
+ <Button className={styles.button} kind="primary" disabled={!selectedProgram} type="submit">
168
+ {t('saveAndClose', 'Save and close')}
179
169
  </Button>
180
- </div>
170
+ </ButtonSet>
181
171
  </Form>
182
172
  );
183
173
  };
@@ -2,12 +2,6 @@
2
2
  @import "~carbon-components/src/globals/scss/vars";
3
3
  @import "~carbon-components/src/globals/scss/mixins";
4
4
 
5
- .alreadyEnrolledText {
6
- @include carbon--type-style("body-short-01");
7
- color: $text-02;
8
- min-height: 1rem;
9
- }
10
-
11
5
  .selectContainer {
12
6
  display: flex;
13
7
  align-items: center;
@@ -17,4 +11,46 @@
17
11
  margin-left: 1rem;
18
12
  width: 2rem;
19
13
  min-height: 2.5rem;
14
+ }
15
+
16
+
17
+ .button {
18
+ height: 4rem;
19
+ display: flex;
20
+ align-content: flex-start;
21
+ align-items: baseline;
22
+ min-width: 50%;
23
+ }
24
+
25
+ .tablet {
26
+ padding: 1.5rem 1rem;
27
+ background-color: $ui-02;
28
+ }
29
+
30
+ .desktop {
31
+ padding: 0rem;
32
+ }
33
+
34
+ .formContainer {
35
+ margin: 1rem;
36
+ }
37
+
38
+ .form {
39
+ display: flex;
40
+ flex-direction: column;
41
+ justify-content: space-between;
42
+ }
43
+
44
+ /* Desktop */
45
+ :global(.omrs-breakpoint-gt-tablet) {
46
+ .form {
47
+ height: calc(100vh - 6rem);
48
+ }
49
+ }
50
+
51
+ /* Tablet */
52
+ :global(.omrs-breakpoint-lt-desktop) {
53
+ .form {
54
+ height: calc(100vh - 3rem);
55
+ }
20
56
  }
@@ -4,14 +4,7 @@ import * as SWR from 'swr';
4
4
  import { throwError } from 'rxjs';
5
5
  import { of } from 'rxjs/internal/observable/of';
6
6
  import { fireEvent, render, screen } from '@testing-library/react';
7
- import {
8
- createErrorHandler,
9
- detach,
10
- openmrsFetch,
11
- showNotification,
12
- showToast,
13
- useLocations,
14
- } from '@openmrs/esm-framework';
7
+ import { createErrorHandler, openmrsFetch, showNotification, showToast, useLocations } from '@openmrs/esm-framework';
15
8
  import { mockPatient } from '../../../../__mocks__/patient.mock';
16
9
  import {
17
10
  mockCareProgramsResponse,
@@ -24,15 +17,14 @@ import ProgramsForm from './programs-form.component';
24
17
  const testProps = {
25
18
  patientUuid: mockPatient.id,
26
19
  isTablet: false,
20
+ closeWorkspace: jest.fn(),
27
21
  };
28
22
 
29
23
  const mockCreateErrorHandler = createErrorHandler as jest.Mock;
30
24
  const mockCreateProgramEnrollment = createProgramEnrollment as jest.Mock;
31
- const mockDetach = detach as jest.Mock;
32
25
  const mockOpenmrsFetch = openmrsFetch as jest.Mock;
33
26
  const mockShowNotification = showNotification as jest.Mock;
34
27
  const mockShowToast = showToast as jest.Mock;
35
- const mockUseLocations = useLocations as jest.Mock;
36
28
 
37
29
  jest.mock('@openmrs/esm-framework', () => {
38
30
  const originalModule = jest.requireActual('@openmrs/esm-framework');
@@ -40,7 +32,6 @@ jest.mock('@openmrs/esm-framework', () => {
40
32
  return {
41
33
  ...originalModule,
42
34
  createErrorHandler: jest.fn(),
43
- detach: jest.fn(),
44
35
  showNotification: jest.fn(),
45
36
  showToast: jest.fn(),
46
37
  useLocations: jest.fn().mockImplementation(() => mockLocationsResponse),
@@ -78,7 +69,7 @@ describe('ProgramsForm: ', () => {
78
69
  expect(screen.getByRole('option', { name: /Laboratory/i })).toBeInTheDocument();
79
70
 
80
71
  const cancelButton = screen.getByRole('button', { name: /cancel/i });
81
- const enrollButton = screen.getByRole('button', { name: /enroll/i });
72
+ const enrollButton = screen.getByRole('button', { name: /save and close/i });
82
73
  expect(cancelButton).toBeInTheDocument();
83
74
  expect(enrollButton).toBeInTheDocument();
84
75
  expect(enrollButton).toBeDisabled();
@@ -90,8 +81,7 @@ describe('ProgramsForm: ', () => {
90
81
  const cancelButton = screen.getByRole('button', { name: /Cancel/i });
91
82
  fireEvent.click(cancelButton);
92
83
 
93
- expect(mockDetach).toHaveBeenCalledTimes(1);
94
- expect(mockDetach).toHaveBeenCalledWith('patient-chart-workspace-slot', 'programs-form-workspace');
84
+ expect(testProps.closeWorkspace).toHaveBeenCalledTimes(1);
95
85
  });
96
86
 
97
87
  it('renders a light background for date inputs in the tablet viewport ', () => {
@@ -116,7 +106,7 @@ describe('ProgramsForm: ', () => {
116
106
  renderProgramsForm();
117
107
 
118
108
  cancelButton = screen.getByRole('button', { name: /cancel/i });
119
- enrollButton = screen.getByRole('button', { name: /enroll/i });
109
+ enrollButton = screen.getByRole('button', { name: /save and close/i });
120
110
  enrollmentDateInput = screen.getAllByRole('textbox', { name: '' })[0];
121
111
  selectLocationInput = screen.getAllByRole('combobox', { name: '' })[1];
122
112
  selectProgramInput = screen.getAllByRole('combobox', { name: '' })[0];