Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ propComponents: [
hideDarkMode: true
---

import { Chart, ChartArea, ChartAxis, ChartGroup, ChartThreshold, ChartThemeColor, ChartThemeVariant, ChartVoronoiContainer, createContainer } from '@patternfly/react-charts';
import { Chart, ChartArea, ChartAxis, ChartGroup, ChartThreshold, ChartThemeColor, ChartLegendTooltip, ChartThemeVariant, ChartVoronoiContainer, createContainer } from '@patternfly/react-charts';
import '@patternfly/patternfly/patternfly-charts.css';

## Introduction
Expand Down Expand Up @@ -90,13 +90,14 @@ BasicRightAlignedLegend = (

```js title=Cyan-with-bottom-aligned-legend-and-axis-label
import React from 'react';
import { Chart, ChartArea, ChartAxis, ChartGroup, ChartThemeColor, ChartVoronoiContainer, createContainer } from '@patternfly/react-charts';
import { Chart, ChartArea, ChartAxis, ChartGroup, ChartThemeColor, ChartLegendTooltip, ChartVoronoiContainer, createContainer } from '@patternfly/react-charts';
// import '@patternfly/patternfly/patternfly-charts.css'; // Required for mix-blend-mode CSS property

class BottomAlignedLegend extends React.Component {
render() {
// Note: Container order is important
const CursorVoronoiContainer = createContainer("cursor", "voronoi");
const legendData = [{ name: 'Cats' }, { name: 'Dogs', symbol: { type: 'dash' } }, { name: 'Birds' }];

return (
<div>
Expand All @@ -107,15 +108,15 @@ class BottomAlignedLegend extends React.Component {
ariaTitle="Area chart example"
containerComponent={
<CursorVoronoiContainer
constrainToVisibleArea
cursorDimension="x"
labels={({ datum }) => `${datum.name}: ${datum.y}`}
labels={({ datum }) => `${datum.y}`}
labelComponent={<ChartLegendTooltip legendData={legendData} title={(datum) => datum.x}/>}
mouseFollowTooltips
voronoiDimension="x"
voronoiPadding={50}
/>
}
legendData={[{ name: 'Cats' }, { name: 'Dogs' }, { name: 'Birds' }]}
legendData={legendData}
legendPosition="bottom"
height={250}
padding={{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import { ChartArea } from '../ChartArea';
import { ChartGroup } from '../ChartGroup';
import { ChartCursorContainer } from './ChartCursorContainer';

Object.values([true, false]).forEach(() => {
test('ChartVoronoiContainer', () => {
const view = shallow(<ChartCursorContainer />);
expect(view).toMatchSnapshot();
});
});

test('renders container via ChartGroup', () => {
const view = shallow(
<ChartGroup containerComponent={<ChartCursorContainer />} height={200} width={200}>
<ChartArea
data={[
{ name: 'Cats', x: 1, y: 1 },
{ name: 'Cats', x: 2, y: 2 },
{ name: 'Cats', x: 3, y: 3.2 },
{ name: 'Cats', x: 4, y: 3.5 }
]}
/>
<ChartArea
data={[
{ name: 'Dogs', x: 1, y: 0.5 },
{ name: 'Dogs', x: 2, y: 1 },
{ name: 'Dogs', x: 3, y: 2 },
{ name: 'Dogs', x: 4, y: 2.5 },
{ name: 'Dogs', x: 5, y: 2.5 }
]}
/>
</ChartGroup>
);
expect(view).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import * as React from 'react';
import hoistNonReactStatics from 'hoist-non-react-statics';
import { CoordinatesPropType, OriginType } from 'victory-core';
import { VictoryCursorContainer, VictoryCursorContainerProps } from 'victory-cursor-container';
import { ChartLabel } from '../ChartLabel';
import { ChartThemeDefinition } from '../ChartTheme';
import { ChartCursorTooltip } from '../ChartTooltip';
import { getClassName, getTheme } from '../ChartUtils';

/**
* See https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/victory/index.d.ts
*/
export interface ChartCursorContainerProps extends VictoryCursorContainerProps {
/**
* he children prop specifies the child or children that will be rendered within the container. It will be set by
* whatever Victory component is rendering the container.
*
* **This prop should not be set manually.**
*/
children?: React.ReactElement | React.ReactElement[];
/**
* The className prop specifies a className that will be applied to the outer-most div rendered by the container
*/
className?: string;
/**
* The containerId prop may be used to set a deterministic id for the container. When a containerId is not manually
* set, a unique id will be generated. It is usually necessary to set deterministic ids for automated testing.
*/
containerId?: number | string;
/**
* The cursorComponent prop takes a component instance which will be used to render a cursor element. The new element
* created will be supplied with x1, y1, x2 and y2 positioning props.
*/
cursorComponent?: React.ReactElement<any>;
/**
* When the cursorDimension prop is set, the cursor will be a line to inspect the given dimension (either "x" or "y").
* When this prop is not specified, the cursor will be a 2-dimensional crosshair. For example, if you would like to
* inspect the time of time-series data, set dimension={"x"}; the cursor will then be a vertical line that will
* inspect the time value of the current mouse position.
*/
cursorDimension?: 'x' | 'y';
/**
* The cursorLabel prop defines the label that will appear next to the cursor. A label will only appear if cursorLabel
* is set. This prop should be given as a function of a point (an Object with x and y properties).
*
* example: cursorLabel={(point) => point.x}
*/
cursorLabel?: (point: CoordinatesPropType) => any | void;
/**
* The cursorLabelComponent prop takes a component instance which will be used to render a label for the cursor. The
* new element created from the passed cursorLabelComponent will be supplied with the following props: x, y, active,
* text. If cursorLabelComponent is omitted, a new ChartLabel will be created with the props described above.
*/
cursorLabelComponent?: React.ReactElement;
/**
* The cursorLabelOffset prop determines the pixel offset of the cursor label from the cursor point. This prop should
* be an Object with x and y properties, or a number to be used for both dimensions.
*/
cursorLabelOffset?: number | CoordinatesPropType;
/**
* Whenever the mouse is not over the chart, the cursor will not be displayed. If instead you would like to keep it
* displayed, use the defaultCursorValue prop to set the default value. The prop should be a point (an Object with x
* and y properties) for 2-dimensional cursors, or a number for 1-dimensional cursors.
*
* examples: defaultCursorValue={{x: 1, y: 1}}, defaultCursorValue={0}
*/
defaultCursorValue?: number | CoordinatesPropType;
/**
* The desc prop specifies the description of the chart/SVG to assist with
* accessibility for screen readers. The more info about the chart provided in
* the description, the more usable it will be for people using screen readers.
* This prop defaults to an empty string.
*
* @example "Golden retreivers make up 30%, Labs make up 25%, and other dog breeds are
* not represented above 5% each."
*/
desc?: string;
/**
* When the disable prop is set to true, ChartCursorContainer events will not fire.
*/
disable?: boolean;
/**
* The events prop attaches arbitrary event handlers to the container component.
* Event handlers passed from other Victory components are called with their
* corresponding events as well as scale, style, width, height, and data when
* applicable. Use the invert method to convert event coordinate information to
* data. `scale.x.invert(evt.offsetX)`.
*
* @example {{ onClick: (evt) => alert(`x: ${evt.clientX}, y: ${evt.clientY}`)}}
*/
events?: React.DOMAttributes<any>;
/**
* The height props specifies the height the svg viewBox of the container.
* This value should be given as a number of pixels. If no height prop
* is given, the height prop from the child component passed will be used.
*/
height?: number;
/**
* The name prop is used to reference a component instance when defining shared events.
*/
name?: string;
/**
* If provided, the onCursorChange function will be called every time the cursor value changes. onCursorChange is
* called with value (the updated cursor value) and props (the props used by ChartCursorContainer). A common use for
* onCursorChange is to save the cursor value to state and use it in another part of the view.
*
* example: onCursorChange={(value, props) => this.setState({cursorValue: value})}
*/
onCursorChange?: (value: CoordinatesPropType, props: VictoryCursorContainerProps) => void;
/**
* Victory components will pass an origin prop is to define the center point in svg coordinates for polar charts.
*
* **This prop should not be set manually.**
*/
origin?: OriginType;
/**
* Victory components can pass a boolean polar prop to specify whether a label is part of a polar chart.
*
* **This prop should not be set manually.**
*/
polar?: boolean;
/**
* The portalComponent prop takes a component instance which will be used as a container for children that should
* render inside a top-level container so that they will always appear above other elements. ChartTooltip renders
* inside a portal so that tooltips always render above data. VictoryPortal is used to define elements that should
* render in the portal container. This prop defaults to Portal, and should only be overridden when changing rendered
* elements from SVG to another type of element e.g., react-native-svg elements.
*/
portalComponent?: React.ReactElement;
/**
* The portalZIndex prop determines the z-index of the div enclosing the portal component. If a portalZIndex prop is
* not set, the z-index of the enclosing div will be set to 99.
*/
portalZIndex?: number;
/**
* 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.
*
* @default true
*/
responsive?: boolean;
/**
* The style prop specifies styles for your ChartContainer. Any valid inline style properties
* will be applied. Height and width should be specified via the height
* and width props, as they are used to calculate the alignment of
* components within the container. Styles from the child component will
* also be passed, if any exist.
*
* @example {border: 1px solid red}
*/
style?: React.CSSProperties;
/**
* The tabIndex prop specifies the description of the chart/SVG to assist with accessibility.
*/
tabIndex?: number;
/**
* The theme prop specifies a theme to use for determining styles and layout properties for a component. Any styles or
* props defined in theme may be overwritten by props specified on the component instance.
*/
theme?: ChartThemeDefinition;
/**
* Specifies the theme color. Valid values are 'blue', 'green', 'multi', etc.
*
* Note: Not compatible with theme prop
*
* @example themeColor={ChartThemeColor.blue}
*/
themeColor?: string;
/**
* Specifies the theme variant. Valid values are 'dark' or 'light'
*
* Note: Not compatible with theme prop
*
* @example themeVariant={ChartThemeVariant.light}
*/
themeVariant?: string;
/**
* The width props specifies the width of the svg viewBox of the container
* This value should be given as a number of pixels. If no width prop
* is given, the width prop from the child component passed will be used.
*/
width?: number;
}

export const ChartCursorContainer: React.FunctionComponent<ChartCursorContainerProps> = ({
className,
themeColor,
themeVariant,

// destructure last
theme = getTheme(themeColor, themeVariant),
cursorLabelComponent = <ChartLabel />, // Note that Victory provides its own label component here
...rest
}: ChartCursorContainerProps) => {
const chartClassName = getClassName({ className });
const chartCursorLabelComponent = React.cloneElement(cursorLabelComponent, {
theme,
...cursorLabelComponent.props
});

// Note: theme is required by voronoiContainerMixin
return (
// Note: className is valid, but Victory is missing a type
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
<VictoryCursorContainer
className={chartClassName}
cursorLabelComponent={chartCursorLabelComponent}
theme={theme}
{...rest}
/>
);
};
ChartCursorContainer.defaultProps = (VictoryCursorContainer as any).defaultProps;

// Note: VictoryCursorContainer.defaultEvents & VictoryContainer.role must be hoisted
hoistNonReactStatics(ChartCursorContainer, VictoryCursorContainer);
Loading