This is an automated email from the ASF dual-hosted git repository.

jli pushed a commit to branch 6.0
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/6.0 by this push:
     new dbe0845dc0 fix(ui-core): Invalid postTransform process (#34874)
dbe0845dc0 is described below

commit dbe0845dc07fcf0b503b4a03cca52549002e31f5
Author: JUST.in DO IT <[email protected]>
AuthorDate: Wed Sep 3 10:17:19 2025 -0700

    fix(ui-core): Invalid postTransform process (#34874)
    
    (cherry picked from commit 448a28545b3c61aef1eb70052734b493915f5059)
---
 .../src/chart/components/SuperChartCore.test.tsx   | 90 ++++++++++++++++++++++
 .../src/chart/components/SuperChartCore.tsx        | 61 ++++++++++++---
 .../src/components/Chart/ChartRenderer.jsx         |  3 +-
 .../src/components/Chart/ChartRenderer.test.jsx    | 23 +++++-
 4 files changed, 165 insertions(+), 12 deletions(-)

diff --git 
a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChartCore.test.tsx
 
b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChartCore.test.tsx
new file mode 100644
index 0000000000..4a77e184d5
--- /dev/null
+++ 
b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChartCore.test.tsx
@@ -0,0 +1,90 @@
+/*
+ * 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 { render, waitFor } from '@testing-library/react';
+
+import {
+  ChartPlugin,
+  ChartMetadata,
+  DatasourceType,
+  getChartComponentRegistry,
+} from '@superset-ui/core';
+
+import SuperChartCore from './SuperChartCore';
+
+const props = {
+  chartType: 'line',
+};
+const FakeChart = () => <span>test</span>;
+
+beforeEach(() => {
+  const metadata = new ChartMetadata({
+    name: 'test-chart',
+    thumbnail: '',
+  });
+  const buildQuery = () => ({
+    datasource: { id: 1, type: DatasourceType.Table },
+    queries: [{ granularity: 'day' }],
+    force: false,
+    result_format: 'json',
+    result_type: 'full',
+  });
+  const controlPanel = { abc: 1 };
+  const plugin = new ChartPlugin({
+    metadata,
+    Chart: FakeChart,
+    buildQuery,
+    controlPanel,
+  });
+  plugin.configure({ key: props.chartType }).register();
+});
+
+test('should return the result from cache unless transformProps has changed', 
async () => {
+  const pre = jest.fn(x => x);
+  const transform = jest.fn(x => x);
+  const post = jest.fn(x => x);
+  expect(getChartComponentRegistry().get(props.chartType)).toBe(FakeChart);
+
+  expect(pre).toHaveBeenCalledTimes(0);
+  const { rerender } = render(
+    <SuperChartCore
+      {...props}
+      preTransformProps={pre}
+      overrideTransformProps={transform}
+      postTransformProps={post}
+    />,
+  );
+
+  await waitFor(() => expect(pre).toHaveBeenCalledTimes(1));
+  expect(transform).toHaveBeenCalledTimes(1);
+  expect(post).toHaveBeenCalledTimes(1);
+
+  const updatedPost = jest.fn(x => x);
+
+  rerender(
+    <SuperChartCore
+      {...props}
+      preTransformProps={pre}
+      overrideTransformProps={transform}
+      postTransformProps={updatedPost}
+    />,
+  );
+  await waitFor(() => expect(updatedPost).toHaveBeenCalledTimes(1));
+  expect(transform).toHaveBeenCalledTimes(1);
+  expect(pre).toHaveBeenCalledTimes(1);
+});
diff --git 
a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChartCore.tsx
 
b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChartCore.tsx
index f980ed13cf..3f847ea4b5 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChartCore.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChartCore.tsx
@@ -85,31 +85,74 @@ export default class SuperChartCore extends 
PureComponent<Props, {}> {
   container?: HTMLElement | null;
 
   /**
-   * memoized function so it will not recompute
-   * and return previous value
+   * memoized function so it will not recompute and return previous value
    * unless one of
    * - preTransformProps
-   * - transformProps
-   * - postTransformProps
    * - chartProps
    * is changed.
    */
-  processChartProps = createSelector(
+  preSelector = createSelector(
     [
       (input: {
         chartProps: ChartProps;
         preTransformProps?: PreTransformProps;
-        transformProps?: TransformProps;
-        postTransformProps?: PostTransformProps;
       }) => input.chartProps,
       input => input.preTransformProps,
+    ],
+    (chartProps, pre = IDENTITY) => pre(chartProps),
+  );
+
+  /**
+   * memoized function so it will not recompute and return previous value
+   * unless one of the input arguments have changed.
+   */
+  transformSelector = createSelector(
+    [
+      (input: { chartProps: ChartProps; transformProps?: TransformProps }) =>
+        input.chartProps,
       input => input.transformProps,
+    ],
+    (preprocessedChartProps, transform = IDENTITY) =>
+      transform(preprocessedChartProps),
+  );
+
+  /**
+   * memoized function so it will not recompute and return previous value
+   * unless one of the input arguments have changed.
+   */
+  postSelector = createSelector(
+    [
+      (input: {
+        chartProps: ChartProps;
+        postTransformProps?: PostTransformProps;
+      }) => input.chartProps,
       input => input.postTransformProps,
     ],
-    (chartProps, pre = IDENTITY, transform = IDENTITY, post = IDENTITY) =>
-      post(transform(pre(chartProps))),
+    (transformedChartProps, post = IDENTITY) => post(transformedChartProps),
   );
 
+  /**
+   * Using each memoized function to retrieve the computed chartProps
+   */
+  processChartProps = ({
+    chartProps,
+    preTransformProps,
+    transformProps,
+    postTransformProps,
+  }: {
+    chartProps: ChartProps;
+    preTransformProps?: PreTransformProps;
+    transformProps?: TransformProps;
+    postTransformProps?: PostTransformProps;
+  }) =>
+    this.postSelector({
+      chartProps: this.transformSelector({
+        chartProps: this.preSelector({ chartProps, preTransformProps }),
+        transformProps,
+      }),
+      postTransformProps,
+    });
+
   /**
    * memoized function so it will not recompute
    * and return previous value
diff --git a/superset-frontend/src/components/Chart/ChartRenderer.jsx 
b/superset-frontend/src/components/Chart/ChartRenderer.jsx
index 5fd83affa1..0916e27405 100644
--- a/superset-frontend/src/components/Chart/ChartRenderer.jsx
+++ b/superset-frontend/src/components/Chart/ChartRenderer.jsx
@@ -167,7 +167,8 @@ class ChartRenderer extends Component {
         nextProps.formData.subcategories !==
           this.props.formData.subcategories ||
         nextProps.cacheBusterProp !== this.props.cacheBusterProp ||
-        nextProps.emitCrossFilters !== this.props.emitCrossFilters
+        nextProps.emitCrossFilters !== this.props.emitCrossFilters ||
+        nextProps.postTransformProps !== this.props.postTransformProps
       );
     }
     return false;
diff --git a/superset-frontend/src/components/Chart/ChartRenderer.test.jsx 
b/superset-frontend/src/components/Chart/ChartRenderer.test.jsx
index 13ba818d3c..22fe9f2daa 100644
--- a/superset-frontend/src/components/Chart/ChartRenderer.test.jsx
+++ b/superset-frontend/src/components/Chart/ChartRenderer.test.jsx
@@ -27,8 +27,10 @@ import { ChartSource } from 'src/types/ChartSource';
 
 jest.mock('@superset-ui/core', () => ({
   ...jest.requireActual('@superset-ui/core'),
-  SuperChart: ({ formData }) => (
-    <div data-test="mock-super-chart">{JSON.stringify(formData)}</div>
+  SuperChart: ({ postTransformProps = x => x, ...props }) => (
+    <div data-test="mock-super-chart">
+      {JSON.stringify(postTransformProps(props).formData)}
+    </div>
   ),
 }));
 
@@ -91,3 +93,20 @@ test('should not render chart context menu if the context 
menu is suppressed for
   );
   expect(queryByTestId('mock-chart-context-menu')).not.toBeInTheDocument();
 });
+
+test('should detect changes in postTransformProps', () => {
+  const postTransformProps = jest.fn(x => x);
+  const initialProps = {
+    ...requiredProps,
+    queriesResponse: [{ data: 'initial' }],
+    chartStatus: 'success',
+  };
+  const { rerender } = render(<ChartRenderer {...initialProps} />);
+  const updatedProps = {
+    ...initialProps,
+    postTransformProps,
+  };
+  expect(postTransformProps).toHaveBeenCalledTimes(0);
+  rerender(<ChartRenderer {...updatedProps} />);
+  expect(postTransformProps).toHaveBeenCalledTimes(1);
+});

Reply via email to