@availity/hooks 5.2.0 → 6.0.0

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 (58) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.d.ts +208 -0
  3. package/dist/index.js +156 -0
  4. package/package.json +25 -15
  5. package/project.json +14 -3
  6. package/src/index.ts +20 -0
  7. package/src/useCurrentRegion.ts +23 -0
  8. package/src/useCurrentUser.ts +24 -0
  9. package/src/useEffectAsync.ts +9 -0
  10. package/src/useMount.ts +6 -0
  11. package/src/useOrganizations.ts +73 -0
  12. package/src/usePermissions.ts +23 -0
  13. package/src/useProviders.ts +48 -0
  14. package/src/useStash.ts +21 -0
  15. package/src/{useTimeout.js → useTimeout.ts} +1 -1
  16. package/src/{useToggle.js → useToggle.ts} +2 -2
  17. package/src/{useUpdateNav.js → useUpdateNav.ts} +1 -1
  18. package/src/{useWindowDimensions.js → useWindowDimensions.ts} +9 -9
  19. package/stories/ResourceComponent.tsx +12 -10
  20. package/tests/useCurrentRegion.test.jsx +96 -0
  21. package/tests/useCurrentUser.test.jsx +94 -0
  22. package/tests/useEffectAsync.test.jsx +36 -0
  23. package/tests/useMount.test.jsx +24 -0
  24. package/tests/useOrganizations.test.jsx +112 -0
  25. package/tests/usePermissions.test.jsx +90 -0
  26. package/tests/useProviders.test.jsx +106 -0
  27. package/tests/useStash.test.jsx +89 -0
  28. package/tests/useTimeout.test.jsx +33 -0
  29. package/tests/useToggle.test.jsx +42 -0
  30. package/tests/useUpdateNav.test.jsx +46 -0
  31. package/tests/useWindowDimensions.test.jsx +38 -0
  32. package/vitest.config.ts +17 -0
  33. package/index.d.ts +0 -12
  34. package/index.js +0 -12
  35. package/jest.config.js +0 -7
  36. package/src/useCurrentRegion.js +0 -15
  37. package/src/useCurrentUser.js +0 -8
  38. package/src/useEffectAsync.js +0 -8
  39. package/src/useMount.js +0 -6
  40. package/src/useOrganizations.js +0 -8
  41. package/src/usePermissions.js +0 -8
  42. package/src/useProviders.js +0 -8
  43. package/src/useStash.js +0 -14
  44. package/tsconfig.spec.json +0 -10
  45. package/types/useCurrentRegion.d.ts +0 -12
  46. package/types/useCurrentUser.d.ts +0 -21
  47. package/types/useEffectAsync.d.ts +0 -3
  48. package/types/useMount.d.ts +0 -5
  49. package/types/useOrganizations.d.ts +0 -75
  50. package/types/usePermissions.d.ts +0 -22
  51. package/types/useProviders.d.ts +0 -50
  52. package/types/useStash.d.ts +0 -10
  53. package/types/useTimeout.d.ts +0 -3
  54. package/types/useToggle.d.ts +0 -3
  55. package/types/useUpdateNav.d.ts +0 -3
  56. package/types/useWindowDimensions.d.ts +0 -8
  57. /package/{types/aries.d.ts → src/types.ts} +0 -0
  58. /package/tests/{util.js → util.jsx} +0 -0
