Opening a row on the customers, invoices, verifikat, supplier-invoice, supplier, article and salary-run lists flashed three unrelated layouts: the segment's list-shaped loading.tsx (or, for suppliers and articles, the Hem-shaped dashboard fallback) during the RSC round trip, then the client page's bare centred spinner in an h-64 box while it fetched, then the content with a full layout change. Two flashes per click on the most travelled drill-down path. - components/common/DetailPageSkeleton.tsx: back link + title row + card grid + line block, the silhouette of a document/register detail page; InvoiceEditorSkeleton for the editor routes (same shape the Ny faktura dialog shows while its chunk loads). - loading.tsx for every [id] segment (customers, invoices, invoices/edit, invoices/credit, bookkeeping, supplier-invoices, suppliers, articles, salary/runs) and for the two list segments that had none (suppliers, articles, cloned from customers/loading.tsx). - The client pages render the same skeleton while they fetch instead of the centred Loader2, so the RSC fallback to client fallback handoff is invisible. The reference-data gates are already gone (A1 to A6); this only covers the primary-entity fetch. - app/(dashboard)/__tests__/detail-loading-states.test.ts pins both. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
42 lines
1.4 KiB
TypeScript
42 lines
1.4 KiB
TypeScript
/**
|
|
* Every [id] detail segment ships a route-level loading.tsx, and its client
|
|
* page no longer collapses into the bare centred spinner while it fetches:
|
|
* the RSC fallback and the client fallback share one silhouette
|
|
* (components/common/DetailPageSkeleton), so opening a row never flashes
|
|
* three unrelated layouts.
|
|
*/
|
|
import { describe, it, expect } from 'vitest'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
const APP = path.resolve(__dirname, '..')
|
|
const DETAIL_SEGMENTS = [
|
|
'customers/[id]',
|
|
'invoices/[id]',
|
|
'invoices/[id]/edit',
|
|
'invoices/[id]/credit',
|
|
'bookkeeping/[id]',
|
|
'supplier-invoices/[id]',
|
|
'suppliers/[id]',
|
|
'articles/[id]',
|
|
'salary/runs/[id]',
|
|
]
|
|
const LIST_SEGMENTS = ['customers', 'invoices', 'suppliers', 'articles', 'supplier-invoices']
|
|
|
|
describe('detail and list segments have a route-level loading state', () => {
|
|
for (const segment of [...DETAIL_SEGMENTS, ...LIST_SEGMENTS]) {
|
|
it(`${segment}/loading.tsx exists`, () => {
|
|
expect(fs.existsSync(path.join(APP, segment, 'loading.tsx'))).toBe(true)
|
|
})
|
|
}
|
|
})
|
|
|
|
describe('detail pages do not gate on the bare centred spinner', () => {
|
|
for (const segment of DETAIL_SEGMENTS) {
|
|
it(`${segment}/page.tsx uses the shared skeleton, not h-64 + Loader2`, () => {
|
|
const source = fs.readFileSync(path.join(APP, segment, 'page.tsx'), 'utf8')
|
|
expect(source).not.toMatch(/justify-center h-64">\s*<Loader2/)
|
|
})
|
|
}
|
|
})
|