@openmrs/esm-patient-lists-app 11.3.1-pre.9452 → 11.3.1-pre.9455

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.
@@ -1,50 +1,56 @@
1
1
  import React, { type ComponentProps, useCallback } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { Button } from '@carbon/react';
4
- import { ArrowLeftIcon, formatDate, launchWorkspace, parseDate } from '@openmrs/esm-framework';
5
- import { type DefaultPatientWorkspaceProps } from '@openmrs/esm-patient-common-lib';
4
+ import { ArrowLeftIcon, formatDate, parseDate, Workspace2 } from '@openmrs/esm-framework';
5
+ import { type PatientWorkspace2DefinitionProps } from '@openmrs/esm-patient-common-lib';
6
6
  import { type MappedList, usePatientListMembers } from '../patient-lists.resource';
7
7
  import PatientListDetailsTable from './patient-list-details-table.component';
8
8
  import styles from './patient-list-details.scss';
9
9
 
10
- interface PatientListDetailsWorkspaceProps extends DefaultPatientWorkspaceProps {
10
+ export interface PatientListDetailsWorkspaceProps {
11
11
  list: MappedList;
12
12
  }
13
13
 
14
- function PatientListDetailsWorkspace({ list }: PatientListDetailsWorkspaceProps) {
14
+ const PatientListDetailsWorkspace: React.FC<PatientWorkspace2DefinitionProps<PatientListDetailsWorkspaceProps, {}>> = ({
15
+ workspaceProps: { list },
16
+ closeWorkspace,
17
+ }) => {
15
18
  const { t } = useTranslation();
16
19
  const { listMembers, isLoading } = usePatientListMembers(list.id);
17
20
 
18
- const closeListDetailsWorkspace = useCallback(() => {
19
- launchWorkspace('patient-lists');
20
- }, []);
21
+ const closeListDetailsWorkspace = useCallback(() => closeWorkspace(), [closeWorkspace]);
21
22
 
22
23
  return (
23
- <main className={styles.container}>
24
- <section className={styles.header}>
25
- <h4 className={styles.description}>{list.description ?? '--'}</h4>
26
- <p className={styles.details}>
27
- {list.size} {t('patients', 'patients')} &middot;{' '}
28
- <span className={styles.label}>{t('createdOn', 'Created on')}:</span>{' '}
29
- {list.startDate ? formatDate(parseDate(list.startDate)) : null}
30
- </p>
31
- </section>
32
- <div className={styles.backButton}>
33
- <Button
34
- kind="ghost"
35
- renderIcon={(props: ComponentProps<typeof ArrowLeftIcon>) => <ArrowLeftIcon size={24} {...props} />}
36
- iconDescription="Back to patient lists"
37
- size="sm"
38
- onClick={closeListDetailsWorkspace}
39
- >
40
- <span>{t('backToPatientLists', 'Back to patient lists')}</span>
41
- </Button>
42
- </div>
43
- <section className={styles.tableContainer}>
44
- <PatientListDetailsTable isLoading={isLoading} listMembers={listMembers} />
45
- </section>
46
- </main>
24
+ <Workspace2
25
+ title={list.name || t('patientListDetailWorkspaceTitle', 'Patient List Details')}
26
+ hasUnsavedChanges={false}
27
+ >
28
+ <main className={styles.container}>
29
+ <section className={styles.header}>
30
+ <h4 className={styles.description}>{list.description ?? '--'}</h4>
31
+ <p className={styles.details}>
32
+ {list.size} {t('patients', 'patients')} &middot;{' '}
33
+ <span className={styles.label}>{t('createdOn', 'Created on')}:</span>{' '}
34
+ {list.startDate ? formatDate(parseDate(list.startDate)) : null}
35
+ </p>
36
+ </section>
37
+ <div className={styles.backButton}>
38
+ <Button
39
+ kind="ghost"
40
+ renderIcon={(props: ComponentProps<typeof ArrowLeftIcon>) => <ArrowLeftIcon size={24} {...props} />}
41
+ iconDescription="Back to patient lists"
42
+ size="sm"
43
+ onClick={closeListDetailsWorkspace}
44
+ >
45
+ <span>{t('backToPatientLists', 'Back to patient lists')}</span>
46
+ </Button>
47
+ </div>
48
+ <section className={styles.tableContainer}>
49
+ <PatientListDetailsTable isLoading={isLoading} listMembers={listMembers} />
50
+ </section>
51
+ </main>
52
+ </Workspace2>
47
53
  );
48
- }
54
+ };
49
55
 
50
56
  export default PatientListDetailsWorkspace;
@@ -23,8 +23,7 @@ it('renders an empty state if patient list data is unavailable', async () => {
23
23
  mutate: jest.fn(),
24
24
  isValidating: false,
25
25
  });
26
-
27
- render(<PatientListsWorkspace />);
26
+ renderPatientListWorkspace();
28
27
 
29
28
  expect(screen.getByTitle(/empty data illustration/i)).toBeInTheDocument();
30
29
  expect(screen.getByText(/no patient lists to display/i)).toBeInTheDocument();
@@ -52,7 +51,7 @@ it('renders a tabular overview of the available patient lists', async () => {
52
51
  isValidating: false,
53
52
  });
54
53
 
55
- render(<PatientListsWorkspace />);
54
+ renderPatientListWorkspace();
56
55
 
57
56
  await screen.findByRole('table');
58
57
 
@@ -75,3 +74,23 @@ it('renders a tabular overview of the available patient lists', async () => {
75
74
  await user.type(searchbox, 'COTD');
76
75
  expect(screen.getByRole('row', { name: /COTD Study My List 2/i })).toBeInTheDocument();
77
76
  });
77
+
78
+ function renderPatientListWorkspace() {
79
+ render(
80
+ <PatientListsWorkspace
81
+ workspaceName={''}
82
+ launchChildWorkspace={jest.fn()}
83
+ closeWorkspace={jest.fn()}
84
+ workspaceProps={{}}
85
+ windowProps={{}}
86
+ groupProps={{
87
+ mutateVisitContext: jest.fn(),
88
+ patient: null,
89
+ patientUuid: null,
90
+ visitContext: null,
91
+ }}
92
+ windowName={''}
93
+ isRootWorkspace={false}
94
+ />,
95
+ );
96
+ }
@@ -18,21 +18,24 @@ import {
18
18
  TableToolbarSearch,
19
19
  Tile,
20
20
  } from '@carbon/react';
21
- import { EmptyDataIllustration } from '@openmrs/esm-patient-common-lib';
22
- import { launchWorkspace, useLayoutType } from '@openmrs/esm-framework';
23
- import { usePatientLists, type MappedList } from '../patient-lists.resource';
21
+ import { EmptyDataIllustration, type PatientWorkspace2DefinitionProps } from '@openmrs/esm-patient-common-lib';
22
+ import { launchWorkspace, useLayoutType, Workspace2 } from '@openmrs/esm-framework';
23
+ import { usePatientLists } from '../patient-lists.resource';
24
24
  import styles from './patient-lists.scss';
25
25
 
26
- function PatientListsWorkspace() {
26
+ function PatientListsWorkspace({ launchChildWorkspace }: PatientWorkspace2DefinitionProps<{}, {}>) {
27
27
  const { t } = useTranslation();
28
28
  const layout = useLayoutType();
29
29
  const responsiveSize = layout === 'tablet' ? 'lg' : 'sm';
30
30
  const [searchTerm, setSearchTerm] = useState('');
31
31
  const { patientLists, isLoading } = usePatientLists();
32
32
 
33
- const launchListDetailsWorkspace = useCallback((list: MappedList) => {
34
- launchWorkspace('patient-list-details', { list, workspaceTitle: list.name });
35
- }, []);
33
+ const launchListDetailsWorkspace = useCallback(
34
+ (list) => {
35
+ launchChildWorkspace('patient-list-details', { list });
36
+ },
37
+ [launchChildWorkspace],
38
+ );
36
39
 
37
40
  const tableHeaders = [
38
41
  {
@@ -66,90 +69,96 @@ function PatientListsWorkspace() {
66
69
 
67
70
  if (isLoading)
68
71
  return (
69
- <div className={styles.skeletonContainer}>
70
- <DataTableSkeleton className={styles.dataTableSkeleton} rowCount={5} columnCount={5} zebra />
71
- </div>
72
+ <Workspace2 title={t('patientListsWorkspaceTitle', 'Patient Lists')} hasUnsavedChanges={false}>
73
+ <div className={styles.skeletonContainer}>
74
+ <DataTableSkeleton className={styles.dataTableSkeleton} rowCount={5} columnCount={5} zebra />
75
+ </div>
76
+ </Workspace2>
72
77
  );
73
78
 
74
79
  if (patientLists?.length > 0) {
75
80
  return (
76
- <section className={styles.container}>
77
- <DataTable headers={tableHeaders} rows={filteredLists || []} size={responsiveSize} useZebraStyles>
78
- {({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
79
- <>
80
- <TableContainer className={styles.tableContainer}>
81
- <div className={styles.toolbarWrapper}>
82
- <TableToolbar className={styles.tableToolbar}>
83
- <TableToolbarContent>
84
- <TableToolbarSearch
85
- className={styles.search}
86
- expanded
87
- onChange={handleSearchTermChange}
88
- placeholder={t('searchThisList', 'Search this list')}
89
- size="sm"
90
- />
91
- </TableToolbarContent>
92
- </TableToolbar>
93
- </div>
94
- {rows.length > 0 ? (
95
- <Table aria-label="patient lists" className={styles.table} {...getTableProps()}>
96
- <TableHead>
97
- <TableRow>
98
- {headers.map((header) => (
99
- <TableHeader key={header.key} {...getHeaderProps({ header })}>
100
- {header.header}
101
- </TableHeader>
102
- ))}
103
- </TableRow>
104
- </TableHead>
105
- <TableBody>
106
- {rows.map((row) => {
107
- const currentList = patientLists?.find((list) => list?.id === row.id);
81
+ <Workspace2 title={t('patientListsWorkspaceTitle', 'Patient Lists')} hasUnsavedChanges={false}>
82
+ <section className={styles.container}>
83
+ <DataTable headers={tableHeaders} rows={filteredLists || []} size={responsiveSize} useZebraStyles>
84
+ {({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
85
+ <>
86
+ <TableContainer className={styles.tableContainer}>
87
+ <div className={styles.toolbarWrapper}>
88
+ <TableToolbar className={styles.tableToolbar}>
89
+ <TableToolbarContent>
90
+ <TableToolbarSearch
91
+ className={styles.search}
92
+ expanded
93
+ onChange={handleSearchTermChange}
94
+ placeholder={t('searchThisList', 'Search this list')}
95
+ size={responsiveSize}
96
+ />
97
+ </TableToolbarContent>
98
+ </TableToolbar>
99
+ </div>
100
+ {rows.length > 0 ? (
101
+ <Table aria-label="patient lists" className={styles.table} {...getTableProps()}>
102
+ <TableHead>
103
+ <TableRow>
104
+ {headers.map((header) => (
105
+ <TableHeader key={header.key} {...getHeaderProps({ header })}>
106
+ {header.header}
107
+ </TableHeader>
108
+ ))}
109
+ </TableRow>
110
+ </TableHead>
111
+ <TableBody>
112
+ {rows.map((row) => {
113
+ const currentList = patientLists?.find((list) => list?.id === row.id);
108
114
 
109
- return (
110
- <TableRow {...getRowProps({ row })} key={row.id}>
111
- <TableCell>
112
- <Link className={styles.link} onClick={() => launchListDetailsWorkspace(currentList)}>
113
- {currentList?.name ?? ''}
114
- </Link>
115
- </TableCell>
116
- <TableCell>{currentList?.type ?? ''}</TableCell>
117
- <TableCell>{currentList?.size ?? ''}</TableCell>
118
- </TableRow>
119
- );
120
- })}
121
- </TableBody>
122
- </Table>
115
+ return (
116
+ <TableRow {...getRowProps({ row })} key={row.id}>
117
+ <TableCell>
118
+ <Link className={styles.link} onClick={() => launchListDetailsWorkspace(currentList)}>
119
+ {currentList?.name ?? ''}
120
+ </Link>
121
+ </TableCell>
122
+ <TableCell>{currentList?.type ?? ''}</TableCell>
123
+ <TableCell>{currentList?.size ?? ''}</TableCell>
124
+ </TableRow>
125
+ );
126
+ })}
127
+ </TableBody>
128
+ </Table>
129
+ ) : null}
130
+ </TableContainer>
131
+ {filteredLists?.length === 0 ? (
132
+ <div className={styles.tileContainer}>
133
+ <Tile className={styles.tile}>
134
+ <div className={styles.tileContent}>
135
+ <p className={styles.content}>{t('noMatchingListsFound', 'No matching lists to display')}</p>
136
+ <p className={styles.helper}>{t('checkFilters', 'Check the filters above')}</p>
137
+ </div>
138
+ </Tile>
139
+ </div>
123
140
  ) : null}
124
- </TableContainer>
125
- {filteredLists?.length === 0 ? (
126
- <div className={styles.tileContainer}>
127
- <Tile className={styles.tile}>
128
- <div className={styles.tileContent}>
129
- <p className={styles.content}>{t('noMatchingListsFound', 'No matching lists to display')}</p>
130
- <p className={styles.helper}>{t('checkFilters', 'Check the filters above')}</p>
131
- </div>
132
- </Tile>
133
- </div>
134
- ) : null}
135
- </>
136
- )}
137
- </DataTable>
138
- </section>
141
+ </>
142
+ )}
143
+ </DataTable>
144
+ </section>
145
+ </Workspace2>
139
146
  );
140
147
  }
141
148
 
142
149
  return (
143
- <div className={styles.emptyStateContainer}>
144
- <Layer>
145
- <Tile className={styles.emptyStateTile}>
146
- <div className={styles.tileContent}>
147
- <EmptyDataIllustration />
148
- <p className={styles.emptyStateContent}>{t('noPatientListsToDisplay', 'No patient lists to display')}</p>
149
- </div>
150
- </Tile>
151
- </Layer>
152
- </div>
150
+ <Workspace2 title={t('patientListsWorkspaceTitle', 'Patient Lists')} hasUnsavedChanges={false}>
151
+ <div className={styles.emptyStateContainer}>
152
+ <Layer>
153
+ <Tile className={styles.emptyStateTile}>
154
+ <div className={styles.tileContent}>
155
+ <EmptyDataIllustration />
156
+ <p className={styles.emptyStateContent}>{t('noPatientListsToDisplay', 'No patient lists to display')}</p>
157
+ </div>
158
+ </Tile>
159
+ </Layer>
160
+ </div>
161
+ </Workspace2>
153
162
  );
154
163
  }
155
164