From 71a9d0d403e122a0c8115f829883151fdcd1d4f1 Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Mon, 6 Mar 2023 11:25:52 +0200 Subject: [PATCH] fix(plugin-chart-echarts): render horizontal categories from top (#23273) --- .../src/Timeseries/transformProps.ts | 1 + .../test/Timeseries/transformProps.test.ts | 38 ++++++ ...b5b83_invert_horizontal_bar_chart_order.py | 126 ++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 superset/migrations/versions/2023-03-05_10-06_d0ac08bb5b83_invert_horizontal_bar_chart_order.py 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 eadded44a9..8e198cc5f2 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts @@ -370,6 +370,7 @@ export default function transformProps( if (isHorizontal) { [xAxis, yAxis] = [yAxis, xAxis]; [padding.bottom, padding.left] = [padding.left, padding.bottom]; + yAxis.inverse = true; } const echartOptions: EChartsCoreOption = { diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts index df48354a81..b185c3a767 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformProps.test.ts @@ -87,6 +87,44 @@ describe('EchartsTimeseries transformProps', () => { ); }); + it('should transform chart props for horizontal viz', () => { + const chartProps = new ChartProps({ + ...chartPropsConfig, + formData: { + ...formData, + orientation: 'horizontal', + }, + }); + expect(transformProps(chartProps as EchartsTimeseriesChartProps)).toEqual( + expect.objectContaining({ + width: 800, + height: 600, + echartOptions: expect.objectContaining({ + legend: expect.objectContaining({ + data: ['San Francisco', 'New York'], + }), + series: expect.arrayContaining([ + expect.objectContaining({ + data: [ + [1, 599616000000], + [3, 599916000000], + ], + name: 'San Francisco', + }), + expect.objectContaining({ + data: [ + [2, 599616000000], + [4, 599916000000], + ], + name: 'New York', + }), + ]), + yAxis: expect.objectContaining({ inverse: true }), + }), + }), + ); + }); + it('should add a formula annotation to viz', () => { const formula: FormulaAnnotationLayer = { name: 'My Formula', diff --git a/superset/migrations/versions/2023-03-05_10-06_d0ac08bb5b83_invert_horizontal_bar_chart_order.py b/superset/migrations/versions/2023-03-05_10-06_d0ac08bb5b83_invert_horizontal_bar_chart_order.py new file mode 100644 index 0000000000..6003c70d69 --- /dev/null +++ b/superset/migrations/versions/2023-03-05_10-06_d0ac08bb5b83_invert_horizontal_bar_chart_order.py @@ -0,0 +1,126 @@ +# 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. +"""invert_horizontal_bar_chart_order + +Revision ID: d0ac08bb5b83 +Revises: c0a3ea245b61 +Create Date: 2023-03-05 10:06:23.250310 + +""" + +# revision identifiers, used by Alembic. +revision = "d0ac08bb5b83" +down_revision = "c0a3ea245b61" + +import json + +import sqlalchemy as sa +from alembic import op +from sqlalchemy import and_, Column, Integer, String, Text +from sqlalchemy.ext.declarative import declarative_base + +from superset import db + +Base = declarative_base() + +ORIENTATION = "horizontal" +CHART_TYPE = "echarts_timeseries_bar" + + +class Slice(Base): + """Declarative class to do query in upgrade""" + + __tablename__ = "slices" + id = Column(Integer, primary_key=True) + viz_type = Column(String(250)) + params = Column(Text) + + +def upgrade(): + bind = op.get_bind() + session = db.Session(bind=bind) + + slices = ( + session.query(Slice) + .filter( + and_( + Slice.viz_type == CHART_TYPE, + Slice.params.like("%x_axis_sort%"), + Slice.params.like("%x_axis_sort_asc%"), + Slice.params.like(f"%{ORIENTATION}%"), + ) + ) + .all() + ) + changes = 0 + for slc in slices: + try: + params = json.loads(slc.params) + orientation = params.get("orientation") + x_axis_sort = params.get("x_axis_sort") + x_axis_sort_asc = params.get("x_axis_sort_asc", None) + if orientation == ORIENTATION and x_axis_sort: + changes += 1 + params["x_axis_sort_asc"] = not x_axis_sort_asc + slc.params = json.dumps(params, sort_keys=True) + except Exception as e: + print(e) + print(f"Parsing params for slice {slc.id} failed.") + pass + + session.commit() + session.close() + if changes: + print(f"Updated {changes} bar chart sort orders.") + + +def downgrade(): + bind = op.get_bind() + session = db.Session(bind=bind) + + slices = ( + session.query(Slice) + .filter( + and_( + Slice.viz_type == CHART_TYPE, + Slice.params.like("%x_axis_sort%"), + Slice.params.like("%x_axis_sort_asc%"), + Slice.params.like(f"%{ORIENTATION}%"), + ) + ) + .all() + ) + changes = 0 + for slc in slices: + try: + params = json.loads(slc.params) + orientation = params.get("orientation") + x_axis_sort = params.get("x_axis_sort") + x_axis_sort_asc = params.pop("x_axis_sort_asc", None) + if orientation == ORIENTATION and x_axis_sort: + changes += 1 + params["x_axis_sort_asc"] = not x_axis_sort_asc + slc.params = json.dumps(params, sort_keys=True) + except Exception as e: + print(e) + print(f"Parsing params for slice {slc.id} failed.") + pass + + session.commit() + session.close() + if changes: + print(f"Updated {changes} bar chart sort orders.")