@openmrs/esm-react-utils 3.4.1-pre.96 → 4.0.0-pre.1

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/jest.config.js CHANGED
@@ -11,4 +11,8 @@ module.exports = {
11
11
  "@openmrs/esm-styleguide":
12
12
  "<rootDir>/__mocks__/openmrs-esm-styleguide.mock.tsx",
13
13
  },
14
+ testEnvironment: "jsdom",
15
+ testEnvironmentOptions: {
16
+ url: "http://localhost/",
17
+ },
14
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-react-utils",
3
- "version": "3.4.1-pre.96",
3
+ "version": "4.0.0-pre.1",
4
4
  "license": "MPL-2.0",
5
5
  "description": "React utilities for OpenMRS.",
6
6
  "browser": "dist/openmrs-esm-react-utils.js",
@@ -39,34 +39,34 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "lodash-es": "^4.17.21",
42
- "single-spa-react": "^4.1.1",
42
+ "single-spa-react": "^4.6.1",
43
43
  "swr": "^1.2.2"
44
44
  },
45
45
  "peerDependencies": {
46
- "@openmrs/esm-api": "3.x",
47
- "@openmrs/esm-config": "3.x",
48
- "@openmrs/esm-error-handling": "3.x",
49
- "@openmrs/esm-extensions": "3.x",
50
- "@openmrs/esm-globals": "3.x",
46
+ "@openmrs/esm-api": "4.x",
47
+ "@openmrs/esm-config": "4.x",
48
+ "@openmrs/esm-error-handling": "4.x",
49
+ "@openmrs/esm-extensions": "4.x",
50
+ "@openmrs/esm-globals": "4.x",
51
51
  "dayjs": "1.x",
52
52
  "i18next": "19.x",
53
- "react": "16.x",
54
- "react-dom": "16.x",
53
+ "react": "18.x",
54
+ "react-dom": "18.x",
55
55
  "react-i18next": "11.x"
56
56
  },
57
57
  "devDependencies": {
58
- "@openmrs/esm-api": "^3.4.1-pre.96",
59
- "@openmrs/esm-config": "^3.4.1-pre.96",
60
- "@openmrs/esm-error-handling": "^3.4.1-pre.96",
61
- "@openmrs/esm-extensions": "^3.4.1-pre.96",
62
- "@openmrs/esm-globals": "^3.4.1-pre.96",
58
+ "@openmrs/esm-api": "^4.0.0-pre.1",
59
+ "@openmrs/esm-config": "^4.0.0-pre.1",
60
+ "@openmrs/esm-error-handling": "^4.0.0-pre.1",
61
+ "@openmrs/esm-extensions": "^4.0.0-pre.1",
62
+ "@openmrs/esm-globals": "^4.0.0-pre.1",
63
63
  "dayjs": "^1.10.8",
64
64
  "i18next": "^19.6.0",
65
- "react": "^16.13.1",
66
- "react-dom": "^16.13.1",
67
- "react-i18next": "^11.7.0",
65
+ "react": "^18.1.0",
66
+ "react-dom": "^18.1.0",
67
+ "react-i18next": "^11.16.9",
68
68
  "rxjs": "^6.5.3",
69
69
  "unistore": "^3.5.2"
70
70
  },
71
- "gitHead": "593214cd88408e07e9c18eab4c204be4a3722c73"
71
+ "gitHead": "9f58b801f07526083a9edc1cc5a1e58660d71eb0"
72
72
  }
@@ -5,6 +5,8 @@ import { navigate, interpolateUrl } from "@openmrs/esm-config";
5
5
  import userEvent from "@testing-library/user-event";
6
6
  import { ConfigurableLink } from "./ConfigurableLink";
7
7
 
8
+ jest.mock("single-spa");
9
+
8
10
  jest.mock("@openmrs/esm-config");
9
11
  const mockNavigate = navigate as jest.Mock;
10
12
 
@@ -34,18 +36,18 @@ describe(`ConfigurableLink`, () => {
34
36
  expect(link.closest("a")).toHaveAttribute("href", "/openmrs/spa/home");
35
37
  });
36
38
 
37
- it(`calls navigate on normal click but not right click`, async () => {
39
+ it(`calls navigate on normal click but not special clicks`, async () => {
38
40
  const link = screen.getByRole("link", { name: /spa home/i });
39
- userEvent.click(link, { button: 2 }); // right-click
41
+ await userEvent.pointer({ target: link, keys: "[MouseRight]" });
40
42
  expect(navigate).not.toHaveBeenCalled();
41
- userEvent.click(link);
43
+ await userEvent.click(link);
42
44
  expect(navigate).toHaveBeenCalledWith({ to: path });
43
45
  });
44
46
 
45
47
  it(`calls navigate on enter`, async () => {
46
48
  expect(navigate).not.toHaveBeenCalled();
47
49
  const link = screen.getByRole("link", { name: /spa home/i });
48
- userEvent.type(link, "{enter}");
50
+ await userEvent.type(link, "{enter}");
49
51
  expect(navigate).toHaveBeenCalledWith({ to: path });
50
52
  });
51
53
  });
@@ -1,8 +1,12 @@
1
1
  /** @module @category Navigation */
2
2
  import React, { MouseEvent, AnchorHTMLAttributes } from "react";
3
- import { navigate, interpolateUrl } from "@openmrs/esm-config";
3
+ import { navigate, interpolateUrl, TemplateParams } from "@openmrs/esm-config";
4
4
 
