Events

@alviere/ui components communicate through Custom Events dispatched on the host element. This page is the canonical reference for all event names, payload shapes, and dispatch behaviour.

Event dispatch model

All form, flow, and input component events are dispatched with:

new CustomEvent(name, {
  detail: payload,
  bubbles: true,
  composed: true,
})

bubbles: true means events propagate up through the DOM tree. composed: true means they cross shadow DOM boundaries — events dispatched inside a Web Component's shadow root reach listeners on the host element and beyond. This is what allows MultiStepFlow to surface domain events from its child steps without any manual forwarding.

Element component events (Checkbox, ListItem, Modal) use Svelte's event dispatcher and may not be composed by default — do not rely on them crossing shadow DOM boundaries.


Quick reference

Form and flow events

Component Events dispatched
CreateAccount / CreateConsumerAccount / CreateBusinessAccount account, form-success, form-error, form-barrier, form-action-required
AddBankAccount payment-method, form-success
StartOnboarding form-success, form-error, form-barrier
CheckoutConfirm form-success
MultiStepFlow flow-step-complete, flow-step-error, flow-complete, flow-reset + bubbles child events

Input events

Component Events dispatched
TextInput, CurrencyInput, DateOfBirthInput, EinInput text-input, text-change, text-focus, text-blur
PhoneInput phone-input, phone-change, phone-focus, phone-blur
SearchSelect search-select-change, search-select-input, search-select-open, search-select-close
FileUpload (none)

Element events

Component Events dispatched
Checkbox change
ListItem select
Modal close
LegalTextConsent legal-texts-change, view

Payload interfaces

All interfaces below are exported from @alviere/ui.

BaseComponentEventDetail

Foundation for all input events.

interface BaseComponentEventDetail {
  value: string;
  rawValue: string;
  isValid: boolean;
  validationState: ValidationState;
  errorMessage?: string;
  hasInteracted: boolean;
}

InputEventDetail

Dispatched by TextInput, CurrencyInput, DateOfBirthInput, EinInput.

interface InputEventDetail extends BaseComponentEventDetail {
  characterCount?: number;
  maxLength?: number;
}

PhoneInputEventDetail

Dispatched by PhoneInput.

interface PhoneInputEventDetail extends BaseComponentEventDetail {
  formattedValue: string;
  countryCode?: string;
}

SearchSelectEventDetail

Dispatched by SearchSelect.

interface SearchSelectEventDetail extends BaseComponentEventDetail {
  selectedOption: any;   // full option object { value, label }
  options: any[];
  searchQuery?: string;
}

AccountEventDetail

Dispatched as the account domain event by CreateAccount and its variants.

interface AccountEventDetail {
  account_uuid: string;
  status: string;
  account_status: string;  // mirrors status
  skipped?: true;          // present when the account already existed and was skipped
}

PaymentMethodEventDetail

Dispatched as the payment-method domain event by AddBankAccount.

interface PaymentMethodEventDetail {
  payment_method_uuid: string;
  status: string;
}

StepEventDetail

Dispatched by MultiStepFlow on flow-step-complete and flow-step-error.

interface StepEventDetail {
  stepIndex: number;
  stepType: string;
  result: any;      // accumulated results from all steps up to this point
  error?: Error;
}

StepBarrierEventDetail

Dispatched as form-barrier by form components.

interface StepBarrierEventDetail {
  stepType: string;
  stepIndex?: number;
  account_uuid?: string;
  status?: string;
  title?: string;
  message?: string;
  description?: string;
}

StepActionRequiredEventDetail

Dispatched as form-action-required by CreateAccount.

interface StepActionRequiredEventDetail {
  stepType: string;
  stepIndex?: number;
  account_uuid?: string;
  status?: string;
  status_reason?: string;
  message?: string;
}

FlowEventDetail

Dispatched by MultiStepFlow on flow-complete and flow-reset.

interface FlowEventDetail {
  currentStep: number;
  totalSteps: number;
  stepResults: Record<number, any>;
  isComplete: boolean;
}

Event flows

Standalone form

user interaction
  → component logic + API call
    → domain event (account / payment-method)  ← optional
    → form-success / form-error / form-barrier / form-action-required

MultiStepFlow

step emits form-success
  → MultiStepFlow stores result, merges account_uuid / payment_method_uuid
  → MultiStepFlow emits flow-step-complete
  → next step receives merged data automatically
  ...
final step completes
  → MultiStepFlow emits flow-complete

Domain events (account, payment-method) from child steps bubble through the flow element automatically — no forwarding needed. Listen for them on alviere-multi-step-flow directly.


Component events

