mirror of
https://github.com/apache/superset.git
synced 2026-08-12 11:11:01 +00:00
feat: Add all missing controls to Pie chart React control panel
- Added Filters control (adhoc_filters) to Query section - Added Percentage threshold and Threshold for Other controls - Added Rose Type control for Nightingale chart visualization - Added comprehensive Labels section with: - Label Type selector with all 8 options - Conditional Label Template field - Number format, Currency format, and Date format controls - Conditional Put labels outside control - Show Total checkbox - Reorganized controls into logical sections: Query, Chart Options, Labels, and Pie shape - All controls now use proper React components with conditional rendering - Added all missing controlOverrides with proper defaults and renderTrigger flags
This commit is contained in:
@@ -18,12 +18,21 @@
|
||||
*/
|
||||
import { FC } from 'react';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { ColorSchemeControl } from '@superset-ui/chart-controls';
|
||||
import {
|
||||
ColorSchemeControl,
|
||||
D3_FORMAT_OPTIONS,
|
||||
D3_TIME_FORMAT_OPTIONS,
|
||||
D3_FORMAT_DOCS,
|
||||
D3_NUMBER_FORMAT_DESCRIPTION_VALUES_TEXT,
|
||||
} 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 SelectControl from '../../../../src/explore/components/controls/SelectControl';
|
||||
import CurrencyControl from '../../../../src/explore/components/controls/CurrencyControl';
|
||||
import ControlHeader from '../../../../src/explore/components/ControlHeader';
|
||||
import Control from '../../../../src/explore/components/Control';
|
||||
|
||||
@@ -84,7 +93,7 @@ export const PieControlPanel: FC<PieControlPanelProps> = ({
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<h4>{t('Query')}</h4>
|
||||
|
||||
{/* Group by - NO DOUBLE HEADER */}
|
||||
{/* Group by */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<ControlHeader
|
||||
label={t('Group by')}
|
||||
@@ -97,7 +106,7 @@ export const PieControlPanel: FC<PieControlPanelProps> = ({
|
||||
onChange={handleChange('groupby')}
|
||||
options={safeColumns}
|
||||
name="groupby"
|
||||
label="" // Remove the duplicate label
|
||||
label=""
|
||||
multi
|
||||
canDelete
|
||||
ghostButtonText={t('Add dimension')}
|
||||
@@ -111,7 +120,7 @@ export const PieControlPanel: FC<PieControlPanelProps> = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Metric - NO DOUBLE HEADER */}
|
||||
{/* Metric */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<ControlHeader
|
||||
label={t('Metric')}
|
||||
@@ -124,7 +133,7 @@ export const PieControlPanel: FC<PieControlPanelProps> = ({
|
||||
onChange={handleChange('metric')}
|
||||
datasource={safeDataSource}
|
||||
name="metric"
|
||||
label="" // Remove the duplicate label
|
||||
label=""
|
||||
multi={false}
|
||||
savedMetrics={safeMetrics}
|
||||
/>
|
||||
@@ -133,6 +142,33 @@ export const PieControlPanel: FC<PieControlPanelProps> = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<ControlHeader
|
||||
label={t('Filters')}
|
||||
description={t('Filters to apply to the data')}
|
||||
hovered
|
||||
/>
|
||||
{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={formValues.metric ? [formValues.metric] : []}
|
||||
type="DndFilterSelect"
|
||||
actions={actions}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ padding: '10px' }}>
|
||||
{t('No columns available for filtering.')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Row limit and Sort by metric */}
|
||||
<div style={{ display: 'flex', gap: 16 }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
@@ -145,7 +181,7 @@ export const PieControlPanel: FC<PieControlPanelProps> = ({
|
||||
value={formValues.row_limit}
|
||||
onChange={handleChange('row_limit')}
|
||||
isInt
|
||||
placeholder="10000"
|
||||
placeholder="100"
|
||||
controlId="row_limit"
|
||||
/>
|
||||
</div>
|
||||
@@ -189,11 +225,222 @@ export const PieControlPanel: FC<PieControlPanelProps> = ({
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* Percentage threshold */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<ControlHeader
|
||||
label={t('Percentage threshold')}
|
||||
description={t(
|
||||
'Minimum threshold in percentage points for showing labels.',
|
||||
)}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
<TextControl
|
||||
value={formValues.show_labels_threshold ?? 5}
|
||||
onChange={handleChange('show_labels_threshold')}
|
||||
isFloat
|
||||
placeholder="5"
|
||||
controlId="show_labels_threshold"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Threshold for Other */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<ControlHeader
|
||||
label={t('Threshold for Other')}
|
||||
description={t(
|
||||
'Values less than this percentage will be grouped into the Other category.',
|
||||
)}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
<TextControl
|
||||
value={formValues.threshold_for_other ?? 0}
|
||||
onChange={handleChange('threshold_for_other')}
|
||||
isFloat
|
||||
placeholder="0"
|
||||
controlId="threshold_for_other"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Rose Type (Nightingale chart) */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<SelectControl
|
||||
label={t('Rose Type')}
|
||||
description={t('Whether to show as Nightingale chart.')}
|
||||
value={formValues.roseType || null}
|
||||
onChange={handleChange('roseType')}
|
||||
choices={[
|
||||
['area', t('Area')],
|
||||
['radius', t('Radius')],
|
||||
[null, t('None')],
|
||||
]}
|
||||
clearable={false}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
</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>
|
||||
|
||||
{/* Labels Section */}
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<h4>{t('Labels')}</h4>
|
||||
|
||||
{/* Label Type */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<SelectControl
|
||||
label={t('Label Type')}
|
||||
description={t('What should be shown on the label?')}
|
||||
value={formValues.label_type || 'key'}
|
||||
onChange={handleChange('label_type')}
|
||||
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}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Label Template - CONDITIONAL */}
|
||||
{formValues.label_type === 'template' && (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<ControlHeader
|
||||
label={t('Label Template')}
|
||||
description={t(
|
||||
'Format data labels. Use variables: {name}, {value}, {percent}. \\n represents a new line. ECharts compatibility: {a} (series), {b} (name), {c} (value), {d} (percentage)',
|
||||
)}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
<TextControl
|
||||
value={formValues.label_template || ''}
|
||||
onChange={handleChange('label_template')}
|
||||
placeholder="{name}: {value}"
|
||||
controlId="label_template"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Number format */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<SelectControl
|
||||
label={t('Number format')}
|
||||
description={`${D3_FORMAT_DOCS} ${D3_NUMBER_FORMAT_DESCRIPTION_VALUES_TEXT}`}
|
||||
value={formValues.number_format || 'SMART_NUMBER'}
|
||||
onChange={handleChange('number_format')}
|
||||
choices={D3_FORMAT_OPTIONS}
|
||||
freeForm
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Currency format */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<CurrencyControl
|
||||
value={formValues.currency_format}
|
||||
onChange={handleChange('currency_format')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Date format */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<SelectControl
|
||||
label={t('Date format')}
|
||||
description={D3_FORMAT_DOCS}
|
||||
value={formValues.date_format || 'smart_date'}
|
||||
onChange={handleChange('date_format')}
|
||||
choices={D3_TIME_FORMAT_OPTIONS}
|
||||
freeForm
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Show Labels checkbox */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<CheckboxControl
|
||||
label={t('Show Labels')}
|
||||
description={t('Whether to display the labels.')}
|
||||
value={formValues.show_labels ?? true}
|
||||
onChange={handleChange('show_labels')}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Put labels outside - CONDITIONAL */}
|
||||
{formValues.show_labels && (
|
||||
<>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<CheckboxControl
|
||||
label={t('Put labels outside')}
|
||||
description={t('Put the labels outside of the pie?')}
|
||||
value={formValues.labels_outside ?? true}
|
||||
onChange={handleChange('labels_outside')}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Label Line - CONDITIONAL */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<CheckboxControl
|
||||
label={t('Label Line')}
|
||||
description={t(
|
||||
'Draw line from Pie to label when labels outside?',
|
||||
)}
|
||||
value={formValues.label_line ?? false}
|
||||
onChange={handleChange('label_line')}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Show Total */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<CheckboxControl
|
||||
label={t('Show Total')}
|
||||
description={t('Whether to display the aggregate count')}
|
||||
value={formValues.show_total ?? false}
|
||||
onChange={handleChange('show_total')}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pie shape Section */}
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<h4>{t('Pie shape')}</h4>
|
||||
|
||||
{/* Outer Radius Slider */}
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<ControlHeader
|
||||
label={t('Outer Radius')}
|
||||
description={t('Outer edge of the pie/donut')}
|
||||
description={t('Outer edge of Pie chart')}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
@@ -232,44 +479,6 @@ export const PieControlPanel: FC<PieControlPanelProps> = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 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>
|
||||
|
||||
{/* Label Line checkbox - CONDITIONAL */}
|
||||
{formValues.show_labels && (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<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')}
|
||||
renderTrigger
|
||||
hovered
|
||||
/>
|
||||
</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>
|
||||
);
|
||||
@@ -297,6 +506,10 @@ const config = {
|
||||
default: null,
|
||||
label: t('Metric'),
|
||||
},
|
||||
adhoc_filters: {
|
||||
default: [],
|
||||
label: t('Filters'),
|
||||
},
|
||||
row_limit: {
|
||||
default: 100,
|
||||
label: t('Row limit'),
|
||||
@@ -310,6 +523,66 @@ const config = {
|
||||
label: t('Color scheme'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
show_labels_threshold: {
|
||||
default: 5,
|
||||
label: t('Percentage threshold'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
threshold_for_other: {
|
||||
default: 0,
|
||||
label: t('Threshold for Other'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
roseType: {
|
||||
default: null,
|
||||
label: t('Rose Type'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
label_type: {
|
||||
default: 'key',
|
||||
label: t('Label Type'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
label_template: {
|
||||
default: '',
|
||||
label: t('Label Template'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
number_format: {
|
||||
default: 'SMART_NUMBER',
|
||||
label: t('Number format'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
currency_format: {
|
||||
default: undefined,
|
||||
label: t('Currency format'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
date_format: {
|
||||
default: 'smart_date',
|
||||
label: t('Date format'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
show_labels: {
|
||||
default: true,
|
||||
label: t('Show labels'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
labels_outside: {
|
||||
default: true,
|
||||
label: t('Put labels outside'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
label_line: {
|
||||
default: false,
|
||||
label: t('Label Line'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
show_total: {
|
||||
default: false,
|
||||
label: t('Show Total'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
outerRadius: {
|
||||
default: 70,
|
||||
label: t('Outer Radius'),
|
||||
@@ -325,16 +598,6 @@ const config = {
|
||||
label: t('Inner Radius'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
show_labels: {
|
||||
default: true,
|
||||
label: t('Show labels'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
label_line: {
|
||||
default: false,
|
||||
label: t('Label Line'),
|
||||
renderTrigger: true,
|
||||
},
|
||||
show_legend: {
|
||||
default: true,
|
||||
label: t('Show legend'),
|
||||
|
||||
Reference in New Issue
Block a user