diff --git a/packages/react-charts/src/components/Chart/Chart.tsx b/packages/react-charts/src/components/Chart/Chart.tsx index ffcf53e8ffe..c0eecfe025c 100644 --- a/packages/react-charts/src/components/Chart/Chart.tsx +++ b/packages/react-charts/src/components/Chart/Chart.tsx @@ -35,6 +35,7 @@ import { getDefaultData, getDefaultPatternScale } from '../ChartUtils'; +import { PatternScaleInterface } from '../ChartUtils/chart-patterns'; /** * See https://github.com/FormidableLabs/victory/blob/master/packages/victory-core/src/index.d.ts @@ -307,13 +308,6 @@ export interface ChartProps extends VictoryChartProps { * @propType number | { top: number, bottom: number, left: number, right: number } */ padding?: PaddingProps; - /** - * The optional ID to prefix pattern defs - * - * @example patternId="pattern" - * @beta - */ - patternId?: string; /** * The patternScale prop is an optional prop that defines a pattern to be applied to the children, where applicable. * This prop should be given as an array of CSS colors, or as a string corresponding to a URL. Patterns will be @@ -326,7 +320,7 @@ export interface ChartProps extends VictoryChartProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; /** * Note: This prop should not be set manually. * @@ -469,7 +463,6 @@ export const Chart: React.FunctionComponent = ({ themeColor, // eslint-disable-next-line @typescript-eslint/no-unused-vars themeVariant, - patternId = getPatternId(), patternScale, isPatternDefs = false, @@ -488,12 +481,13 @@ export const Chart: React.FunctionComponent = ({ top: getPaddingForSide('top', padding, theme.chart.padding) }; + const patternId = React.useMemo(() => getPatternId(), []); const defaultColorScale = getDefaultColorScale(colorScale as any, theme.chart.colorScale as string[]); const defaultPatternScale = getDefaultPatternScale({ colorScale: defaultColorScale, - patternScale, + isPatternDefs, patternId, - isPatternDefs + patternScale }); // Add pattern props for legend tooltip @@ -605,7 +599,7 @@ export const Chart: React.FunctionComponent = ({ > {renderChildren()} {getLegend()} - {isPatternDefs && getPatternDefs({ patternId, patternScale: defaultColorScale })} + {isPatternDefs && getPatternDefs({ patternId, colorScale: defaultColorScale })} ); }; diff --git a/packages/react-charts/src/components/ChartDonut/ChartDonut.tsx b/packages/react-charts/src/components/ChartDonut/ChartDonut.tsx index b0823d83168..2934b6df37a 100644 --- a/packages/react-charts/src/components/ChartDonut/ChartDonut.tsx +++ b/packages/react-charts/src/components/ChartDonut/ChartDonut.tsx @@ -22,7 +22,7 @@ import { ChartContainer } from '../ChartContainer'; import { ChartLabel } from '../ChartLabel'; import { ChartPie, ChartPieLegendPosition, ChartPieProps } from '../ChartPie'; import { ChartCommonStyles, ChartDonutStyles, ChartThemeDefinition } from '../ChartTheme'; -import { getPieLabelX, getPieLabelY, getPaddingForSide } from '../ChartUtils'; +import { getPieLabelX, getPieLabelY, getPaddingForSide, PatternScaleInterface } from '../ChartUtils'; interface ChartDonutSubTitleInterface { dy?: number; @@ -361,13 +361,6 @@ export interface ChartDonutProps extends ChartPieProps { * @propType number | Function */ padAngle?: NumberOrCallback; - /** - * The optional ID to prefix pattern defs - * - * @example patternId="pattern" - * @beta - */ - patternId?: string; /** * The patternScale prop is an optional prop that defines a pattern to be applied to the children, where applicable. * This prop should be given as an array of CSS colors, or as a string corresponding to a URL. Patterns will be @@ -380,7 +373,7 @@ export interface ChartDonutProps extends ChartPieProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; /** * The padding props specifies the amount of padding in number of pixels between * the edge of the chart and any rendered child components. This prop can be given diff --git a/packages/react-charts/src/components/ChartDonutUtilization/ChartDonutThreshold.tsx b/packages/react-charts/src/components/ChartDonutUtilization/ChartDonutThreshold.tsx index 2b54538c8e0..810d20d0e28 100644 --- a/packages/react-charts/src/components/ChartDonutUtilization/ChartDonutThreshold.tsx +++ b/packages/react-charts/src/components/ChartDonutUtilization/ChartDonutThreshold.tsx @@ -22,13 +22,10 @@ import { ChartContainer } from '../ChartContainer'; import { ChartDonut, ChartDonutProps } from '../ChartDonut'; import { ChartDonutStyles, ChartThemeDefinition } from '../ChartTheme'; import { - getDefaultColorScale, - getDefaultPatternScale, getDonutThresholdDynamicTheme, getDonutThresholdStaticTheme, getPaddingForSide, - getPatternDefs, - getPatternId + PatternScaleInterface } from '../ChartUtils'; export enum ChartDonutThresholdDonutOrientation { @@ -320,13 +317,6 @@ export interface ChartDonutThresholdProps extends ChartDonutProps { * @propType number | { top: number, bottom: number, left: number, right: number } */ padding?: PaddingProps; - /** - * The optional ID to prefix pattern defs - * - * @example patternId="pattern" - * @beta - */ - patternId?: string; /** * The patternScale prop is an optional prop that defines a pattern to be applied to the children, where applicable. * This prop should be given as an array of CSS colors, or as a string corresponding to a URL. Patterns will be @@ -339,7 +329,7 @@ export interface ChartDonutThresholdProps extends ChartDonutProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; /** * Specifies the radius of the chart. If this property is not provided it is computed * from width, height, and padding props @@ -480,7 +470,6 @@ export const ChartDonutThreshold: React.FunctionComponent { // Format and sort data. Sorting ensures thresholds are displayed in the correct order and simplifies calculations. @@ -549,13 +530,13 @@ export const ChartDonutThreshold: React.FunctionComponent React.Children.toArray(children).map((child, index) => { if (React.isValidElement(child)) { const { data: childData, ...childProps } = child.props; const datum = Data.formatData([childData], childProps, ['x', 'y']); // Format child data independently of this component's props const dynamicTheme = childProps.theme || getDonutThresholdDynamicTheme(childProps.themeColor || themeColor); - return React.cloneElement(child, { constrainToVisibleArea, colorScale, @@ -565,7 +546,7 @@ export const ChartDonutThreshold: React.FunctionComponent {chart} {renderChildren()} - {isPatternDefs && getPatternDefs({ offset: 1, patternId, patternScale: defaultColorScale })} ); }; diff --git a/packages/react-charts/src/components/ChartDonutUtilization/ChartDonutUtilization.tsx b/packages/react-charts/src/components/ChartDonutUtilization/ChartDonutUtilization.tsx index ad4496c962c..27f71e74132 100644 --- a/packages/react-charts/src/components/ChartDonutUtilization/ChartDonutUtilization.tsx +++ b/packages/react-charts/src/components/ChartDonutUtilization/ChartDonutUtilization.tsx @@ -21,7 +21,7 @@ import { SliceProps, VictoryPie, VictorySliceLabelPositionType } from 'victory-p import { ChartContainer } from '../ChartContainer'; import { ChartDonut, ChartDonutProps } from '../ChartDonut'; import { ChartCommonStyles, ChartThemeDefinition, ChartDonutUtilizationStyles } from '../ChartTheme'; -import { getDefaultColorScale, getDefaultPatternScale, getDonutUtilizationTheme, getPatternId } from '../ChartUtils'; +import { getDonutUtilizationTheme, PatternScaleInterface } from '../ChartUtils'; export enum ChartDonutUtilizationLabelPosition { centroid = 'centroid', @@ -243,13 +243,6 @@ export interface ChartDonutUtilizationProps extends ChartDonutProps { * to a tag on web, and a react-native-svg tag on mobile */ groupComponent?: React.ReactElement; - /** - * Flag indicating parent isPatternDefs prop is in use - * Do not use - * @hide - * @private - */ - hasPatternDefs?: boolean; /** * Specifies the height the svg viewBox of the chart container. This value should be given as a * number of pixels. @@ -389,13 +382,6 @@ export interface ChartDonutUtilizationProps extends ChartDonutProps { * @propType number | { top: number, bottom: number, left: number, right: number } */ padding?: PaddingProps; - /** - * The optional ID to prefix pattern defs - * - * @example patternId="pattern" - * @beta - */ - patternId?: string; /** * The patternScale prop is an optional prop that defines a pattern to be applied to the children, where applicable. * This prop should be given as an array of CSS colors, or as a string corresponding to a URL. Patterns will be @@ -408,7 +394,15 @@ export interface ChartDonutUtilizationProps extends ChartDonutProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; + /** + * Flag indicating to shift pattern scale for ChartDonutThreshold + * Do not use + * + * @private + * @hide + */ + patternShift?: number; /** * Specifies the radius of the chart. If this property is not provided it is computed * from width, height, and padding props @@ -613,14 +607,13 @@ export const ChartDonutUtilization: React.FunctionComponent, data, - hasPatternDefs, invert = false, legendPosition = ChartCommonStyles.legend.position as ChartDonutUtilizationLegendPosition, padding, - patternId = getPatternId(), patternScale, + patternShift, showStatic = true, - showStaticPattern = false, + showStaticPattern = true, standalone = true, themeColor, // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -636,23 +629,11 @@ export const ChartDonutUtilization: React.FunctionComponent { - const defaultColorScale = getDefaultColorScale(colorScale, theme.pie.colorScale as string[]); - const defaultPatternScale = getDefaultPatternScale({ - colorScale: defaultColorScale, - patternScale, - patternId, - isPatternDefs - }); - - // Hide static pattern and handle edge case where parent does not use isPatternDefs + // Hide pattern for the static, unused portion of the donut utilization chart + const defaultPatternScale: PatternScaleInterface[] = []; const hideStaticPattern = showStatic && !showStaticPattern; - const hideThresholdPatterns = !patternScale && hasPatternDefs === false; - if (defaultPatternScale && (hideStaticPattern || hideThresholdPatterns)) { - for (let i = 0; i < defaultPatternScale.length; i++) { - if (i !== 0) { - defaultPatternScale[i] = null; - } - } + if (!patternScale && hideStaticPattern) { + defaultPatternScale.push({ visible: true }, { visible: false }); } // Returns computed data representing pie chart slices @@ -692,9 +673,8 @@ export const ChartDonutUtilization: React.FunctionComponent { const newTheme = { ...theme }; - - if (data) { - const datum = getData(); + const datum = getData(); + if (datum) { const donutThresholds = getDonutThresholds(); const mergeThemeProps = (i: number) => { // Merge just the first color of dynamic (blue, green, etc.) with static (gray) for expected colorScale @@ -731,14 +711,14 @@ export const ChartDonutUtilization: React.FunctionComponent 0 ? defaultPatternScale : patternScale} + patternShift={patternShift} standalone={false} theme={getThresholdTheme()} - isPatternDefs={isPatternDefs} width={width} {...rest} /> diff --git a/packages/react-charts/src/components/ChartGroup/ChartGroup.tsx b/packages/react-charts/src/components/ChartGroup/ChartGroup.tsx index 9aa5523854c..d167e212859 100644 --- a/packages/react-charts/src/components/ChartGroup/ChartGroup.tsx +++ b/packages/react-charts/src/components/ChartGroup/ChartGroup.tsx @@ -28,7 +28,8 @@ import { getPatternId, getPatternDefs, getTheme, - renderChildrenWithPatterns + renderChildrenWithPatterns, + PatternScaleInterface } from '../ChartUtils'; export enum ChartGroupSortOrder { @@ -298,13 +299,6 @@ export interface ChartGroupProps extends VictoryGroupProps { * @propType number | { top: number, bottom: number, left: number, right: number } */ padding?: PaddingProps; - /** - * The optional ID to prefix pattern defs - * - * @example patternId="pattern" - * @beta - */ - patternId?: string; /** * The patternScale prop is an optional prop that defines a pattern to be applied to the children, where applicable. * This prop should be given as an array of CSS colors, or as a string corresponding to a URL. Patterns will be @@ -317,7 +311,7 @@ export interface ChartGroupProps extends VictoryGroupProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; /** * Victory components can pass a boolean polar prop to specify whether a label is part of a polar chart. */ @@ -484,7 +478,6 @@ export const ChartGroup: React.FunctionComponent = ({ children, colorScale, containerComponent = , - patternId = getPatternId(), patternScale, themeColor, // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -504,12 +497,13 @@ export const ChartGroup: React.FunctionComponent = ({ className: getClassName({ className: containerComponent.props.className }) // Override VictoryContainer class name }); + const patternId = React.useMemo(() => getPatternId(), []); const defaultColorScale = getDefaultColorScale(colorScale, theme.group.colorScale as string[]); const defaultPatternScale = getDefaultPatternScale({ colorScale: defaultColorScale, - patternScale, + isPatternDefs, patternId, - isPatternDefs + patternScale }); // Note: containerComponent is required for theme @@ -520,7 +514,7 @@ export const ChartGroup: React.FunctionComponent = ({ patternId, patternScale: defaultPatternScale })} - {isPatternDefs && getPatternDefs({ patternId, patternScale: defaultColorScale })} + {isPatternDefs && getPatternDefs({ patternId, colorScale: defaultColorScale })} ); }; diff --git a/packages/react-charts/src/components/ChartLegend/ChartLegend.tsx b/packages/react-charts/src/components/ChartLegend/ChartLegend.tsx index ef63f636460..f24c6e8d0dd 100644 --- a/packages/react-charts/src/components/ChartLegend/ChartLegend.tsx +++ b/packages/react-charts/src/components/ChartLegend/ChartLegend.tsx @@ -21,7 +21,7 @@ import { ChartContainer } from '../ChartContainer'; import { ChartLabel } from '../ChartLabel'; import { ChartPoint } from '../ChartPoint'; import { ChartThemeDefinition } from '../ChartTheme'; -import { getTheme } from '../ChartUtils'; +import { getTheme, PatternScaleInterface } from '../ChartUtils'; export enum ChartLegendOrientation { horizontal = 'horizontal', @@ -213,7 +213,7 @@ export interface ChartLegendProps extends VictoryLegendProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; /** * The responsive prop specifies whether the rendered container should be a responsive container with a viewBox * attribute, or a static container with absolute width and height. @@ -362,7 +362,7 @@ export const ChartLegend: React.FunctionComponent = ({ : undefined; const pattern = patternScale[index % patternScale.length]; const color = colorScale ? colorScale[index % colorScale.length] : themeColor; // Sync color scale - return pattern && pattern !== null ? pattern : color; + return pattern && pattern.visible !== false ? pattern.value : color; }, ..._style.data }; diff --git a/packages/react-charts/src/components/ChartLegendTooltip/ChartLegendTooltip.tsx b/packages/react-charts/src/components/ChartLegendTooltip/ChartLegendTooltip.tsx index af664cf53c9..ebe5bc4347d 100644 --- a/packages/react-charts/src/components/ChartLegendTooltip/ChartLegendTooltip.tsx +++ b/packages/react-charts/src/components/ChartLegendTooltip/ChartLegendTooltip.tsx @@ -18,7 +18,8 @@ import { getLegendTooltipSize, getLegendTooltipVisibleData, getLegendTooltipVisibleText, - getTheme + getTheme, + PatternScaleInterface } from '../ChartUtils'; /** @@ -237,7 +238,7 @@ export interface ChartLegendTooltipProps extends ChartCursorTooltipProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; /** * The pointerLength prop determines the length of the triangular pointer extending from the flyout. This prop may be * given as a positive number or a function of datum. diff --git a/packages/react-charts/src/components/ChartLegendTooltip/ChartLegendTooltipContent.tsx b/packages/react-charts/src/components/ChartLegendTooltip/ChartLegendTooltipContent.tsx index 895212f7a45..32e1f100e26 100644 --- a/packages/react-charts/src/components/ChartLegendTooltip/ChartLegendTooltipContent.tsx +++ b/packages/react-charts/src/components/ChartLegendTooltip/ChartLegendTooltipContent.tsx @@ -11,7 +11,8 @@ import { getLegendTooltipSize, getLegendTooltipVisibleData, getLegendTooltipVisibleText, - getTheme + getTheme, + PatternScaleInterface } from '../ChartUtils'; /** @@ -128,7 +129,7 @@ export interface ChartLegendTooltipContentProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; /** * The text prop defines the text ChartTooltip will render. The text prop may be given as a string, number, or * function of datum. When ChartLabel is used as the labelComponent, strings may include newline characters, which diff --git a/packages/react-charts/src/components/ChartPie/ChartPie.tsx b/packages/react-charts/src/components/ChartPie/ChartPie.tsx index 84c78cff522..5d697f8920a 100644 --- a/packages/react-charts/src/components/ChartPie/ChartPie.tsx +++ b/packages/react-charts/src/components/ChartPie/ChartPie.tsx @@ -34,7 +34,8 @@ import { getPatternDefs, getTheme, getDefaultColorScale, - getDefaultPatternScale + getDefaultPatternScale, + PatternScaleInterface } from '../ChartUtils'; export enum ChartPieLabelPosition { @@ -363,13 +364,6 @@ export interface ChartPieProps extends VictoryPieProps { * @propType number | { top: number, bottom: number, left: number, right: number } */ padding?: PaddingProps; - /** - * The optional ID to prefix pattern defs - * - * @example patternId="pattern" - * @beta - */ - patternId?: string; /** * The patternScale prop is an optional prop that defines a pattern to be applied to the children, where applicable. * This prop should be given as an array of CSS colors, or as a string corresponding to a URL. Patterns will be @@ -382,7 +376,15 @@ export interface ChartPieProps extends VictoryPieProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; + /** + * Flag indicating to shift pattern scale for ChartDonutThreshold + * Do not use + * + * @private + * @hide + */ + patternShift?: number; /** * Specifies the radius of the chart. If this property is not provided it is computed * from width, height, and padding props @@ -505,8 +507,8 @@ export const ChartPie: React.FunctionComponent = ({ legendComponent = , legendData, legendPosition = ChartCommonStyles.legend.position as ChartPieLegendPosition, - patternId = getPatternId(), patternScale, + patternShift, padding, radius, standalone = true, @@ -534,6 +536,16 @@ export const ChartPie: React.FunctionComponent = ({ right: getPaddingForSide('right', padding, theme.pie.padding), top: getPaddingForSide('top', padding, theme.pie.padding) }; + + const patternId = React.useMemo(() => getPatternId(), []); + const defaultColorScale = getDefaultColorScale(colorScale, theme.pie.colorScale as string[]); + const defaultPatternScale = getDefaultPatternScale({ + colorScale: defaultColorScale, + isPatternDefs, + patternId, + patternScale + }); + // Ensure non-negative value is returned const getDefaultRadius = () => { const result = Helpers.getRadius({ @@ -545,14 +557,6 @@ export const ChartPie: React.FunctionComponent = ({ }; const chartRadius = radius ? radius : getDefaultRadius(); - const defaultColorScale = getDefaultColorScale(colorScale, theme.pie.colorScale as string[]); - const defaultPatternScale = getDefaultPatternScale({ - colorScale: defaultColorScale, - patternScale, - patternId, - isPatternDefs - }); - // Merge pattern IDs with `style.data.fill` property const getDefaultStyle = () => { if (!defaultPatternScale) { @@ -562,7 +566,9 @@ export const ChartPie: React.FunctionComponent = ({ _style.data = { fill: ({ slice }: any) => { const pattern = defaultPatternScale[slice.index % defaultPatternScale.length]; - return pattern && pattern !== null ? pattern : defaultColorScale[slice.index % defaultColorScale.length]; + return pattern && pattern.visible !== false + ? pattern.value + : defaultColorScale[slice.index % defaultColorScale.length].value; }, ..._style.data }; @@ -624,7 +630,11 @@ export const ChartPie: React.FunctionComponent = ({ theme, ...containerComponent.props }, - [chart, getLegend(), isPatternDefs && getPatternDefs({ patternId, patternScale: defaultColorScale })] + [ + chart, + getLegend(), + isPatternDefs && getPatternDefs({ patternId, colorScale: defaultColorScale, patternShift }) + ] ) : null; @@ -634,7 +644,7 @@ export const ChartPie: React.FunctionComponent = ({ {chart} {getLegend()} - {isPatternDefs && getPatternDefs({ patternId, patternScale: defaultColorScale })} + {isPatternDefs && getPatternDefs({ patternId, colorScale: defaultColorScale, patternShift })} ); }; diff --git a/packages/react-charts/src/components/ChartStack/ChartStack.tsx b/packages/react-charts/src/components/ChartStack/ChartStack.tsx index 73e32fa3acb..4f91ad0867f 100644 --- a/packages/react-charts/src/components/ChartStack/ChartStack.tsx +++ b/packages/react-charts/src/components/ChartStack/ChartStack.tsx @@ -25,6 +25,7 @@ import { getDefaultPatternScale, getPatternId, getTheme, + PatternScaleInterface, renderChildrenWithPatterns } from '../ChartUtils'; @@ -277,13 +278,6 @@ export interface ChartStackProps extends VictoryStackProps { * @propType number | { top: number, bottom: number, left: number, right: number } */ padding?: PaddingProps; - /** - * The optional ID to prefix pattern defs - * - * @example patternId="pattern" - * @beta - */ - patternId?: string; /** * The patternScale prop is an optional prop that defines a pattern to be applied to the children, where applicable. * This prop should be given as an array of CSS colors, or as a string corresponding to a URL. Patterns will be @@ -296,7 +290,7 @@ export interface ChartStackProps extends VictoryStackProps { * @example patternScale={['url("#pattern:0")', 'url("#pattern:1")', 'url("#pattern:2")']} * @beta */ - patternScale?: string[]; + patternScale?: PatternScaleInterface[]; /** * Victory components can pass a boolean polar prop to specify whether a label is part of a polar chart. * @@ -418,7 +412,6 @@ export const ChartStack: React.FunctionComponent = ({ children, colorScale, containerComponent = , - patternId = getPatternId(), patternScale, themeColor, // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -438,12 +431,13 @@ export const ChartStack: React.FunctionComponent = ({ className: getClassName({ className: containerComponent.props.className }) // Override VictoryContainer class name }); + const patternId = React.useMemo(() => getPatternId(), []); const defaultColorScale = getDefaultColorScale(colorScale, theme.stack.colorScale as string[]); const defaultPatternScale = getDefaultPatternScale({ colorScale: defaultColorScale, - patternScale, + isPatternDefs, patternId, - isPatternDefs + patternScale }); // Note: containerComponent is required for theme diff --git a/packages/react-charts/src/components/ChartUtils/chart-legend.ts b/packages/react-charts/src/components/ChartUtils/chart-legend.ts index c4ebd392ea8..56abe0fd803 100644 --- a/packages/react-charts/src/components/ChartUtils/chart-legend.ts +++ b/packages/react-charts/src/components/ChartUtils/chart-legend.ts @@ -5,6 +5,7 @@ import { ChartLegendOrientation, ChartLegendPosition, ChartLegendProps } from '. import { ChartCommonStyles, ChartThemeDefinition } from '../ChartTheme'; import { getPieOrigin } from './chart-origin'; import * as React from 'react'; +import { PatternScaleInterface } from './chart-patterns'; interface ChartLegendInterface { allowWrap?: boolean; // Allow legend items to wrap to the next line @@ -16,7 +17,7 @@ interface ChartLegendInterface { legendComponent: React.ReactElement; // The base legend component to render orientation?: 'horizontal' | 'vertical'; // Orientation of legend padding: PaddingProps; // Chart padding - patternScale?: string[]; // Legend symbol patterns + patternScale?: PatternScaleInterface[]; // Legend symbol patterns position: 'bottom' | 'bottom-left' | 'right'; // The legend position theme: ChartThemeDefinition; // The theme that will be applied to the chart width: number; // Overall width of SVG diff --git a/packages/react-charts/src/components/ChartUtils/chart-patterns.tsx b/packages/react-charts/src/components/ChartUtils/chart-patterns.tsx index dccc97aaf46..4da025a07ba 100644 --- a/packages/react-charts/src/components/ChartUtils/chart-patterns.tsx +++ b/packages/react-charts/src/components/ChartUtils/chart-patterns.tsx @@ -1,14 +1,22 @@ import * as React from 'react'; +import merge from 'lodash/merge'; import uniqueId from 'lodash/uniqueId'; +// @beta +export interface PatternScaleInterface { + value?: string; // This value is output as "fill" for component styles and as "stroke" for pattern defs + visible?: boolean; +} + // @beta interface PatternPropsInterface { children?: any; colorScale?: any; + isPatternDefs?: boolean; offset?: number; patternId?: string; - patternScale?: string[]; - isPatternDefs?: boolean; + patternScale?: PatternScaleInterface[]; + patternShift?: number; } /** @@ -230,13 +238,23 @@ export const getPatternDefsId = (prefix: string, index: number) => { * Helper function to return pattern defs * @private */ -export const getPatternDefs = ({ offset = 0, patternId, patternScale }: PatternPropsInterface) => { +export const getPatternDefs = ({ colorScale, offset = 0, patternId, patternShift = 0 }: PatternPropsInterface) => { + const defaultPatterns = [...patterns]; + + if (patternShift > 0) { + for (let i = 0; i < patternShift; i++) { + defaultPatterns.push(defaultPatterns.splice(0, 1)[0]); + } + } + // This is wrapped in an empty tag so Victory does not apply child props to defs const defs = ( - {patternScale.map((c: string, index: number) => { - const { d, fill, stroke = c, strokeWidth, ...rest } = patterns[(index + offset) % patterns.length]; + {colorScale.map((color: PatternScaleInterface, index: number) => { + const { d, fill, stroke = color.value, strokeWidth, ...rest } = defaultPatterns[ + (index + offset) % defaultPatterns.length + ]; const id = getPatternDefsId(patternId, index); return ( @@ -254,15 +272,24 @@ export const getPatternDefs = ({ offset = 0, patternId, patternScale }: PatternP * Helper function to return pattern IDs to use as color scale * @private */ -export const getPatternScale = (patternId: string, colorScale: string[]) => - colorScale.map((c: any, index: number) => `url(#${getPatternDefsId(patternId, index)})`); +export const getPatternScale = (colorScale: string[], patternId: string) => + colorScale.map((val: any, index: number) => `url(#${getPatternDefsId(patternId, index)})`); /** * Helper function to return default color scale * @private */ -export const getDefaultColorScale = (colorScale: string[], themeColorScale: string[]) => - colorScale ? colorScale : themeColorScale; +export const getDefaultColorScale = (colorScale: string[], themeColorScale: string[]) => { + const result: PatternScaleInterface[] = []; + const colors = colorScale ? colorScale : themeColorScale; + + colors.forEach(val => + result.push({ + value: val + }) + ); + return result; +}; /** * Helper function to return default pattern scale @@ -270,31 +297,39 @@ export const getDefaultColorScale = (colorScale: string[], themeColorScale: stri */ export const getDefaultPatternScale = ({ colorScale, - patternScale, + isPatternDefs, patternId, - isPatternDefs + patternScale }: PatternPropsInterface) => { - if (patternScale) { - return patternScale; - } + const result: PatternScaleInterface[] = []; + if (isPatternDefs) { - return getPatternScale(patternId, colorScale); + const defaultPatterns = getPatternScale(colorScale, patternId); + defaultPatterns.forEach(p => + result.push({ + value: p, + visible: true + }) + ); + } + if (patternScale) { + merge(result, patternScale); } - return undefined; + return result.length > 0 ? result : undefined; }; /** * Merge pattern IDs with `data.fill` property, used by interactive pie chart legend * @private */ -export const getDefaultData = (data: any, patternScale: string[]) => { +export const getDefaultData = (data: any, patternScale: PatternScaleInterface[]) => { if (!patternScale) { return data; } return data.map((datum: any, index: number) => { const pattern = patternScale[index % patternScale.length]; return { - ...(pattern && pattern !== null && { _fill: pattern }), + ...(pattern && pattern.visible !== false && { _fill: pattern.value }), ...datum }; }); @@ -312,9 +347,9 @@ export const renderChildrenWithPatterns = ({ children, patternId, patternScale } // Merge pattern IDs with `style.data.fill` property if (patternScale) { - const fill = patternScale[index % patternScale.length]; + const pattern = patternScale[index % patternScale.length]; style.data = { - ...(fill && fill !== null && { fill }), + ...(pattern && pattern.visible !== false && { fill: pattern.value }), ...style.data }; } diff --git a/packages/react-charts/src/components/ChartUtils/chart-tooltip.ts b/packages/react-charts/src/components/ChartUtils/chart-tooltip.ts index 08974b84e02..6768cbde4a5 100644 --- a/packages/react-charts/src/components/ChartUtils/chart-tooltip.ts +++ b/packages/react-charts/src/components/ChartUtils/chart-tooltip.ts @@ -4,6 +4,7 @@ import { Helpers, OrientationTypes, StringOrNumberOrCallback } from 'victory-cor import { ChartLegendProps } from '../ChartLegend'; import { ChartLegendTooltipStyles, ChartThemeDefinition } from '../ChartTheme'; import { getLegendDimensions } from './chart-legend'; +import { PatternScaleInterface } from './chart-patterns'; interface ChartCursorTooltipCenterOffsetInterface { offsetCursorDimensionX?: boolean; // Adjust the tooltip to appear to the right of the vertical cursor @@ -28,7 +29,7 @@ interface ChartLegendTooltipVisibleDataInterface { activePoints?: any[]; colorScale?: string[]; legendData: any; - patternScale?: string[]; // Legend symbol patterns + patternScale?: PatternScaleInterface[]; // Legend symbol patterns text?: StringOrNumberOrCallback | string[] | number[]; textAsLegendData?: boolean; theme: ChartThemeDefinition; @@ -243,7 +244,7 @@ export const getLegendTooltipVisibleData = ({ result.push({ name: textAsLegendData ? _text[index] : data.name, symbol: { - fill: pattern && pattern !== null ? pattern : color, + fill: pattern && pattern.visible !== false ? pattern.value : color, ...data.symbol } }); diff --git a/packages/react-charts/src/components/Patterns/examples/patterms.md b/packages/react-charts/src/components/Patterns/examples/patterms.md index c47b3953abb..2c308c32f8e 100644 --- a/packages/react-charts/src/components/Patterns/examples/patterms.md +++ b/packages/react-charts/src/components/Patterns/examples/patterms.md @@ -56,6 +56,10 @@ Note: PatternFly React charts live in its own package at [@patternfly/react-char PatternFly React charts are based on the [Victory](https://formidable.com/open-source/victory/docs/victory-chart/) chart library, along with additional functionality, custom components, and theming for PatternFly. This provides a collection of React based components you can use to build PatternFly patterns with consistent markup, styling, and behavior. +### Interactive legend with pie chart + +This demonstrates how to add an interactive legend to a pie chart using events such as `onMouseOver`, `onMouseOut`, and `onClick`. + ## Examples ### Basic pie chart ```js @@ -368,7 +372,6 @@ class InteractivePieLegendChart extends React.Component { right: 20, top: 20 }} - patternId="pattern_a" // Required for interactive legend functionality showAxis={false} themeColor={ChartThemeColor.multiUnordered} isPatternDefs @@ -685,8 +688,13 @@ import { ChartPie, ChartThemeColor } from '@patternfly/react-charts'; right: 140, // Adjusted to accommodate legend top: 20 }} - patternId="pattern_b" - patternScale={['url("#pattern_b:0")', 'url("#pattern_b:1")', null, null, null]} + patternScale={[ + { visible: true }, + { visible: true }, + { visible: false }, + { visible: false }, + { visible: false } + ]} themeColor={ChartThemeColor.multiUnordered} isPatternDefs width={350} @@ -711,7 +719,7 @@ import chart_color_green_300 from '@patternfly/react-tokens/dist/esm/chart_color @@ -750,10 +761,10 @@ import chart_color_green_300 from '@patternfly/react-tokens/dist/esm/chart_color
- + - + @@ -774,7 +785,11 @@ import chart_color_green_300 from '@patternfly/react-tokens/dist/esm/chart_color right: 140, // Adjusted to accommodate legend top: 20 }} - patternScale={['url("#pattern_d:0")', 'url("#pattern_d:1")', null]} + patternScale={[ + { value: 'url("#pattern:0")' }, + { value: 'url("#pattern:1")' }, + { visible: false } + ]} themeColor={ChartThemeColor.multiUnordered} width={350} />