Skip to content
Snippets Groups Projects
Unverified Commit 27ceae1d authored by Emmad Usmani's avatar Emmad Usmani Committed by GitHub
Browse files

stack from top to bottom for area/bar charts (#29005)

parent b2a822cb
No related branches found
No related tags found
No related merge requests found
......@@ -22,6 +22,7 @@ import {
} from "metabase/visualizations/lib/settings/validation";
import { getOrderedSeries } from "metabase/visualizations/lib/series";
import { getAccentColors } from "metabase/lib/colors/groups";
import { isEmpty } from "metabase/lib/validate";
import {
isNumeric,
isDate,
......@@ -349,7 +350,10 @@ export default class LineAreaBarChart extends Component {
settings,
} = this.props;
const orderedSeries = getOrderedSeries(series, settings);
// Note (EmmadUsmani): Stacked charts should be reversed so series are stacked
// from top to bottom, matching the sidebar (metabase#28772).
const isReversed = !isEmpty(settings["stackable.stack_type"]);
const orderedSeries = getOrderedSeries(series, settings, isReversed);
const {
title,
......@@ -391,6 +395,7 @@ export default class LineAreaBarChart extends Component {
onHoverChange={onHoverChange}
onRemoveSeries={!hasBreakout ? onRemoveSeries : undefined}
onSelectSeries={this.handleSelectSeries}
isReversed={isReversed}
>
<CardRenderer
{...this.props}
......
import React, { useCallback, useRef, useState } from "react";
import PropTypes from "prop-types";
import { t } from "ttag";
import _ from "underscore";
import Popover from "metabase/components/Popover";
import {
LegendLink,
......@@ -25,19 +27,21 @@ const propTypes = {
onHoverChange: PropTypes.func,
onSelectSeries: PropTypes.func,
onRemoveSeries: PropTypes.func,
isReversed: PropTypes.bool,
};
const Legend = ({
className,
labels,
colors,
labels: originalLabels,
colors: originalColors,
hovered,
visibleIndex = 0,
visibleLength = labels.length,
visibleLength = originalLabels.length,
isVertical,
onHoverChange,
onSelectSeries,
onRemoveSeries,
isReversed,
}) => {
const targetRef = useRef();
const [isOpened, setIsOpened] = useState(null);
......@@ -53,6 +57,13 @@ const Legend = ({
setMaxWidth(0);
}, []);
const labels = isReversed
? _.clone(originalLabels).reverse()
: originalLabels;
const colors = isReversed
? _.clone(originalColors).reverse()
: originalColors;
const overflowIndex = visibleIndex + visibleLength;
const visibleLabels = labels.slice(visibleIndex, overflowIndex);
const overflowLength = labels.length - overflowIndex;
......@@ -60,14 +71,17 @@ const Legend = ({
return (
<LegendRoot className={className} isVertical={isVertical}>
{visibleLabels.map((label, index) => {
const itemIndex = index + visibleIndex;
const localIndex = index + visibleIndex;
const itemIndex = isReversed
? labels.length - 1 - localIndex
: localIndex;
return (
<LegendItem
key={itemIndex}
label={label}
index={itemIndex}
color={colors[itemIndex % colors.length]}
color={colors[localIndex % colors.length]}
isMuted={hovered && itemIndex !== hovered.index}
isVertical={isVertical}
onHoverChange={onHoverChange}
......@@ -94,8 +108,8 @@ const Legend = ({
>
<LegendPopoverContainer style={{ maxWidth }}>
<Legend
labels={labels}
colors={colors}
labels={originalLabels}
colors={originalColors}
hovered={hovered}
visibleIndex={overflowIndex}
visibleLength={overflowLength}
......@@ -103,6 +117,7 @@ const Legend = ({
onHoverChange={onHoverChange}
onSelectSeries={onSelectSeries}
onRemoveSeries={onRemoveSeries}
isReversed={isReversed}
/>
</LegendPopoverContainer>
</Popover>
......
......@@ -31,6 +31,7 @@ const propTypes = {
onHoverChange: PropTypes.func,
onSelectSeries: PropTypes.func,
onRemoveSeries: PropTypes.func,
isReversed: PropTypes.bool,
};
const LegendLayout = ({
......@@ -48,6 +49,7 @@ const LegendLayout = ({
onHoverChange,
onSelectSeries,
onRemoveSeries,
isReversed,
}) => {
const itemHeight = !isFullscreen ? MIN_ITEM_HEIGHT : MIN_ITEM_HEIGHT_LARGE;
const maxXItems = Math.floor(width / MIN_ITEM_WIDTH);
......@@ -76,6 +78,7 @@ const LegendLayout = ({
onHoverChange={onHoverChange}
onSelectSeries={onSelectSeries}
onRemoveSeries={onRemoveSeries}
isReversed={isReversed}
/>
{!isVertical && actionButtons && (
<LegendActions>{actionButtons}</LegendActions>
......
......@@ -19,6 +19,7 @@ export const findSeriesByKey = (series: Series, key: string) => {
export const getOrderedSeries = (
series: Series,
settings: VisualizationSettings,
isReversed?: boolean,
) => {
if (
(settings["graph.dimensions"] &&
......@@ -33,6 +34,10 @@ export const getOrderedSeries = (
.map(orderedItem => findSeriesByKey(series, orderedItem.key))
.filter(isNotNull);
if (isReversed) {
orderedSeries.reverse();
}
if ("_raw" in series) {
const transformedOrderedSeries = [...orderedSeries] as TransformedSeries;
transformedOrderedSeries._raw = series._raw;
......
......@@ -62,5 +62,19 @@ describe("series utils", () => {
const orderedSeries = getOrderedSeries(transformedSeries, settings);
expect(orderedSeries).toHaveProperty("_raw");
});
it("should reverse the order of a series when `isReversed` is `true`", () => {
const { series, settings } = setupSeries([
{ name: "foo" },
{ name: "bar" },
{ name: "baz" },
]);
const stackedOrderedSeries = getOrderedSeries(series, settings, true);
expect(stackedOrderedSeries).toHaveLength(3);
expect(stackedOrderedSeries[0].card.name).toBe("baz");
expect(stackedOrderedSeries[1].card.name).toBe("bar");
expect(stackedOrderedSeries[2].card.name).toBe("foo");
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment