diff --git a/apps/storybook/src/patterns/RepeatableRowsGuidance.stories.tsx b/apps/storybook/src/patterns/RepeatableRowsGuidance.stories.tsx
index 11d93a830..c8207b627 100644
--- a/apps/storybook/src/patterns/RepeatableRowsGuidance.stories.tsx
+++ b/apps/storybook/src/patterns/RepeatableRowsGuidance.stories.tsx
@@ -781,8 +781,8 @@ function RepeatableRowsGuidancePage({ globalTheme }: { globalTheme: string }) {
Each control is named by its row and its column, such as “Condition 2 Field”.
- Stacked rows, in narrow panels: each field gets its own small label, a{' '}
- FormFieldHeader with the muted variant. Column labels
+ Stacked rows, in narrow panels: each field gets its own label, the same{' '}
+ FormFieldLabel as cards and other form fields. Column labels
can't reach a value that has dropped to its own line.
@@ -796,9 +796,10 @@ function RepeatableRowsGuidancePage({ globalTheme }: { globalTheme: string }) {
Build labels from the Field Anatomy parts. A standalone field is a{' '}
FormField with a FormFieldLabel, or a{' '}
FormFieldHeader when its label row carries a badge or actions.
- Shared column labels are one muted FormFieldHeader per column,
- outside any field, and each control names itself with that label through{' '}
- aria-labelledby.
+ Shared column labels are one FormFieldLabel per column. Wrap
+ the column labels and the row list together in a FormField so
+ the first row sits the same distance below its label as any other field. Each control
+ names itself with its column label through aria-labelledby.
diff --git a/apps/storybook/src/patterns/repeatable-row-primitives.tsx b/apps/storybook/src/patterns/repeatable-row-primitives.tsx
index acf19e6eb..c0febf8ae 100644
--- a/apps/storybook/src/patterns/repeatable-row-primitives.tsx
+++ b/apps/storybook/src/patterns/repeatable-row-primitives.tsx
@@ -33,7 +33,12 @@ import {
type ValueMode,
ValueModeIndicator,
} from '@/components/ui/field-addons';
-import { FormField, FormFieldError, FormFieldHeader } from '@/components/ui/form-field';
+import {
+ FormField,
+ FormFieldError,
+ FormFieldHeader,
+ FormFieldLabel,
+} from '@/components/ui/form-field';
import { Input } from '@/components/ui/input';
import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group';
import { MultiSelect } from '@/components/ui/multi-select';
@@ -553,12 +558,12 @@ function ConditionColumnHeaders({
return (
-
-
-
+ Field
+ Operator
+ Value
{/* Keeps the labels over their columns: the delete button's width. */}
-
+
);
}
@@ -831,16 +836,16 @@ export function ConditionRow({
-
+ Field
{fieldControl}
-
+ Operator
{operatorControl}
{valueControl && (
-
+ Value
{valueControl}
)}
@@ -848,7 +853,7 @@ export function ConditionRow({
{/* A label-height spacer, then a control-height box, so the icon centres on the first
line of controls rather than on the labels. */}
-
+
{deleteControl}
@@ -1087,59 +1092,61 @@ function GroupBody({
)}
- {columns &&
}
-
-
- {node.children.length === 0 ? (
-
{nested ? 'No conditions in this group.' : emptyText}
- ) : (
- node.children.map((child, position) => {
- if (child.kind === 'group') {
- nestedGroups += 1;
+
+ {columns && }
+
+
+ {node.children.length === 0 ? (
+ {nested ? 'No conditions in this group.' : emptyText}
+ ) : (
+ node.children.map((child, position) => {
+ if (child.kind === 'group') {
+ nestedGroups += 1;
+ return (
+ deleteChild(child.id, position)}
+ />
+ );
+ }
return (
- deleteChild(child.id, position)}
+ onChange={(patch) =>
+ onRootChange(
+ updateNode(
+ root,
+ child.id,
+ (current) => ({ ...current, ...patch }) as Condition
+ )
+ )
+ }
+ onDelete={() => deleteChild(child.id, position)}
/>
);
- }
- return (
-
- onRootChange(
- updateNode(
- root,
- child.id,
- (current) => ({ ...current, ...patch }) as Condition
- )
- )
- }
- onDelete={() => deleteChild(child.id, position)}
- />
- );
- })
- )}
-
-
+ })
+ )}
+
+
+
addNode(condition())}>
@@ -1335,56 +1342,58 @@ export function KeyValueList({
}
/>
- {pairs.length > 0 && (
- // Visible column labels: the persistent labels for every row's Name and Value.
-
- {reorderable &&
}
-
-
-
-
-
-
- )}
-
- {pairs.map((pair, position) => (
-
-
- {label} {position + 1}
-
- {reorderable && (
-
0 ? () => move(position, position - 1) : undefined}
- onMoveDown={
- position < pairs.length - 1 ? () => move(position, position + 1) : undefined
- }
- />
- )}
+
+ {pairs.length > 0 && (
+ // Visible column labels: the persistent labels for every row's Name and Value.
+
- ))}
- {pairs.length === 0 && No {itemName}s.}
-
+ )}
+
+ {pairs.map((pair, position) => (
+
+ ))}
+ {pairs.length === 0 &&
No {itemName}s.}
+
+
void }) {
-
+ How to look up the record