Line data Source code
1 : import * as React from 'react';
2 :
3 : import { useQueryState } from '../../connections/query_executor.js';
4 : import { ButtonSize, ButtonVariant, IconButton } from '../../ui/foundations/button.js';
5 : import { ButtonGroup } from '../../ui/foundations/button_group.js';
6 : import { Overlay, OverlaySize } from '../../ui/foundations/overlay.js';
7 : import { CheckIcon, ScreenFullIcon, ScreenNormalIcon, XIcon } from '../../ui/foundations/symbol_icon.js';
8 : import { ChartBuilderOverlay } from '../chart_builder/chart_builder_overlay.js';
9 : import { ResultTableOverlay } from '../../views/query_result/result_table_overlay.js';
10 : import { ScriptEditor } from '../scripts/script_editor.js';
11 : import type { ScriptData } from '../scripts/session_scripts.js';
12 : import { scriptDisplayName } from '../scripts/script_types.js';
13 : import * as styles from './dashboard.module.css';
14 :
15 : /// The overlay that the board shows over its cards. The board owns it, a card that gets a renderer is rendered anew
16 : /// as a visualized card and would close an overlay of its own.
17 : export type DashboardOverlayState = { scriptKey: number; kind: 'chart' | 'editor' | 'table' } | null;
18 :
19 : interface EditorProps {
20 : sessionId: string;
21 : script: ScriptData;
22 : onAcceptDiff: (scriptKey: number) => void;
23 : onRejectDiff: (scriptKey: number) => void;
24 : onClose: () => void;
25 : }
26 :
27 : /// The editor of a script in an overlay over the board. A staged rewrite shows as a diff in the editor, the header
28 : /// accepts or rejects it. The overlay expands to fill the window like the chart builder.
29 1 : export function DashboardEditorOverlay(props: EditorProps): React.ReactElement {
30 1 : const displayName = scriptDisplayName(props.script.fileName);
31 1 : const ignoreOutsideClick = React.useCallback(() => {}, []);
32 1 : const [expanded, setExpanded] = React.useState(false);
33 1 : return (
34 1 : <Overlay
35 1 : centered
36 1 : width={OverlaySize.XL}
37 1 : height={OverlaySize.L}
38 1 : maxHeight={OverlaySize.L}
39 1 : onEscape={props.onClose}
40 1 : onClickOutside={ignoreOutsideClick}
41 1 : expanded={expanded}
42 : >
43 1 : <section className={styles.editor_dialog} role="dialog" aria-modal="true" aria-label={`Edit ${displayName}`} data-testid="dashboard-editor-overlay">
44 1 : <header className={styles.editor_dialog_header}>
45 1 : <h2 className={styles.card_title}>{displayName}</h2>
46 1 : {props.script.pendingDiff != null && (
47 0 : <ButtonGroup aria-label="Accept or reject the change">
48 0 : <IconButton
49 0 : variant={ButtonVariant.Default}
50 0 : size={ButtonSize.Small}
51 0 : aria-label="Accept change"
52 0 : data-testid="dashboard-editor-accept"
53 0 : onClick={() => props.onAcceptDiff(props.script.scriptKey)}
54 : >
55 0 : <CheckIcon size={14} />
56 0 : </IconButton>
57 0 : <IconButton
58 0 : variant={ButtonVariant.Default}
59 0 : size={ButtonSize.Small}
60 0 : aria-label="Reject change"
61 0 : data-testid="dashboard-editor-reject"
62 0 : onClick={() => props.onRejectDiff(props.script.scriptKey)}
63 : >
64 0 : <XIcon size={14} />
65 0 : </IconButton>
66 0 : </ButtonGroup>
67 : )}
68 1 : <IconButton
69 1 : variant={ButtonVariant.Invisible}
70 1 : size={ButtonSize.Small}
71 1 : aria-label={expanded ? 'Restore size' : 'Expand'}
72 1 : aria-pressed={expanded}
73 1 : data-testid="dashboard-editor-expand"
74 1 : onClick={() => setExpanded(e => !e)}
75 : >
76 1 : {expanded ? <ScreenNormalIcon size={16} /> : <ScreenFullIcon size={16} />}
77 1 : </IconButton>
78 1 : <IconButton variant={ButtonVariant.Invisible} size={ButtonSize.Small} aria-label="Close" onClick={props.onClose}>
79 1 : <XIcon size={16} />
80 1 : </IconButton>
81 1 : </header>
82 1 : <ScriptEditor sessionId={props.sessionId} scriptKey={props.script.scriptKey} className={styles.editor_dialog_editor} />
83 1 : </section>
84 1 : </Overlay>
85 : );
86 1 : }
87 :
88 : interface ChartProps {
89 : sessionId: string;
90 : script: ScriptData;
91 : onChange: (spec: string) => void;
92 : onClose: () => void;
93 : }
94 :
95 : /// The chart builder of a script, the preview shows its latest result
96 0 : export function DashboardChartOverlay(props: ChartProps): React.ReactElement {
97 0 : const queryState = useQueryState(props.sessionId, props.script.latestQueryId);
98 0 : return (
99 0 : <ChartBuilderOverlay
100 0 : scriptName={scriptDisplayName(props.script.fileName)}
101 0 : visualizeQuery={props.script.annotations.visualizeQuery}
102 0 : queryState={queryState}
103 0 : onChange={props.onChange}
104 0 : onClose={props.onClose}
105 0 : />
106 : );
107 0 : }
108 :
109 : interface TableProps {
110 : sessionId: string;
111 : script: ScriptData;
112 : onClose: () => void;
113 : }
114 :
115 : /// The full result table of a script, the card shows the rows that fit into it
116 0 : export function DashboardTableOverlay(props: TableProps): React.ReactElement | null {
117 0 : const queryState = useQueryState(props.sessionId, props.script.latestQueryId);
118 0 : if (queryState == null) return null;
119 0 : return <ResultTableOverlay scriptName={scriptDisplayName(props.script.fileName)} query={queryState} onClose={props.onClose} />;
120 0 : }
|