Repository navigation
📝 [SC-8017] Docs: Document useWidgetEvents (onInfo/onData and widget-to-widget events) - #28
AgustinPelaez wants to merge 1 commit into
Conversation
…to-widget events) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🔍 PR Quality Check Summary
📋 Checks Performed:
|
rarc41
left a comment
There was a problem hiding this comment.
Revisé el doc contra lo que hace el dashboard hoy (el CustomMetricWidget de ubi.frontend y los templates de ubidots-widgets). Las formas de onInfo y onData coinciden con buildWidgetInfo y normalizeWidgetData, y también están bien el nombre v2:widget:<event>:<widgetId>, el comodín :* y lo del JSDoc invertido.
Dejo en línea tres puntos donde el doc describe algo que el dashboard no hace (eventos entre widgets, formato de start/end, onReady/onError) y algunos menores. Lo verifiqué leyendo el código, no en runtime.
Las páginas Widget data y Widget events del Dev Center tienen los mismos tres puntos, y Widget events todavía usa onWidgetEvent('highlight', …), la forma corta que nunca se dispara.
|
|
||
| ### Widget-to-widget events | ||
|
|
||
| The sender's id is part of the event name. To hear an event from any widget, subscribe with a wildcard: |
There was a problem hiding this comment.
El dashboard no reenvía estos eventos a los otros widgets. emitWidgetEvent hace window.parent.postMessage, así que el mensaje llega solo al dashboard. En el frontend, cada WidgetEventHandler le escribe únicamente a su propio iframe, y los listeners de message del dashboard solo atienden nombres puntuales (setFullScreen, el rango de fechas, v2:widget:ready:<id>). Nada toma un v2:widget:highlight:<id> y lo manda a los demás iframes, así que este ejemplo no hace nada en un dashboard real y tampoco muestra error.
El asistente del editor tiene una entrada para widget-to-widget en su índice de capacidades, así que va a encontrar esta sección y generar código con ella. Propongo marcarla como todavía no soportada por el dashboard (o sacarla) hasta que el frontend reenvíe estos eventos. Para confirmarlo basta con dos Custom Metric en un mismo dashboard.
|
|
||
| ```js | ||
| { | ||
| start, // ISO 8601 string or undefined |
There was a problem hiding this comment.
En el dashboard, start y end llegan en epoch ms, no en ISO. CustomMetricWidget pasa initialDate/finalDate sin convertirlos, y esas props son PropTypes.number en toda la cadena (Dashboard.jsx, DashboardGrid/Widget.jsx). Quien manda ISO es el preview del editor (previewEmitter.js, con toISOString()), así que esto describe el preview. Sugiero documentar epoch ms; el preview habría que alinearlo aparte. Mientras tanto, new Date(start) funciona con los dos formatos.
| | `onReady(cb)` | `v2:widget:ready:<widgetId>`. | | ||
| | `onError(cb)` | `v2:widget:error:<widgetId>`. | |
There was a problem hiding this comment.
Hoy el dashboard no emite ninguno de los dos, así que estos callbacks nunca se disparan dentro de un dashboard. En el frontend, emitWidgetReady y emitWidgetError no tienen quién los llame, y el ready de la línea 26 va a window.parent (el dashboard lo usa como handshake): no vuelve al iframe. Convendría decirlo aquí y aclarar en la línea 26 a dónde va ese evento.
| ), | ||
| [onData] | ||
| ); | ||
| useEffect(() => onError(() => {/* show a friendly message */}), [onError]); |
There was a problem hiding this comment.
Por lo mismo, este onError nunca corre. Lo sacaría del ejemplo para que nadie (tampoco el asistente) cuente con él para mostrar el estado de error.
| | Field | Description | | ||
| | --- | --- | | ||
| | `onInfo(cb)` | The widget's descriptors (`v2:widget:info:<widgetId>`). Fires on load and whenever the widget's settings or devices change. | | ||
| | `onData(cb)` | The widget's values (`v2:widget:data:<widgetId>`). Fires on load and on every real-time tick. | |
There was a problem hiding this comment.
En los ticks de tiempo real, results trae solo la variable que cambió (y en polling, solo las variables que no son last_value). El ejemplo ya acumula con { ...prev }, que es lo correcto; vale la pena decirlo también en el texto: acumula por variableId, no reemplaces.
| { | ||
| start, // ISO 8601 string or undefined | ||
| end, // ISO 8601 string or undefined | ||
| results: [{ variableId, deviceId, value, timestamp }] // timestamp: epoch ms; value: number or null |
There was a problem hiding this comment.
timestamp también puede venir en null, no solo value: cuando la variable no tiene datos en la ventana, normalizeWidgetData deja los dos en null.
|
|
||
| | Field | Description | | ||
| | --- | --- | | ||
| | `onInfo(cb)` | The widget's descriptors (`v2:widget:info:<widgetId>`). Fires on load and whenever the widget's settings or devices change. | |
There was a problem hiding this comment.
También se vuelve a disparar cuando se recargan los datos del widget (por ejemplo, al cambiar el rango de fechas), porque el payload se arma con esa respuesta. Conviene tratarlo como idempotente.
|
|
||
| ## Description | ||
|
|
||
| Widget events are namespaced per widget: `v2:widget:<event>:<widgetId>`. The widget id comes from the `widgetId` argument, or else from the `widgetId` prop of [`UbidotsProvider`](../UbidotsProvider.md). Without a widget id, `onInfo` / `onData` / `onReady` / `onError` never fire, and `emitWidgetEvent` only logs a warning. |
There was a problem hiding this comment.
En un Custom Metric, el valor está en el ?id= de la URL del iframe (en el preview también, vía un shim). Una línea con new URLSearchParams(location.search).get('id'), o un link a Project setup del Dev Center, ayudaría a evitar el error más común: sin ese id no llega nada.
Pull Request
📝 Description
Adds
docs/hooks/useWidgetEvents.mdand links it fromdocs/README.md.useWidgetEventsis the hook a Custom Metric widget gets its own data from, and until now it existed only insrc/hooks/useWidgetEvents.tswith no docs page.The page covers:
onInfo,onData,onReady,onError,emitWidgetEvent,onWidgetEvent,onAnyWidgetEventandwidgetId.onInfo:templateId,variables[],devices[].onData:start/endas ISO strings, andresults[]withvariableId,deviceId,valueand an epoch-mstimestamp.results[].variableId === variables[].id.UbidotsProvider'swidgetId. Without one, theon*callbacks never fire andemitonly warns.v2:widget:<event>:<widgetId>) and the:*prefix wildcard.Why now: the Ubidots editor assistants are moving from a private RAG to our public docs (web search), and this hook was the main gap (sc-8017, sc-8005). The Dev Center has the matching section: https://dev.ubidots.com/dashboards-and-widgets/custom-metric
🔗 Related Issues
🧪 Type of Change
🧪 Testing
Docs only: no source, test or build changes, so I didn't run
pnpm test,lint,typecheckorbuild. The documented behaviour was checked againstsrc/hooks/useWidgetEvents.tsonmain.pnpm test)pnpm lint)pnpm typecheck)pnpm build)Test Instructions
docs/hooks/useWidgetEvents.mdand check it renders, and that its links (UbidotsProvider, the related hooks, the complete-widget example) resolve.emitWidgetEvent(`v2:widget:${event}:${widgetId}`) and the wildcard branch (key.endsWith(':*')) insrc/hooks/useWidgetEvents.ts.✅ Checklist
Code Quality
Documentation
Dependencies
Breaking Changes
🚀 Deployment Notes
None. Docs only, no version bump.
📋 Additional Notes
v2:widget:<widgetId>:<event>, butemitWidgetEventsendsv2:widget:<event>:<widgetId>. This page documents what the code does. The JSDoc should be fixed in a follow-up.onWidgetEventmatches the full event name.onWidgetEvent('highlight', cb)never fires: subscribe to'v2:widget:highlight:*'or to the sender's full name. The Best Practices section says so, because the earlier internal docs showed the short form.Reviewer Guidelines:
🤖 Generated with Claude Code