Skip to content
This repository was archived by the owner on Aug 23, 2025. It is now read-only.

Commit c3d63d3

Browse files
authored
Move vis modal into sidebar (#869)
* Use components to isolate splitter logic * Use editor store for vis form visible state * Move EditorPaneVis to new component * Move debouned function to util * EditorPaneSchemaSidebar * Add EditorPaneVisProperties * Remove ChartInputsContainer * Display inputs in single column * Fix field inputs * Add warning * cleanup
1 parent dc1f42f commit c3d63d3

11 files changed

Lines changed: 293 additions & 260 deletions

client/src/common/tauChartRef.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import debounce from 'lodash/debounce';
2+
13
const chartRefs: { [key: string]: any } = {};
24

35
export function setFakeChartRef(queryId: string, chart: any) {
@@ -21,3 +23,5 @@ export function resizeChart(queryId: string) {
2123
chart.resize();
2224
}
2325
}
26+
27+
export const debouncedResizeChart = debounce(resizeChart, 700);
Lines changed: 103 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,50 @@
1-
import React, { CSSProperties } from 'react';
1+
import React, { ChangeEvent, CSSProperties } from 'react';
22
import Input from '../common/Input';
33
import Select from '../common/Select';
4+
import { handleChartConfigurationFieldsChange } from '../stores/editor-actions';
5+
import {
6+
useLastStatementId,
7+
useSessionChartFields,
8+
useSessionChartType,
9+
useStatementColumns,
10+
} from '../stores/editor-store';
411
import chartDefinitions from '../utilities/chartDefinitions';
512

6-
function cleanBoolean(value: any) {
13+
function cleanBoolean(value: string | boolean) {
14+
if (typeof value === 'boolean') {
15+
return value;
16+
}
717
if (typeof value === 'string') {
818
if (value.toLowerCase() === 'true') {
9-
value = true;
19+
return true;
1020
} else if (value.toLowerCase() === 'false') {
11-
value = false;
21+
return false;
1222
}
1323
}
14-
return value;
24+
25+
// If unexpected value return false
26+
return false;
1527
}
1628

1729
const inputStyle: CSSProperties = {
1830
marginBottom: 16,
19-
boxSizing: 'border-box',
20-
width: `calc(1/2*100% - 8px)`,
21-
};
22-
23-
type OwnProps = {
24-
chartType?: string;
25-
onChartConfigurationFieldsChange: (...args: any[]) => any;
26-
queryChartConfigurationFields?: any;
27-
queryResult?: any;
31+
width: '100%',
2832
};
2933

30-
type Props = OwnProps & typeof ChartInputs.defaultProps;
34+
function ChartInputs() {
35+
const chartType = useSessionChartType();
36+
const chartFields = useSessionChartFields();
37+
const lastStatementId = useLastStatementId();
38+
const columns = useStatementColumns(lastStatementId);
3139

32-
function ChartInputs({
33-
onChartConfigurationFieldsChange,
34-
queryChartConfigurationFields,
35-
queryResult,
36-
chartType,
37-
}: Props) {
3840
const changeChartConfigurationField = (
39-
chartFieldId: any,
40-
queryResultField: any
41+
chartFieldId: string,
42+
queryResultField: string | boolean | number
4143
) => {
42-
onChartConfigurationFieldsChange(chartFieldId, queryResultField);
44+
handleChartConfigurationFieldsChange(chartFieldId, queryResultField);
4345
};
4446

45-
const renderFormGroup = (inputDefinitionFields: any) => {
46-
let resultColumnNames: string[] = [];
47-
if (queryResult && queryResult.columns) {
48-
resultColumnNames = queryResult.columns.map((c: any) => c.name);
49-
}
50-
51-
return inputDefinitionFields.map((field: any) => {
52-
if (field.inputType === 'field-dropdown') {
53-
const optionNodes = resultColumnNames.map((qrfield) => {
54-
return (
55-
<option key={qrfield} value={qrfield}>
56-
{qrfield}
57-
</option>
58-
);
59-
});
60-
const selectedQueryResultField =
61-
queryChartConfigurationFields[field.fieldId];
62-
if (
63-
selectedQueryResultField &&
64-
resultColumnNames.indexOf(selectedQueryResultField) === -1
65-
) {
66-
optionNodes.push(
67-
<option
68-
key={'selectedQueryResultField'}
69-
value={selectedQueryResultField}
70-
>
71-
{selectedQueryResultField}
72-
</option>
73-
);
74-
}
75-
return (
76-
<div style={inputStyle} key={field.fieldId}>
77-
<label>{field.label}</label>
78-
<Select
79-
className="w-100"
80-
value={selectedQueryResultField}
81-
onChange={(event: any) =>
82-
changeChartConfigurationField(field.fieldId, event.target.value)
83-
}
84-
>
85-
<option value="" />
86-
{optionNodes}
87-
</Select>
88-
</div>
89-
);
90-
} else if (field.inputType === 'checkbox') {
91-
const checked =
92-
cleanBoolean(queryChartConfigurationFields[field.fieldId]) || false;
93-
return (
94-
<div style={inputStyle} key={field.fieldId}>
95-
<input
96-
type="checkbox"
97-
checked={checked}
98-
id={field.fieldId}
99-
name={field.fieldId}
100-
onChange={(e) =>
101-
changeChartConfigurationField(field.fieldId, e.target.checked)
102-
}
103-
/>
104-
<label htmlFor={field.fieldId} style={{ marginLeft: 8 }}>
105-
{field.label}
106-
</label>
107-
</div>
108-
);
109-
} else if (field.inputType === 'textbox') {
110-
const value = queryChartConfigurationFields[field.fieldId] || '';
111-
return (
112-
<div style={inputStyle} key={field.fieldId}>
113-
<label>{field.label}</label>
114-
<Input
115-
value={value}
116-
onChange={(e: any) =>
117-
changeChartConfigurationField(field.fieldId, e.target.value)
118-
}
119-
className="w-100"
120-
/>
121-
</div>
122-
);
123-
} else {
124-
throw Error(`field.inputType ${field.inputType} not supported`);
125-
}
126-
});
127-
};
47+
const columnNames = (columns || []).map((c) => c.name);
12848

12949
const chartDefinition = chartDefinitions.find(
13050
(def) => def.chartType === chartType
@@ -134,6 +54,81 @@ function ChartInputs({
13454
return null;
13555
}
13656

57+
const content = chartDefinition.fields.map((field) => {
58+
if (field.inputType === 'field-dropdown') {
59+
const optionNodes = columnNames.map((qrfield) => {
60+
return (
61+
<option key={qrfield} value={qrfield}>
62+
{qrfield}
63+
</option>
64+
);
65+
});
66+
const selectedQueryResultField = chartFields[field.fieldId];
67+
if (
68+
selectedQueryResultField &&
69+
columnNames.indexOf(selectedQueryResultField) === -1
70+
) {
71+
optionNodes.push(
72+
<option
73+
key={'selectedQueryResultField'}
74+
value={selectedQueryResultField}
75+
>
76+
{selectedQueryResultField}
77+
</option>
78+
);
79+
}
80+
return (
81+
<div style={inputStyle} key={field.fieldId}>
82+
<label>{field.label}</label>
83+
<Select
84+
className="w-100"
85+
value={selectedQueryResultField}
86+
onChange={(event: ChangeEvent<HTMLSelectElement>) =>
87+
changeChartConfigurationField(field.fieldId, event.target.value)
88+
}
89+
>
90+
<option value="" />
91+
{optionNodes}
92+
</Select>
93+
</div>
94+
);
95+
} else if (field.inputType === 'checkbox') {
96+
const checked = cleanBoolean(chartFields[field.fieldId]);
97+
return (
98+
<div style={inputStyle} key={field.fieldId}>
99+
<input
100+
type="checkbox"
101+
checked={checked}
102+
id={field.fieldId}
103+
name={field.fieldId}
104+
onChange={(e) =>
105+
changeChartConfigurationField(field.fieldId, e.target.checked)
106+
}
107+
/>
108+
<label htmlFor={field.fieldId} style={{ marginLeft: 8 }}>
109+
{field.label}
110+
</label>
111+
</div>
112+
);
113+
} else if (field.inputType === 'textbox') {
114+
const value = chartFields[field.fieldId] || '';
115+
return (
116+
<div style={inputStyle} key={field.fieldId}>
117+
<label>{field.label}</label>
118+
<Input
119+
value={value}
120+
onChange={(e: ChangeEvent<HTMLInputElement>) =>
121+
changeChartConfigurationField(field.fieldId, e.target.value)
122+
}
123+
className="w-100"
124+
/>
125+
</div>
126+
);
127+
} else {
128+
throw Error(`field.inputType ${field.inputType} not supported`);
129+
}
130+
});
131+
137132
return (
138133
<div
139134
style={{
@@ -146,14 +141,9 @@ function ChartInputs({
146141
justifyContent: 'space-between',
147142
}}
148143
>
149-
{renderFormGroup(chartDefinition.fields)}
144+
{content}
150145
</div>
151146
);
152147
}
153148

154-
ChartInputs.defaultProps = {
155-
queryChartConfigurationFields: {},
156-
queryResult: {},
157-
};
158-
159149
export default ChartInputs;

client/src/queryEditor/ChartInputsContainer.tsx

Lines changed: 0 additions & 25 deletions
This file was deleted.

client/src/queryEditor/ChartTypeSelect.tsx

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
1-
import React from 'react';
1+
import React, { ChangeEvent } from 'react';
22
import Select from '../common/Select';
33
import { handleChartTypeChange } from '../stores/editor-actions';
44
import { useSessionChartType } from '../stores/editor-store';
55
import chartDefinitions from '../utilities/chartDefinitions';
66

7-
function ChartTypeSelect({ className, style }: any) {
7+
function ChartTypeSelect() {
88
const chartType = useSessionChartType();
99

10-
const chartOptions = chartDefinitions.map((d) => {
11-
return (
12-
<option key={d.chartType} value={d.chartType}>
13-
{d.chartLabel}
14-
</option>
15-
);
16-
});
17-
1810
return (
19-
<Select
20-
className={className}
21-
onChange={(event: any) => handleChartTypeChange(event.target.value)}
22-
style={style}
23-
value={chartType}
24-
>
25-
<option value="">No visualization</option>
26-
{chartOptions}
27-
</Select>
11+
<>
12+
<label>Visualization</label>
13+
<Select
14+
onChange={(event: ChangeEvent<HTMLSelectElement>) =>
15+
handleChartTypeChange(event.target.value)
16+
}
17+
value={chartType}
18+
>
19+
<option value="">No visualization</option>
20+
{chartDefinitions.map((d) => {
21+
return (
22+
<option key={d.chartType} value={d.chartType}>
23+
{d.chartLabel}
24+
</option>
25+
);
26+
})}
27+
</Select>
28+
</>
2829
);
2930
}
3031

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React, { FunctionComponent, ReactElement } from 'react';
2+
import SplitPane from 'react-split-pane';
3+
import { debouncedResizeChart } from '../common/tauChartRef';
4+
import SchemaSidebar from '../schema/SchemaSidebar';
5+
import { useSessionShowSchema } from '../stores/editor-store';
6+
7+
interface EditorPaneSchemaSidebarProps {
8+
queryId: string;
9+
children: ReactElement;
10+
}
11+
12+
const EditorPaneSchemaSidebar: FunctionComponent<EditorPaneSchemaSidebarProps> = ({
13+
children,
14+
queryId,
15+
}: EditorPaneSchemaSidebarProps) => {
16+
const showSchema = useSessionShowSchema();
17+
18+
if (!showSchema) {
19+
return children;
20+
}
21+
22+
return (
23+
<SplitPane
24+
split="vertical"
25+
minSize={150}
26+
defaultSize={260}
27+
maxSize={-100}
28+
onChange={() => debouncedResizeChart(queryId)}
29+
>
30+
<SchemaSidebar />
31+
{children}
32+
</SplitPane>
33+
);
34+
};
35+
36+
export default EditorPaneSchemaSidebar;

0 commit comments

Comments
 (0)