fix(dataeditor): apply default column type and id to reactive columns

AN
21h ago
+151-11

Summary

DataEditor.create only applies the documented default column type ("str") to literal column lists. Columns passed as a reactive state Var skip that branch and reach Glide DataEditor without a type, producing an invalid TextCell and crashing at render. Untyped reactive columns also reach Glide without an id, so dict-row data hits the same crash.

This applies the defaults at render time for reactive columns: the columns expression is wrapped so each entry is spread over { type: "str", id: (col.title ?? "").toLowerCase() }, so an explicit per-column type or id still wins (same semantics as the literal path via format_data_editor_column). The wrap uses Var._replace, which preserves the original var's _var_data, so state subscriptions and imports are unchanged.

Alternative considered: patching the default branch of formatCell in .templates/web/utils/helpers/dataeditor.js. That would only stop the TextCell crash at render; the python-side wrap normalizes the column definition itself, which keeps the default co-located with the literal path and also fixes dict-row lookup (id). Note the wrap produces a fresh columns array identity on each render (.map over the reactive value); Glide already re-renders on reactive prop changes, so no memoization contract changes.

Fixes #7596

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Testing

  • test_dataeditor_reactive_columns_default_type: reactive Var columns render as (state.columns).map((col) => ({ type: "str", id: (col.title ?? "").toLowerCase(), ...col })) (columns prop looked up by name).
  • test_dataeditor_reactive_columns_preserve_var_data: rx.State-backed test pinning that the wrap preserves _var_data (state dependency tracked).
  • test_dataeditor_reactive_columns_default_id_supports_dict_rows: dict-row data renders without crashing.
  • test_dataeditor_reactive_columns_js_semantics: node-checked JS semantics - default applies, explicit type/id override wins.
  • test_dataeditor_literal_columns_default_type: pins existing literal-path behavior.
  • New tests fail without the fix; pytest tests/units/components/datadisplay/test_dataeditor.py: 8 passed. pyright: 0 errors. ruff check / ruff format --check: clean.
  • Full pytest tests/units: 12746 passed; 249 failures are all in reflex_cli/v2 (deploy/auth/gcp cloud tests needing credentials/network) plus one tty frontend test, none import or touch dataeditor.

cc @masenf