diff --git a/demo/demo-data.ts b/demo/demo-data.ts
index 0ce6d4c40..ced1d633f 100644
--- a/demo/demo-data.ts
+++ b/demo/demo-data.ts
@@ -91,3 +91,21 @@ export const accessibilityErrorBarData = [
{ x: 34, y: 65, error: 0.15 },
{ x: 43, y: 50, error: 0.2 }
];
+
+export const accessibilityGroupData = {
+ a: [
+ { x: 1, y: 1 },
+ { x: 2, y: 2 },
+ { x: 3, y: 5 }
+ ],
+ b: [
+ { x: 1, y: 2 },
+ { x: 2, y: 1 },
+ { x: 3, y: 7 }
+ ],
+ c: [
+ { x: 1, y: 3 },
+ { x: 2, y: 4 },
+ { x: 3, y: 9 }
+ ]
+};
diff --git a/demo/js/components/accessibility-demo.js b/demo/js/components/accessibility-demo.js
index b18d0aedc..85023cc0a 100644
--- a/demo/js/components/accessibility-demo.js
+++ b/demo/js/components/accessibility-demo.js
@@ -1,6 +1,7 @@
import React from "react";
import { Curve } from "Packages/victory-line";
import { VictoryLine } from "Packages/victory-line";
+import { VictoryGroup } from "Packages/victory-group";
import { VictoryStack } from "Packages/victory-stack";
import { VictoryChart } from "Packages/victory-chart";
import { VictoryScatter } from "Packages/victory-scatter";
@@ -11,13 +12,21 @@ import { VictoryArea, Area } from "Packages/victory-area";
import { VictoryVoronoi, Voronoi } from "Packages/victory-voronoi";
import { ErrorBar, VictoryErrorBar } from "Packages/victory-errorbar";
import { Candle, VictoryCandlestick } from "Packages/victory-candlestick";
-import { LineSegment, Whisker, Border, Point, VictoryLabel } from "Packages/victory-core";
+import {
+ LineSegment,
+ Whisker,
+ Border,
+ Point,
+ VictoryLabel,
+ VictoryAccessibleGroup
+} from "Packages/victory-core";
import {
accessibilityBarData,
accessibilityBoxData,
accessibilityPieData,
- accessibilityLineData,
accessibilityAreaData,
+ accessibilityLineData,
+ accessibilityGroupData,
accessibilityScatterData,
accessibilityVoronoiData,
accessibilityErrorBarData,
@@ -34,14 +43,16 @@ const pageHeadingStyle = {
const chartHeadingStyle = {
marginBottom: "0px",
- marginTop: "25px"
+ marginTop: "25px",
+ fontSize: "calc(1vw + 5px)"
};
const containerStyle = {
display: "flex",
flexFlow: "row wrap",
alignItems: "center",
- justifyContent: "flex-start"
+ justifyContent: "flex-start",
+ maxWidth: "1300px"
};
const chartContainerStyle = {
@@ -49,7 +60,8 @@ const chartContainerStyle = {
flexDirection: "column",
alignItems: "center",
width: "50%",
- height: "50%"
+ height: "50%",
+ padding: "25px"
};
export default class App extends React.Component {
@@ -76,6 +88,7 @@ export default class App extends React.Component {
/>
+
{/** BOX PLOT */}
Boxplot
@@ -293,6 +306,38 @@ export default class App extends React.Component {
/>
+
+ {/**ACCESSIBLE GROUP */}
+
+
Accessible Group
+
+
+ }
+ >
+
+
+ }
+ />
+
+
+
+
>
);
diff --git a/demo/ts/components/accessibility-demo.tsx b/demo/ts/components/accessibility-demo.tsx
index 3d4af6d1d..07b0fb8f4 100644
--- a/demo/ts/components/accessibility-demo.tsx
+++ b/demo/ts/components/accessibility-demo.tsx
@@ -1,6 +1,7 @@
import React from "react";
import { isNumber } from "lodash";
import { Curve } from "@packages/victory-line";
+import { VictoryGroup } from "@packages/victory-group";
import { VictoryStack } from "@packages/victory-stack";
import { VictoryLine } from "@packages/victory-line";
import { VictoryBar, Bar } from "@packages/victory-bar";
@@ -12,13 +13,21 @@ import { VictoryBoxPlot } from "@packages/victory-box-plot";
import { VictoryVoronoi, Voronoi } from "@packages/victory-voronoi";
import { ErrorBar, VictoryErrorBar } from "@packages/victory-errorbar";
import { Candle, VictoryCandlestick } from "@packages/victory-candlestick";
-import { LineSegment, Whisker, Border, Point, VictoryLabel } from "@packages/victory-core";
+import {
+ LineSegment,
+ Whisker,
+ Border,
+ Point,
+ VictoryLabel,
+ VictoryAccessibleGroup
+} from "@packages/victory-core";
import {
accessibilityBarData,
accessibilityBoxData,
accessibilityPieData,
- accessibilityLineData,
accessibilityAreaData,
+ accessibilityLineData,
+ accessibilityGroupData,
accessibilityScatterData,
accessibilityVoronoiData,
accessibilityErrorBarData,
@@ -35,7 +44,8 @@ const pageHeadingStyle: React.CSSProperties = {
const chartHeadingStyle: React.CSSProperties = {
marginBottom: "0px",
- marginTop: "25px"
+ marginTop: "25px",
+ fontSize: "calc(1vw + 5px)"
};
const containerStyle: React.CSSProperties = {
@@ -50,7 +60,8 @@ const chartContainerStyle: React.CSSProperties = {
flexDirection: "column",
alignItems: "center",
width: "50%",
- height: "50%"
+ height: "50%",
+ padding: "25px"
};
export const assignIndexValue = (index: number | string, value: number): number => {
@@ -66,6 +77,7 @@ export default class VictoryAccessibilityDemo extends React.Component {
Tabbable charts with aria-labels
+ {/**BAR */}
Bar chart
@@ -81,6 +93,8 @@ export default class VictoryAccessibilityDemo extends React.Component {
/>
+
+ {/** BOXPLOT */}
BoxPlot
@@ -137,10 +151,20 @@ export default class VictoryAccessibilityDemo extends React.Component {
{/** AREA */}
Area
-
-
+
+
+ }
+ >
`area chart stack ${data[0]._stack}`}
@@ -149,6 +173,7 @@ export default class VictoryAccessibilityDemo extends React.Component {
}
/>
{
/>
`area chart stack ${data[0]._stack}`}
@@ -168,6 +194,7 @@ export default class VictoryAccessibilityDemo extends React.Component {
/>
`area chart stack ${data[0]._stack}`}
@@ -299,6 +326,38 @@ export default class VictoryAccessibilityDemo extends React.Component {
/>
+
+ {/**ACCESSIBLE GROUP */}
+
+
Accessible Group
+
+
+ }
+ >
+
+
+ }
+ />
+
+
+
+
>
);
diff --git a/packages/victory-core/src/index.d.ts b/packages/victory-core/src/index.d.ts
index 3e647634c..0e237203d 100644
--- a/packages/victory-core/src/index.d.ts
+++ b/packages/victory-core/src/index.d.ts
@@ -271,6 +271,7 @@ export class VictoryContainer extends React.Component
{}
+
+// #endregion
+
// #region Victory Theme
export type ThemeBaseProps = {
diff --git a/packages/victory-core/src/index.js b/packages/victory-core/src/index.js
index 3e5f4f3b8..ce81cb6a8 100644
--- a/packages/victory-core/src/index.js
+++ b/packages/victory-core/src/index.js
@@ -1,3 +1,4 @@
+export { default as VictoryAccessibleGroup } from "./victory-accessible-group/victory-accessible-group";
export { default as VictoryAnimation } from "./victory-animation/victory-animation";
export { default as VictoryContainer } from "./victory-container/victory-container";
export { default as VictoryLabel } from "./victory-label/victory-label";
diff --git a/packages/victory-core/src/victory-accessible-group/victory-accessible-group.js b/packages/victory-core/src/victory-accessible-group/victory-accessible-group.js
new file mode 100644
index 000000000..7e2147259
--- /dev/null
+++ b/packages/victory-core/src/victory-accessible-group/victory-accessible-group.js
@@ -0,0 +1,46 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+class VictoryAccessibleGroup extends React.Component {
+ static displayName = "VictoryAccessibleGroup";
+ static propTypes = {
+ "aria-describedby": PropTypes.string,
+ "aria-label": PropTypes.string,
+ desc: PropTypes.string,
+ children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
+ className: PropTypes.string,
+ tabIndex: PropTypes.number
+ };
+
+ static defaultProps = {
+ className: "VictoryAccessibleGroup"
+ };
+
+ render() {
+ const { desc, children, className, tabIndex } = this.props;
+ const descId = desc && (this.props["aria-describedby"] || desc.split(" ").join("-"));
+
+ return desc ? (
+
+ {desc}
+ {children}
+
+ ) : (
+
+ {children}
+
+ );
+ }
+}
+
+export default VictoryAccessibleGroup;
diff --git a/packages/victory-core/src/victory-clip-container/victory-clip-container.js b/packages/victory-core/src/victory-clip-container/victory-clip-container.js
index d6b46b263..29b667147 100644
--- a/packages/victory-core/src/victory-clip-container/victory-clip-container.js
+++ b/packages/victory-core/src/victory-clip-container/victory-clip-container.js
@@ -11,6 +11,7 @@ export default class VictoryClipContainer extends React.Component {
static displayName = "VictoryClipContainer";
static role = "container";
static propTypes = {
+ "aria-label": PropTypes.string,
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
circleComponent: PropTypes.element,
className: PropTypes.string,
@@ -30,6 +31,7 @@ export default class VictoryClipContainer extends React.Component {
polar: PropTypes.bool,
radius: CustomPropTypes.nonNegative,
style: PropTypes.object,
+ tabIndex: PropTypes.number,
transform: PropTypes.string,
translateX: PropTypes.number,
translateY: PropTypes.number
@@ -61,7 +63,7 @@ export default class VictoryClipContainer extends React.Component {
}
renderClippedGroup(props, clipId) {
- const { style, events, transform, children, className, groupComponent } = props;
+ const { style, events, transform, children, className, groupComponent, tabIndex } = props;
const clipComponent = this.renderClipComponent(props, clipId);
const groupProps = assign(
{
@@ -73,17 +75,18 @@ export default class VictoryClipContainer extends React.Component {
},
events
);
- return React.cloneElement(groupComponent, groupProps, [
- clipComponent,
- ...React.Children.toArray(children)
- ]);
+ return React.cloneElement(
+ groupComponent,
+ { ...groupProps, "aria-label": props["aria-label"], tabIndex },
+ [clipComponent, ...React.Children.toArray(children)]
+ );
}
renderGroup(props) {
- const { style, events, transform, children, className, groupComponent } = props;
+ const { style, events, transform, children, className, groupComponent, tabIndex } = props;
return React.cloneElement(
groupComponent,
- assign({ className, style, transform }, events),
+ assign({ className, style, transform, "aria-label": props["aria-label"], tabIndex }, events),
children
);
}
diff --git a/test/client/spec/victory-core/victory-accessible-group/victory-accessible-group.spec.js b/test/client/spec/victory-core/victory-accessible-group/victory-accessible-group.spec.js
new file mode 100644
index 000000000..01cb9dc46
--- /dev/null
+++ b/test/client/spec/victory-core/victory-accessible-group/victory-accessible-group.spec.js
@@ -0,0 +1,50 @@
+/**
+ * Client tests
+ */
+/* global sinon */
+/*eslint-disable max-nested-callbacks */
+/* eslint no-unused-expressions: 0 */
+import React from "react";
+import { shallow, mount } from "enzyme";
+import VictoryAccessibleGroup from "packages/victory-core/src/victory-accessible-group/victory-accessible-group";
+
+describe("components/victory-accessible-group", () => {
+ it("renders an g with an aria-label", () => {
+ const wrapper = shallow();
+ expect(wrapper.find("g")).to.have.length(1);
+ expect(wrapper.find("g").prop("aria-label")).to.equal("test-aria-label");
+ });
+
+ it("renders an g with a tabIndex and className", () => {
+ const wrapper = shallow();
+ expect(wrapper.find("g").prop("tabIndex")).to.equal(5);
+ expect(wrapper.find("g").prop("className")).to.equal("accessibility");
+ });
+
+ it("renders an g with a desc node if given", () => {
+ const wrapper = shallow(
+
+ );
+ expect(wrapper.find("g").prop("aria-describedby")).to.equal("describes group");
+ expect(wrapper.find("desc").text()).to.equal("test description");
+ expect(wrapper.find("desc").props().id).to.equal("describes group");
+ });
+
+ it("uses the desc prop value for descId and aria-describedby if no aria-describedby prop value", () => {
+ const wrapper = shallow(
+
+ );
+ expect(wrapper.find("desc").text()).to.equal("applies to both aria-describeby and descId");
+ expect(wrapper.find("g").prop("aria-describedby")).to.equal(
+ "applies-to-both-aria-describeby-and-descId"
+ );
+ expect(wrapper.find("desc").props().id).to.equal("applies-to-both-aria-describeby-and-descId");
+ });
+});