@openmrs/esm-styleguide 8.0.1-pre.3585 → 8.0.1-pre.3600
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 +4 -7
- package/dist/cards/card-header.component.d.ts +11 -0
- package/dist/cards/index.d.ts +1 -0
- package/dist/dashboard-extension/index.d.ts +5 -2
- package/dist/empty-card/empty-card-registration.d.ts +1 -0
- package/dist/empty-card/empty-card.component.d.ts +17 -0
- package/dist/empty-card/index.d.ts +1 -0
- package/dist/error-state/error-state.component.d.ts +4 -0
- package/dist/icons/icons.d.ts +1 -1
- package/dist/internal.d.ts +3 -0
- package/dist/openmrs-esm-styleguide.css +1 -1
- package/dist/openmrs-esm-styleguide.css.map +1 -1
- package/dist/openmrs-esm-styleguide.js +2 -2
- package/dist/openmrs-esm-styleguide.js.map +1 -1
- package/dist/pagination/index.d.ts +1 -0
- package/dist/pagination/pagination.component.d.ts +22 -0
- package/dist/public.d.ts +19 -18
- package/dist/snackbars/index.d.ts +1 -0
- package/dist/toasts/index.d.ts +1 -0
- package/package.json +12 -12
- package/src/cards/card-header.component.tsx +30 -0
- package/src/cards/card-header.module.scss +45 -0
- package/src/cards/index.ts +1 -0
- package/src/dashboard-extension/index.tsx +13 -3
- package/src/declarations.d.ts +14 -2
- package/src/empty-card/empty-card-registration.ts +6 -0
- package/src/empty-card/empty-card.component.tsx +55 -0
- package/src/empty-card/empty-card.module.scss +27 -0
- package/src/empty-card/empty-card.test.tsx +58 -0
- package/src/empty-card/empty-data-illustration.svg +32 -0
- package/src/empty-card/index.ts +1 -0
- package/src/error-state/error-state.component.tsx +7 -6
- package/src/error-state/error-state.module.scss +0 -29
- package/src/error-state/error-state.test.tsx +2 -2
- package/src/icons/icons.tsx +3 -3
- package/src/index.ts +5 -3
- package/src/internal.ts +3 -0
- package/src/pagination/index.ts +1 -0
- package/src/pagination/pagination.component.tsx +77 -0
- package/src/pagination/pagination.module.scss +66 -0
- package/src/pagination/pagination.test.tsx +72 -0
- package/src/public.ts +19 -18
- package/src/snackbars/index.tsx +2 -0
- package/src/toasts/index.tsx +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './pagination.component';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type PaginationProps as CarbonPaginationProps } from '@carbon/react';
|
|
3
|
+
export interface PaginationProps {
|
|
4
|
+
/** The count of current items displayed */
|
|
5
|
+
currentItems: number;
|
|
6
|
+
/** The count of total items displayed */
|
|
7
|
+
totalItems: number;
|
|
8
|
+
/** The current page number */
|
|
9
|
+
pageNumber: number;
|
|
10
|
+
/** The size of each page */
|
|
11
|
+
pageSize: number;
|
|
12
|
+
/** A callback to be called when the page changes */
|
|
13
|
+
onPageNumberChange?: CarbonPaginationProps['onChange'];
|
|
14
|
+
/** An optional URL the user should be directed to if they click on the link to see all results */
|
|
15
|
+
dashboardLinkUrl?: string;
|
|
16
|
+
/** Optional text to display instead of the default "See all" */
|
|
17
|
+
dashboardLinkLabel?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Re-usable pagination bar
|
|
21
|
+
*/
|
|
22
|
+
export declare const Pagination: React.FC<PaginationProps>;
|
package/dist/public.d.ts
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export { showToast } from './toasts';
|
|
5
|
-
export { showModal } from './modals';
|
|
6
|
-
export * from './workspaces/public';
|
|
7
|
-
export { type ToastDescriptor, type ToastType, type ToastNotificationMeta } from './toasts/toast.component';
|
|
8
|
-
export { showSnackbar } from './snackbars';
|
|
9
|
-
export { type SnackbarDescriptor, type SnackbarType, type SnackbarMeta } from './snackbars/snackbar.component';
|
|
10
|
-
export * from './left-nav';
|
|
1
|
+
export { type StyleguideConfigObject } from './config-schema';
|
|
2
|
+
export * from './cards';
|
|
3
|
+
export * from './custom-overflow-menu';
|
|
11
4
|
export * from './dashboard-extension';
|
|
12
|
-
export * from './error-state';
|
|
13
5
|
export * from './datepicker';
|
|
14
|
-
export * from './
|
|
15
|
-
export * from './
|
|
16
|
-
export * from './
|
|
17
|
-
export * from './custom-overflow-menu';
|
|
6
|
+
export * from './diagnosis-tags';
|
|
7
|
+
export * from './empty-card';
|
|
8
|
+
export * from './error-state';
|
|
18
9
|
export * from './icons/icons';
|
|
10
|
+
export * from './left-nav';
|
|
11
|
+
export * from './location-picker';
|
|
12
|
+
export { showModal } from './modals';
|
|
13
|
+
export { showNotification, showActionableNotification } from './notifications';
|
|
14
|
+
export { type ActionableNotificationDescriptor, type ActionableNotificationType, } from './notifications/actionable-notification.component';
|
|
15
|
+
export { type NotificationDescriptor, type InlineNotificationType } from './notifications/notification.component';
|
|
19
16
|
export * from './page-header';
|
|
17
|
+
export * from './pagination';
|
|
18
|
+
export * from './patient-banner';
|
|
19
|
+
export * from './patient-photo';
|
|
20
20
|
export * from './pictograms/pictograms';
|
|
21
|
-
export
|
|
22
|
-
export
|
|
23
|
-
export
|
|
21
|
+
export * from './responsive-wrapper';
|
|
22
|
+
export { showSnackbar, type SnackbarDescriptor, type SnackbarType, type SnackbarMeta } from './snackbars';
|
|
23
|
+
export { showToast, type ToastDescriptor, type ToastType, type ToastNotificationMeta } from './toasts';
|
|
24
|
+
export * from './workspaces/public';
|
|
24
25
|
export { launchWorkspace2, launchWorkspaceGroup2, closeWorkspaceGroup2, getRegisteredWorkspace2Names, useWorkspace2Context, ActionMenuButton2, Workspace2, type Workspace2Definition, type Workspace2DefinitionProps, } from './workspaces2';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SnackbarDescriptor } from './snackbar.component';
|
|
2
|
+
export { type SnackbarDescriptor, type SnackbarType, type SnackbarMeta } from './snackbar.component';
|
|
2
3
|
/**
|
|
3
4
|
* Starts a rendering host for snack bar notifications. Should only be used by the app shell.
|
|
4
5
|
* Under normal conditions there is no need to use this function.
|
package/dist/toasts/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ToastDescriptor } from './toast.component';
|
|
2
|
+
export { type ToastDescriptor, type ToastType, type ToastNotificationMeta } from './toast.component';
|
|
2
3
|
/**
|
|
3
4
|
* Starts a rendering host for toast notifications. Should only be used by the app shell.
|
|
4
5
|
* Under normal conditions there is no need to use this function.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-styleguide",
|
|
3
|
-
"version": "8.0.1-pre.
|
|
3
|
+
"version": "8.0.1-pre.3600",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "The styleguide for OpenMRS SPA",
|
|
6
6
|
"main": "dist/openmrs-esm-styleguide.js",
|
|
@@ -98,17 +98,17 @@
|
|
|
98
98
|
"swr": "2.x"
|
|
99
99
|
},
|
|
100
100
|
"devDependencies": {
|
|
101
|
-
"@openmrs/esm-api": "8.0.1-pre.
|
|
102
|
-
"@openmrs/esm-config": "8.0.1-pre.
|
|
103
|
-
"@openmrs/esm-emr-api": "8.0.1-pre.
|
|
104
|
-
"@openmrs/esm-error-handling": "8.0.1-pre.
|
|
105
|
-
"@openmrs/esm-extensions": "8.0.1-pre.
|
|
106
|
-
"@openmrs/esm-globals": "8.0.1-pre.
|
|
107
|
-
"@openmrs/esm-navigation": "8.0.1-pre.
|
|
108
|
-
"@openmrs/esm-react-utils": "8.0.1-pre.
|
|
109
|
-
"@openmrs/esm-state": "8.0.1-pre.
|
|
110
|
-
"@openmrs/esm-translations": "8.0.1-pre.
|
|
111
|
-
"@openmrs/esm-utils": "8.0.1-pre.
|
|
101
|
+
"@openmrs/esm-api": "8.0.1-pre.3600",
|
|
102
|
+
"@openmrs/esm-config": "8.0.1-pre.3600",
|
|
103
|
+
"@openmrs/esm-emr-api": "8.0.1-pre.3600",
|
|
104
|
+
"@openmrs/esm-error-handling": "8.0.1-pre.3600",
|
|
105
|
+
"@openmrs/esm-extensions": "8.0.1-pre.3600",
|
|
106
|
+
"@openmrs/esm-globals": "8.0.1-pre.3600",
|
|
107
|
+
"@openmrs/esm-navigation": "8.0.1-pre.3600",
|
|
108
|
+
"@openmrs/esm-react-utils": "8.0.1-pre.3600",
|
|
109
|
+
"@openmrs/esm-state": "8.0.1-pre.3600",
|
|
110
|
+
"@openmrs/esm-translations": "8.0.1-pre.3600",
|
|
111
|
+
"@openmrs/esm-utils": "8.0.1-pre.3600",
|
|
112
112
|
"@rspack/cli": "^1.3.11",
|
|
113
113
|
"@rspack/core": "^1.3.11",
|
|
114
114
|
"@types/geopattern": "^1.2.9",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { useLayoutType } from '@openmrs/esm-react-utils';
|
|
4
|
+
import styles from './card-header.module.scss';
|
|
5
|
+
|
|
6
|
+
export interface CardHeaderProps {
|
|
7
|
+
/** The title for this card. This must be a pre-translated string. */
|
|
8
|
+
title: string;
|
|
9
|
+
/** The contents of the card header to render if any. */
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Re-usable header component for O3-style cards, like those found on the patient chart
|
|
15
|
+
*/
|
|
16
|
+
export function CardHeader({ title, children }: CardHeaderProps) {
|
|
17
|
+
const isTablet = useLayoutType() === 'tablet';
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className={classNames({
|
|
22
|
+
[styles.tabletHeader]: isTablet,
|
|
23
|
+
[styles.desktopHeader]: !isTablet,
|
|
24
|
+
})}
|
|
25
|
+
>
|
|
26
|
+
<h4>{title}</h4>
|
|
27
|
+
{children}
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '../vars' as *;
|
|
4
|
+
|
|
5
|
+
.desktopHeader,
|
|
6
|
+
.tabletHeader {
|
|
7
|
+
display: flex;
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
align-items: center;
|
|
10
|
+
padding: layout.$spacing-04 0 layout.$spacing-04 layout.$spacing-05;
|
|
11
|
+
background-color: $ui-background;
|
|
12
|
+
|
|
13
|
+
h4:after {
|
|
14
|
+
content: '';
|
|
15
|
+
display: block;
|
|
16
|
+
width: layout.$spacing-07;
|
|
17
|
+
padding-top: 0.188rem;
|
|
18
|
+
border-bottom: 0.375rem solid var(--brand-03);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.desktopHeader {
|
|
23
|
+
height: layout.$spacing-09;
|
|
24
|
+
h4 {
|
|
25
|
+
@include type.type-style('heading-compact-02');
|
|
26
|
+
color: $text-02;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.tabletHeader {
|
|
31
|
+
height: 4.5rem;
|
|
32
|
+
h4 {
|
|
33
|
+
@include type.type-style('heading-03');
|
|
34
|
+
color: $text-02;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Overriding styles for RTL support
|
|
39
|
+
html[dir='rtl'] {
|
|
40
|
+
.desktopHeader,
|
|
41
|
+
.tabletHeader {
|
|
42
|
+
text-align: right;
|
|
43
|
+
padding: layout.$spacing-04 layout.$spacing-05 layout.$spacing-04 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CardHeader, type CardHeaderProps } from './card-header.component';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import { useLocation } from 'react-router-dom';
|
|
4
|
+
import { BrowserRouter, useLocation } from 'react-router-dom';
|
|
5
5
|
import { shallowEqual } from '@openmrs/esm-utils';
|
|
6
6
|
import { ConfigurableLink, MaybeIcon } from '@openmrs/esm-framework';
|
|
7
7
|
import { type IconId } from '../icons';
|
|
@@ -10,8 +10,8 @@ import styles from './dashboard.module.scss';
|
|
|
10
10
|
export interface DashboardExtensionProps {
|
|
11
11
|
path: string;
|
|
12
12
|
title: string;
|
|
13
|
-
basePath
|
|
14
|
-
icon
|
|
13
|
+
basePath?: string;
|
|
14
|
+
icon?: IconId;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export const DashboardExtension = ({ path, title, basePath, icon }: DashboardExtensionProps) => {
|
|
@@ -49,3 +49,13 @@ export const DashboardExtension = ({ path, title, basePath, icon }: DashboardExt
|
|
|
49
49
|
</div>
|
|
50
50
|
);
|
|
51
51
|
};
|
|
52
|
+
|
|
53
|
+
export function createDashboard(props: Omit<DashboardExtensionProps, 'basePath'>) {
|
|
54
|
+
return function ({ basePath }: { basePath: string }) {
|
|
55
|
+
return (
|
|
56
|
+
<BrowserRouter>
|
|
57
|
+
<DashboardExtension basePath={basePath} {...props} />
|
|
58
|
+
</BrowserRouter>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
}
|
package/src/declarations.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
declare module '*.css'
|
|
2
|
-
|
|
1
|
+
declare module '*.css' {
|
|
2
|
+
interface Styles {
|
|
3
|
+
[key: string]: string;
|
|
4
|
+
}
|
|
5
|
+
const content: Styles;
|
|
6
|
+
export default content;
|
|
7
|
+
}
|
|
8
|
+
declare module '*.scss' {
|
|
9
|
+
interface Styles {
|
|
10
|
+
[key: string]: string;
|
|
11
|
+
}
|
|
12
|
+
const content: Styles;
|
|
13
|
+
export default content;
|
|
14
|
+
}
|
|
3
15
|
declare module '*.svg' {
|
|
4
16
|
const content: string;
|
|
5
17
|
export default content;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { Button, Layer, Tile } from '@carbon/react';
|
|
4
|
+
import { useLayoutType } from '@openmrs/esm-react-utils';
|
|
5
|
+
import { getCoreTranslation } from '@openmrs/esm-translations';
|
|
6
|
+
import { CardHeader } from '../cards';
|
|
7
|
+
import styles from './empty-card.module.scss';
|
|
8
|
+
|
|
9
|
+
export interface EmptyCardProps {
|
|
10
|
+
/** The name of the type of item that would be displayed here if not empty. This must be a pre-translated string. */
|
|
11
|
+
displayText: string;
|
|
12
|
+
/** The title to use for this empty component. This must be a pre-translated string. */
|
|
13
|
+
headerTitle: string;
|
|
14
|
+
/** A callback to invoke when the user tries to record a new item. */
|
|
15
|
+
launchForm?(): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const EmptyCardIllustration = ({ width = '64', height = '64' }) => {
|
|
19
|
+
return (
|
|
20
|
+
<svg width={width} height={height} viewBox="0 0 64 64">
|
|
21
|
+
<use href={`#omrs-empty-data-illustration`} />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Re-usable card for displaying an empty state
|
|
28
|
+
*/
|
|
29
|
+
export const EmptyCard: React.FC<EmptyCardProps> = (props) => {
|
|
30
|
+
const isTablet = useLayoutType() === 'tablet';
|
|
31
|
+
const launchForm = props.launchForm;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Layer className={styles.layer}>
|
|
35
|
+
<Tile className={styles.tile}>
|
|
36
|
+
<CardHeader title={props.headerTitle} />
|
|
37
|
+
<EmptyCardIllustration />
|
|
38
|
+
<p className={styles.content}>
|
|
39
|
+
{getCoreTranslation('emptyStateText', 'There are no {{displayText}} to display', {
|
|
40
|
+
displayText: props.displayText,
|
|
41
|
+
})}
|
|
42
|
+
</p>
|
|
43
|
+
<p className={styles.action}>
|
|
44
|
+
{launchForm && (
|
|
45
|
+
<Button onClick={() => launchForm()} kind="ghost" size={isTablet ? 'lg' : 'sm'}>
|
|
46
|
+
{getCoreTranslation('recordNewEntry', 'Record {{ displayText }}', {
|
|
47
|
+
displayText: props.displayText,
|
|
48
|
+
})}
|
|
49
|
+
</Button>
|
|
50
|
+
)}
|
|
51
|
+
</p>
|
|
52
|
+
</Tile>
|
|
53
|
+
</Layer>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '../vars' as *;
|
|
4
|
+
|
|
5
|
+
.action {
|
|
6
|
+
margin-bottom: layout.$spacing-03;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.content {
|
|
10
|
+
@include type.type-style('heading-compact-01');
|
|
11
|
+
color: $text-02;
|
|
12
|
+
margin-top: layout.$spacing-05;
|
|
13
|
+
margin-bottom: layout.$spacing-03;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.heading:after {
|
|
17
|
+
content: '';
|
|
18
|
+
display: block;
|
|
19
|
+
width: layout.$spacing-07;
|
|
20
|
+
padding-top: 0.188rem;
|
|
21
|
+
border-bottom: 0.375rem solid var(--brand-03);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.tile {
|
|
25
|
+
text-align: center;
|
|
26
|
+
border: 1px solid $ui-03;
|
|
27
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { EmptyCard } from './empty-card.component';
|
|
6
|
+
|
|
7
|
+
describe('EmptyCard', () => {
|
|
8
|
+
const defaultProps = {
|
|
9
|
+
displayText: 'medications',
|
|
10
|
+
headerTitle: 'Medications',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
it('renders the header title', () => {
|
|
14
|
+
render(<EmptyCard {...defaultProps} />);
|
|
15
|
+
expect(screen.getByRole('heading', { name: /medications/i })).toBeInTheDocument();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('renders the empty state text with the display text', () => {
|
|
19
|
+
render(<EmptyCard {...defaultProps} />);
|
|
20
|
+
expect(screen.getByText(/there are no medications to display/i)).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('does not render the action button when launchForm is not provided', () => {
|
|
24
|
+
render(<EmptyCard {...defaultProps} />);
|
|
25
|
+
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('renders the action button when launchForm is provided', () => {
|
|
29
|
+
const launchForm = vi.fn();
|
|
30
|
+
render(<EmptyCard {...defaultProps} launchForm={launchForm} />);
|
|
31
|
+
expect(screen.getByRole('button', { name: /record medications/i })).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('calls launchForm when the action button is clicked', async () => {
|
|
35
|
+
const user = userEvent.setup();
|
|
36
|
+
const launchForm = vi.fn();
|
|
37
|
+
render(<EmptyCard {...defaultProps} launchForm={launchForm} />);
|
|
38
|
+
|
|
39
|
+
const button = screen.getByRole('button', { name: /record medications/i });
|
|
40
|
+
await user.click(button);
|
|
41
|
+
|
|
42
|
+
expect(launchForm).toHaveBeenCalledTimes(1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('renders with different display text', () => {
|
|
46
|
+
render(<EmptyCard displayText="allergies" headerTitle="Allergies" />);
|
|
47
|
+
expect(screen.getByRole('heading', { name: /allergies/i })).toBeInTheDocument();
|
|
48
|
+
expect(screen.getByText(/there are no allergies to display/i)).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('renders the empty data illustration', () => {
|
|
52
|
+
render(<EmptyCard {...defaultProps} />);
|
|
53
|
+
// eslint-disable-next-line testing-library/no-node-access
|
|
54
|
+
const svg = document.querySelector('svg');
|
|
55
|
+
expect(svg).toBeInTheDocument();
|
|
56
|
+
expect(svg).toHaveAttribute('viewBox', '0 0 64 64');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<svg viewBox="0 0 64 64">
|
|
2
|
+
<g fill="none" fillRule="evenodd">
|
|
3
|
+
<path
|
|
4
|
+
d="M38.133 13.186H21.947c-.768.001-1.39.623-1.39 1.391V50.55l-.186.057-3.97 1.216a.743.743 0 01-.927-.493L3.664 12.751a.742.742 0 01.492-.926l6.118-1.874 17.738-5.43 6.119-1.873a.741.741 0 01.926.492L38.076 13l.057.186z"
|
|
5
|
+
fill="#F4F4F4"
|
|
6
|
+
/>
|
|
7
|
+
<path
|
|
8
|
+
d="M41.664 13L38.026 1.117A1.576 1.576 0 0036.056.07l-8.601 2.633-17.737 5.43-8.603 2.634a1.578 1.578 0 00-1.046 1.97l12.436 40.616a1.58 1.58 0 001.969 1.046l5.897-1.805.185-.057v-.194l-.185.057-5.952 1.822a1.393 1.393 0 01-1.737-.923L.247 12.682a1.39 1.39 0 01.923-1.738L9.772 8.31 27.51 2.881 36.112.247a1.393 1.393 0 011.737.923L41.47 13l.057.186h.193l-.057-.185z"
|
|
9
|
+
fill="#8D8D8D"
|
|
10
|
+
/>
|
|
11
|
+
<path
|
|
12
|
+
d="M11.378 11.855a.836.836 0 01-.798-.59L9.385 7.361a.835.835 0 01.554-1.042l16.318-4.996a.836.836 0 011.042.554l1.195 3.902a.836.836 0 01-.554 1.043l-16.318 4.995a.831.831 0 01-.244.037z"
|
|
13
|
+
fill="#C6C6C6"
|
|
14
|
+
/>
|
|
15
|
+
<circle fill="#C6C6C6" cx="17.636" cy="2.314" r="1.855" />
|
|
16
|
+
<circle fill="#FFF" fillRule="nonzero" cx="17.636" cy="2.314" r="1.175" />
|
|
17
|
+
<path
|
|
18
|
+
d="M55.893 53.995H24.544a.79.79 0 01-.788-.789V15.644a.79.79 0 01.788-.788h31.349a.79.79 0 01.788.788v37.562a.79.79 0 01-.788.789z"
|
|
19
|
+
fill="#F4F4F4"
|
|
20
|
+
/>
|
|
21
|
+
<path
|
|
22
|
+
d="M41.47 13H21.948a1.579 1.579 0 00-1.576 1.577V52.4l.185-.057V14.577c.001-.768.623-1.39 1.391-1.39h19.581L41.471 13zm17.02 0H21.947a1.579 1.579 0 00-1.576 1.577v42.478c0 .87.706 1.576 1.576 1.577H58.49a1.579 1.579 0 001.576-1.577V14.577a1.579 1.579 0 00-1.576-1.576zm1.39 44.055c0 .768-.622 1.39-1.39 1.392H21.947c-.768-.001-1.39-.624-1.39-1.392V14.577c0-.768.622-1.39 1.39-1.39H58.49c.768 0 1.39.622 1.39 1.39v42.478z"
|
|
23
|
+
fill="#8D8D8D"
|
|
24
|
+
/>
|
|
25
|
+
<path
|
|
26
|
+
d="M48.751 17.082H31.686a.836.836 0 01-.835-.835v-4.081c0-.46.374-.834.835-.835H48.75c.461 0 .834.374.835.835v4.08c0 .462-.374.835-.835.836z"
|
|
27
|
+
fill="#C6C6C6"
|
|
28
|
+
/>
|
|
29
|
+
<circle fill="#C6C6C6" cx="40.218" cy="9.755" r="1.855" />
|
|
30
|
+
<circle fill="#FFF" fillRule="nonzero" cx="40.218" cy="9.755" r="1.13" />
|
|
31
|
+
</g>
|
|
32
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { EmptyCard, EmptyCardIllustration, type EmptyCardProps } from './empty-card.component';
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Layer, Tile } from '@carbon/react';
|
|
3
3
|
import { getCoreTranslation } from '@openmrs/esm-translations';
|
|
4
|
-
import { useLayoutType } from '@openmrs/esm-react-utils';
|
|
5
4
|
import styles from './error-state.module.scss';
|
|
5
|
+
import { CardHeader } from '../cards';
|
|
6
6
|
|
|
7
7
|
export interface ErrorStateProps {
|
|
8
|
+
/** The error that caused this error card to be rendered. Expected to be a failed fetch result. */
|
|
8
9
|
error: any;
|
|
10
|
+
/** The title to use for this empty component. This must be a pre-translated string. */
|
|
9
11
|
headerTitle: string;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export const ErrorState: React.FC<ErrorStateProps> = ({ error, headerTitle }) => {
|
|
13
|
-
const isTablet = useLayoutType() === 'tablet';
|
|
14
|
-
|
|
15
15
|
return (
|
|
16
16
|
<Layer>
|
|
17
17
|
<Tile className={styles.tile}>
|
|
18
|
-
<
|
|
19
|
-
<h4>{headerTitle}</h4>
|
|
20
|
-
</div>
|
|
18
|
+
<CardHeader title={headerTitle} />
|
|
21
19
|
<p className={styles.errorMessage}>
|
|
22
20
|
{getCoreTranslation('error', 'Error')} {`${error?.response?.status}: `}
|
|
23
21
|
{error?.response?.statusText}
|
|
@@ -27,3 +25,6 @@ export const ErrorState: React.FC<ErrorStateProps> = ({ error, headerTitle }) =>
|
|
|
27
25
|
</Layer>
|
|
28
26
|
);
|
|
29
27
|
};
|
|
28
|
+
|
|
29
|
+
export const ErrorCard = ErrorState;
|
|
30
|
+
export type ErrorCardProps = ErrorStateProps;
|
|
@@ -15,35 +15,6 @@
|
|
|
15
15
|
color: $text-02;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
.desktopHeading {
|
|
19
|
-
h4 {
|
|
20
|
-
@include type.type-style('heading-compact-02');
|
|
21
|
-
color: $text-02;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.tabletHeading {
|
|
26
|
-
h4 {
|
|
27
|
-
@include type.type-style('heading-03');
|
|
28
|
-
color: $text-02;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.desktopHeading,
|
|
33
|
-
.tabletHeading {
|
|
34
|
-
text-align: left;
|
|
35
|
-
text-transform: capitalize;
|
|
36
|
-
margin-bottom: layout.$spacing-05;
|
|
37
|
-
|
|
38
|
-
h4:after {
|
|
39
|
-
content: '';
|
|
40
|
-
display: block;
|
|
41
|
-
width: layout.$spacing-07;
|
|
42
|
-
padding-top: 0.188rem;
|
|
43
|
-
border-bottom: 0.375rem solid var(--brand-03);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
18
|
.tile {
|
|
48
19
|
text-align: center;
|
|
49
20
|
border: 1px solid $ui-03;
|
|
@@ -31,7 +31,7 @@ describe('ErrorState', () => {
|
|
|
31
31
|
|
|
32
32
|
render(<ErrorState headerTitle="test" error={{}} />);
|
|
33
33
|
// eslint-disable-next-line testing-library/no-node-access
|
|
34
|
-
expect(screen.getByRole('heading').parentElement?.getAttribute('class')).toContain('
|
|
34
|
+
expect(screen.getByRole('heading').parentElement?.getAttribute('class')).toContain('tabletHeader');
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
it('should render desktop layout when layout type is not tablet', () => {
|
|
@@ -39,7 +39,7 @@ describe('ErrorState', () => {
|
|
|
39
39
|
|
|
40
40
|
render(<ErrorState headerTitle="test" error={{}} />);
|
|
41
41
|
// eslint-disable-next-line testing-library/no-node-access
|
|
42
|
-
expect(screen.getByRole('heading').parentElement?.getAttribute('class')).toContain('
|
|
42
|
+
expect(screen.getByRole('heading').parentElement?.getAttribute('class')).toContain('desktopHeader');
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it('should handle error with partial response data', () => {
|
package/src/icons/icons.tsx
CHANGED
|
@@ -862,18 +862,18 @@ export const ShoppingCartAddItemIcon = ShoppingCartArrowDownIcon;
|
|
|
862
862
|
* ```
|
|
863
863
|
*/
|
|
864
864
|
export const MaybeIcon = memo(
|
|
865
|
-
forwardRef<SVGSVGElement, { icon: string; fallback?: React.ReactNode } & IconProps>(function MaybeIcon(
|
|
865
|
+
forwardRef<SVGSVGElement, { icon: string | undefined; fallback?: React.ReactNode } & IconProps>(function MaybeIcon(
|
|
866
866
|
{ icon, fallback, ...iconProps },
|
|
867
867
|
ref,
|
|
868
868
|
) {
|
|
869
|
-
const iconRef = useRef(document.getElementById(icon));
|
|
869
|
+
const iconRef = useRef(icon ? document.getElementById(icon) : undefined);
|
|
870
870
|
|
|
871
871
|
useEffect(() => {
|
|
872
872
|
const container = document.getElementById('omrs-svgs-container');
|
|
873
873
|
const callback: MutationCallback = (mutationList) => {
|
|
874
874
|
for (const mutation of mutationList) {
|
|
875
875
|
if (mutation.type === 'childList') {
|
|
876
|
-
iconRef.current = document.getElementById(icon);
|
|
876
|
+
iconRef.current = icon ? document.getElementById(icon) : undefined;
|
|
877
877
|
}
|
|
878
878
|
}
|
|
879
879
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { defineConfigSchema } from '@openmrs/esm-config';
|
|
2
|
-
import {
|
|
3
|
-
import { setupIcons } from './icons/icon-registration';
|
|
2
|
+
import { registerModal } from '@openmrs/esm-extensions';
|
|
4
3
|
import { setupBranding } from './brand';
|
|
5
4
|
import { esmStyleGuideSchema } from './config-schema';
|
|
5
|
+
import { setupEmptyCard } from './empty-card/empty-card-registration';
|
|
6
|
+
import { setupIcons } from './icons/icon-registration';
|
|
7
|
+
import { setupLogo } from './logo';
|
|
6
8
|
import { setupPictograms } from './pictograms/pictogram-registration';
|
|
7
|
-
import { registerModal } from '@openmrs/esm-extensions';
|
|
8
9
|
import { getSyncLifecycle } from '@openmrs/esm-react-utils';
|
|
9
10
|
import Workspace2ClosePromptModal from './workspaces2/workspace2-close-prompt.modal';
|
|
10
11
|
|
|
@@ -13,6 +14,7 @@ setupBranding();
|
|
|
13
14
|
setupLogo();
|
|
14
15
|
setupIcons();
|
|
15
16
|
setupPictograms();
|
|
17
|
+
setupEmptyCard();
|
|
16
18
|
|
|
17
19
|
registerModal({
|
|
18
20
|
name: 'workspace2-close-prompt',
|
package/src/internal.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export * from './breakpoints';
|
|
2
|
+
export * from './cards';
|
|
2
3
|
export * from './config-schema';
|
|
3
4
|
export * from './custom-overflow-menu';
|
|
4
5
|
export * from './dashboard-extension';
|
|
5
6
|
export * from './datepicker';
|
|
6
7
|
export * from './diagnosis-tags';
|
|
8
|
+
export * from './empty-card';
|
|
7
9
|
export * from './error-state';
|
|
8
10
|
export * from './icons/icons';
|
|
9
11
|
export * from './left-nav';
|
|
@@ -13,6 +15,7 @@ export * from './notifications';
|
|
|
13
15
|
export * from './notifications/actionable-notification.component';
|
|
14
16
|
export * from './notifications/notification.component';
|
|
15
17
|
export * from './page-header';
|
|
18
|
+
export * from './pagination';
|
|
16
19
|
export * from './patient-banner';
|
|
17
20
|
export * from './patient-photo';
|
|
18
21
|
export * from './pictograms/pictograms';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './pagination.component';
|