Files
accounted/app/(dashboard)/__tests__/detail-loading-states.test.ts
T
41424a1650 feat(ux): one silhouette from route fallback to detail content (#1944)
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>
2026-08-26 15:02:26 +02:00

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/)
})
}
})