@openmrs/esm-appointments-app 4.3.1-pre.1421 → 4.3.1-pre.1443

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.
@@ -0,0 +1,146 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { useUnscheduledAppointments } from '../../hooks/useUnscheduledAppointments';
4
+ import { downloadUnscheduledAppointments } from '../../helpers/excel';
5
+ import { usePagination } from '@openmrs/esm-framework';
6
+ import UnscheduledAppointments from './unscheduled-appointments.component';
7
+
8
+ const mockUsePagination = usePagination as jest.Mock;
9
+ const mockGoToPage = jest.fn();
10
+ const mockDownloadAppointmentsAsExcel = downloadUnscheduledAppointments as jest.Mock;
11
+ const mockUseUnscheduledAppointments = useUnscheduledAppointments as jest.Mock;
12
+
13
+ jest.mock('../../helpers/excel');
14
+ jest.mock('../../hooks/useOverlay');
15
+ jest.mock('../../hooks/useUnscheduledAppointments');
16
+
17
+ jest.mock('@openmrs/esm-framework', () => {
18
+ const originalModule = jest.requireActual('@openmrs/esm-framework');
19
+ return {
20
+ ...originalModule,
21
+ openmrsFetch: jest.fn(),
22
+ };
23
+ });
24
+
25
+ describe('UnscheduledAppointments component', () => {
26
+ const mockUnscheduledAppointments = [
27
+ {
28
+ uuid: '1234',
29
+ name: 'Test Patient',
30
+ identifier: '1234-56-78',
31
+ gender: 'M',
32
+ phoneNumber: '123-456-7890',
33
+ age: 20,
34
+ dob: 1262304000,
35
+ },
36
+ {
37
+ uuid: '5678',
38
+ name: 'Another Patient',
39
+ identifier: '2345-67-89',
40
+ gender: 'F',
41
+ phoneNumber: '',
42
+ age: 30,
43
+ dob: 1262304000,
44
+ },
45
+ ];
46
+
47
+ it('renders the component correctly', async () => {
48
+ mockUseUnscheduledAppointments.mockReturnValue({
49
+ isLoading: false,
50
+ data: mockUnscheduledAppointments,
51
+ error: null,
52
+ });
53
+
54
+ mockUsePagination.mockReturnValue({
55
+ results: mockUnscheduledAppointments.slice(0, 2),
56
+ goTo: mockGoToPage,
57
+ currentPage: 1,
58
+ });
59
+ render(<UnscheduledAppointments />);
60
+ const header = screen.getByText('Unscheduled appointments 2');
61
+ expect(header).toBeInTheDocument();
62
+
63
+ const patientName = await screen.findByText('Test Patient');
64
+ expect(patientName).toBeInTheDocument();
65
+
66
+ const identifier = screen.getByText('1234-56-78');
67
+ expect(identifier).toBeInTheDocument();
68
+
69
+ const gender = screen.getByText('Male');
70
+ expect(gender).toBeInTheDocument();
71
+
72
+ const phoneNumber = screen.getByText('123-456-7890');
73
+ expect(phoneNumber).toBeInTheDocument();
74
+ });
75
+
76
+ it('allows the user to search for appointments', async () => {
77
+ mockUseUnscheduledAppointments.mockReturnValue({
78
+ isLoading: false,
79
+ data: mockUnscheduledAppointments,
80
+ error: null,
81
+ });
82
+
83
+ mockUsePagination.mockReturnValue({
84
+ results: mockUnscheduledAppointments.slice(0, 2),
85
+ goTo: mockGoToPage,
86
+ currentPage: 1,
87
+ });
88
+ render(<UnscheduledAppointments />);
89
+
90
+ const searchInput = await screen.findByRole('searchbox');
91
+ fireEvent.change(searchInput, { target: { value: 'Another' } });
92
+
93
+ const patientName = screen.getByText('Another Patient');
94
+ expect(patientName).toBeInTheDocument();
95
+
96
+ const identifier = screen.getByText('2345-67-89');
97
+ expect(identifier).toBeInTheDocument();
98
+
99
+ const gender = screen.getByText('Female');
100
+ expect(gender).toBeInTheDocument();
101
+
102
+ const phoneNumber = screen.getByText('--');
103
+ expect(phoneNumber).toBeInTheDocument();
104
+ });
105
+
106
+ it('allows the user to download a list of unscheduled appointments', async () => {
107
+ mockUseUnscheduledAppointments.mockReturnValue({
108
+ isLoading: false,
109
+ data: mockUnscheduledAppointments,
110
+ error: null,
111
+ });
112
+
113
+ mockUsePagination.mockReturnValue({
114
+ results: mockUnscheduledAppointments.slice(0, 2),
115
+ goTo: mockGoToPage,
116
+ currentPage: 1,
117
+ });
118
+ render(<UnscheduledAppointments />);
119
+
120
+ const downloadButton = await screen.findByText('Download');
121
+ expect(downloadButton).toBeInTheDocument();
122
+
123
+ fireEvent.click(downloadButton);
124
+
125
+ expect(mockDownloadAppointmentsAsExcel).toHaveBeenCalledWith(mockUnscheduledAppointments);
126
+ });
127
+
128
+ it('renders a message if there are no unscheduled appointments', async () => {
129
+ mockUseUnscheduledAppointments.mockReturnValue({
130
+ isLoading: false,
131
+ data: [],
132
+ error: null,
133
+ });
134
+
135
+ mockUsePagination.mockReturnValue({
136
+ results: mockUnscheduledAppointments.slice(0, 2),
137
+ goTo: mockGoToPage,
138
+ currentPage: 1,
139
+ });
140
+
141
+ render(<UnscheduledAppointments />);
142
+
143
+ const emptyState = await screen.findByText('There are no unscheduled appointments to display');
144
+ expect(emptyState).toBeInTheDocument();
145
+ });
146
+ });
@@ -19,21 +19,18 @@ export function getPageSizes<T>(data: Array<T>, pageSize: number = 10): Array<nu
19
19
  }
