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

fix(charts): align label vertically and add donutHeight/Width defaults #2193

Merged
merged 2 commits into from Jun 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -122,8 +122,14 @@ export interface ChartDonutThresholdProps extends ChartPieProps {
* height of the chart in number of pixels, but instead define an aspect ratio for the chart. The exact number of
* pixels will depend on the size of the container the chart is rendered into.
*
* Note: The parent container must be set to the same height in order to maintain the aspect ratio. Otherwise, the
* innerRadius may need to be set when using this property.
* Note: When adding a legend, height (the overall SVG height) may need to be larger than donutHeight (the donut size)
* in order to accommodate the extra legend.
*
* By default, donutHeight is the min. of either height or width. This covers most use cases in order to accommodate
* legends within the same SVG. However, donutHeight (not height) may need to be set in order to adjust the donut
* height.
*
* The innerRadius may also need to be set when changing the donut size.
*/
donutHeight?: number;
/**
Expand All @@ -138,8 +144,13 @@ export interface ChartDonutThresholdProps extends ChartPieProps {
* height of the chart in number of pixels, but instead define an aspect ratio for the chart. The exact number of
* pixels will depend on the size of the container the chart is rendered into.
*
* Note: The parent container must be set to the same height in order to maintain the aspect ratio. Otherwise, the
* innerRadius may need to be set when using this property.
* Note: When adding a legend, width (the overall SVG width) may need to be larger than donutWidth (the donut size)
* in order to accommodate the extra legend.
*
* By default, donutWidth is the min. of either height or width. This covers most use cases in order to accommodate
* legends within the same SVG. However, donutWidth (not width) may need to be set in order to adjust the donut width.
*
* The innerRadius may also need to be set when changing the donut size.
*/
donutWidth?: number;
/**
Expand Down Expand Up @@ -211,7 +222,10 @@ export interface ChartDonutThresholdProps extends ChartPieProps {
* height of the chart in number of pixels, but instead define an aspect ratio for the chart. The exact number of
* pixels will depend on the size of the container the chart is rendered into.
*
* Note: innerRadius may need to be set when using this property.
* Note: When adding a legend, height (the overall SVG height) may need to be larger than donutHeight (the donut size)
* in order to accommodate the extra legend.
*
* Typically, the parent container is set to the same height in order to maintain the aspect ratio.
*/
height?: number;
/**
Expand Down Expand Up @@ -344,7 +358,10 @@ export interface ChartDonutThresholdProps extends ChartPieProps {
* height of the chart in number of pixels, but instead define an aspect ratio for the chart. The exact number of
* pixels will depend on the size of the container the chart is rendered into.
*
* Note: innerRadius may need to be set when using this property.
* Note: When adding a legend, width (the overall SVG width) may need to be larger than donutWidth (the donut size)
* in order to accommodate the extra legend.
*
* Typically, the parent container is set to the same width in order to maintain the aspect ratio.
*/
width?: number;
/**
Expand Down Expand Up @@ -383,11 +400,12 @@ export const ChartDonutThreshold: React.FunctionComponent<ChartDonutThresholdPro

// destructure last
theme = getDonutThresholdStaticTheme(themeColor, themeVariant),
donutHeight = theme.pie.height,
donutWidth = theme.pie.width,
height = theme.pie.height,
innerRadius = ((donutHeight || donutWidth) - 34) / 2,
width = theme.pie.width,
donutHeight = Math.min(height, width),
donutWidth = Math.min(height, width),
innerRadius = (Math.min(donutHeight, donutWidth) - 34) / 2,

...rest
}: ChartDonutThresholdProps) => {
// Returns computed data representing pie chart slices
Expand All @@ -410,42 +428,48 @@ export const ChartDonutThreshold: React.FunctionComponent<ChartDonutThresholdPro

// Returns the horizontal shift for the dynamic utilization donut cart
const getDynamicDonutDx = (dynamicTheme: ChartThemeDefinition, orientation: string) => {
const dynamicWidth = donutWidth - (theme.pie.width - dynamicTheme.pie.width);
switch (orientation) {
case 'left':
return Math.round((theme.pie.width - dynamicTheme.pie.width) / 2);
return Math.round((donutWidth - dynamicWidth) / 2);
case 'right':
return -Math.round((theme.pie.width - dynamicTheme.pie.width) / 2);
return -Math.round((donutWidth - dynamicWidth) / 2);
default:
return 0;
}
};

// Returns the vertical shift for the dynamic utilization donut cart
const getDynamicDonutDy = (dynamicTheme: ChartThemeDefinition) =>
Math.round((theme.pie.height - dynamicTheme.pie.height) / 2);
const getDynamicDonutDy = (dynamicTheme: ChartThemeDefinition) => {
const dynamicHeight = donutHeight - (theme.pie.height - dynamicTheme.pie.height);
return Math.round((donutHeight - dynamicHeight) / 2);
}

// Render dynamic utilization donut cart
const renderChildren = () =>
React.Children.toArray(children).map(child => {
const datum = getData([{ ...child.props.data }]);
const orientation = child.props.donutOrientation || donutOrientation;
const dynamicTheme =
child.props.theme ||
getDonutThresholdDynamicTheme(child.props.themeColor || themeColor,
child.props.themeVariant || themeVariant);
return React.cloneElement(child, {
donutDx: child.props.donutDx || getDynamicDonutDx(dynamicTheme, orientation),
donutDy: child.props.donutDy || getDynamicDonutDy(dynamicTheme),
donutHeight: child.props.donutHeight || dynamicTheme.pie.height,
donutOrientation: orientation,
donutWidth: child.props.donutWidth || dynamicTheme.pie.width,
endAngle: child.props.endAngle || 360 * (datum[0]._y ? datum[0]._y / 100 : 100),
height: child.props.height || height,
showStatic: child.props.showStatic || false,
standalone: false,
theme: dynamicTheme,
width: child.props.width || width
});
if (child.props) {
const datum = getData([{ ...child.props.data }]);
const orientation = child.props.donutOrientation || donutOrientation;
const dynamicTheme =
child.props.theme ||
getDonutThresholdDynamicTheme(child.props.themeColor || themeColor,
child.props.themeVariant || themeVariant);
return React.cloneElement(child, {
donutDx: child.props.donutDx || getDynamicDonutDx(dynamicTheme, orientation),
donutDy: child.props.donutDy || getDynamicDonutDy(dynamicTheme),
donutHeight: child.props.donutHeight || donutHeight - (theme.pie.height - dynamicTheme.pie.height),
donutOrientation: orientation,
donutWidth: child.props.donutWidth || donutWidth - (theme.pie.width - dynamicTheme.pie.width),
endAngle: child.props.endAngle || 360 * (datum[0]._y ? datum[0]._y / 100 : 100),
height: child.props.height || height,
showStatic: child.props.showStatic || false,
standalone: false,
theme: dynamicTheme,
width: child.props.width || width
});
}
return child;
});

// Static threshold dount chart
Expand Down
Expand Up @@ -60,6 +60,13 @@ export interface ChartDonutUtilizationProps extends ChartPieProps {
* {duration: 500, onExit: () => {}, onEnter: {duration: 500, before: () => ({y: 0})})}
*/
animate?: AnimatePropTypeInterface;
/**
* The capHeight prop defines a text metric for the font being used: the expected height of capital letters.
* This is necessary because of SVG, which (a) positions the *bottom* of the text at `y`, and (b) has no notion of
* line height. The value should ideally use the same units as `lineHeight` and `dy`, preferably ems. If given a
* unitless number, it is assumed to be ems.
*/
capHeight?: StringOrNumberOrCallback;
/**
* The categories prop specifies how categorical data for a chart should be ordered.
* This prop should be given as an array of string values, or an object with
Expand Down Expand Up @@ -134,8 +141,14 @@ export interface ChartDonutUtilizationProps extends ChartPieProps {
* height of the chart in number of pixels, but instead define an aspect ratio for the chart. The exact number of
* pixels will depend on the size of the container the chart is rendered into.
*
* Note: The parent container must be set to the same height in order to maintain the aspect ratio. Otherwise, the
* innerRadius may need to be set when using this property.
* Note: When adding a legend, height (the overall SVG height) may need to be larger than donutHeight (the donut size)
* in order to accommodate the extra legend.
*
* By default, donutHeight is the min. of either height or width. This covers most use cases in order to accommodate
* legends within the same SVG. However, donutHeight (not height) may need to be set in order to adjust the donut
* height.
*
* The innerRadius may also need to be set when changing the donut size.
*/
donutHeight?: number;
/**
Expand All @@ -150,8 +163,13 @@ export interface ChartDonutUtilizationProps extends ChartPieProps {
* height of the chart in number of pixels, but instead define an aspect ratio for the chart. The exact number of
* pixels will depend on the size of the container the chart is rendered into.
*
* Note: The parent container must be set to the same height in order to maintain the aspect ratio. Otherwise, the
* innerRadius may need to be set when using this property.
* Note: When adding a legend, width (the overall SVG width) may need to be larger than donutWidth (the donut size)
* in order to accommodate the extra legend.
*
* By default, donutWidth is the min. of either height or width. This covers most use cases in order to accommodate
* legends within the same SVG. However, donutWidth (not width) may need to be set in order to adjust the donut width.
*
* The innerRadius may also need to be set when changing the donut size.
*/
donutWidth?: number;
/**
Expand Down Expand Up @@ -223,7 +241,10 @@ export interface ChartDonutUtilizationProps extends ChartPieProps {
* height of the chart in number of pixels, but instead define an aspect ratio for the chart. The exact number of
* pixels will depend on the size of the container the chart is rendered into.
*
* Note: innerRadius may need to be set when using this property.
* Note: When adding a legend, height (the overall SVG height) may need to be larger than donutHeight (the donut size)
* in order to accommodate the extra legend.
*
* Typically, the parent container is set to the same height in order to maintain the aspect ratio.
*/
height?: number;
/**
Expand All @@ -250,7 +271,9 @@ export interface ChartDonutUtilizationProps extends ChartPieProps {
*/
labelPosition?: 'startAngle' | 'endAngle' | 'centroid';
/**
* The legend component to render with chart. This overrides other legend props.
* The legend component to render with chart.
*
* Note: Default legend properties may be applied
*/
legendComponent?: React.ReactElement<any>;
/**
Expand All @@ -261,8 +284,6 @@ export interface ChartDonutUtilizationProps extends ChartPieProps {
* Each data point may be any format you wish (depending on the `x` and `y` accessor props),
* but by default, an object with x and y properties is expected.
*
* Note: Not compatible with legendComponent prop.
*
* @example legendData={[{ name: `GBps capacity - 45%` }, { name: 'Unused' }]}
*/
legendData?: any[];
Expand All @@ -274,9 +295,6 @@ export interface ChartDonutUtilizationProps extends ChartPieProps {
* and text-wrapping is not currently supported, so "vertical"
* orientation is both the default setting and recommended for
* displaying many series of data.
*
* Note: May need to set legendHeight and legendWidth in order to position properly.
* Not compatible with legendComponent prop.
*/
legendOrientation?: 'horizontal' | 'vertical';
/**
Expand Down Expand Up @@ -402,7 +420,10 @@ export interface ChartDonutUtilizationProps extends ChartPieProps {
* height of the chart in number of pixels, but instead define an aspect ratio for the chart. The exact number of
* pixels will depend on the size of the container the chart is rendered into.
*
* Note: innerRadius may need to be set when using this property.
* Note: When adding a legend, width (the overall SVG width) may need to be larger than donutWidth (the donut size)
* in order to accommodate the extra legend.
*
* Typically, the parent container is set to the same width in order to maintain the aspect ratio.
*/
width?: number;
/**
Expand Down Expand Up @@ -449,11 +470,12 @@ export const ChartDonutUtilization: React.FunctionComponent<ChartDonutUtilizatio

// destructure last
theme = getDonutUtilizationTheme(themeColor, themeVariant),
donutHeight = theme.pie.height,
donutWidth = theme.pie.width,
capHeight = title && subTitle ? 1.1 : undefined,
height = theme.pie.height,
innerRadius = ((donutHeight || donutWidth) - 34) / 2,
width = theme.pie.width,
donutHeight = Math.min(height, width),
donutWidth = Math.min(height, width),
innerRadius = (Math.min(donutHeight, donutWidth) - 34) / 2,
...rest
}: ChartDonutUtilizationProps) => {
// Returns computed data representing pie chart slices
Expand Down Expand Up @@ -490,9 +512,31 @@ export const ChartDonutUtilization: React.FunctionComponent<ChartDonutUtilizatio
// Returns legend
const getLegend = () => {
if (legendComponent) {
return legendComponent;
}
if (legendData) {
const props = legendComponent.props;
return React.cloneElement(legendComponent, {
data: props.data ? props.data : legendData,
orientation: props.legendOrientation ? props.legendOrientation : legendOrientation,
standalone: false,
theme: props.theme ? props.theme : theme,
x: props.x ? props.x : getLegendX({
chartOrientation: donutOrientation,
legendOrientation: props.legendOrientation ? props.legendOrientation : legendOrientation,
legendWidth: getLegendDimensions().width,
theme,
width
}),
y: props.y ? props.y : getLegendY({
chartDy: donutDy,
chartHeight: donutHeight,
chartOrientation: donutOrientation,
chartType: 'pie',
height,
legendData: props.data ? props.data : legendData,
legendHeight: getLegendDimensions().height,
theme
})
});
} else if (legendData) {
return (
<ChartLegend
data={legendData}
Expand All @@ -502,7 +546,7 @@ export const ChartDonutUtilization: React.FunctionComponent<ChartDonutUtilizatio
x={getLegendX({
chartOrientation: donutOrientation,
legendOrientation,
legendWidth: legendDimensions.width,
legendWidth: getLegendDimensions().width,
theme,
width
})}
Expand All @@ -513,7 +557,7 @@ export const ChartDonutUtilization: React.FunctionComponent<ChartDonutUtilizatio
chartType: 'pie',
height,
legendData,
legendHeight: legendDimensions.height,
legendHeight: getLegendDimensions().height,
theme
})}
/>
Expand All @@ -522,6 +566,25 @@ export const ChartDonutUtilization: React.FunctionComponent<ChartDonutUtilizatio
return null;
};

// Legend dimensions
const getLegendDimensions = () => {
if (legendComponent) {
const props = legendComponent.props;
return (VictoryLegend as any).getDimensions({
data: props.data ? props.data : legendData,
orientation: props.legendOrientation ? props.legendOrientation : legendOrientation,
theme: props.theme ? props.theme : theme,
});
} else if (legendData) {
return (VictoryLegend as any).getDimensions({
data: legendData,
orientation: legendOrientation,
theme
});
}
return {};
}

// Returns theme based on threshold and current value
const getThresholdTheme = () => {
const newTheme = { ...theme };
Expand All @@ -543,18 +606,10 @@ export const ChartDonutUtilization: React.FunctionComponent<ChartDonutUtilizatio
return newTheme;
};

// Legend dimensions
const legendDimensions = legendData
? (VictoryLegend as any).getDimensions({
data: legendData,
orientation: legendOrientation,
theme
})
: {};

const chart = (
<React.Fragment>
<ChartLabel
capHeight={capHeight}
style={[DonutUtilizationStyles.label.title, DonutUtilizationStyles.label.subTitle] as any} // Todo: Array supported, but @types/victory is wrong
text={title && subTitle ? [title, subTitle] : title}
textAnchor="middle"
Expand Down
Expand Up @@ -983,12 +983,12 @@ exports[`renders component data 1`] = `
},
]
}
height={230}
innerRadius={98}
height={200}
innerRadius={83}
origin={
Object {
"x": 115,
"y": 115,
"x": 100,
"y": 100,
}
}
standalone={false}
Expand Down Expand Up @@ -1444,7 +1444,7 @@ exports[`renders component data 1`] = `
},
}
}
width={230}
width={200}
/>
</Component>
`;