PromoCodeValidator
ImplementedPromo-code entry molecule with an embedded async validation state machine (idle → validating → valid / invalid). Renders input + Apply + a live-region feedback line; the result is a {valid, message} text line — no discount/expiry/terms display. Used in checkout.
patterns/molecules/promocodevalidator.htmlPromo-code entry field with an embedded async validation state machine (idle → validating → valid / invalid). Barrel-exported molecule; the only consumer is the storefront checkout.
PromoCodeValidator
Molecule — Form & Validation
Raw input + Button atom + live-region feedback. The consumer owns the validation call (onValidate) and any applied-code display.
Try WELCOME10 (valid) — any other code is invalid.
Controlled value: (empty)
This component supports light and dark mode.
Props
valuestringControlled value. Omit to run uncontrolled (internal state).
onChange*(value: string) => voidCalled on every keystroke with the (auto-formatted) value.
onValidate*(code: string) => Promise<{ valid: boolean; message?: string; discount?: string }>Called on Apply / Enter. Drives the validating → valid / invalid lifecycle.
loadingbooleanfalseForces the busy state (parent-driven async work).
placeholderstringEnter promo codePlaceholder text shown when the field is empty.
disabledbooleanfalseDisables both the input and the Apply button.
autoFormatbooleantrueUppercases input and filters to A-Z / 0-9.
size'sm' | 'md' | 'lg'mdSize preset applied to the input and the Apply button.
labelstringVisible label, associated via htmlFor.
hintstringHelper text shown below the field in the idle state.
errorstringError message; takes precedence over validation feedback.
successMessagestringSuccess message shown when validation succeeds.
classNamestringAdditional CSS classes on the root element.
idstringInput id override; derived from useId otherwise.
Usage
Barrel import. Both onChange and onValidate are required — the component does not default them. The real-world wiring (storefront checkout) calls a server endpoint from onValidate and reports a valid / message pair; the molecule owns the Apply button, Enter-to-submit and the status lifecycle.
import { useState } from 'react'
import { PromoCodeValidator } from '@epasslive/design-system'
export function PromoSection() {
const [code, setCode] = useState('')
return (
<PromoCodeValidator
label="Promo code"
value={code}
onChange={setCode}
onValidate={async (candidate) => {
const res = await fetch('/api/promo-codes/apply', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: candidate }),
})
const data = await res.json().catch(() => ({}))
return res.ok
? { valid: true, message: 'Promo code applied.' }
: { valid: false, message: data.message ?? 'This code is not valid.' }
}}
/>
)
}Props
PromoCodeValidator
valuestring—Controlled value. Omit to run uncontrolled (internal state).
onChange(value: string) => void—Called on every keystroke with the (auto-formatted) value.
onValidate(code: string) => Promise<{ valid: boolean; message?: string; discount?: string }>—Called on Apply / Enter. Drives the validating → valid / invalid lifecycle.
loadingbooleanfalseForces the busy state (parent-driven async work).
placeholderstring'Enter promo code'Placeholder text shown when the field is empty.
disabledbooleanfalseDisables both the input and the Apply button.
autoFormatbooleantrueUppercases input and filters to A-Z / 0-9.
size'sm' | 'md' | 'lg''md'Size preset applied to the input and the Apply button.
labelstring—Visible label, associated via htmlFor.
hintstring—Helper text shown below the field in the idle state.
errorstring—Error message; takes precedence over validation feedback.
successMessagestring—Success message shown when validation succeeds.
applyLabelstring'Apply'Label for the Apply button — pass a localized string (e.g. t('form.promo.apply')) to avoid hardcoded English.
classNamestring—Additional CSS classes on the root element.
idstringgenerated useIdInput id override; derived from useId otherwise.
<PromoCodeValidator />Accessibility
Type
1 ruleARIA
1 ruleUsage
Import
import { PromoCodeValidator } from "@epasslive/design-system"Example 1
<PromoCodeValidator label={t("form.promo.label")} placeholder={t("form.promo.placeholder")} value={code} onChange={setCode} onValidate={applyPromo} successMessage={t("form.promo.applied")} />Anti-patterns
— Do not hardcode data in molecules
— Avoid complex business logic in molecules