@openmrs/esm-patient-task-list-app 12.1.1-pre.10907
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/.turbo/turbo-build.log +7 -0
- package/README.md +28 -0
- package/dist/105.js +1 -0
- package/dist/105.js.map +1 -0
- package/dist/117.js +1 -0
- package/dist/117.js.map +1 -0
- package/dist/149.js +1 -0
- package/dist/149.js.map +1 -0
- package/dist/229.js +43 -0
- package/dist/229.js.map +1 -0
- package/dist/304.js +1 -0
- package/dist/304.js.map +1 -0
- package/dist/339.js +1 -0
- package/dist/339.js.map +1 -0
- package/dist/378.js +1 -0
- package/dist/378.js.map +1 -0
- package/dist/396.js +1 -0
- package/dist/396.js.map +1 -0
- package/dist/409.js +6 -0
- package/dist/409.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/61.js +1 -0
- package/dist/61.js.map +1 -0
- package/dist/66.js +1 -0
- package/dist/66.js.map +1 -0
- package/dist/697.js +1 -0
- package/dist/697.js.map +1 -0
- package/dist/712.js +1 -0
- package/dist/712.js.map +1 -0
- package/dist/720.js +1 -0
- package/dist/720.js.map +1 -0
- package/dist/752.js +1 -0
- package/dist/752.js.map +1 -0
- package/dist/771.js +1 -0
- package/dist/771.js.map +1 -0
- package/dist/789.js +1 -0
- package/dist/789.js.map +1 -0
- package/dist/989.js +1 -0
- package/dist/989.js.map +1 -0
- package/dist/main.js +6 -0
- package/dist/main.js.map +1 -0
- package/dist/openmrs-esm-patient-task-list-app.js +6 -0
- package/dist/openmrs-esm-patient-task-list-app.js.buildmanifest.json +651 -0
- package/dist/openmrs-esm-patient-task-list-app.js.map +1 -0
- package/dist/routes.json +1 -0
- package/jest.config.js +3 -0
- package/package.json +61 -0
- package/rspack.config.js +1 -0
- package/src/config-schema.ts +13 -0
- package/src/declarations.d.ts +3 -0
- package/src/index.ts +25 -0
- package/src/launch-button/task-list-launch-button.extension.tsx +20 -0
- package/src/loader/loader.component.tsx +10 -0
- package/src/loader/loader.scss +9 -0
- package/src/routes.json +28 -0
- package/src/types.d.ts +9 -0
- package/src/workspace/add-task-form.component.tsx +609 -0
- package/src/workspace/add-task-form.scss +49 -0
- package/src/workspace/add-task-form.test.tsx +615 -0
- package/src/workspace/delete-task.modal.test.tsx +99 -0
- package/src/workspace/delete-task.modal.tsx +71 -0
- package/src/workspace/delete-task.scss +7 -0
- package/src/workspace/task-details-view.component.tsx +212 -0
- package/src/workspace/task-details-view.scss +61 -0
- package/src/workspace/task-details-view.test.tsx +408 -0
- package/src/workspace/task-list-view.component.tsx +154 -0
- package/src/workspace/task-list-view.scss +111 -0
- package/src/workspace/task-list-view.test.tsx +246 -0
- package/src/workspace/task-list.resource.ts +543 -0
- package/src/workspace/task-list.scss +37 -0
- package/src/workspace/task-list.workspace.test.tsx +135 -0
- package/src/workspace/task-list.workspace.tsx +99 -0
- package/translations/en.json +66 -0
- package/tsconfig.json +4 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
5
|
+
import { useTaskList, setTaskStatusCompleted, type Task } from './task-list.resource';
|
|
6
|
+
import TaskListView from './task-list-view.component';
|
|
7
|
+
|
|
8
|
+
jest.mock('./task-list.resource', () => ({
|
|
9
|
+
useTaskList: jest.fn(),
|
|
10
|
+
setTaskStatusCompleted: jest.fn(),
|
|
11
|
+
getPriorityLabel: jest.fn((priority) => priority),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const mockUseTaskList = jest.mocked(useTaskList);
|
|
15
|
+
const mockSetTaskStatusCompleted = jest.mocked(setTaskStatusCompleted);
|
|
16
|
+
const mockShowSnackbar = jest.mocked(showSnackbar);
|
|
17
|
+
|
|
18
|
+
const patientUuid = 'patient-uuid-123';
|
|
19
|
+
|
|
20
|
+
const baseTasks: Task[] = [
|
|
21
|
+
{
|
|
22
|
+
uuid: 'task-1',
|
|
23
|
+
name: 'Check vitals',
|
|
24
|
+
status: 'not-started',
|
|
25
|
+
createdDate: new Date('2024-01-15T10:00:00Z'),
|
|
26
|
+
completed: false,
|
|
27
|
+
createdBy: 'Dr. Smith',
|
|
28
|
+
assignee: { uuid: 'provider-1', display: 'Nurse Johnson', type: 'person' },
|
|
29
|
+
priority: 'high',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
uuid: 'task-2',
|
|
33
|
+
name: 'Order labs',
|
|
34
|
+
status: 'not-started',
|
|
35
|
+
createdDate: new Date('2024-01-15T11:00:00Z'),
|
|
36
|
+
completed: false,
|
|
37
|
+
createdBy: 'Dr. Smith',
|
|
38
|
+
rationale: 'Patient reported symptoms',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
describe('TaskListView', () => {
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
jest.useFakeTimers();
|
|
45
|
+
jest.setSystemTime(new Date('2024-01-15T12:00:00Z'));
|
|
46
|
+
jest.clearAllMocks();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
jest.useRealTimers();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('renders loading state', () => {
|
|
54
|
+
mockUseTaskList.mockReturnValue({
|
|
55
|
+
tasks: [],
|
|
56
|
+
isLoading: true,
|
|
57
|
+
error: null,
|
|
58
|
+
mutate: jest.fn(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
62
|
+
|
|
63
|
+
expect(screen.queryByText(/no tasks/i)).not.toBeInTheDocument();
|
|
64
|
+
expect(screen.queryByText(/problem loading/i)).not.toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('renders error state', () => {
|
|
68
|
+
mockUseTaskList.mockReturnValue({
|
|
69
|
+
tasks: [],
|
|
70
|
+
isLoading: false,
|
|
71
|
+
error: new Error('Network error'),
|
|
72
|
+
mutate: jest.fn(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
76
|
+
|
|
77
|
+
expect(screen.getByText(/problem loading the task list/i)).toBeInTheDocument();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('renders empty state when there are no tasks', () => {
|
|
81
|
+
mockUseTaskList.mockReturnValue({
|
|
82
|
+
tasks: [],
|
|
83
|
+
isLoading: false,
|
|
84
|
+
error: null,
|
|
85
|
+
mutate: jest.fn(),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
89
|
+
|
|
90
|
+
expect(screen.getByText(/no tasks/i)).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('renders task items with name, assignee, and priority', () => {
|
|
94
|
+
mockUseTaskList.mockReturnValue({
|
|
95
|
+
tasks: baseTasks,
|
|
96
|
+
isLoading: false,
|
|
97
|
+
error: null,
|
|
98
|
+
mutate: jest.fn(),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
102
|
+
|
|
103
|
+
expect(screen.getByText('Check vitals')).toBeInTheDocument();
|
|
104
|
+
expect(screen.getByText('Nurse Johnson')).toBeInTheDocument();
|
|
105
|
+
expect(screen.getByText('high')).toBeInTheDocument();
|
|
106
|
+
|
|
107
|
+
expect(screen.getByText('Order labs')).toBeInTheDocument();
|
|
108
|
+
expect(screen.getByText('Patient reported symptoms')).toBeInTheDocument();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('shows "No assignment" when task has no assignee', () => {
|
|
112
|
+
mockUseTaskList.mockReturnValue({
|
|
113
|
+
tasks: [baseTasks[1]],
|
|
114
|
+
isLoading: false,
|
|
115
|
+
error: null,
|
|
116
|
+
mutate: jest.fn(),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
120
|
+
|
|
121
|
+
expect(screen.getByText(/no assignment/i)).toBeInTheDocument();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('shows overdue tag for tasks past their due date', () => {
|
|
125
|
+
const overdueTask: Task = {
|
|
126
|
+
...baseTasks[0],
|
|
127
|
+
dueDate: {
|
|
128
|
+
type: 'DATE',
|
|
129
|
+
date: new Date('2024-01-10T00:00:00Z'),
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
mockUseTaskList.mockReturnValue({
|
|
134
|
+
tasks: [overdueTask],
|
|
135
|
+
isLoading: false,
|
|
136
|
+
error: null,
|
|
137
|
+
mutate: jest.fn(),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
141
|
+
|
|
142
|
+
expect(screen.getByText(/overdue/i)).toBeInTheDocument();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('does not show overdue tag for completed tasks', () => {
|
|
146
|
+
const completedOverdueTask: Task = {
|
|
147
|
+
...baseTasks[0],
|
|
148
|
+
completed: true,
|
|
149
|
+
dueDate: {
|
|
150
|
+
type: 'DATE',
|
|
151
|
+
date: new Date('2024-01-10T00:00:00Z'),
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
mockUseTaskList.mockReturnValue({
|
|
156
|
+
tasks: [completedOverdueTask],
|
|
157
|
+
isLoading: false,
|
|
158
|
+
error: null,
|
|
159
|
+
mutate: jest.fn(),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
163
|
+
|
|
164
|
+
expect(screen.queryByText(/overdue/i)).not.toBeInTheDocument();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('does not show overdue tag for tasks without a due date', () => {
|
|
168
|
+
mockUseTaskList.mockReturnValue({
|
|
169
|
+
tasks: [baseTasks[0]],
|
|
170
|
+
isLoading: false,
|
|
171
|
+
error: null,
|
|
172
|
+
mutate: jest.fn(),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
176
|
+
|
|
177
|
+
expect(screen.queryByText(/overdue/i)).not.toBeInTheDocument();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('calls onTaskClick when a task is clicked', async () => {
|
|
181
|
+
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
|
|
182
|
+
const mockOnTaskClick = jest.fn();
|
|
183
|
+
|
|
184
|
+
mockUseTaskList.mockReturnValue({
|
|
185
|
+
tasks: [baseTasks[0]],
|
|
186
|
+
isLoading: false,
|
|
187
|
+
error: null,
|
|
188
|
+
mutate: jest.fn(),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
render(<TaskListView patientUuid={patientUuid} onTaskClick={mockOnTaskClick} />);
|
|
192
|
+
|
|
193
|
+
await user.click(screen.getByText('Check vitals'));
|
|
194
|
+
|
|
195
|
+
expect(mockOnTaskClick).toHaveBeenCalledWith(baseTasks[0]);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('toggles task completion via checkbox', async () => {
|
|
199
|
+
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
|
|
200
|
+
const mockMutate = jest.fn();
|
|
201
|
+
mockSetTaskStatusCompleted.mockResolvedValue({} as any);
|
|
202
|
+
|
|
203
|
+
mockUseTaskList.mockReturnValue({
|
|
204
|
+
tasks: [baseTasks[0]],
|
|
205
|
+
isLoading: false,
|
|
206
|
+
error: null,
|
|
207
|
+
mutate: mockMutate,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
211
|
+
|
|
212
|
+
const checkbox = screen.getByRole('checkbox');
|
|
213
|
+
await user.click(checkbox);
|
|
214
|
+
|
|
215
|
+
await waitFor(() => {
|
|
216
|
+
expect(mockSetTaskStatusCompleted).toHaveBeenCalledWith(patientUuid, baseTasks[0], true);
|
|
217
|
+
});
|
|
218
|
+
expect(mockMutate).toHaveBeenCalled();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('shows error snackbar when toggle fails', async () => {
|
|
222
|
+
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
|
|
223
|
+
mockSetTaskStatusCompleted.mockRejectedValue(new Error('Network error'));
|
|
224
|
+
|
|
225
|
+
mockUseTaskList.mockReturnValue({
|
|
226
|
+
tasks: [baseTasks[0]],
|
|
227
|
+
isLoading: false,
|
|
228
|
+
error: null,
|
|
229
|
+
mutate: jest.fn(),
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
render(<TaskListView patientUuid={patientUuid} />);
|
|
233
|
+
|
|
234
|
+
const checkbox = screen.getByRole('checkbox');
|
|
235
|
+
await user.click(checkbox);
|
|
236
|
+
|
|
237
|
+
await waitFor(() => {
|
|
238
|
+
expect(mockShowSnackbar).toHaveBeenCalledWith(
|
|
239
|
+
expect.objectContaining({
|
|
240
|
+
kind: 'error',
|
|
241
|
+
title: 'Unable to update task',
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
});
|