Files
probo/.cursor/rules/relay-connection-item-components.mdc
Émile Ré c158eb9be4 Rework frontend rules for the v2 UI kit
Make contrib/claude the single source of truth for v2 frontend work
on the compliance-portal app and packages/ui/src/v2, treating console
and the legacy @probo/ui tree as non-compliant code to migrate rather
than precedent.

Rewrite ui.md around the v2 kit: flat folders, Base UI as the headless
layer styled thinly (controlled open/onOpenChange, no imperative ref or
cloneElement plumbing), tailwind-variants only, separate components over
structure-changing variants, and bundle-safe skeletons that never drag
Base UI into the loading path.

Add a naming/suffix taxonomy to react-components.md, replacing the
Table/Row and connection-item Card suffixes with List/ListItem, and add
an error/fallback props convention. Document _lib and _locales special
folders plus routes.ts placement in app-arborescence.md, with at most
one _locales per routes.ts.

Add error-handling.md (reusable ErrorBoundary usable at any level plus
async try/catch) and i18n.md (i18next key-based catalogs). Update the
relay file-organization and fragment examples, the connection-item
cursor rule, and the AGENTS.md index to match.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-26 18:52:05 +02:00

77 lines
2.1 KiB
Plaintext

---
description: Extract connection list items (table rows, list entries) into their own fragment component
globs: "**/*.tsx"
alwaysApply: false
---
# Extract connection items into fragment components
When rendering items from a Relay connection (e.g. `edges.map(…)`), each item
MUST be rendered by a dedicated component that owns its own fragment — never
inline the rendering of node fields directly in the parent's `.map()` body.
This ensures:
- Data requirements are colocated with the rendering component
- Adding/removing fields in an item doesn't bloat the parent's fragment
- The item component is independently testable and reusable
## Pattern
```tsx
// _components/ThingListItem.tsx — owns its fragment
const thingListItemFragment = graphql`
fragment ThingListItem_thing on Thing {
id
name
status
}
`;
interface ThingListItemProps {
thingKey: ThingListItem_thing$key;
}
export function ThingListItem({ thingKey }: ThingListItemProps) {
const thing = useFragment(thingListItemFragment, thingKey);
return (
<Tr>
<Td>{thing.name}</Td>
<Td>{thing.status}</Td>
</Tr>
);
}
```
```tsx
// Parent — spreads the item fragment in its connection and renders the component
const parentFragment = graphql`
fragment ParentPage_things on Query
@refetchable(queryName: "ParentPageRefetchQuery") {
things(first: $first, after: $after) @connection(key: "ParentPage_things") {
edges {
node {
id
...ThingListItem_thing
}
}
}
}
`;
// In JSX:
{things.map(thing => (
<ThingListItem key={thing.id} thingKey={thing} />
))}
```
## Naming
- File: `_components/<NodeType>ListItem.tsx` — the canonical connection-item
suffix, regardless of whether the item renders as a table row, a card, or a
plain list entry (the layout is internal to the component, not its identity).
Do **not** use `*Row` or `*Card` suffixes.
- Fragment: `<ComponentName>_<typeName>` (e.g. `ThingListItem_thing`)
- Prop: `<typeName>Key` (e.g. `thingKey`)
See `contrib/claude/react-components.md` for the full suffix taxonomy.