You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2025/08/13 19:52:44 UTC
(superset) 02/06: feat(controls): Migrate control panels to JSON Forms format
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
commit 06b98c109550af7c1921733471b4dd8416cdde9d
Author: Evan Rusackas <ev...@rusackas.com>
AuthorDate: Fri Aug 8 14:48:15 2025 -0700
feat(controls): Migrate control panels to JSON Forms format
Major milestone in modernizing the control panel system:
## Changes Made
### Infrastructure
- Created ControlPanelsContainerWrapper to auto-detect JSON Forms vs legacy format
- Updated ExploreViewContainer to use the wrapper for automatic format detection
- Both legacy and JSON Forms control panels now work seamlessly
### Migrated Control Panels (15 total)
- 10 legacy-plugin-chart-* control panels
- 2 BigNumber ECharts control panels
- 1 Deck.gl Arc layer control panel
- 1 Word Cloud control panel (test case)
- 1 Chord diagram (previously attempted, now proper)
### Migration Pattern Established
- JSON Schema for data structure
- UI Schema for layout with collapsible groups
- Preserved all controlOverrides and formDataOverrides
- Full TypeScript support with proper types
### Benefits
- Industry-standard JSON Forms format
- Better separation of data model and UI
- Cleaner, more maintainable code
- Foundation for future enhancements
Next phase: Migrate remaining 40+ control panels and deprecate legacy format.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <no...@anthropic.com>
---
.../src/controlPanelModern.tsx | 319 +++++++++++++
.../src/controlPanelModern.tsx | 182 ++++++++
.../src/controlPanelModern.tsx | 150 ++++++
.../src/controlPanelModern.tsx | 223 +++++++++
.../src/controlPanelModern.tsx | 415 +++++++++++++++++
.../src/controlPanelModern.tsx | 190 ++++++++
.../src/controlPanelModern.tsx | 181 ++++++++
.../src/controlPanelModern.tsx | 515 +++++++++++++++++++++
.../src/controlPanelModern.tsx | 420 +++++++++++++++++
.../src/controlPanelModern.tsx | 271 +++++++++++
.../src/layers/Arc/controlPanelModern.tsx | 321 +++++++++++++
.../controlPanelModern.tsx | 283 +++++++++++
.../BigNumberTotal/controlPanelModern.tsx | 244 ++++++++++
.../src/plugin/controlPanelModern.tsx | 188 ++++++++
.../plugin-chart-word-cloud/src/plugin/index.ts | 4 +-
.../components/ControlPanelsContainerWrapper.tsx | 52 +++
.../components/ExploreViewContainer/index.jsx | 2 +-
17 files changed, 3958 insertions(+), 2 deletions(-)
diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..c725ecff61
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/controlPanelModern.tsx
@@ -0,0 +1,319 @@
+/**
+ * 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, legacyValidateInteger } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+ D3_FORMAT_DOCS,
+ D3_TIME_FORMAT_OPTIONS,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Time Section
+ granularity_sqla: {
+ type: 'string',
+ title: t('Time Column'),
+ default: '',
+ },
+ time_range: {
+ type: 'string',
+ title: t('Time range'),
+ default: 'No filter',
+ },
+
+ // Query Section
+ domain_granularity: {
+ type: 'string',
+ title: t('Domain'),
+ enum: ['hour', 'day', 'week', 'month', 'year'],
+ default: 'month',
+ },
+ subdomain_granularity: {
+ type: 'string',
+ title: t('Subdomain'),
+ enum: ['min', 'hour', 'day', 'week', 'month'],
+ default: 'day',
+ },
+ metrics: {
+ type: 'array',
+ title: t('Metrics'),
+ items: { type: 'object' },
+ default: [],
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+
+ // Chart Options Section
+ linear_color_scheme: {
+ type: 'string',
+ title: t('Linear color scheme'),
+ default: 'blue_white_yellow',
+ },
+ cell_size: {
+ type: 'integer',
+ title: t('Cell Size'),
+ minimum: 1,
+ default: 10,
+ },
+ cell_padding: {
+ type: 'integer',
+ title: t('Cell Padding'),
+ minimum: 0,
+ default: 2,
+ },
+ cell_radius: {
+ type: 'integer',
+ title: t('Cell Radius'),
+ minimum: 0,
+ default: 0,
+ },
+ steps: {
+ type: 'integer',
+ title: t('Color Steps'),
+ minimum: 1,
+ default: 10,
+ },
+ y_axis_format: {
+ type: 'string',
+ title: t('Number Format'),
+ default: 'SMART_NUMBER',
+ },
+ x_axis_time_format: {
+ type: 'string',
+ title: t('Time Format'),
+ default: 'smart_date',
+ },
+ show_legend: {
+ type: 'boolean',
+ title: t('Legend'),
+ default: true,
+ },
+ show_values: {
+ type: 'boolean',
+ title: t('Show Values'),
+ default: false,
+ },
+ show_metric_name: {
+ type: 'boolean',
+ title: t('Show Metric Names'),
+ default: true,
+ },
+ },
+ required: ['metrics'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Time'), [
+ {
+ type: 'Control',
+ scope: '#/properties/granularity_sqla',
+ options: {
+ controlType: 'GranularitySqlaControl',
+ description: t('Time related form attributes'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/time_range',
+ options: { controlType: 'TimeRangeControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Query'), [
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/domain_granularity',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ ['hour', t('hour')],
+ ['day', t('day')],
+ ['week', t('week')],
+ ['month', t('month')],
+ ['year', t('year')],
+ ],
+ description: t('The time unit used for the grouping of blocks'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/subdomain_granularity',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ ['min', t('min')],
+ ['hour', t('hour')],
+ ['day', t('day')],
+ ['week', t('week')],
+ ['month', t('month')],
+ ],
+ description: t(
+ 'The time unit for each block. Should be a smaller unit than ' +
+ 'domain_granularity. Should be larger or equal to Time Grain',
+ ),
+ },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/metrics',
+ options: { controlType: 'MetricsControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Chart Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/linear_color_scheme',
+ options: { controlType: 'LinearColorSchemeControl' },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/cell_size',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ validators: [legacyValidateInteger],
+ renderTrigger: true,
+ description: t('The size of the square cell, in pixels'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/cell_padding',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ validators: [legacyValidateInteger],
+ renderTrigger: true,
+ description: t('The distance between cells, in pixels'),
+ },
+ },
+ ]),
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/cell_radius',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ validators: [legacyValidateInteger],
+ renderTrigger: true,
+ description: t('The pixel radius'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/steps',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ validators: [legacyValidateInteger],
+ renderTrigger: true,
+ description: t('The number color "steps"'),
+ },
+ },
+ ]),
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/y_axis_format',
+ options: { controlType: 'YAxisFormatControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/x_axis_time_format',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ renderTrigger: true,
+ choices: D3_TIME_FORMAT_OPTIONS,
+ description: D3_FORMAT_DOCS,
+ },
+ },
+ ]),
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/show_legend',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Whether to display the legend (toggles)'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/show_values',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Whether to display the numerical values within the cells'),
+ },
+ },
+ ]),
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/show_metric_name',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Whether to display the metric name as a title'),
+ },
+ },
+ ]),
+ ], true, 'customize'),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ controlOverrides: {
+ y_axis_format: {
+ label: t('Number Format'),
+ },
+ },
+ formDataOverrides: (formData: any) => ({
+ ...formData,
+ metrics: getStandardizedControls().popAllMetrics(),
+ }),
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-plugin-chart-chord/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-chord/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..8577b2875e
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-chord/src/controlPanelModern.tsx
@@ -0,0 +1,182 @@
+/**
+ * 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 { ensureIsArray, t, validateNonEmpty } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ groupby: {
+ type: 'array',
+ title: t('Source'),
+ items: { type: 'string' },
+ maxItems: 1,
+ default: [],
+ },
+ columns: {
+ type: 'array',
+ title: t('Target'),
+ items: { type: 'string' },
+ maxItems: 1,
+ default: [],
+ },
+ metric: {
+ type: 'string',
+ title: t('Metric'),
+ default: '',
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+ sort_by_metric: {
+ type: 'boolean',
+ title: t('Sort by metric'),
+ default: true,
+ },
+
+ // Chart Options Section
+ y_axis_format: {
+ type: 'string',
+ title: t('Number format'),
+ default: 'SMART_NUMBER',
+ },
+ color_scheme: {
+ type: 'string',
+ title: t('Color scheme'),
+ default: 'supersetColors',
+ },
+ },
+ required: ['groupby', 'columns', 'metric'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/groupby',
+ options: {
+ controlType: 'GroupByControl',
+ multi: false,
+ validators: [validateNonEmpty],
+ description: t('Choose a source'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/columns',
+ options: {
+ controlType: 'ColumnsControl',
+ multi: false,
+ validators: [validateNonEmpty],
+ description: t('Choose a target'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/metric',
+ options: { controlType: 'MetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/sort_by_metric',
+ options: { controlType: 'SortByMetricControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Chart Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/y_axis_format',
+ options: {
+ controlType: 'YAxisFormatControl',
+ description: t('Choose a number format'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/color_scheme',
+ options: { controlType: 'ColorSchemeControl' },
+ },
+ ], true, 'customize'),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ controlOverrides: {
+ y_axis_format: {
+ label: t('Number format'),
+ description: t('Choose a number format'),
+ },
+ groupby: {
+ label: t('Source'),
+ multi: false,
+ validators: [validateNonEmpty],
+ description: t('Choose a source'),
+ },
+ columns: {
+ label: t('Target'),
+ multi: false,
+ validators: [validateNonEmpty],
+ description: t('Choose a target'),
+ },
+ },
+ formDataOverrides: (formData: any) => {
+ const groupby = getStandardizedControls()
+ .popAllColumns()
+ .filter(col => !ensureIsArray(formData.columns).includes(col));
+ return {
+ ...formData,
+ groupby,
+ metric: getStandardizedControls().shiftMetric(),
+ };
+ },
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-plugin-chart-country-map/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-country-map/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..7bb9247bf7
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-country-map/src/controlPanelModern.tsx
@@ -0,0 +1,150 @@
+/**
+ * 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, validateNonEmpty } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createCollapsibleGroup,
+ D3_FORMAT_OPTIONS,
+ D3_FORMAT_DOCS,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+import { countryOptions } from './countries';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ select_country: {
+ type: 'string',
+ title: t('Country'),
+ enum: countryOptions.map(option => option[0]),
+ default: null,
+ },
+ entity: {
+ type: 'string',
+ title: t('ISO 3166-2 Codes'),
+ default: '',
+ },
+ metric: {
+ type: 'string',
+ title: t('Metric'),
+ default: '',
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+
+ // Chart Options Section
+ number_format: {
+ type: 'string',
+ title: t('Number format'),
+ default: 'SMART_NUMBER',
+ },
+ linear_color_scheme: {
+ type: 'string',
+ title: t('Linear color scheme'),
+ default: 'blue_white_yellow',
+ },
+ },
+ required: ['select_country', 'entity', 'metric'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/select_country',
+ options: {
+ controlType: 'SelectControl',
+ choices: countryOptions,
+ description: t('Which country to plot the map for?'),
+ validators: [validateNonEmpty],
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/entity',
+ options: { controlType: 'EntityControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/metric',
+ options: { controlType: 'MetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Chart Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/number_format',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ renderTrigger: true,
+ choices: D3_FORMAT_OPTIONS,
+ description: D3_FORMAT_DOCS,
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/linear_color_scheme',
+ options: { controlType: 'LinearColorSchemeControl' },
+ },
+ ], true, 'customize'),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ controlOverrides: {
+ entity: {
+ label: t('ISO 3166-2 Codes'),
+ description: t(
+ 'Column containing ISO 3166-2 codes of region/province/department in your table.',
+ ),
+ },
+ metric: {
+ label: t('Metric'),
+ description: t('Metric to display bottom title'),
+ },
+ linear_color_scheme: {
+ renderTrigger: false,
+ },
+ },
+ formDataOverrides: (formData: any) => ({
+ ...formData,
+ entity: getStandardizedControls().shiftColumn(),
+ metric: getStandardizedControls().shiftMetric(),
+ }),
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-plugin-chart-horizon/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-horizon/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..86fef86be2
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-horizon/src/controlPanelModern.tsx
@@ -0,0 +1,223 @@
+/**
+ * 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 { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+ formatSelectOptions,
+} from '@superset-ui/chart-controls';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Time Section
+ granularity_sqla: {
+ type: 'string',
+ title: t('Time Column'),
+ default: '',
+ },
+ time_range: {
+ type: 'string',
+ title: t('Time range'),
+ default: 'No filter',
+ },
+
+ // Query Section
+ metrics: {
+ type: 'array',
+ title: t('Metrics'),
+ items: { type: 'object' },
+ default: [],
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ groupby: {
+ type: 'array',
+ title: t('Group by'),
+ items: { type: 'string' },
+ default: [],
+ },
+ limit: {
+ type: 'integer',
+ title: t('Series limit'),
+ minimum: 0,
+ default: 25,
+ },
+ timeseries_limit_metric: {
+ type: 'string',
+ title: t('Sort by'),
+ default: '',
+ },
+ order_desc: {
+ type: 'boolean',
+ title: t('Sort descending'),
+ default: true,
+ },
+ contribution: {
+ type: 'boolean',
+ title: t('Contribution'),
+ default: false,
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+
+ // Chart Options Section
+ series_height: {
+ type: 'string',
+ title: t('Series Height'),
+ enum: ['10', '25', '40', '50', '75', '100', '150', '200'],
+ default: '25',
+ },
+ horizon_color_scale: {
+ type: 'string',
+ title: t('Value Domain'),
+ enum: ['series', 'overall', 'change'],
+ default: 'series',
+ },
+ },
+ required: ['metrics'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Time'), [
+ {
+ type: 'Control',
+ scope: '#/properties/granularity_sqla',
+ options: {
+ controlType: 'GranularitySqlaControl',
+ description: t('Time related form attributes'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/time_range',
+ options: { controlType: 'TimeRangeControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/metrics',
+ options: { controlType: 'MetricsControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/groupby',
+ options: { controlType: 'GroupByControl' },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/limit',
+ options: { controlType: 'LimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/timeseries_limit_metric',
+ options: { controlType: 'TimeLimitMetricControl' },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/order_desc',
+ options: { controlType: 'OrderDescControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/contribution',
+ options: {
+ controlType: 'CheckboxControl',
+ description: t('Compute the contribution to the total'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Chart Options'), [
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/series_height',
+ options: {
+ controlType: 'SelectControl',
+ renderTrigger: true,
+ freeForm: true,
+ choices: formatSelectOptions([
+ '10',
+ '25',
+ '40',
+ '50',
+ '75',
+ '100',
+ '150',
+ '200',
+ ]),
+ description: t('Pixel height of each series'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/horizon_color_scale',
+ options: {
+ controlType: 'SelectControl',
+ renderTrigger: true,
+ choices: [
+ ['series', t('series')],
+ ['overall', t('overall')],
+ ['change', t('change')],
+ ],
+ description: t(
+ 'series: Treat each series independently; overall: All series use the same scale; change: Show changes compared to the first data point in each series',
+ ),
+ },
+ },
+ ]),
+ ], true),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..618cd0fc77
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanelModern.tsx
@@ -0,0 +1,415 @@
+/**
+ * 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, validateMapboxStylesUrl } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+ columnChoices,
+ formatSelectOptions,
+ sharedControls,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+
+const colorChoices = [
+ ['rgb(0, 139, 139)', t('Dark Cyan')],
+ ['rgb(128, 0, 128)', t('Purple')],
+ ['rgb(255, 215, 0)', t('Gold')],
+ ['rgb(69, 69, 69)', t('Dim Gray')],
+ ['rgb(220, 20, 60)', t('Crimson')],
+ ['rgb(34, 139, 34)', t('Forest Green')],
+];
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ all_columns_x: {
+ type: 'string',
+ title: t('Longitude'),
+ default: '',
+ },
+ all_columns_y: {
+ type: 'string',
+ title: t('Latitude'),
+ default: '',
+ },
+ clustering_radius: {
+ type: 'string',
+ title: t('Clustering Radius'),
+ enum: ['0', '20', '40', '60', '80', '100', '200', '500', '1000'],
+ default: '60',
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ groupby: {
+ type: 'array',
+ title: t('Group by'),
+ items: { type: 'string' },
+ default: [],
+ },
+
+ // Points Section
+ point_radius: {
+ type: 'string',
+ title: t('Point Radius'),
+ default: 'Auto',
+ },
+ point_radius_unit: {
+ type: 'string',
+ title: t('Point Radius Unit'),
+ enum: ['Pixels', 'Miles', 'Kilometers'],
+ default: 'Pixels',
+ },
+
+ // Labelling Section
+ mapbox_label: {
+ type: 'array',
+ title: t('label'),
+ items: { type: 'string' },
+ default: [],
+ },
+ pandas_aggfunc: {
+ type: 'string',
+ title: t('Cluster label aggregator'),
+ enum: ['sum', 'mean', 'min', 'max', 'std', 'var'],
+ default: 'sum',
+ },
+
+ // Visual Tweaks Section
+ render_while_dragging: {
+ type: 'boolean',
+ title: t('Live render'),
+ default: true,
+ },
+ mapbox_style: {
+ type: 'string',
+ title: t('Map Style'),
+ enum: [
+ 'mapbox://styles/mapbox/streets-v9',
+ 'mapbox://styles/mapbox/dark-v9',
+ 'mapbox://styles/mapbox/light-v9',
+ 'mapbox://styles/mapbox/satellite-streets-v9',
+ 'mapbox://styles/mapbox/satellite-v9',
+ 'mapbox://styles/mapbox/outdoors-v9'
+ ],
+ default: 'mapbox://styles/mapbox/light-v9',
+ },
+ global_opacity: {
+ type: 'number',
+ title: t('Opacity'),
+ minimum: 0,
+ maximum: 1,
+ default: 1,
+ },
+ mapbox_color: {
+ type: 'string',
+ title: t('RGB Color'),
+ enum: colorChoices.map(choice => choice[0]),
+ default: colorChoices[0][0],
+ },
+
+ // Viewport Section
+ viewport_longitude: {
+ type: 'number',
+ title: t('Default longitude'),
+ default: -122.405293,
+ },
+ viewport_latitude: {
+ type: 'number',
+ title: t('Default latitude'),
+ default: 37.772123,
+ },
+ viewport_zoom: {
+ type: 'number',
+ title: t('Zoom'),
+ default: 11,
+ },
+ },
+ required: ['all_columns_x', 'all_columns_y'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/all_columns_x',
+ options: {
+ controlType: 'SelectControl',
+ description: t('Column containing longitude data'),
+ mapStateToProps: (state: any) => ({
+ choices: columnChoices(state.datasource),
+ }),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/all_columns_y',
+ options: {
+ controlType: 'SelectControl',
+ description: t('Column containing latitude data'),
+ mapStateToProps: (state: any) => ({
+ choices: columnChoices(state.datasource),
+ }),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/clustering_radius',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ choices: formatSelectOptions([
+ '0',
+ '20',
+ '40',
+ '60',
+ '80',
+ '100',
+ '200',
+ '500',
+ '1000',
+ ]),
+ description: t(
+ 'The radius (in pixels) the algorithm uses to define a cluster. ' +
+ 'Choose 0 to turn off clustering, but beware that a large ' +
+ 'number of points (>1000) will cause lag.',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/groupby',
+ options: { controlType: 'GroupByControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Points'), [
+ {
+ type: 'Control',
+ scope: '#/properties/point_radius',
+ options: {
+ controlType: 'SelectControl',
+ description: t(
+ 'The radius of individual points (ones that are not in a cluster). ' +
+ 'Either a numerical column or `Auto`, which scales the point based ' +
+ 'on the largest cluster',
+ ),
+ mapStateToProps: (state: any) => {
+ const datasourceChoices = columnChoices(state.datasource);
+ const choices: [string, string][] = [['Auto', t('Auto')]];
+ return {
+ choices: choices.concat(datasourceChoices),
+ };
+ },
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/point_radius_unit',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ ['Pixels', t('Pixels')],
+ ['Miles', t('Miles')],
+ ['Kilometers', t('Kilometers')],
+ ],
+ description: t('The unit of measure for the specified point radius'),
+ },
+ },
+ ]),
+
+ createCollapsibleGroup(t('Labelling'), [
+ {
+ type: 'Control',
+ scope: '#/properties/mapbox_label',
+ options: {
+ controlType: 'SelectControl',
+ multi: true,
+ description: t(
+ '`count` is COUNT(*) if a group by is used. ' +
+ 'Numerical columns will be aggregated with the aggregator. ' +
+ 'Non-numerical columns will be used to label points. ' +
+ 'Leave empty to get a count of points in each cluster.',
+ ),
+ mapStateToProps: (state: any) => ({
+ choices: columnChoices(state.datasource),
+ }),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/pandas_aggfunc',
+ options: {
+ controlType: 'SelectControl',
+ clearable: false,
+ choices: [
+ ['sum', t('sum')],
+ ['mean', t('mean')],
+ ['min', t('min')],
+ ['max', t('max')],
+ ['std', t('std')],
+ ['var', t('var')],
+ ],
+ description: t(
+ 'Aggregate function applied to the list of points ' +
+ 'in each cluster to produce the cluster label.',
+ ),
+ },
+ },
+ ]),
+
+ createCollapsibleGroup(t('Visual Tweaks'), [
+ {
+ type: 'Control',
+ scope: '#/properties/render_while_dragging',
+ options: {
+ controlType: 'CheckboxControl',
+ description: t('Points and clusters will update as the viewport is being changed'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/mapbox_style',
+ options: {
+ controlType: 'SelectControl',
+ clearable: false,
+ renderTrigger: true,
+ freeForm: true,
+ validators: [validateMapboxStylesUrl],
+ choices: [
+ ['mapbox://styles/mapbox/streets-v9', t('Streets')],
+ ['mapbox://styles/mapbox/dark-v9', t('Dark')],
+ ['mapbox://styles/mapbox/light-v9', t('Light')],
+ ['mapbox://styles/mapbox/satellite-streets-v9', t('Satellite Streets')],
+ ['mapbox://styles/mapbox/satellite-v9', t('Satellite')],
+ ['mapbox://styles/mapbox/outdoors-v9', t('Outdoors')],
+ ],
+ description: t(
+ 'Base layer map style. See Mapbox documentation: %s',
+ 'https://docs.mapbox.com/help/glossary/style-url/',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/global_opacity',
+ options: {
+ controlType: 'TextControl',
+ isFloat: true,
+ description: t('Opacity of all clusters, points, and labels. Between 0 and 1.'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/mapbox_color',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ choices: colorChoices,
+ description: t('The color for points and clusters in RGB'),
+ },
+ },
+ ]),
+
+ createCollapsibleGroup(t('Viewport'), [
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/viewport_longitude',
+ options: {
+ controlType: 'TextControl',
+ renderTrigger: true,
+ isFloat: true,
+ description: t('Longitude of default viewport'),
+ places: 8,
+ dontRefreshOnChange: true,
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/viewport_latitude',
+ options: {
+ controlType: 'TextControl',
+ renderTrigger: true,
+ isFloat: true,
+ description: t('Latitude of default viewport'),
+ places: 8,
+ dontRefreshOnChange: true,
+ },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/viewport_zoom',
+ options: {
+ controlType: 'TextControl',
+ renderTrigger: true,
+ isFloat: true,
+ description: t('Zoom level of the map'),
+ places: 8,
+ dontRefreshOnChange: true,
+ },
+ },
+ ], true),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ controlOverrides: {
+ groupby: {
+ description: t(
+ 'One or many controls to group by. If grouping, latitude ' +
+ 'and longitude columns must be present.',
+ ),
+ },
+ },
+ formDataOverrides: (formData: any) => ({
+ ...formData,
+ groupby: getStandardizedControls().popAllColumns(),
+ }),
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..77603ea2c1
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-paired-t-test/src/controlPanelModern.tsx
@@ -0,0 +1,190 @@
+/**
+ * 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, validateNonEmpty } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+} from '@superset-ui/chart-controls';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ metrics: {
+ type: 'array',
+ title: t('Metrics'),
+ items: { type: 'object' },
+ default: [],
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ groupby: {
+ type: 'array',
+ title: t('Group by'),
+ items: { type: 'string' },
+ default: [],
+ },
+ limit: {
+ type: 'integer',
+ title: t('Series limit'),
+ minimum: 0,
+ default: 25,
+ },
+ timeseries_limit_metric: {
+ type: 'string',
+ title: t('Sort by'),
+ default: '',
+ },
+ order_desc: {
+ type: 'boolean',
+ title: t('Sort descending'),
+ default: true,
+ },
+ contribution: {
+ type: 'boolean',
+ title: t('Contribution'),
+ default: false,
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+
+ // Parameters Section
+ significance_level: {
+ type: 'number',
+ title: t('Significance Level'),
+ minimum: 0,
+ maximum: 1,
+ default: 0.05,
+ },
+ pvalue_precision: {
+ type: 'integer',
+ title: t('p-value precision'),
+ minimum: 0,
+ default: 6,
+ },
+ liftvalue_precision: {
+ type: 'integer',
+ title: t('Lift percent precision'),
+ minimum: 0,
+ default: 4,
+ },
+ },
+ required: ['metrics', 'groupby'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/metrics',
+ options: { controlType: 'MetricsControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/groupby',
+ options: {
+ controlType: 'GroupByControl',
+ validators: [validateNonEmpty],
+ },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/limit',
+ options: { controlType: 'LimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/timeseries_limit_metric',
+ options: { controlType: 'TimeLimitMetricControl' },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/order_desc',
+ options: { controlType: 'OrderDescControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/contribution',
+ options: {
+ controlType: 'CheckboxControl',
+ description: t('Compute the contribution to the total'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Parameters'), [
+ {
+ type: 'Control',
+ scope: '#/properties/significance_level',
+ options: {
+ controlType: 'TextControl',
+ description: t('Threshold alpha level for determining significance'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/pvalue_precision',
+ options: {
+ controlType: 'TextControl',
+ description: t('Number of decimal places with which to display p-values'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/liftvalue_precision',
+ options: {
+ controlType: 'TextControl',
+ description: t('Number of decimal places with which to display lift values'),
+ },
+ },
+ ], false),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..a5220c9ed4
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/src/controlPanelModern.tsx
@@ -0,0 +1,181 @@
+/**
+ * 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 { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+} from '@superset-ui/chart-controls';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ series: {
+ type: 'array',
+ title: t('Series'),
+ items: { type: 'string' },
+ default: [],
+ },
+ metrics: {
+ type: 'array',
+ title: t('Metrics'),
+ items: { type: 'object' },
+ default: [],
+ },
+ secondary_metric: {
+ type: 'string',
+ title: t('Color Metric'),
+ default: '',
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ limit: {
+ type: 'integer',
+ title: t('Series limit'),
+ minimum: 0,
+ default: 25,
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+ timeseries_limit_metric: {
+ type: 'string',
+ title: t('Sort by'),
+ default: '',
+ },
+ order_desc: {
+ type: 'boolean',
+ title: t('Sort descending'),
+ default: true,
+ },
+
+ // Options Section
+ show_datatable: {
+ type: 'boolean',
+ title: t('Data Table'),
+ default: false,
+ },
+ include_series: {
+ type: 'boolean',
+ title: t('Include Series'),
+ default: false,
+ },
+ linear_color_scheme: {
+ type: 'string',
+ title: t('Linear color scheme'),
+ default: 'blue_white_yellow',
+ },
+ },
+ required: ['series', 'metrics'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/series',
+ options: { controlType: 'SeriesControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/metrics',
+ options: { controlType: 'MetricsControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/secondary_metric',
+ options: { controlType: 'SecondaryMetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/limit',
+ options: { controlType: 'LimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/timeseries_limit_metric',
+ options: { controlType: 'TimeLimitMetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/order_desc',
+ options: { controlType: 'OrderDescControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Options'), [
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/show_datatable',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Whether to display the interactive data table'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/include_series',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Include series name as an axis'),
+ },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/linear_color_scheme',
+ options: { controlType: 'LinearColorSchemeControl' },
+ },
+ ], true),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-plugin-chart-partition/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-partition/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..9b7e2448c5
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-partition/src/controlPanelModern.tsx
@@ -0,0 +1,515 @@
+/**
+ * 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, validateNonEmpty } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ ColumnMeta,
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+ D3_FORMAT_DOCS,
+ D3_FORMAT_OPTIONS,
+ D3_TIME_FORMAT_OPTIONS,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+import OptionDescription from './OptionDescription';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ metrics: {
+ type: 'array',
+ title: t('Metrics'),
+ items: { type: 'object' },
+ default: [],
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ groupby: {
+ type: 'array',
+ title: t('Group by'),
+ items: { type: 'string' },
+ default: [],
+ },
+ limit: {
+ type: 'integer',
+ title: t('Series limit'),
+ minimum: 0,
+ default: 25,
+ },
+ timeseries_limit_metric: {
+ type: 'string',
+ title: t('Sort by'),
+ default: '',
+ },
+ order_desc: {
+ type: 'boolean',
+ title: t('Sort descending'),
+ default: true,
+ },
+ contribution: {
+ type: 'boolean',
+ title: t('Contribution'),
+ default: false,
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+
+ // Time Series Options Section
+ time_series_option: {
+ type: 'string',
+ title: t('Options'),
+ enum: [
+ 'not_time',
+ 'time_series',
+ 'agg_mean',
+ 'agg_sum',
+ 'point_diff',
+ 'point_percent',
+ 'point_factor',
+ 'adv_anal',
+ ],
+ default: 'not_time',
+ },
+
+ // Chart Options Section
+ color_scheme: {
+ type: 'string',
+ title: t('Color scheme'),
+ default: 'supersetColors',
+ },
+ number_format: {
+ type: 'string',
+ title: t('Number format'),
+ default: 'SMART_NUMBER',
+ },
+ date_time_format: {
+ type: 'string',
+ title: t('Date Time Format'),
+ default: 'smart_date',
+ },
+ partition_limit: {
+ type: 'integer',
+ title: t('Partition Limit'),
+ minimum: 1,
+ default: 5,
+ },
+ partition_threshold: {
+ type: 'number',
+ title: t('Partition Threshold'),
+ minimum: 0,
+ maximum: 1,
+ default: 0.05,
+ },
+ log_scale: {
+ type: 'boolean',
+ title: t('Log Scale'),
+ default: false,
+ },
+ equal_date_size: {
+ type: 'boolean',
+ title: t('Equal Date Sizes'),
+ default: true,
+ },
+ rich_tooltip: {
+ type: 'boolean',
+ title: t('Rich Tooltip'),
+ default: true,
+ },
+
+ // Advanced Analytics Section
+ rolling_type: {
+ type: 'string',
+ title: t('Rolling Function'),
+ enum: ['None', 'mean', 'sum', 'std', 'cumsum'],
+ default: 'None',
+ },
+ rolling_periods: {
+ type: 'integer',
+ title: t('Periods'),
+ minimum: 1,
+ },
+ min_periods: {
+ type: 'integer',
+ title: t('Min Periods'),
+ minimum: 1,
+ },
+ time_compare: {
+ type: 'array',
+ title: t('Time Shift'),
+ items: { type: 'string' },
+ default: [],
+ },
+ comparison_type: {
+ type: 'string',
+ title: t('Calculation type'),
+ enum: ['values', 'absolute', 'percentage', 'ratio'],
+ default: 'values',
+ },
+ resample_rule: {
+ type: 'string',
+ title: t('Rule'),
+ enum: ['1T', '1H', '1D', '7D', '1M', '1AS'],
+ },
+ resample_method: {
+ type: 'string',
+ title: t('Method'),
+ enum: ['asfreq', 'bfill', 'ffill', 'median', 'mean', 'sum'],
+ },
+ },
+ required: ['metrics', 'time_series_option'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/metrics',
+ options: { controlType: 'MetricsControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/groupby',
+ options: { controlType: 'GroupByControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/limit',
+ options: { controlType: 'LimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/timeseries_limit_metric',
+ options: { controlType: 'TimeLimitMetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/order_desc',
+ options: { controlType: 'OrderDescControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/contribution',
+ options: {
+ controlType: 'CheckboxControl',
+ description: t('Compute the contribution to the total'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Time Series Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/time_series_option',
+ options: {
+ controlType: 'SelectControl',
+ validators: [validateNonEmpty],
+ valueKey: 'value',
+ options: [
+ {
+ label: t('Not Time Series'),
+ value: 'not_time',
+ description: t('Ignore time'),
+ },
+ {
+ label: t('Time Series'),
+ value: 'time_series',
+ description: t('Standard time series'),
+ },
+ {
+ label: t('Aggregate Mean'),
+ value: 'agg_mean',
+ description: t('Mean of values over specified period'),
+ },
+ {
+ label: t('Aggregate Sum'),
+ value: 'agg_sum',
+ description: t('Sum of values over specified period'),
+ },
+ {
+ label: t('Difference'),
+ value: 'point_diff',
+ description: t('Metric change in value from `since` to `until`'),
+ },
+ {
+ label: t('Percent Change'),
+ value: 'point_percent',
+ description: t('Metric percent change in value from `since` to `until`'),
+ },
+ {
+ label: t('Factor'),
+ value: 'point_factor',
+ description: t('Metric factor change from `since` to `until`'),
+ },
+ {
+ label: t('Advanced Analytics'),
+ value: 'adv_anal',
+ description: t('Use the Advanced Analytics options below'),
+ },
+ ],
+ optionRenderer: (op: ColumnMeta) => <OptionDescription option={op} />,
+ valueRenderer: (op: ColumnMeta) => <OptionDescription option={op} />,
+ description: t('Settings for time series'),
+ },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Chart Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/color_scheme',
+ options: { controlType: 'ColorSchemeControl' },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/number_format',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ renderTrigger: true,
+ choices: D3_FORMAT_OPTIONS,
+ description: D3_FORMAT_DOCS,
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/date_time_format',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ renderTrigger: true,
+ choices: D3_TIME_FORMAT_OPTIONS,
+ description: D3_FORMAT_DOCS,
+ },
+ },
+ ]),
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/partition_limit',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ description: t(
+ 'The maximum number of subdivisions of each group; lower values are pruned first',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/partition_threshold',
+ options: {
+ controlType: 'TextControl',
+ isFloat: true,
+ description: t(
+ 'Partitions whose height to parent height proportions are below this value are pruned',
+ ),
+ },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/log_scale',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Use a log scale'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/equal_date_size',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Check to force date partitions to have the same height'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/rich_tooltip',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('The rich tooltip shows a list of all series for that point in time'),
+ },
+ },
+ ], true, 'customize'),
+
+ createCollapsibleGroup(t('Advanced Analytics'), [
+ // Rolling Window section
+ {
+ type: 'Control',
+ scope: '#/properties/rolling_type',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ ['None', t('None')],
+ ['mean', t('mean')],
+ ['sum', t('sum')],
+ ['std', t('std')],
+ ['cumsum', t('cumsum')],
+ ],
+ description: t(
+ 'Defines a rolling window function to apply, works along with the [Periods] text box',
+ ),
+ },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/rolling_periods',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ description: t(
+ 'Defines the size of the rolling window function, relative to the time granularity selected',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/min_periods',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ description: t(
+ 'The minimum number of rolling periods required to show a value. For instance if you do a cumulative sum on 7 days you may want your "Min Period" to be 7, so that all data points shown are the total of 7 periods. This will hide the "ramp up" taking place over the first 7 periods',
+ ),
+ },
+ },
+ ]),
+ // Time Comparison section
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/time_compare',
+ options: {
+ controlType: 'SelectControl',
+ multi: true,
+ freeForm: true,
+ choices: [
+ ['1 day', t('1 day')],
+ ['1 week', t('1 week')],
+ ['28 days', t('28 days')],
+ ['30 days', t('30 days')],
+ ['52 weeks', t('52 weeks')],
+ ['1 year', t('1 year')],
+ ['104 weeks', t('104 weeks')],
+ ['2 years', t('2 years')],
+ ['156 weeks', t('156 weeks')],
+ ['3 years', t('3 years')],
+ ],
+ description: t(
+ 'Overlay one or more timeseries from a relative time period. Expects relative time deltas in natural language (example: 24 hours, 7 days, 52 weeks, 365 days). Free text is supported.',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/comparison_type',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ ['values', t('Actual Values')],
+ ['absolute', t('Difference')],
+ ['percentage', t('Percentage change')],
+ ['ratio', t('Ratio')],
+ ],
+ description: t(
+ 'How to display time shifts: as individual lines; as the difference between the main time series and each time shift; as the percentage change; or as the ratio between series and time shifts.',
+ ),
+ },
+ },
+ ]),
+ // Resample section
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/resample_rule',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ choices: [
+ ['1T', t('1T')],
+ ['1H', t('1H')],
+ ['1D', t('1D')],
+ ['7D', t('7D')],
+ ['1M', t('1M')],
+ ['1AS', t('1AS')],
+ ],
+ description: t('Pandas resample rule'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/resample_method',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ choices: [
+ ['asfreq', t('asfreq')],
+ ['bfill', t('bfill')],
+ ['ffill', t('ffill')],
+ ['median', t('median')],
+ ['mean', t('mean')],
+ ['sum', t('sum')],
+ ],
+ description: t('Pandas resample method'),
+ },
+ },
+ ]),
+ ], false, 'data'),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ formDataOverrides: (formData: any) => ({
+ ...formData,
+ groupby: getStandardizedControls().popAllColumns(),
+ metrics: getStandardizedControls().popAllMetrics(),
+ }),
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-plugin-chart-rose/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-rose/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..e46c52ef4c
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-rose/src/controlPanelModern.tsx
@@ -0,0 +1,420 @@
+/**
+ * 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 { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+ D3_FORMAT_DOCS,
+ D3_FORMAT_OPTIONS,
+ D3_TIME_FORMAT_OPTIONS,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Time Section
+ granularity_sqla: {
+ type: 'string',
+ title: t('Time Column'),
+ default: '',
+ },
+ time_range: {
+ type: 'string',
+ title: t('Time range'),
+ default: 'No filter',
+ },
+
+ // Query Section
+ metrics: {
+ type: 'array',
+ title: t('Metrics'),
+ items: { type: 'object' },
+ default: [],
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ groupby: {
+ type: 'array',
+ title: t('Group by'),
+ items: { type: 'string' },
+ default: [],
+ },
+ limit: {
+ type: 'integer',
+ title: t('Series limit'),
+ minimum: 0,
+ default: 25,
+ },
+ timeseries_limit_metric: {
+ type: 'string',
+ title: t('Sort by'),
+ default: '',
+ },
+ order_desc: {
+ type: 'boolean',
+ title: t('Sort descending'),
+ default: true,
+ },
+ contribution: {
+ type: 'boolean',
+ title: t('Contribution'),
+ default: false,
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+
+ // Chart Options Section
+ color_scheme: {
+ type: 'string',
+ title: t('Color scheme'),
+ default: 'supersetColors',
+ },
+ number_format: {
+ type: 'string',
+ title: t('Number format'),
+ default: 'SMART_NUMBER',
+ },
+ date_time_format: {
+ type: 'string',
+ title: t('Date Time Format'),
+ default: 'smart_date',
+ },
+ rich_tooltip: {
+ type: 'boolean',
+ title: t('Rich Tooltip'),
+ default: true,
+ },
+ rose_area_proportion: {
+ type: 'boolean',
+ title: t('Use Area Proportions'),
+ default: false,
+ },
+
+ // Advanced Analytics Section
+ rolling_type: {
+ type: 'string',
+ title: t('Rolling Function'),
+ enum: ['None', 'mean', 'sum', 'std', 'cumsum'],
+ default: 'None',
+ },
+ rolling_periods: {
+ type: 'integer',
+ title: t('Periods'),
+ minimum: 1,
+ },
+ min_periods: {
+ type: 'integer',
+ title: t('Min Periods'),
+ minimum: 1,
+ },
+ time_compare: {
+ type: 'array',
+ title: t('Time Shift'),
+ items: { type: 'string' },
+ default: [],
+ },
+ comparison_type: {
+ type: 'string',
+ title: t('Calculation type'),
+ enum: ['values', 'absolute', 'percentage', 'ratio'],
+ default: 'values',
+ },
+ resample_rule: {
+ type: 'string',
+ title: t('Rule'),
+ enum: ['1T', '1H', '1D', '7D', '1M', '1AS'],
+ },
+ resample_method: {
+ type: 'string',
+ title: t('Method'),
+ enum: ['asfreq', 'bfill', 'ffill', 'median', 'mean', 'sum'],
+ },
+ },
+ required: ['metrics'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ // Legacy Time Section
+ createCollapsibleGroup(t('Time'), [
+ {
+ type: 'Control',
+ scope: '#/properties/granularity_sqla',
+ options: { controlType: 'GranularitySqlaControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/time_range',
+ options: { controlType: 'TimeRangeControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/metrics',
+ options: { controlType: 'MetricsControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/groupby',
+ options: { controlType: 'GroupByControl' },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/limit',
+ options: { controlType: 'LimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/timeseries_limit_metric',
+ options: { controlType: 'TimeLimitMetricControl' },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/order_desc',
+ options: { controlType: 'OrderDescControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/contribution',
+ options: {
+ controlType: 'CheckboxControl',
+ description: t('Compute the contribution to the total'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Chart Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/color_scheme',
+ options: { controlType: 'ColorSchemeControl' },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/number_format',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ renderTrigger: true,
+ choices: D3_FORMAT_OPTIONS,
+ description: D3_FORMAT_DOCS,
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/date_time_format',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ renderTrigger: true,
+ choices: D3_TIME_FORMAT_OPTIONS,
+ description: D3_FORMAT_DOCS,
+ },
+ },
+ ]),
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/rich_tooltip',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('The rich tooltip shows a list of all series for that point in time'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/rose_area_proportion',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t(
+ 'Check if the Rose Chart should use segment area instead of segment radius for proportioning',
+ ),
+ },
+ },
+ ]),
+ ], true),
+
+ createCollapsibleGroup(t('Advanced Analytics'), [
+ // Rolling Window section
+ {
+ type: 'Control',
+ scope: '#/properties/rolling_type',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ ['None', t('None')],
+ ['mean', t('mean')],
+ ['sum', t('sum')],
+ ['std', t('std')],
+ ['cumsum', t('cumsum')],
+ ],
+ description: t(
+ 'Defines a rolling window function to apply, works along with the [Periods] text box',
+ ),
+ },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/rolling_periods',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ description: t(
+ 'Defines the size of the rolling window function, relative to the time granularity selected',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/min_periods',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ description: t(
+ 'The minimum number of rolling periods required to show a value. For instance if you do a cumulative sum on 7 days you may want your "Min Period" to be 7, so that all data points shown are the total of 7 periods. This will hide the "ramp up" taking place over the first 7 periods',
+ ),
+ },
+ },
+ ]),
+ // Time Comparison section
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/time_compare',
+ options: {
+ controlType: 'SelectControl',
+ multi: true,
+ freeForm: true,
+ choices: [
+ ['1 day', t('1 day')],
+ ['1 week', t('1 week')],
+ ['28 days', t('28 days')],
+ ['30 days', t('30 days')],
+ ['52 weeks', t('52 weeks')],
+ ['1 year', t('1 year')],
+ ['104 weeks', t('104 weeks')],
+ ['2 years', t('2 years')],
+ ['156 weeks', t('156 weeks')],
+ ['3 years', t('3 years')],
+ ],
+ description: t(
+ 'Overlay one or more timeseries from a relative time period. Expects relative time deltas in natural language (example: 24 hours, 7 days, 52 weeks, 365 days). Free text is supported.',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/comparison_type',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ ['values', t('Actual Values')],
+ ['absolute', t('Difference')],
+ ['percentage', t('Percentage change')],
+ ['ratio', t('Ratio')],
+ ],
+ description: t(
+ 'How to display time shifts: as individual lines; as the difference between the main time series and each time shift; as the percentage change; or as the ratio between series and time shifts.',
+ ),
+ },
+ },
+ ]),
+ // Resample section
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/resample_rule',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ choices: [
+ ['1T', t('1T')],
+ ['1H', t('1H')],
+ ['1D', t('1D')],
+ ['7D', t('7D')],
+ ['1M', t('1M')],
+ ['1AS', t('1AS')],
+ ],
+ description: t('Pandas resample rule'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/resample_method',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ choices: [
+ ['asfreq', t('asfreq')],
+ ['bfill', t('bfill')],
+ ['ffill', t('ffill')],
+ ['median', t('median')],
+ ['mean', t('mean')],
+ ['sum', t('sum')],
+ ],
+ description: t('Pandas resample method'),
+ },
+ },
+ ]),
+ ], false, 'data'),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ formDataOverrides: (formData: any) => ({
+ ...formData,
+ groupby: getStandardizedControls().popAllColumns(),
+ metrics: getStandardizedControls().popAllMetrics(),
+ }),
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanelModern.tsx b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanelModern.tsx
new file mode 100644
index 0000000000..ea9743658b
--- /dev/null
+++ b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/controlPanelModern.tsx
@@ -0,0 +1,271 @@
+/**
+ * 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 { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createCollapsibleGroup,
+ formatSelectOptions,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+import { ColorBy } from './utils';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ entity: {
+ type: 'string',
+ title: t('Country Column'),
+ default: '',
+ },
+ country_fieldtype: {
+ type: 'string',
+ title: t('Country Field Type'),
+ enum: ['name', 'cioc', 'cca2', 'cca3'],
+ default: 'cca2',
+ },
+ metric: {
+ type: 'string',
+ title: t('Metric'),
+ default: '',
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+ sort_by_metric: {
+ type: 'boolean',
+ title: t('Sort by metric'),
+ default: true,
+ },
+
+ // Options Section
+ show_bubbles: {
+ type: 'boolean',
+ title: t('Show Bubbles'),
+ default: false,
+ },
+ secondary_metric: {
+ type: 'string',
+ title: t('Bubble Size'),
+ default: '',
+ },
+ max_bubble_size: {
+ type: 'string',
+ title: t('Max Bubble Size'),
+ enum: ['5', '10', '15', '25', '50', '75', '100'],
+ default: '25',
+ },
+ color_picker: {
+ type: 'string',
+ title: t('Bubble Color'),
+ default: '#1f77b4',
+ },
+ color_by: {
+ type: 'string',
+ title: t('Color by'),
+ enum: [ColorBy.Metric, ColorBy.Country],
+ default: ColorBy.Metric,
+ },
+ linear_color_scheme: {
+ type: 'string',
+ title: t('Country Color Scheme'),
+ default: 'blue_white_yellow',
+ },
+ color_scheme: {
+ type: 'string',
+ title: t('Country Color Scheme'),
+ default: 'supersetColors',
+ },
+
+ // Chart Options Section
+ y_axis_format: {
+ type: 'string',
+ title: t('Number format'),
+ default: 'SMART_NUMBER',
+ },
+ currency_format: {
+ type: 'object',
+ title: t('Currency format'),
+ },
+ },
+ required: ['entity', 'metric'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/entity',
+ options: { controlType: 'EntityControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/country_fieldtype',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ ['name', t('Full name')],
+ ['cioc', t('code International Olympic Committee (cioc)')],
+ ['cca2', t('code ISO 3166-1 alpha-2 (cca2)')],
+ ['cca3', t('code ISO 3166-1 alpha-3 (cca3)')],
+ ],
+ description: t(
+ 'The country code standard that Superset should expect to find in the [country] column',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/metric',
+ options: { controlType: 'MetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/sort_by_metric',
+ options: { controlType: 'SortByMetricControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/show_bubbles',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Whether to display bubbles on top of countries'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/secondary_metric',
+ options: { controlType: 'SecondaryMetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/max_bubble_size',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ choices: formatSelectOptions(['5', '10', '15', '25', '50', '75', '100']),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/color_picker',
+ options: { controlType: 'ColorPickerControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/color_by',
+ options: {
+ controlType: 'RadioButtonControl',
+ options: [
+ [ColorBy.Metric, t('Metric')],
+ [ColorBy.Country, t('Country')],
+ ],
+ description: t(
+ 'Choose whether a country should be shaded by the metric, or assigned a color based on a categorical color palette',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/linear_color_scheme',
+ options: { controlType: 'LinearColorSchemeControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/color_scheme',
+ options: { controlType: 'ColorSchemeControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Chart Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/y_axis_format',
+ options: { controlType: 'YAxisFormatControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/currency_format',
+ options: { controlType: 'CurrencyFormatControl' },
+ },
+ ], true),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ controlOverrides: {
+ entity: {
+ label: t('Country Column'),
+ description: t('3 letter code of the country'),
+ },
+ secondary_metric: {
+ label: t('Bubble Size'),
+ description: t('Metric that defines the size of the bubble'),
+ },
+ color_picker: {
+ label: t('Bubble Color'),
+ },
+ linear_color_scheme: {
+ label: t('Country Color Scheme'),
+ visibility: ({ controls }: any) =>
+ Boolean(controls?.color_by.value === ColorBy.Metric),
+ },
+ color_scheme: {
+ label: t('Country Color Scheme'),
+ visibility: ({ controls }: any) =>
+ Boolean(controls?.color_by.value === ColorBy.Country),
+ },
+ },
+ formDataOverrides: (formData: any) => ({
+ ...formData,
+ entity: getStandardizedControls().shiftColumn(),
+ metric: getStandardizedControls().shiftMetric(),
+ }),
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/controlPanelModern.tsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/controlPanelModern.tsx
new file mode 100644
index 0000000000..0b9b845d4f
--- /dev/null
+++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Arc/controlPanelModern.tsx
@@ -0,0 +1,321 @@
+/**
+ * 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, validateNonEmpty, legacyValidateInteger } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+ formatSelectOptions,
+} from '@superset-ui/chart-controls';
+import timeGrainSqlaAnimationOverrides, {
+ columnChoices,
+ PRIMARY_COLOR,
+} from '../../utilities/controls';
+import {
+ COLOR_SCHEME_TYPES,
+ isColorSchemeTypeVisible,
+} from '../../utilities/utils';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ start_spatial: {
+ type: 'object',
+ title: t('Start Longitude & Latitude'),
+ },
+ end_spatial: {
+ type: 'object',
+ title: t('End Longitude & Latitude'),
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+ filter_nulls: {
+ type: 'boolean',
+ title: t('Filter Nulls'),
+ default: true,
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+
+ // Map Section
+ mapbox_style: {
+ type: 'string',
+ title: t('Map Style'),
+ default: 'mapbox://styles/mapbox/light-v9',
+ },
+ autozoom: {
+ type: 'boolean',
+ title: t('Auto Zoom'),
+ default: true,
+ },
+ viewport: {
+ type: 'object',
+ title: t('Viewport'),
+ },
+
+ // Arc Section
+ color_scheme_type: {
+ type: 'string',
+ title: t('Color Scheme Type'),
+ enum: [COLOR_SCHEME_TYPES.fixed_color, COLOR_SCHEME_TYPES.categorical_palette],
+ default: COLOR_SCHEME_TYPES.fixed_color,
+ },
+ color_picker: {
+ type: 'string',
+ title: t('Source Color'),
+ default: PRIMARY_COLOR,
+ },
+ target_color_picker: {
+ type: 'string',
+ title: t('Target Color'),
+ default: PRIMARY_COLOR,
+ },
+ categorical_color: {
+ type: 'string',
+ title: t('Categorical Color'),
+ },
+ color_scheme: {
+ type: 'string',
+ title: t('Color scheme'),
+ default: 'supersetColors',
+ },
+ stroke_width: {
+ type: 'string',
+ title: t('Stroke Width'),
+ enum: ['1', '2', '3', '4', '5'],
+ default: null,
+ },
+ legend_position: {
+ type: 'string',
+ title: t('Legend Position'),
+ },
+ legend_format: {
+ type: 'string',
+ title: t('Legend Format'),
+ },
+
+ // Advanced Section
+ js_columns: {
+ type: 'array',
+ title: t('Extra data for JS'),
+ items: { type: 'string' },
+ default: [],
+ },
+ js_data_mutator: {
+ type: 'string',
+ title: t('Javascript data interceptor'),
+ },
+ js_tooltip: {
+ type: 'string',
+ title: t('Javascript tooltip generator'),
+ },
+ js_onclick_href: {
+ type: 'string',
+ title: t('Javascript onClick href'),
+ },
+ },
+ required: ['start_spatial', 'end_spatial'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/start_spatial',
+ options: {
+ controlType: 'SpatialControl',
+ validators: [validateNonEmpty],
+ description: t('Point to your spatial columns'),
+ mapStateToProps: (state: any) => ({
+ choices: columnChoices(state.datasource),
+ }),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/end_spatial',
+ options: {
+ controlType: 'SpatialControl',
+ validators: [validateNonEmpty],
+ description: t('Point to your spatial columns'),
+ mapStateToProps: (state: any) => ({
+ choices: columnChoices(state.datasource),
+ }),
+ },
+ },
+ ]),
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/filter_nulls',
+ options: { controlType: 'CheckboxControl' },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Map'), [
+ {
+ type: 'Control',
+ scope: '#/properties/mapbox_style',
+ options: { controlType: 'SelectControl' },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/autozoom',
+ options: { controlType: 'CheckboxControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/viewport',
+ options: { controlType: 'ViewportControl' },
+ },
+ ]),
+ ]),
+
+ createCollapsibleGroup(t('Arc'), [
+ {
+ type: 'Control',
+ scope: '#/properties/color_scheme_type',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ [COLOR_SCHEME_TYPES.fixed_color, t('Fixed color')],
+ [COLOR_SCHEME_TYPES.categorical_palette, t('Categorical palette')],
+ ],
+ },
+ },
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/color_picker',
+ options: {
+ controlType: 'ColorPickerControl',
+ description: t('Color of the source location'),
+ renderTrigger: true,
+ visibility: ({ controls }: any) =>
+ isColorSchemeTypeVisible(controls, COLOR_SCHEME_TYPES.fixed_color),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/target_color_picker',
+ options: {
+ controlType: 'ColorPickerControl',
+ description: t('Color of the target location'),
+ renderTrigger: true,
+ visibility: ({ controls }: any) =>
+ isColorSchemeTypeVisible(controls, COLOR_SCHEME_TYPES.fixed_color),
+ },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/categorical_color',
+ options: { controlType: 'CategoricalColorControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/color_scheme',
+ options: { controlType: 'ColorSchemeControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/stroke_width',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ validators: [legacyValidateInteger],
+ renderTrigger: true,
+ choices: formatSelectOptions([1, 2, 3, 4, 5]),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/legend_position',
+ options: { controlType: 'LegendPositionControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/legend_format',
+ options: { controlType: 'LegendFormatControl' },
+ },
+ ]),
+
+ createCollapsibleGroup(t('Advanced'), [
+ {
+ type: 'Control',
+ scope: '#/properties/js_columns',
+ options: { controlType: 'SelectControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/js_data_mutator',
+ options: { controlType: 'TextAreaControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/js_tooltip',
+ options: { controlType: 'TextAreaControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/js_onclick_href',
+ options: { controlType: 'TextAreaControl' },
+ },
+ ]),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ controlOverrides: {
+ size: {
+ validators: [],
+ },
+ time_grain_sqla: timeGrainSqlaAnimationOverrides,
+ },
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanelModern.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanelModern.tsx
new file mode 100644
index 0000000000..850d163578
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanelModern.tsx
@@ -0,0 +1,283 @@
+/**
+ * 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, GenericDataType } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createCollapsibleGroup,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+import { noop } from 'lodash';
+import { ColorSchemeEnum } from './types';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ metric: {
+ type: 'string',
+ title: t('Metric'),
+ default: '',
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 10000,
+ },
+
+ // Chart Options Section
+ y_axis_format: {
+ type: 'string',
+ title: t('Number format'),
+ default: 'SMART_NUMBER',
+ },
+ percentDifferenceFormat: {
+ type: 'string',
+ title: t('Percent Difference format'),
+ default: 'SMART_NUMBER',
+ },
+ currency_format: {
+ type: 'object',
+ title: t('Currency format'),
+ },
+ header_font_size: {
+ type: 'string',
+ title: t('Header Font Size'),
+ default: '0.2',
+ },
+ subheader: {
+ type: 'string',
+ title: t('Subheader'),
+ default: '',
+ },
+ subheader_font_size: {
+ type: 'string',
+ title: t('Subheader Font Size'),
+ default: '0.15',
+ },
+ show_metric_name: {
+ type: 'boolean',
+ title: t('Show Metric Names'),
+ default: true,
+ },
+ metric_name_font_size: {
+ type: 'string',
+ title: t('Metric name font size'),
+ default: '0.15',
+ },
+ comparison_font_size: {
+ type: 'string',
+ title: t('Comparison font size'),
+ default: '0.125',
+ },
+ comparison_color_enabled: {
+ type: 'boolean',
+ title: t('Add color for positive/negative change'),
+ default: false,
+ },
+ comparison_color_scheme: {
+ type: 'string',
+ title: t('color scheme for comparison'),
+ enum: [ColorSchemeEnum.Green, ColorSchemeEnum.Red],
+ default: ColorSchemeEnum.Green,
+ },
+ column_config: {
+ type: 'object',
+ title: t('Customize columns'),
+ },
+
+ // Time Comparison Section
+ time_compare: {
+ type: 'array',
+ title: t('Time Shift'),
+ items: { type: 'string' },
+ default: [],
+ },
+ },
+ required: ['metric'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/metric',
+ options: { controlType: 'MetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Chart Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/y_axis_format',
+ options: { controlType: 'YAxisFormatControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/percentDifferenceFormat',
+ options: { controlType: 'YAxisFormatControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/currency_format',
+ options: { controlType: 'CurrencyFormatControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/header_font_size',
+ options: { controlType: 'HeaderFontSizeControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/subheader',
+ options: { controlType: 'SubtitleControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/subheader_font_size',
+ options: { controlType: 'SubtitleFontSizeControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/show_metric_name',
+ options: { controlType: 'ShowMetricNameControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/metric_name_font_size',
+ options: { controlType: 'MetricNameFontSizeControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/comparison_font_size',
+ options: { controlType: 'SubheaderFontSizeControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/comparison_color_enabled',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Add color for positive/negative change'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/comparison_color_scheme',
+ options: {
+ controlType: 'SelectControl',
+ renderTrigger: true,
+ choices: [
+ [ColorSchemeEnum.Green, 'Green for increase, red for decrease'],
+ [ColorSchemeEnum.Red, 'Red for increase, green for decrease'],
+ ],
+ visibility: ({ controls }: any) =>
+ controls?.comparison_color_enabled?.value === true,
+ description: t(
+ 'Adds color to the chart symbols based on the positive or negative change from the comparison value.',
+ ),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/column_config',
+ options: {
+ controlType: 'ColumnConfigControl',
+ description: t('Further customize how to display each column'),
+ width: 400,
+ height: 320,
+ renderTrigger: true,
+ configFormLayout: {
+ [GenericDataType.Numeric]: [
+ {
+ tab: t('General'),
+ children: [['customColumnName'], ['displayTypeIcon'], ['visible']],
+ },
+ ],
+ },
+ shouldMapStateToProps() {
+ return true;
+ },
+ mapStateToProps(explore: any, _: any, chart: any) {
+ noop(explore, _, chart);
+ return {
+ columnsPropsObject: {
+ colnames: ['Previous value', 'Delta', 'Percent change'],
+ coltypes: [
+ GenericDataType.Numeric,
+ GenericDataType.Numeric,
+ GenericDataType.Numeric,
+ ],
+ },
+ };
+ },
+ },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Time Comparison'), [
+ {
+ type: 'Control',
+ scope: '#/properties/time_compare',
+ options: {
+ controlType: 'SelectControl',
+ multi: false,
+ showCalculationType: false,
+ showFullChoices: false,
+ },
+ },
+ ]),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ controlOverrides: {
+ y_axis_format: {
+ label: t('Number format'),
+ },
+ },
+ formDataOverrides: (formData: any) => ({
+ ...formData,
+ metric: getStandardizedControls().shiftMetric(),
+ }),
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanelModern.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanelModern.tsx
new file mode 100644
index 0000000000..17ba9b83ba
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanelModern.tsx
@@ -0,0 +1,244 @@
+/**
+ * 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 { GenericDataType, SMART_DATE_ID, t } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createCollapsibleGroup,
+ D3_FORMAT_DOCS,
+ D3_TIME_FORMAT_OPTIONS,
+ Dataset,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ metric: {
+ type: 'string',
+ title: t('Metric'),
+ default: '',
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ granularity: {
+ type: 'string',
+ title: t('Time Column'),
+ default: '',
+ },
+
+ // Chart Options Section
+ header_font_size: {
+ type: 'string',
+ title: t('Header Font Size'),
+ default: '0.4',
+ },
+ subheader: {
+ type: 'string',
+ title: t('Subheader'),
+ default: '',
+ },
+ subheader_font_size: {
+ type: 'string',
+ title: t('Subheader Font Size'),
+ default: '0.15',
+ },
+ show_metric_name: {
+ type: 'boolean',
+ title: t('Show Metric Names'),
+ default: true,
+ },
+ metric_name_font_size: {
+ type: 'string',
+ title: t('Metric name font size'),
+ default: '0.15',
+ },
+ y_axis_format: {
+ type: 'string',
+ title: t('Number format'),
+ default: 'SMART_NUMBER',
+ },
+ currency_format: {
+ type: 'object',
+ title: t('Currency format'),
+ },
+ time_format: {
+ type: 'string',
+ title: t('Date format'),
+ default: SMART_DATE_ID,
+ },
+ force_timestamp_formatting: {
+ type: 'boolean',
+ title: t('Force date format'),
+ default: false,
+ },
+ conditional_formatting: {
+ type: 'array',
+ title: t('Conditional Formatting'),
+ items: { type: 'object' },
+ default: [],
+ },
+ },
+ required: ['metric'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/metric',
+ options: { controlType: 'MetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/granularity',
+ options: {
+ controlType: 'GranularityControl',
+ description: t('Select the time column for temporal filtering'),
+ clearable: true,
+ temporalColumnsOnly: true,
+ },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Chart Options'), [
+ {
+ type: 'Control',
+ scope: '#/properties/header_font_size',
+ options: { controlType: 'HeaderFontSizeControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/subheader',
+ options: { controlType: 'SubtitleControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/subheader_font_size',
+ options: { controlType: 'SubtitleFontSizeControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/show_metric_name',
+ options: { controlType: 'ShowMetricNameControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/metric_name_font_size',
+ options: { controlType: 'MetricNameFontSizeControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/y_axis_format',
+ options: { controlType: 'YAxisFormatControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/currency_format',
+ options: { controlType: 'CurrencyFormatControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/time_format',
+ options: {
+ controlType: 'SelectControl',
+ freeForm: true,
+ renderTrigger: true,
+ choices: D3_TIME_FORMAT_OPTIONS,
+ description: D3_FORMAT_DOCS,
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/force_timestamp_formatting',
+ options: {
+ controlType: 'CheckboxControl',
+ renderTrigger: true,
+ description: t('Use date formatting even when metric value is not a timestamp'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/conditional_formatting',
+ options: {
+ controlType: 'ConditionalFormattingControl',
+ renderTrigger: true,
+ description: t('Apply conditional color formatting to metric'),
+ shouldMapStateToProps() {
+ return true;
+ },
+ mapStateToProps(explore: any, _: any, chart: any) {
+ const verboseMap = explore?.datasource?.hasOwnProperty('verbose_map')
+ ? (explore?.datasource as Dataset)?.verbose_map
+ : (explore?.datasource?.columns ?? {});
+ const { colnames, coltypes } = chart?.queriesResponse?.[0] ?? {};
+ const numericColumns =
+ Array.isArray(colnames) && Array.isArray(coltypes)
+ ? colnames
+ .filter(
+ (_: string, index: number) =>
+ coltypes[index] === GenericDataType.Numeric,
+ )
+ .map((colname: string | number) => ({
+ value: colname,
+ label:
+ (Array.isArray(verboseMap)
+ ? verboseMap[colname as number]
+ : verboseMap[colname as string]) ?? colname,
+ }))
+ : [];
+ return {
+ columnOptions: numericColumns,
+ verboseMap,
+ };
+ },
+ },
+ },
+ ], true),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ controlOverrides: {
+ y_axis_format: {
+ label: t('Number format'),
+ },
+ },
+ formDataOverrides: (formData: any) => ({
+ ...formData,
+ metric: getStandardizedControls().shiftMetric(),
+ }),
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/plugin-chart-word-cloud/src/plugin/controlPanelModern.tsx b/superset-frontend/plugins/plugin-chart-word-cloud/src/plugin/controlPanelModern.tsx
new file mode 100644
index 0000000000..ba1787dd8f
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-word-cloud/src/plugin/controlPanelModern.tsx
@@ -0,0 +1,188 @@
+/**
+ * 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, validateNonEmpty } from '@superset-ui/core';
+import { JsonSchema, UISchemaElement } from '@jsonforms/core';
+import {
+ JsonFormsControlPanelConfig,
+ createVerticalLayout,
+ createHorizontalLayout,
+ createCollapsibleGroup,
+ createControl,
+ getStandardizedControls,
+} from '@superset-ui/chart-controls';
+
+// JSON Schema - defines the data structure
+const schema: JsonSchema = {
+ type: 'object',
+ properties: {
+ // Query Section
+ series: {
+ type: 'array',
+ title: t('Series'),
+ items: { type: 'string' },
+ default: [],
+ },
+ metric: {
+ type: 'string',
+ title: t('Metric'),
+ default: '',
+ },
+ adhoc_filters: {
+ type: 'array',
+ title: t('Filters'),
+ items: { type: 'object' },
+ default: [],
+ },
+ row_limit: {
+ type: 'integer',
+ title: t('Row limit'),
+ minimum: 0,
+ default: 100,
+ },
+ sort_by_metric: {
+ type: 'boolean',
+ title: t('Sort by metric'),
+ default: true,
+ },
+
+ // Options Section
+ size_from: {
+ type: 'integer',
+ title: t('Minimum Font Size'),
+ minimum: 1,
+ default: 10,
+ },
+ size_to: {
+ type: 'integer',
+ title: t('Maximum Font Size'),
+ minimum: 1,
+ default: 70,
+ },
+ rotation: {
+ type: 'string',
+ title: t('Word Rotation'),
+ enum: ['random', 'flat', 'square'],
+ default: 'square',
+ },
+ color_scheme: {
+ type: 'string',
+ title: t('Color scheme'),
+ default: 'supersetColors',
+ },
+ },
+ required: ['series', 'metric'],
+};
+
+// UI Schema - defines the layout
+const uischema: UISchemaElement = createVerticalLayout([
+ createCollapsibleGroup(t('Query'), [
+ {
+ type: 'Control',
+ scope: '#/properties/series',
+ options: { controlType: 'SeriesControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/metric',
+ options: { controlType: 'MetricControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/adhoc_filters',
+ options: { controlType: 'AdhocFiltersControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/row_limit',
+ options: { controlType: 'RowLimitControl' },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/sort_by_metric',
+ options: { controlType: 'SortByMetricControl' },
+ },
+ ], true),
+
+ createCollapsibleGroup(t('Options'), [
+ createHorizontalLayout([
+ {
+ type: 'Control',
+ scope: '#/properties/size_from',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ renderTrigger: true,
+ description: t('Font size for the smallest value in the list'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/size_to',
+ options: {
+ controlType: 'TextControl',
+ isInt: true,
+ renderTrigger: true,
+ description: t('Font size for the biggest value in the list'),
+ },
+ },
+ ]),
+ {
+ type: 'Control',
+ scope: '#/properties/rotation',
+ options: {
+ controlType: 'SelectControl',
+ choices: [
+ ['random', t('random')],
+ ['flat', t('flat')],
+ ['square', t('square')],
+ ],
+ renderTrigger: true,
+ clearable: false,
+ description: t('Rotation to apply to words in the cloud'),
+ },
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/color_scheme',
+ options: { controlType: 'ColorSchemeControl' },
+ },
+ ], true),
+]);
+
+// Control panel configuration
+const config: JsonFormsControlPanelConfig = {
+ schema,
+ uischema,
+ controlOverrides: {
+ series: {
+ validators: [validateNonEmpty],
+ clearable: false,
+ },
+ row_limit: {
+ default: 100,
+ },
+ },
+ formDataOverrides: (formData: any) => ({
+ ...formData,
+ series: getStandardizedControls().shiftColumn(),
+ metric: getStandardizedControls().shiftMetric(),
+ }),
+};
+
+export default config;
\ No newline at end of file
diff --git a/superset-frontend/plugins/plugin-chart-word-cloud/src/plugin/index.ts b/superset-frontend/plugins/plugin-chart-word-cloud/src/plugin/index.ts
index 371727bc9a..b00a484226 100644
--- a/superset-frontend/plugins/plugin-chart-word-cloud/src/plugin/index.ts
+++ b/superset-frontend/plugins/plugin-chart-word-cloud/src/plugin/index.ts
@@ -24,7 +24,9 @@ import { WordCloudFormData } from '../types';
import thumbnail from '../images/thumbnail.png';
import example1 from '../images/Word_Cloud.jpg';
import example2 from '../images/Word_Cloud_2.jpg';
-import controlPanel from './controlPanel';
+// Temporarily use modern control panel for testing
+// TODO: Remove old control panel once migration is complete
+import controlPanel from './controlPanelModern';
import configureEncodable from '../configureEncodable';
configureEncodable();
diff --git a/superset-frontend/src/explore/components/ControlPanelsContainerWrapper.tsx b/superset-frontend/src/explore/components/ControlPanelsContainerWrapper.tsx
new file mode 100644
index 0000000000..d859631497
--- /dev/null
+++ b/superset-frontend/src/explore/components/ControlPanelsContainerWrapper.tsx
@@ -0,0 +1,52 @@
+/**
+ * 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 { useMemo } from 'react';
+import { getChartControlPanelRegistry } from '@superset-ui/core';
+import ControlPanelsContainer from './ControlPanelsContainer';
+import ControlPanelsContainerJsonForms from './ControlPanelsContainerJsonForms';
+
+/**
+ * Wrapper component that automatically selects the appropriate control panel container
+ * based on whether the chart's control panel uses JSON Forms or legacy format
+ */
+export const ControlPanelsContainerWrapper = (props: any) => {
+ const { exploreState, ...restProps } = props;
+ const { vizType } = exploreState;
+
+ // Check if the control panel uses JSON Forms format
+ const isJsonFormsFormat = useMemo(() => {
+ if (!vizType) return false;
+
+ const controlPanelConfig = getChartControlPanelRegistry().get(vizType);
+ if (!controlPanelConfig) return false;
+
+ // JSON Forms format has 'schema' and 'uischema' properties
+ // Legacy format has 'controlPanelSections' property
+ return !!(controlPanelConfig.schema && controlPanelConfig.uischema);
+ }, [vizType]);
+
+ // Use the appropriate container based on the format
+ const Container = isJsonFormsFormat
+ ? ControlPanelsContainerJsonForms
+ : ControlPanelsContainer;
+
+ return <Container exploreState={exploreState} {...restProps} />;
+};
+
+export default ControlPanelsContainerWrapper;
\ No newline at end of file
diff --git a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
index ea7b934cba..d4392ad087 100644
--- a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
+++ b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx
@@ -66,7 +66,7 @@ import * as saveModalActions from 'src/explore/actions/saveModalActions';
import { useTabId } from 'src/hooks/useTabId';
import withToasts from 'src/components/MessageToasts/withToasts';
import ExploreChartPanel from '../ExploreChartPanel';
-import ConnectedControlPanelsContainer from '../ControlPanelsContainer';
+import ConnectedControlPanelsContainer from '../ControlPanelsContainerWrapper';
import SaveModal from '../SaveModal';
import DataSourcePanel from '../DatasourcePanel';
import ConnectedExploreChartHeader from '../ExploreChartHeader';