@@ -0,0 +1,48 @@
1
+ import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
2
+ import { avProvidersApi } from '@availity/api-axios';
3
+ import { AriesHookBase } from './types';
4
+
5
+ export interface ProvidersResponse extends AriesHookBase {
6
+ data: AriesHookBase['data'] & {
7
+ providers: {
8
+ id: string;
9
+ lastName: string;
10
+ firstName: string;
11
+ middleName: string;
12
+ uiDisplayName: string;
13
+ atypical: boolean;
14
+ npi: string;
15
+ customerIds: string[];
16
+ roles: { code: string; value: string }[];
17
+ primarySpecialty: { code: string; value: string };
18
+ primaryFax: {
19
+ internationalCellularCode: string;
20
+ areaCode: string;
21
+ phoneNumber: string;
22
+ };
23
+ primaryAddress: {
24
+ line1: string;
25
+ line2: string;
26
+ city: string;
27
+ state: string;
28
+ stateCode: string;
29
+ zip: { code: string; addon: string };
30
+ };
31
+ }[];
32
+ };
33
+ }
34
+
35
+ export interface AvProvidersConfig {
36
+ customerId: string;
37
+ [key: string]: unknown;
38
+ }
39
+
40
+ const fetchProviders = async (config: AvProvidersConfig) =>
41
+ avProvidersApi.getProviders(config.customerId, config) as unknown as ProvidersResponse;
42
+
43
+ export default function useProviders(
44
+ config: AvProvidersConfig,
45
+ options?: Omit<UseQueryOptions<ProvidersResponse, unknown>, 'queryKey' | 'queryFn'>
46
+ ): UseQueryResult<ProvidersResponse, unknown> {
47
+ return useQuery({ queryKey: ['providers', config], queryFn: () => fetchProviders(config), ...options });
48
+ }
@@ -0,0 +1,21 @@
1
+ import { avStashApi } from '@availity/api-axios';
2
+ import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
3
+
4
+ export type StashData = Record<string, unknown>;
5
+
6
+ const fetchStash = async (sessionId: string) => {
7
+ const response = await avStashApi.get(sessionId);
8
+ return response?.data;
9
+ };
10
+
11
+ export default function useStash(
12
+ sessionId: string,
13
+ options?: Omit<UseQueryOptions<StashData, unknown>, 'queryKey' | 'queryFn'>
14
+ ): UseQueryResult<StashData, unknown> {
15
+ return useQuery({
16
+ queryKey: ['stash', sessionId],
17
+ queryFn: () => fetchStash(sessionId),
18
+ enabled: !!sessionId,
19
+ ...options,
20
+ });
21
+ }
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useState } from 'react';
2
2
 
