rusackas commented on code in PR #44550:
URL: https://github.com/apache/superset/pull/44550#discussion_r4320737512


##########
superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts:
##########
@@ -439,6 +442,15 @@ export default function transformProps(
     ) {
       derivedComparisonSeries.add(entry[0]);
       entry[1].shift();
+    } else if (
+      entry[1].length > groupBy.length &&
+      Array.isArray(timeCompare) &&
+      timeCompare.some(offset => entry[1][0].endsWith(`, ${offset}`))

Review Comment:
   @sadpandajoe good catch. The check now matches only the `<metric>, <offset>` 
pairs built from the displayed and sort-only metrics, so a base entry like 
`sales, 1 year ago` is no longer treated as derived. I did not add a dedicated 
regression for that label shape.



##########
superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts:
##########
@@ -4303,6 +4303,59 @@ test('tooltip does not apply a metric currency format to 
a grouped Percentage ti
   expect(result).not.toContain('$ 0.25');
 });
 
+test('tooltip does not apply a metric currency format to a Percentage 
comparison labeled `<metric>, <offset>`', () => {
+  // A hidden sort-only metric makes renameOperator keep the metric prefix on 
the
+  // comparison column, so the label_map entry leads with `<metric>, <offset>`.
+  const chartProps = createTestChartProps({
+    formData: {
+      metric: 'revenue',
+      metrics: ['revenue'],
+      groupby: ['region'],
+      richTooltip: true,
+      time_compare: ['1 year ago'],

Review Comment:
   @sadpandajoe you are right, the fixture now sets `timeCompare` alongside 
`time_compare`. I could not run jest locally here, so the 
red-without-the-branch check is unconfirmed; CI will show it.



##########
superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/xAxisSortWithDimensions.test.ts:
##########
@@ -0,0 +1,774 @@
+/**
+ * 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 {
+  ChartDataResponseResult,
+  DataRecord,
+  QueryFormMetric,
+} from '@superset-ui/core';
+import { SortSeriesType } from '@superset-ui/chart-controls';
+import transformProps from '../../src/Timeseries/transformProps';
+import { DEFAULT_FORM_DATA } from '../../src/Timeseries/constants';
+import {
+  EchartsTimeseriesChartProps,
+  EchartsTimeseriesFormData,
+  EchartsTimeseriesSeriesType,
+} from '../../src/Timeseries/types';
+import { createEchartsTimeseriesTestChartProps } from '../helpers';
+
+// A bar chart of COUNT(*) per genre, split by platform, with the axis sorted
+// by a metric that is neither the value metric nor a dimension (#34352).
+// The rows are what the backend returns for that query: the value metric's
+// columns lose their metric label (`truncate_metric`), the sort-only metric
+// keeps it, and `label_map` records the structure behind each column.
+const naSales: QueryFormMetric = {
+  expressionType: 'SIMPLE',
+  aggregate: 'SUM',
+  column: { column_name: 'na_sales' },
+  label: 'SUM(na_sales)',
+};
+
+const rows: DataRecord[] = [
+  {
+    genre: 'Action',
+    PS4: 30,
+    XOne: 20,
+    'SUM(na_sales), PS4': 5,
+    'SUM(na_sales), XOne': 3,
+  },
+  {
+    genre: 'Puzzle',
+    PS4: 5,
+    XOne: 5,
+    'SUM(na_sales), PS4': 20,
+    'SUM(na_sales), XOne': 10,
+  },
+  {
+    genre: 'Sports',
+    PS4: 40,
+    XOne: 10,
+    'SUM(na_sales), PS4': 1,
+    'SUM(na_sales), XOne': 1,
+  },
+];
+
+const labelMap = {
+  genre: ['genre'],
+  PS4: ['PS4'],
+  XOne: ['XOne'],
+  'SUM(na_sales), PS4': ['SUM(na_sales)', 'PS4'],
+  'SUM(na_sales), XOne': ['SUM(na_sales)', 'XOne'],
+};
+
+const queriesData = [
+  {
+    annotation_data: null,
+    cache_key: null,
+    cache_timeout: null,
+    cached_dttm: null,
+    queried_dttm: null,
+    data: rows,
+    colnames: [],
+    coltypes: [],
+    error: null,
+    is_cached: false,
+    query: '',
+    rowcount: rows.length,
+    sql_rowcount: rows.length,
+    stacktrace: null,
+    status: 'success',
+    from_dttm: null,
+    to_dttm: null,
+    label_map: labelMap,
+  } as unknown as ChartDataResponseResult,
+];
+
+type Series = {
+  name: string;
+  data: [string, number][];
+  label: {
+    formatter: (params: {
+      value: [string, number];
+      dataIndex: number;
+      seriesIndex: number;
+    }) => string;
+  };
+};
+
+// transformProps reads camelCase keys from `formData` and snake_case keys
+// from `rawFormData`; the test helper hands the same object to both.
+function transform(
+  overrides: Record<string, unknown>,
+  verboseMap = {},
+  data = rows,
+): {
+  series: Series[];
+  seriesNames: string[];
+  axisOrder: string[];
+  stackedTotals: string[];
+} {
+  const chartProps = createEchartsTimeseriesTestChartProps<
+    EchartsTimeseriesFormData,
+    EchartsTimeseriesChartProps
+  >({
+    defaultFormData: DEFAULT_FORM_DATA,
+    defaultVizType: 'echarts_timeseries_bar',
+    formData: {
+      colorScheme: 'bnbColors',
+      seriesType: EchartsTimeseriesSeriesType.Bar,
+      x_axis: 'genre',
+      xAxis: 'genre',
+      metrics: ['count'],
+      groupby: ['platform'],
+      timeseries_limit_metric: naSales,
+      truncate_metric: true,
+      stack: true,
+      showValue: true,
+      onlyTotal: true,
+      ...overrides,
+    } as Partial<EchartsTimeseriesFormData>,
+    queriesData:
+      data === rows
+        ? queriesData
+        : [{ ...queriesData[0], data } as ChartDataResponseResult],
+    datasource: { verboseMap },
+  });
+  const transformed = transformProps(chartProps);
+  const series = transformed.echartOptions.series as Series[];
+  return {
+    series,
+    // Legend order is a separate control; only the set of series matters here.
+    seriesNames: series.map(({ name }) => name).sort(),
+    axisOrder: series[0].data.map(([genre]) => genre),
+    // With `onlyTotal` the top series' label carries the stacked total.
+    stackedTotals: series
+      .flatMap((entry, seriesIndex) =>
+        entry.data.map((value, dataIndex) =>
+          entry.label.formatter({ value, dataIndex, seriesIndex }),
+        ),
+      )
+      .filter(label => label !== ''),
+  };
+}
+
+test('sorts the axis by the sort-only metric across its pivoted columns', () 
=> {
+  const { axisOrder, seriesNames, stackedTotals } = transform({
+    x_axis_sort: 'SUM(na_sales)',
+    xAxisSort: 'SUM(na_sales)',
+    x_axis_sort_asc: false,
+    xAxisSortAsc: false,
+  });
+  // SUM(na_sales) totals: Puzzle 30, Action 8, Sports 2. Sorting by the value
+  // metric would put Action/Sports first, sorting by name would put Puzzle
+  // second.
+  expect(axisOrder).toEqual(['Puzzle', 'Action', 'Sports']);
+  expect(seriesNames).toEqual(['PS4', 'XOne']);
+  // COUNT(*) totals only, in the sorted order.
+  expect(stackedTotals).toEqual(['10', '50', '50']);
+});
+
+test('sorts the axis by the sort-only metric ascending', () => {
+  const { axisOrder } = transform({
+    x_axis_sort: 'SUM(na_sales)',
+    xAxisSort: 'SUM(na_sales)',
+    x_axis_sort_asc: true,
+    xAxisSortAsc: true,
+  });
+  expect(axisOrder).toEqual(['Sports', 'Action', 'Puzzle']);
+});
+
+test('sorts by the sort-only metric when it has a verbose name', () => {
+  const { axisOrder, seriesNames } = transform(
+    {
+      x_axis_sort: 'SUM(na_sales)',
+      xAxisSort: 'SUM(na_sales)',
+      x_axis_sort_asc: false,
+      xAxisSortAsc: false,
+    },
+    { 'SUM(na_sales)': 'NA Sales' },
+  );
+  expect(axisOrder).toEqual(['Puzzle', 'Action', 'Sports']);
+  expect(seriesNames).toEqual(['PS4', 'XOne']);
+});
+
+test('sorts the axis by the x-axis column with dimensions set', () => {

Review Comment:
   @sadpandajoe added a grouped case with axis values `a` and `B`, sorted 
ascending by the x-axis column, asserting `['B', 'a']`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to