Skip to content

QuoteRequestButton

Trigger, dialog and open state. Renders nothing when the buyer may not request one, and takes the whole cart because commercetools refuses one whose cart has no address.

import { QuoteRequestButton } from "@evolve-storefront/feature-quote/quote-request-button";

Renders inside QuoteProvider.

Preview

DefaultOpen in Storybook ↗

Usage

// One element on the basket page: the dialog comes with it, and it renders
// nothing for a buyer who may not request a quote.
<QuoteRequestButton
cart={cart}
onRequested={(request) => router.push(quoteRequestPath(request.id.id))}
/>;

Props

PropTypeDescription
cartQuotableCart

The cart to quote; its lines and address become the request.

isPending?boolean

The action is running. The button keeps its label, so its width and accessible name hold, shows a spinner and is aria-busy. It stays focusable rather than disabled, so focus is not dropped mid-save; clicks and the form submit it would trigger are ignored. Not supported with asChild.

type?"submit" | "reset" | "button"
tone?ButtonTone
data-component?undefined

A button's identity is not a caller's to rename — see buttonProps. Typed away so a call site renaming it fails to compile rather than passing an attribute that is silently discarded.

variant?ButtonVariant
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.

More

Not inferred from the children. A kebab menu is as often a glyph or an SVG as an <Icon>, and a visually-hidden label — the accessible thing to add — would break any :only-child predicate silently.

asChild?boolean
defaultValues?Partial<{ purchaseOrderNumber: string; comment?: string | undefined; }>
onRequested?((quoteRequest: QuoteRequest) => void)

Runs with the created request after success (e.g. navigate to it).

Any other props go to the root element, like on a plain HTML element.

Change the layout

Pass children to QuoteRequestButton to arrange its parts yourself. Every part is exported from @evolve-storefront/feature-quote/quote-request-button.

QuoteRequestDialogRequest a quote from a cart, as a modal.

Per evolve-dialog-form: the dialog owns its DialogFooter, cancel is a DialogClose, and the submit part lives inside the <form>. Server errors are cleared by onClosed — after the exit animation, so the content does not blank while it is still fading out.

It takes the cart as an id rather than reading one: a feature never depends on another feature, so the surface that knows the active cart passes it in.

One call. The cart already carries its delivery address — set on the cart, where the buyer can see the shipping costs it produces — so this dialog only asks for the reference and the comment.

PropTypeDescription
cartIdstring

The cart the request is created from; its lines become the quote.

openboolean
onOpenChange(open: boolean) => void
defaultValues?Partial<{ purchaseOrderNumber: string; comment?: string | undefined; }>
onRequested?((quoteRequest: QuoteRequest) => void)

Runs with the created request after success (e.g. navigate to it).

QuoteRequestFormRequest a quote from the current basket on the evolve-feature-form pattern (RAC + Zod).

It seeds QuoteRequestFormContext so the composed Fields/Submit pieces read the extras without prop-drilling. onValidSubmit runs the mutation; QuoteRequestDialog is the ready-made composition.

PropTypeDescription
onValidSubmit(values: { purchaseOrderNumber: string; comment?: string | undefined; }) => void

Runs with the validated values on a valid submit — where the mutation goes.

childrenReactNode

The composed pieces — typically <QuoteRequestFormFields /> and a footer with <QuoteRequestFormSubmit>. The form owns state and the <form> element; the caller owns the layout (so a dialog keeps its DialogFooter).

validationBehavior?"aria" | "native"

Default "aria": mark fields via ARIA rather than blocking native submit. The form is then noValidate, so an input's own constraint (type="email") never stops the submit with a browser tooltip that the field's error does not match.

defaultValues?Partial<{ purchaseOrderNumber: string; comment?: string | undefined; }>
isPending?boolean
error?string | null

Form-level error (e.g. a failed mutation), shown above the actions.

serverErrors?Partial<Record<"purchaseOrderNumber" | "comment", string | string[]>>

Server-provided field errors, merged into the same display path.

Any other props go to the root element, like on a plain HTML element.

QuoteRequestFormContextThe context QuoteRequestForm fills.

No props of its own.

QuoteRequestFormFieldsThe PO reference + optional comment fields, seeded from QuoteRequestForm's context.

No props of its own.

QuoteRequestFormSubmitThe 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).
PropTypeDescription
pendingLabelReactNode

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 aria-busy. It stays focusable rather than disabled, so focus is not dropped mid-save; clicks and the form submit it would trigger are ignored. Not supported with asChild.

tone?ButtonTone
data-component?undefined

A button's identity is not a caller's to rename — see buttonProps. Typed away so a call site renaming it fails to compile rather than passing an attribute that is silently discarded.

variant?ButtonVariant
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.

More

Not inferred from the children. A kebab menu is as often a glyph or an SVG as an <Icon>, and a visually-hidden label — the accessible thing to add — would break any :only-child predicate silently.

asChild?boolean

Any other props go to the root element, like on a plain HTML element.