Sanity Library Reference Docs
    Preparing search index...

    Module @sanity/workflow-components - v0.32.0

    @sanity/workflow-components

    Reusable React controls for Workflows member, assignee, and date fields. The package uses @sanity/ui but has no dependency on Sanity Studio or the App SDK, so the same components can be used in either integration.

    Render the components inside a Sanity UI ThemeProvider. Member controls take account-global user ids (sanityUserId) — the one identity namespace @sanity/workflow-engine stores and compares. The integration owns the join from its host's project-scoped member records to these rows — for example useProjectMembers in @sanity/workflow-sdk; the Studio plugin ships its own equivalent — built on this package's projectMemberRow.

    Supply the project directory explicitly. App SDK integrations can pass the result of useProjectMembers(projectId) from @sanity/workflow-sdk; Studio integrations can adapt their existing project-member query to the same shape.

    import {MemberPicker, type ProjectMember} from '@sanity/workflow-components'

    const members: ProjectMember[] = [
    {
    id: 'gAccountUserId',
    displayName: 'Ada Lovelace',
    email: 'ada@example.com',
    roles: [{name: 'editor', title: 'Editor'}],
    },
    ]

    export function OwnerPicker() {
    return (
    <MemberPicker
    error={undefined}
    loading={false}
    members={members}
    onSelect={(member) => console.log(member?.id)}
    selectedIds={new Set()}
    unassignRow
    />
    )
    }

    MemberPicker reports a ProjectMember or null for the optional unassigned row. AssigneePicker is a controlled multi-value input: value is the current engine Assignee array and onChange receives the complete next array after a role or member is toggled. AssigneeBadge, AssigneeBadges, MemberAvatar, and MemberAvatarGroup provide matching read-only presentation.

    AssigneeStack presents a scope's whole assignment as one overlapping cluster for a dense list row: members render as round avatars, roles as square ones through RoleAvatar, which initials a role from its label and colours it from its name. Roles render last, so a cluster past three items collapses to roles rather than to faces. Both take display data only — resolving user ids to people, and naming everyone in full once the cluster abbreviates, stay with the integration.

    Neither avatar renders a tooltip or carries an accessible name of its own, so a host rendering RoleAvatar outside AssigneeStack owns naming it.

    A ProjectMember's roles carry both spellings a host supplies. name is the machine name: the only spelling the engine stores in an assignee value and compares against an actor's roles. title is the project's human label, present when the host's membership records include one. Every surface showing a role reads the title, falling back to the name when there is no title, so a record without one reads as its machine name rather than as a blank label.

    ProjectMembersState.roles is the project's own catalog of assignable roles, supplied by the host alongside the members. It is the authority on a role's title and the only account of a role nobody currently holds, which no membership record can carry. A RoleVocabulary — the catalog plus the members — is what resolution takes.

    roleLabel resolves the name/title pair. memberRoleFor and roleLabelFor resolve the other direction, from a name stored in an assignee value back to the role record or straight to its label. The catalog answers first, and any name it does not account for falls through to the roles members hold — whether the catalog is still loading, could not be read, or simply omits that name — so a label degrades to a worse title rather than to none. A name in neither resolves to itself.

    roleMemberCount reports the distinct people who can fulfill any supplied role under a workflow definition's aliases. Pass members, requiredRoles, and the definition's normalized roleAliases; the result includes alias-only fulfillers and deduplicates them by account-global user id. It does not expand or rewrite a member's literal roles, which remain the source for profile display and search.

    Without eligibleRoles, AssigneePicker offers the whole catalog, so a role with no holders is assignable. It always merges in any name the current value carries that the catalog does not account for — a role deleted since it was assigned would otherwise have no row, and so no way to be seen or removed. Those value-derived rows sort in by label like every other one, and stay listed for the picker's lifetime once present, so clearing one does not delete the row under the pointer; a host that renders the picker in a popover gets them dropped on the next open, since closing unmounts the content. A host mounting it inline should remount it (or change its key) to get the same reset. MemberPicker takes no catalog: it lists members only.

    Both pickers match role names and titles in their search, so typing a role narrows the list to its holders, and both list members by display name. A host's own member order is the sequence people joined the project, which on a large project puts whoever joined first at the top rather than anyone findable.

    A picker row shows a name and nothing else, because a name is not enough to tell two members apart — the account behind it lives in MemberPreview, which the row previews beside itself on hover: a larger avatar badged with the person's identity provider, their email, and the roles they hold. Rows share one tooltip delay group, so the wait is paid once and moving along the list swaps the preview instead of making the reader wait again at every row.

    The preview belongs to the pointer and does not follow the keyboard-active row. Driving it from both reads as broken from either: with the keyboard holding one row, moving the mouse onto another leaves the first preview up through the hover delay, so the reader watches the wrong card and then a jump. The active row has its own highlight.

    MemberAvatar takes an optional loginProvider to badge the mark onto its corner; leave it off in a dense list, where the mark is larger than the corner it would sit in.

    Marks exist for Google, GitHub, and any saml--prefixed deployment, which reads as an SSO wordmark. Any other provider badges nothing at all — no mark and no circle — so an unrecognised value costs a reader nothing rather than showing an empty badge.

    Beyond the value-derived rows described above, neither picker reorders or re-sections its list in response to the value: selecting a catalogued role or a member decides what is checkmarked and nothing else. AssigneePicker can opt into field-value eligibility with eligibleRoles and roleAliases. It then offers users who fulfil at least one listed role and only the listed collective roles. The picker names the active eligible roles and distinguishes an empty eligible set from a search with no matches. A current value that no longer qualifies stays visible so an editor can remove or replace it. This is distinct from action permission and field editability; callers should pass eligibility only from an assignment field's declared roles facet.

    DatePicker emits local JavaScript Date values after complete calendar or time gestures. Storage remains the caller's responsibility, so the control works with an engine field edit, an action parameter, or local application state.

    import {ClearableDatePicker} from '@sanity/workflow-components'
    ;<ClearableDatePicker
    clearLabel="Clear due date"
    onClear={() => setDueDate(undefined)}
    onPick={setDueDate}
    value={dueDate}
    />

    Set selectTime when time-of-day is part of the workflow field.

    Interfaces

    AssignmentIdentity
    MemberAvatarData
    MemberRole
    ProjectMember
    ProjectMembersState

    Type Aliases

    AssignmentMatch
    RoleVocabulary

    Functions

    AssigneeBadge
    AssigneeBadges
    AssigneePicker
    AssigneeStack
    assignmentMatch
    clampProgress
    ClearableDatePicker
    DatePicker
    MemberAvatar
    MemberAvatarGroup
    memberCountLabel
    MemberPicker
    memberRoleFor
    ProgressBar
    progressCaption
    projectMemberRow
    providerTitle
    RoleAvatar
    roleLabel
    roleLabelFor
    roleListLabel
    roleMemberCount
    sameAssignee