generated from coulomb/repo-seed
ihf-react-adapter.js: useWidgetEnvelope (EnvelopeEmissionContract v1.0 props), withWidgetEnvelope HOC, useInteractionReporter (POSTs to reporting contract endpoint). Plain ESM module — no IHP build toolchain required. docs/react-adapter.md with usage examples. static/ihf-react-test.html: React widget + native IHP widget on same page demonstrating annotation launcher pickup and reportEvent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
133 lines
3.7 KiB
Markdown
133 lines
3.7 KiB
Markdown
# IHF React Adapter
|
|
|
|
`static/js/ihf-react-adapter.js` provides three exports that let React
|
|
components participate in the IHF widget protocol:
|
|
|
|
| Export | What it does |
|
|
|------------------------|---------------------------------------------------------|
|
|
| `useWidgetEnvelope` | Hook — returns `data-*` props for the envelope |
|
|
| `withWidgetEnvelope` | HOC — wraps a component with the envelope div |
|
|
| `useInteractionReporter` | Hook — returns `reportEvent(type)` for API submission |
|
|
|
|
The adapter conforms to:
|
|
- **EnvelopeEmissionContract v1.0** — emits `data-widget-id`, `data-view-context`, `data-hub-id`
|
|
- **InteractionReportingContract v1.0** — POSTs to `/api/v1/interaction-events` with Bearer auth
|
|
|
|
No build toolchain is added to the IHP project. Consumers bundle this file
|
|
themselves (webpack, vite, esbuild, etc.) or load it via `<script type="module">`.
|
|
|
|
---
|
|
|
|
## useWidgetEnvelope
|
|
|
|
```jsx
|
|
import { useWidgetEnvelope } from '/js/ihf-react-adapter.js';
|
|
|
|
function MetricsPanel({ widgetId, hubId }) {
|
|
const { ref, envelopeProps } = useWidgetEnvelope(
|
|
widgetId,
|
|
hubId,
|
|
'ops-dashboard', // viewContext
|
|
{ policyScope: 'internal' } // optional
|
|
);
|
|
|
|
return (
|
|
<div ref={ref} {...envelopeProps} className="p-4 border rounded">
|
|
<h2>Metrics</h2>
|
|
{/* content */}
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
The `envelopeProps` object contains all required `data-*` attributes.
|
|
The `ref` allows the annotation launcher to find the element.
|
|
|
|
---
|
|
|
|
## withWidgetEnvelope
|
|
|
|
```jsx
|
|
import { withWidgetEnvelope } from '/js/ihf-react-adapter.js';
|
|
|
|
// Wrap at module definition time, supplying stable IDs:
|
|
const GovernedMetricsPanel = withWidgetEnvelope(
|
|
MetricsPanel,
|
|
'a3f1c2b4-...', // widgetId (UUID from widget registry)
|
|
'b9e2d3a1-...', // hubId
|
|
'ops-dashboard' // viewContext
|
|
);
|
|
|
|
// Use anywhere:
|
|
function Dashboard() {
|
|
return <GovernedMetricsPanel title="CPU Usage" />;
|
|
}
|
|
```
|
|
|
|
The HOC wraps the component in an envelope `<div>` carrying the `data-*`
|
|
attributes. The inner component receives its props unchanged.
|
|
|
|
---
|
|
|
|
## useInteractionReporter
|
|
|
|
```jsx
|
|
import { useInteractionReporter } from '/js/ihf-react-adapter.js';
|
|
|
|
function ActionButton({ widgetId, hubId, apiKey }) {
|
|
const { reportEvent } = useInteractionReporter(widgetId, hubId, apiKey);
|
|
|
|
async function handleClick() {
|
|
await reportEvent('clicked');
|
|
// proceed with action
|
|
}
|
|
|
|
return <button onClick={handleClick}>Run Report</button>;
|
|
}
|
|
```
|
|
|
|
Accepted event types (InteractionReportingContract v1.0):
|
|
`clicked`, `viewed`, `submitted`, `dismissed`, `errored`
|
|
|
|
The `apiKey` is the hub's bearer token (`hubs.api_key`). Retrieve it from
|
|
your app's server-side config — do not embed it in public bundle output.
|
|
|
|
---
|
|
|
|
## Combined example
|
|
|
|
```jsx
|
|
import {
|
|
useWidgetEnvelope,
|
|
useInteractionReporter,
|
|
} from '/js/ihf-react-adapter.js';
|
|
|
|
function GovernedButton({ widgetId, hubId, apiKey, label, onAction }) {
|
|
const { envelopeProps } = useWidgetEnvelope(widgetId, hubId, 'action-bar');
|
|
const { reportEvent } = useInteractionReporter(widgetId, hubId, apiKey);
|
|
|
|
async function handleClick() {
|
|
await reportEvent('clicked');
|
|
onAction();
|
|
}
|
|
|
|
return (
|
|
<div {...envelopeProps}>
|
|
<button onClick={handleClick}>{label}</button>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
The annotation launcher (`ihf-annotation-launcher.js`) will automatically
|
|
detect the `data-widget-id` attribute and inject an Annotate trigger alongside
|
|
this component if `IHP_ANNOTATION_LAUNCHER=true`.
|
|
|
|
---
|
|
|
|
## Test fixture
|
|
|
|
`static/ihf-react-test.html` demonstrates a React widget alongside an
|
|
IHP-rendered widget on the same page. Open it directly in the browser
|
|
(no server needed for the React side).
|