From f9d829cda91a3119cd9043e2fd5a9e5d011b81ab Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Fri, 10 Jan 2020 16:47:39 -0800 Subject: [PATCH 1/3] typescript(vx-shape): add copy() to Scale type --- packages/vx-shape/src/types.ts | 1 + packages/vx-shape/test/AreaClosed.test.tsx | 2 ++ packages/vx-shape/test/BarGroup.test.tsx | 3 +++ .../vx-shape/test/BarGroupHorizontal.test.tsx | 3 +++ packages/vx-shape/test/BarStack.test.tsx | 23 ++++++++++--------- .../vx-shape/test/BarStackHorizontal.test.tsx | 1 + 6 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/vx-shape/src/types.ts b/packages/vx-shape/src/types.ts index 6a5725b93..68d716438 100644 --- a/packages/vx-shape/src/types.ts +++ b/packages/vx-shape/src/types.ts @@ -116,4 +116,5 @@ export interface ScaleType { range(): Output[] | [Output, Output]; domain(): Input[] | [Input, Input]; bandwidth?: () => number; + copy(): this; } diff --git a/packages/vx-shape/test/AreaClosed.test.tsx b/packages/vx-shape/test/AreaClosed.test.tsx index 6657d009a..0f638bd4e 100644 --- a/packages/vx-shape/test/AreaClosed.test.tsx +++ b/packages/vx-shape/test/AreaClosed.test.tsx @@ -18,10 +18,12 @@ const data: Datum[] = [ const xScale = () => 50; xScale.range = () => [0, 100]; xScale.domain = () => [0, 100]; +xScale.copy = () => xScale; const yScale = () => 50; yScale.range = () => [100, 0] as [number, number]; yScale.domain = () => [0, 100] as [number, number]; +yScale.copy = () => yScale; const x = () => xScale(); const y = () => yScale(); diff --git a/packages/vx-shape/test/BarGroup.test.tsx b/packages/vx-shape/test/BarGroup.test.tsx index 9f5528941..4e6dcf0e2 100644 --- a/packages/vx-shape/test/BarGroup.test.tsx +++ b/packages/vx-shape/test/BarGroup.test.tsx @@ -32,15 +32,18 @@ const x0Scale = () => 2; x0Scale.bandwidth = () => 10; x0Scale.domain = () => [0, 100] as [number, number]; x0Scale.range = () => [0, 100] as [number, number]; +x0Scale.copy = () => x0Scale; const x1Scale = () => 5; x1Scale.bandwidth = () => 2; x1Scale.domain = () => [0, 100] as [number, number]; x1Scale.range = () => [0, 100] as [number, number]; +x1Scale.copy = () => x1Scale; const yScale = () => 5; yScale.domain = () => [0, 100] as [number, number]; yScale.range = () => [0, 100] as [number, number]; +yScale.copy = () => yScale; const color = () => 'skyblue'; const keys = ['New York', 'San Francisco', 'Austin']; diff --git a/packages/vx-shape/test/BarGroupHorizontal.test.tsx b/packages/vx-shape/test/BarGroupHorizontal.test.tsx index 8dbf885cc..9747db97e 100644 --- a/packages/vx-shape/test/BarGroupHorizontal.test.tsx +++ b/packages/vx-shape/test/BarGroupHorizontal.test.tsx @@ -32,13 +32,16 @@ const y0Scale = () => 2; y0Scale.bandwidth = () => 10; y0Scale.domain = () => [0, 100] as [number, number]; y0Scale.range = () => [0, 100] as [number, number]; +y0Scale.copy = () => y0Scale; const y1Scale = () => 1; y1Scale.bandwidth = () => 2; y1Scale.domain = () => [0, 100] as [number, number]; y1Scale.range = () => [0, 100] as [number, number]; +y1Scale.copy = () => y1Scale; const xScale = (d: Datum) => 5; xScale.domain = () => [0, 100] as [number, number]; xScale.range = () => [0, 100] as [number, number]; +xScale.copy = () => xScale; const color = () => 'violet'; const keys = ['New York', 'San Francisco', 'Austin']; const width = 1; diff --git a/packages/vx-shape/test/BarStack.test.tsx b/packages/vx-shape/test/BarStack.test.tsx index 81d5fdd00..ea50b5398 100644 --- a/packages/vx-shape/test/BarStack.test.tsx +++ b/packages/vx-shape/test/BarStack.test.tsx @@ -3,11 +3,12 @@ import { shallow } from 'enzyme'; import { BarStack } from '../src'; -const xScale = () => 2; -xScale.domain = () => [0, 100] as [number, number]; -xScale.range = () => [0, 100] as [number, number]; -xScale.bandwidth = () => 2; -xScale.step = () => 2; +const scale = () => 2; +scale.domain = () => [0, 100] as [number, number]; +scale.range = () => [0, 100] as [number, number]; +scale.bandwidth = () => 2; +scale.step = () => 2; +scale.copy = () => scale; describe('', () => { test('it should be defined', () => { @@ -21,8 +22,8 @@ describe('', () => { top={2} left={3} x={d => d} - xScale={xScale} - yScale={xScale} + xScale={scale} + yScale={scale} color={d => d} keys={[]} />, @@ -38,8 +39,8 @@ describe('', () => { top={2} left={3} x={d => d} - xScale={xScale} - yScale={xScale} + xScale={scale} + yScale={scale} color={d => d} keys={[]} />, @@ -55,8 +56,8 @@ describe('', () => { top={2} left={3} x={d => d} - xScale={xScale} - yScale={xScale} + xScale={scale} + yScale={scale} color={d => d} keys={[]} />, diff --git a/packages/vx-shape/test/BarStackHorizontal.test.tsx b/packages/vx-shape/test/BarStackHorizontal.test.tsx index 03fa7fd96..89c5d4497 100644 --- a/packages/vx-shape/test/BarStackHorizontal.test.tsx +++ b/packages/vx-shape/test/BarStackHorizontal.test.tsx @@ -10,6 +10,7 @@ scale.bandwidth = () => 5; scale.step = () => 5; scale.paddingInner = () => 5; scale.paddingOuter = () => 5; +scale.copy = () => scale; describe('', () => { test('it should be defined', () => { From ccb497f787faa2b437518e99826da983a284aab0 Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Fri, 10 Jan 2020 16:48:08 -0800 Subject: [PATCH 2/3] typescript(vx-demo): rewrite Brush demo in TypeScript --- packages/vx-demo/src/components/gallery.js | 4 +- .../components/tiles/{brush.js => Brush.tsx} | 80 +++++++++------- .../vx-demo/src/pages/{brush.js => Brush.tsx} | 91 +++++++++++-------- 3 files changed, 100 insertions(+), 75 deletions(-) rename packages/vx-demo/src/components/tiles/{brush.js => Brush.tsx} (72%) rename packages/vx-demo/src/pages/{brush.js => Brush.tsx} (70%) diff --git a/packages/vx-demo/src/components/gallery.js b/packages/vx-demo/src/components/gallery.js index aec18f02b..25f3d08ff 100644 --- a/packages/vx-demo/src/components/gallery.js +++ b/packages/vx-demo/src/components/gallery.js @@ -42,7 +42,7 @@ import Threshold from './tiles/Threshold.tsx'; import Chord from './tiles/Chord.tsx'; import Polygons from './tiles/Polygons.tsx'; import ZoomI from './tiles/Zoom-i.tsx'; -import BrushChart from './tiles/brush'; +import Brush from './tiles/Brush.tsx'; const items = [ '#242424', @@ -935,7 +935,7 @@ export default function() {
- {({ width, height }) => } + {({ width, height }) => }
diff --git a/packages/vx-demo/src/components/tiles/brush.js b/packages/vx-demo/src/components/tiles/Brush.tsx similarity index 72% rename from packages/vx-demo/src/components/tiles/brush.js rename to packages/vx-demo/src/components/tiles/Brush.tsx index 66614e4f4..e1949399b 100644 --- a/packages/vx-demo/src/components/tiles/brush.js +++ b/packages/vx-demo/src/components/tiles/Brush.tsx @@ -1,24 +1,25 @@ import React, { useState } from 'react'; import { Group } from '@vx/group'; import { AreaClosed, Bar } from '@vx/shape'; +import { ScaleType } from '@vx/shape/lib/types'; import { AxisLeft, AxisBottom } from '@vx/axis'; import { curveMonotoneX } from '@vx/curve'; import { scaleTime, scaleLinear } from '@vx/scale'; -import { appleStock } from '@vx/mock-data'; +import appleStock, { AppleStock } from '@vx/mock-data/lib/mocks/appleStock'; import { Brush } from '@vx/brush'; import { PatternLines } from '@vx/pattern'; import { LinearGradient } from '@vx/gradient'; +import { max, extent } from 'd3-array'; +import { ShowProvidedProps, MarginShape } from '../../types'; +import { Bounds } from '@vx/brush/lib/types'; /** * Initialize some variables */ const stock = appleStock.slice(1200); -const min = (arr, fn) => Math.min(...arr.map(fn)); -const max = (arr, fn) => Math.max(...arr.map(fn)); -const extent = (arr, fn) => [min(arr, fn), max(arr, fn)]; const axisColor = '#fff'; const axisBottomTickLabelProps = { - textAnchor: 'middle', + textAnchor: 'middle' as const, fontFamily: 'Arial', fontSize: 10, fill: axisColor, @@ -28,13 +29,13 @@ const axisLeftTickLabelProps = { dy: '0.25em', fontFamily: 'Arial', fontSize: 10, - textAnchor: 'end', + textAnchor: 'end' as const, fill: axisColor, }; // accessors -const xStock = d => new Date(d.date); -const yStock = d => d.close; +const getDate = (d: AppleStock) => new Date(d.date); +const getStockValue = (d: AppleStock) => d.close; function AreaChart({ data, @@ -49,13 +50,26 @@ function AreaChart({ top, left, children, +}: { + data: AppleStock[]; + xScale: ScaleType; + yScale: ScaleType; + width: number; + height: number; + yMax: number; + margin: MarginShape; + hideBottomAxis?: boolean; + hideLeftAxis?: boolean; + top?: number; + left?: number; + children?: React.ReactNode; }) { return ( {!hideBottomAxis && ( - top={yMax} scale={xScale} numTicks={width > 520 ? 10 : 5} @@ -65,7 +79,7 @@ function AreaChart({ /> )} {!hideLeftAxis && ( - scale={yScale} numTicks={5} stroke={axisColor} @@ -73,10 +87,10 @@ function AreaChart({ tickLabelProps={() => axisLeftTickLabelProps} /> )} - data={data} - x={d => xScale(xStock(d))} - y={d => yScale(yStock(d))} + x={d => xScale(getDate(d)) || 0} + y={d => yScale(getStockValue(d)) || 0} yScale={yScale} strokeWidth={1} stroke="url(#gradient)" @@ -99,19 +113,19 @@ function BrushChart({ bottom: 0, right: 20, }, -}) { +}: ShowProvidedProps & { compact?: boolean }) { const [filteredStock, setFilteredStock] = useState(stock); - function onBrushChange(domain) { + const onBrushChange = (domain: Bounds | null) => { if (!domain) return; const { x0, x1, y0, y1 } = domain; const stockCopy = stock.filter(s => { - const x = xStock(s).getTime(); - const y = yStock(s); + const x = getDate(s).getTime(); + const y = getStockValue(s); return x > x0 && x < x1 && y > y0 && y < y1; }); setFilteredStock(stockCopy); - } + }; const brushMargin = { top: 0, bottom: 20, left: 50, right: 20 }; const chartSeparation = 10; @@ -125,22 +139,22 @@ function BrushChart({ const yBrushMax = Math.max(heightBottomChart - brushMargin.top - brushMargin.bottom, 0); // scales - const xScale = scaleTime({ + const dateScale = scaleTime({ range: [0, xMax], - domain: extent(filteredStock, xStock), + domain: extent(filteredStock, getDate) as [Date, Date], }); - const yScale = scaleLinear({ + const stockScale = scaleLinear({ range: [yMax, 0], - domain: [0, max(filteredStock, yStock) + yMax / 3], + domain: [0, (max(filteredStock, getStockValue) || 0) + yMax / 3], nice: true, }); - const xBrushScale = scaleTime({ + const brushDateScale = scaleTime({ range: [0, xBrushMax], - domain: extent(stock, xStock), + domain: extent(stock, getDate) as [Date, Date], }); - const yBrushScale = scaleLinear({ + const brushStockScale = scaleLinear({ range: [yBrushMax, 0], - domain: [0, max(stock, yStock) + yBrushMax / 3], + domain: [0, (max(stock, getStockValue) || 0) + yBrushMax / 3], nice: true, }); @@ -162,9 +176,8 @@ function BrushChart({ height={heightTopChart} margin={margin} yMax={yMax} - xMax={xMax} - xScale={xScale} - yScale={yScale} + xScale={dateScale} + yScale={stockScale} /> @@ -188,8 +200,8 @@ function BrushChart({ orientation={['diagonal']} /> { return ( { {`import React, { useState } from 'react'; import { Group } from '@vx/group'; import { AreaClosed, Bar } from '@vx/shape'; +import { ScaleType } from '@vx/shape/lib/types'; import { AxisLeft, AxisBottom } from '@vx/axis'; import { curveMonotoneX } from '@vx/curve'; import { scaleTime, scaleLinear } from '@vx/scale'; -import { appleStock } from '@vx/mock-data'; +import appleStock, { AppleStock } from '@vx/mock-data/lib/mocks/appleStock'; import { Brush } from '@vx/brush'; import { PatternLines } from '@vx/pattern'; import { LinearGradient } from '@vx/gradient'; +import { max, extent } from 'd3-array'; +import { ShowProvidedProps, MarginShape } from '../../types'; +import { Bounds } from '@vx/brush/lib/types'; /** * Initialize some variables */ const stock = appleStock.slice(1200); -const min = (arr, fn) => Math.min(...arr.map(fn)); -const max = (arr, fn) => Math.max(...arr.map(fn)); -const extent = (arr, fn) => [min(arr, fn), max(arr, fn)]; const axisColor = '#fff'; const axisBottomTickLabelProps = { - textAnchor: 'middle', + textAnchor: 'middle' as const, fontFamily: 'Arial', fontSize: 10, fill: axisColor, @@ -44,13 +45,13 @@ const axisLeftTickLabelProps = { dy: '0.25em', fontFamily: 'Arial', fontSize: 10, - textAnchor: 'end', + textAnchor: 'end' as const, fill: axisColor, }; // accessors -const xStock = d => new Date(d.date); -const yStock = d => d.close; +const getDate = (d: AppleStock) => new Date(d.date); +const getStockValue = (d: AppleStock) => d.close; function AreaChart({ data, @@ -65,13 +66,26 @@ function AreaChart({ top, left, children, +}: { + data: AppleStock[]; + xScale: ScaleType; + yScale: ScaleType; + width: number; + height: number; + yMax: number; + margin: MarginShape; + hideBottomAxis?: boolean; + hideLeftAxis?: boolean; + top?: number; + left?: number; + children?: React.ReactNode; }) { return ( {!hideBottomAxis && ( - top={yMax} scale={xScale} numTicks={width > 520 ? 10 : 5} @@ -81,7 +95,7 @@ function AreaChart({ /> )} {!hideLeftAxis && ( - scale={yScale} numTicks={5} stroke={axisColor} @@ -89,10 +103,10 @@ function AreaChart({ tickLabelProps={() => axisLeftTickLabelProps} /> )} - data={data} - x={d => xScale(xStock(d))} - y={d => yScale(yStock(d))} + x={d => xScale(getDate(d)) || 0} + y={d => yScale(getStockValue(d)) || 0} yScale={yScale} strokeWidth={1} stroke="url(#gradient)" @@ -115,19 +129,19 @@ function BrushChart({ bottom: 0, right: 20, }, -}) { +}: ShowProvidedProps & { compact?: boolean }) { const [filteredStock, setFilteredStock] = useState(stock); - function onBrushChange(domain) { + const onBrushChange = (domain: Bounds | null) => { if (!domain) return; const { x0, x1, y0, y1 } = domain; const stockCopy = stock.filter(s => { - const x = xStock(s).getTime(); - const y = yStock(s); + const x = getDate(s).getTime(); + const y = getStockValue(s); return x > x0 && x < x1 && y > y0 && y < y1; }); setFilteredStock(stockCopy); - } + }; const brushMargin = { top: 0, bottom: 20, left: 50, right: 20 }; const chartSeparation = 10; @@ -141,22 +155,22 @@ function BrushChart({ const yBrushMax = Math.max(heightBottomChart - brushMargin.top - brushMargin.bottom, 0); // scales - const xScale = scaleTime({ + const dateScale = scaleTime({ range: [0, xMax], - domain: extent(filteredStock, xStock), + domain: extent(filteredStock, getDate) as [Date, Date], }); - const yScale = scaleLinear({ + const stockScale = scaleLinear({ range: [yMax, 0], - domain: [0, max(filteredStock, yStock) + yMax / 3], + domain: [0, (max(filteredStock, getStockValue) || 0) + yMax / 3], nice: true, }); - const xBrushScale = scaleTime({ + const brushDateScale = scaleTime({ range: [0, xBrushMax], - domain: extent(stock, xStock), + domain: extent(stock, getDate) as [Date, Date], }); - const yBrushScale = scaleLinear({ + const brushStockScale = scaleLinear({ range: [yBrushMax, 0], - domain: [0, max(stock, yStock) + yBrushMax / 3], + domain: [0, (max(stock, getStockValue) || 0) + yBrushMax / 3], nice: true, }); @@ -178,9 +192,8 @@ function BrushChart({ height={heightTopChart} margin={margin} yMax={yMax} - xMax={xMax} - xScale={xScale} - yScale={yScale} + xScale={dateScale} + yScale={stockScale} /> @@ -204,14 +216,15 @@ function BrushChart({ orientation={['diagonal']} /> setFilteredStock(stock)} selectedBoxStyle={{ fill: 'url(#brush_pattern)', stroke: 'white', From 4635199a0d23e89c2b4e064773cd4fc5b7354adc Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Fri, 10 Jan 2020 17:04:33 -0800 Subject: [PATCH 3/3] lint(vx-demo): lint Brush demo --- packages/vx-demo/src/components/tiles/Brush.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vx-demo/src/components/tiles/Brush.tsx b/packages/vx-demo/src/components/tiles/Brush.tsx index e1949399b..2fc6cd0b7 100644 --- a/packages/vx-demo/src/components/tiles/Brush.tsx +++ b/packages/vx-demo/src/components/tiles/Brush.tsx @@ -7,11 +7,11 @@ import { curveMonotoneX } from '@vx/curve'; import { scaleTime, scaleLinear } from '@vx/scale'; import appleStock, { AppleStock } from '@vx/mock-data/lib/mocks/appleStock'; import { Brush } from '@vx/brush'; +import { Bounds } from '@vx/brush/lib/types'; import { PatternLines } from '@vx/pattern'; import { LinearGradient } from '@vx/gradient'; import { max, extent } from 'd3-array'; import { ShowProvidedProps, MarginShape } from '../../types'; -import { Bounds } from '@vx/brush/lib/types'; /** * Initialize some variables