Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,31 @@ See the [docs/](./docs/) folder for comprehensive API documentation:

### 🎯 Working Examples

See the [examples/](./examples/) folder for complete working examples:
#### Running Examples Locally

```bash
# Install dependencies
pnpm install

# Start the examples server
pnpm run dev:examples
```

This will open `http://localhost:3000` with an interactive gallery of all examples.

#### Available Examples

- **[Basic Usage](./examples/basic-usage/)** - Simple setup and device display
- **[Device Selector](./examples/device-selector/)** - Interactive device selection with single/multi-select
- **[Real-time Dashboard](./examples/real-time-dashboard/)** - Live data streaming and controls
- **[Complete Widget](./examples/complete-widget/)** - Comprehensive example testing all features
- **[Event Versioning](./examples/event-versioning/)** - V1 and V2 event compatibility
- **[Widget Events - Basic](./examples/widget-events-basic/)** ⭐ NEW - Widget-specific events with `v2:widget:<event>:<widgetId>` pattern
- **[Widget Events - Multi](./examples/widget-events-multi/)** ⭐ NEW - Multiple widgets with isolated event namespaces
- **[With HOCs](./examples/with-hocs/)** - Higher-Order Components usage

📖 **[Full Guide: How to Run Examples](./examples/RUNNING_EXAMPLES.md)**
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README links to ./examples/RUNNING_EXAMPLES.md, but that file doesn’t exist in the repo. This will be a broken link for users; either add the referenced guide or remove/update the link to an existing doc (e.g. examples/README.md).

Suggested change
📖 **[Full Guide: How to Run Examples](./examples/RUNNING_EXAMPLES.md)**
📖 **[Full Guide: How to Run Examples](./examples/README.md)**

Copilot uses AI. Check for mistakes.

## Configurable Ready State

You can control which events must occur before considering the system "ready":
Expand Down
154 changes: 154 additions & 0 deletions docs/EventVersioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Event Versioning (V1 & V2)

This library supports both V1 (legacy) and V2 event naming conventions for Ubidots dashboard communication.

## Overview

When V1 events are received or sent, the library **automatically emits the corresponding V2 events** as well. This ensures backward compatibility while supporting the new V2 event structure.

## Event Mapping

### Auth Events

| V1 Event | V2 Event | Description |
| ------------------ | --------------- | ----------------------------- |
| `receivedToken` | `v2:auth:token` | Authentication token received |
| `receivedJWTToken` | `v2:auth:jwt` | JWT token received |

### Dashboard Events

| V1 Event | V2 Event | Description |
| ---------------------------- | --------------------------------- | ------------------------------------------------- |
| `selectedDevice` | `v2:dashboard:devices:selected` | Single device selected (converted to array in V2) |
| `selectedDevices` | `v2:dashboard:devices:selected` | Multiple devices selected |
| `selectedDashboardDateRange` | `v2:dashboard:settings:daterange` | Date range selected |
| `selectedDashboardObject` | `v2:dashboard:self` | Dashboard object received |
| `selectedFilters` | `v2:dashboard:settings:filters` | Filters selected |
| `isRealTimeActive` | `v2:dashboard:settings:rt` | Real-time status |

### Outbound Events

| V1 Event | V2 Event | Description |
| ----------------------------- | ---------------------------------- | -------------------- |
| `setDashboardDevice` | `v2:dashboard:devices:selected` | Set single device |
| `setDashboardMultipleDevices` | `v2:dashboard:devices:selected` | Set multiple devices |
| `setDashboardDateRange` | `v2:dashboard:settings:daterange` | Set date range |
| `setRealTime` | `v2:dashboard:settings:rt` | Set real-time mode |
| `refreshDashboard` | `v2:dashboard:settings:refreshed` | Refresh dashboard |
| `setFullScreen` | `v2:dashboard:settings:fullscreen` | Set fullscreen mode |
| `openDrawer` | `v2:dashboard:drawer:open` | Open drawer |

## How It Works

### Inbound Events (Receiving)

When the library receives a V1 event, it:

1. Processes the event normally (updates state, triggers callbacks)
2. **Automatically emits the corresponding V2 event** to the parent window

Example:

```typescript
// When 'receivedToken' arrives:
// 1. Updates state.token
// 2. Emits 'v2:auth:token' to parent window
```

The library can also receive V2 events directly and process them the same way.

### Outbound Events (Sending)

When you call an action (e.g., `setDashboardDevice`), the library:

1. Sends the V1 event to the parent window
2. **Automatically sends the corresponding V2 event** as well

Example:

```typescript
const { setDashboardDevice } = useUbidotsActions();

// This sends BOTH:
// - 'setDashboardDevice' with deviceId
// - 'v2:dashboard:devices:selected' with [{ id: deviceId }]
setDashboardDevice('device-123');
```

## Data Format Differences

### Single Device vs Array

V1 uses a single device or device ID string, while V2 always uses an array:

```typescript
// V1
setDashboardDevice('device-123');
// Sends: { event: 'setDashboardDevice', payload: 'device-123' }

// V2 (automatically sent)
// Sends: { event: 'v2:dashboard:devices:selected', payload: [{ id: 'device-123' }] }
```

## Usage

No changes are required in your code! The library handles V2 events automatically:

