This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch move-controls
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/move-controls by this push:
new 980c06e7d7 refactor: Clean up Pie control panel implementation
980c06e7d7 is described below
commit 980c06e7d7edd3455b411d2f08c0f005495c9330
Author: Evan Rusackas <[email protected]>
AuthorDate: Sun Aug 17 10:29:21 2025 -0700
refactor: Clean up Pie control panel implementation
- Remove unnecessary control panel files (controlPanel.tsx,
controlPanelModern.tsx, PieControlPanel.tsx)
- Keep only the simplified working version (PieControlPanelSimple.tsx)
- Fix all linting and type-checking errors
- Remove unused imports and color literals
---
.../src/Pie/PieControlPanel.tsx | 490 ---------------------
.../src/Pie/PieControlPanelSimple.tsx | 474 ++++++--------------
.../plugin-chart-echarts/src/Pie/controlPanel.tsx | 322 --------------
.../src/Pie/controlPanelModern.tsx | 401 -----------------
4 files changed, 126 insertions(+), 1561 deletions(-)
diff --git
a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/PieControlPanel.tsx
b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/PieControlPanel.tsx
deleted file mode 100644
index e072f8d6e2..0000000000
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/PieControlPanel.tsx
+++ /dev/null
@@ -1,490 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { FC } from 'react';
-import { t } from '@superset-ui/core';
-import { Collapse, Row, Col, Typography } from 'antd';
-import {
- ControlPanelConfig,
- D3_FORMAT_OPTIONS,
- D3_NUMBER_FORMAT_DESCRIPTION_VALUES_TEXT,
- D3_TIME_FORMAT_OPTIONS,
- D3_FORMAT_DOCS,
-} from '@superset-ui/chart-controls';
-
-// Import the control map which contains all control components
-import controlMap from 'src/explore/components/controls';
-
-import { DEFAULT_FORM_DATA } from './types';
-
-console.log('PieControlPanel.tsx - Loading file');
-
-const { Panel } = Collapse;
-const { Title } = Typography;
-
-interface PieControlPanelProps {
- onChange: (field: string, value: any) => void;
- value: Record<string, any>;
- datasource: any;
- actions?: any;
- controls?: any;
- form_data?: any;
-}
-
-/**
- * A TRUE React component-based control panel for Pie charts.
- * No legacy controlPanelSections, no controlSetRows, no config objects.
- * Just pure React components with Ant Design layout.
- */
-export const PieControlPanel: FC<PieControlPanelProps> = ({
- onChange,
- value,
- datasource,
- form_data,
- actions,
- controls,
-}) => {
- console.log('PieControlPanel rendering with:', {
- value,
- datasource,
- form_data,
- controls,
- });
-
- // If no valid data yet, show loading state
- if (!datasource || !form_data) {
- return <div>Loading control panel...</div>;
- }
-
- // Get control components from controlMap
- const { DndColumnSelect } = controlMap;
- const { DndMetricSelect } = controlMap;
- const { AdhocFilterControl } = controlMap;
- const { CheckboxControl } = controlMap;
- const { SelectControl } = controlMap;
- const { TextControl } = controlMap;
- const { SliderControl } = controlMap;
- const { ColorSchemeControl } = controlMap;
-
- // Helper to handle control changes using actions if available
- const handleChange = (field: string) => (val: any) => {
- console.log('Control change:', field, val);
- if (actions?.setControlValue) {
- actions.setControlValue(field, val);
- } else if (onChange) {
- onChange(field, val);
- }
- };
-
- // Make sure we have valid values or defaults
- const formValues = form_data || value || {};
-
- return (
- <div
- style={{
- padding: '16px',
- width: '100%',
- background: '#f0f0f0',
- minHeight: '500px',
- }}
- >
- <h2>🎉 Pie Control Panel - Pure React Based! 🎉</h2>
- <p>This is a TRUE React component control panel with:</p>
- <ul>
- <li>✅ No controlPanelSections</li>
- <li>✅ No controlSetRows</li>
- <li>✅ No config objects</li>
- <li>✅ Just pure React + Ant Design</li>
- </ul>
- <Collapse
- defaultActiveKey={['query', 'chart', 'labels', 'pie', 'legend']}
- ghost
- >
- {/* Query Section */}
- <Panel header={<Title level={5}>{t('Query')}</Title>} key="query">
- <Row gutter={[16, 16]}>
- <Col span={24}>
- <div
- style={{
- padding: '10px',
- border: '1px dashed #999',
- borderRadius: '4px',
- }}
- >
- <strong>Group by</strong>
- <p>Current value: {JSON.stringify(formValues.groupby)}</p>
- <button
- onClick={() => handleChange('groupby')(['test_column'])}
- >
- Set test value
- </button>
- </div>
- </Col>
- </Row>
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={24}>
- <div
- style={{
- padding: '10px',
- border: '1px dashed #999',
- borderRadius: '4px',
- }}
- >
- <strong>Metric</strong>
- <p>Current value: {JSON.stringify(formValues.metric)}</p>
- <button onClick={() => handleChange('metric')('COUNT(*)')}>
- Set COUNT(*)
- </button>
- </div>
- </Col>
- </Row>
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={24}>
- <div
- style={{
- padding: '10px',
- border: '1px dashed #999',
- borderRadius: '4px',
- }}
- >
- <strong>Filters</strong>
- <p>Current value:
{JSON.stringify(formValues.adhoc_filters)}</p>
- </div>
- </Col>
- </Row>
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={12}>
- <div
- style={{
- padding: '10px',
- border: '1px dashed #999',
- borderRadius: '4px',
- }}
- >
- <strong>Row limit</strong>
- <input
- type="number"
- value={formValues.row_limit || 100}
- onChange={e =>
- handleChange('row_limit')(parseInt(e.target.value))
- }
- />
- </div>
- </Col>
- <Col span={12}>
- <div
- style={{
- padding: '10px',
- border: '1px dashed #999',
- borderRadius: '4px',
- }}
- >
- <strong>Sort by metric</strong>
- <input
- type="checkbox"
- checked={formValues.sort_by_metric ?? true}
- onChange={e =>
- handleChange('sort_by_metric')(e.target.checked)
- }
- />
- </div>
- </Col>
- </Row>
- </Panel>
-
- {/* Chart Options */}
- <Panel
- header={<Title level={5}>{t('Chart Options')}</Title>}
- key="chart"
- >
- <Row gutter={[16, 16]}>
- <Col span={24}>
- <div
- style={{
- padding: '10px',
- border: '1px dashed #999',
- borderRadius: '4px',
- }}
- >
- <strong>Color scheme</strong>
- <p>
- Current value: {formValues.color_scheme || 'supersetColors'}
- </p>
- </div>
- </Col>
- </Row>
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={12}>
- <TextControl
- label={t('Percentage threshold')}
- onChange={handleChange('show_labels_threshold')}
- value={formValues.show_labels_threshold ?? 5}
- controlId="show_labels_threshold"
- renderTrigger
- />
- </Col>
- <Col span={12}>
- <TextControl
- label={t('Threshold for Other')}
- onChange={handleChange('threshold_for_other')}
- value={formValues.threshold_for_other ?? 0}
- controlId="threshold_for_other"
- renderTrigger
- />
- </Col>
- </Row>
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={12}>
- <SelectControl
- label={t('Rose Type')}
- onChange={handleChange('roseType')}
- value={formValues.roseType || null}
- choices={[
- ['area', t('Area')],
- ['radius', t('Radius')],
- [null, t('None')],
- ]}
- clearable={false}
- />
- </Col>
- </Row>
- </Panel>
-
- {/* Labels Section */}
- <Panel header={<Title level={5}>{t('Labels')}</Title>} key="labels">
- <Row gutter={[16, 16]}>
- <Col span={24}>
- <SelectControl
- label={t('Label Type')}
- onChange={handleChange('label_type')}
- value={formValues.label_type || 'key'}
- choices={[
- ['key', t('Category Name')],
- ['value', t('Value')],
- ['percent', t('Percentage')],
- ['key_value', t('Category and Value')],
- ['key_percent', t('Category and Percentage')],
- ['key_value_percent', t('Category, Value and Percentage')],
- ['value_percent', t('Value and Percentage')],
- ['template', t('Template')],
- ]}
- clearable={false}
- />
- </Col>
- </Row>
-
- {formValues.label_type === 'template' && (
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={24}>
- <TextControl
- label={t('Label Template')}
- onChange={handleChange('label_template')}
- value={formValues.label_template || ''}
- controlId="label_template"
- renderTrigger
- />
- </Col>
- </Row>
- )}
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={12}>
- <SelectControl
- label={t('Number format')}
- onChange={handleChange('number_format')}
- value={formValues.number_format || 'SMART_NUMBER'}
- choices={D3_FORMAT_OPTIONS}
- freeForm
- />
- </Col>
- <Col span={12}>
- <SelectControl
- label={t('Date format')}
- onChange={handleChange('date_format')}
- value={formValues.date_format || 'smart_date'}
- choices={D3_TIME_FORMAT_OPTIONS}
- freeForm
- />
- </Col>
- </Row>
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={8}>
- <CheckboxControl
- label={t('Show Labels')}
- onChange={handleChange('show_labels')}
- value={formValues.show_labels ?? DEFAULT_FORM_DATA.showLabels}
- controlId="show_labels"
- renderTrigger
- />
- </Col>
- <Col span={8}>
- <CheckboxControl
- label={t('Put labels outside')}
- onChange={handleChange('labels_outside')}
- value={
- formValues.labels_outside ?? DEFAULT_FORM_DATA.labelsOutside
- }
- controlId="labels_outside"
- renderTrigger
- />
- </Col>
- <Col span={8}>
- <CheckboxControl
- label={t('Label Line')}
- onChange={handleChange('label_line')}
- value={formValues.label_line ?? DEFAULT_FORM_DATA.labelLine}
- controlId="label_line"
- renderTrigger
- />
- </Col>
- </Row>
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={12}>
- <CheckboxControl
- label={t('Show Total')}
- onChange={handleChange('show_total')}
- value={formValues.show_total ?? false}
- controlId="show_total"
- renderTrigger
- />
- </Col>
- </Row>
- </Panel>
-
- {/* Pie Shape Section */}
- <Panel header={<Title level={5}>{t('Pie shape')}</Title>} key="pie">
- <Row gutter={[16, 16]}>
- <Col span={24}>
- <SliderControl
- label={t('Outer Radius')}
- onChange={handleChange('outerRadius')}
- value={formValues.outerRadius ?? DEFAULT_FORM_DATA.outerRadius}
- />
- </Col>
- </Row>
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={12}>
- <CheckboxControl
- label={t('Donut')}
- onChange={handleChange('donut')}
- value={formValues.donut ?? DEFAULT_FORM_DATA.donut}
- controlId="donut"
- renderTrigger
- />
- </Col>
- </Row>
-
- {formValues.donut && (
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={24}>
- <SliderControl
- label={t('Inner Radius')}
- onChange={handleChange('innerRadius')}
- value={
- formValues.innerRadius ?? DEFAULT_FORM_DATA.innerRadius
- }
- />
- </Col>
- </Row>
- )}
- </Panel>
-
- {/* Legend Section */}
- <Panel header={<Title level={5}>{t('Legend')}</Title>} key="legend">
- <Row gutter={[16, 16]}>
- <Col span={24}>
- <CheckboxControl
- label={t('Show legend')}
- onChange={handleChange('show_legend')}
- value={formValues.show_legend ?? true}
- controlId="show_legend"
- renderTrigger
- />
- </Col>
- </Row>
-
- {formValues.show_legend && (
- <>
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={12}>
- <SelectControl
- label={t('Legend type')}
- onChange={handleChange('legendType')}
- value={formValues.legendType || 'scroll'}
- choices={[
- ['scroll', t('Scroll')],
- ['plain', t('Plain')],
- ]}
- clearable={false}
- />
- </Col>
- <Col span={12}>
- <SelectControl
- label={t('Legend orientation')}
- onChange={handleChange('legendOrientation')}
- value={formValues.legendOrientation || 'top'}
- choices={[
- ['top', t('Top')],
- ['bottom', t('Bottom')],
- ['left', t('Left')],
- ['right', t('Right')],
- ]}
- clearable={false}
- />
- </Col>
- </Row>
-
- <Row gutter={[16, 16]} style={{ marginTop: 16 }}>
- <Col span={24}>
- <TextControl
- label={t('Legend margin')}
- onChange={handleChange('legendMargin')}
- value={formValues.legendMargin || 0}
- controlId="legendMargin"
- renderTrigger
- />
- </Col>
- </Row>
- </>
- )}
- </Panel>
- </Collapse>
- </div>
- );
-};
-
-/**
- * Mark this component as a modern panel so the renderer knows how to handle it
- */
-(PieControlPanel as any).isModernPanel = true;
-
-console.log(
- 'PieControlPanel.tsx - Component defined, isModernPanel:',
- (PieControlPanel as any).isModernPanel,
-);
-
-// Export the component directly as the default export
-export default PieControlPanel;
diff --git
a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/PieControlPanelSimple.tsx
b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/PieControlPanelSimple.tsx
index b45443343a..cbdd3a586a 100644
---
a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/PieControlPanelSimple.tsx
+++
b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/PieControlPanelSimple.tsx
@@ -18,18 +18,11 @@
*/
import { FC } from 'react';
import { t } from '@superset-ui/core';
-// Import the real drag-and-drop controls
-import { ColorSchemeControl as SharedColorSchemeControl } from
'@superset-ui/chart-controls';
import { DndColumnSelect } from
'../../../../src/explore/components/controls/DndColumnSelectControl/DndColumnSelect';
import { DndMetricSelect } from
'../../../../src/explore/components/controls/DndColumnSelectControl/DndMetricSelect';
-import { DndFilterSelect } from
'../../../../src/explore/components/controls/DndColumnSelectControl/DndFilterSelect';
import TextControl from
'../../../../src/explore/components/controls/TextControl';
import CheckboxControl from
'../../../../src/explore/components/controls/CheckboxControl';
-import SliderControl from
'../../../../src/explore/components/controls/SliderControl';
import ControlHeader from '../../../../src/explore/components/ControlHeader';
-import Control from '../../../../src/explore/components/Control';
-
-console.log('PieControlPanelSimple.tsx - Loading file');
interface PieControlPanelProps {
onChange?: (field: string, value: any) => void;
@@ -41,8 +34,7 @@ interface PieControlPanelProps {
}
/**
- * A TRUE React component-based control panel for Pie charts.
- * This uses the real drag-and-drop controls from Superset.
+ * A modern React component-based control panel for Pie charts.
*/
export const PieControlPanel: FC<PieControlPanelProps> = ({
onChange,
@@ -52,16 +44,6 @@ export const PieControlPanel: FC<PieControlPanelProps> = ({
actions,
controls,
}) => {
- console.log('PieControlPanel rendering with:', {
- value,
- datasource,
- form_data,
- controls,
- });
- console.log('Datasource type:', typeof datasource);
- console.log('Datasource columns:', datasource?.columns);
- console.log('Datasource metrics:', datasource?.metrics);
-
// If no valid data yet, show loading state
if (!datasource || !form_data) {
return <div>Loading control panel...</div>;
@@ -81,303 +63,151 @@ export const PieControlPanel: FC<PieControlPanelProps> =
({
metrics: safeMetrics,
};
- console.log('Safe datasource:', safeDataSource);
-
- // Helper to handle control changes using actions if available
- const handleChange =
- (field: string, renderTrigger = false) =>
- (val: any) => {
- console.log(
- 'Control change:',
- field,
- val,
- 'renderTrigger:',
- renderTrigger,
- );
- if (actions?.setControlValue) {
- actions.setControlValue(field, val);
- // If renderTrigger is true and we have chart update capability,
trigger it
- if (renderTrigger && actions?.updateQueryFormData) {
- actions.updateQueryFormData({ [field]: val }, false);
- }
- } else if (onChange) {
- onChange(field, val);
- }
- };
+ // Helper to handle control changes
+ const handleChange = (field: string) => (val: any) => {
+ if (actions?.setControlValue) {
+ actions.setControlValue(field, val);
+ } else if (onChange) {
+ onChange(field, val);
+ }
+ };
- // Make sure we have valid values or defaults
+ // Get form values
const formValues = form_data || value || {};
return (
- <div style={{ padding: '16px', width: '100%' }}>
- <div>
- {/* Query Section */}
- <div style={{ marginBottom: 24 }}>
- <h4 style={{ marginBottom: 16 }}>{t('Query')}</h4>
- <div style={{ marginBottom: 16 }}>
- <div>
- <div style={{ marginBottom: '8px' }}>
- <strong>{t('Group by')}</strong>
- </div>
- {safeColumns.length > 0 ? (
- <DndColumnSelect
- value={formValues.groupby || []}
- onChange={handleChange('groupby')}
- options={safeColumns}
- name="groupby"
- label={t('Group by')}
- multi
- canDelete
- ghostButtonText={t('Add dimension')}
- type="DndColumnSelect"
- actions={actions}
- />
- ) : (
- <div
- style={{
- padding: '10px',
- borderRadius: '4px',
- }}
- >
- {t('No columns available. Please select a dataset first.')}
- </div>
- )}
- </div>
- </div>
-
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <div style={{ marginBottom: '8px' }}>
- <strong>{t('Metric')}</strong>
- </div>
- {safeDataSource && safeDataSource.columns ? (
- <DndMetricSelect
- value={formValues.metric}
- onChange={handleChange('metric')}
- datasource={safeDataSource}
- name="metric"
- label={t('Metric')}
- multi={false}
- savedMetrics={safeMetrics}
- />
- ) : (
- <div
- style={{
- padding: '10px',
- borderRadius: '4px',
- }}
- >
- {t('No metrics available.')}
- </div>
- )}
- </div>
- </div>
-
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <div style={{ marginBottom: '8px' }}>
- <strong>{t('Filters')}</strong>
- </div>
- {safeDataSource && safeColumns.length > 0 ? (
- <DndFilterSelect
- value={formValues.adhoc_filters || []}
- onChange={handleChange('adhoc_filters')}
- datasource={safeDataSource}
- columns={safeColumns}
- formData={formValues}
- name="adhoc_filters"
- savedMetrics={safeMetrics}
- selectedMetrics={[]}
- type="DndFilterSelect"
- actions={actions}
- />
- ) : (
- <div
- style={{
- padding: '10px',
- borderRadius: '4px',
- }}
- >
- {t('No columns available for filtering.')}
- </div>
- )}
- </div>
- </div>
-
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <ControlHeader
- label={t('Row limit')}
- description={t('Limit the number of rows that are returned')}
- hovered
- />
- <TextControl
- value={formValues.row_limit}
- onChange={handleChange('row_limit')}
- isInt
- placeholder="10000"
- controlId="row_limit"
- />
- <div style={{ flex: 1 }}>
- <CheckboxControl
- label={t('Sort by metric')}
- description={t(
- 'Whether to sort results by the selected metric in
descending order',
- )}
- value={formValues.sort_by_metric ?? true}
- onChange={handleChange('sort_by_metric')}
- hovered
- />
+ <div style={{ padding: '16px' }}>
+ {/* Query Section */}
+ <div style={{ marginBottom: 24 }}>
+ <h4>{t('Query')}</h4>
+
+ {/* Group by */}
+ <div style={{ marginBottom: 16 }}>
+ <ControlHeader
+ label={t('Group by')}
+ description={t('Columns to group by')}
+ hovered
+ />
+ {safeColumns.length > 0 ? (
+ <DndColumnSelect
+ value={formValues.groupby || []}
+ onChange={handleChange('groupby')}
+ options={safeColumns}
+ name="groupby"
+ label={t('Group by')}
+ multi
+ canDelete
+ ghostButtonText={t('Add dimension')}
+ type="DndColumnSelect"
+ actions={actions}
+ />
+ ) : (
+ <div style={{ padding: '10px' }}>
+ {t('No columns available. Please select a dataset first.')}
</div>
- </div>
+ )}
</div>
- {/* Chart Options */}
- <div style={{ marginBottom: 24 }}>
- <h4 style={{ marginBottom: 16 }}>{t('Chart Options')}</h4>
- <div style={{ display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- {(() => {
- const colorSchemeControl = SharedColorSchemeControl();
- const { hidden, ...cleanConfig } = colorSchemeControl.config
|| {};
- return (
- <Control
- {...cleanConfig}
- name="color_scheme"
- value={formValues.color_scheme}
- actions={{
- ...actions,
- setControlValue: (field: string, val: any) => {
- handleChange('color_scheme', true)(val);
- },
- }}
- renderTrigger
- label={t('Color Scheme')}
- description={t('Select color scheme for the chart')}
- />
- );
- })()}
- </div>
- </div>
-
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <ControlHeader
- label={t('Outer Radius')}
- description={t('Outer edge of the pie/donut')}
- renderTrigger
- hovered
- />
- <SliderControl
- value={formValues.outerRadius || 70}
- onChange={handleChange('outerRadius', true)}
- name="outerRadius"
- renderTrigger
- {...{ min: 10, max: 100, step: 1 }}
- />
- </div>
- </div>
+ {/* Metric */}
+ <div style={{ marginBottom: 16 }}>
+ <ControlHeader
+ label={t('Metric')}
+ description={t('Metric to display')}
+ hovered
+ />
+ {safeDataSource && safeDataSource.columns ? (
+ <DndMetricSelect
+ value={formValues.metric}
+ onChange={handleChange('metric')}
+ datasource={safeDataSource}
+ name="metric"
+ label={t('Metric')}
+ multi={false}
+ savedMetrics={safeMetrics}
+ />
+ ) : (
+ <div style={{ padding: '10px' }}>{t('No metrics available.')}</div>
+ )}
+ </div>
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <CheckboxControl
- label={t('Donut')}
- description={t('Do you want a donut or a pie?')}
- value={formValues.donut || false}
- onChange={handleChange('donut', true)}
- renderTrigger
- hovered
- />
- </div>
+ {/* Row limit and Sort by metric */}
+ <div style={{ display: 'flex', gap: 16 }}>
+ <div style={{ flex: 1 }}>
+ <ControlHeader
+ label={t('Row limit')}
+ description={t('Limit the number of rows that are returned')}
+ hovered
+ />
+ <TextControl
+ value={formValues.row_limit}
+ onChange={handleChange('row_limit')}
+ isInt
+ placeholder="10000"
+ controlId="row_limit"
+ />
</div>
-
- {formValues.donut && (
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <ControlHeader
- label={t('Inner Radius')}
- description={t('Inner radius of donut hole')}
- renderTrigger
- hovered
- />
- <SliderControl
- value={formValues.innerRadius || 30}
- onChange={handleChange('innerRadius', true)}
- name="innerRadius"
- renderTrigger
- {...{ min: 0, max: 100, step: 1 }}
- />
- )}
-
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <CheckboxControl
- label={t('Show Labels')}
- description={t('Whether to display labels on the pie slices')}
- value={formValues.show_labels ?? true}
- onChange={handleChange('show_labels', true)}
- renderTrigger
- hovered
- />
- </div>
+ <div style={{ flex: 1 }}>
+ <CheckboxControl
+ label={t('Sort by metric')}
+ description={t(
+ 'Whether to sort results by the selected metric in descending
order',
+ )}
+ value={formValues.sort_by_metric ?? true}
+ onChange={handleChange('sort_by_metric')}
+ hovered
+ />
</div>
+ </div>
+ </div>
- {formValues.show_labels && (
- <>
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <CheckboxControl
- label={t('Put Labels Outside')}
- description={t('Put the labels outside of the pie slices')}
- value={formValues.labels_outside ?? true}
- onChange={handleChange('labels_outside', true)}
- renderTrigger
- hovered
- />
+ {/* Chart Options Section */}
+ <div style={{ marginBottom: 24 }}>
+ <h4>{t('Chart Options')}</h4>
+
+ {/* Donut checkbox */}
+ <div style={{ marginBottom: 16 }}>
+ <CheckboxControl
+ label={t('Donut')}
+ description={t('Do you want a donut or a pie?')}
+ value={formValues.donut || false}
+ onChange={handleChange('donut')}
+ renderTrigger
+ hovered
+ />
+ </div>
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <CheckboxControl
- label={t('Label Line')}
- description={t('Draw a line from the label to the slice')}
- value={formValues.label_line ?? false}
- onChange={handleChange('label_line', true)}
- renderTrigger
- hovered
- />
- </>
- )}
+ {/* Show Labels checkbox */}
+ <div style={{ marginBottom: 16 }}>
+ <CheckboxControl
+ label={t('Show Labels')}
+ description={t('Whether to display labels on the pie slices')}
+ value={formValues.show_labels ?? true}
+ onChange={handleChange('show_labels')}
+ renderTrigger
+ hovered
+ />
+ </div>
- <div style={{ marginTop: 16, display: 'flex', gap: 16 }}>
- <div style={{ flex: 1 }}>
- <CheckboxControl
- label={t('Show Legend')}
- description={t('Whether to display a legend for the chart')}
- value={formValues.show_legend ?? true}
- onChange={handleChange('show_legend', true)}
- renderTrigger
- hovered
- />
- </div>
- </div>
+ {/* Show Legend checkbox */}
+ <div style={{ marginBottom: 16 }}>
+ <CheckboxControl
+ label={t('Show Legend')}
+ description={t('Whether to display a legend for the chart')}
+ value={formValues.show_legend ?? true}
+ onChange={handleChange('show_legend')}
+ renderTrigger
+ hovered
+ />
</div>
</div>
</div>
);
};
-/**
- * Mark this component as a modern panel so the renderer knows how to handle it
- */
+// Mark this component as a modern panel
(PieControlPanel as any).isModernPanel = true;
-console.log(
- 'PieControlPanelSimple.tsx - Component defined, isModernPanel:',
- (PieControlPanel as any).isModernPanel,
-);
-
-// For now, we need to provide a minimal config structure to prevent errors
-// This is a temporary bridge until the system fully supports pure React panels
+// Provide a minimal config structure to prevent errors
const config = {
controlPanelSections: [
{
@@ -386,40 +216,24 @@ const config = {
controlSetRows: [[PieControlPanel as any]],
},
],
- // Provide default control overrides to prevent undefined errors
+ // Provide default control overrides
controlOverrides: {
groupby: {
default: [],
label: t('Group by'),
- description: t('Columns to group by'),
},
metric: {
default: null,
label: t('Metric'),
- description: t('Metric to calculate'),
- },
- adhoc_filters: {
- default: [],
- label: t('Filters'),
- description: t('Filters to apply'),
},
row_limit: {
default: 100,
label: t('Row limit'),
- description: t('Number of rows to display'),
},
sort_by_metric: {
default: true,
label: t('Sort by metric'),
- description: t('Sort results by metric value'),
},
- color_scheme: {
- default: 'supersetColors',
- label: t('Color scheme'),
- description: t('Color scheme for the chart'),
- renderTrigger: true,
- },
- // Add more control defaults that Pie chart might expect
donut: {
default: false,
label: t('Donut'),
@@ -430,48 +244,12 @@ const config = {
label: t('Show labels'),
renderTrigger: true,
},
- labels_outside: {
- default: true,
- label: t('Put labels outside'),
- renderTrigger: true,
- },
- label_type: {
- default: 'key',
- label: t('Label type'),
- renderTrigger: true,
- },
- label_line: {
- default: false,
- label: t('Label line'),
- renderTrigger: true,
- },
show_legend: {
default: true,
label: t('Show legend'),
renderTrigger: true,
},
- legendType: {
- default: 'scroll',
- label: t('Legend type'),
- renderTrigger: true,
- },
- legendOrientation: {
- default: 'top',
- label: t('Legend orientation'),
- renderTrigger: true,
- },
- outerRadius: {
- default: 70,
- label: t('Outer radius'),
- renderTrigger: true,
- },
- innerRadius: {
- default: 30,
- label: t('Inner radius'),
- renderTrigger: true,
- },
},
};
-// Export the config with the component embedded
export default config;
diff --git
a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx
b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx
deleted file mode 100644
index af33b34938..0000000000
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanel.tsx
+++ /dev/null
@@ -1,322 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { ensureIsInt, t, validateNonEmpty } from '@superset-ui/core';
-import {
- AdhocFiltersControl,
- ColorSchemeControl,
- ControlPanelConfig,
- ControlPanelsContainerProps,
- ControlSubSectionHeader,
- CurrencyFormatControl,
- D3_FORMAT_DOCS,
- D3_FORMAT_OPTIONS,
- D3_NUMBER_FORMAT_DESCRIPTION_VALUES_TEXT,
- D3_TIME_FORMAT_OPTIONS,
- GroupByControl,
- MetricControl,
- RowLimitControl,
- getStandardizedControls,
- sharedControls,
-} from '@superset-ui/chart-controls';
-import { DEFAULT_FORM_DATA } from './types';
-import { legendSection } from '../controls';
-
-const {
- donut,
- innerRadius,
- labelsOutside,
- labelType,
- labelLine,
- outerRadius,
- numberFormat,
- showLabels,
- roseType,
-} = DEFAULT_FORM_DATA;
-
-const config: ControlPanelConfig = {
- controlPanelSections: [
- {
- label: t('Query'),
- expanded: true,
- controlSetRows: [
- [GroupByControl()],
- [MetricControl()],
- [AdhocFiltersControl()],
- [RowLimitControl()],
- [
- {
- name: 'sort_by_metric',
- config: {
- ...sharedControls.sort_by_metric,
- default: true,
- },
- },
- ],
- ],
- },
- {
- label: t('Chart Options'),
- expanded: true,
- controlSetRows: [
- [ColorSchemeControl()],
- [
- {
- name: 'show_labels_threshold',
- config: {
- type: 'TextControl',
- label: t('Percentage threshold'),
- renderTrigger: true,
- isFloat: true,
- default: 5,
- description: t(
- 'Minimum threshold in percentage points for showing labels.',
- ),
- },
- },
- ],
- [
- {
- name: 'threshold_for_other',
- config: {
- type: 'NumberControl',
- label: t('Threshold for Other'),
- min: 0,
- step: 0.5,
- max: 100,
- default: 0,
- renderTrigger: true,
- description: t(
- 'Values less than this percentage will be grouped into the
Other category.',
- ),
- },
- },
- ],
- [
- {
- name: 'roseType',
- config: {
- type: 'SelectControl',
- label: t('Rose Type'),
- default: roseType,
- renderTrigger: true,
- choices: [
- ['area', t('Area')],
- ['radius', t('Radius')],
- [null, t('None')],
- ],
- description: t('Whether to show as Nightingale chart.'),
- },
- },
- ],
- ...legendSection,
- // eslint-disable-next-line react/jsx-key
- [<ControlSubSectionHeader>{t('Labels')}</ControlSubSectionHeader>],
- [
- {
- name: 'label_type',
- config: {
- type: 'SelectControl',
- label: t('Label Type'),
- default: labelType,
- renderTrigger: true,
- choices: [
- ['key', t('Category Name')],
- ['value', t('Value')],
- ['percent', t('Percentage')],
- ['key_value', t('Category and Value')],
- ['key_percent', t('Category and Percentage')],
- ['key_value_percent', t('Category, Value and Percentage')],
- ['value_percent', t('Value and Percentage')],
- ['template', t('Template')],
- ],
- description: t('What should be shown on the label?'),
- },
- },
- ],
- [
- {
- name: 'label_template',
- config: {
- type: 'TextControl',
- label: t('Label Template'),
- renderTrigger: true,
- description: t(
- 'Format data labels. ' +
- 'Use variables: {name}, {value}, {percent}. ' +
- '\\n represents a new line. ' +
- 'ECharts compatibility:\n' +
- '{a} (series), {b} (name), {c} (value), {d} (percentage)',
- ),
- visibility: ({ controls }: ControlPanelsContainerProps) =>
- controls?.label_type?.value === 'template',
- },
- },
- ],
- [
- {
- name: 'number_format',
- config: {
- type: 'SelectControl',
- freeForm: true,
- label: t('Number format'),
- renderTrigger: true,
- default: numberFormat,
- choices: D3_FORMAT_OPTIONS,
- description: `${D3_FORMAT_DOCS}
${D3_NUMBER_FORMAT_DESCRIPTION_VALUES_TEXT}`,
- tokenSeparators: ['\n', '\t', ';'],
- },
- },
- ],
- [CurrencyFormatControl()],
- [
- {
- name: 'date_format',
- config: {
- type: 'SelectControl',
- freeForm: true,
- label: t('Date format'),
- renderTrigger: true,
- choices: D3_TIME_FORMAT_OPTIONS,
- default: 'smart_date',
- description: D3_FORMAT_DOCS,
- },
- },
- ],
- [
- {
- name: 'show_labels',
- config: {
- type: 'CheckboxControl',
- label: t('Show Labels'),
- renderTrigger: true,
- default: showLabels,
- description: t('Whether to display the labels.'),
- },
- },
- ],
- [
- {
- name: 'labels_outside',
- config: {
- type: 'CheckboxControl',
- label: t('Put labels outside'),
- default: labelsOutside,
- renderTrigger: true,
- description: t('Put the labels outside of the pie?'),
- visibility: ({ controls }: ControlPanelsContainerProps) =>
- Boolean(controls?.show_labels?.value),
- },
- },
- ],
- [
- {
- name: 'label_line',
- config: {
- type: 'CheckboxControl',
- label: t('Label Line'),
- default: labelLine,
- renderTrigger: true,
- description: t(
- 'Draw line from Pie to label when labels outside?',
- ),
- visibility: ({ controls }: ControlPanelsContainerProps) =>
- Boolean(controls?.show_labels?.value),
- },
- },
- ],
- [
- {
- name: 'show_total',
- config: {
- type: 'CheckboxControl',
- label: t('Show Total'),
- default: false,
- renderTrigger: true,
- description: t('Whether to display the aggregate count'),
- },
- },
- ],
- // eslint-disable-next-line react/jsx-key
- [<ControlSubSectionHeader>{t('Pie shape')}</ControlSubSectionHeader>],
- [
- {
- name: 'outerRadius',
- config: {
- type: 'SliderControl',
- label: t('Outer Radius'),
- renderTrigger: true,
- min: 10,
- max: 100,
- step: 1,
- default: outerRadius,
- description: t('Outer edge of Pie chart'),
- },
- },
- ],
- [
- {
- name: 'donut',
- config: {
- type: 'CheckboxControl',
- label: t('Donut'),
- default: donut,
- renderTrigger: true,
- description: t('Do you want a donut or a pie?'),
- },
- },
- ],
- [
- {
- name: 'innerRadius',
- config: {
- type: 'SliderControl',
- label: t('Inner Radius'),
- renderTrigger: true,
- min: 0,
- max: 100,
- step: 1,
- default: innerRadius,
- description: t('Inner radius of donut hole'),
- visibility: ({ controls }: ControlPanelsContainerProps) =>
- Boolean(controls?.donut?.value),
- },
- },
- ],
- ],
- },
- ],
- controlOverrides: {
- series: {
- validators: [validateNonEmpty],
- clearable: false,
- },
- row_limit: {
- default: 100,
- },
- },
- formDataOverrides: formData => ({
- ...formData,
- metric: getStandardizedControls().shiftMetric(),
- groupby: getStandardizedControls().popAllColumns(),
- row_limit:
- ensureIsInt(formData.row_limit, 100) >= 100 ? 100 : formData.row_limit,
- }),
-};
-
-export default config;
diff --git
a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanelModern.tsx
b/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanelModern.tsx
deleted file mode 100644
index 5f48cd1279..0000000000
---
a/superset-frontend/plugins/plugin-chart-echarts/src/Pie/controlPanelModern.tsx
+++ /dev/null
@@ -1,401 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { t } from '@superset-ui/core';
-import {
- ControlPanelConfig,
- D3_FORMAT_DOCS,
- D3_FORMAT_OPTIONS,
- D3_NUMBER_FORMAT_DESCRIPTION_VALUES_TEXT,
- D3_TIME_FORMAT_OPTIONS,
- getStandardizedControls,
- sharedControls,
-} from '@superset-ui/chart-controls';
-import { DEFAULT_FORM_DATA } from './types';
-
-/**
- * Modern Pie Chart Control Panel using the existing control infrastructure
- * This version creates individual control items that work with the current
system
- */
-const config: ControlPanelConfig = {
- controlPanelSections: [
- {
- label: t('Query'),
- expanded: true,
- controlSetRows: [
- [
- {
- name: 'groupby',
- config: {
- ...sharedControls.groupby,
- label: t('Group by'),
- description: t('One or many columns to group by'),
- },
- },
- ],
- [
- {
- name: 'metric',
- config: {
- ...sharedControls.metrics,
- multi: false,
- label: t('Metric'),
- description: t('Metric to display in the pie chart'),
- },
- },
- ],
- [
- {
- name: 'adhoc_filters',
- config: {
- ...sharedControls.adhoc_filters,
- },
- },
- ],
- [
- {
- name: 'row_limit',
- config: {
- ...sharedControls.row_limit,
- default: 100,
- },
- },
- ],
- [
- {
- name: 'sort_by_metric',
- config: {
- type: 'CheckboxControl',
- label: t('Sort by metric'),
- default: true,
- renderTrigger: true,
- description: t('Sort series by metric values'),
- },
- },
- ],
- ],
- },
- {
- label: t('Chart Options'),
- expanded: true,
- controlSetRows: [
- [
- {
- name: 'color_scheme',
- config: {
- ...sharedControls.color_scheme,
- },
- },
- ],
- [
- {
- name: 'show_labels_threshold',
- config: {
- type: 'TextControl',
- label: t('Percentage threshold'),
- renderTrigger: true,
- isFloat: true,
- default: 5,
- description: t(
- 'Minimum threshold in percentage points for showing labels.',
- ),
- },
- },
- {
- name: 'threshold_for_other',
- config: {
- type: 'TextControl',
- label: t('Threshold for Other'),
- renderTrigger: true,
- isFloat: true,
- default: 0,
- description: t(
- 'Values less than this percentage will be grouped into the
Other category.',
- ),
- },
- },
- ],
- [
- {
- name: 'roseType',
- config: {
- type: 'SelectControl',
- label: t('Rose Type'),
- description: t('Whether to show as Nightingale chart.'),
- renderTrigger: true,
- choices: [
- ['area', t('Area')],
- ['radius', t('Radius')],
- [null, t('None')],
- ],
- default: null,
- clearable: false,
- },
- },
- ],
- ],
- },
- {
- label: t('Labels'),
- expanded: true,
- controlSetRows: [
- [
- {
- name: 'label_type',
- config: {
- type: 'SelectControl',
- label: t('Label Type'),
- default: 'key',
- renderTrigger: true,
- choices: [
- ['key', t('Category Name')],
- ['value', t('Value')],
- ['percent', t('Percentage')],
- ['key_value', t('Category and Value')],
- ['key_percent', t('Category and Percentage')],
- ['key_value_percent', t('Category, Value and Percentage')],
- ['value_percent', t('Value and Percentage')],
- ['template', t('Template')],
- ],
- description: t('What should be shown on the label?'),
- clearable: false,
- },
- },
- ],
- [
- {
- name: 'label_template',
- config: {
- type: 'TextControl',
- label: t('Label Template'),
- renderTrigger: true,
- description: t(
- 'Format data labels. Use variables: {name}, {value},
{percent}.',
- ),
- visibility: ({ controls }) =>
- controls?.label_type?.value === 'template',
- },
- },
- ],
- [
- {
- name: 'number_format',
- config: {
- type: 'SelectControl',
- freeForm: true,
- label: t('Number format'),
- renderTrigger: true,
- default: 'SMART_NUMBER',
- choices: D3_FORMAT_OPTIONS,
- description: D3_NUMBER_FORMAT_DESCRIPTION_VALUES_TEXT,
- },
- },
- {
- name: 'date_format',
- config: {
- type: 'SelectControl',
- freeForm: true,
- label: t('Date format'),
- renderTrigger: true,
- choices: D3_TIME_FORMAT_OPTIONS,
- default: 'smart_date',
- description: D3_FORMAT_DOCS,
- },
- },
- ],
- [
- {
- name: 'show_labels',
- config: {
- type: 'CheckboxControl',
- label: t('Show Labels'),
- renderTrigger: true,
- default: DEFAULT_FORM_DATA.showLabels,
- description: t('Whether to display the labels.'),
- },
- },
- {
- name: 'labels_outside',
- config: {
- type: 'CheckboxControl',
- label: t('Put labels outside'),
- renderTrigger: true,
- default: DEFAULT_FORM_DATA.labelsOutside,
- description: t('Put the labels outside of the pie?'),
- },
- },
- {
- name: 'label_line',
- config: {
- type: 'CheckboxControl',
- label: t('Label Line'),
- renderTrigger: true,
- default: DEFAULT_FORM_DATA.labelLine,
- description: t(
- 'Draw line from Pie to label when labels outside?',
- ),
- },
- },
- ],
- [
- {
- name: 'show_total',
- config: {
- type: 'CheckboxControl',
- label: t('Show Total'),
- renderTrigger: true,
- default: false,
- description: t('Whether to display the aggregate count'),
- },
- },
- ],
- ],
- },
- {
- label: t('Pie shape'),
- expanded: true,
- controlSetRows: [
- [
- {
- name: 'outerRadius',
- config: {
- type: 'SliderControl',
- label: t('Outer Radius'),
- renderTrigger: true,
- min: 10,
- max: 100,
- step: 1,
- default: DEFAULT_FORM_DATA.outerRadius,
- description: t('Outer edge of Pie chart'),
- },
- },
- ],
- [
- {
- name: 'donut',
- config: {
- type: 'CheckboxControl',
- label: t('Donut'),
- renderTrigger: true,
- default: DEFAULT_FORM_DATA.donut,
- description: t('Do you want a donut or a pie?'),
- },
- },
- ],
- [
- {
- name: 'innerRadius',
- config: {
- type: 'SliderControl',
- label: t('Inner Radius'),
- renderTrigger: true,
- min: 0,
- max: 100,
- step: 1,
- default: DEFAULT_FORM_DATA.innerRadius,
- description: t('Inner radius of donut hole'),
- visibility: ({ controls }) => Boolean(controls?.donut?.value),
- },
- },
- ],
- ],
- },
- {
- label: t('Legend'),
- expanded: true,
- controlSetRows: [
- [
- {
- name: 'show_legend',
- config: {
- type: 'CheckboxControl',
- label: t('Show legend'),
- renderTrigger: true,
- default: true,
- description: t('Whether to display a legend for the chart'),
- },
- },
- ],
- [
- {
- name: 'legendType',
- config: {
- type: 'SelectControl',
- label: t('Legend type'),
- renderTrigger: true,
- choices: [
- ['scroll', t('Scroll')],
- ['plain', t('Plain')],
- ],
- default: 'scroll',
- clearable: false,
- description: t('Legend type'),
- visibility: ({ controls }) =>
- Boolean(controls?.show_legend?.value),
- },
- },
- {
- name: 'legendOrientation',
- config: {
- type: 'SelectControl',
- label: t('Legend orientation'),
- renderTrigger: true,
- choices: [
- ['top', t('Top')],
- ['bottom', t('Bottom')],
- ['left', t('Left')],
- ['right', t('Right')],
- ],
- default: 'top',
- clearable: false,
- description: t('Legend orientation'),
- visibility: ({ controls }) =>
- Boolean(controls?.show_legend?.value),
- },
- },
- ],
- [
- {
- name: 'legendMargin',
- config: {
- type: 'TextControl',
- label: t('Legend margin'),
- renderTrigger: true,
- isInt: true,
- default: 0,
- description: t(
- 'Additional margin to add between legend and chart',
- ),
- visibility: ({ controls }) =>
- Boolean(controls?.show_legend?.value),
- },
- },
- ],
- ],
- },
- ],
- // Temporarily disable formDataOverrides to debug
- // formDataOverrides: formData => ({
- // ...formData,
- // metric: getStandardizedControls().shiftMetric(),
- // groupby: getStandardizedControls().popAllColumns(),
- // row_limit: formData.row_limit ?? 100,
- // }),
-};
-
-export default config;