From f19304643d1d320aaeecf7864c482504d85edf40 Mon Sep 17 00:00:00 2001 From: alex241728 Date: Wed, 29 Oct 2025 18:35:32 -0400 Subject: [PATCH 1/8] add more formats --- .../src/shared-controls/sharedControls.tsx | 24 ++++++++++++++ .../Regular/Scatter/controlPanel.tsx | 32 +++++++++++++++++++ .../src/Timeseries/transformProps.ts | 3 ++ .../src/Timeseries/types.ts | 1 + 4 files changed, 60 insertions(+) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx index 76730673bea5..39d379af9b60 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx @@ -342,6 +342,29 @@ const x_axis_time_format: SharedControlConfig< option.label.includes(search) || option.value.includes(search), }; +const x_axis_number_format: SharedControlConfig<'SelectControl', SelectDefaultOption> = + { + type: 'SelectControl', + freeForm: true, + label: t('X Axis Number Format'), + renderTrigger: true, + default: DEFAULT_NUMBER_FORMAT, + choices: D3_FORMAT_OPTIONS, + description: D3_FORMAT_DOCS, + tokenSeparators: ['\n', '\t', ';'], + filterOption: ({ data: option }, search) => + option.label.includes(search) || option.value.includes(search), + mapStateToProps: state => { + const isPercentage = + state.controls?.comparison_type?.value === ComparisonType.Percentage; + return { + choices: isPercentage + ? D3_FORMAT_OPTIONS.filter(option => option[0].includes('%')) + : D3_FORMAT_OPTIONS, + }; + }, + }; + const color_scheme: SharedControlConfig<'ColorSchemeControl'> = { type: 'ColorSchemeControl', label: t('Color Scheme'), @@ -456,6 +479,7 @@ const sharedControls: Record> = { size: dndSizeControl, y_axis_format, x_axis_time_format, + x_axis_number_format, adhoc_filters: dndAdhocFilterControl, color_scheme, time_shift_color, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx index bf23eadd61ff..2d9ad5332fe5 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx @@ -112,8 +112,40 @@ const config: ControlPanelConfig = { ...sharedControls.x_axis_time_format, default: 'smart_date', description: `${D3_TIME_FORMAT_DOCS}. ${TIME_SERIES_DESCRIPTION_TEXT}`, + visibility: ({ controls }: ControlPanelsContainerProps) => { + const xAxisColumn = controls?.x_axis?.value; + const xAxisOptions = controls?.x_axis?.options; + + if (!xAxisColumn || !Array.isArray(xAxisOptions)) + { + return false; + } + + const xAxisType = Array.isArray(xAxisOptions) ? xAxisOptions.find(option => option.column_name === xAxisColumn)?.type : null; + + return typeof xAxisType === 'string' && xAxisType.toUpperCase().includes('TIME'); + }, }, }, + { + name: 'x_axis_format', + config: { + ...sharedControls.x_axis_number_format, + visibility: ({ controls }: ControlPanelsContainerProps) => { + const xAxisColumn = controls?.x_axis?.value; + const xAxisOptions = controls?.x_axis?.options; + + if (!xAxisColumn || !Array.isArray(xAxisOptions)) + { + return false; + } + + const xAxisType = Array.isArray(xAxisOptions) ? xAxisOptions.find(option => option.column_name === xAxisColumn)?.type : null; + + return typeof xAxisType === 'string' && xAxisType.toUpperCase().includes('FLOAT'); + }, + }, + } ], [xAxisLabelRotation], [xAxisLabelInterval], diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts index 9e897e2952cb..60551e36c8fb 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts @@ -189,6 +189,7 @@ export default function transformProps( xAxisSort, xAxisSortAsc, xAxisTimeFormat, + xAxisNumberFormat, xAxisTitle, xAxisTitleMargin, yAxisBounds, @@ -485,6 +486,8 @@ export default function transformProps( const xAxisFormatter = xAxisDataType === GenericDataType.Temporal ? getXAxisFormatter(xAxisTimeFormat) + : xAxisDataType === GenericDataType.Numeric + ? getNumberFormatter(xAxisNumberFormat) : String; const { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/types.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/types.ts index 72ea97019ef5..49b764e6a033 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/types.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/types.ts @@ -84,6 +84,7 @@ export type EchartsTimeseriesFormData = QueryFormData & { yAxisFormat?: string; xAxisForceCategorical?: boolean; xAxisTimeFormat?: string; + xAxisNumberFormat?: string; timeGrainSqla?: TimeGranularity; forceMaxInterval?: boolean; xAxisBounds: [number | undefined | null, number | undefined | null]; From 9d324848335a02c27b811240039d2ecd966a97bc Mon Sep 17 00:00:00 2001 From: "Wentao (Alex) Yang" Date: Wed, 29 Oct 2025 20:46:14 -0400 Subject: [PATCH 2/8] complete fixing --- .../Regular/Scatter/controlPanel.tsx | 18 ++++++++++++++---- .../src/Timeseries/constants.ts | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx index 2d9ad5332fe5..92577a6b1bc8 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx @@ -113,6 +113,7 @@ const config: ControlPanelConfig = { default: 'smart_date', description: `${D3_TIME_FORMAT_DOCS}. ${TIME_SERIES_DESCRIPTION_TEXT}`, visibility: ({ controls }: ControlPanelsContainerProps) => { + // check if x axis is a time column const xAxisColumn = controls?.x_axis?.value; const xAxisOptions = controls?.x_axis?.options; @@ -121,17 +122,18 @@ const config: ControlPanelConfig = { return false; } - const xAxisType = Array.isArray(xAxisOptions) ? xAxisOptions.find(option => option.column_name === xAxisColumn)?.type : null; + const xAxisType = xAxisOptions.find(option => option.column_name === xAxisColumn)?.type; return typeof xAxisType === 'string' && xAxisType.toUpperCase().includes('TIME'); }, }, }, { - name: 'x_axis_format', + name: 'x_axis_number_format', config: { ...sharedControls.x_axis_number_format, visibility: ({ controls }: ControlPanelsContainerProps) => { + // check if x axis is a floating-point column const xAxisColumn = controls?.x_axis?.value; const xAxisOptions = controls?.x_axis?.options; @@ -140,9 +142,17 @@ const config: ControlPanelConfig = { return false; } - const xAxisType = Array.isArray(xAxisOptions) ? xAxisOptions.find(option => option.column_name === xAxisColumn)?.type : null; + const xAxisType = xAxisOptions.find(option => option.column_name === xAxisColumn)?.type; + console.log('xAxisType', xAxisType); + + if (typeof xAxisType !== 'string') + { + return false; + } + + const typeUpper = xAxisType.toUpperCase(); - return typeof xAxisType === 'string' && xAxisType.toUpperCase().includes('FLOAT'); + return ['FLOAT', 'DOUBLE', 'REAL', 'NUMERIC', 'DECIMAL'].some(t => typeUpper.includes(t)); }, }, } diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/constants.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/constants.ts index 6378da8f2ea3..5aac81dc7f1c 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/constants.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/constants.ts @@ -72,6 +72,7 @@ export const DEFAULT_FORM_DATA: EchartsTimeseriesFormData = { stack: false, tooltipTimeFormat: 'smart_date', xAxisTimeFormat: 'smart_date', + xAxisNumberFormat: 'SMART_NUMBER', truncateXAxis: true, truncateYAxis: false, yAxisBounds: [null, null], From b183d7b42437d8172bf82f620cad53429e42bd03 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 29 Oct 2025 23:33:14 -0400 Subject: [PATCH 3/8] Clean up debug message --- .../src/Timeseries/Regular/Scatter/controlPanel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx index 92577a6b1bc8..d93f49ecdd51 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx @@ -143,7 +143,6 @@ const config: ControlPanelConfig = { } const xAxisType = xAxisOptions.find(option => option.column_name === xAxisColumn)?.type; - console.log('xAxisType', xAxisType); if (typeof xAxisType !== 'string') { From 4aa504d42dc9bbd807adec4cdb7dceb9e59a3e92 Mon Sep 17 00:00:00 2001 From: "Wentao (Alex) Yang" Date: Tue, 4 Nov 2025 23:12:43 -0500 Subject: [PATCH 4/8] control panel test --- .../Timeseries/Scatter/controlPanel.test.ts | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts new file mode 100644 index 000000000000..72151707e67a --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts @@ -0,0 +1,149 @@ +/** + * 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 { ControlPanelsContainerProps } from '@superset-ui/chart-controls/types'; +import controlPanel from '../../../src/Timeseries/Regular/Scatter/controlPanel'; + +const config = controlPanel; + +describe('Scatter Chart Control Panel', () => { + const getControl = (controlName: string) => { + for (const section of config.controlPanelSections) { + if (section && section.controlSetRows) { + for (const row of section.controlSetRows) { + for (const control of row) { + if ( + typeof control === 'object' && + control !== null && + 'name' in control && + control.name === controlName + ) { + return control; + } + } + } + } + } + + return null; + } + + const mockControls = (xAxisColumn: string | null, xAxisType: string | null): ControlPanelsContainerProps => { + const options = xAxisType ? [{ column_name: xAxisColumn, type: xAxisType }] : []; + + return { + controls: { + // @ts-ignore + x_axis: { + value: xAxisColumn, + options: options, + }, + }, + }; + }; + + describe('x_axis_time_format control', () => { + const timeFormatControl: any = getControl('x_axis_time_format'); + + it('should include x_axis_time_format control in the panel', () => { + expect(timeFormatControl).toBeDefined(); + }); + + it('should have correct default value for x_axis_time_format', () => { + expect(timeFormatControl).toBeDefined(); + expect(timeFormatControl.config).toBeDefined(); + expect(timeFormatControl.config.default).toBe('smart_date'); + }); + + it('should have visibility function for x_axis_time_format', () => { + expect(timeFormatControl).toBeDefined(); + expect(timeFormatControl.config.visibility).toBeDefined(); + expect(typeof timeFormatControl.config.visibility).toBe('function'); + + // The visibility function exists - the exact logic is tested implicitly through UI behavior + // The important part is that the control has proper visibility configuration + }); + + describe('x_axis_time_format control visibility', () => { + const isVisible = (xAxisColumn: string | null, xAxisType: string | null): boolean => { + const props = mockControls(xAxisColumn, xAxisType); + const visibilityFn = timeFormatControl?.config?.visibility; + return visibilityFn ? visibilityFn(props) : false; + }; + + it('should be visible for any data types include TIME', () => { + expect(isVisible("time_column", "TIME")).toBe(true); + expect(isVisible("time_column", "TIME WITH TIME ZONE")).toBe(true); + expect(isVisible("time_column", "TIMESTAMP WITH TIME ZONE")).toBe(true); + expect(isVisible("time_column", "TIMESTAMP WITHOUT TIME ZONE")).toBe(true); + }); + + it('should be hidden for any data types include TIME', () => { + expect(isVisible("null", "null")).toBe(false); + expect(isVisible(null, null)).toBe(false); + expect(isVisible("float_column", "FLOAT")).toBe(false); + }); + }); + }); + + describe('x_axis_number_format control', () => { + const numberFormatControl: any = getControl('x_axis_number_format'); + + it('should include x_axis_number_format control in the panel', () => { + expect(numberFormatControl).toBeDefined(); + }); + + it('should have correct default value for x_axis_number_format', () => { + expect(numberFormatControl).toBeDefined(); + expect(numberFormatControl.config).toBeDefined(); + expect(numberFormatControl.config.default).toBe('SMART_NUMBER'); + }); + + it('should have visibility function for x_axis_number_format', () => { + expect(numberFormatControl).toBeDefined(); + expect(numberFormatControl.config.visibility).toBeDefined(); + expect(typeof numberFormatControl.config.visibility).toBe('function'); + + // The visibility function exists - the exact logic is tested implicitly through UI behavior + // The important part is that the control has proper visibility configuration + }); + + describe('x_axis_number_format control visibility', () => { + const isVisible = (xAxisColumn: string | null, xAxisType: string | null): boolean => { + const props = mockControls(xAxisColumn, xAxisType); + const visibilityFn = numberFormatControl?.config?.visibility; + return visibilityFn ? visibilityFn(props) : false; + }; + + it('should be visible for any floating-point data types', () => { + expect(isVisible("float_column", 'FLOAT')).toBe(true); + expect(isVisible("double_column", "DOUBLE")).toBe(true); + expect(isVisible("real_column", "REAL")).toBe(true); + expect(isVisible("numeric_column", "NUMERIC")).toBe(true); + expect(isVisible("decimal_column", "DECIMAL")).toBe(true); + }); + + it('should be hidden for any non-floating-point data types', () => { + expect(isVisible("string_column", "VARCHAR")).toBe(false); + expect(isVisible("null", "null")).toBe(false); + expect(isVisible(null, null)).toBe(false); + expect(isVisible("time_column", "TIMESTAMP WITHOUT TIME ZONE")).toBe(false); + }); + }); + }); +}); From bd9dbf37d366771624daad21f9d66c590ea6e6f3 Mon Sep 17 00:00:00 2001 From: "Wentao (Alex) Yang" Date: Wed, 5 Nov 2025 17:33:39 -0500 Subject: [PATCH 5/8] transform props test --- .../Regular/Scatter/controlPanel.tsx | 6 +- .../Timeseries/Scatter/transformProps.test.ts | 166 ++++++++++++++++++ 2 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx index d93f49ecdd51..5fea04843e8d 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx @@ -123,7 +123,7 @@ const config: ControlPanelConfig = { } const xAxisType = xAxisOptions.find(option => option.column_name === xAxisColumn)?.type; - + return typeof xAxisType === 'string' && xAxisType.toUpperCase().includes('TIME'); }, }, @@ -141,7 +141,7 @@ const config: ControlPanelConfig = { { return false; } - + const xAxisType = xAxisOptions.find(option => option.column_name === xAxisColumn)?.type; if (typeof xAxisType !== 'string') @@ -150,7 +150,7 @@ const config: ControlPanelConfig = { } const typeUpper = xAxisType.toUpperCase(); - + return ['FLOAT', 'DOUBLE', 'REAL', 'NUMERIC', 'DECIMAL'].some(t => typeUpper.includes(t)); }, }, diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts new file mode 100644 index 000000000000..a0ec5434d77e --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts @@ -0,0 +1,166 @@ +/** + * 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 { ChartProps, SMART_DATE_ID, supersetTheme } from '@superset-ui/core'; +import transformProps from '../../../src/Timeseries/transformProps'; +import { DEFAULT_FORM_DATA } from '../../../src/Timeseries/constants'; +import { EchartsTimeseriesSeriesType, EchartsTimeseriesFormData, EchartsTimeseriesChartProps } from '../../../src/Timeseries/types'; +import { GenericDataType } from '@apache-superset/core/api/core'; +import { D3_FORMAT_OPTIONS, D3_TIME_FORMAT_OPTIONS } from '@superset-ui/chart-controls'; + +describe('Scatter Chart X-axis Time Formatting', () => { + const baseFormData: EchartsTimeseriesFormData = { + ...DEFAULT_FORM_DATA, + colorScheme: 'supersetColors', + datasource: '1__table', + granularity_sqla: '__timestamp', + metric: ['column 1'], + groupby: [], + viz_type: 'echarts_timeseries_scatter', + seriesType: EchartsTimeseriesSeriesType.Scatter, + }; + + const timeseriesData = [ + { + data: [ + { column_1: 0.72099, __timestamp: 1609459200000 }, + { column_1: 0.77954, __timestamp: 1612137600000 }, + { column_1: 2.83434, __timestamp: 1614556800000 }, + ], + colnames: ['column_1', '__timestamp'], + coltypes: [GenericDataType.Numeric, GenericDataType.Temporal], + }, + ]; + + const baseChartPropsConfig = { + width: 800, + height: 600, + queriesData: timeseriesData, + theme: supersetTheme, + }; + + it('xAxisTimeFormat has no default formatter', () => { + const chartProps = new ChartProps({ + ...baseChartPropsConfig, + formData: baseFormData, + }); + + const transformedProps = transformProps( + chartProps as EchartsTimeseriesChartProps, + ); + + expect(transformedProps.echartOptions.xAxis).toHaveProperty('axisLabel'); + const xAxis = transformedProps.echartOptions.xAxis as any; + expect(xAxis.axisLabel).toHaveProperty('formatter'); + expect(xAxis.axisLabel.formatter).toBeUndefined(); + }); + + it.each(D3_TIME_FORMAT_OPTIONS)('should handle %s format', (format) => { + const chartProps = new ChartProps({ + ...baseChartPropsConfig, + formData: { + ...baseFormData, + xAxisTimeFormat: format, + }, + }); + + const transformedProps = transformProps( + chartProps as EchartsTimeseriesChartProps, + ); + + const xAxis = transformedProps.echartOptions.xAxis as any; + expect(xAxis.axisLabel).toHaveProperty('formatter'); + if (format === SMART_DATE_ID) { + expect(xAxis.axisLabel.formatter).toBeUndefined(); + } + else { + expect(typeof xAxis.axisLabel.formatter).toBe('function'); + expect(xAxis.axisLabel.formatter.id).toBe(format); + } + }); +}); + +describe('Scatter Chart X-axis Number Formatting', () => { + const baseFormData: EchartsTimeseriesFormData = { + ...DEFAULT_FORM_DATA, + colorScheme: 'supersetColors', + datasource: '1__table', + metric: ['column_1'], + x_axis: 'column_2', + groupby: [], + viz_type: 'echarts_timeseries_scatter', + seriesType: EchartsTimeseriesSeriesType.Scatter, + }; + + const timeseriesData = [ + { + data: [ + { column_1: 0.72099, column_2: 3.01699 }, + { column_1: 0.77954, column_2: 3.44802 }, + { column_1: 2.83434, column_2: 3.58095 }, + ], + colnames: ['column_1', 'column_2'], + coltypes: [GenericDataType.Numeric, GenericDataType.Numeric], + }, + ]; + + const baseChartPropsConfig = { + width: 800, + height: 600, + queriesData: timeseriesData, + theme: supersetTheme, + }; + + it('should use SMART_NUMBER as default xAxisNumberFormat', () => { + const chartProps = new ChartProps({ + ...baseChartPropsConfig, + formData: baseFormData, + }); + + const transformedProps = transformProps( + chartProps as EchartsTimeseriesChartProps, + ); + + expect(transformedProps.echartOptions.xAxis).toHaveProperty('axisLabel'); + const xAxis = transformedProps.echartOptions.xAxis as any; + expect(xAxis.axisLabel).toHaveProperty('formatter'); + expect(typeof xAxis.axisLabel.formatter).toBe('function'); + expect(xAxis.axisLabel.formatter.id).toBe('SMART_NUMBER'); + }); + + it.each(D3_FORMAT_OPTIONS)('should handle %s format', (format) => { + const chartProps = new ChartProps({ + ...baseChartPropsConfig, + formData: { + ...baseFormData, + xAxisNumberFormat: format, + }, + }); + + const transformedProps = transformProps( + chartProps as EchartsTimeseriesChartProps, + ); + + expect(transformedProps.echartOptions.xAxis).toHaveProperty('axisLabel'); + const xAxis = transformedProps.echartOptions.xAxis as any; + expect(xAxis.axisLabel).toHaveProperty('formatter'); + expect(typeof xAxis.axisLabel.formatter).toBe('function'); + expect(xAxis.axisLabel.formatter.id).toBe(format); + }); +}); From f1aded75a97c6151f2d156b9dc9da7146bf5fb0b Mon Sep 17 00:00:00 2001 From: Wentao Yang Date: Wed, 5 Nov 2025 18:57:48 -0500 Subject: [PATCH 6/8] precommit --- .../src/shared-controls/sharedControls.tsx | 46 +++++------ .../Regular/Scatter/controlPanel.tsx | 28 ++++--- .../src/Timeseries/transformProps.ts | 4 +- .../Timeseries/Scatter/controlPanel.test.ts | 77 +++++++++++-------- .../Timeseries/Scatter/transformProps.test.ts | 18 +++-- 5 files changed, 101 insertions(+), 72 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx index 39d379af9b60..1ac7fe9effcc 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/sharedControls.tsx @@ -342,28 +342,30 @@ const x_axis_time_format: SharedControlConfig< option.label.includes(search) || option.value.includes(search), }; -const x_axis_number_format: SharedControlConfig<'SelectControl', SelectDefaultOption> = - { - type: 'SelectControl', - freeForm: true, - label: t('X Axis Number Format'), - renderTrigger: true, - default: DEFAULT_NUMBER_FORMAT, - choices: D3_FORMAT_OPTIONS, - description: D3_FORMAT_DOCS, - tokenSeparators: ['\n', '\t', ';'], - filterOption: ({ data: option }, search) => - option.label.includes(search) || option.value.includes(search), - mapStateToProps: state => { - const isPercentage = - state.controls?.comparison_type?.value === ComparisonType.Percentage; - return { - choices: isPercentage - ? D3_FORMAT_OPTIONS.filter(option => option[0].includes('%')) - : D3_FORMAT_OPTIONS, - }; - }, - }; +const x_axis_number_format: SharedControlConfig< + 'SelectControl', + SelectDefaultOption +> = { + type: 'SelectControl', + freeForm: true, + label: t('X Axis Number Format'), + renderTrigger: true, + default: DEFAULT_NUMBER_FORMAT, + choices: D3_FORMAT_OPTIONS, + description: D3_FORMAT_DOCS, + tokenSeparators: ['\n', '\t', ';'], + filterOption: ({ data: option }, search) => + option.label.includes(search) || option.value.includes(search), + mapStateToProps: state => { + const isPercentage = + state.controls?.comparison_type?.value === ComparisonType.Percentage; + return { + choices: isPercentage + ? D3_FORMAT_OPTIONS.filter(option => option[0].includes('%')) + : D3_FORMAT_OPTIONS, + }; + }, +}; const color_scheme: SharedControlConfig<'ColorSchemeControl'> = { type: 'ColorSchemeControl', diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx index 5fea04843e8d..a081b850360c 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Scatter/controlPanel.tsx @@ -117,14 +117,18 @@ const config: ControlPanelConfig = { const xAxisColumn = controls?.x_axis?.value; const xAxisOptions = controls?.x_axis?.options; - if (!xAxisColumn || !Array.isArray(xAxisOptions)) - { + if (!xAxisColumn || !Array.isArray(xAxisOptions)) { return false; } - const xAxisType = xAxisOptions.find(option => option.column_name === xAxisColumn)?.type; + const xAxisType = xAxisOptions.find( + option => option.column_name === xAxisColumn, + )?.type; - return typeof xAxisType === 'string' && xAxisType.toUpperCase().includes('TIME'); + return ( + typeof xAxisType === 'string' && + xAxisType.toUpperCase().includes('TIME') + ); }, }, }, @@ -137,24 +141,26 @@ const config: ControlPanelConfig = { const xAxisColumn = controls?.x_axis?.value; const xAxisOptions = controls?.x_axis?.options; - if (!xAxisColumn || !Array.isArray(xAxisOptions)) - { + if (!xAxisColumn || !Array.isArray(xAxisOptions)) { return false; } - const xAxisType = xAxisOptions.find(option => option.column_name === xAxisColumn)?.type; + const xAxisType = xAxisOptions.find( + option => option.column_name === xAxisColumn, + )?.type; - if (typeof xAxisType !== 'string') - { + if (typeof xAxisType !== 'string') { return false; } const typeUpper = xAxisType.toUpperCase(); - return ['FLOAT', 'DOUBLE', 'REAL', 'NUMERIC', 'DECIMAL'].some(t => typeUpper.includes(t)); + return ['FLOAT', 'DOUBLE', 'REAL', 'NUMERIC', 'DECIMAL'].some( + t => typeUpper.includes(t), + ); }, }, - } + }, ], [xAxisLabelRotation], [xAxisLabelInterval], diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts index 60551e36c8fb..0a0e08e761c4 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts @@ -487,8 +487,8 @@ export default function transformProps( xAxisDataType === GenericDataType.Temporal ? getXAxisFormatter(xAxisTimeFormat) : xAxisDataType === GenericDataType.Numeric - ? getNumberFormatter(xAxisNumberFormat) - : String; + ? getNumberFormatter(xAxisNumberFormat) + : String; const { setDataMask = () => {}, diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts index 72151707e67a..3cc0c7be9a87 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts @@ -41,21 +41,26 @@ describe('Scatter Chart Control Panel', () => { } return null; - } - - const mockControls = (xAxisColumn: string | null, xAxisType: string | null): ControlPanelsContainerProps => { - const options = xAxisType ? [{ column_name: xAxisColumn, type: xAxisType }] : []; - - return { - controls: { - // @ts-ignore - x_axis: { - value: xAxisColumn, - options: options, - }, - }, - }; - }; + }; + + const mockControls = ( + xAxisColumn: string | null, + xAxisType: string | null, + ): ControlPanelsContainerProps => { + const options = xAxisType + ? [{ column_name: xAxisColumn, type: xAxisType }] + : []; + + return { + controls: { + // @ts-ignore + x_axis: { + value: xAxisColumn, + options: options, + }, + }, + }; + }; describe('x_axis_time_format control', () => { const timeFormatControl: any = getControl('x_axis_time_format'); @@ -80,23 +85,28 @@ describe('Scatter Chart Control Panel', () => { }); describe('x_axis_time_format control visibility', () => { - const isVisible = (xAxisColumn: string | null, xAxisType: string | null): boolean => { + const isVisible = ( + xAxisColumn: string | null, + xAxisType: string | null, + ): boolean => { const props = mockControls(xAxisColumn, xAxisType); const visibilityFn = timeFormatControl?.config?.visibility; return visibilityFn ? visibilityFn(props) : false; }; it('should be visible for any data types include TIME', () => { - expect(isVisible("time_column", "TIME")).toBe(true); - expect(isVisible("time_column", "TIME WITH TIME ZONE")).toBe(true); - expect(isVisible("time_column", "TIMESTAMP WITH TIME ZONE")).toBe(true); - expect(isVisible("time_column", "TIMESTAMP WITHOUT TIME ZONE")).toBe(true); + expect(isVisible('time_column', 'TIME')).toBe(true); + expect(isVisible('time_column', 'TIME WITH TIME ZONE')).toBe(true); + expect(isVisible('time_column', 'TIMESTAMP WITH TIME ZONE')).toBe(true); + expect(isVisible('time_column', 'TIMESTAMP WITHOUT TIME ZONE')).toBe( + true, + ); }); it('should be hidden for any data types include TIME', () => { - expect(isVisible("null", "null")).toBe(false); + expect(isVisible('null', 'null')).toBe(false); expect(isVisible(null, null)).toBe(false); - expect(isVisible("float_column", "FLOAT")).toBe(false); + expect(isVisible('float_column', 'FLOAT')).toBe(false); }); }); }); @@ -124,25 +134,30 @@ describe('Scatter Chart Control Panel', () => { }); describe('x_axis_number_format control visibility', () => { - const isVisible = (xAxisColumn: string | null, xAxisType: string | null): boolean => { + const isVisible = ( + xAxisColumn: string | null, + xAxisType: string | null, + ): boolean => { const props = mockControls(xAxisColumn, xAxisType); const visibilityFn = numberFormatControl?.config?.visibility; return visibilityFn ? visibilityFn(props) : false; }; it('should be visible for any floating-point data types', () => { - expect(isVisible("float_column", 'FLOAT')).toBe(true); - expect(isVisible("double_column", "DOUBLE")).toBe(true); - expect(isVisible("real_column", "REAL")).toBe(true); - expect(isVisible("numeric_column", "NUMERIC")).toBe(true); - expect(isVisible("decimal_column", "DECIMAL")).toBe(true); + expect(isVisible('float_column', 'FLOAT')).toBe(true); + expect(isVisible('double_column', 'DOUBLE')).toBe(true); + expect(isVisible('real_column', 'REAL')).toBe(true); + expect(isVisible('numeric_column', 'NUMERIC')).toBe(true); + expect(isVisible('decimal_column', 'DECIMAL')).toBe(true); }); it('should be hidden for any non-floating-point data types', () => { - expect(isVisible("string_column", "VARCHAR")).toBe(false); - expect(isVisible("null", "null")).toBe(false); + expect(isVisible('string_column', 'VARCHAR')).toBe(false); + expect(isVisible('null', 'null')).toBe(false); expect(isVisible(null, null)).toBe(false); - expect(isVisible("time_column", "TIMESTAMP WITHOUT TIME ZONE")).toBe(false); + expect(isVisible('time_column', 'TIMESTAMP WITHOUT TIME ZONE')).toBe( + false, + ); }); }); }); diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts index a0ec5434d77e..a80ff966a0a1 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts @@ -20,9 +20,16 @@ import { ChartProps, SMART_DATE_ID, supersetTheme } from '@superset-ui/core'; import transformProps from '../../../src/Timeseries/transformProps'; import { DEFAULT_FORM_DATA } from '../../../src/Timeseries/constants'; -import { EchartsTimeseriesSeriesType, EchartsTimeseriesFormData, EchartsTimeseriesChartProps } from '../../../src/Timeseries/types'; +import { + EchartsTimeseriesSeriesType, + EchartsTimeseriesFormData, + EchartsTimeseriesChartProps, +} from '../../../src/Timeseries/types'; import { GenericDataType } from '@apache-superset/core/api/core'; -import { D3_FORMAT_OPTIONS, D3_TIME_FORMAT_OPTIONS } from '@superset-ui/chart-controls'; +import { + D3_FORMAT_OPTIONS, + D3_TIME_FORMAT_OPTIONS, +} from '@superset-ui/chart-controls'; describe('Scatter Chart X-axis Time Formatting', () => { const baseFormData: EchartsTimeseriesFormData = { @@ -71,7 +78,7 @@ describe('Scatter Chart X-axis Time Formatting', () => { expect(xAxis.axisLabel.formatter).toBeUndefined(); }); - it.each(D3_TIME_FORMAT_OPTIONS)('should handle %s format', (format) => { + it.each(D3_TIME_FORMAT_OPTIONS)('should handle %s format', format => { const chartProps = new ChartProps({ ...baseChartPropsConfig, formData: { @@ -88,8 +95,7 @@ describe('Scatter Chart X-axis Time Formatting', () => { expect(xAxis.axisLabel).toHaveProperty('formatter'); if (format === SMART_DATE_ID) { expect(xAxis.axisLabel.formatter).toBeUndefined(); - } - else { + } else { expect(typeof xAxis.axisLabel.formatter).toBe('function'); expect(xAxis.axisLabel.formatter.id).toBe(format); } @@ -144,7 +150,7 @@ describe('Scatter Chart X-axis Number Formatting', () => { expect(xAxis.axisLabel.formatter.id).toBe('SMART_NUMBER'); }); - it.each(D3_FORMAT_OPTIONS)('should handle %s format', (format) => { + it.each(D3_FORMAT_OPTIONS)('should handle %s format', format => { const chartProps = new ChartProps({ ...baseChartPropsConfig, formData: { From c49c0f7384426f92d7c912c5263a00c49a483721 Mon Sep 17 00:00:00 2001 From: "Wentao (Alex) Yang" Date: Sat, 8 Nov 2025 23:27:40 -0500 Subject: [PATCH 7/8] modify test --- .../Timeseries/Scatter/controlPanel.test.ts | 258 +++++++++--------- .../Timeseries/Scatter/transformProps.test.ts | 8 +- 2 files changed, 129 insertions(+), 137 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts index 3cc0c7be9a87..4202ea5079fd 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts @@ -21,144 +21,136 @@ import controlPanel from '../../../src/Timeseries/Regular/Scatter/controlPanel'; const config = controlPanel; -describe('Scatter Chart Control Panel', () => { - const getControl = (controlName: string) => { - for (const section of config.controlPanelSections) { - if (section && section.controlSetRows) { - for (const row of section.controlSetRows) { - for (const control of row) { - if ( - typeof control === 'object' && - control !== null && - 'name' in control && - control.name === controlName - ) { - return control; - } +const getControl = (controlName: string) => { + for (const section of config.controlPanelSections) { + if (section && section.controlSetRows) { + for (const row of section.controlSetRows) { + for (const control of row) { + if ( + typeof control === 'object' && + control !== null && + 'name' in control && + control.name === controlName + ) { + return control; } } } } - - return null; - }; - - const mockControls = ( - xAxisColumn: string | null, - xAxisType: string | null, - ): ControlPanelsContainerProps => { - const options = xAxisType - ? [{ column_name: xAxisColumn, type: xAxisType }] - : []; - - return { - controls: { - // @ts-ignore - x_axis: { - value: xAxisColumn, - options: options, - }, + } + + return null; +}; + +const mockControls = ( + xAxisColumn: string | null, + xAxisType: string | null, +): ControlPanelsContainerProps => { + const options = xAxisType + ? [{ column_name: xAxisColumn, type: xAxisType }] + : []; + + return { + controls: { + // @ts-ignore + x_axis: { + value: xAxisColumn, + options: options, }, - }; + }, }; +}; + +// tests for x_axis_time_format control +const timeFormatControl: any = getControl('x_axis_time_format'); + +test('scatter chart control panel should include x_axis_time_format control in the panel', () => { + expect(timeFormatControl).toBeDefined(); +}); + +test('scatter chart control panel should have correct default value for x_axis_time_format', () => { + expect(timeFormatControl).toBeDefined(); + expect(timeFormatControl.config).toBeDefined(); + expect(timeFormatControl.config.default).toBe('smart_date'); +}); + +test('scatter chart control panel should have visibility function for x_axis_time_format', () => { + expect(timeFormatControl).toBeDefined(); + expect(timeFormatControl.config.visibility).toBeDefined(); + expect(typeof timeFormatControl.config.visibility).toBe('function'); + + // The visibility function exists - the exact logic is tested implicitly through UI behavior + // The important part is that the control has proper visibility configuration +}); + +const isTimeVisible = ( + xAxisColumn: string | null, + xAxisType: string | null, +): boolean => { + const props = mockControls(xAxisColumn, xAxisType); + const visibilityFn = timeFormatControl?.config?.visibility; + return visibilityFn ? visibilityFn(props) : false; +}; + +test('x_axis_time_format control should be visible for any data types include TIME', () => { + expect(isTimeVisible('time_column', 'TIME')).toBe(true); + expect(isTimeVisible('time_column', 'TIME WITH TIME ZONE')).toBe(true); + expect(isTimeVisible('time_column', 'TIMESTAMP WITH TIME ZONE')).toBe(true); + expect(isTimeVisible('time_column', 'TIMESTAMP WITHOUT TIME ZONE')).toBe( + true, + ); +}); + +test('x_axis_time_format control should be hidden for any data types include TIME', () => { + expect(isTimeVisible('null', 'null')).toBe(false); + expect(isTimeVisible(null, null)).toBe(false); + expect(isTimeVisible('float_column', 'FLOAT')).toBe(false); +}); + +// tests for x_axis_number_format control +const numberFormatControl: any = getControl('x_axis_number_format'); + +test('scatter chart control panel should include x_axis_number_format control in the panel', () => { + expect(numberFormatControl).toBeDefined(); +}); + +test('scatter chart control panel should have correct default value for x_axis_number_format', () => { + expect(numberFormatControl).toBeDefined(); + expect(numberFormatControl.config).toBeDefined(); + expect(numberFormatControl.config.default).toBe('SMART_NUMBER'); +}); + +test('scatter chart control panel should have visibility function for x_axis_number_format', () => { + expect(numberFormatControl).toBeDefined(); + expect(numberFormatControl.config.visibility).toBeDefined(); + expect(typeof numberFormatControl.config.visibility).toBe('function'); + + // The visibility function exists - the exact logic is tested implicitly through UI behavior + // The important part is that the control has proper visibility configuration +}); + +const isNumberVisible = ( + xAxisColumn: string | null, + xAxisType: string | null, +): boolean => { + const props = mockControls(xAxisColumn, xAxisType); + const visibilityFn = numberFormatControl?.config?.visibility; + return visibilityFn ? visibilityFn(props) : false; +}; + +test('x_axis_number_format control should be visible for any floating-point data types', () => { + expect(isNumberVisible('float_column', 'FLOAT')).toBe(true); + expect(isNumberVisible('double_column', 'DOUBLE')).toBe(true); + expect(isNumberVisible('real_column', 'REAL')).toBe(true); + expect(isNumberVisible('numeric_column', 'NUMERIC')).toBe(true); + expect(isNumberVisible('decimal_column', 'DECIMAL')).toBe(true); +}); - describe('x_axis_time_format control', () => { - const timeFormatControl: any = getControl('x_axis_time_format'); - - it('should include x_axis_time_format control in the panel', () => { - expect(timeFormatControl).toBeDefined(); - }); - - it('should have correct default value for x_axis_time_format', () => { - expect(timeFormatControl).toBeDefined(); - expect(timeFormatControl.config).toBeDefined(); - expect(timeFormatControl.config.default).toBe('smart_date'); - }); - - it('should have visibility function for x_axis_time_format', () => { - expect(timeFormatControl).toBeDefined(); - expect(timeFormatControl.config.visibility).toBeDefined(); - expect(typeof timeFormatControl.config.visibility).toBe('function'); - - // The visibility function exists - the exact logic is tested implicitly through UI behavior - // The important part is that the control has proper visibility configuration - }); - - describe('x_axis_time_format control visibility', () => { - const isVisible = ( - xAxisColumn: string | null, - xAxisType: string | null, - ): boolean => { - const props = mockControls(xAxisColumn, xAxisType); - const visibilityFn = timeFormatControl?.config?.visibility; - return visibilityFn ? visibilityFn(props) : false; - }; - - it('should be visible for any data types include TIME', () => { - expect(isVisible('time_column', 'TIME')).toBe(true); - expect(isVisible('time_column', 'TIME WITH TIME ZONE')).toBe(true); - expect(isVisible('time_column', 'TIMESTAMP WITH TIME ZONE')).toBe(true); - expect(isVisible('time_column', 'TIMESTAMP WITHOUT TIME ZONE')).toBe( - true, - ); - }); - - it('should be hidden for any data types include TIME', () => { - expect(isVisible('null', 'null')).toBe(false); - expect(isVisible(null, null)).toBe(false); - expect(isVisible('float_column', 'FLOAT')).toBe(false); - }); - }); - }); - - describe('x_axis_number_format control', () => { - const numberFormatControl: any = getControl('x_axis_number_format'); - - it('should include x_axis_number_format control in the panel', () => { - expect(numberFormatControl).toBeDefined(); - }); - - it('should have correct default value for x_axis_number_format', () => { - expect(numberFormatControl).toBeDefined(); - expect(numberFormatControl.config).toBeDefined(); - expect(numberFormatControl.config.default).toBe('SMART_NUMBER'); - }); - - it('should have visibility function for x_axis_number_format', () => { - expect(numberFormatControl).toBeDefined(); - expect(numberFormatControl.config.visibility).toBeDefined(); - expect(typeof numberFormatControl.config.visibility).toBe('function'); - - // The visibility function exists - the exact logic is tested implicitly through UI behavior - // The important part is that the control has proper visibility configuration - }); - - describe('x_axis_number_format control visibility', () => { - const isVisible = ( - xAxisColumn: string | null, - xAxisType: string | null, - ): boolean => { - const props = mockControls(xAxisColumn, xAxisType); - const visibilityFn = numberFormatControl?.config?.visibility; - return visibilityFn ? visibilityFn(props) : false; - }; - - it('should be visible for any floating-point data types', () => { - expect(isVisible('float_column', 'FLOAT')).toBe(true); - expect(isVisible('double_column', 'DOUBLE')).toBe(true); - expect(isVisible('real_column', 'REAL')).toBe(true); - expect(isVisible('numeric_column', 'NUMERIC')).toBe(true); - expect(isVisible('decimal_column', 'DECIMAL')).toBe(true); - }); - - it('should be hidden for any non-floating-point data types', () => { - expect(isVisible('string_column', 'VARCHAR')).toBe(false); - expect(isVisible('null', 'null')).toBe(false); - expect(isVisible(null, null)).toBe(false); - expect(isVisible('time_column', 'TIMESTAMP WITHOUT TIME ZONE')).toBe( - false, - ); - }); - }); - }); +test('x_axis_number_format control should be hidden for any non-floating-point data types', () => { + expect(isNumberVisible('string_column', 'VARCHAR')).toBe(false); + expect(isNumberVisible('null', 'null')).toBe(false); + expect(isNumberVisible(null, null)).toBe(false); + expect(isNumberVisible('time_column', 'TIMESTAMP WITHOUT TIME ZONE')).toBe( + false, + ); }); diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts index a80ff966a0a1..c28f63622f58 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts @@ -62,7 +62,7 @@ describe('Scatter Chart X-axis Time Formatting', () => { theme: supersetTheme, }; - it('xAxisTimeFormat has no default formatter', () => { + test('xAxisTimeFormat has no default formatter', () => { const chartProps = new ChartProps({ ...baseChartPropsConfig, formData: baseFormData, @@ -78,7 +78,7 @@ describe('Scatter Chart X-axis Time Formatting', () => { expect(xAxis.axisLabel.formatter).toBeUndefined(); }); - it.each(D3_TIME_FORMAT_OPTIONS)('should handle %s format', format => { + test.each(D3_TIME_FORMAT_OPTIONS)('should handle %s format', format => { const chartProps = new ChartProps({ ...baseChartPropsConfig, formData: { @@ -133,7 +133,7 @@ describe('Scatter Chart X-axis Number Formatting', () => { theme: supersetTheme, }; - it('should use SMART_NUMBER as default xAxisNumberFormat', () => { + test('should use SMART_NUMBER as default xAxisNumberFormat', () => { const chartProps = new ChartProps({ ...baseChartPropsConfig, formData: baseFormData, @@ -150,7 +150,7 @@ describe('Scatter Chart X-axis Number Formatting', () => { expect(xAxis.axisLabel.formatter.id).toBe('SMART_NUMBER'); }); - it.each(D3_FORMAT_OPTIONS)('should handle %s format', format => { + test.each(D3_FORMAT_OPTIONS)('should handle %s format', format => { const chartProps = new ChartProps({ ...baseChartPropsConfig, formData: { From feeb12a46106c448fe0261f3e834bd9b19bff051 Mon Sep 17 00:00:00 2001 From: "Wentao (Alex) Yang" Date: Wed, 12 Nov 2025 15:58:07 -0500 Subject: [PATCH 8/8] solve copilot suggestions --- .../Timeseries/Scatter/controlPanel.test.ts | 2 +- .../Timeseries/Scatter/transformProps.test.ts | 95 +++++++++++-------- 2 files changed, 54 insertions(+), 43 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts index 4202ea5079fd..7be756386865 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/controlPanel.test.ts @@ -101,7 +101,7 @@ test('x_axis_time_format control should be visible for any data types include TI ); }); -test('x_axis_time_format control should be hidden for any data types include TIME', () => { +test('x_axis_time_format control should be hidden for data types that do NOT include TIME', () => { expect(isTimeVisible('null', 'null')).toBe(false); expect(isTimeVisible(null, null)).toBe(false); expect(isTimeVisible('float_column', 'FLOAT')).toBe(false); diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts index c28f63622f58..560f5c19ea19 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Scatter/transformProps.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { ChartProps, SMART_DATE_ID, supersetTheme } from '@superset-ui/core'; +import { ChartProps, SMART_DATE_ID } from '@superset-ui/core'; import transformProps from '../../../src/Timeseries/transformProps'; import { DEFAULT_FORM_DATA } from '../../../src/Timeseries/constants'; import { @@ -30,6 +30,7 @@ import { D3_FORMAT_OPTIONS, D3_TIME_FORMAT_OPTIONS, } from '@superset-ui/chart-controls'; +import { supersetTheme } from '@apache-superset/core/ui'; describe('Scatter Chart X-axis Time Formatting', () => { const baseFormData: EchartsTimeseriesFormData = { @@ -69,6 +70,7 @@ describe('Scatter Chart X-axis Time Formatting', () => { }); const transformedProps = transformProps( + // @ts-ignore chartProps as EchartsTimeseriesChartProps, ); @@ -78,28 +80,32 @@ describe('Scatter Chart X-axis Time Formatting', () => { expect(xAxis.axisLabel.formatter).toBeUndefined(); }); - test.each(D3_TIME_FORMAT_OPTIONS)('should handle %s format', format => { - const chartProps = new ChartProps({ - ...baseChartPropsConfig, - formData: { - ...baseFormData, - xAxisTimeFormat: format, - }, - }); - - const transformedProps = transformProps( - chartProps as EchartsTimeseriesChartProps, - ); - - const xAxis = transformedProps.echartOptions.xAxis as any; - expect(xAxis.axisLabel).toHaveProperty('formatter'); - if (format === SMART_DATE_ID) { - expect(xAxis.axisLabel.formatter).toBeUndefined(); - } else { - expect(typeof xAxis.axisLabel.formatter).toBe('function'); - expect(xAxis.axisLabel.formatter.id).toBe(format); - } - }); + test.each(D3_TIME_FORMAT_OPTIONS.map(([id]) => id))( + 'should handle %s format', + format => { + const chartProps = new ChartProps({ + ...baseChartPropsConfig, + formData: { + ...baseFormData, + xAxisTimeFormat: format, + }, + }); + + const transformedProps = transformProps( + // @ts-ignore + chartProps as EchartsTimeseriesChartProps, + ); + + const xAxis = transformedProps.echartOptions.xAxis as any; + expect(xAxis.axisLabel).toHaveProperty('formatter'); + if (format === SMART_DATE_ID) { + expect(xAxis.axisLabel.formatter).toBeUndefined(); + } else { + expect(typeof xAxis.axisLabel.formatter).toBe('function'); + expect(xAxis.axisLabel.formatter.id).toBe(format); + } + }, + ); }); describe('Scatter Chart X-axis Number Formatting', () => { @@ -140,6 +146,7 @@ describe('Scatter Chart X-axis Number Formatting', () => { }); const transformedProps = transformProps( + // @ts-ignore chartProps as EchartsTimeseriesChartProps, ); @@ -150,23 +157,27 @@ describe('Scatter Chart X-axis Number Formatting', () => { expect(xAxis.axisLabel.formatter.id).toBe('SMART_NUMBER'); }); - test.each(D3_FORMAT_OPTIONS)('should handle %s format', format => { - const chartProps = new ChartProps({ - ...baseChartPropsConfig, - formData: { - ...baseFormData, - xAxisNumberFormat: format, - }, - }); - - const transformedProps = transformProps( - chartProps as EchartsTimeseriesChartProps, - ); - - expect(transformedProps.echartOptions.xAxis).toHaveProperty('axisLabel'); - const xAxis = transformedProps.echartOptions.xAxis as any; - expect(xAxis.axisLabel).toHaveProperty('formatter'); - expect(typeof xAxis.axisLabel.formatter).toBe('function'); - expect(xAxis.axisLabel.formatter.id).toBe(format); - }); + test.each(D3_FORMAT_OPTIONS.map(([id]) => id))( + 'should handle %s format', + format => { + const chartProps = new ChartProps({ + ...baseChartPropsConfig, + formData: { + ...baseFormData, + xAxisNumberFormat: format, + }, + }); + + const transformedProps = transformProps( + // @ts-ignore + chartProps as EchartsTimeseriesChartProps, + ); + + expect(transformedProps.echartOptions.xAxis).toHaveProperty('axisLabel'); + const xAxis = transformedProps.echartOptions.xAxis as any; + expect(xAxis.axisLabel).toHaveProperty('formatter'); + expect(typeof xAxis.axisLabel.formatter).toBe('function'); + expect(xAxis.axisLabel.formatter.id).toBe(format); + }, + ); });