Dos and Don’ts

Practical rules for building maintainable SDK widgets.

Schema and Defaults

Do:

  • Keep defaults in schema default fields.

  • Add title and description for every field users can edit.

  • Use level when settings should be role-gated.

Don’t:

  • Duplicate defaults in both schema and runtime constants.

  • Hide side effects (workspace switch or variable update) behind unclear field names.

Custom Inputs

Do:

  • Use INPUT_* shortcuts for Tango/workspace/variable fields.

  • Use normalization helpers (normalizeTangoAttributeInputs, normalizeTangoAttributeRows).

Don’t:

  • Hand-roll endpoint and attribute parsing repeatedly in each widget.

  • Mix incompatible value shapes for the same setting.

Data Flow

Do:

  • Expose explicit useLiveData behavior when both stream and polling are valid.

  • Render clear loading/error/empty states.

  • Use useWidgetRefreshRate(instanceId) for polling cadence.

Don’t:

  • Assume stream payload shape is always complete.

  • Hardcode polling intervals unrelated to dashboard refresh settings.

Tables

Do:

  • Use InteractiveTable and useInteractiveTableState.

  • Persist order/width/sort changes if users expect state to survive reloads.

Don’t:

  • Reimplement column drag/resize/sort mechanics for each widget.

  • Store persisted table state outside widget config without a clear reason.

Host State Integration

Do:

  • Use useVariables, useWorkspace, and useBrush for shared behavior.

  • Document click actions that update workspace/variables.

Don’t:

  • Use custom global state channels for behavior already provided by SDK hooks.

  • Trigger hidden global side effects from purely visual controls.

Plotly Chart Widgets

Do:

  • Use usePlotlyResize for container sizing — it wires a ResizeObserver, rAF, and 48 ms debounce for you.

  • Use useThemeObserver (and readTheme) to read CSS custom properties for colors. Never hardcode hex values.

  • Use canonicalAttr to normalise attribute paths before comparing or displaying them.

  • Use parseMargins, buildAxisTitle, formatLegendName, dedupeLegendLabels from the SDK instead of writing local copies.

  • Use getPlotlyLegendConfig(pos) for the legend anchor config; only add widget-specific margin adjustments locally.

  • Use applyVerticalColorRanking for ranking trace colors by peak value.

Don’t:

  • Inline a ResizeObserver + Plotly.Plots.resize pattern — usePlotlyResize already does this.

  • Inline a MutationObserver on document.body for theme changes — useThemeObserver already does this.

  • Re-implement parseMargins, canonicalAttr, or formatLegendName locally. These functions exist in the SDK and are tested there.

  • Add plotly.js-dist-min to the SDK — the SDK has no Plotly runtime dependency by design; use plain types or generics in any code contributed to the SDK.

Testing

Do:

  • Use @ska-octopus-widget-sdk/widget-sdk/testing for predictable mocks.

  • Test schema/definition shape and critical runtime paths.

Don’t:

  • Depend on network services in unit tests.

  • Skip tests for config normalization and persistence callbacks.