EditRolesForm
Change an associate's role on the evolve-feature-form pattern:
a RAC Form whose validationErrors drive display, clean until the first
submit then live, and a submit button that is never disabled.
import { EditRolesForm } from "@evolve-storefront/feature-organization/edit-roles-form";Renders inside OrganizationProvider.
Design notes
Compound: this root owns the <form> and its state and seeds
EditRolesFormContext, while EditRolesFormFields and EditRolesFormSubmit
read it from there. That is what lets EditRolesDialog put cancel and submit in
a DialogFooter without this form importing anything dialog-specific.
Preview
Usage
Nothing pre-selected — submitting marks the field invalid.
const update = useUpdateAssociateRoles();
<EditRolesForm roles={unit.availableRoles} onValidSubmit={({ roleId }) => update.mutate({ customerId: associate.id, roleIds: [roleId] })}> <EditRolesFormFields /> <EditRolesFormSubmit pendingLabel={t("saving")}>{t("save")}</EditRolesFormSubmit></EditRolesForm>;Props
| Prop | Type | Description |
|---|---|---|
roles | BusinessUnitRole[] | |
onValidSubmit | (values: { roleId: string; }) => void | Runs with the validated values on a valid submit — where the mutation goes. |
children | ReactNode | The composed pieces — typically |
validationBehavior? | "aria" | "native" | Default |
defaultRoleId? | string | |
isPending? | boolean | |
error? | string | null | Form-level error (e.g. a failed mutation), shown above the actions. |
serverErrors? | FormValidationErrors | Server-provided field errors, merged into the same display path. |
Any other props go to the root element, like on a plain HTML element.
Change the layout
Pass children to EditRolesForm to arrange its parts yourself. Every part is exported from @evolve-storefront/feature-organization/edit-roles-form.
const update = useUpdateAssociateRoles();
<EditRolesForm roles={unit.availableRoles} onValidSubmit={({ roleId }) => update.mutate({ customerId: associate.id, roleIds: [roleId] })}> <EditRolesFormFields /> <EditRolesFormSubmit pendingLabel={t("saving")}>{t("save")}</EditRolesFormSubmit></EditRolesForm>;EditRolesDialogModal role editor.
| Prop | Type | Description |
|---|---|---|
open | boolean | |
onOpenChange | (open: boolean) => void | |
associate | BusinessUnitAssociate | |
roles | BusinessUnitRole[] | |
defaultOpen? | boolean |
EditRolesFormContextThe context EditRolesForm fills.
No props of its own.
EditRolesFormFieldsThe role field, seeded from EditRolesForm's context.
The form-level error sits at the end of this part rather than in the footer, so it reads above the actions wherever the caller places them.
No props of its own.
EditRolesFormSubmitThe form's submit button — never disabled (per the evolve-feature-form pattern: submission is gated by validity in the form's onSubmit, not by a disabled control).
| Prop | Type | Description |
|---|---|---|
pendingLabel | ReactNode | Shown while a submit is in flight. |
isPending? | boolean | The action is running. The button keeps its label, so its width and
accessible name hold, shows a spinner and is |
variant? | ButtonVariant | |
tone? | ButtonTone | |
isIconOnly? | boolean | The button has no visible label, so it is square. Structural rather than a size: it says the button has nothing to be wide for, and says nothing about how big — the brand's control tokens still decide that. MoreNot inferred from the children. A kebab menu is as often a glyph or an SVG
as an |
asChild? | boolean | |
data-component? | undefined | A button's identity is not a caller's to rename — see |
Any other props go to the root element, like on a plain HTML element.
Examples
SeededSeeded with the associate's current role.
// Seed the select with the role the associate holds today.<EditRolesForm roles={roles} defaultRoleId={associate.roles[0]?.id} onValidSubmit={submit}> <EditRolesFormFields /> <EditRolesFormSubmit pendingLabel={t("saving")}>{t("save")}</EditRolesFormSubmit></EditRolesForm>;PendingSubmitting: the button shows the pending label (never disabled).
// Straight off the mutation: the submit swaps its label and stays enabled.<EditRolesForm roles={roles} isPending={update.isPending} onValidSubmit={submit}> <EditRolesFormFields /> <EditRolesFormSubmit pendingLabel={t("saving")}>{t("save")}</EditRolesFormSubmit></EditRolesForm>;With ErrorA failed mutation surfaces a form-level error above the actions.
// A form-level error, e.g. from a failed mutation, reads above the actions.<EditRolesForm roles={roles} error={error} serverErrors={serverErrors} onValidSubmit={submit}> <EditRolesFormFields /> <EditRolesFormSubmit pendingLabel={t("saving")}>{t("save")}</EditRolesFormSubmit></EditRolesForm>;
