LCOV - code coverage report
Current view: top level - dashql-app/src/session/chart_builder - chart_spec_json_editor.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 97.8 % 46 45
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : import * as themes from '../editor/themes/index.js';
       3              : 
       4              : import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
       5              : import { json } from '@codemirror/lang-json';
       6              : import { bracketMatching, foldGutter, foldKeymap } from '@codemirror/language';
       7              : import { type Extension } from '@codemirror/state';
       8              : import { EditorView, drawSelection, keymap, lineNumbers } from '@codemirror/view';
       9              : 
      10              : import { CodeMirror } from '../editor/codemirror.js';
      11              : import type { ChartSpec } from './chart_spec_paths.js';
      12              : import * as styles from './chart_builder.module.css';
      13              : 
      14              : /// Parse the text of the editor, a spec must be a JSON object
      15            1 : export function parseChartSpec(text: string): { spec: ChartSpec } | { error: string } {
      16            1 :     let value: unknown;
      17            1 :     try {
      18            1 :         value = JSON.parse(text);
      19            1 :     } catch (e: unknown) {
      20            1 :         return { error: e instanceof Error ? e.message : String(e) };
      21            1 :     }
      22            1 :     if (value == null || typeof value !== 'object' || Array.isArray(value)) {
      23            0 :         return { error: 'The Vega-Lite spec must be a JSON object' };
      24              :     }
      25            1 :     return { spec: value as ChartSpec };
      26            1 : }
      27              : 
      28              : interface Props {
      29              :     /// The spec when the editor opens. The editor owns its text afterwards.
      30              :     initialSpec: ChartSpec;
      31              :     /// A valid edit of the spec
      32              :     onChange: (spec: ChartSpec) => void;
      33              : }
      34              : 
      35              : /// The JSON of the chart spec. Every edit that parses to an object updates the chart, others show the parse error.
      36            1 : export function ChartSpecJsonEditor(props: Props): React.ReactElement {
      37            1 :     const [error, setError] = React.useState<string | null>(null);
      38            1 :     const onChangeRef = React.useRef(props.onChange);
      39            1 :     onChangeRef.current = props.onChange;
      40            1 :     const initialDoc = React.useMemo(() => JSON.stringify(props.initialSpec, null, 2), []);
      41              : 
      42            1 :     const extensions = React.useMemo<Extension[]>(() => [
      43            1 :         themes.xcode,
      44            1 :         json(),
      45            1 :         lineNumbers(),
      46            1 :         foldGutter(),
      47            1 :         bracketMatching(),
      48            1 :         drawSelection(),
      49            1 :         history(),
      50            1 :         keymap.of([...defaultKeymap, ...historyKeymap, ...foldKeymap]),
      51            1 :         EditorView.contentAttributes.of({ 'aria-label': 'Vega-Lite spec JSON' }),
      52            1 :         EditorView.updateListener.of(update => {
      53            1 :             if (!update.docChanged) return;
      54            1 :             const parsed = parseChartSpec(update.state.doc.toString());
      55            1 :             if ('error' in parsed) {
      56            1 :                 setError(parsed.error);
      57            1 :             } else {
      58            1 :                 setError(null);
      59            1 :                 onChangeRef.current(parsed.spec);
      60            1 :             }
      61            1 :         }),
      62            1 :     ], []);
      63              : 
      64            1 :     return (
      65            1 :         <div className={styles.json_pane}>
      66            1 :             <div className={styles.json_editor} data-testid="chart-json-editor">
      67            1 :                 <CodeMirror extensions={extensions} initialDoc={initialDoc} />
      68            1 :             </div>
      69            1 :             {error != null && (
      70            1 :                 <div className={styles.json_error} role="alert" data-testid="chart-json-error">{error}</div>
      71              :             )}
      72            1 :         </div>
      73              :     );
      74            1 : }
        

Generated by: LCOV version 2.0-1