From 4261a299342613374fd3f9c8181b46a771a82b49 Mon Sep 17 00:00:00 2001 From: Tim Hostetler <6970899+thostetler@users.noreply.github.com> Date: Thu, 23 Oct 2025 11:29:47 -0400 Subject: [PATCH] fix(abs): stabilize similar page hydration --- src/components/Layout/AbsLayout.tsx | 20 +++--- .../Layout/__tests__/AbsLayout.test.tsx | 65 +++++++++++++++++++ src/pages/abs/[id]/similar.tsx | 13 ++-- 3 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 src/components/Layout/__tests__/AbsLayout.test.tsx diff --git a/src/components/Layout/AbsLayout.tsx b/src/components/Layout/AbsLayout.tsx index 52790ce4f..585e50d37 100644 --- a/src/components/Layout/AbsLayout.tsx +++ b/src/components/Layout/AbsLayout.tsx @@ -14,7 +14,7 @@ import { unwrapStringValue } from '@/utils/common/formatters'; import { IDocsEntity } from '@/api/search/types'; interface IAbsLayoutProps { - doc: IDocsEntity; + doc?: IDocsEntity | null; titleDescription: string; label: string; } @@ -22,11 +22,9 @@ interface IAbsLayoutProps { export const AbsLayout: FC = ({ children, doc, titleDescription, label }) => { const { getSearchHref, show: showBackLink } = useBackToSearchResults(); - if (!doc) { - return <>{children}; - } - - const title = unwrapStringValue(doc?.title); + const hasDoc = Boolean(doc); + const title = hasDoc ? unwrapStringValue(doc.title) : ''; + const pageTitle = hasDoc ? `${title} - ${BRAND_NAME_FULL} ${label}` : `${BRAND_NAME_FULL} ${label}`; return ( @@ -47,21 +45,21 @@ export const AbsLayout: FC = ({ children, doc, titleDescription )} - {`${unwrapStringValue(doc.title)} - ${BRAND_NAME_FULL} ${label}`} - + {pageTitle} + {hasDoc ? : null} - + {hasDoc ? : null} - + {hasDoc ? : null} {titleDescription} {' '} - +
{children}
diff --git a/src/components/Layout/__tests__/AbsLayout.test.tsx b/src/components/Layout/__tests__/AbsLayout.test.tsx new file mode 100644 index 000000000..b1d255962 --- /dev/null +++ b/src/components/Layout/__tests__/AbsLayout.test.tsx @@ -0,0 +1,65 @@ +import { describe, expect, it, vi } from 'vitest'; + +vi.mock('next/router', () => ({ + useRouter: () => ({ + asPath: '/', + basePath: '', + isReady: true, + pathname: '/', + query: {}, + push: vi.fn(), + replace: vi.fn(), + prefetch: vi.fn(), + back: vi.fn(), + forward: vi.fn(), + events: { + on: vi.fn(), + off: vi.fn(), + emit: vi.fn(), + }, + }), +})); + +vi.mock('@/components/AbstractSideNav', () => ({ + AbstractSideNav: () =>
, +})); + +vi.mock('@/components/AbstractSources', () => ({ + AbstractSources: () =>
, +})); + +import { AbsLayout } from '@/components/Layout/AbsLayout'; +import { render, screen } from '@/test-utils'; + +const baseDoc = { + id: 'test-id', + bibcode: '2020TEST....1A', + title: ['Test Document'], +} as const; + +describe('AbsLayout', () => { + it('renders stable structure when doc is unavailable', () => { + render( + +
Loading content
+
, + {}, + ); + + expect(document.getElementById('abstract-subview-content')).toBeInTheDocument(); + expect(screen.getByTestId('content')).toBeInTheDocument(); + }); + + it('renders document specific details when doc is provided', () => { + render( + +
Loaded content
+
, + {}, + ); + + expect(screen.getByTestId('abstract-side-nav')).toBeInTheDocument(); + expect(screen.getByTestId('abstract-sources')).toBeInTheDocument(); + expect(screen.getByTestId('content')).toBeInTheDocument(); + }); +}); diff --git a/src/pages/abs/[id]/similar.tsx b/src/pages/abs/[id]/similar.tsx index 7bc23ebb2..ac714ac50 100644 --- a/src/pages/abs/[id]/similar.tsx +++ b/src/pages/abs/[id]/similar.tsx @@ -19,18 +19,23 @@ const SimilarPage: NextPage = () => { const doc = path(['docs', 0], abstractResult); const pageIndex = router.query.p ? parseInt(router.query.p as string) - 1 : 0; - const { getParams, onPageChange } = useGetAbstractParams(doc?.bibcode); + const bibcode = doc?.bibcode ?? ''; + const { getParams, onPageChange } = useGetAbstractParams(bibcode); + const shouldFetchSimilar = Boolean(bibcode); const { data, isSuccess, isLoading, isFetching, isError, error } = useGetSimilar( { ...getParams(), start: pageIndex * APP_DEFAULTS.RESULT_PER_PAGE }, - { keepPreviousData: true }, + { + keepPreviousData: true, + enabled: shouldFetchSimilar, + }, ); - const similarParams = getSimilarParams(doc?.bibcode, 0); + const similarParams = shouldFetchSimilar ? getSimilarParams(bibcode, 0) : null; return ( {isLoading || isFetching ? : null} {isError ? : null} - {isSuccess ? ( + {isSuccess && doc && similarParams ? (