@get-asset/sdk
Locked Upsell
Shared machinery behind every chart card's locked / upsell state. LockedUpsell has no Rootof its own — each lockable card's own Root accepts locked and upselland provides the context these parts read. Compose them inside that card's Locked state gate.
Preview
Loading preview…
Anatomy
Import the primitive and compose the parts you need.
<ProfitLossCard.Root locked upsell={{ ctaLabel: "Upgrade now", onUpgrade }}><ProfitLossCard.Locked><LockedUpsell.Teaser>{/* the report's normal chart/bars/metrics markup, rendered as a blurred, inert teaser */}</LockedUpsell.Teaser><LockedUpsell.Overlay><LockedUpsell.Title /><LockedUpsell.Description /><LockedUpsell.Cta /></LockedUpsell.Overlay></ProfitLossCard.Locked></ProfitLossCard.Root>
API reference
Root props
| Prop | Type | Default | Description |
|---|---|---|---|
locked | boolean | — | On each lockable card's Root (default false). Gates the report for a viewer who isn't entitled to it — activates the Locked state (mutually exclusive with loading / error / empty / success) and skips the API fetch, including via retry(). Wins over the state override. |
upsell | { title?, description?, ctaLabel?, onUpgrade? } | — | On each lockable card's Root. Tenant-customizable copy + upgrade handler for the locked state. Resolved per field: the upsell prop, then the card's own default copy, then generic fallback copy. |
Parts
Parts
Compose these inside a lockable card's `Locked` state gate — they read the copy and upgrade handler from that card's Root.
| Part | Description |
|---|---|
LockedUpsell.TeaserasChild? | Wraps the report's normal markup as a blurred, inert teaser: aria-hidden, data-locked-teaser, pointer-events/user-select disabled, and the node set inert. |
LockedUpsell.OverlayasChild? | Wrapper for the lock badge, copy, and CTA. Renders role="group" with an aria-label of the resolved title. |
LockedUpsell.TitleasChild? | Resolved upsell headline. |
LockedUpsell.DescriptionasChild? | Resolved upsell disclaimer. |
LockedUpsell.CtaasChild? | Button that calls the tenant's onUpgrade on click. No-op when onUpgrade is omitted. |
Hooks
| Part | Description |
|---|---|
useLockedUpsell() | Hook returning { locked, upsell }, where upsell is the resolved { title, description, ctaLabel, onUpgrade }. Throws when used outside a lockable card's Root. |
Behavior notes
- No request fires while locked — including via
retry(), which is a no-op whenlockedis set (TanStack Query'srefetch()otherwise bypassesenabled). - With no data override, the card serves deterministic, built-in sample data as the teaser — healthy-looking and obviously generic. Passing a data override (e.g.
months) still wins over that sample data. - Unlocking is the tenant's responsibility: re-render with
lockedset tofalse. The SDK never auto-unlocks. lockedwins over thestateoverride — a card forced intostate="loading"(orerror/empty) still rendersLockedwhenlockedis also set.- Never pass real entitlement-gated data as the teaser — the teaser is meant to be a generic, illustrative sample, not the viewer's actual (gated) numbers.