Map Visualization TODO:
diff --git a/packages/2019-housing/src/components/HomeOwnershipRates/HomeOwnershipRatesVisualization.js b/packages/2019-housing/src/components/HomeOwnershipRates/HomeOwnershipRatesVisualization.js
index a9a6cd043..c5e73d5b9 100644
--- a/packages/2019-housing/src/components/HomeOwnershipRates/HomeOwnershipRatesVisualization.js
+++ b/packages/2019-housing/src/components/HomeOwnershipRates/HomeOwnershipRatesVisualization.js
@@ -33,6 +33,7 @@ const HomeOwnershipRatesVisualization = ({ data }) => {
yLabel="Home Ownership Rate"
xNumberFormatter={x => civicFormat.year(x)}
yNumberFormatter={y => civicFormat.decimalToPercent(y)}
+ protect
/>
)
);
diff --git a/packages/2019-housing/src/components/HousingDisplacement/HousingDisplacementVisualization.js b/packages/2019-housing/src/components/HousingDisplacement/HousingDisplacementVisualization.js
index c861d6f9b..bd3d153f8 100644
--- a/packages/2019-housing/src/components/HousingDisplacement/HousingDisplacementVisualization.js
+++ b/packages/2019-housing/src/components/HousingDisplacement/HousingDisplacementVisualization.js
@@ -82,6 +82,7 @@ const HousingDisplacementVisualization = ({ isLoading, data }) => {
xLabel={xLabel}
yLabel={yLabel}
xNumberFormatter={tick => tick.toString()}
+ protect
/>
@@ -94,6 +95,7 @@ const HousingDisplacementVisualization = ({ isLoading, data }) => {
xLabel={xLabel}
yLabel={yLabel}
xNumberFormatter={tick => tick.toString()}
+ protect
/>
diff --git a/packages/2019-template/src/components/DemoCard/DemoCardVisualization.js b/packages/2019-template/src/components/DemoCard/DemoCardVisualization.js
index 7460b39a0..86a91184e 100644
--- a/packages/2019-template/src/components/DemoCard/DemoCardVisualization.js
+++ b/packages/2019-template/src/components/DemoCard/DemoCardVisualization.js
@@ -76,19 +76,18 @@ export default function DemoCardVisualization({ isLoading, data }) {
Late Gentrification
- {!isLoading && data[dataType] && (
-
- )}
+
);
}
diff --git a/packages/component-library/src/BarChart/BarChart.js b/packages/component-library/src/BarChart/BarChart.js
index bb273b922..97d7eaec3 100644
--- a/packages/component-library/src/BarChart/BarChart.js
+++ b/packages/component-library/src/BarChart/BarChart.js
@@ -34,7 +34,8 @@ const BarChart = ({
error,
theme
}) => {
- const chartDomain = domain || getDefaultDomain(data, dataKey, dataValue);
+ const safeData = data && data.length ? data : [{}];
+ const chartDomain = domain || getDefaultDomain(safeData, dataKey, dataValue);
return (
@@ -44,7 +45,7 @@ const BarChart = ({
loading={loading}
error={error}
>
-
+
}
- data={data.map(d => ({
+ data={safeData.map(d => ({
dataKey: d[dataKey],
dataValue: d[dataValue],
label: `${xLabel}: ${xNumberFormatter(
diff --git a/packages/component-library/src/ChartContainer/ChartContainer.js b/packages/component-library/src/ChartContainer/ChartContainer.js
index b2f31016b..5d0cd39ab 100644
--- a/packages/component-library/src/ChartContainer/ChartContainer.js
+++ b/packages/component-library/src/ChartContainer/ChartContainer.js
@@ -1,14 +1,9 @@
import PropTypes from "prop-types";
/** @jsx jsx */
import { jsx, css } from "@emotion/core";
-
+import { BrandColors } from "../index";
import ChartTitle from "../ChartTitle";
-
-const chartLoading = css`
- text-align: center;
- background: #eee;
- height: 100%;
-`;
+import Logo from "../Logo/Logo";
const chartError = css`
text-align: center;
@@ -16,6 +11,8 @@ const chartError = css`
height: 100%;
`;
+const defaultVictoryAspectRatio = 650 / 350;
+
/**
ChartContainer renders titles, subtitles, and provides some default styling for charts.
It is designed to render a VictoryChart as children.
@@ -29,7 +26,8 @@ const ChartContainer = ({
loading,
subtitle,
children,
- className
+ className,
+ aspectRatio
}) => {
const figureWrapper = css`
margin: 0;
@@ -40,21 +38,42 @@ const ChartContainer = ({
width: 100%;
${className};
`;
+ const fullHeight = css`
+ position: relative;
+ padding-top: ${100 / aspectRatio}%;
+ height: 100%;
+ `;
+
+ const skeletonStyle = css`
+ margin: 0 auto;
+ width: 100%;
+ max-width: 900px;
+ height: calc(100vw / ${aspectRatio});
+ max-height: ${900 / aspectRatio}px;
+ background-color: ${BrandColors.subdued.hex};
+ display: grid;
+ justify-items: center;
+ align-items: center;
+ `;
let content = (
-
+
+
+
);
- if (loading) {
- content = Loading...
;
+ if (!loading) {
+ content = {children}
;
} else if (error) {
- content = {error}
;
+ content = {error}
;
}
- return content;
+ return (
+
+ );
};
ChartContainer.propTypes = {
@@ -63,9 +82,12 @@ ChartContainer.propTypes = {
loading: PropTypes.bool,
children: PropTypes.node,
subtitle: PropTypes.string,
- className: PropTypes.string
+ className: PropTypes.string,
+ aspectRatio: PropTypes.number
};
-ChartContainer.defaultProps = {};
+ChartContainer.defaultProps = {
+ aspectRatio: defaultVictoryAspectRatio
+};
export default ChartContainer;
diff --git a/packages/component-library/src/HorizontalBarChart/HorizontalBarChart.js b/packages/component-library/src/HorizontalBarChart/HorizontalBarChart.js
index 725d3d37f..26386171e 100644
--- a/packages/component-library/src/HorizontalBarChart/HorizontalBarChart.js
+++ b/packages/component-library/src/HorizontalBarChart/HorizontalBarChart.js
@@ -21,6 +21,7 @@ import {
} from "../utils/chartHelpers";
import groupByKey from "../utils/groupByKey";
import DataChecker from "../utils/DataChecker";
+import protectData from "../utils/protectData";
import { VictoryTheme } from "../_Themes/index";
const HorizontalBarChart = ({
@@ -43,26 +44,35 @@ const HorizontalBarChart = ({
dataSeriesKey,
dataSeriesLabel,
legendComponent,
- theme
+ theme,
+ protect
}) => {
+ const safeData =
+ // eslint-disable-next-line no-nested-ternary
+ data && data.length
+ ? protect
+ ? protectData(data, { dataLabel, dataSeriesKey })
+ : data
+ : [{}];
+
const groupedDataIfStacked = () => {
if (stacked) {
if (hundredPercentData) {
return transformDatato100(
- groupByKey(data, dataSeriesKey, dataLabel),
+ groupByKey(safeData, dataSeriesKey, dataLabel),
dataLabel,
dataValue
);
}
- return groupByKey(data, dataSeriesKey, dataLabel);
+ return groupByKey(safeData, dataSeriesKey, dataLabel);
}
- return data;
+ return safeData;
};
const groupedData = groupedDataIfStacked();
const barData =
sortOrder && sortOrder.length
- ? data
- : data.map((d, index) => {
+ ? safeData
+ : safeData.map((d, index) => {
return { ...d, defaultSort: index + 1 };
});
const sortOrderKey =
@@ -73,13 +83,13 @@ const HorizontalBarChart = ({
const barHeight = theme.bar.style.data.width;
const spaceHeight = theme.bar.style.data.padding * 2;
const additionalHeight = padding.bottom + padding.top;
- const minValue = Math.min(0, ...data.map(d => d[dataValue]));
+ const minValue = Math.min(0, ...safeData.map(d => d[dataValue]));
- const bars = stacked ? groupedData.length : data.length;
+ const bars = stacked ? groupedData.length : safeData.length;
const spaces = bars - 1;
const dataHeight = bars * barHeight + spaces * spaceHeight;
const dataSeriesLabels = dataSeriesKey
- ? dataSeriesLabel || getDefaultDataSeriesLabels(data, dataSeriesKey)
+ ? dataSeriesLabel || getDefaultDataSeriesLabels(safeData, dataSeriesKey)
: null;
const legendData =
dataSeriesLabels && dataSeriesLabels.length
@@ -100,8 +110,9 @@ const HorizontalBarChart = ({
subtitle={subtitle}
loading={loading}
error={error}
+ aspectRatio={650 / (dataHeight + additionalHeight)}
>
-
+
{legendData &&
(legendComponent ? (
legendComponent(legendData)
@@ -287,7 +298,8 @@ HorizontalBarChart.propTypes = {
dataSeriesKey: PropTypes.string,
dataSeriesLabel: PropTypes.shape({}),
legendComponent: PropTypes.func,
- theme: PropTypes.shape({})
+ theme: PropTypes.shape({}),
+ protect: PropTypes.bool
};
HorizontalBarChart.defaultProps = {
@@ -308,7 +320,8 @@ HorizontalBarChart.defaultProps = {
dataSeriesKey: null,
dataSeriesLabel: {},
legendComponent: null,
- theme: VictoryTheme
+ theme: VictoryTheme,
+ protect: false
};
export default HorizontalBarChart;
diff --git a/packages/component-library/src/HorizontalBarChart/HorizontalBarChart.test.js b/packages/component-library/src/HorizontalBarChart/HorizontalBarChart.test.js
index e52a31057..fc8fe4098 100644
--- a/packages/component-library/src/HorizontalBarChart/HorizontalBarChart.test.js
+++ b/packages/component-library/src/HorizontalBarChart/HorizontalBarChart.test.js
@@ -18,6 +18,21 @@ const updatedSimpleData = [
{ x: 500, y: "bat" }
];
+const simpleColorData = [
+ { x: 100, y: "black" },
+ { x: 200, y: "white" },
+ { x: 300, y: "red" },
+ { x: 400, y: "rat" }
+];
+
+const updatedSimpleColorData = [
+ { x: 100, y: "black" },
+ { x: 200, y: "white" },
+ { x: 300, y: "red" },
+ { x: 400, y: "rat" },
+ { x: 500, y: "bat" }
+];
+
const simpleDataDomain = { x: [0, 400], y: [0, 4] };
const sampleUnstructuredData = [
@@ -148,6 +163,7 @@ describe("HorizontalBarChart", () => {
const wrapper = shallow();
const dataProp = wrapper
.find("VictoryBar")
+
.first()
.prop("data");
expect(dataProp).to.have.length(4);
@@ -166,4 +182,45 @@ describe("HorizontalBarChart", () => {
expect(xProp).to.eql("sortOrder");
expect(yProp).to.eql("dataValue");
});
+
+ // Tests for https://github.com/FormidableLabs/victory/issues/928 workaround
+
+ it("renders a HorizontalBarChart correctly with sample color data **invisible whitespace warning, see protectData util**", () => {
+ const c = ""; // U+200B (zero-width space character)
+ const wrapper = shallow(
+
+ );
+ expect(wrapper.find({ title: "Horizontal Bar Chart" }).length).to.eql(1);
+ expect(wrapper.find({ title: "Horizontal Bar Chart" }).props().data).to.eql(
+ // invisible whitespace character injected in label before :
+ [
+ { sortOrder: 1, dataValue: 100, label: `black${c}: 100` },
+ { sortOrder: 2, dataValue: 200, label: `white${c}: 200` },
+ { sortOrder: 3, dataValue: 300, label: `red${c}: 300` },
+ { sortOrder: 4, dataValue: 400, label: `rat${c}: 400` }
+ ]
+ );
+ });
+
+ it("renders an updated HorizontalBarChart when passed new data **invisible whitespace warning, see protectData util**", () => {
+ const c = ""; // U+200B (zero-width space character)
+ const wrapper = shallow(
+
+ );
+ expect(wrapper.find({ title: "Horizontal Bar Chart" }).length).to.eql(1);
+ wrapper.setProps({ data: updatedSimpleColorData });
+
+ expect(wrapper.find({ title: "Horizontal Bar Chart" }).props().data).to.eql(
+ // invisible whitespace character injected in label before :
+ [
+ { sortOrder: 1, dataValue: 100, label: `black${c}: 100` },
+ { sortOrder: 2, dataValue: 200, label: `white${c}: 200` },
+ { sortOrder: 3, dataValue: 300, label: `red${c}: 300` },
+ { sortOrder: 4, dataValue: 400, label: `rat${c}: 400` },
+ { sortOrder: 5, dataValue: 500, label: `bat${c}: 500` }
+ ]
+ );
+ });
+
+ // End tests for https://github.com/FormidableLabs/victory/issues/928 workaround
});
diff --git a/packages/component-library/src/LineChart/LineChart.js b/packages/component-library/src/LineChart/LineChart.js
index 9f6d360c4..2c7750f6f 100644
--- a/packages/component-library/src/LineChart/LineChart.js
+++ b/packages/component-library/src/LineChart/LineChart.js
@@ -18,6 +18,7 @@ import { VictoryTheme } from "../_Themes/index";
import ChartContainer from "../ChartContainer";
import SimpleLegend from "../SimpleLegend";
import civicFormat from "../utils/civicFormat";
+import protectData from "../utils/protectData";
import DataChecker from "../utils/DataChecker";
import {
chartEvents,
@@ -45,12 +46,28 @@ const LineChart = ({
xNumberFormatter,
yNumberFormatter,
legendComponent,
- theme
+ loading,
+ theme,
+ protect
}) => {
- const chartDomain = domain || getDefaultDomain(data, dataKey, dataValue);
+ const safeData =
+ // eslint-disable-next-line no-nested-ternary
+ data && data.length
+ ? protect
+ ? protectData(data, { dataSeries, dataSeriesLabel })
+ : data
+ : [{}];
+ const safeDataSeriesLabel =
+ // eslint-disable-next-line no-nested-ternary
+ dataSeriesLabel && dataSeriesLabel.length
+ ? protect
+ ? protectData(dataSeriesLabel, { x: "category", y: "label" })
+ : dataSeriesLabel
+ : null;
+ const chartDomain = domain || getDefaultDomain(safeData, dataKey, dataValue);
const dataSeriesLabels = dataSeries
- ? dataSeriesLabel || getDefaultDataSeriesLabels(data, dataSeries)
+ ? safeDataSeriesLabel || getDefaultDataSeriesLabels(safeData, dataSeries)
: null;
const scatterPlotStyle =
@@ -61,11 +78,16 @@ const LineChart = ({
? dataSeriesLabels.map(series => ({ name: series.label }))
: null;
- const lineData = dataSeries ? groupBy(data, dataSeries) : { category: data };
+ const lineData = dataSeries
+ ? groupBy(safeData, dataSeries)
+ : { category: safeData };
+ // TODO: if you have line data with categories that don't have the same length,
+ // then the below won't work.
const lines = lineData
? Object.keys(lineData).map((category, index) => (
({
dataKey: d[dataKey],
@@ -87,8 +109,8 @@ const LineChart = ({
return (
-
-
+
+
{legendData &&
(legendComponent ? (
legendComponent(legendData, theme)
@@ -140,7 +162,7 @@ const LineChart = ({
{lines}
({
+ data={safeData.map(d => ({
dataKey: d[dataKey],
dataValue: d[dataValue],
label: `${dataKeyLabel || xLabel}: ${xNumberFormatter(
@@ -200,7 +222,9 @@ LineChart.propTypes = {
xNumberFormatter: PropTypes.func,
yNumberFormatter: PropTypes.func,
legendComponent: PropTypes.func,
- theme: PropTypes.shape({})
+ theme: PropTypes.shape({}),
+ loading: PropTypes.bool,
+ protect: PropTypes.bool
};
LineChart.defaultProps = {
@@ -221,7 +245,9 @@ LineChart.defaultProps = {
xNumberFormatter: civicFormat.numeric,
yNumberFormatter: civicFormat.numeric,
legendComponent: null,
- theme: VictoryTheme
+ theme: VictoryTheme,
+ loading: null,
+ protect: false
};
export default LineChart;
diff --git a/packages/component-library/src/LineChart/LineChart.test.js b/packages/component-library/src/LineChart/LineChart.test.js
index 55c2e6f27..8c35add3d 100644
--- a/packages/component-library/src/LineChart/LineChart.test.js
+++ b/packages/component-library/src/LineChart/LineChart.test.js
@@ -4,23 +4,146 @@ import { shallow } from "enzyme";
import LineChart from "./LineChart";
describe("LineChart", () => {
- const data = [
- { year: 2015, population: 1 },
- { year: 2016, population: 2 },
- { year: 2017, population: 3 }
+ const simpleData = [{ x: 100, y: 10 }, { x: 200, y: 20 }];
+
+ const updatedSimpleData = [
+ { x: 100, y: 10 },
+ { x: 200, y: 20 },
+ { x: 300, y: 30 }
+ ];
+
+ const simpleCategoryData = [
+ { x: 100, y: 10, category: "cat" },
+ { x: 200, y: 20, category: "cat" },
+ { x: 100, y: 20, category: "dog" },
+ { x: 200, y: 10, category: "dog" }
+ ];
+
+ const updatedSimpleCategoryData = [
+ { x: 200, y: 20, category: "cat" },
+ { x: 300, y: 30, category: "cat" },
+ { x: 100, y: 20, category: "dog" },
+ { x: 200, y: 10, category: "dog" },
+ { x: 100, y: 10, category: "fish" },
+ { x: 300, y: 25, category: "fish" }
+ ];
+
+ const simpleColorData = [
+ { x: 100, y: 10, category: "black" },
+ { x: 200, y: 20, category: "black" },
+ { x: 100, y: 20, category: "white" },
+ { x: 200, y: 10, category: "white" }
];
- const defaultProps = {
- data,
- dataKey: "year",
- dataValue: "population"
- };
+ const updatedSimpleColorData = [
+ { x: 200, y: 20, category: "black" },
+ { x: 300, y: 30, category: "black" },
+ { x: 100, y: 20, category: "white" },
+ { x: 200, y: 10, category: "white" },
+ { x: 100, y: 10, category: "fish" },
+ { x: 300, y: 25, category: "fish" }
+ ];
it("should render a VictoryChart", () => {
- const wrapper = shallow();
+ const wrapper = shallow();
expect(wrapper.find("VictoryChart").length).to.eql(1);
});
+
+ it("renders a LineChart with sample data", () => {
+ const wrapper = shallow();
+ expect(wrapper.find({ title: "Line Chart" }).length).to.eql(1);
+ expect(wrapper.find({ title: "Line Chart" }).props().data).to.eql([
+ { dataKey: 100, dataValue: 10, series: undefined },
+ { dataKey: 200, dataValue: 20, series: undefined }
+ ]);
+ });
+
+ it("renders an updated LineChart when passed new data", () => {
+ const wrapper = shallow();
+ expect(wrapper.find({ title: "Line Chart" }).length).to.eql(1);
+ wrapper.setProps({ data: updatedSimpleData });
+
+ expect(wrapper.find({ title: "Line Chart" }).props().data).to.eql([
+ { dataKey: 100, dataValue: 10, series: undefined },
+ { dataKey: 200, dataValue: 20, series: undefined },
+ { dataKey: 300, dataValue: 30, series: undefined }
+ ]);
+ });
+
+ it("renders a LineChart with sample category data", () => {
+ const wrapper = shallow(
+
+ );
+ expect(wrapper.find({ title: "Line Chart" }).length).to.eql(2);
+ expect(
+ wrapper
+ .find({ title: "Line Chart" })
+ .first()
+ .props().data
+ ).to.eql([
+ { dataKey: 100, dataValue: 10, series: "cat" },
+ { dataKey: 200, dataValue: 20, series: "cat" }
+ ]);
+ });
+
+ it("renders an updated LineChart when passed new category data", () => {
+ const wrapper = shallow(
+
+ );
+ expect(wrapper.find({ title: "Line Chart" }).length).to.eql(2);
+ wrapper.setProps({ data: updatedSimpleCategoryData });
+ expect(wrapper.find({ title: "Line Chart" }).length).to.eql(3);
+
+ expect(
+ wrapper
+ .find({ title: "Line Chart" })
+ .first()
+ .props().data
+ ).to.eql([
+ { dataKey: 200, dataValue: 20, series: "cat" },
+ { dataKey: 300, dataValue: 30, series: "cat" }
+ ]);
+ });
+
+ // Tests for https://github.com/FormidableLabs/victory/issues/928 workaround
+
+ it("renders a LineChart with sample color category data", () => {
+ const c = ""; // U+200B (zero-width space character)
+ const wrapper = shallow(
+
+ );
+ expect(wrapper.find({ title: "Line Chart" }).length).to.eql(2);
+ expect(
+ wrapper
+ .find({ title: "Line Chart" })
+ .first()
+ .props().data
+ ).to.eql([
+ { dataKey: 100, dataValue: 10, series: `black${c}` },
+ { dataKey: 200, dataValue: 20, series: `black${c}` }
+ ]);
+ });
+
+ it("renders an updated LineChart when passed new color category data", () => {
+ const c = ""; // U+200B (zero-width space character)
+ const wrapper = shallow(
+
+ );
+ expect(wrapper.find({ title: "Line Chart" }).length).to.eql(2);
+ wrapper.setProps({ data: updatedSimpleColorData });
+ expect(wrapper.find({ title: "Line Chart" }).length).to.eql(3);
+
+ expect(
+ wrapper
+ .find({ title: "Line Chart" })
+ .first()
+ .props().data
+ ).to.eql([
+ { dataKey: 200, dataValue: 20, series: `black${c}` },
+ { dataKey: 300, dataValue: 30, series: `black${c}` }
+ ]);
+ });
/* TODO: rewrite these tests
it('should render the relevant axis', () => {
const wrapper = shallow();
diff --git a/packages/component-library/src/PieChart/PieChart.js b/packages/component-library/src/PieChart/PieChart.js
index c08e1ca20..37f2f8d37 100644
--- a/packages/component-library/src/PieChart/PieChart.js
+++ b/packages/component-library/src/PieChart/PieChart.js
@@ -24,12 +24,14 @@ const PieChart = props => {
theme
} = props;
+ const safeData = data && data.length ? data : [{}];
const startAngle = halfDoughnut ? -90 : 0;
const endAngle = halfDoughnut ? 90 : 360;
const adjustedHeight = halfDoughnut ? height / 2 : height;
- const legendLabels = data.map(value => ({ name: value[dataLabel] }));
+ const legendLabels = safeData.map(value => ({ name: value[dataLabel] }));
const legendProps = {};
const colorScale = colors.length ? colors : theme.group.colorScale;
+ const aspectRatio = halfDoughnut ? 650 / 175 : 650 / 350;
if (useLegend) {
legendProps.labels = () => null;
@@ -42,8 +44,9 @@ const PieChart = props => {
subtitle={subtitle}
loading={loading}
error={error}
+ aspectRatio={aspectRatio}
>
-
+
{useLegend && (
{
{
- const chartDomain = domain || getDefaultDomain(data, dataKey, dataValue);
+ const safeData =
+ // eslint-disable-next-line no-nested-ternary
+ data && data.length
+ ? protect
+ ? protectData(data, { dataSeries, dataSeriesLabel })
+ : data
+ : [{}];
+ const safeDataSeriesLabel =
+ // eslint-disable-next-line no-nested-ternary
+ dataSeriesLabel && dataSeriesLabel.length
+ ? protect
+ ? protectData(dataSeriesLabel, { x: "category", y: "label" })
+ : dataSeriesLabel
+ : null;
+ const chartDomain = domain || getDefaultDomain(safeData, dataKey, dataValue);
const dataSeriesLabels = dataSeries
- ? dataSeriesLabel || getDefaultDataSeriesLabels(data, dataSeries)
+ ? safeDataSeriesLabel || getDefaultDataSeriesLabels(safeData, dataSeries)
: null;
const scatterPlotStyle =
@@ -75,7 +92,7 @@ const Scatterplot = ({
: null;
return (
-
+
{legendData &&
(legendComponent ? (
legendComponent(legendData, theme)
@@ -86,7 +103,7 @@ const Scatterplot = ({
theme={theme}
/>
))}
-
+
({
+ data={safeData.map(d => ({
dataKey: d[dataKey],
dataValue: d[dataValue],
label: `${
@@ -199,7 +216,9 @@ Scatterplot.propTypes = {
invertX: PropTypes.bool,
invertY: PropTypes.bool,
legendComponent: PropTypes.func,
- theme: PropTypes.shape({})
+ theme: PropTypes.shape({}),
+ loading: PropTypes.bool,
+ protect: PropTypes.bool
};
Scatterplot.defaultProps = {
@@ -222,7 +241,9 @@ Scatterplot.defaultProps = {
invertX: false,
invertY: false,
legendComponent: null,
- theme: VictoryTheme
+ theme: VictoryTheme,
+ loading: null,
+ protect: false
};
export default Scatterplot;
diff --git a/packages/component-library/src/Scatterplot/Scatterplot.test.js b/packages/component-library/src/Scatterplot/Scatterplot.test.js
index 02d2f40f5..ef232e1ea 100644
--- a/packages/component-library/src/Scatterplot/Scatterplot.test.js
+++ b/packages/component-library/src/Scatterplot/Scatterplot.test.js
@@ -35,6 +35,20 @@ const unstructuredMultiSeriesData = [
{ amount: 200, rate: 3, type: "second" }
];
+const multiSeriesColorData = [
+ { amount: 100, rate: 1, series: "black" },
+ { amount: 200, rate: 2, series: "black" },
+ { amount: 100, rate: 3, series: "white" },
+ { amount: 200, rate: 3, series: "white" }
+];
+
+const unstructuredMultiSeriesColorData = [
+ { amount: 100, rate: 1, type: "black" },
+ { amount: 200, rate: 2, type: "black" },
+ { amount: 100, rate: 3, type: "white" },
+ { amount: 200, rate: 3, type: "white" }
+];
+
describe("Scatterplot", () => {
it("renders a VictoryChart", () => {
const wrapper = shallow();
@@ -160,6 +174,85 @@ describe("Scatterplot", () => {
]);
});
+ // Tests for https://github.com/FormidableLabs/victory/issues/928 workaround
+ it("renders multi-series color data", () => {
+ const c = ""; // U+200B (zero-width space character)
+ const props = {
+ data: multiSeriesColorData,
+ dataKey: "amount",
+ dataValue: "rate",
+ dataSeries: "series",
+ protect: true
+ };
+ const wrapper = shallow();
+ expect(wrapper.find({ title: "Scatter Plot" }).props().data).to.eql([
+ {
+ dataKey: 100,
+ dataValue: 1,
+ label: "X: 100 • Y: 1",
+ series: `black${c}`
+ },
+ {
+ dataKey: 200,
+ dataValue: 2,
+ label: "X: 200 • Y: 2",
+ series: `black${c}`
+ },
+ {
+ dataKey: 100,
+ dataValue: 3,
+ label: "X: 100 • Y: 3",
+ series: `white${c}`
+ },
+ {
+ dataKey: 200,
+ dataValue: 3,
+ label: "X: 200 • Y: 3",
+ series: `white${c}`
+ }
+ ]);
+ });
+
+ it("renders unstructured multi-series color data", () => {
+ const c = ""; // U+200B (zero-width space character)
+ const props = {
+ data: unstructuredMultiSeriesColorData,
+ dataKey: "amount",
+ dataValue: "rate",
+ dataSeries: "type",
+ protect: true
+ };
+ const wrapper = shallow();
+ expect(wrapper.find({ title: "Scatter Plot" }).props().data).to.eql([
+ {
+ dataKey: 100,
+ dataValue: 1,
+ label: "X: 100 • Y: 1",
+ series: `black${c}`
+ },
+ {
+ dataKey: 200,
+ dataValue: 2,
+ label: "X: 200 • Y: 2",
+ series: `black${c}`
+ },
+ {
+ dataKey: 100,
+ dataValue: 3,
+ label: "X: 100 • Y: 3",
+ series: `white${c}`
+ },
+ {
+ dataKey: 200,
+ dataValue: 3,
+ label: "X: 200 • Y: 3",
+ series: `white${c}`
+ }
+ ]);
+ });
+
+ // End tests for https://github.com/FormidableLabs/victory/issues/928 workaround
+
// TODO: make this test pass
// it('sets the data point size if size key is specified', () => {
diff --git a/packages/component-library/src/StackedAreaChart/StackedAreaChart.js b/packages/component-library/src/StackedAreaChart/StackedAreaChart.js
index 7ba886915..920282980 100644
--- a/packages/component-library/src/StackedAreaChart/StackedAreaChart.js
+++ b/packages/component-library/src/StackedAreaChart/StackedAreaChart.js
@@ -44,13 +44,15 @@ const StackedAreaChart = ({
xNumberFormatter,
yNumberFormatter,
legendComponent,
- theme
+ theme,
+ loading
}) => {
+ const safeData = data && data.length ? data : [{}];
const chartDomain =
- domain || getDefaultStackedDomain(data, dataKey, dataValue);
+ domain || getDefaultStackedDomain(safeData, dataKey, dataValue);
const dataSeriesLabels = dataSeries
- ? dataSeriesLabel || getDefaultDataSeriesLabels(data, dataSeries)
+ ? dataSeriesLabel || getDefaultDataSeriesLabels(safeData, dataSeries)
: null;
const scatterPlotStyle = style || {
@@ -62,7 +64,9 @@ const StackedAreaChart = ({
? dataSeriesLabels.map(series => ({ name: series.label }))
: null;
- const lineData = dataSeries ? groupBy(data, dataSeries) : { category: data };
+ const lineData = dataSeries
+ ? groupBy(safeData, dataSeries)
+ : { category: safeData };
const areas = lineData
? Object.keys(lineData).map((category, index) => (
@@ -120,8 +124,8 @@ const StackedAreaChart = ({
: null;
return (
-
-
+
+
{legendData &&
(legendComponent ? (
legendComponent(legendData, theme)
@@ -203,7 +207,8 @@ StackedAreaChart.propTypes = {
xNumberFormatter: PropTypes.func,
yNumberFormatter: PropTypes.func,
legendComponent: PropTypes.func,
- theme: PropTypes.shape({})
+ theme: PropTypes.shape({}),
+ loading: PropTypes.bool
};
StackedAreaChart.defaultProps = {
@@ -224,7 +229,8 @@ StackedAreaChart.defaultProps = {
xNumberFormatter: civicFormat.numeric,
yNumberFormatter: civicFormat.numeric,
legendComponent: null,
- theme: VictoryTheme
+ theme: VictoryTheme,
+ loading: null
};
export default StackedAreaChart;
diff --git a/packages/component-library/src/utils.js b/packages/component-library/src/utils.js
index 3c3aa5044..6b5d5eee3 100644
--- a/packages/component-library/src/utils.js
+++ b/packages/component-library/src/utils.js
@@ -1,2 +1,3 @@
export { default as civicFormat } from "./utils/civicFormat";
export { default as ungroupBy } from "./utils/ungroupBy";
+export { default as protectData } from "./utils/protectData";
diff --git a/packages/component-library/src/utils/protectData.js b/packages/component-library/src/utils/protectData.js
new file mode 100644
index 000000000..466221529
--- /dev/null
+++ b/packages/component-library/src/utils/protectData.js
@@ -0,0 +1,27 @@
+import { fromPairs } from "lodash";
+
+const c = ""; // U+200B (zero-width space character)
+
+/**
+ * This function protects data attributes from being accessed by d3-interpolate
+ * to work around https://github.com/FormidableLabs/victory/issues/928
+ *
+ * @param {array} data - An array of data objects
+ * @param {object} dataAccessors - An object with { dataKey, dataValue, etc ..}
+ * @return {array} An array with each element transformed for each dataAcccessor to add a zero-width space character
+ */
+
+function protectData(data, dataAccessors) {
+ return data.map(d => {
+ return {
+ ...d,
+ ...fromPairs(
+ Object.values(dataAccessors).map(property => {
+ return [property, `${d[property]}${c}`];
+ })
+ )
+ };
+ });
+}
+
+export default protectData;
diff --git a/packages/component-library/stories/HorizontalBarChart.story.js b/packages/component-library/stories/HorizontalBarChart.story.js
index 2c5c81f16..3faa75f26 100644
--- a/packages/component-library/stories/HorizontalBarChart.story.js
+++ b/packages/component-library/stories/HorizontalBarChart.story.js
@@ -118,6 +118,7 @@ export default () =>
{ display: "select" },
GROUP_IDS.CUSTOM
);
+ const loading = boolean("Loading", false, GROUP_IDS.CUSTOM);
return (
dataValueFormatter={x => civicFormat[optionSelectX](x)}
minimalist={minimalist}
theme={(name => themes[name])(theme)}
+ loading={loading}
/>
);
},
diff --git a/packages/component-library/stories/LineChart.story.js b/packages/component-library/stories/LineChart.story.js
index 40b063bf1..307b2b1ef 100644
--- a/packages/component-library/stories/LineChart.story.js
+++ b/packages/component-library/stories/LineChart.story.js
@@ -7,7 +7,8 @@ import {
text,
number,
withKnobs,
- optionsKnob as options
+ optionsKnob as options,
+ boolean
} from "@storybook/addon-knobs";
import {
LineChart,
@@ -250,6 +251,7 @@ export default () =>
{ display: "select" },
GROUP_IDS.CUSTOM
);
+ const loading = boolean("Loading", false, GROUP_IDS.CUSTOM);
return (
yNumberFormatter={y => civicFormat[optionSelectY](y)}
legendComponent={customLegend}
theme={(name => themes[name])(theme)}
+ loading={loading}
/>
);
},
diff --git a/packages/component-library/stories/PieChart.story.js b/packages/component-library/stories/PieChart.story.js
index f3965c290..636758dc0 100644
--- a/packages/component-library/stories/PieChart.story.js
+++ b/packages/component-library/stories/PieChart.story.js
@@ -85,6 +85,7 @@ export default () =>
const chartHeight = number("Chart height", 350, {}, GROUP_IDS.CUSTOM);
const chartWidth = number("Chart width", 650, {}, GROUP_IDS.CUSTOM);
const innerRadius = number("Inner radius", 50, {}, GROUP_IDS.CUSTOM);
+ const loading = boolean("Loading", false, GROUP_IDS.CUSTOM);
const themes = {
VictoryTheme,
VictoryCrazyTheme
@@ -112,6 +113,7 @@ export default () =>
halfDoughnut={halfDoughnut}
useLegend={useLegend}
theme={(name => themes[name])(theme)}
+ loading={loading}
/>
);
},
diff --git a/packages/component-library/stories/Scatterplot.story.js b/packages/component-library/stories/Scatterplot.story.js
index 9a2481fd3..04fc069df 100644
--- a/packages/component-library/stories/Scatterplot.story.js
+++ b/packages/component-library/stories/Scatterplot.story.js
@@ -273,6 +273,7 @@ export default () =>
{ display: "select" },
GROUP_IDS.CUSTOM
);
+ const loading = boolean("Loading", false, GROUP_IDS.CUSTOM);
return (
invertY={invertY}
legendComponent={customLegend}
theme={(name => themes[name])(theme)}
+ loading={loading}
/>
);
},
diff --git a/packages/component-library/stories/StackedAreaChart.story.js b/packages/component-library/stories/StackedAreaChart.story.js
index 7de733320..3d88705fa 100644
--- a/packages/component-library/stories/StackedAreaChart.story.js
+++ b/packages/component-library/stories/StackedAreaChart.story.js
@@ -7,7 +7,8 @@ import {
object,
text,
withKnobs,
- optionsKnob as options
+ optionsKnob as options,
+ boolean
} from "@storybook/addon-knobs";
import { StackedAreaChart, civicFormat, SimpleLegend } from "../src";
import { getKeyNames } from "./shared";
@@ -273,6 +274,7 @@ export default () =>
{ display: "select" },
GROUP_IDS.CUSTOM
);
+ const loading = boolean("Loading", false, GROUP_IDS.CUSTOM);
return (
yNumberFormatter={y => civicFormat[optionSelectY](y)}
legendComponent={customLegend}
theme={(name => themes[name])(theme)}
+ loading={loading}
/>
);
},
diff --git a/yarn.lock b/yarn.lock
index 49c344bb0..3c3605d14 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5802,6 +5802,11 @@ d3-array@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc"
+d3-array@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.3.1.tgz#145cb03967578e675db8c62e41f3c406fa7acc73"
+ integrity sha512-YlOh8kwqIz0pDECEdCeqVNelaLQXznD0g6yidhhklMgKxKqbNDrYfoudLMkk9THlqvFll+pXMmXYAyN49yWsmg==
+
d3-axis@1:
version "1.0.12"
resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.12.tgz#cdf20ba210cfbb43795af33756886fb3638daac9"