20
20
 
21
21
  /**
22
- * Returns a filtered array of appointments based on a search string.
23
- * If the search string is empty or null, the original appointments array is returned.
24
- *
25
- * @param {Array<MappedAppointment>} appointments The array of appointments to search.
26
- * @param {string} searchString The search string to use for filtering.
27
- * @returns {Array<MappedAppointment>} The filtered array of appointments.
28
- */
29
- export function useSearchResults(
30
- appointments: Array<MappedAppointment>,
31
- searchString: string,
32
- ): Array<MappedAppointment> {
22
+
23
+ Returns an array of filtered data that contains search string
24
+ @template T
25
+ @param {T[]} data - The array of data to filter
26
+ @param {string} searchString - The string to search for in the data
27
+ @returns {T[]} The filtered array of data
28
+ */
29
+ export function useSearchResults<T>(data: T[], searchString: string): T[] {
33
30
  const searchResults = useMemo(() => {
34
31
  if (searchString && searchString.trim() !== '') {
35
32
  const search = searchString.toLowerCase();
36
- return appointments.filter((appointment) =>
33
+ return data.filter((appointment) =>
37
34
  Object.entries(appointment).some(([header, value]) => {
38
35
  if (header === 'patientUuid') {
39
36
  return false;
@@ -43,8 +40,8 @@ export function useSearchResults(
43
40
  );
44
41
  }
45
42
 
46
- return appointments;
47
- }, [searchString, appointments]);
43
+ return data;
44
+ }, [searchString, data]);
48
45
 
49
46
  return searchResults;
50
47
  }
@@ -27,7 +27,7 @@ const MonthlyWorkload = ({ type, dateTime, currentDate, events }) => {
27
27
  } ${layout === 'small-desktop' ? styles.smallDesktop : styles.largeDesktop}`}>
28
28
  {type === 'monthly' && isSameMonth(dateTime, currentDate) && (
29
29
  <p>
30
- <b>{dateTime.format('D')}</b>
30
+ <b className={styles.calendarDate}>{dateTime.format('D')}</b>
31
31
  {currentData?.service && (
32
32
  <div className={styles.currentData}>
33
33
  {currentData.service.map(({ serviceName, count }) => (
@@ -36,7 +36,7 @@ const MonthlyWorkload = ({ type, dateTime, currentDate, events }) => {
36
36
  role="button"
37
37
  tabIndex={0}
38
38
  onClick={() => serviceAreaOnClick(serviceName)}
39
- className={`${styles.serviceArea} ${styles[colorCoding[serviceName]]}`}>
39
+ className={`${styles.serviceArea}`}>
40
40
  <span>{serviceName}</span>
41
41
  <span>{count}</span>
42
42
  </div>
@@ -77,24 +77,22 @@
77
77
 
78
78
  .currentData {
79
79
  margin: 2px;
80
+ padding-top: 2px;
81
+ background-color: colors.$gray-10;
80
82
 
81
83
  & > span {
82
84
  @include type.type-style('label-02');
83
85
  }
84
86
  }
85
87
 
86
- .greyBackground {
87
- background-color: colors.$gray-10;
88
- }
89
-
90
88
  .serviceArea {
91
89
  padding: 0.0125rem;
92
90
  display: grid;
93
91
  grid-template-columns: 4fr 1fr;
94
92
  justify-content: flex-start;
95
93
  text-align: left;
96
- margin: 0.0125rem;
97
- @include type.type-style('label-02');
94
+ margin: 0.125rem;
95
+ @include type.type-style('label-01');
98
96
  color: #020f1b;
99
97
  cursor: pointer;
100
98
 
@@ -144,3 +142,7 @@
144
142
  color: colors.$white;
145
143
  font-weight: bold;
146
144
  }
145
+
146
+ .calendarDate {
147
+ @include type.type-style('heading-compact-01');
148
+ }
@@ -84,6 +84,8 @@
84
84
 
85
85
  .serviceContainer {
86
86
  display: flex;
87
+ align-items: center;
88
+ width: max-content;
87
89
  }
88
90
 
89
91
  .identifier {
@@ -76,7 +76,6 @@
76
76
  "monthly": "Monthly",
77
77
  "name": "Name",
78
78
  "next": "Next",
79
- "nextAppointmentDate": "Next appointment date",
80
79
  "noAppointmentsScheduledForTodayToDisplay": "There are no appointments scheduled for today to display for this location",
81
80
  "noAppointmentsToDisplay": "There are no appointments to display for this location",
82
81
  "noEncountersFound": "No encounters found",