Remove log viewer histogram code
parent
fe04ef3476
commit
757fd82ac5
|
@ -1,96 +0,0 @@
|
|||
import React from 'react'
|
||||
import {mount} from 'enzyme'
|
||||
|
||||
import HistogramChart from 'src/shared/components/HistogramChart'
|
||||
import HistogramChartTooltip from 'src/shared/components/HistogramChartTooltip'
|
||||
|
||||
import {RemoteDataState} from 'src/types'
|
||||
|
||||
describe('HistogramChart', () => {
|
||||
test('displays a HistogramChartSkeleton if empty data is passed', () => {
|
||||
const props = {
|
||||
data: [],
|
||||
dataStatus: RemoteDataState.Done,
|
||||
width: 600,
|
||||
height: 400,
|
||||
colorScale: () => 'blue',
|
||||
colors: [],
|
||||
sortBarGroups: (a, b) => a.value - b.value,
|
||||
}
|
||||
|
||||
const wrapper = mount(<HistogramChart {...props} />)
|
||||
|
||||
expect(wrapper).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('displays a nothing if passed width and height of 0', () => {
|
||||
const props = {
|
||||
data: [],
|
||||
dataStatus: RemoteDataState.Done,
|
||||
width: 0,
|
||||
height: 0,
|
||||
colorScale: () => 'blue',
|
||||
colors: [],
|
||||
sortBarGroups: (a, b) => a.value - b.value,
|
||||
}
|
||||
|
||||
const wrapper = mount(<HistogramChart {...props} />)
|
||||
|
||||
expect(wrapper).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('displays the visualization with bars if nonempty data is passed', () => {
|
||||
const props = {
|
||||
data: [
|
||||
{key: '0', time: 0, value: 0, group: 'a'},
|
||||
{key: '1', time: 1, value: 1, group: 'a'},
|
||||
{key: '2', time: 2, value: 2, group: 'b'},
|
||||
],
|
||||
dataStatus: RemoteDataState.Done,
|
||||
width: 600,
|
||||
height: 400,
|
||||
colorScale: () => 'blue',
|
||||
colors: [],
|
||||
sortBarGroups: (a, b) => a.value - b.value,
|
||||
}
|
||||
|
||||
const wrapper = mount(<HistogramChart {...props} />)
|
||||
|
||||
expect(wrapper).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('displays a HistogramChartTooltip when hovering over bars', () => {
|
||||
const props = {
|
||||
data: [
|
||||
{key: '0', time: 0, value: 0, group: 'a'},
|
||||
{key: '1', time: 1, value: 1, group: 'a'},
|
||||
{key: '2', time: 2, value: 2, group: 'b'},
|
||||
],
|
||||
dataStatus: RemoteDataState.Done,
|
||||
width: 600,
|
||||
height: 400,
|
||||
colorScale: () => 'blue',
|
||||
colors: [],
|
||||
sortBarGroups: (a, b) => a.value - b.value,
|
||||
}
|
||||
|
||||
const wrapper = mount(<HistogramChart {...props} />)
|
||||
|
||||
const fakeMouseOverEvent = {
|
||||
target: {
|
||||
getBoundingClientRect() {
|
||||
return {top: 10, right: 10, bottom: 5, left: 5}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
wrapper
|
||||
.find('.histogram-chart-bars--bars')
|
||||
.first()
|
||||
.simulate('mouseover', fakeMouseOverEvent)
|
||||
|
||||
const tooltip = wrapper.find(HistogramChartTooltip)
|
||||
|
||||
expect(tooltip).toMatchSnapshot()
|
||||
})
|
||||
})
|
|
@ -1,214 +0,0 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
import {scaleLinear, scaleTime, ScaleLinear, ScaleTime} from 'd3-scale'
|
||||
|
||||
import HistogramChartAxes from 'src/shared/components/HistogramChartAxes'
|
||||
import HistogramChartBars from 'src/shared/components/HistogramChartBars'
|
||||
import HistogramChartTooltip from 'src/shared/components/HistogramChartTooltip'
|
||||
import HistogramChartSkeleton from 'src/shared/components/HistogramChartSkeleton'
|
||||
|
||||
import extentBy from 'src/utils/extentBy'
|
||||
|
||||
import {
|
||||
HistogramData,
|
||||
Margins,
|
||||
HoverData,
|
||||
ColorScale,
|
||||
HistogramColor,
|
||||
SortFn,
|
||||
} from 'src/types/histogram'
|
||||
|
||||
const PADDING_TOP = 0.2
|
||||
|
||||
// Rather than use these magical constants, we could also render a digit and
|
||||
// capture its measured width with as state before rendering anything else.
|
||||
// Doing so would be robust but overkill.
|
||||
const DIGIT_WIDTH = 7
|
||||
const PERIOD_DIGIT_WIDTH = 4
|
||||
|
||||
interface RenderPropArgs {
|
||||
xScale: ScaleTime<number, number>
|
||||
yScale: ScaleLinear<number, number>
|
||||
adjustedWidth: number
|
||||
adjustedHeight: number
|
||||
margins: Margins
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data: HistogramData
|
||||
width: number
|
||||
height: number
|
||||
colors: HistogramColor[]
|
||||
colorScale: ColorScale
|
||||
onBarClick?: (time: string) => void
|
||||
sortBarGroups: SortFn
|
||||
children?: (args: RenderPropArgs) => JSX.Element
|
||||
}
|
||||
|
||||
interface State {
|
||||
hoverData?: HoverData
|
||||
}
|
||||
|
||||
class HistogramChart extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
colorScale,
|
||||
colors,
|
||||
onBarClick,
|
||||
sortBarGroups,
|
||||
children,
|
||||
} = this.props
|
||||
const {margins} = this
|
||||
|
||||
if (width === 0 || height === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
return (
|
||||
<HistogramChartSkeleton
|
||||
width={width}
|
||||
height={height}
|
||||
margins={margins}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const {hoverData} = this.state
|
||||
const {xScale, yScale, adjustedWidth, adjustedHeight, bodyTransform} = this
|
||||
|
||||
const renderPropArgs = {
|
||||
xScale,
|
||||
yScale,
|
||||
adjustedWidth,
|
||||
adjustedHeight,
|
||||
margins,
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="histogram-chart">
|
||||
<svg width={width} height={height} className="histogram-chart">
|
||||
<defs>
|
||||
<clipPath id="histogram-chart--bars-clip">
|
||||
<rect x="0" y="0" width={adjustedWidth} height={adjustedHeight} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g className="histogram-chart--axes">
|
||||
<HistogramChartAxes
|
||||
width={width}
|
||||
height={height}
|
||||
margins={margins}
|
||||
xScale={xScale}
|
||||
yScale={yScale}
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
transform={bodyTransform}
|
||||
className="histogram-chart--bars"
|
||||
clipPath="url(#histogram-chart--bars-clip)"
|
||||
>
|
||||
<HistogramChartBars
|
||||
width={adjustedWidth}
|
||||
height={adjustedHeight}
|
||||
data={data}
|
||||
xScale={xScale}
|
||||
yScale={yScale}
|
||||
colorScale={colorScale}
|
||||
hoverData={hoverData}
|
||||
onHover={this.handleHover}
|
||||
colors={colors}
|
||||
onBarClick={onBarClick}
|
||||
sortBarGroups={sortBarGroups}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<div className="histogram-chart--overlays">
|
||||
{!!children && children(renderPropArgs)}
|
||||
{hoverData && (
|
||||
<HistogramChartTooltip
|
||||
data={hoverData}
|
||||
colorScale={colorScale}
|
||||
colors={colors}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get xScale(): ScaleTime<number, number> {
|
||||
const {adjustedWidth} = this
|
||||
const {data} = this.props
|
||||
|
||||
const [t0, t1] = extentBy(data, d => d.time)
|
||||
|
||||
return scaleTime()
|
||||
.domain([new Date(t0.time), new Date(t1.time)])
|
||||
.range([0, adjustedWidth])
|
||||
}
|
||||
|
||||
private get yScale(): ScaleLinear<number, number> {
|
||||
const {adjustedHeight, maxAggregateCount} = this
|
||||
|
||||
return scaleLinear()
|
||||
.domain([0, maxAggregateCount + PADDING_TOP * maxAggregateCount])
|
||||
.range([adjustedHeight, 0])
|
||||
}
|
||||
|
||||
private get adjustedWidth(): number {
|
||||
const {margins} = this
|
||||
|
||||
return this.props.width - margins.left - margins.right
|
||||
}
|
||||
|
||||
private get adjustedHeight(): number {
|
||||
const {margins} = this
|
||||
|
||||
return this.props.height - margins.top - margins.bottom
|
||||
}
|
||||
|
||||
private get bodyTransform(): string {
|
||||
const {margins} = this
|
||||
|
||||
return `translate(${margins.left}, ${margins.top})`
|
||||
}
|
||||
|
||||
private get margins(): Margins {
|
||||
const {maxAggregateCount} = this
|
||||
|
||||
const domainTop = maxAggregateCount + PADDING_TOP * maxAggregateCount
|
||||
const left = domainTop.toString().length * DIGIT_WIDTH + PERIOD_DIGIT_WIDTH
|
||||
|
||||
return {top: 5, right: 0, bottom: 20, left}
|
||||
}
|
||||
|
||||
private get maxAggregateCount(): number {
|
||||
const {data} = this.props
|
||||
|
||||
if (!data.length) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const groups = _.groupBy(data, 'time')
|
||||
const counts = Object.values(groups).map(group =>
|
||||
group.reduce((sum, current) => sum + current.value, 0)
|
||||
)
|
||||
|
||||
return Math.max(...counts)
|
||||
}
|
||||
|
||||
private handleHover = (hoverData: HoverData): void => {
|
||||
this.setState({hoverData})
|
||||
}
|
||||
}
|
||||
|
||||
export default HistogramChart
|
|
@ -1,92 +0,0 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import {ScaleLinear, ScaleTime} from 'd3-scale'
|
||||
|
||||
import {Margins} from 'src/types/histogram'
|
||||
|
||||
const Y_TICK_COUNT = 5
|
||||
const Y_TICK_PADDING_RIGHT = 7
|
||||
const X_TICK_COUNT = 10
|
||||
const X_TICK_PADDING_TOP = 8
|
||||
|
||||
interface Props {
|
||||
width: number
|
||||
height: number
|
||||
margins: Margins
|
||||
xScale: ScaleTime<number, number>
|
||||
yScale: ScaleLinear<number, number>
|
||||
}
|
||||
|
||||
class HistogramChartAxes extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {xTickData, yTickData} = this
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.renderYTicks(yTickData)}
|
||||
{this.renderYLabels(yTickData)}
|
||||
{this.renderXLabels(xTickData)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
private renderYTicks(yTickData) {
|
||||
return yTickData.map(({x1, x2, y, key}) => (
|
||||
<line className="y-tick" key={key} x1={x1} x2={x2} y1={y} y2={y} />
|
||||
))
|
||||
}
|
||||
|
||||
private renderYLabels(yTickData) {
|
||||
return yTickData.map(({x1, y, label, key}) => (
|
||||
<text className="y-label" key={key} x={x1 - Y_TICK_PADDING_RIGHT} y={y}>
|
||||
{label}
|
||||
</text>
|
||||
))
|
||||
}
|
||||
|
||||
private renderXLabels(xTickData) {
|
||||
return xTickData.map(({x, y, label, key}) => (
|
||||
<text className="x-label" key={key} y={y} x={x}>
|
||||
{label}
|
||||
</text>
|
||||
))
|
||||
}
|
||||
|
||||
private get xTickData() {
|
||||
const {margins, xScale, width, height} = this.props
|
||||
|
||||
const y = height - margins.bottom + X_TICK_PADDING_TOP
|
||||
const formatTime = xScale.tickFormat()
|
||||
|
||||
return xScale
|
||||
.ticks(X_TICK_COUNT)
|
||||
.filter(val => {
|
||||
const x = xScale(val)
|
||||
|
||||
// Don't render labels that will be cut off
|
||||
return x > margins.left && x < width - margins.right
|
||||
})
|
||||
.map(val => {
|
||||
const x = xScale(val)
|
||||
const label = formatTime(val)
|
||||
const key = `${label}-${x}-${y}`
|
||||
|
||||
return {label, x, y, key}
|
||||
})
|
||||
}
|
||||
|
||||
private get yTickData() {
|
||||
const {width, margins, yScale} = this.props
|
||||
|
||||
return yScale.ticks(Y_TICK_COUNT).map(val => {
|
||||
const label = val
|
||||
const x1 = margins.left
|
||||
const x2 = margins.left + width
|
||||
const y = margins.top + yScale(val)
|
||||
const key = `${label}-${x1}-${x2}-${y}`
|
||||
|
||||
return {label, x1, x2, y, key}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default HistogramChartAxes
|
|
@ -1,261 +0,0 @@
|
|||
import React, {PureComponent, MouseEvent} from 'react'
|
||||
import _ from 'lodash'
|
||||
import {ScaleLinear, ScaleTime} from 'd3-scale'
|
||||
import {color} from 'd3-color'
|
||||
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
|
||||
import {
|
||||
HistogramData,
|
||||
HistogramDatum,
|
||||
HoverData,
|
||||
TooltipAnchor,
|
||||
ColorScale,
|
||||
HistogramColor,
|
||||
SortFn,
|
||||
} from 'src/types/histogram'
|
||||
|
||||
const BAR_BORDER_RADIUS = 3
|
||||
const BAR_PADDING_SIDES = 4
|
||||
const HOVER_BRIGTHEN_FACTOR = 0.4
|
||||
const TOOLTIP_HORIZONTAL_MARGIN = 5
|
||||
const TOOLTIP_REFLECT_DIST = 100
|
||||
|
||||
const getBarWidth = ({data, xScale, width}): number => {
|
||||
const dataInView = data.filter(
|
||||
d => xScale(d.time) >= 0 && xScale(d.time) <= width
|
||||
)
|
||||
const barCount = Object.values(_.groupBy(dataInView, 'time')).length
|
||||
|
||||
return Math.round(width / barCount - BAR_PADDING_SIDES)
|
||||
}
|
||||
|
||||
interface BarGroup {
|
||||
key: string
|
||||
clip: {
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
bars: Array<{
|
||||
key: string
|
||||
group: string
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
fill: string
|
||||
}>
|
||||
data: HistogramData
|
||||
}
|
||||
|
||||
const getBarGroups = ({
|
||||
data,
|
||||
width,
|
||||
xScale,
|
||||
yScale,
|
||||
colorScale,
|
||||
hoverData,
|
||||
colors,
|
||||
sortBarGroups,
|
||||
}: Partial<Props>): BarGroup[] => {
|
||||
const barWidth = getBarWidth({data, xScale, width})
|
||||
const visibleData = data.filter(d => d.value !== 0)
|
||||
const timeGroups = Object.values(_.groupBy(visibleData, 'time'))
|
||||
|
||||
for (const timeGroup of timeGroups) {
|
||||
timeGroup.sort(sortBarGroups)
|
||||
}
|
||||
|
||||
let hoverDataKeys = []
|
||||
|
||||
if (!!hoverData) {
|
||||
hoverDataKeys = hoverData.data.map(h => h.key)
|
||||
}
|
||||
|
||||
return timeGroups.map(timeGroup => {
|
||||
const time = timeGroup[0].time
|
||||
const x = xScale(time) - barWidth / 2
|
||||
const total = _.sumBy(timeGroup, 'value')
|
||||
|
||||
const barGroup = {
|
||||
key: `${time}-${total}-${x}`,
|
||||
clip: {
|
||||
x,
|
||||
y: yScale(total),
|
||||
width: barWidth,
|
||||
height: yScale(0) - yScale(total) + BAR_BORDER_RADIUS,
|
||||
},
|
||||
bars: [],
|
||||
data: timeGroup,
|
||||
}
|
||||
|
||||
let offset = 0
|
||||
|
||||
timeGroup.forEach((d: HistogramDatum) => {
|
||||
const height = yScale(0) - yScale(d.value)
|
||||
const k = hoverDataKeys.includes(d.key) ? HOVER_BRIGTHEN_FACTOR : 0
|
||||
const groupColor = colors.find(c => c.group === d.group)
|
||||
const fill = color(colorScale(_.get(groupColor, 'color', ''), d.group))
|
||||
.brighter(k)
|
||||
.hex()
|
||||
|
||||
barGroup.bars.push({
|
||||
key: d.key,
|
||||
group: d.group,
|
||||
x,
|
||||
y: yScale(d.value) - offset,
|
||||
width: barWidth,
|
||||
height,
|
||||
fill,
|
||||
})
|
||||
|
||||
offset += height
|
||||
})
|
||||
|
||||
return barGroup
|
||||
})
|
||||
}
|
||||
|
||||
interface BarGroup {
|
||||
key: string
|
||||
clip: {
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
bars: Array<{
|
||||
key: string
|
||||
group: string
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
fill: string
|
||||
}>
|
||||
data: HistogramData
|
||||
}
|
||||
interface Props {
|
||||
width: number
|
||||
height: number
|
||||
data: HistogramData
|
||||
xScale: ScaleTime<number, number>
|
||||
yScale: ScaleLinear<number, number>
|
||||
colorScale: ColorScale
|
||||
hoverData?: HoverData
|
||||
colors: HistogramColor[]
|
||||
onHover: (h: HoverData) => void
|
||||
onBarClick?: (time: string) => void
|
||||
sortBarGroups: SortFn
|
||||
}
|
||||
|
||||
interface State {
|
||||
barGroups: BarGroup[]
|
||||
}
|
||||
|
||||
class HistogramChartBars extends PureComponent<Props, State> {
|
||||
public static getDerivedStateFromProps(props: Props) {
|
||||
return {barGroups: getBarGroups(props)}
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {barGroups: []}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {barGroups} = this.state
|
||||
|
||||
return barGroups.map(group => {
|
||||
const {key, clip, bars} = group
|
||||
|
||||
return (
|
||||
<g
|
||||
key={key}
|
||||
className="histogram-chart-bars--bars"
|
||||
data-key={key}
|
||||
onMouseOver={this.handleMouseOver}
|
||||
onMouseOut={this.handleMouseOut}
|
||||
onClick={this.handleBarClick(group.data)}
|
||||
>
|
||||
<defs>
|
||||
<clipPath id={`histogram-chart-bars--clip-${key}`}>
|
||||
<rect
|
||||
x={clip.x}
|
||||
y={clip.y}
|
||||
width={clip.width}
|
||||
height={clip.height}
|
||||
rx={BAR_BORDER_RADIUS}
|
||||
ry={BAR_BORDER_RADIUS}
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
{bars.map(d => (
|
||||
<rect
|
||||
key={d.key}
|
||||
className="histogram-chart-bars--bar"
|
||||
x={d.x}
|
||||
y={d.y}
|
||||
width={d.width}
|
||||
height={d.height}
|
||||
fill={d.fill}
|
||||
clipPath={`url(#histogram-chart-bars--clip-${key})`}
|
||||
data-group={d.group}
|
||||
data-key={d.key}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
private handleBarClick = data => (): void => {
|
||||
const {onBarClick} = this.props
|
||||
|
||||
if (onBarClick) {
|
||||
const time = data[0].time
|
||||
onBarClick(time)
|
||||
}
|
||||
}
|
||||
|
||||
private handleMouseOver = (e: MouseEvent<SVGGElement>): void => {
|
||||
const groupKey = getDeep<string>(e, 'currentTarget.dataset.key', '')
|
||||
|
||||
if (!groupKey) {
|
||||
return
|
||||
}
|
||||
|
||||
const {barGroups} = this.state
|
||||
const hoverGroup = barGroups.find(d => d.key === groupKey)
|
||||
|
||||
if (!hoverGroup) {
|
||||
return
|
||||
}
|
||||
|
||||
const data = _.get(hoverGroup, 'data').reverse()
|
||||
const barGroup = e.currentTarget as SVGGElement
|
||||
const boundingRect = barGroup.getBoundingClientRect()
|
||||
const boundingRectHeight = boundingRect.bottom - boundingRect.top
|
||||
const y = boundingRect.top + boundingRectHeight / 2
|
||||
|
||||
let x = boundingRect.right + TOOLTIP_HORIZONTAL_MARGIN
|
||||
let anchor: TooltipAnchor = 'left'
|
||||
|
||||
// This makes an assumption that the component is within the viewport
|
||||
if (x >= window.innerWidth - TOOLTIP_REFLECT_DIST) {
|
||||
x = window.innerWidth - boundingRect.left + TOOLTIP_HORIZONTAL_MARGIN
|
||||
anchor = 'right'
|
||||
}
|
||||
|
||||
this.props.onHover({data, x, y, anchor})
|
||||
}
|
||||
|
||||
private handleMouseOut = (): void => {
|
||||
this.props.onHover(null)
|
||||
}
|
||||
}
|
||||
|
||||
export default HistogramChartBars
|
|
@ -1,37 +0,0 @@
|
|||
import React, {SFC} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {Margins} from 'src/types/histogram'
|
||||
|
||||
const NUM_TICKS = 5
|
||||
|
||||
interface Props {
|
||||
width: number
|
||||
height: number
|
||||
margins: Margins
|
||||
}
|
||||
|
||||
const HistogramChartSkeleton: SFC<Props> = props => {
|
||||
const {margins, width, height} = props
|
||||
|
||||
const spacing = (height - margins.top - margins.bottom) / NUM_TICKS
|
||||
const y1 = height - margins.bottom
|
||||
const tickYs = _.range(0, NUM_TICKS).map(i => y1 - i * spacing)
|
||||
|
||||
return (
|
||||
<svg className="histogram-chart-skeleton" width={width} height={height}>
|
||||
{tickYs.map((y, i) => (
|
||||
<line
|
||||
key={i}
|
||||
className="y-tick"
|
||||
x1={margins.left}
|
||||
x2={width - margins.right}
|
||||
y1={y}
|
||||
y2={y}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default HistogramChartSkeleton
|
|
@ -1,63 +0,0 @@
|
|||
import React, {SFC, CSSProperties} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {HoverData, ColorScale, HistogramColor} from 'src/types/histogram'
|
||||
|
||||
interface Props {
|
||||
data: HoverData
|
||||
colorScale: ColorScale
|
||||
colors: HistogramColor[]
|
||||
}
|
||||
|
||||
const HistogramChartTooltip: SFC<Props> = props => {
|
||||
const {colorScale, colors} = props
|
||||
const {data, x, y, anchor = 'left'} = props.data
|
||||
|
||||
const tooltipStyle: CSSProperties = {
|
||||
position: 'fixed',
|
||||
top: y,
|
||||
}
|
||||
|
||||
if (anchor === 'left') {
|
||||
tooltipStyle.left = x
|
||||
} else {
|
||||
tooltipStyle.right = x
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="histogram-chart-tooltip" style={tooltipStyle}>
|
||||
<div className="histogram-chart-tooltip--column">
|
||||
{data.map(d => {
|
||||
const groupColor = colors.find(c => c.group === d.group)
|
||||
return (
|
||||
<div
|
||||
key={d.key}
|
||||
style={{
|
||||
color: colorScale(_.get(groupColor, 'color', ''), d.group),
|
||||
}}
|
||||
>
|
||||
{d.value}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="histogram-chart-tooltip--column">
|
||||
{data.map(d => {
|
||||
const groupColor = colors.find(c => c.group === d.group)
|
||||
return (
|
||||
<div
|
||||
key={d.key}
|
||||
style={{
|
||||
color: colorScale(_.get(groupColor, 'color', ''), d.group),
|
||||
}}
|
||||
>
|
||||
{d.group}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HistogramChartTooltip
|
|
@ -1,430 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`HistogramChart displays a HistogramChartSkeleton if empty data is passed 1`] = `
|
||||
<HistogramChart
|
||||
colorScale={[Function]}
|
||||
colors={Array []}
|
||||
data={Array []}
|
||||
dataStatus="Done"
|
||||
height={400}
|
||||
sortBarGroups={[Function]}
|
||||
width={600}
|
||||
>
|
||||
<HistogramChartSkeleton
|
||||
height={400}
|
||||
margins={
|
||||
Object {
|
||||
"bottom": 20,
|
||||
"left": 11,
|
||||
"right": 0,
|
||||
"top": 5,
|
||||
}
|
||||
}
|
||||
width={600}
|
||||
>
|
||||
<svg
|
||||
className="histogram-chart-skeleton"
|
||||
height={400}
|
||||
width={600}
|
||||
>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="0"
|
||||
x1={11}
|
||||
x2={600}
|
||||
y1={380}
|
||||
y2={380}
|
||||
/>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="1"
|
||||
x1={11}
|
||||
x2={600}
|
||||
y1={305}
|
||||
y2={305}
|
||||
/>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="2"
|
||||
x1={11}
|
||||
x2={600}
|
||||
y1={230}
|
||||
y2={230}
|
||||
/>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="3"
|
||||
x1={11}
|
||||
x2={600}
|
||||
y1={155}
|
||||
y2={155}
|
||||
/>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="4"
|
||||
x1={11}
|
||||
x2={600}
|
||||
y1={80}
|
||||
y2={80}
|
||||
/>
|
||||
</svg>
|
||||
</HistogramChartSkeleton>
|
||||
</HistogramChart>
|
||||
`;
|
||||
|
||||
exports[`HistogramChart displays a HistogramChartTooltip when hovering over bars 1`] = `
|
||||
<HistogramChartTooltip
|
||||
colorScale={[Function]}
|
||||
colors={Array []}
|
||||
data={
|
||||
Object {
|
||||
"anchor": "left",
|
||||
"data": Array [
|
||||
Object {
|
||||
"group": "a",
|
||||
"key": "1",
|
||||
"time": 1,
|
||||
"value": 1,
|
||||
},
|
||||
],
|
||||
"x": 5,
|
||||
"y": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="histogram-chart-tooltip"
|
||||
style={
|
||||
Object {
|
||||
"left": 5,
|
||||
"position": "fixed",
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="histogram-chart-tooltip--column"
|
||||
>
|
||||
<div
|
||||
key="1"
|
||||
style={
|
||||
Object {
|
||||
"color": "blue",
|
||||
}
|
||||
}
|
||||
>
|
||||
1
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="histogram-chart-tooltip--column"
|
||||
>
|
||||
<div
|
||||
key="1"
|
||||
style={
|
||||
Object {
|
||||
"color": "blue",
|
||||
}
|
||||
}
|
||||
>
|
||||
a
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</HistogramChartTooltip>
|
||||
`;
|
||||
|
||||
exports[`HistogramChart displays a nothing if passed width and height of 0 1`] = `
|
||||
<HistogramChart
|
||||
colorScale={[Function]}
|
||||
colors={Array []}
|
||||
data={Array []}
|
||||
dataStatus="Done"
|
||||
height={0}
|
||||
sortBarGroups={[Function]}
|
||||
width={0}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`HistogramChart displays the visualization with bars if nonempty data is passed 1`] = `
|
||||
<HistogramChart
|
||||
colorScale={[Function]}
|
||||
colors={Array []}
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"group": "a",
|
||||
"key": "0",
|
||||
"time": 0,
|
||||
"value": 0,
|
||||
},
|
||||
Object {
|
||||
"group": "a",
|
||||
"key": "1",
|
||||
"time": 1,
|
||||
"value": 1,
|
||||
},
|
||||
Object {
|
||||
"group": "b",
|
||||
"key": "2",
|
||||
"time": 2,
|
||||
"value": 2,
|
||||
},
|
||||
]
|
||||
}
|
||||
dataStatus="Done"
|
||||
height={400}
|
||||
sortBarGroups={[Function]}
|
||||
width={600}
|
||||
>
|
||||
<div
|
||||
className="histogram-chart"
|
||||
>
|
||||
<svg
|
||||
className="histogram-chart"
|
||||
height={400}
|
||||
width={600}
|
||||
>
|
||||
<defs>
|
||||
<clipPath
|
||||
id="histogram-chart--bars-clip"
|
||||
>
|
||||
<rect
|
||||
height={375}
|
||||
width={575}
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g
|
||||
className="histogram-chart--axes"
|
||||
>
|
||||
<HistogramChartAxes
|
||||
height={400}
|
||||
margins={
|
||||
Object {
|
||||
"bottom": 20,
|
||||
"left": 25,
|
||||
"right": 0,
|
||||
"top": 5,
|
||||
}
|
||||
}
|
||||
width={600}
|
||||
xScale={[Function]}
|
||||
yScale={[Function]}
|
||||
>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="0-25-625-380"
|
||||
x1={25}
|
||||
x2={625}
|
||||
y1={380}
|
||||
y2={380}
|
||||
/>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="0.5-25-625-301.875"
|
||||
x1={25}
|
||||
x2={625}
|
||||
y1={301.875}
|
||||
y2={301.875}
|
||||
/>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="1-25-625-223.75"
|
||||
x1={25}
|
||||
x2={625}
|
||||
y1={223.75}
|
||||
y2={223.75}
|
||||
/>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="1.5-25-625-145.625"
|
||||
x1={25}
|
||||
x2={625}
|
||||
y1={145.625}
|
||||
y2={145.625}
|
||||
/>
|
||||
<line
|
||||
className="y-tick"
|
||||
key="2-25-625-67.5"
|
||||
x1={25}
|
||||
x2={625}
|
||||
y1={67.5}
|
||||
y2={67.5}
|
||||
/>
|
||||
<text
|
||||
className="y-label"
|
||||
key="0-25-625-380"
|
||||
x={18}
|
||||
y={380}
|
||||
>
|
||||
0
|
||||
</text>
|
||||
<text
|
||||
className="y-label"
|
||||
key="0.5-25-625-301.875"
|
||||
x={18}
|
||||
y={301.875}
|
||||
>
|
||||
0.5
|
||||
</text>
|
||||
<text
|
||||
className="y-label"
|
||||
key="1-25-625-223.75"
|
||||
x={18}
|
||||
y={223.75}
|
||||
>
|
||||
1
|
||||
</text>
|
||||
<text
|
||||
className="y-label"
|
||||
key="1.5-25-625-145.625"
|
||||
x={18}
|
||||
y={145.625}
|
||||
>
|
||||
1.5
|
||||
</text>
|
||||
<text
|
||||
className="y-label"
|
||||
key="2-25-625-67.5"
|
||||
x={18}
|
||||
y={67.5}
|
||||
>
|
||||
2
|
||||
</text>
|
||||
<text
|
||||
className="x-label"
|
||||
key=".001-287.5-388"
|
||||
x={287.5}
|
||||
y={388}
|
||||
>
|
||||
.001
|
||||
</text>
|
||||
<text
|
||||
className="x-label"
|
||||
key=".002-575-388"
|
||||
x={575}
|
||||
y={388}
|
||||
>
|
||||
.002
|
||||
</text>
|
||||
</HistogramChartAxes>
|
||||
</g>
|
||||
<g
|
||||
className="histogram-chart--bars"
|
||||
clipPath="url(#histogram-chart--bars-clip)"
|
||||
transform="translate(25, 5)"
|
||||
>
|
||||
<HistogramChartBars
|
||||
colorScale={[Function]}
|
||||
colors={Array []}
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"group": "a",
|
||||
"key": "0",
|
||||
"time": 0,
|
||||
"value": 0,
|
||||
},
|
||||
Object {
|
||||
"group": "a",
|
||||
"key": "1",
|
||||
"time": 1,
|
||||
"value": 1,
|
||||
},
|
||||
Object {
|
||||
"group": "b",
|
||||
"key": "2",
|
||||
"time": 2,
|
||||
"value": 2,
|
||||
},
|
||||
]
|
||||
}
|
||||
height={375}
|
||||
onHover={[Function]}
|
||||
sortBarGroups={[Function]}
|
||||
width={575}
|
||||
xScale={[Function]}
|
||||
yScale={[Function]}
|
||||
>
|
||||
<g
|
||||
className="histogram-chart-bars--bars"
|
||||
data-key="1-1-193.5"
|
||||
key="1-1-193.5"
|
||||
onClick={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<defs>
|
||||
<clipPath
|
||||
id="histogram-chart-bars--clip-1-1-193.5"
|
||||
>
|
||||
<rect
|
||||
height={159.25}
|
||||
rx={3}
|
||||
ry={3}
|
||||
width={188}
|
||||
x={193.5}
|
||||
y={218.75}
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect
|
||||
className="histogram-chart-bars--bar"
|
||||
clipPath="url(#histogram-chart-bars--clip-1-1-193.5)"
|
||||
data-group="a"
|
||||
data-key="1"
|
||||
fill="#0000ff"
|
||||
height={156.25}
|
||||
key="1"
|
||||
width={188}
|
||||
x={193.5}
|
||||
y={218.75}
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
className="histogram-chart-bars--bars"
|
||||
data-key="2-2-481"
|
||||
key="2-2-481"
|
||||
onClick={[Function]}
|
||||
onMouseOut={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
>
|
||||
<defs>
|
||||
<clipPath
|
||||
id="histogram-chart-bars--clip-2-2-481"
|
||||
>
|
||||
<rect
|
||||
height={315.5}
|
||||
rx={3}
|
||||
ry={3}
|
||||
width={188}
|
||||
x={481}
|
||||
y={62.5}
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect
|
||||
className="histogram-chart-bars--bar"
|
||||
clipPath="url(#histogram-chart-bars--clip-2-2-481)"
|
||||
data-group="b"
|
||||
data-key="2"
|
||||
fill="#0000ff"
|
||||
height={312.5}
|
||||
key="2"
|
||||
width={188}
|
||||
x={481}
|
||||
y={62.5}
|
||||
/>
|
||||
</g>
|
||||
</HistogramChartBars>
|
||||
</g>
|
||||
</svg>
|
||||
<div
|
||||
className="histogram-chart--overlays"
|
||||
/>
|
||||
</div>
|
||||
</HistogramChart>
|
||||
`;
|
Loading…
Reference in New Issue