diff options
Diffstat (limited to 'src/utils')
| -rw-r--r-- | src/utils/hooks/index.ts | 2 | ||||
| -rw-r--r-- | src/utils/hooks/use-topic/index.ts | 1 | ||||
| -rw-r--r-- | src/utils/hooks/use-topic/use-topic.test.ts | 54 | ||||
| -rw-r--r-- | src/utils/hooks/use-topic/use-topic.ts | 28 | ||||
| -rw-r--r-- | src/utils/hooks/use-topics-list/index.ts | 1 | ||||
| -rw-r--r-- | src/utils/hooks/use-topics-list/use-topics-list.test.ts | 48 | ||||
| -rw-r--r-- | src/utils/hooks/use-topics-list/use-topics-list.ts | 46 |
7 files changed, 180 insertions, 0 deletions
diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts index 3fb0ad4..da4ed9e 100644 --- a/src/utils/hooks/index.ts +++ b/src/utils/hooks/index.ts @@ -27,3 +27,5 @@ export * from './use-thematics-list'; export * from './use-theme'; export * from './use-timeout'; export * from './use-toggle'; +export * from './use-topic'; +export * from './use-topics-list'; diff --git a/src/utils/hooks/use-topic/index.ts b/src/utils/hooks/use-topic/index.ts new file mode 100644 index 0000000..e87ab38 --- /dev/null +++ b/src/utils/hooks/use-topic/index.ts @@ -0,0 +1 @@ +export * from './use-topic'; diff --git a/src/utils/hooks/use-topic/use-topic.test.ts b/src/utils/hooks/use-topic/use-topic.test.ts new file mode 100644 index 0000000..e160a3e --- /dev/null +++ b/src/utils/hooks/use-topic/use-topic.test.ts @@ -0,0 +1,54 @@ +import { + afterEach, + beforeEach, + describe, + expect, + it, + jest, +} from '@jest/globals'; +import { renderHook, waitFor } from '@testing-library/react'; +import { wpTopicsFixture } from '../../../../tests/fixtures'; +import { ROUTES } from '../../constants'; +import { useTopic } from './use-topic'; + +describe('useTopic', () => { + beforeEach(() => { + /* Not sure why it is needed, but without it Jest was complaining with + * `Jest worker encountered 4 child process exceptions`... Maybe because of + * useSWR? */ + jest.useFakeTimers({ + doNotFake: ['queueMicrotask'], + }); + }); + + afterEach(() => { + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + }); + + /* eslint-disable max-statements */ + it('fetch the requested topic', async () => { + const { result } = renderHook(() => useTopic(wpTopicsFixture[0].slug)); + + // Inaccurate assertions count because of waitFor... + //expect.assertions(8); + expect.hasAssertions(); + + expect(result.current.topic).toBeUndefined(); + expect(result.current.isError).toBe(false); + expect(result.current.isLoading).toBe(true); + expect(result.current.isValidating).toBe(true); + + jest.advanceTimersToNextTimer(); + + await waitFor(() => + expect(result.current.topic?.slug).toBe( + `${ROUTES.TOPICS}/${wpTopicsFixture[0].slug}` + ) + ); + expect(result.current.isError).toBe(false); + expect(result.current.isLoading).toBe(false); + expect(result.current.isValidating).toBe(false); + }); + /* eslint-enable max-statements */ +}); diff --git a/src/utils/hooks/use-topic/use-topic.ts b/src/utils/hooks/use-topic/use-topic.ts new file mode 100644 index 0000000..bd7ee49 --- /dev/null +++ b/src/utils/hooks/use-topic/use-topic.ts @@ -0,0 +1,28 @@ +import useSWR from 'swr'; +import { convertWPTopicToTopic, fetchTopic } from '../../../services/graphql'; +import type { Maybe, Topic, WPTopic } from '../../../types'; + +export type UseTopicReturn<T extends Maybe<WPTopic>> = { + isError: boolean; + isLoading: boolean; + isValidating: boolean; + topic: T extends undefined ? Maybe<Topic> : Topic; +}; + +export const useTopic = <T extends Maybe<WPTopic>>( + slug: string, + fallback?: T +): UseTopicReturn<T> => { + const { data, error, isLoading, isValidating } = useSWR(slug, fetchTopic, { + fallbackData: fallback, + }); + + if (error) console.error(error); + + return { + isError: !!error, + isLoading, + isValidating, + topic: data ? convertWPTopicToTopic(data) : undefined, + } as UseTopicReturn<T>; +}; diff --git a/src/utils/hooks/use-topics-list/index.ts b/src/utils/hooks/use-topics-list/index.ts new file mode 100644 index 0000000..c08400f --- /dev/null +++ b/src/utils/hooks/use-topics-list/index.ts @@ -0,0 +1 @@ +export * from './use-topics-list'; diff --git a/src/utils/hooks/use-topics-list/use-topics-list.test.ts b/src/utils/hooks/use-topics-list/use-topics-list.test.ts new file mode 100644 index 0000000..c8fa607 --- /dev/null +++ b/src/utils/hooks/use-topics-list/use-topics-list.test.ts @@ -0,0 +1,48 @@ +import { + afterEach, + beforeEach, + describe, + expect, + it, + jest, +} from '@jest/globals'; +import { renderHook, waitFor } from '@testing-library/react'; +import { useTopicsList } from './use-topics-list'; + +describe('useTopicsList', () => { + beforeEach(() => { + /* Not sure why it is needed, but without it Jest was complaining with + * `Jest worker encountered 4 child process exceptions`... Maybe because of + * useSWR? */ + jest.useFakeTimers({ + doNotFake: ['queueMicrotask'], + }); + }); + + afterEach(() => { + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + }); + + /* eslint-disable max-statements */ + it('fetch the requested topics list', async () => { + const { result } = renderHook(() => useTopicsList()); + + // Inaccurate assertions count because of waitFor... + //expect.assertions(8); + expect.hasAssertions(); + + expect(result.current.topics).toBeUndefined(); + expect(result.current.isError).toBe(false); + expect(result.current.isLoading).toBe(true); + expect(result.current.isValidating).toBe(true); + + jest.advanceTimersToNextTimer(); + + await waitFor(() => expect(result.current.topics).toBeDefined()); + expect(result.current.isError).toBe(false); + expect(result.current.isLoading).toBe(false); + expect(result.current.isValidating).toBe(false); + }); + /* eslint-enable max-statements */ +}); diff --git a/src/utils/hooks/use-topics-list/use-topics-list.ts b/src/utils/hooks/use-topics-list/use-topics-list.ts new file mode 100644 index 0000000..7860486 --- /dev/null +++ b/src/utils/hooks/use-topics-list/use-topics-list.ts @@ -0,0 +1,46 @@ +import useSWR from 'swr'; +import { + type FetchTopicsListInput, + fetchTopicsList, +} from '../../../services/graphql'; +import type { GraphQLConnection, Maybe, WPTopicPreview } from '../../../types'; + +export type UseTopicsListReturn< + T extends Maybe<GraphQLConnection<WPTopicPreview>>, +> = { + isError: boolean; + isLoading: boolean; + isValidating: boolean; + topics: T extends undefined + ? Maybe<GraphQLConnection<WPTopicPreview>> + : GraphQLConnection<WPTopicPreview>; +}; + +export type UseTopicsListConfig< + T extends Maybe<GraphQLConnection<WPTopicPreview>>, +> = { + input?: FetchTopicsListInput; + fallback?: T; +}; + +export const useTopicsList = < + T extends Maybe<GraphQLConnection<WPTopicPreview>>, +>( + config?: UseTopicsListConfig<T> +): UseTopicsListReturn<T> => { + const { fallback, input } = config ?? {}; + const { data, error, isLoading, isValidating } = useSWR( + input ?? {}, + fetchTopicsList, + { fallbackData: fallback } + ); + + if (error) console.error(error); + + return { + isError: !!error, + isLoading, + isValidating, + topics: data, + } as UseTopicsListReturn<T>; +}; |
