diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-12-01 19:34:58 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-12-04 19:00:04 +0100 |
| commit | 53b63ac27c2275262db9a04be02210a3287aa71d (patch) | |
| tree | 814968e10cad25e1b34ab251de42ac5ecb82b346 /src/utils/hooks/use-articles-list | |
| parent | 11e3ee75fcab0ab54b2bc1713a402c5cc3070c2d (diff) | |
refactor(pages): refine Blog pages
* replace usePostsList with useArticlesList to keep names coherent
* remove useIsMounted hook
* rewrite useRedirection hook
* add redirect in getStaticProps to avoid unecessary fetching
* move Pagination component in a noscript tag
* use hooks to refresh thematics and topics lists
* complete Cypress tests
Diffstat (limited to 'src/utils/hooks/use-articles-list')
| -rw-r--r-- | src/utils/hooks/use-articles-list/index.ts | 1 | ||||
| -rw-r--r-- | src/utils/hooks/use-articles-list/use-articles-list.test.tsx | 109 | ||||
| -rw-r--r-- | src/utils/hooks/use-articles-list/use-articles-list.ts | 86 |
3 files changed, 196 insertions, 0 deletions
diff --git a/src/utils/hooks/use-articles-list/index.ts b/src/utils/hooks/use-articles-list/index.ts new file mode 100644 index 0000000..5f42aeb --- /dev/null +++ b/src/utils/hooks/use-articles-list/index.ts @@ -0,0 +1 @@ +export * from './use-articles-list'; diff --git a/src/utils/hooks/use-articles-list/use-articles-list.test.tsx b/src/utils/hooks/use-articles-list/use-articles-list.test.tsx new file mode 100644 index 0000000..6191ed6 --- /dev/null +++ b/src/utils/hooks/use-articles-list/use-articles-list.test.tsx @@ -0,0 +1,109 @@ +import { + afterEach, + beforeEach, + describe, + expect, + it, + jest, +} from '@jest/globals'; +import { act, renderHook, waitFor } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { SWRConfig } from 'swr'; +import { wpPostsFixture } from '../../../../tests/fixtures'; +import { getConnection } from '../../../../tests/utils/graphql'; +import { convertPostPreviewToArticlePreview } from '../../../services/graphql'; +import { useArticlesList } from './use-articles-list'; + +const wrapper = ({ children }: { children?: ReactNode }) => { + const map = new Map(); + + return ( + <SWRConfig + value={{ + provider: () => map, + isOnline() { + return true; + }, + isVisible() { + return true; + }, + initFocus() { + /* nothing */ + }, + initReconnect() { + /* nothing */ + }, + }} + > + {children} + </SWRConfig> + ); +}; + +describe('useArticlesList', () => { + beforeEach(() => { + /* Not sure why it is needed, but without it Jest was complaining with `You + * are trying to import a file after the Jest environment has been torn + * down`... Maybe because of useSWR? */ + jest.useFakeTimers({ + doNotFake: ['queueMicrotask'], + }); + }); + + afterEach(() => { + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + }); + + it('can return the first new result index when loading more posts', async () => { + const perPage = 5; + const { result } = renderHook(() => useArticlesList({ perPage }), { + wrapper, + }); + + expect.assertions(2); + + expect(result.current.firstNewResultIndex).toBeUndefined(); + + await act(async () => { + await result.current.loadMore(); + }); + + // Assuming there is more than one page. + expect(result.current.firstNewResultIndex).toBe(perPage + 1); + }); + + it('converts a WordPress post connection to an article connection', async () => { + const perPage = 1; + const { result } = renderHook(() => useArticlesList({ perPage }), { + wrapper, + }); + const connection = getConnection({ + after: null, + data: wpPostsFixture, + first: perPage, + }); + + expect.hasAssertions(); + + await waitFor(() => { + expect(result.current.articles).toBeDefined(); + }); + + expect(result.current.articles).toStrictEqual([ + { + edges: connection.edges.map((edge) => { + return { + cursor: edge.cursor, + node: convertPostPreviewToArticlePreview(edge.node), + }; + }), + pageInfo: { + endCursor: connection.pageInfo.endCursor, + hasNextPage: connection.pageInfo.hasNextPage, + total: connection.pageInfo.total, + }, + }, + ]); + }); +}); diff --git a/src/utils/hooks/use-articles-list/use-articles-list.ts b/src/utils/hooks/use-articles-list/use-articles-list.ts new file mode 100644 index 0000000..8a52702 --- /dev/null +++ b/src/utils/hooks/use-articles-list/use-articles-list.ts @@ -0,0 +1,86 @@ +import { useCallback, useState } from 'react'; +import { + convertPostPreviewToArticlePreview, + fetchPostsList, +} from '../../../services/graphql'; +import type { + ArticlePreview, + GraphQLConnection, + GraphQLEdge, + Maybe, + WPPostPreview, +} from '../../../types'; +import { + type UsePaginationConfig, + usePagination, + type UsePaginationReturn, +} from '../use-pagination'; + +export type useArticlesListReturn = Omit< + UsePaginationReturn<WPPostPreview>, + 'data' +> & { + /** + * The articles list. + */ + articles: Maybe<GraphQLConnection<ArticlePreview>[]>; + /** + * The index of the first new result when loading more posts. + */ + firstNewResultIndex: Maybe<number>; +}; + +export const useArticlesList = ( + config: Omit<UsePaginationConfig<WPPostPreview>, 'fetcher'> +): useArticlesListReturn => { + const { + data, + error, + hasNextPage, + isEmpty, + isError, + isLoading, + isLoadingMore, + isRefreshing, + isValidating, + loadMore, + size, + } = usePagination({ ...config, fetcher: fetchPostsList }); + const [firstNewResultIndex, setFirstNewResultIndex] = + useState<Maybe<number>>(undefined); + + const handleLoadMore = useCallback(async () => { + setFirstNewResultIndex(size * config.perPage + 1); + + await loadMore(); + }, [config.perPage, loadMore, size]); + + const articles: Maybe<GraphQLConnection<ArticlePreview>[]> = data?.map( + ({ edges, pageInfo }): GraphQLConnection<ArticlePreview> => { + return { + edges: edges.map((edge): GraphQLEdge<ArticlePreview> => { + return { + cursor: edge.cursor, + node: convertPostPreviewToArticlePreview(edge.node), + }; + }), + pageInfo, + }; + } + ); + + return { + articles, + error, + firstNewResultIndex, + hasNextPage, + isEmpty, + isError, + isLoading, + isLoadingMore, + isRefreshing, + isValidating, + loadMore: handleLoadMore, + size, + }; +}; |
