Address compliance-portal review feedback
Fix the valid issues raised in the scaffold review. UI kit: the Button loading state now replaces only the leading icon instead of dropping the label, Button consumes the `active` variant so it no longer leaks onto the DOM, and every v2 skeleton sets aria-hidden after the prop spread so a consumer cannot override it. @probo/relay: guard the caller-supplied onCompleted/onError callbacks so a throwing callback still settles the awaitable mutation promise instead of leaving it pending. compliance-portal: normalize external website hrefs and read hostname via URL.hostname, add a localized catch-all not-found route, and widen the .gitattributes glob so colocated __generated__ artifacts at any depth are marked generated. Docs: correct the forms guide (Base UI passes plain values, Zod v3 flatten API), spread the child fragment in the permissions example, and drop references to v2 components that do not exist in the ui guide. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -93,10 +93,10 @@ export function CreateMeasureForm({ onValid }: CreateMeasureFormProps) {
|
||||
<Form
|
||||
errors={errors}
|
||||
onClearErrors={setErrors}
|
||||
onFormSubmit={(formData) => {
|
||||
const result = schema.safeParse(Object.fromEntries(formData));
|
||||
onFormSubmit={(formValues) => {
|
||||
const result = schema.safeParse(formValues);
|
||||
if (!result.success) {
|
||||
setErrors(z.flattenError(result.error).fieldErrors);
|
||||
setErrors(result.error.flatten().fieldErrors);
|
||||
return;
|
||||
}
|
||||
onValid(result.data);
|
||||
|
||||
@@ -23,12 +23,15 @@ const documentListItemFragment = graphql`
|
||||
title
|
||||
canUpdate: permission(action: "core:document:update")
|
||||
canDelete: permission(action: "core:document:delete")
|
||||
...EditDocumentDialog_document
|
||||
}
|
||||
`;
|
||||
```
|
||||
|
||||
Colocate the permission with the action it gates — never drill a `canDelete` boolean down as a prop from a parent (the same data-as-props rule as everywhere else; see [`react-components.md`](react-components.md#props-are-for-configuration-and-composition-not-data)).
|
||||
|
||||
Spread a child's fragment (`...EditDocumentDialog_document`) when you forward the node to that child as a fragment key. `useFragment` returns plain data, but Relay keeps the spread fragment refs on it, so the resolved `document` doubles as the key the child's own `useFragment` expects. Without the spread, `documentKey={document}` would hand the child masked data with no ref and fail at runtime (see [`relay.md`](relay.md)).
|
||||
|
||||
## Gate the action on the boolean
|
||||
|
||||
Read the boolean via `useFragment` and gate the control. Default to **hiding** an action the user cannot perform; **disable** (with an explanatory tooltip) only when the action's *absence* would be confusing.
|
||||
|
||||
@@ -295,13 +295,13 @@ Components fall into two categories: **primitives** and **compound** components.
|
||||
|
||||
### Primitives
|
||||
|
||||
**Primitives** (`Text`, `Image`, form inputs, layout helpers, `ListItem`) are self-contained — they render a single semantic element with their own styling. A primitive **is its own shell**: there is no separate shell wrapper. Each primitive has a paired skeleton (`TextSkeleton`, `ImageSkeleton`) that matches its dimensions.
|
||||
**Primitives** (`Text`, `Avatar`, `Badge`, form inputs, layout helpers) are self-contained — they render a single semantic element with their own styling. A primitive **is its own shell**: there is no separate shell wrapper. Each primitive has a paired skeleton (`TextSkeleton`, `AvatarSkeleton`) that matches its dimensions.
|
||||
|
||||
### Compound components
|
||||
|
||||
**Compound components** (`ImageCard`, …) assemble multiple primitives into a larger region. When logic (state, effects, data) lives inside the top-level component, a **shell** separates layout from behavior:
|
||||
**Compound components** (`Card`, `Dropdown`, …) assemble multiple primitives into a larger region. When logic (state, effects, data) lives inside the top-level component, a **shell** separates layout from behavior:
|
||||
|
||||
- **Shell** — pure layout frame that accepts region props (`image`, `text`, …) as `ReactNode` and applies `tv` slot classes. No state, no effects, no data.
|
||||
- **Shell** — pure layout frame that accepts region props (`media`, `text`, …) as `ReactNode` and applies `tv` slot classes. No state, no effects, no data.
|
||||
- **Root** — owns the logic and renders the shell, passing primitives into its region props.
|
||||
- **Skeleton** — reuses the **same shell** with skeleton primitives, so the loading placeholder is structurally identical without pulling in the logic graph.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user