npm package: @opensite/blocks
Description: Core DashTrack site rendering block renderer
Author: OpenSite Team
Latest version: 0.4.3

# @opensite/blocks

OpenSite Blocks is the rendering runtime for DashTrack's Chai design payloads. It consumes the `design_payload` JSON saved by the page builder, fetches remote payloads on demand, and renders block trees with optimized image and video primitives.

## Features

- **Design Payload Rendering** – Render any Chai `blocks` array with a single React component.
- **Remote Fetching** – Resolve page payloads automatically from `https://cdn.ing/assets/components/:page_id`.
- **Optimized Media** – First-class support for DashTrack media via `@opensite/img` and `@opensite/video`.
- **Tree-Shakable APIs** – Import only the pieces you need (`core/BlocksRenderer`, `api/fetchDesignPayload`, etc.).
- **TypeScript Ready** – Strictly typed Chai blocks, payloads, and rendering contracts.

## Installation

```bash
npm install @opensite/blocks @opensite/img @opensite/video react react-dom
```

The package expects React 18+ in the host application. `@opensite/img` and `@opensite/video` are peer dependencies that ship the media primitives used by the renderer.

## Quick Start

```tsx
import React from "react";
import { BlocksRenderer, type ChaiBlock } from "@opensite/blocks";

const payloadBlocks: ChaiBlock[] = [
  {
    _id: "hero",
    _type: "Box",
    styles: "#styles:,container mx-auto py-16",
    tag: "section",
  },
];

export function Example() {
  return <BlocksRenderer blocks={payloadBlocks} />;
}
```

`BlocksRenderer` expects a fully expanded array of Chai blocks (the same structure stored in `design_payload.blocks`). Children are resolved automatically by `_parent` references.

## Rendering Remote Pages

```tsx
import React from "react";
import { PageBlocksRenderer } from "@opensite/blocks";

export function RemotePage({ pageId }: { pageId: string }) {
  return (
    <PageBlocksRenderer
      pageId={pageId}
      loadingFallback={<div className="p-8 text-center">Loading…</div>}
      errorFallback={(error) => (
        <div className="p-8 text-center text-red-500">Failed: {error.message}</div>
      )}
    />
  );
}
```

`PageBlocksRenderer` fetches `https://cdn.ing/assets/components/:page_id` using `fetchDesignPayloadForPage` under the hood. To customise the endpoint or inject a mock fetch implementation (e.g., during testing), use the `fetchOptions` prop:

```tsx
<PageBlocksRenderer
  pageId="49457"
  fetchOptions={{ baseUrl: "https://cdn.ing/assets/components", fetchImpl: customFetch }}
/>
```

To hydrate an already-fetched payload, pass `designPayload` directly instead of `pageId`.

## Manual Fetching

```tsx
import { fetchDesignPayloadForPage, BlocksRenderer } from "@opensite/blocks";

export async function renderPage(pageId: string) {
  const payload = await fetchDesignPayloadForPage(pageId);
  return <BlocksRenderer blocks={payload.blocks} />;
}
```

`fetchDesignPayloadForPage` throws if the payload cannot be retrieved or parsed. Wrap calls in a try/catch or surface the error via your own fallback UI.

## Extending Block Support

The renderer includes handlers for the current CMS block set (FAQs, links, layout containers, images, videos, etc.). To extend support for new block types:

1. Create a specialised renderer in `src/core/renderers/blockRenderer.tsx`.
2. Register it in the `rendererMap` keyed by the block’s `_type`.
3. Export any supporting components or utilities from the appropriate module (e.g., `src/components/<feature>`).

This keeps the module tree-shakable and aligned with the ecosystem guidelines.

## API Reference

### `BlocksRenderer`
- **Props**: `{ blocks: ChaiBlock[] }`
- Renders the provided array using the internal block registry.

### `PageBlocksRenderer`
- **Props**:
  - `pageId?: string` – Remote page token to fetch.
  - `designPayload?: DesignPayload | string` – Inline payload (string form is parsed automatically).
  - `fetchOptions?: FetchComponentOptions` – Override base URL, fetch implementation, or abort signal.
  - `loadingFallback?: React.ReactNode` – Placeholder while fetching remote payloads.
  - `errorFallback?: (error: Error) => React.ReactNode` – Custom error renderer.

### `fetchDesignPayloadForPage(pageId, options?)`
- Returns a parsed `DesignPayload`.
- `options.baseUrl` defaults to `https://cdn.ing/assets/components`.
- `options.fetchImpl` defaults to the global `fetch`.

### `parseDesignPayload(raw)`
- Accepts either a parsed object or the JSON string stored in `component.design_payload`.

### Types
- `ChaiBlock` – Base block shape used throughout the renderer.
- `DesignPayload` – `{ version: string; blocks: ChaiBlock[] }`.
- `ComponentApiResponse` – Convenience type for CDN responses.
- `FetchComponentOptions` – Options accepted by `fetchDesignPayloadForPage` and `PageBlocksRenderer`.

## Contributing

- Follow the module layout from `ECOSYSTEM_GUIDELINES.md`.
- New functionality must live in feature folders (`core`, `components`, `utils`, `api`, etc.) and remain tree-shakable.
- Prefer type-safe utilities and avoid exporting temporary demo code from this package.
- Run `npm run typecheck` before submitting changes.

## License

Internal DashTrack module – published with restricted access.