CreateAccount / CreateConsumerAccount / CreateBusinessAccount

Event When Key payload fields
account Account created or confirmed account_uuid, status, account_status, skipped?
form-success Step completed account_uuid, status, success: true, skipped?, action_required?
form-error Critical failure error, stepType
form-barrier Account in manual review account_uuid, status, title, message, description?
form-action-required Account needs additional fields account_uuid, status, status_reason, message

form-success is also dispatched in action-required mode (with action_required: true) when the account exists but needs field updates.

AddBankAccount

Event When Key payload fields
payment-method Bank account linked payment_method_uuid, status
form-success User confirms selection payment_method_uuid, payment_method_last4, payment_method_status, success: true

form-error is not dispatched for submission failures — errors are shown inline. It fires only for critical authentication failures.

StartOnboarding

Event When Key payload fields
form-success Onboarding submitted (or auto-skipped) account_uuid, status, success: true, accepted_legal_documents?
form-error Onboarding failed error, success: false
form-barrier Account in manual review account_uuid, status, title, message

accepted_legal_documents is an array of legal text UUIDs. Present only when legal texts were configured and accepted.

CheckoutConfirm

Event When Key payload fields
form-success User confirms payment amount, success: true, accepted_legal_documents?

CheckoutConfirm makes no API calls and dispatches no error or barrier events.

MultiStepFlow

Event When
flow-step-complete A step finished successfully — StepEventDetail
flow-step-error A step failed — StepEventDetail with error
flow-complete All steps finished — FlowEventDetail
flow-reset Flow was reset — FlowEventDetail

Child step events (account, payment-method, form-success, etc.) bubble through the flow element automatically.


Input component events

Text-style inputs

TextInput, CurrencyInput, DateOfBirthInput, EinInput all dispatch InputEventDetail:

Event Trigger
text-input Every keystroke
text-change Value committed (blur or enter)
text-focus Field focused
text-blur Field blurred

PhoneInput

Dispatches PhoneInputEventDetail (phone-input, phone-change, phone-focus, phone-blur).

value is the E.164 normalised string (e.g. "+14155552671"). rawValue is digits only, no + prefix.

SearchSelect

Dispatches SearchSelectEventDetail:

Event Trigger
search-select-change Option selected or cleared
search-select-input Search query changed
search-select-open Dropdown opened
search-select-close Dropdown closed

Also dispatches a legacy change event on selection for backward compatibility.

FileUpload

Dispatches no events. Read the selected file via bind:value.


Element component events

Checkbox

Event: change

{
  checked: boolean;
  value: any;   // the component's value prop
  name: string;
}

ListItem

Event: select — fires when a selectable item is clicked.

{ value: any }  // from the selectable prop

Event: close — fires whenever the modal closes.

{ reason: 'backdrop' | 'escape' | 'button' | 'programmatic' }

LegalTextConsent

Event Payload
legal-texts-change { accepted: Record<string, boolean>; allAccepted: boolean; acceptedLegalTextUuids: string[] }
view { document: LegalTextDocumentConfig; legalText: LegalText }

ComponentEvents constants

Import event name strings directly to avoid typos:

import { ComponentEvents } from '@alviere/ui';

element.addEventListener(ComponentEvents.FORM_SUCCESS, handler);
element.addEventListener(ComponentEvents.ACCOUNT, handler);
export const ComponentEvents = {
  // Text inputs
  TEXT_INPUT:  'text-input',
  TEXT_CHANGE: 'text-change',
  TEXT_FOCUS:  'text-focus',
  TEXT_BLUR:   'text-blur',

  // Phone input
  PHONE_INPUT:  'phone-input',
  PHONE_CHANGE: 'phone-change',
  PHONE_FOCUS:  'phone-focus',
  PHONE_BLUR:   'phone-blur',

  // Search select
  SEARCH_SELECT_INPUT:  'search-select-input',
  SEARCH_SELECT_CHANGE: 'search-select-change',
  SEARCH_SELECT_OPEN:   'search-select-open',
  SEARCH_SELECT_CLOSE:  'search-select-close',

  // Form lifecycle
  FORM_SUCCESS:         'form-success',
  FORM_ERROR:           'form-error',
  FORM_BARRIER:         'form-barrier',
  FORM_ACTION_REQUIRED: 'form-action-required',
  FORM_SUBMIT:          'form-submit',
  FORM_RESET:           'form-reset',

  // Flow lifecycle
  FLOW_STEP_COMPLETE: 'flow-step-complete',
  FLOW_STEP_ERROR:    'flow-step-error',
  FLOW_COMPLETE:      'flow-complete',
  FLOW_RESET:         'flow-reset',

  // Domain events
  ACCOUNT:        'account',
  PAYMENT_METHOD: 'payment-method',

  // Legacy card inputs
  CARD_PAN_INPUT:  'card-pan-input',
  CARD_PAN_CHANGE: 'card-pan-change',
  CARD_PAN_FOCUS:  'card-pan-focus',
  CARD_PAN_BLUR:   'card-pan-blur',
  CARD_CVV_INPUT:  'card-cvv-input',
  CARD_CVV_CHANGE: 'card-cvv-change',
  CARD_CVV_FOCUS:  'card-cvv-focus',
  CARD_CVV_BLUR:   'card-cvv-blur',
  CARD_EXP_INPUT:  'card-exp-input',
  CARD_EXP_CHANGE: 'card-exp-change',
  CARD_EXP_FOCUS:  'card-exp-focus',
  CARD_EXP_BLUR:   'card-exp-blur',
} as const;

FORM_SUBMIT and FORM_RESET are defined as constants but are not currently dispatched by any component — they are reserved for future use.


Usage examples

Standalone form (Web Component)

<alviere-create-account
  jwt="your-jwt-token"
  business-uuid="your-business-uuid"
  public-certificate="your-cert"
  public-certificate-id="your-cert-id"
></alviere-create-account>

<script>
  const el = document.querySelector('alviere-create-account');

  el.addEventListener('account', e => {
    console.log('Account created:', e.detail.account_uuid);
  });

  el.addEventListener('form-success', e => {
    console.log('Step done:', e.detail);
  });

  el.addEventListener('form-barrier', e => {
    showBarrierUI(e.detail.title, e.detail.message);
  });

  el.addEventListener('form-action-required', e => {
    console.log('Additional fields required:', e.detail.status_reason);
  });

  el.addEventListener('form-error', e => {
    console.error('Error:', e.detail.error);
  });
</script>

MultiStepFlow (Web Component)

<alviere-multi-step-flow
  jwt="your-jwt-token"
  business-uuid="your-business-uuid"
  config='{"steps":[{"type":"CREATE_ACCOUNT","label":"Account"},{"type":"ADD_BANK_ACCOUNT","label":"Bank"}]}'
></alviere-multi-step-flow>

<script>
  const flow = document.querySelector('alviere-multi-step-flow');

  // Flow lifecycle
  flow.addEventListener('flow-step-complete', e => {
    console.log('Step', e.detail.stepType, 'done. Result:', e.detail.result);
  });

  flow.addEventListener('flow-complete', e => {
    console.log('All steps done:', e.detail.stepResults);
  });

  flow.addEventListener('flow-step-error', e => {
    console.error('Step failed:', e.detail.stepType, e.detail.error);
  });

  // Domain events bubble up from child steps
  flow.addEventListener('account', e => {
    console.log('Account:', e.detail.account_uuid);
  });

  flow.addEventListener('payment-method', e => {
    console.log('Payment method:', e.detail.payment_method_uuid);
  });
</script>

TypeScript

import {
  ComponentEvents,
  type AccountEventDetail,
  type PaymentMethodEventDetail,
  type StepEventDetail,
  type FlowEventDetail,
} from '@alviere/ui';

const flow = document.querySelector('alviere-multi-step-flow')!;

flow.addEventListener(
  ComponentEvents.ACCOUNT,
  ((e: CustomEvent<AccountEventDetail>) => {
    const { account_uuid, status } = e.detail;
    console.log(account_uuid, status);
  }) as EventListener
);

flow.addEventListener(
  ComponentEvents.FLOW_COMPLETE,
  ((e: CustomEvent<FlowEventDetail>) => {
    console.log('Steps:', e.detail.totalSteps, 'Results:', e.detail.stepResults);
  }) as EventListener
);

Best practices

  1. Prefer domain events for business state. Listen for account and payment-method to capture UUIDs — these fire before form-success and carry the raw API data.
  2. Always handle form-error and flow-step-error. Unhandled errors leave users with no feedback path.
  3. Handle form-barrier and form-action-required explicitly when your onboarding policy may put accounts into manual review. Silently ignoring them blocks users with no UI feedback.
  4. Use ComponentEvents constants in TypeScript code rather than raw strings to catch renames at compile time.
  5. Clean up listeners on unmount in SPA frameworks — Web Components don't clean up event listeners automatically.
  6. Don't assume form-success means API completion for CheckoutConfirm — it makes no API calls. The payload contains the confirmed amount; your code submits the payment.

Form Components Link to a page in the guide Flow Components Link to a page in the guide Input Components Link to a page in the guide