5
- function handleClick(event: MouseEvent, to: string) {
5
+ function handleClick(
6
+ event: MouseEvent,
7
+ to: string,
8
+ templateParams?: TemplateParams
9
+ ) {
6
10
  if (
7
11
  !event.metaKey &&
8
12
  !event.ctrlKey &&
@@ -10,7 +14,7 @@ function handleClick(event: MouseEvent, to: string) {
10
14
  event.button == 0
11
15
  ) {
12
16
  event.preventDefault();
13
- navigate({ to });
17
+ navigate({ to, templateParams });
14
18
  }
15
19
  }
16
20
 
@@ -20,25 +24,30 @@ function handleClick(event: MouseEvent, to: string) {
20
24
  export interface ConfigurableLinkProps
21
25
  extends AnchorHTMLAttributes<HTMLAnchorElement> {
22
26
  to: string;
27
+ templateParams?: TemplateParams;
23
28
  }
24
29
 
25
30
  /**
26
31
  * A React link component which calls [[navigate]] when clicked
27
32
  *
28
33
  * @param to The target path or URL. Supports interpolation. See [[navigate]]
34
+ * @param urlParams: A dictionary of values to interpolate into the URL, in addition to the default keys `openmrsBase` and `openmrsSpaBase`.
29
35
  * @param children Inline elements within the link
30
36
  * @param otherProps Any other valid props for an <a> tag except `href` and `onClick`
31
37
  */
32
- export const ConfigurableLink: React.FC<ConfigurableLinkProps> = ({
38
+ export function ConfigurableLink({
33
39
  to,
40
+ templateParams,
34
41
  children,
35
42
  ...otherProps
36
- }) => (
37
- <a
38
- onClick={(event) => handleClick(event, to)}
39
- href={interpolateUrl(to)}
40
- {...otherProps}
41
- >
42
- {children}
43
- </a>
44
- );
43
+ }: ConfigurableLinkProps) {
44
+ return (
45
+ <a
46
+ onClick={(event) => handleClick(event, to, templateParams)}
47
+ href={interpolateUrl(to, templateParams)}
48
+ {...otherProps}
49
+ >
50
+ {children}
51
+ </a>
52
+ );
53
+ }
@@ -1,13 +1,16 @@
1
1
  /** @module @category API */
2
+ import { getCurrentUser, LoggedInUser, userHasAccess } from "@openmrs/esm-api";
2
3
  import React, { useEffect, useState } from "react";
3
- import { getCurrentUser, userHasAccess, LoggedInUser } from "@openmrs/esm-api";
4
4
 
5
5
  export interface UserHasAccessProps {
6
6
  privilege: string;
7
+ fallback?: React.ReactNode;
8
+ children?: React.ReactNode;
7
9
  }
8
10
 
9
11
  export const UserHasAccess: React.FC<UserHasAccessProps> = ({
10
12
  privilege,
13
+ fallback,
11
14
  children,
12
15
  }) => {
13
16
  const [user, setUser] = useState<LoggedInUser | null>(null);
@@ -21,7 +24,7 @@ export const UserHasAccess: React.FC<UserHasAccessProps> = ({
21
24
 
22
25
  if (user && userHasAccess(privilege, user)) {
23
26
  return <>{children}</>;
27
+ } else {
28
+ return fallback ? <>{fallback}</> : null;
24
29
  }
25
-
26
- return null;
27
30
  };
@@ -18,6 +18,7 @@ const defaultOpts = {
18
18
  interface I18nextLoadNamespaceProps {
19
19
  forceUpdate(): void;
20
20
  ns: string;
21
+ children?: React.ReactNode;
21
22
  }
22
23
 
23
24
  const I18nextLoadNamespace: React.FC<I18nextLoadNamespaceProps> = (props) => {
@@ -80,7 +81,7 @@ export function openmrsComponentDecorator(userOpts: ComponentDecoratorOptions) {
80
81
  const opts = Object.assign({}, defaultOpts, userOpts);
81
82
 
82
83
  return function decorateComponent(
83
- Comp: React.ComponentType
84
+ Comp: React.ComponentType<any>
84
85
  ): React.ComponentType<any> {
85
86
  return class OpenmrsReactComponent extends React.Component<
86
87
  OpenmrsReactComponentProps,
@@ -1,7 +1,7 @@
1
1
  /** @module @category UI */
2
2
  import { useEffect, useState } from "react";
3
3
 
4
- export type LayoutType = "tablet" | "phone" | "desktop";
4
+ export type LayoutType = "phone" | "tablet" | "small-desktop" | "large-desktop";
5
5
 
6
6
  function getLayout() {
7
7
  let layout: LayoutType = "tablet";
@@ -11,8 +11,11 @@ function getLayout() {
11
11
  case "omrs-breakpoint-lt-tablet":
12
12
  layout = "phone";
13
13
  break;
14
+ case "omrs-breakpoint-gt-small-desktop":
15
+ layout = "large-desktop";
16
+ break;
14
17
  case "omrs-breakpoint-gt-tablet":
15
- layout = "desktop";
18
+ layout = "small-desktop";
16
19
  break;
17
20
  }
18
21
  });
@@ -33,3 +36,6 @@ export function useLayoutType() {
33
36
 
34
37
  return type;
35
38
  }
39
+
40
+ export const isDesktop = (layout: LayoutType) =>
41
+ layout === "small-desktop" || layout === "large-desktop";