diff options
Diffstat (limited to 'src/utils/hooks')
| -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>; +}; | 
