Skip to content

Commit

Permalink
Pie Widget: Add maxNumber of elements + a sorted by size desc by defa…
Browse files Browse the repository at this point in the history
…ult (#774)
  • Loading branch information
vmilan authored Sep 19, 2023
1 parent e6b9309 commit de8d79c
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Not released

- Pie Widget: Add maxNumber of elements + a sorted by size desc by default [#774](https://github.com/CartoDB/carto-react/pull/774)
- Pie Widget: Add number of selected categories + clear button [#771](https://github.com/CartoDB/carto-react/pull/771)

## 2.2
Expand Down
99 changes: 73 additions & 26 deletions packages/react-ui/src/widgets/PieWidgetUI/PieWidgetUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React, { useMemo, useRef, useEffect, useState, useCallback } from 'react'
import PropTypes from 'prop-types';
import ReactEcharts from '../../custom-components/echarts-for-react';
import { Grid, Link, styled, useTheme } from '@mui/material';
import { disableSerie, setColor } from '../utils/chartUtils';
import { disableSerie, setColor, sortDataDescending } from '../utils/chartUtils';
import { processFormatterRes } from '../utils/formatterUtils';
import PieSkeleton from './PieSkeleton';
import PieSkeleton from './components/PieSkeleton';
import Typography from '../../components/atoms/Typography';

export const OptionsBar = styled(Grid)(({ theme }) => ({
Expand All @@ -26,44 +26,75 @@ function PieWidgetUI({
filterable,
selectedCategories,
onSelectedCategoriesChange,
isLoading
isLoading,
maxItems,
order
}) {
const theme = useTheme();
const [showLabel, setShowLabel] = useState(true);
const [showTooltip, setShowTooltip] = useState(true);
const colorByCategory = useRef({});
const othersCategory = 'Others';

// Reset colorByCategory when colors changes
// Sort data by size if order is ranking, otherwise keep the original order
const orderedData = useMemo(() => {
let orderedCategories = [];

if (order === PieWidgetUI.ORDER_TYPES.RANKING) {
orderedCategories = sortDataDescending(data);
} else {
orderedCategories = [...data];
}

return orderedCategories;
}, [data, order]);

// Limit the number of categories to display, then group the rest into an "Others" category
const groupedData = useMemo(() => {
let categories = [];
let othersValue = 0;

for (const category of orderedData) {
if (categories.length < maxItems) {
categories.push({ ...category });
} else {
othersValue += category.value;
}
}

if (othersValue > 0) {
categories.push({
name: othersCategory,
value: othersValue,
emphasis: { scale: false }
});
}

return categories;
}, [maxItems, orderedData]);

// Add a color to each category
const dataWithColor = useMemo(() => {
return groupedData.map(processDataItem(colorByCategory, colors, theme));
}, [groupedData, colors, theme]);

// Reset colorByCategory when colors and categories change
useEffect(() => {
colorByCategory.current = {};
// Spread colors array to avoid reference problems
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...colors]);

const dataWithColor = useMemo(() => {
return data.map(processDataItem(colorByCategory, colors, theme));
}, [data, colors, theme]);
}, [...colors, dataWithColor]);

// Tooltip
const tooltipOptions = useMemo(
() => ({
show: showTooltip,
showDelay: 100,
transitionDuration: 0.4,
backgroundColor: theme.palette.black[90],
borderColor: 'transparent',
textStyle: { color: theme.palette.common.white },
confine: true,
formatter:
!!tooltipFormatter && ((params) => tooltipFormatter({ ...params, formatter }))
}),
[
showTooltip,
formatter,
theme.palette.common.white,
theme.palette.black,
tooltipFormatter
]
[formatter, theme.palette.common.white, theme.palette.black, tooltipFormatter]
);

// Legend
Expand Down Expand Up @@ -201,7 +232,12 @@ function PieWidgetUI({
(params) => {
if (onSelectedCategoriesChange) {
const newSelectedCategories = [...selectedCategories];
const { name } = data[params.dataIndex];
const { name } = dataWithColor[params.dataIndex];

// Avoid clicking if the category name is "Others"
if (name === othersCategory) {
return;
}

const selectedCategoryIdx = newSelectedCategories.indexOf(name);
if (selectedCategoryIdx === -1) {
Expand All @@ -213,18 +249,16 @@ function PieWidgetUI({
onSelectedCategoriesChange(newSelectedCategories);
}
},
[data, onSelectedCategoriesChange, selectedCategories]
[dataWithColor, onSelectedCategoriesChange, selectedCategories]
);

const onEvents = {
...(filterable && { click: clickEvent }),
mouseover: () => {
setShowLabel(false);
setShowTooltip(true);
},
mouseout: () => {
setShowLabel(true);
setShowTooltip(false);
}
};

Expand Down Expand Up @@ -256,6 +290,15 @@ function PieWidgetUI({
);
}

/**
* Enum for PieWidgetUI order types. 'RANKING' orders the data by value and 'FIXED' keeps the order present in the original data
* @enum {string}
*/
PieWidgetUI.ORDER_TYPES = {
RANKING: 'ranking',
FIXED: 'fixed'
};

PieWidgetUI.defaultProps = {
name: null,
formatter: (v) => v,
Expand All @@ -265,7 +308,9 @@ PieWidgetUI.defaultProps = {
height: '260px',
animation: true,
filterable: true,
selectedCategories: []
selectedCategories: [],
maxItems: 11,
order: PieWidgetUI.ORDER_TYPES.RANKING
};

PieWidgetUI.propTypes = {
Expand All @@ -285,7 +330,9 @@ PieWidgetUI.propTypes = {
filterable: PropTypes.bool,
selectedCategories: PropTypes.array,
onSelectedCategoriesChange: PropTypes.func,
isLoading: PropTypes.bool
isLoading: PropTypes.bool,
maxItems: PropTypes.number,
order: PropTypes.oneOf(Object.values(PieWidgetUI.ORDER_TYPES))
};

export default PieWidgetUI;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Box, Skeleton, styled } from '@mui/material';
import { SKELETON_HEIGHT, SkeletonMask } from '../SkeletonWidgets';
import { SKELETON_HEIGHT, SkeletonMask } from '../../SkeletonWidgets';

const GUTTER = 16;
const LEYEND_SIZE = 64;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { useTheme } from '@mui/material';
import { lighten } from '@mui/material/styles';
import EchartsWrapper from '../../custom-components/echarts-for-react';
import PieSkeleton from '../PieWidgetUI/PieSkeleton';
import PieSkeleton from '../PieWidgetUI/components/PieSkeleton';

/**
* process incoming data to assign labels, colors and selected / unselected styles
Expand Down
11 changes: 11 additions & 0 deletions packages/react-ui/src/widgets/utils/chartUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,14 @@ export function applyChartFilter(serie, clickedSerieIndex, theme) {

return serie;
}

// Sort data from largest to smallest
export function sortDataDescending(data) {
const sortedData = [...data];

sortedData.sort((a, b) => {
return b.value - a.value;
});

return sortedData;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import { Label, ThinContainer } from '../../utils/storyStyles';
const options = {
title: 'Widgets/PieWidgetUI',
component: PieWidgetUI,
argTypes: {
order: {
defaultValue: 'ranking',
control: {
type: 'select',
options: ['ranking', 'fixed']
}
}
},
parameters: {
docs: {
source: {
Expand Down Expand Up @@ -56,8 +65,7 @@ const CustomColorsProps = {
{ name: 'Elephants', value: 100 },
{ name: 'Mamouths', value: 120 },
{ name: 'Torttles', value: 150 },
{ name: 'Snakes', value: 90 },
{ name: 'others', value: 200 }
{ name: 'Snakes', value: 90 }
],
colors: [
'#855C75',
Expand All @@ -70,8 +78,7 @@ const CustomColorsProps = {
'#9C9C5E',
'#A06177',
'#8C785D',
'#467378',
'#7C7C7C'
'#467378'
]
};
CustomColors.args = CustomColorsProps;
Expand All @@ -90,6 +97,27 @@ const SelectedCategoriesProps = {
};
SelectedCategories.args = SelectedCategoriesProps;

export const CollapseMoreThan12Categories = Template.bind({});
const CollapseCategoriesProps = {
data: [
{ name: 'Dogs', value: 100 },
{ name: 'Cats', value: 120 },
{ name: 'Rabbits', value: 150 },
{ name: 'Canaries', value: 90 },
{ name: 'Passerines', value: 200 },
{ name: 'Elephants', value: 100 },
{ name: 'Mamouths', value: 120 },
{ name: 'Torttles', value: 150 },
{ name: 'Spiders', value: 80 },
{ name: 'Frogs', value: 30 },
{ name: 'Pigeons', value: 150 },
{ name: 'Owls', value: 90 },
{ name: 'Snakes', value: 80 },
{ name: 'Birds', value: 220 }
]
};
CollapseMoreThan12Categories.args = CollapseCategoriesProps;

export const Loading = LoadingTemplate.bind({});
const LoadingProps = { data: dataDefault, isLoading: true };
Loading.args = LoadingProps;

0 comments on commit de8d79c

Please sign in to comment.