@openmrs/esm-react-utils 5.3.3-pre.1237 → 5.3.3-pre.1247

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.
Files changed (48) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/__mocks__/openmrs-esm-state.mock.ts +8 -20
  3. package/dist/openmrs-esm-react-utils.js.map +1 -1
  4. package/jest.config.js +9 -11
  5. package/package.json +7 -7
  6. package/src/ComponentContext.ts +2 -2
  7. package/src/ConfigurableLink.test.tsx +19 -23
  8. package/src/ConfigurableLink.tsx +5 -19
  9. package/src/Extension.tsx +36 -76
  10. package/src/ExtensionSlot.tsx +15 -24
  11. package/src/UserHasAccess.tsx +3 -7
  12. package/src/extensions.test.tsx +100 -179
  13. package/src/getLifecycle.ts +8 -18
  14. package/src/index.ts +31 -31
  15. package/src/openmrsComponentDecorator.test.tsx +12 -12
  16. package/src/openmrsComponentDecorator.tsx +12 -26
  17. package/src/public.ts +27 -27
  18. package/src/setup-tests.js +4 -4
  19. package/src/useAbortController.test.tsx +8 -8
  20. package/src/useAbortController.ts +1 -1
  21. package/src/useAssignedExtensionIds.ts +4 -5
  22. package/src/useAssignedExtensions.ts +3 -7
  23. package/src/useBodyScrollLock.ts +2 -2
  24. package/src/useConfig.test.tsx +96 -112
  25. package/src/useConfig.ts +15 -43
  26. package/src/useConnectedExtensions.ts +8 -17
  27. package/src/useConnectivity.ts +3 -6
  28. package/src/useDebounce.ts +1 -1
  29. package/src/useExtensionInternalStore.ts +3 -8
  30. package/src/useExtensionSlot.ts +5 -7
  31. package/src/useExtensionSlotMeta.ts +5 -11
  32. package/src/useExtensionStore.ts +3 -5
  33. package/src/useFeatureFlag.ts +4 -4
  34. package/src/useForceUpdate.ts +1 -1
  35. package/src/useLayoutType.ts +12 -13
  36. package/src/useLocations.tsx +3 -3
  37. package/src/useOnClickOutside.test.tsx +10 -10
  38. package/src/useOnClickOutside.ts +2 -5
  39. package/src/useOpenmrsSWR.ts +9 -9
  40. package/src/usePagination.ts +6 -18
  41. package/src/usePatient.ts +9 -13
  42. package/src/useSession.test.tsx +35 -43
  43. package/src/useSession.ts +9 -13
  44. package/src/useStore.test.ts +11 -13
  45. package/src/useStore.ts +10 -31
  46. package/src/useVisit.ts +14 -37
  47. package/src/useVisitTypes.ts +3 -3
  48. package/webpack.config.js +14 -14
@@ -1,16 +1,16 @@
1
- import React, { Suspense } from "react";
2
- import { act, render, screen } from "@testing-library/react";
3
- import "@testing-library/jest-dom";
4
- import { useSession, __cleanup } from "./useSession";
5
- import { createGlobalStore } from "@openmrs/esm-state";
6
- import { SessionStore } from "@openmrs/esm-api";
1
+ import React, { Suspense } from 'react';
2
+ import { act, render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import { useSession, __cleanup } from './useSession';
5
+ import { createGlobalStore } from '@openmrs/esm-state';
6
+ import { SessionStore } from '@openmrs/esm-api';
7
7
 
8
- const mockSessionStore = createGlobalStore<SessionStore>("mockSessionStore", {
8
+ const mockSessionStore = createGlobalStore<SessionStore>('mockSessionStore', {
9
9
  loaded: false,
10
10
  session: null,
11
11
  });
12
12
 
13
- jest.mock("@openmrs/esm-api", () => ({
13
+ jest.mock('@openmrs/esm-api', () => ({
14
14
  getSessionStore: jest.fn(() => mockSessionStore),
15
15
  }));
16
16
 
@@ -19,101 +19,93 @@ function Component() {
19
19
  return <div>{JSON.stringify(session)}</div>;
20
20
  }
21
21
 
22
- describe("useSession", () => {
22
+ describe('useSession', () => {
23
23
  beforeEach(() => {
24
24
  __cleanup();
25
25
  mockSessionStore.setState({ loaded: false, session: null });
26
26
  });
27
27
 
28
- it("should suspend and then resolve to the session", async () => {
28
+ it('should suspend and then resolve to the session', async () => {
29
29
  render(
30
- <Suspense fallback={"suspended"}>
30
+ <Suspense fallback={'suspended'}>
31
31
  <Component />
32
- </Suspense>
32
+ </Suspense>,
33
33
  );
34
34
 
35
- expect(screen.getByText("suspended")).toBeInTheDocument();
35
+ expect(screen.getByText('suspended')).toBeInTheDocument();
36
36
  act(() => {
37
37
  mockSessionStore.setState({
38
38
  loaded: true,
39
- session: { authenticated: false, sessionId: "test1" },
39
+ session: { authenticated: false, sessionId: 'test1' },
40
40
  });
41
41
  });
42
42
  await screen.findByText(/"authenticated":false/);
43
43
  });
44
44
 
45
- it("should resolve immediately when the session is present", async () => {
45
+ it('should resolve immediately when the session is present', async () => {
46
46
  mockSessionStore.setState({
47
47
  loaded: true,
48
- session: { authenticated: false, sessionId: "test2" },
48
+ session: { authenticated: false, sessionId: 'test2' },
49
49
  });
50
50
  render(
51
- <Suspense fallback={"suspended"}>
51
+ <Suspense fallback={'suspended'}>
52
52
  <Component />
53
- </Suspense>
53
+ </Suspense>,
54
54
  );
55
55
  expect(screen.getByText(/"authenticated":false/)).toBeInTheDocument();
56
56
  });
57
57
 
58
- it("should not return stale data when re-created", async () => {
58
+ it('should not return stale data when re-created', async () => {
59
59
  const { unmount } = render(
60
- <Suspense fallback={"suspended"}>
60
+ <Suspense fallback={'suspended'}>
61
61
  <Component />
62
- </Suspense>
62
+ </Suspense>,
63
63
  );
64
- expect(screen.getByText("suspended")).toBeInTheDocument();
64
+ expect(screen.getByText('suspended')).toBeInTheDocument();
65
65
  act(() => {
66
66
  mockSessionStore.setState({
67
67
  loaded: true,
68
- session: { authenticated: true, sessionId: "test3" },
68
+ session: { authenticated: true, sessionId: 'test3' },
69
69
  });
70
70
  });
71
71
  await screen.findByText(/"authenticated":true/);
72
72
  unmount();
73
73
  mockSessionStore.setState({
74
74
  loaded: true,
75
- session: { authenticated: false, sessionId: "test3" },
75
+ session: { authenticated: false, sessionId: 'test3' },
76
76
  });
77
77
  render(
78
- <Suspense fallback={"suspended"}>
78
+ <Suspense fallback={'suspended'}>
79
79
  <Component />
80
- </Suspense>
80
+ </Suspense>,
81
81
  );
82
82
  expect(screen.getByText(/"authenticated":false/)).toBeInTheDocument();
83
83
  });
84
84
 
85
- it("supports multiple instances of useSession that all receive updates", async () => {
85
+ it('supports multiple instances of useSession that all receive updates', async () => {
86
86
  mockSessionStore.setState({
87
87
  loaded: true,
88
- session: { authenticated: false, sessionId: "test2" },
88
+ session: { authenticated: false, sessionId: 'test2' },
89
89
  });
90
90
  render(
91
- <Suspense fallback={"suspended"}>
91
+ <Suspense fallback={'suspended'}>
92
92
  <div data-testid="component1">
93
93
  <Component />
94
94
  </div>
95
95
  <div data-testid="component2">
96
96
  <Component />
97
97
  </div>
98
- </Suspense>
99
- );
100
- expect(screen.getByTestId("component1")).toHaveTextContent(
101
- /"authenticated":false/
102
- );
103
- expect(screen.getByTestId("component2")).toHaveTextContent(
104
- /"authenticated":false/
98
+ </Suspense>,
105
99
  );
100
+ expect(screen.getByTestId('component1')).toHaveTextContent(/"authenticated":false/);
101
+ expect(screen.getByTestId('component2')).toHaveTextContent(/"authenticated":false/);
106
102
  act(() => {
107
103
  mockSessionStore.setState({
108
104
  loaded: true,
109
- session: { authenticated: true, sessionId: "test3" },
105
+ session: { authenticated: true, sessionId: 'test3' },
110
106
  });
111
107
  });
112
- expect(screen.getByTestId("component1")).toHaveTextContent(
113
- /"authenticated":true/
114
- );
115
- expect(screen.getByTestId("component2")).toHaveTextContent(
116
- /"authenticated":true/
117
- );
108
+ expect(screen.getByTestId('component1')).toHaveTextContent(/"authenticated":true/);
109
+ expect(screen.getByTestId('component2')).toHaveTextContent(/"authenticated":true/);
118
110
  });
119
111
  });
package/src/useSession.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /** @module @category API */
2
- import { getSessionStore, Session } from "@openmrs/esm-api";
3
- import { useState, useEffect, useRef } from "react";
2
+ import { getSessionStore, Session } from '@openmrs/esm-api';
3
+ import { useState, useEffect, useRef } from 'react';
4
4
 
5
5
  let promise: undefined | Promise<Session>;
6
6
 
@@ -85,14 +85,12 @@ export function useSession(): Session {
85
85
  // get here.
86
86
  useEffect(() => {
87
87
  if (!unsubscribe.current) {
88
- unsubscribe.current = getSessionStore().subscribe(
89
- ({ loaded, session: newSession }) => {
90
- if (loaded) {
91
- session = newSession;
92
- setStateSession(newSession);
93
- }
88
+ unsubscribe.current = getSessionStore().subscribe(({ loaded, session: newSession }) => {
89
+ if (loaded) {
90
+ session = newSession;
91
+ setStateSession(newSession);
94
92
  }
95
- );
93
+ });
96
94
  }
97
95
  return () => {
98
96
  unsubscribe.current && unsubscribe.current();
@@ -103,12 +101,10 @@ export function useSession(): Session {
103
101
  const result = stateSession || session;
104
102
  if (!result) {
105
103
  if (promise) {
106
- console.warn(
107
- "useSession is in an unexpected state. Attempting to recover."
108
- );
104
+ console.warn('useSession is in an unexpected state. Attempting to recover.');
109
105
  throw promise;
110
106
  } else {
111
- throw Error("useSession is in an invalid state.");
107
+ throw Error('useSession is in an invalid state.');
112
108
  }
113
109
  }
114
110
  return result;
@@ -1,10 +1,10 @@
1
- import { act, renderHook } from "@testing-library/react";
2
- import { createGlobalStore } from "@openmrs/esm-state";
3
- import { useStore, useStoreWithActions } from "@openmrs/esm-react-utils";
1
+ import { act, renderHook } from '@testing-library/react';
2
+ import { createGlobalStore } from '@openmrs/esm-state';
3
+ import { useStore, useStoreWithActions } from '@openmrs/esm-react-utils';
4
4
 
5
- describe("useStore", () => {
6
- it("updates state, selects, and correctly binds actions", () => {
7
- const store = createGlobalStore("scoreboard", {
5
+ describe('useStore', () => {
6
+ it('updates state, selects, and correctly binds actions', () => {
7
+ const store = createGlobalStore('scoreboard', {
8
8
  good: { count: 0 },
9
9
  evil: { count: 0 },
10
10
  });
@@ -14,21 +14,19 @@ describe("useStore", () => {
14
14
  }),
15
15
  };
16
16
 
17
- const { result } = renderHook(() =>
18
- useStore(store, (state) => state.good, actions)
19
- );
17
+ const { result } = renderHook(() => useStore(store, (state) => state.good, actions));
20
18
 
21
19
  expect(result.current.count).toBe(0);
22
20
  act(() => {
23
- result.current.tally("good", 2);
21
+ result.current.tally('good', 2);
24
22
  });
25
23
  expect(result.current.count).toBe(2);
26
24
  });
27
25
  });
28
26
 
29
- describe("useStoreWithActions", () => {
30
- it("should correctly bind actions", () => {
31
- const store = createGlobalStore("counter", { count: 0 });
27
+ describe('useStoreWithActions', () => {
28
+ it('should correctly bind actions', () => {
29
+ const store = createGlobalStore('counter', { count: 0 });
32
30
  const actions = {
33
31
  increment: (state) => ({ count: state.count + 1 }),
34
32
  incrementBy: (state, number) => ({ count: state.count + number }),
package/src/useStore.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /** @module @category Store */
2
- import { subscribeTo } from "@openmrs/esm-state";
3
- import { useEffect, useMemo, useState } from "react";
4
- import type { StoreApi } from "zustand";
2
+ import { subscribeTo } from '@openmrs/esm-state';
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import type { StoreApi } from 'zustand';
5
5
 
6
6
  export type ActionFunction<T> = (state: T, ...args: any[]) => Partial<T>;
7
7
  export type Actions<T> =
@@ -10,7 +10,7 @@ export type Actions<T> =
10
10
  export type BoundActions = { [key: string]: (...args: any[]) => void };
11
11
 
12
12
  function bindActions<T>(store: StoreApi<T>, actions: Actions<T>): BoundActions {
13
- if (typeof actions == "function") {
13
+ if (typeof actions == 'function') {
14
14
  actions = actions(store);
15
15
  }
16
16
 
@@ -37,28 +37,13 @@ const defaultSelectFunction = (x) => x;
37
37
 
38
38
  function useStore<T, U>(store: StoreApi<T>): T;
39
39
  function useStore<T, U>(store: StoreApi<T>, select: (state: T) => U): U;
40
- function useStore<T, U>(
41
- store: StoreApi<T>,
42
- select: undefined,
43
- actions: Actions<T>
44
- ): T & BoundActions;
45
- function useStore<T, U>(
46
- store: StoreApi<T>,
47
- select: (state: T) => U,
48
- actions: Actions<T>
49
- ): U & BoundActions;
50
- function useStore<T, U>(
51
- store: StoreApi<T>,
52
- select: (state: T) => U = defaultSelectFunction,
53
- actions?: Actions<T>
54
- ) {
40
+ function useStore<T, U>(store: StoreApi<T>, select: undefined, actions: Actions<T>): T & BoundActions;
41
+ function useStore<T, U>(store: StoreApi<T>, select: (state: T) => U, actions: Actions<T>): U & BoundActions;
42
+ function useStore<T, U>(store: StoreApi<T>, select: (state: T) => U = defaultSelectFunction, actions?: Actions<T>) {
55
43
  const [state, setState] = useState<U>(() => select(store.getState()));
56
44
  useEffect(() => subscribeTo(store, select, setState), [store, select]);
57
45
 
58
- let boundActions: BoundActions = useMemo(
59
- () => (actions ? bindActions(store, actions) : {}),
60
- [store, actions]
61
- );
46
+ let boundActions: BoundActions = useMemo(() => (actions ? bindActions(store, actions) : {}), [store, actions]);
62
47
 
63
48
  return { ...state, ...boundActions };
64
49
  }
@@ -69,10 +54,7 @@ function useStore<T, U>(
69
54
  * @param actions
70
55
  * @returns
71
56
  */
72
- function useStoreWithActions<T>(
73
- store: StoreApi<T>,
74
- actions: Actions<T>
75
- ): T & BoundActions {
57
+ function useStoreWithActions<T>(store: StoreApi<T>, actions: Actions<T>): T & BoundActions {
76
58
  return useStore(store, defaultSelectFunction, actions);
77
59
  }
78
60
 
@@ -87,10 +69,7 @@ function createUseStore<T>(store: StoreApi<T>) {
87
69
  function useStore(actions?: Actions<T>) {
88
70
  const [state, set] = useState(store.getState());
89
71
  useEffect(() => store.subscribe((state) => set(state)), []);
90
- let boundActions: BoundActions = useMemo(
91
- () => (actions ? bindActions(store, actions) : {}),
92
- [actions]
93
- );
72
+ let boundActions: BoundActions = useMemo(() => (actions ? bindActions(store, actions) : {}), [actions]);
94
73
 
95
74
  return { ...state, ...boundActions };
96
75
  }
package/src/useVisit.ts CHANGED
@@ -1,15 +1,10 @@
1
1
  /** @module @category API */
2
- import {
3
- defaultVisitCustomRepresentation,
4
- getVisitStore,
5
- openmrsFetch,
6
- Visit,
7
- } from "@openmrs/esm-api";
8
- import useSWR from "swr";
9
- import dayjs from "dayjs";
10
- import isToday from "dayjs/plugin/isToday";
11
- import { useMemo } from "react";
12
- import { useStore } from "./useStore";
2
+ import { defaultVisitCustomRepresentation, getVisitStore, openmrsFetch, Visit } from '@openmrs/esm-api';
3
+ import useSWR from 'swr';
4
+ import dayjs from 'dayjs';
5
+ import isToday from 'dayjs/plugin/isToday';
6
+ import { useMemo } from 'react';
7
+ import { useStore } from './useStore';
13
8
 
14
9
  dayjs.extend(isToday);
15
10
 
@@ -43,14 +38,9 @@ export interface VisitReturnType {
43
38
  * @param patientUuid Unique patient identifier `string`
44
39
  */
45
40
  export function useVisit(patientUuid: string): VisitReturnType {
46
- const { patientUuid: visitStorePatientUuid, manuallySetVisitUuid } = useStore(
47
- getVisitStore()
48
- );
41
+ const { patientUuid: visitStorePatientUuid, manuallySetVisitUuid } = useStore(getVisitStore());
49
42
  // Ignore the visit store data if it is not for this patient
50
- const retrospectiveVisitUuid =
51
- patientUuid && visitStorePatientUuid == patientUuid
52
- ? manuallySetVisitUuid
53
- : null;
43
+ const retrospectiveVisitUuid = patientUuid && visitStorePatientUuid == patientUuid ? manuallySetVisitUuid : null;
54
44
  const activeVisitUrlSuffix = `?patient=${patientUuid}&v=${defaultVisitCustomRepresentation}&includeInactive=false`;
55
45
  const retrospectiveVisitUrlSuffix = `/${retrospectiveVisitUuid}`;
56
46
 
@@ -61,10 +51,7 @@ export function useVisit(patientUuid: string): VisitReturnType {
61
51
  isValidating: activeIsValidating,
62
52
  } = useSWR<{
63
53
  data: Visit | { results: Array<Visit> };
64
- }>(
65
- patientUuid ? `/ws/rest/v1/visit${activeVisitUrlSuffix}` : null,
66
- openmrsFetch
67
- );
54
+ }>(patientUuid ? `/ws/rest/v1/visit${activeVisitUrlSuffix}` : null, openmrsFetch);
68
55
 
69
56
  const {
70
57
  data: retroData,
@@ -73,23 +60,16 @@ export function useVisit(patientUuid: string): VisitReturnType {
73
60
  isValidating: retroIsValidating,
74
61
  } = useSWR<{
75
62
  data: Visit | { results: Array<Visit> };
76
- }>(
77
- patientUuid && retrospectiveVisitUuid
78
- ? `/ws/rest/v1/visit${retrospectiveVisitUrlSuffix}`
79
- : null,
80
- openmrsFetch
81
- );
63
+ }>(patientUuid && retrospectiveVisitUuid ? `/ws/rest/v1/visit${retrospectiveVisitUrlSuffix}` : null, openmrsFetch);
82
64
 
83
65
  const activeVisit = useMemo(
84
- () =>
85
- activeData?.data.results.find((visit) => visit.stopDatetime === null) ??
86
- null,
87
- [activeData]
66
+ () => activeData?.data.results.find((visit) => visit.stopDatetime === null) ?? null,
67
+ [activeData],
88
68
  );
89
69
 
90
70
  const currentVisit = useMemo(
91
71
  () => (retrospectiveVisitUuid ? retroData?.data : activeVisit ?? null),
92
- [retroData, activeVisit, retrospectiveVisitUuid]
72
+ [retroData, activeVisit, retrospectiveVisitUuid],
93
73
  );
94
74
 
95
75
  return {
@@ -102,9 +82,6 @@ export function useVisit(patientUuid: string): VisitReturnType {
102
82
  activeVisit,
103
83
  currentVisit,
104
84
  currentVisitIsRetrospective: Boolean(retrospectiveVisitUuid),
105
- isLoading: Boolean(
106
- (!activeData || (retrospectiveVisitUuid && !retroData)) &&
107
- (!activeError || !retroError)
108
- ),
85
+ isLoading: Boolean((!activeData || (retrospectiveVisitUuid && !retroData)) && (!activeError || !retroError)),
109
86
  };
110
87
  }
@@ -1,6 +1,6 @@
1
1
  /** @module @category API */
2
- import { getVisitTypes, VisitType } from "@openmrs/esm-api";
3
- import { useEffect, useState } from "react";
2
+ import { getVisitTypes, VisitType } from '@openmrs/esm-api';
3
+ import { useEffect, useState } from 'react';
4
4
 
5
5
  export function useVisitTypes() {
6
6
  const [visitTypes, setVisitTypes] = useState<Array<VisitType>>([]);
@@ -10,7 +10,7 @@ export function useVisitTypes() {
10
10
  (visitTypes) => {
11
11
  setVisitTypes(visitTypes);
12
12
  },
13
- (error) => console.error(error)
13
+ (error) => console.error(error),
14
14
  );
15
15
 
16
16
  return () => visitTypesSub.unsubscribe();
package/webpack.config.js CHANGED
@@ -1,42 +1,42 @@
1
- const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
2
- const { resolve } = require("path");
3
- const { CleanWebpackPlugin } = require("clean-webpack-plugin");
4
- const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
1
+ const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
2
+ const { resolve } = require('path');
3
+ const { CleanWebpackPlugin } = require('clean-webpack-plugin');
4
+ const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
5
5
 
6
- const { peerDependencies } = require("./package.json");
6
+ const { peerDependencies } = require('./package.json');
7
7
 
8
8
  module.exports = (env) => ({
9
- entry: [resolve(__dirname, "src/index.ts")],
9
+ entry: [resolve(__dirname, 'src/index.ts')],
10
10
  output: {
11
- filename: "openmrs-esm-react-utils.js",
12
- path: resolve(__dirname, "dist"),
13
- library: { type: "system" },
11
+ filename: 'openmrs-esm-react-utils.js',
12
+ path: resolve(__dirname, 'dist'),
13
+ library: { type: 'system' },
14
14
  },
15
- devtool: "source-map",
15
+ devtool: 'source-map',
16
16
  module: {
17
17
  rules: [
18
18
  {
19
19
  test: /\.m?(js|ts|tsx)$/,
20
20
  exclude: /node_modules/,
21
- use: "swc-loader",
21
+ use: 'swc-loader',
22
22
  },
23
23
  ],
24
24
  },
25
25
  resolve: {
26
- extensions: [".ts", ".js", ".tsx", ".jsx"],
26
+ extensions: ['.ts', '.js', '.tsx', '.jsx'],
27
27
  },
28
28
  plugins: [
29
29
  new CleanWebpackPlugin(),
30
30
  new ForkTsCheckerWebpackPlugin(),
31
31
  new BundleAnalyzerPlugin({
32
- analyzerMode: env && env.analyze ? "static" : "disabled",
32
+ analyzerMode: env && env.analyze ? 'static' : 'disabled',
33
33
  }),
34
34
  ],
35
35
  externals: Object.keys(peerDependencies || {}),
36
36
  devServer: {
37
37
  disableHostCheck: true,
38
38
  headers: {
39
- "Access-Control-Allow-Origin": "*",
39
+ 'Access-Control-Allow-Origin': '*',
40
40
  },
41
41
  },
42
42
  });