|
| 1 | +/*eslint-disable max-nested-callbacks */ |
| 2 | +import React from "react"; |
| 3 | +import { render, screen, fireEvent } from "@testing-library/react"; |
| 4 | +import { range } from "lodash"; |
| 5 | +import { VictoryCandlestick, Candle } from "victory-candlestick"; |
| 6 | + |
| 7 | +const MyCandle = () => <div data-testid="my-candle" />; |
| 8 | + |
| 9 | +const dataSet = [ |
| 10 | + { x: 5, open: 10, close: 20, high: 25, low: 5 }, |
| 11 | + { x: 1, open: 80, close: 40, high: 120, low: 10, label: "1" } |
| 12 | +]; |
| 13 | + |
| 14 | +describe("components/victory-candlestick", () => { |
| 15 | + describe("default component rendering", () => { |
| 16 | + it("accepts user props", () => { |
| 17 | + render( |
| 18 | + <VictoryCandlestick |
| 19 | + data-testid="victory-candlestick" |
| 20 | + aria-label="Chart" |
| 21 | + /> |
| 22 | + ); |
| 23 | + |
| 24 | + const svgNode = screen.getByTestId("victory-candlestick"); |
| 25 | + expect(svgNode.getAttribute("aria-label")).toEqual("Chart"); |
| 26 | + }); |
| 27 | + |
| 28 | + it("renders an svg with the correct width and height", () => { |
| 29 | + const { container } = render(<VictoryCandlestick data={dataSet} />); |
| 30 | + const svg = container.querySelector("svg"); |
| 31 | + expect(svg.getAttribute("style")).toContain("width: 100%; height: 100%"); |
| 32 | + }); |
| 33 | + |
| 34 | + it("renders an svg with the correct viewBox", () => { |
| 35 | + const { container } = render(<VictoryCandlestick data={dataSet} />); |
| 36 | + const svg = container.querySelector("svg"); |
| 37 | + const viewBoxValue = `0 0 ${450} ${300}`; |
| 38 | + expect(svg.getAttribute("viewBox")).toEqual(viewBoxValue); |
| 39 | + }); |
| 40 | + |
| 41 | + it("renders 8 points", () => { |
| 42 | + const { container } = render(<VictoryCandlestick />); |
| 43 | + const points = container.querySelectorAll("rect"); |
| 44 | + expect(points).toHaveLength(8); |
| 45 | + }); |
| 46 | + }); |
| 47 | + |
| 48 | + describe("rendering data", () => { |
| 49 | + it("renders injected points for {x, y} shaped data (default)", () => { |
| 50 | + const data = range(5).map((i) => ({ |
| 51 | + x: i, |
| 52 | + open: i, |
| 53 | + close: i, |
| 54 | + high: i, |
| 55 | + low: i |
| 56 | + })); |
| 57 | + render(<VictoryCandlestick data={data} dataComponent={<MyCandle />} />); |
| 58 | + |
| 59 | + const points = screen.getAllByTestId("my-candle"); |
| 60 | + expect(points).toHaveLength(5); |
| 61 | + }); |
| 62 | + |
| 63 | + it("renders points for {x, y} shaped data (default)", () => { |
| 64 | + const data = range(5).map((i) => ({ |
| 65 | + x: i, |
| 66 | + open: i, |
| 67 | + close: i, |
| 68 | + high: i, |
| 69 | + low: i |
| 70 | + })); |
| 71 | + const { container } = render(<VictoryCandlestick data={data} />); |
| 72 | + const points = container.querySelectorAll("rect"); |
| 73 | + expect(points).toHaveLength(5); |
| 74 | + }); |
| 75 | + |
| 76 | + it("renders ordered bars when sortKey is passed", () => { |
| 77 | + const data = range(5) |
| 78 | + .map((i) => ({ x: i, open: i, close: i, high: i, low: i })) |
| 79 | + .reverse(); |
| 80 | + const { container } = render( |
| 81 | + <VictoryCandlestick data={data} sortKey="x" /> |
| 82 | + ); |
| 83 | + const candles = container.querySelectorAll("rect"); |
| 84 | + const xValues = Array.from(candles).map((bar) => bar.getAttribute("x")); |
| 85 | + const xValuesAscending = [...xValues].sort((a, b) => a - b); |
| 86 | + expect(xValues).toEqual(xValuesAscending); |
| 87 | + }); |
| 88 | + |
| 89 | + it("renders reverse ordered bars when sortOrder is descending", () => { |
| 90 | + const data = range(5) |
| 91 | + .map((i) => ({ x: i, open: i, close: i, high: i, low: i })) |
| 92 | + .reverse(); |
| 93 | + const { container } = render( |
| 94 | + <VictoryCandlestick data={data} sortKey="x" sortOrder="descending" /> |
| 95 | + ); |
| 96 | + const candles = container.querySelectorAll("rect"); |
| 97 | + const xValues = Array.from(candles).map((bar) => bar.getAttribute("x")); |
| 98 | + const xValuesDescending = [...xValues].sort((a, b) => b - a); |
| 99 | + expect(xValues).toEqual(xValuesDescending); |
| 100 | + }); |
| 101 | + |
| 102 | + it("renders points for array-shaped data", () => { |
| 103 | + const data = range(10).map((i) => [i, i, i, i, i]); |
| 104 | + const { container } = render( |
| 105 | + <VictoryCandlestick |
| 106 | + data={data} |
| 107 | + x={0} |
| 108 | + open={1} |
| 109 | + close={2} |
| 110 | + high={3} |
| 111 | + low={4} |
| 112 | + /> |
| 113 | + ); |
| 114 | + const points = container.querySelectorAll("rect"); |
| 115 | + expect(points).toHaveLength(10); |
| 116 | + }); |
| 117 | + |
| 118 | + it("renders points for deeply-nested data", () => { |
| 119 | + const data = range(20).map((i) => ({ |
| 120 | + a: { b: [{ x: i, open: i, close: i, high: i, low: i }] } |
| 121 | + })); |
| 122 | + const { container } = render( |
| 123 | + <VictoryCandlestick |
| 124 | + data={data} |
| 125 | + x="a.b[0].x" |
| 126 | + open="a.b[0].open" |
| 127 | + close="a.b[0].close" |
| 128 | + high="a.b[0].high" |
| 129 | + low="a.b[0].low" |
| 130 | + /> |
| 131 | + ); |
| 132 | + const points = container.querySelectorAll("rect"); |
| 133 | + expect(points).toHaveLength(20); |
| 134 | + }); |
| 135 | + |
| 136 | + it("renders data values with null accessor", () => { |
| 137 | + const data = range(10); |
| 138 | + const { container } = render( |
| 139 | + <VictoryCandlestick |
| 140 | + data={data} |
| 141 | + x={null} |
| 142 | + open={null} |
| 143 | + close={null} |
| 144 | + high={null} |
| 145 | + low={null} |
| 146 | + /> |
| 147 | + ); |
| 148 | + const points = container.querySelectorAll("rect"); |
| 149 | + expect(points).toHaveLength(10); |
| 150 | + }); |
| 151 | + |
| 152 | + it("does not render data with null x, open, close, high, or low values", () => { |
| 153 | + const data = [ |
| 154 | + { x: 1, open: 10, close: 17, high: 19, low: 8 }, |
| 155 | + { x: null, open: 17, close: 17, high: 17, low: 17 }, |
| 156 | + { x: 2, open: null, close: 17, high: 17, low: 17 }, |
| 157 | + { x: 3, open: 17, close: null, high: 17, low: 17 }, |
| 158 | + { x: 4, open: 17, close: 17, high: null, low: 17 }, |
| 159 | + { x: 5, open: 17, close: 17, high: 17, low: null } |
| 160 | + ]; |
| 161 | + const { container } = render(<VictoryCandlestick data={data} />); |
| 162 | + const points = container.querySelectorAll("rect"); |
| 163 | + expect(points).toHaveLength(1); |
| 164 | + }); |
| 165 | + }); |
| 166 | + |
| 167 | + describe("event handling", () => { |
| 168 | + const clickHandler = jest.fn(); |
| 169 | + |
| 170 | + beforeEach(() => { |
| 171 | + clickHandler.mockReset(); |
| 172 | + }); |
| 173 | + |
| 174 | + it("attaches an event to data", () => { |
| 175 | + const { container } = render( |
| 176 | + <VictoryCandlestick |
| 177 | + data={dataSet} |
| 178 | + events={[ |
| 179 | + { |
| 180 | + target: "data", |
| 181 | + eventHandlers: { onClick: clickHandler } |
| 182 | + } |
| 183 | + ]} |
| 184 | + /> |
| 185 | + ); |
| 186 | + |
| 187 | + const data = container.querySelectorAll("rect"); |
| 188 | + |
| 189 | + data.forEach((node, index) => { |
| 190 | + clickHandler.mockReset(); |
| 191 | + fireEvent.click(node); |
| 192 | + const { key } = clickHandler.mock.calls[0][1]; |
| 193 | + expect(key).toEqual(`candlestick-data-${index}`); |
| 194 | + }); |
| 195 | + }); |
| 196 | + }); |
| 197 | + |
| 198 | + describe("accessibility", () => { |
| 199 | + it("adds an aria role to each point in the series", () => { |
| 200 | + const data = [ |
| 201 | + { x: 0, open: 9, close: 30, high: 56, low: 7 }, |
| 202 | + { x: 1, open: 80, close: 40, high: 120, low: 10 }, |
| 203 | + { x: 2, open: 50, close: 80, high: 90, low: 20 } |
| 204 | + ]; |
| 205 | + render(<VictoryCandlestick data={data} />); |
| 206 | + |
| 207 | + const presentationElements = screen.getAllByRole("presentation"); |
| 208 | + |
| 209 | + // Each data point is 3 (rect and 2 lines) for 9 total, plus the container element |
| 210 | + expect(presentationElements).toHaveLength(10); |
| 211 | + }); |
| 212 | + |
| 213 | + it("adds an aria-label and tabIndex to Candle primitive", () => { |
| 214 | + const data = [ |
| 215 | + { x: new Date(2016, 6, 1), open: 20, close: 43, high: 66, low: 7 }, |
| 216 | + { x: new Date(2016, 6, 2), open: 80, close: 40, high: 120, low: 10 }, |
| 217 | + { x: new Date(2016, 6, 3), open: 50, close: 80, high: 90, low: 20 } |
| 218 | + ]; |
| 219 | + const { container } = render( |
| 220 | + <VictoryCandlestick |
| 221 | + data={data} |
| 222 | + dataComponent={ |
| 223 | + <Candle |
| 224 | + data-testid="candle" |
| 225 | + ariaLabel={({ datum }) => |
| 226 | + `open ${datum.open}, close ${datum.close}` |
| 227 | + } |
| 228 | + tabIndex={({ index }) => index + 5} |
| 229 | + /> |
| 230 | + } |
| 231 | + /> |
| 232 | + ); |
| 233 | + |
| 234 | + container.querySelectorAll("rect").forEach((node, index) => { |
| 235 | + const expectedLabel = `open ${data[index].open}, close ${data[index].close}`; |
| 236 | + expect(node.getAttribute("aria-label")).toEqual(expectedLabel); |
| 237 | + expect(node.getAttribute("tabindex")).toEqual(`${index + 5}`); |
| 238 | + }); |
| 239 | + }); |
| 240 | + }); |
| 241 | +}); |
0 commit comments