```tsx
import { UbidotsProvider, useUbidotsActions } from '@ubidots/react-html-canvas';

function MyWidget() {
const { setDashboardDevice } = useUbidotsActions();

// This automatically sends both V1 and V2 events
const handleClick = () => {
setDashboardDevice('device-123');
};

return <button onClick={handleClick}>Select Device</button>;
}

export default function App() {
return (
<UbidotsProvider>
<MyWidget />
</UbidotsProvider>
);
}
```

## Ready Events

You can use either V1 or V2 event names in the `readyEvents` prop:

Comment on lines +120 to +123
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section states that readyEvents can be configured with V2 event names, but the current implementation records satisfied events using V1 names even when receiving V2 events (and V1→V2 emission won’t satisfy V2 readiness in an iframe). Either adjust the implementation to track V2 names (or both) or update this documentation to reflect the actual behavior.

Copilot uses AI. Check for mistakes.
```tsx
// Using V1 events (legacy)
<UbidotsProvider readyEvents={['receivedToken', 'selectedDevice']}>
<MyWidget />
</UbidotsProvider>

// Using V2 events
<UbidotsProvider readyEvents={['v2:auth:token', 'v2:dashboard:devices:selected']}>
<MyWidget />
</UbidotsProvider>

// Mixing both (not recommended, but supported)
<UbidotsProvider readyEvents={['receivedToken', 'v2:dashboard:devices:selected']}>
<MyWidget />
</UbidotsProvider>
```

## Migration Guide

If you're migrating from V1 to V2:

1. **No immediate action required** - V1 events continue to work
2. **Gradual migration** - Update your event listeners to use V2 event names
3. **Update readyEvents** - Switch to V2 event names in your `UbidotsProvider`
4. **Test thoroughly** - Ensure both V1 and V2 events are being received correctly

## Notes

- Widget events (`v2:widget:*`) are **not** automatically emitted by this library
- The library maintains full backward compatibility with V1 events
- Both V1 and V2 events can be received and processed simultaneously
12 changes: 12 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ Complete example demonstrating all available functionalities.

Examples using Higher-Order Components to inject props.

### 6. **event-versioning** - Event Versioning

Demonstrates V1 and V2 event compatibility and automatic event emission.

### 7. **widget-events-basic** - Widget Events (Basic)

Shows how to use widget-specific events with the `v2:widget:<event>:<widgetId>` pattern. Demonstrates event emission, monitoring, and lifecycle events.

### 8. **widget-events-multi** - Widget Events (Multi-Widget)

Advanced example showing multiple widgets on the same page with isolated event namespaces and inter-widget communication.

## How to Use Examples

Each example is an independent React component that you can copy and adapt to your project.
Expand Down
4 changes: 3 additions & 1 deletion examples/basic-usage/BasicUsage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import {
useUbidotsSelectedDevice,
useUbidotsActions,
} from '@ubidots/react-html-canvas';
import { EventEmitterPanel } from '../shared/EventEmitterPanel';
import './styles.css';

function DeviceInfo() {
const ready = useUbidotsReady();
const device = useUbidotsSelectedDevice();
const { setDashboardDevice, refreshDashboard } = useUbidotsActions();

if (!ready) {
return (
<div className='loading'>
Expand Down Expand Up @@ -76,6 +76,8 @@ export function BasicUsage() {
<main>
<DeviceInfo />
</main>

<EventEmitterPanel />
</div>
</UbidotsProvider>
);
Expand Down
2 changes: 2 additions & 0 deletions examples/complete-widget/CompleteWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
useUbidotsActions,
useUbidotsAPI,
} from '@ubidots/react-html-canvas';
import { EventEmitterPanel } from '../shared/EventEmitterPanel';
import './styles.css';

function DataDisplay() {
Expand Down Expand Up @@ -369,6 +370,7 @@ export function CompleteWidget() {
@ubidots/react-html-canvas
</p>
</footer>
<EventEmitterPanel />
</div>
</UbidotsProvider>
);
Expand Down
127 changes: 127 additions & 0 deletions examples/dev/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ubidots React HTML Canvas - Examples</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, sans-serif;
margin: 0;
padding: 0;
}

.gallery-wrapper {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}

.container {
max-width: 1200px;
margin: 0 auto;
}

header {
text-align: center;
color: white;
margin-bottom: 40px;
}

header h1 {
font-size: 3em;
margin-bottom: 10px;
}

header p {
font-size: 1.2em;
opacity: 0.9;
}

.examples-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 40px;
}

.example-card {
background: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transition:
transform 0.2s,
box-shadow 0.2s;
cursor: pointer;
text-decoration: none;
color: inherit;
display: block;
}

.example-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
}

.example-card h3 {
color: #667eea;
margin-bottom: 10px;
font-size: 1.5em;
}

.example-card p {
color: #666;
line-height: 1.6;
margin-bottom: 15px;
}

.example-card .tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}

.tag {
background: #f0f0f0;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.85em;
color: #555;
}

.tag.new {
background: #4caf50;
color: white;
}

footer {
text-align: center;
color: white;
margin-top: 40px;
opacity: 0.8;
}

footer a {
color: white;
text-decoration: underline;
}

#root {
min-height: 100vh;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="/main.jsx"></script>
</body>
</html>
Loading
Loading