Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FLAG-1145][FLAG-1110] Support legend in bar chart widgets + add legend primary forest loss widget #4859

Merged
merged 10 commits into from
Oct 28, 2024
Merged
2 changes: 1 addition & 1 deletion components/charts/composed-chart/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const XAxisTickWithoutGap = ({ x, y, payload }) => {
</g>
);

/*
/*
Work around to show number 100 in the end of X axis in the chart
since the Data API sends 0 to 90 percent in the tree cover density widget
0 stands to 0%-9% as 90 stands to 90%-99%
Expand Down
3 changes: 3 additions & 0 deletions components/widget/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Widget extends PureComponent {
title: PropTypes.string.isRequired,
type: PropTypes.string,
active: PropTypes.bool,
analysis: PropTypes.bool,
downloadDisabled: PropTypes.bool,
filterSelected: PropTypes.bool,
maxSize: PropTypes.number,
Expand Down Expand Up @@ -85,6 +86,7 @@ class Widget extends PureComponent {
colors,
type,
active,
analysis,
downloadDisabled,
filterSelected,
maxSize,
Expand Down Expand Up @@ -218,6 +220,7 @@ class Widget extends PureComponent {
large={large}
autoHeight={autoHeight}
embed={embed}
analysis={analysis}
location={location}
locationName={locationLabelFull}
active={active}
Expand Down
78 changes: 78 additions & 0 deletions components/widget/components/widget-chart-legend/component.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';

import cx from 'classnames';

class WidgetChartLegend extends PureComponent {
render() {
const { className, vertical = false, data = {} } = this.props;
const { columns = [] } = data;

const anyColumnsHaveTitle = !!columns.find((column) => column.title);

return (
<div className={cx('c-widget-chart-legend', className, { vertical })}>
{columns.map(({ title, items }, columnIdx) => {
return (
<div key={columnIdx} className="c-widget-chart-legend__column">
{title && (
<span className="c-widget-chart-legend__column-title">
{title}
</span>
)}
<ul
className={cx('c-widget-chart-legend__column-items', {
padded: anyColumnsHaveTitle && !title,
})}
>
{items.map(({ label, color, dashline }, titleIdx) => {
return (
<li
key={titleIdx}
className="c-widget-chart-legend__column-item"
>
{dashline ? (
<span
className="c-widget-chart-legend__column-item--dashline"
style={{
borderColor: color,
}}
/>
) : (
<span
className="c-widget-chart-legend__column-item--circle"
style={{
backgroundColor: color,
}}
/>
)}
<p>{label}</p>
</li>
);
})}
</ul>
</div>
);
})}
</div>
);
}
}

WidgetChartLegend.propTypes = {
className: PropTypes.string,
vertical: PropTypes.bool,
data: {
columns: {
title: PropTypes.string,
items: PropTypes.arrayOf(
PropTypes.shape({
color: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
})
),
},
},
};

export default WidgetChartLegend;
3 changes: 3 additions & 0 deletions components/widget/components/widget-chart-legend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Component from './component';

export default Component;
81 changes: 81 additions & 0 deletions components/widget/components/widget-chart-legend/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
@import '~styles/settings.scss';

.c-widget-chart-legend {
display: flex;
margin: 20px 0 0;
width: 100%;
gap: 60px;

&__column {
&-title {
display: block;
font-weight: 500;
font-size: rem(13px);
margin: 0 0 rem(8px) 0;
}

&-items {
display: flex;
flex-direction: column;
gap: 5px;

&.padded {
padding-top: rem(2px);
}
}

&-item {
font-size: rem(12px);
display: flex;

&--circle,
&--dashline {
display: inline-block;
width: rem(12px);
min-width: rem(12px);
min-height: rem(12px);
height: rem(12px);
margin-right: rem(7px);
}

&--circle {
border-radius: 100%;
align-self: flex-start;
margin-top: 1px;
}

&--dashline {
border-radius: 0;
border: none;
border-top: 2px dotted;
align-self: center;
transform: translateY(4px);
}

p {
color: $slate;
font-size: rem(12px);
line-height: 1.4;
}
}
}

&.vertical {
flex-direction: column;
gap: 5px;

.c-widget-chart-legend {
&__column {
&-title {
margin-top: rem(8px);
}

&-items {
&.padded {
padding-top: 0;
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import debounce from 'lodash/debounce';
import ComposedChart from 'components/charts/composed-chart';
import Brush from 'components/charts/brush-chart';
import Legend from 'components/charts/components/chart-legend';
import ChartLegend from '../widget-chart-legend';

class WidgetComposedChart extends Component {
static propTypes = {
analysis: PropTypes.bool,
originalData: PropTypes.array,
data: PropTypes.array,
config: PropTypes.object,
Expand Down Expand Up @@ -63,6 +65,7 @@ class WidgetComposedChart extends Component {

render() {
const {
analysis,
originalData,
data,
config,
Expand All @@ -72,7 +75,7 @@ class WidgetComposedChart extends Component {
barBackground,
toggleSettingsMenu,
} = this.props;
const { brush, legend } = config;
const { brush, legend, chartLegend } = config;
const showLegendSettingsBtn =
settingsConfig &&
settingsConfig.some((conf) => conf.key === 'compareYear');
Expand Down Expand Up @@ -107,6 +110,8 @@ class WidgetComposedChart extends Component {
onBrushEnd={this.handleBrushEnd}
/>
)}

{chartLegend && <ChartLegend data={chartLegend} vertical={analysis} />}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,25 @@ export const parseConfig = createSelector(
})
.reverse()
);

// Example on how to add columns & titles to the Chart Legend
// See: https://gfw.atlassian.net/browse/FLAG-1145
// const chartLegend = {
// columns: [
// {
// items: ['Wildfire', 'Forestry', 'Shifting agriculture']?.map(
// (name) => ({ label: name, color: categoryColors[name] })
// ),
// },
// {
// title: 'Drivers of permanent deforestation',
// items: ['Commodity driven deforestation', 'Urbanization']?.map(
// (name) => ({ label: name, color: categoryColors[name] })
// ),
// },
// ],
// };

const insertIndex = findIndex(tooltip, { key: 'class_Urbanization' });
if (insertIndex > -1) {
tooltip.splice(insertIndex, 0, {
Expand All @@ -181,6 +200,7 @@ export const parseConfig = createSelector(
formatNumber({ num: value, specialSpecifier: '.2s', spaceUnit: true }),
unit: 'tCO2e',
tooltip,
// chartLegend,
};
}
);
Expand Down
3 changes: 3 additions & 0 deletions components/widgets/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Widgets extends PureComponent {
setMapSettings: PropTypes.func.isRequired,
handleClickWidget: PropTypes.func.isRequired,
embed: PropTypes.bool,
analysis: PropTypes.bool,
dashboard: PropTypes.bool,
groupBySubcategory: PropTypes.bool,
modalClosing: PropTypes.bool,
Expand Down Expand Up @@ -60,6 +61,7 @@ class Widgets extends PureComponent {
groupBySubcategory = false,
embed,
dashboard,
analysis,
simple,
modalClosing,
noDataMessage,
Expand Down Expand Up @@ -112,6 +114,7 @@ class Widgets extends PureComponent {
authenticated={authenticated}
active={activeWidget && activeWidget.widget === w.widget}
embed={embed}
analysis={analysis}
dashboard={dashboard}
simple={simple}
location={location}
Expand Down
17 changes: 17 additions & 0 deletions components/widgets/forest-change/tree-loss-primary/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,23 @@ const parseConfig = createSelector([getColors], (colors) => ({
color: colors.primaryForestLoss,
},
],
chartLegend: {
columns: [
{
items: [
{
label: 'Area of tree cover loss within 2001 primary forest extent',
color: colors.primaryForestLoss,
},
{
label: 'Percent of primary forest area in 2001 remaining',
color: colors.primaryForestExtent,
dashline: true,
},
],
},
],
},
}));

export const parseTitle = createSelector(
Expand Down
1 change: 1 addition & 0 deletions pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ import '../components/widget/styles.scss';
import '../components/widgets/styles.scss';
import '../components/widget/components/widget-alert/styles.scss';
import '../components/widget/components/widget-body/styles.scss';
import '../components/widget/components/widget-chart-legend/styles.scss';
import '../components/widget/components/widget-chart-and-list/styles.scss';
import '../components/widget/components/widget-chart-list/styles.scss';
import '../components/widget/components/widget-footer/styles.scss';
Expand Down
Loading