diff --git a/workspaces/js/src/index.ts b/workspaces/js/src/index.ts index aa53f931..87b4e50b 100644 --- a/workspaces/js/src/index.ts +++ b/workspaces/js/src/index.ts @@ -1,6 +1,9 @@ export type { Action, ActiveBlock, + ComponentActiveBlock, + TourComponentActiveBlock, + SurveyActiveBlock, BlockState, FlowsProperties, StateMemory, diff --git a/workspaces/react/src/index.ts b/workspaces/react/src/index.ts index 32cc5912..1819de52 100644 --- a/workspaces/react/src/index.ts +++ b/workspaces/react/src/index.ts @@ -1,6 +1,9 @@ export type { Action, ActiveBlock, + ComponentActiveBlock, + TourComponentActiveBlock, + SurveyActiveBlock, BlockState, FlowsProperties, StateMemory, diff --git a/workspaces/react/src/lib/active-block.ts b/workspaces/react/src/lib/active-block.ts index b2678bb0..1450a81e 100644 --- a/workspaces/react/src/lib/active-block.ts +++ b/workspaces/react/src/lib/active-block.ts @@ -84,7 +84,7 @@ export const blockToActiveBlock = ({ const activeBlock: ActiveBlock = { id: block.id, - type: block.type as ActiveBlock["type"], + type: block.type, component: block.componentType, props, }; diff --git a/workspaces/shared/src/types/active-block.ts b/workspaces/shared/src/types/active-block.ts index 258f48cb..dccf0f13 100644 --- a/workspaces/shared/src/types/active-block.ts +++ b/workspaces/shared/src/types/active-block.ts @@ -1,27 +1,97 @@ -import { type FlowsProperties } from "./components"; +import type { + FlowsProperties, + ComponentProps, + SurveyComponentProps, + TourComponentProps, +} from "./components"; -export interface ActiveBlock { +type ActiveBlockBase = { /** - * Unique identifier of the block, useful for stable key during rendering. Keep in mind each workflow version will have a different id for each block. + * Unique identifier of the block. Useful as a stable `key` during rendering. + * + * Note: each workflow version assigns different ids to each block, so this value changes on workflow publish. */ id: string; /** - * Unique identifier of the tour block this tour-component belongs to. Keep in mind each workflow version will have a different id for each block. + * Discriminant that identifies the block variant. Use this to narrow the type and access type-safe `props`. + * + * - `"component"` — a standalone workflow block + * - `"tour-component"` — a single step within a tour + * - `"survey"` — a survey block */ - tourBlockId?: string; + type: T; /** - * Type of the block, either "component" or "tour-component" or "survey". + * The key of the registered UI component used to render this block. + * Must match a key registered in `FlowsProvider` (React) or `init` (JS). */ - type: "component" | "tour-component" | "survey"; + component: string; /** - * The UI Component used to render this block. + * Props to pass to the component. The exact shape depends on the block `type`: + * + * - `"component"` → `ComponentProps` + * - `"tour-component"` → `TourComponentProps` — includes `continue`, `previous?`, and `cancel` + * - `"survey"` → `SurveyComponentProps` — includes `survey`, `complete`, and `cancel` */ - component: string; + props: P; +}; + +/** + * An `ActiveBlock` representing a standalone workflow component block. + * + * Narrow to this type by checking `block.type === "component"`. + */ +export type ComponentActiveBlock = ActiveBlockBase< + "component", + ComponentProps> +>; + +/** + * An `ActiveBlock` representing a single step within a tour. + * + * Narrow to this type by checking `block.type === "tour-component"`. + */ +export type TourComponentActiveBlock = ActiveBlockBase< + "tour-component", + TourComponentProps> +> & { /** - * Props to be passed to the component including both data and exit node methods. + * Unique identifier of the parent tour block this step belongs to. Useful as a stable `key` during rendering. + * + * Prefer this over `id` when rendering tour steps — reusing the same key across steps lets the browser + * reuse the DOM element rather than unmounting and remounting it between individual steps. + * + * Note: each workflow version assigns different ids to each block, so this value changes on workflow publish. */ - props: { __flows: FlowsProperties } & Record; -} + tourBlockId?: string; +}; + +/** + * An `ActiveBlock` representing a survey block. + * + * Narrow to this type by checking `block.type === "survey"`. + */ +export type SurveyActiveBlock = ActiveBlockBase< + "survey", + SurveyComponentProps> +>; + +/** + * A block that is currently active and ready to render. + * + * Use `block.type` to narrow to a specific variant and access type-safe `props`: + * + * ```ts + * if (block.type === "tour-component") { + * // props is TourComponentProps — includes continue, previous?, cancel + * block.props.continue(); + * } + * ``` + * + * @see {@link ComponentActiveBlock} for `"component"` blocks + * @see {@link TourComponentActiveBlock} for `"tour-component"` blocks + * @see {@link SurveyActiveBlock} for `"survey"` blocks + */ +export type ActiveBlock = ComponentActiveBlock | TourComponentActiveBlock | SurveyActiveBlock; export const createActiveBlockProxy = ( block: ActiveBlock,