3
- export default function useTimeout(ms = 0) {
3
+ export default function useTimeout(ms = 0): boolean {
4
4
  const [ready, setReady] = useState(false);
5
5
 
6
6
  useEffect(() => {
@@ -1,8 +1,8 @@
1
1
  import { useState } from 'react';
2
2
 
3
- export default function useToggle(initialState = false) {
3
+ export default function useToggle(initialState = false): [boolean, (state?: boolean) => void] {
4
4
  const [state, setState] = useState(initialState);
5
- const toggle = (newState) => {
5
+ const toggle = (newState?: boolean) => {
6
6
  if (newState !== undefined && newState !== state) {
7
7
  setState(newState);
8
8
  } else if (newState === undefined) {
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
2
2
  import { useLocation } from 'react-router-dom';
3
3
  import avMessage from '@availity/message-core';
4
4
 
5
- const useUpdateNav = () => {
5
+ const useUpdateNav = (): void => {
6
6
  const location = useLocation();
7
7
 
8
8
  useEffect(() => {
@@ -1,14 +1,16 @@
1
1
  import { useEffect, useState } from 'react';
2
2
 
3
- const getWindowDimensions = () => {
3
+ export interface Dimensions {
4
+ width: number;
5
+ height: number;
6
+ }
7
+
8
+ const getWindowDimensions = (): Dimensions => {
4
9
  const { innerWidth: width, innerHeight: height } = window;
5
- return {
6
- width,
7
- height,
8
- };
10
+ return { width, height };
9
11
  };
10
12
 
11
- const useWindowDimensions = () => {
13
+ export default function useWindowDimensions(): Dimensions {
12
14
  const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
13
15
 
14
16
  useEffect(() => {
@@ -21,6 +23,4 @@ const useWindowDimensions = () => {
21
23
  }, []);
22
24
 
23
25
  return windowDimensions;
24
- };
25
-
26
- export default useWindowDimensions;
26
+ }
@@ -1,19 +1,21 @@
1
1
  import React from 'react';
2
2
  import { Card, CardBody, CardTitle } from 'reactstrap';
3
3
 
4
- type Props = {
5
- data: Record<string, unknown>;
4
+ type Props<TData> = {
5
+ data: TData;
6
6
  loading: boolean;
7
7
  title?: string;
8
8
  };
9
9
 
10
- const ResourceComponent = ({ data, loading, title = '' }: Props): JSX.Element => (
11
- <Card body>
12
- <CardTitle className="text-center" tag="h4">
13
- {title}
14
- </CardTitle>
15
- <CardBody>{loading ? 'Loading...' : <pre>{JSON.stringify(data, null, 2)}</pre>}</CardBody>
16
- </Card>
17
- );
10
+ function ResourceComponent<TData>({ data, loading, title = '' }: Props<TData>) {
11
+ return (
12
+ <Card body>
13
+ <CardTitle className="text-center" tag="h4">
14
+ {title}
15
+ </CardTitle>
16
+ <CardBody>{loading ? 'Loading...' : <pre>{JSON.stringify(data, null, 2)}</pre>}</CardBody>
17
+ </Card>
18
+ );
19
+ }
18
20
 
19
21
  export default ResourceComponent;
@@ -0,0 +1,96 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { waitFor, cleanup } from '@testing-library/react';
4
+ import { avRegionsApi } from '@availity/api-axios';
5
+ import { QueryClient } from '@tanstack/react-query';
6
+ import { useCurrentRegion } from '../src/index';
7
+ import renderWithClient from './util';
8
+
9
+ vi.mock('@availity/api-axios');
10
+
11
+ let queryStates = [];
12
+ beforeEach(() => {
13
+ queryStates = [];
14
+ });
15
+
16
+ const queryClient = new QueryClient();
17
+
18
+ afterEach(() => {
19
+ vi.clearAllMocks();
20
+ cleanup();
21
+ queryClient.clear();
22
+ queryStates = [];
23
+ });
24
+
25
+ const pushState = (state) => {
26
+ queryStates.push(state);
27
+ };
28
+
29
+ const Component = ({ log }) => {
30
+ // Mirror testing methods from react-query instead of relying on timing or booleans
31
+ // https://github.com/tannerlinsley/react-query/blob/master/src/react/tests/useQuery.test.tsx
32
+ const state = useCurrentRegion({ gcTime: 0, retry: false });
33
+
34
+ // not directly used in assertions here, but useful for debugging purposes
35
+ if (log) log(state);
36
+
37
+ const { data, error, status } = state;
38
+
39
+ return (
40
+ <div>
41
+ <h1>Status: {status}</h1>
42
+ <h1>Data: {JSON.stringify(data)}</h1>
43
+ <h1>Error: {error}</h1>
44
+ </div>
45
+ );
46
+ };
47
+
48
+ Component.propTypes = {
49
+ log: PropTypes.func,
50
+ };
51
+
52
+ describe('useCurrentRegion', () => {
53
+ test('handle error', async () => {
54
+ avRegionsApi.getCurrentRegion.mockRejectedValueOnce('An error occurred');
55
+ const { getByText } = renderWithClient(queryClient, <Component log={pushState} />);
56
+
57
+ getByText('Status: pending');
58
+ await waitFor(() => {
59
+ const el = getByText('Status: error');
60
+ expect(el).toBeDefined();
61
+ });
62
+ await waitFor(() => {
63
+ const el = getByText('Error: An error occurred');
64
+ expect(el).toBeDefined();
65
+ });
66
+ });
67
+
68
+ test('handle success', async () => {
69
+ avRegionsApi.getCurrentRegion.mockResolvedValueOnce({
70
+ config: { polling: false },
71
+ data: {
72
+ regions: [
73
+ {
74
+ id: 'FL',
75
+ value: 'Florida',
76
+ },
77
+ ],
78
+ },
79
+ status: 200,
80
+ statusText: 'Ok',
81
+ });
82
+
83
+ const { getByText } = renderWithClient(queryClient, <Component log={pushState} />);
84
+
85
+ getByText('Status: pending');
86
+ await waitFor(() => {
87
+ const el = getByText(
88
+ `Data: ${JSON.stringify({
89
+ code: 'FL',
90
+ value: 'Florida',
91
+ })}`
92
+ );
93
+ expect(el).toBeDefined();
94
+ });
95
+ });
96
+ });
@@ -0,0 +1,94 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { waitFor, cleanup } from '@testing-library/react';
4
+ import { avUserApi } from '@availity/api-axios';
5
+ import { QueryClient } from '@tanstack/react-query';
6
+ import { useCurrentUser } from '../src/index';
7
+ import renderWithClient from './util';
8
+
9
+ vi.mock('@availity/api-axios');
10
+
11
+ let queryStates = [];
12
+ beforeEach(() => {
13
+ queryStates = [];
14
+ });
15
+
16
+ const queryClient = new QueryClient();
17
+
18
+ afterEach(() => {
19
+ vi.clearAllMocks();
20
+ cleanup();
21
+ queryClient.clear();
22
+ queryStates = [];
23
+ });
24
+
25
+ const pushState = (state) => {
26
+ queryStates.push(state);
27
+ };
28
+
29
+ const Component = ({ log }) => {
30
+ // Mirror testing methods from react-query instead of relying on timing or booleans
31
+ // https://github.com/tannerlinsley/react-query/blob/master/src/react/tests/useQuery.test.tsx
32
+ const state = useCurrentUser({ gcTime: 0, retry: false });
33
+
34
+ // not directly used in assertions here, but useful for debugging purposes
35
+ if (log) log(state);
36
+
37
+ const { data, error, status } = state;
38
+
39
+ return (
40
+ <div>
41
+ <h1>Status: {status}</h1>
42
+ <h1>Data: {JSON.stringify(data)}</h1>
43
+ <h1>Error: {error}</h1>
44
+ </div>
45
+ );
46
+ };
47
+
48
+ Component.propTypes = {
49
+ log: PropTypes.func,
50
+ };
51
+
52
+ describe('useCurrentUser', () => {
53
+ test('should set error on rejected promise', async () => {
54
+ avUserApi.me.mockRejectedValueOnce('An error occurred');
55
+
56
+ const { getByText } = renderWithClient(queryClient, <Component log={pushState} />);
57
+
58
+ getByText('Status: pending');
59
+ await waitFor(() => {
60
+ const el = getByText('Status: error');
61
+ expect(el).toBeDefined();
62
+ });
63
+ await waitFor(() => {
64
+ const el = getByText('Error: An error occurred');
65
+ expect(el).toBeDefined();
66
+ });
67
+ });
68
+
69
+ test('should return user', async () => {
70
+ avUserApi.me.mockResolvedValueOnce({
71
+ id: 'aka12345',
72
+ userId: 'testExample',
73
+ akaname: 'aka12345',
74
+ lastName: 'Last',
75
+ firstName: 'First',
76
+ });
77
+
78
+ const { getByText } = renderWithClient(queryClient, <Component log={pushState} />);
79
+
80
+ getByText('Status: pending');
81
+ await waitFor(() => {
82
+ const el = getByText(
83
+ `Data: ${JSON.stringify({
84
+ id: 'aka12345',
85
+ userId: 'testExample',
86
+ akaname: 'aka12345',
87
+ lastName: 'Last',
88
+ firstName: 'First',
89
+ })}`
90
+ );
91
+ expect(el).toBeDefined();
92
+ });
93
+ });
94
+ });
@@ -0,0 +1,36 @@
1
+ import React, { useState } from 'react';
2
+ import { render, waitFor, act, cleanup } from '@testing-library/react';
3
+ import { useEffectAsync } from '../src/index';
4
+
5
+ afterEach(cleanup);
6
+
7
+ // eslint-disable-next-line react/prop-types
8
+ const Component = ({ asyncFunc }) => {
9
+ const [state, setState] = useState('Hello');
10
+
11
+ useEffectAsync(async () => {
12
+ const newState = await asyncFunc();
13
+
14
+ act(() => setState(newState));
15
+ }, []);
16
+
17
+ return <div data-testid="effect-test">{state}</div>;
18
+ };
19
+ const asyncFunc = () => Promise.resolve('World');
20
+
21
+ describe('useEffectAsync', () => {
22
+ test('should render "Hello" then "World"', async () => {
23
+ // Create Async Method
24
+ // Render
25
+ const { getByTestId } = render(<Component asyncFunc={asyncFunc} />);
26
+
27
+ // Expect the component to render "Hello"
28
+ expect(getByTestId('effect-test').textContent).toEqual('Hello');
29
+
30
+ // Wait for the Async Function to be called after mounting
31
+ await waitFor(() => getByTestId('effect-test'));
32
+
33
+ // Expect the component to render "World"
34
+ expect(getByTestId('effect-test').textContent).toEqual('World');
35
+ });
36
+ });
@@ -0,0 +1,24 @@
1
+ import React, { useState } from 'react';
2
+ import { render, cleanup } from '@testing-library/react';
3
+ import { useMount } from '../src/index';
4
+
5
+ afterEach(cleanup);
6
+
7
+ // eslint-disable-next-line react/prop-types
8
+ const Component = () => {
9
+ const [state, setState] = useState();
10
+
11
+ useMount(() => {
12
+ setState('test');
13
+ });
14
+
15
+ return <p data-testid="mount-test">{state}</p>;
16
+ };
17
+
18
+ describe('useMount', () => {
19
+ test('should render "test"', () => {
20
+ const { getByTestId } = render(<Component />);
21
+
22
+ expect(getByTestId('mount-test').textContent).toEqual('test');
23
+ });
24
+ });
@@ -0,0 +1,112 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { waitFor, cleanup } from '@testing-library/react';
4
+ import { avOrganizationsApi } from '@availity/api-axios';
5
+ import { QueryClient } from '@tanstack/react-query';
6
+ import renderWithClient from './util';
7
+ import { useOrganizations } from '../src/index';
8
+
9
+ vi.mock('@availity/api-axios');
10
+
11
+ let queryStates = [];
12
+ beforeEach(() => {
13
+ queryStates = [];
14
+ });
15
+
16
+ const queryClient = new QueryClient();
17
+
18
+ afterEach(() => {
19
+ vi.clearAllMocks();
20
+ cleanup();
21
+ queryClient.clear();
22
+ queryStates = [];
23
+ });
24
+
25
+ const pushState = (state) => {
26
+ queryStates.push(state);
27
+ };
28
+
29
+ const Component = ({ log }) => {
30
+ // Mirror testing methods from react-query instead of relying on timing or booleans
31
+ // https://github.com/tannerlinsley/react-query/blob/master/src/react/tests/useQuery.test.tsx
32
+ const state = useOrganizations({}, { gcTime: 0, retry: false });
33
+
34
+ // not directly used in assertions here, but useful for debugging purposes
35
+ if (log) log(state);
36
+
37
+ const { data, error, status } = state;
38
+
39
+ return (
40
+ <div>
41
+ <h1>Status: {status}</h1>
42
+ <h1>Data: {JSON.stringify(data)}</h1>
43
+ <h1>Error: {error}</h1>
44
+ </div>
45
+ );
46
+ };
47
+
48
+ Component.propTypes = {
49
+ log: PropTypes.func,
50
+ };
51
+
52
+ describe('useOrganizations', () => {
53
+ test('should return an error', async () => {
54
+ avOrganizationsApi.getOrganizations.mockRejectedValueOnce('An error occurred');
55
+
56
+ const { getByText } = renderWithClient(queryClient, <Component log={pushState} />);
57
+
58
+ getByText('Status: pending');
59
+ await waitFor(() => {
60
+ const el = getByText('Status: error');
61
+ expect(el).toBeDefined();
62
+ });
63
+ await waitFor(() => {
64
+ const el = getByText('Error: An error occurred');
65
+ expect(el).toBeDefined();
66
+ });
67
+ });
68
+
69
+ test('should return organizations', async () => {
70
+ avOrganizationsApi.getOrganizations.mockResolvedValueOnce({
71
+ data: {
72
+ organizations: [
73
+ {
74
+ links: {
75
+ permissions: { href: 'test' },
76
+ patients: { href: 'test' },
77
+ self: { href: 'test' },
78
+ admin: { href: 'test' },
79
+ businessArrangements: { href: 'test' },
80
+ users: { href: 'test' },
81
+ },
82
+ },
83
+ ],
84
+ },
85
+ });
86
+
87
+ const { getByText } = renderWithClient(queryClient, <Component log={pushState} />);
88
+
89
+ getByText('Status: pending');
90
+ await waitFor(() => {
91
+ const el = getByText(
92
+ `Data: ${JSON.stringify({
93
+ data: {
94
+ organizations: [
95
+ {
96
+ links: {
97
+ permissions: { href: 'test' },
98
+ patients: { href: 'test' },
99
+ self: { href: 'test' },
100
+ admin: { href: 'test' },
101
+ businessArrangements: { href: 'test' },
102
+ users: { href: 'test' },
103
+ },
104
+ },
105
+ ],
106
+ },
107
+ })}`
108
+ );
109
+ expect(el).toBeDefined();
110
+ });
111
+ });
112
+ });
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { waitFor, cleanup } from '@testing-library/react';
4
+ import { avPermissionsApi } from '@availity/api-axios';
5
+ import { QueryClient } from '@tanstack/react-query';
6
+ import renderWithClient from './util';
7
+ import { usePermissions } from '../src/index';
8
+
9
+ vi.mock('@availity/api-axios');
10
+
11
+ let queryStates = [];
12
+ beforeEach(() => {
13
+ queryStates = [];
14
+ });
15
+
16
+ const queryClient = new QueryClient();
17
+
18
+ afterEach(() => {
19
+ vi.clearAllMocks();
20
+ cleanup();
21
+ queryClient.clear();
22
+ queryStates = [];
23
+ });
24
+
25
+ const pushState = (state) => {
26
+ queryStates.push(state);
27
+ };
28
+
29
+ const Component = ({ log }) => {
30
+ // Mirror testing methods from react-query instead of relying on timing or booleans
31
+ // https://github.com/tannerlinsley/react-query/blob/master/src/react/tests/useQuery.test.tsx
32
+ const state = usePermissions({}, { gcTime: 0, retry: false });
33
+
34
+ // not directly used in assertions here, but useful for debugging purposes
35
+ if (log) log(state);
36
+
37
+ const { data, error, status } = state;
38
+
39
+ return (
40
+ <div>
41
+ <h1>Status: {status}</h1>
42
+ <h1>Data: {JSON.stringify(data)}</h1>
43
+ <h1>Error: {error}</h1>
44
+ </div>
45
+ );
46
+ };
47
+
48
+ Component.propTypes = {
49
+ log: PropTypes.func,
50
+ };
51
+
52
+ describe('usePermissions', () => {
53
+ test('should return an error', async () => {
54
+ avPermissionsApi.getPermissions.mockRejectedValueOnce('An error occurred');
55
+
56
+ const { getByText } = renderWithClient(queryClient, <Component log={pushState} />);
57
+
58
+ getByText('Status: pending');
59
+ await waitFor(() => {
60
+ const el = getByText('Status: error');
61
+ expect(el).toBeDefined();
62
+ });
63
+ await waitFor(() => {
64
+ const el = getByText('Error: An error occurred');
65
+ expect(el).toBeDefined();
66
+ });
67
+ });
68
+
69
+ test('should return permissions', async () => {
70
+ avPermissionsApi.getPermissions.mockResolvedValueOnce({
71
+ id: '44',
72
+ description: 'test',
73
+ links: { self: { href: 'test.com' } },
74
+ });
75
+
76
+ const { getByText } = renderWithClient(queryClient, <Component log={pushState} />);
77
+
78
+ getByText('Status: pending');
79
+ await waitFor(() => {
80
+ const el = getByText(
81
+ `Data: ${JSON.stringify({
82
+ id: '44',
83
+ description: 'test',
84
+ links: { self: { href: 'test.com' } },
85
+ })}`
86
+ );
87
+ expect(el).toBeDefined();
88
+ });
89
+ });
90
+ });