aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils/hooks
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/hooks')
-rw-r--r--src/utils/hooks/index.ts3
-rw-r--r--src/utils/hooks/use-articles-list/index.ts1
-rw-r--r--src/utils/hooks/use-articles-list/use-articles-list.test.tsx (renamed from src/utils/hooks/use-posts-list/use-posts-list.test.tsx)51
-rw-r--r--src/utils/hooks/use-articles-list/use-articles-list.ts (renamed from src/utils/hooks/use-posts-list/use-posts-list.ts)21
-rw-r--r--src/utils/hooks/use-is-mounted.tsx17
-rw-r--r--src/utils/hooks/use-pagination/use-pagination.ts8
-rw-r--r--src/utils/hooks/use-posts-list/index.ts1
-rw-r--r--src/utils/hooks/use-redirection.tsx31
-rw-r--r--src/utils/hooks/use-redirection/index.ts1
-rw-r--r--src/utils/hooks/use-redirection/use-redirection.test.ts80
-rw-r--r--src/utils/hooks/use-redirection/use-redirection.ts41
11 files changed, 183 insertions, 72 deletions
diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts
index da4ed9e..1e0bfe3 100644
--- a/src/utils/hooks/index.ts
+++ b/src/utils/hooks/index.ts
@@ -1,5 +1,6 @@
export * from './use-ackee';
export * from './use-article';
+export * from './use-articles-list';
export * from './use-boolean';
export * from './use-breadcrumb';
export * from './use-comments';
@@ -7,13 +8,11 @@ export * from './use-data-from-api';
export * from './use-form';
export * from './use-github-api';
export * from './use-headings-tree';
-export * from './use-is-mounted';
export * from './use-local-storage';
export * from './use-match-media';
export * from './use-on-click-outside';
export * from './use-on-route-change';
export * from './use-pagination';
-export * from './use-posts-list';
export * from './use-prism';
export * from './use-prism-theme';
export * from './use-redirection';
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-posts-list/use-posts-list.test.tsx b/src/utils/hooks/use-articles-list/use-articles-list.test.tsx
index f23ddde..6191ed6 100644
--- a/src/utils/hooks/use-posts-list/use-posts-list.test.tsx
+++ b/src/utils/hooks/use-articles-list/use-articles-list.test.tsx
@@ -6,11 +6,13 @@ import {
it,
jest,
} from '@jest/globals';
-import { act, renderHook } from '@testing-library/react';
+import { act, renderHook, waitFor } from '@testing-library/react';
import type { ReactNode } from 'react';
import { SWRConfig } from 'swr';
-import { fetchPostsList } from '../../../services/graphql';
-import { usePostsList } from './use-posts-list';
+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();
@@ -38,7 +40,7 @@ const wrapper = ({ children }: { children?: ReactNode }) => {
);
};
-describe('usePostsList', () => {
+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
@@ -55,10 +57,9 @@ describe('usePostsList', () => {
it('can return the first new result index when loading more posts', async () => {
const perPage = 5;
- const { result } = renderHook(
- () => usePostsList({ fetcher: fetchPostsList, perPage }),
- { wrapper }
- );
+ const { result } = renderHook(() => useArticlesList({ perPage }), {
+ wrapper,
+ });
expect.assertions(2);
@@ -71,4 +72,38 @@ describe('usePostsList', () => {
// 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-posts-list/use-posts-list.ts b/src/utils/hooks/use-articles-list/use-articles-list.ts
index bb77f31..8a52702 100644
--- a/src/utils/hooks/use-posts-list/use-posts-list.ts
+++ b/src/utils/hooks/use-articles-list/use-articles-list.ts
@@ -1,4 +1,8 @@
import { useCallback, useState } from 'react';
+import {
+ convertPostPreviewToArticlePreview,
+ fetchPostsList,
+} from '../../../services/graphql';
import type {
ArticlePreview,
GraphQLConnection,
@@ -11,9 +15,8 @@ import {
usePagination,
type UsePaginationReturn,
} from '../use-pagination';
-import { convertPostPreviewToArticlePreview } from 'src/services/graphql';
-export type usePostsListReturn = Omit<
+export type useArticlesListReturn = Omit<
UsePaginationReturn<WPPostPreview>,
'data'
> & {
@@ -27,9 +30,9 @@ export type usePostsListReturn = Omit<
firstNewResultIndex: Maybe<number>;
};
-export const usePostsList = (
- config: UsePaginationConfig<WPPostPreview>
-): usePostsListReturn => {
+export const useArticlesList = (
+ config: Omit<UsePaginationConfig<WPPostPreview>, 'fetcher'>
+): useArticlesListReturn => {
const {
data,
error,
@@ -42,7 +45,7 @@ export const usePostsList = (
isValidating,
loadMore,
size,
- } = usePagination(config);
+ } = usePagination({ ...config, fetcher: fetchPostsList });
const [firstNewResultIndex, setFirstNewResultIndex] =
useState<Maybe<number>>(undefined);
@@ -53,15 +56,15 @@ export const usePostsList = (
}, [config.perPage, loadMore, size]);
const articles: Maybe<GraphQLConnection<ArticlePreview>[]> = data?.map(
- (page): GraphQLConnection<ArticlePreview> => {
+ ({ edges, pageInfo }): GraphQLConnection<ArticlePreview> => {
return {
- edges: page.edges.map((edge): GraphQLEdge<ArticlePreview> => {
+ edges: edges.map((edge): GraphQLEdge<ArticlePreview> => {
return {
cursor: edge.cursor,
node: convertPostPreviewToArticlePreview(edge.node),
};
}),
- pageInfo: page.pageInfo,
+ pageInfo,
};
}
);
diff --git a/src/utils/hooks/use-is-mounted.tsx b/src/utils/hooks/use-is-mounted.tsx
deleted file mode 100644
index 4d85d45..0000000
--- a/src/utils/hooks/use-is-mounted.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { RefObject, useEffect, useState } from 'react';
-
-/**
- * Check if an HTML element is mounted.
- *
- * @param {RefObject<HTMLElement>} ref - A React reference to an HTML element.
- * @returns {boolean} True if the HTML element is mounted.
- */
-export const useIsMounted = (ref: RefObject<HTMLElement>): boolean => {
- const [isMounted, setIsMounted] = useState<boolean>(false);
-
- useEffect(() => {
- if (ref.current) setIsMounted(true);
- }, [ref]);
-
- return isMounted;
-};
diff --git a/src/utils/hooks/use-pagination/use-pagination.ts b/src/utils/hooks/use-pagination/use-pagination.ts
index 2a40aa4..29d5ba2 100644
--- a/src/utils/hooks/use-pagination/use-pagination.ts
+++ b/src/utils/hooks/use-pagination/use-pagination.ts
@@ -11,7 +11,7 @@ export type UsePaginationFetcherInput = GraphQLEdgesInput & {
search?: string;
};
-export type UsePaginationConfig<T> = {
+export type UsePaginationConfig<T> = Pick<GraphQLEdgesInput, 'after'> & {
/**
* The initial data.
*/
@@ -86,6 +86,7 @@ export type UsePaginationReturn<T> = {
* @returns {UsePaginationReturn} An object with pagination data and helpers.
*/
export const usePagination = <T>({
+ after,
fallback,
fetcher,
perPage,
@@ -97,12 +98,11 @@ export const usePagination = <T>({
return {
first: perPage,
- after:
- pageIndex === 0 ? undefined : previousPageData?.pageInfo.endCursor,
+ after: pageIndex === 0 ? after : previousPageData?.pageInfo.endCursor,
search: searchQuery,
};
},
- [perPage, searchQuery]
+ [after, perPage, searchQuery]
);
const { data, error, isLoading, isValidating, setSize, size } =
diff --git a/src/utils/hooks/use-posts-list/index.ts b/src/utils/hooks/use-posts-list/index.ts
deleted file mode 100644
index 664c142..0000000
--- a/src/utils/hooks/use-posts-list/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './use-posts-list';
diff --git a/src/utils/hooks/use-redirection.tsx b/src/utils/hooks/use-redirection.tsx
deleted file mode 100644
index 5a677e2..0000000
--- a/src/utils/hooks/use-redirection.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { useRouter } from 'next/router';
-import { useEffect } from 'react';
-
-export type RouterQuery = {
- param: string;
- value: string;
-};
-
-export type UseRedirectionProps = {
- /**
- * The router query.
- */
- query: RouterQuery;
- /**
- * The redirection url.
- */
- redirectTo: string;
-};
-
-/**
- * Redirect to another url when router query match the given parameters.
- *
- * @param {UseRedirectionProps} props - The redirection parameters.
- */
-export const useRedirection = ({ query, redirectTo }: UseRedirectionProps) => {
- const router = useRouter();
-
- useEffect(() => {
- if (router.query[query.param] === query.value) router.push(redirectTo);
- }, [query, redirectTo, router]);
-};
diff --git a/src/utils/hooks/use-redirection/index.ts b/src/utils/hooks/use-redirection/index.ts
new file mode 100644
index 0000000..c81c82c
--- /dev/null
+++ b/src/utils/hooks/use-redirection/index.ts
@@ -0,0 +1 @@
+export * from './use-redirection';
diff --git a/src/utils/hooks/use-redirection/use-redirection.test.ts b/src/utils/hooks/use-redirection/use-redirection.test.ts
new file mode 100644
index 0000000..c14ac4c
--- /dev/null
+++ b/src/utils/hooks/use-redirection/use-redirection.test.ts
@@ -0,0 +1,80 @@
+import { describe, it } from '@jest/globals';
+import { renderHook } from '@testing-library/react';
+import nextRouterMock from 'next-router-mock';
+import { MemoryRouterProvider } from 'next-router-mock/MemoryRouterProvider';
+import { useRedirection } from './use-redirection';
+
+describe('useRedirection', () => {
+ it('redirects to another page', async () => {
+ const initialPath = '/initial-path';
+ const redirectPath = '/redirect-path';
+
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ expect.assertions(2);
+
+ await nextRouterMock.push('/initial-path');
+
+ expect(nextRouterMock.asPath).toBe(initialPath);
+
+ renderHook(() => useRedirection({ to: redirectPath }), {
+ wrapper: MemoryRouterProvider,
+ });
+
+ expect(nextRouterMock.asPath).toBe(redirectPath);
+ });
+
+ it('can replace the url in the history', async () => {
+ const initialPath = '/initial-path';
+ const redirectPath = '/redirect-path';
+
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ expect.assertions(2);
+
+ await nextRouterMock.push('/initial-path');
+
+ expect(nextRouterMock.asPath).toBe(initialPath);
+
+ renderHook(() => useRedirection({ isReplacing: true, to: redirectPath }), {
+ wrapper: MemoryRouterProvider,
+ });
+
+ expect(nextRouterMock.asPath).toBe(redirectPath);
+
+ /* Ideally we should check if when we use `back()` the current path is
+ * still the redirectPath but it is not yet implemented in the mock. */
+ });
+
+ it('can conditionally redirect to another page', async () => {
+ const paths = {
+ initial: '/initial-path',
+ matching: '/matching-path',
+ redirect: '/redirect-path',
+ };
+
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ expect.assertions(3);
+
+ await nextRouterMock.push('/initial-path');
+
+ expect(nextRouterMock.asPath).toBe(paths.initial);
+
+ const { rerender } = renderHook(
+ () =>
+ useRedirection({
+ to: paths.redirect,
+ whenPathMatches: (path) => path === paths.matching,
+ }),
+ {
+ wrapper: MemoryRouterProvider,
+ }
+ );
+
+ expect(nextRouterMock.asPath).toBe(paths.initial);
+
+ await nextRouterMock.push(paths.matching);
+
+ rerender();
+
+ expect(nextRouterMock.asPath).toBe(paths.redirect);
+ });
+});
diff --git a/src/utils/hooks/use-redirection/use-redirection.ts b/src/utils/hooks/use-redirection/use-redirection.ts
new file mode 100644
index 0000000..1592a33
--- /dev/null
+++ b/src/utils/hooks/use-redirection/use-redirection.ts
@@ -0,0 +1,41 @@
+import { useRouter } from 'next/router';
+import { useEffect } from 'react';
+
+export type UseRedirectionConfig = {
+ /**
+ * Should the url be replaced in the history?
+ *
+ * @default false
+ */
+ isReplacing?: boolean;
+ /**
+ * The destination.
+ */
+ to: string;
+ /**
+ * Redirect only when the current path matches the condition.
+ *
+ * @param {string} path - The current slug.
+ * @returns {boolean} True if the path matches.
+ */
+ whenPathMatches?: (path: string) => boolean;
+};
+
+export const useRedirection = ({
+ isReplacing = false,
+ to,
+ whenPathMatches,
+}: UseRedirectionConfig) => {
+ const router = useRouter();
+
+ useEffect(() => {
+ const shouldRedirect = whenPathMatches
+ ? whenPathMatches(router.asPath)
+ : true;
+
+ if (shouldRedirect) {
+ if (isReplacing) router.replace(to, undefined, { shallow: true });
+ else router.push(to);
+ }
+ }, [isReplacing, router, to, whenPathMatches]);
+};