Skip to content

Commit

Permalink
fix: pagination for PDP reviews (#1971)
Browse files Browse the repository at this point in the history
* fix: pagination for PDP reviews

* fix: PDP reviews pagination labels i18n
  • Loading branch information
migueloller authored Feb 11, 2025
1 parent 4714a08 commit dfa019f
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 39 deletions.
72 changes: 38 additions & 34 deletions core/app/[locale]/(default)/product/[slug]/_components/reviews.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { removeEdgesAndNodes } from '@bigcommerce/catalyst-client';
import { useTranslations } from 'next-intl';
import { getFormatter, getTranslations } from 'next-intl/server';
import { cache } from 'react';

Expand All @@ -9,19 +8,30 @@ import { client } from '~/client';
import { PaginationFragment } from '~/client/fragments/pagination';
import { graphql } from '~/client/graphql';
import { revalidate } from '~/client/revalidate-target';
import { defaultPageInfo, pageInfoTransformer } from '~/data-transformers/page-info-transformer';

import { ProductReviewSchemaFragment } from './product-review-schema/fragment';
import { ProductReviewSchema } from './product-review-schema/product-review-schema';

export const PaginationSearchParamNames = {
BEFORE: 'reviews_before',
AFTER: 'reviews_after',
} as const;

interface SearchParams {
[PaginationSearchParamNames.BEFORE]?: string | null;
[PaginationSearchParamNames.AFTER]?: string | null;
}

const ReviewsQuery = graphql(
`
query ReviewsQuery($entityId: Int!) {
query ReviewsQuery($entityId: Int!, $first: Int, $after: String, $before: String, $last: Int) {
site {
product(entityId: $entityId) {
reviewSummary {
averageRating
}
reviews(first: 5) {
reviews(first: $first, after: $after, before: $before, last: $last) {
pageInfo {
...PaginationFragment
}
Expand All @@ -48,18 +58,22 @@ const ReviewsQuery = graphql(
[ProductReviewSchemaFragment, PaginationFragment],
);

const getReviewsData = cache(async (productId: number) => {
const getReviewsData = cache(async (productId: number, searchParams: Promise<SearchParams>) => {
const { [PaginationSearchParamNames.AFTER]: after, [PaginationSearchParamNames.BEFORE]: before } =
await searchParams;
const paginationArgs = before == null ? { first: 5, after } : { last: 5, before };

const { data } = await client.fetch({
document: ReviewsQuery,
variables: { entityId: productId },
variables: { ...paginationArgs, entityId: productId },
fetchOptions: { next: { revalidate } },
});

return data.site.product;
});

const getReviews = async (productId: number) => {
const product = await getReviewsData(productId);
const getReviews = async (productId: number, searchParams: Promise<SearchParams>) => {
const product = await getReviewsData(productId, searchParams);

if (!product) {
return [];
Expand All @@ -68,8 +82,8 @@ const getReviews = async (productId: number) => {
return removeEdgesAndNodes(product.reviews);
};

const getFormattedReviews = async (productId: number) => {
const reviews = await getReviews(productId);
const getFormattedReviews = async (productId: number, searchParams: Promise<SearchParams>) => {
const reviews = await getReviews(productId, searchParams);
const format = await getFormatter();

return reviews.map((review) => ({
Expand All @@ -86,7 +100,7 @@ const getFormattedReviews = async (productId: number) => {
};

const getAverageRating = async (productId: number) => {
const product = await getReviewsData(productId);
const product = await getReviewsData(productId, Promise.resolve({}));

if (!product) {
return 0;
Expand All @@ -95,45 +109,35 @@ const getAverageRating = async (productId: number) => {
return product.reviewSummary.averageRating;
};

const getPaginationInfo = async (productId: number) => {
const t = await getTranslations('Product.Reviews.Pagination');
const product = await getReviewsData(productId);

if (!product) {
return {};
}

const { hasNextPage, hasPreviousPage, endCursor, startCursor } = product.reviews.pageInfo;
const getPaginationInfo = async (productId: number, searchParams: Promise<SearchParams>) => {
const product = await getReviewsData(productId, searchParams);

return hasNextPage || hasPreviousPage
? {
startCursorParamName: 'before',
endCursorParamName: 'after',
endCursor: hasNextPage ? endCursor : null,
startCursor: hasPreviousPage ? startCursor : null,
nextLabel: t('next'),
previousLabel: t('previous'),
}
: {};
return pageInfoTransformer(product?.reviews.pageInfo ?? defaultPageInfo, {
startCursorParamName: PaginationSearchParamNames.BEFORE,
endCursorParamName: PaginationSearchParamNames.AFTER,
});
};

interface Props {
productId: number;
searchParams: Promise<SearchParams>;
}

export const Reviews = ({ productId }: Props) => {
const t = useTranslations('Product.Reviews');
export const Reviews = async ({ productId, searchParams }: Props) => {
const t = await getTranslations('Product.Reviews');

return (
<>
<ReviewsSection
averageRating={getAverageRating(productId)}
emptyStateMessage={t('empty')}
paginationInfo={getPaginationInfo(productId)}
reviews={getFormattedReviews(productId)}
nextLabel={t('Pagination.next')}
paginationInfo={getPaginationInfo(productId, searchParams)}
previousLabel={t('Pagination.previous')}
reviews={getFormattedReviews(productId, searchParams)}
reviewsLabel={t('title')}
/>
<Stream fallback={null} value={getReviews(productId)}>
<Stream fallback={null} value={getReviews(productId, searchParams)}>
{(reviews) =>
reviews.length > 0 && <ProductReviewSchema productId={productId} reviews={reviews} />
}
Expand Down
11 changes: 9 additions & 2 deletions core/app/[locale]/(default)/product/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { removeEdgesAndNodes } from '@bigcommerce/catalyst-client';
import { Metadata } from 'next';
import { getFormatter, getTranslations, setRequestLocale } from 'next-intl/server';
import { createSearchParamsCache, parseAsString } from 'nuqs/server';
import { cache } from 'react';

import { Stream } from '@/vibes/soul/lib/streamable';
Expand All @@ -14,7 +15,7 @@ import { getPreferredCurrencyCode } from '~/lib/currency';
import { addToCart } from './_actions/add-to-cart';
import { ProductSchema } from './_components/product-schema';
import { ProductViewed } from './_components/product-viewed';
import { Reviews } from './_components/reviews';
import { PaginationSearchParamNames, Reviews } from './_components/reviews';
import { getProductData } from './page-data';

const cachedProductDataVariables = cache(
Expand Down Expand Up @@ -220,6 +221,11 @@ export async function generateMetadata(props: Props): Promise<Metadata> {
};
}

const searchParamsCache = createSearchParamsCache({
[PaginationSearchParamNames.BEFORE]: parseAsString,
[PaginationSearchParamNames.AFTER]: parseAsString,
});

export default async function Product(props: Props) {
const { locale, slug } = await props.params;

Expand All @@ -229,6 +235,7 @@ export default async function Product(props: Props) {

const productId = Number(slug);
const variables = await cachedProductDataVariables(slug, props.searchParams);
const parsedSearchParams = searchParamsCache.parse(props.searchParams);

return (
<>
Expand Down Expand Up @@ -257,7 +264,7 @@ export default async function Product(props: Props) {
title={t('RelatedProducts.title')}
/>

<Reviews productId={productId} />
<Reviews productId={productId} searchParams={parsedSearchParams} />

<Stream fallback={null} value={getProductData(variables)}>
{(product) => (
Expand Down
8 changes: 6 additions & 2 deletions core/data-transformers/page-info-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ export const defaultPageInfo = {

export function pageInfoTransformer(
pageInfo: ResultOf<typeof PaginationFragment>,
{
startCursorParamName = 'before',
endCursorParamName = 'after',
}: { startCursorParamName?: string; endCursorParamName?: string } = {},
): CursorPaginationInfo {
return {
startCursorParamName: 'before',
startCursorParamName,
startCursor: pageInfo.hasPreviousPage ? pageInfo.startCursor : null,
endCursorParamName: 'after',
endCursorParamName,
endCursor: pageInfo.hasNextPage ? pageInfo.endCursor : null,
};
}
13 changes: 12 additions & 1 deletion core/vibes/soul/sections/reviews/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ interface Props {
averageRating: Streamable<number>;
totalCount?: Streamable<number>;
paginationInfo?: Streamable<CursorPaginationInfo>;
nextLabel?: Streamable<string>;
previousLabel?: Streamable<string>;
emptyStateMessage?: string;
reviewsLabel?: string;
}
Expand All @@ -25,6 +27,8 @@ export function Reviews({
averageRating: streamableAverageRating,
totalCount: streamableTotalCount,
paginationInfo: streamablePaginationInfo,
nextLabel,
previousLabel,
emptyStateMessage,
reviewsLabel = 'Reviews',
}: Readonly<Props>) {
Expand Down Expand Up @@ -90,7 +94,14 @@ export function Reviews({

<Stream value={streamablePaginationInfo}>
{(paginationInfo) =>
paginationInfo && <CursorPagination info={paginationInfo} scroll={false} />
paginationInfo && (
<CursorPagination
info={paginationInfo}
nextLabel={nextLabel}
previousLabel={previousLabel}
scroll={false}
/>
)
}
</Stream>
</div>
Expand Down

0 comments on commit dfa019f

Please sign in to comment.