Address code review on documents and UI skeletons

Fold the review feedback from the documents page work into the shared
components and helpers:

- Toaster: give the close control the kit's focus-visible ring, and wrap
  the title/description in Toast.Content so stacked toasts get Base UI's
  height measurement and overflow handling.
- TabsSkeleton: spread rest before the fixed aria-hidden so the
  decorative subtree can't be exposed to assistive tech.
- DocumentsPage: reconcile the active tab against the tab the preloaded
  query actually loaded with, so a tab change during the initial preload
  no longer shows the wrong slice.
- Grouping: drop the duplicated bucketing helpers and reuse the shared
  groupBy from @probo/helpers, keeping field-specific sorting/labeling in
  the callers.
- Documents list items: extract a shared DocumentEntry row and a
  useExportAndOpen hook so the three item components stop repeating the
  export/access behavior while keeping their own fragments.
- Subprocessors skeleton: reuse SelectSkeleton/TextFieldSkeleton instead
  of hand-rolled placeholders.
- useDocumentTab: derive DocumentTab and URL validation from a single
  DOCUMENT_TABS source of truth.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-07-16 09:57:52 +02:00
parent 4c599c17f4
commit 99990d6f6b
13 changed files with 209 additions and 180 deletions

View File

@@ -34,7 +34,7 @@ export function TabsSkeleton(props: TabsSkeletonProps) {
const { root, item } = tabsSkeleton();
return (
<div className={root({ className })} aria-hidden {...rest}>
<div className={root({ className })} {...rest} aria-hidden>
{Array.from({ length: count }, (_, index) => (
<span key={index} className={item()} style={{ width: `${64 + (index % 3) * 16}px` }} />
))}

View File

@@ -66,12 +66,12 @@ export function Toaster() {
<span aria-hidden className={typed.icon()}>
{typeIcons[type]}
</span>
<div className={typed.content()}>
<Toast.Content className={typed.content()}>
<Toast.Title className={typed.title()} />
{toast.description != null && (
<Toast.Description className={typed.description()} />
)}
</div>
</Toast.Content>
<Toast.Close className={typed.close()} aria-label="Close">
<XIcon />
</Toast.Close>

View File

@@ -41,7 +41,10 @@ export const toaster = tv({
content: "flex min-w-0 flex-1 flex-col gap-1",
title: "text-2 font-medium",
description: "text-1",
close: "-mr-1 -mt-1 shrink-0 rounded-2 p-1 opacity-70 transition-opacity hover:opacity-100 [&_svg]:size-4",
close: [
"-mr-1 -mt-1 shrink-0 rounded-2 p-1 opacity-70 outline-none transition-opacity hover:opacity-100 [&_svg]:size-4",
"focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-sand-8 focus-visible:ring-offset-1 focus-visible:ring-offset-sand-1",
],
},
variants: {
// Mirrors Callout's surface tokens: bg step 2, border step 6, text step 11,