Add support for axis labels in plots

pull/12144/head
Christopher Henn 2019-02-22 15:18:27 -08:00 committed by Chris Henn
parent b073c84b7e
commit 27da16305e
5 changed files with 105 additions and 3 deletions

View File

@ -1,6 +1,11 @@
import React, {useRef, useLayoutEffect, SFC} from 'react'
import {PlotEnv, TICK_PADDING_RIGHT, TICK_PADDING_TOP} from 'src/minard'
import {
PlotEnv,
TICK_PADDING_RIGHT,
TICK_PADDING_TOP,
PLOT_PADDING,
} from 'src/minard'
import {clearCanvas} from 'src/minard/utils/clearCanvas'
interface Props {
@ -20,9 +25,13 @@ export const drawAxes = (
const {
width,
height,
innerWidth,
innerHeight,
margins,
xTicks,
yTicks,
xAxisLabel,
yAxisLabel,
baseLayer: {
scales: {x: xScale, y: yScale},
},
@ -77,6 +86,31 @@ export const drawAxes = (
context.fillText(String(yTick), margins.left - TICK_PADDING_RIGHT, y)
}
// Draw the x axis label
if (xAxisLabel) {
context.textAlign = 'center'
context.textBaseline = 'bottom'
context.fillText(
xAxisLabel,
margins.left + innerWidth / 2,
height - PLOT_PADDING
)
}
// Draw the y axis label
if (yAxisLabel) {
const x = PLOT_PADDING
const y = margins.top + innerHeight / 2
context.save()
context.translate(x, y)
context.rotate(-Math.PI / 2)
context.textAlign = 'center'
context.textBaseline = 'top'
context.fillText(yAxisLabel, 0, 0)
context.restore()
}
}
export const Axes: SFC<Props> = props => {

View File

@ -9,6 +9,8 @@ import {
setTable,
setControlledXDomain,
setControlledYDomain,
setXAxisLabel,
setYAxisLabel,
} from 'src/minard/utils/plotEnvActions'
import {plotEnvReducer, INITIAL_PLOT_ENV} from 'src/minard/utils/plotEnvReducer'
@ -29,6 +31,8 @@ export interface Props {
axesStroke?: string
tickFont?: string
tickFill?: string
xAxisLabel?: string
yAxisLabel?: string
// The x domain of the plot can be explicitly set. If this prop is passed,
// then the component is operating in a "controlled" mode, where it always
@ -53,6 +57,8 @@ export const Plot: SFC<Props> = ({
axesStroke = '#31313d',
tickFont = 'bold 10px Roboto',
tickFill = '#8e91a1',
xAxisLabel = '',
yAxisLabel = '',
xDomain = null,
yDomain = null,
}) => {
@ -62,12 +68,16 @@ export const Plot: SFC<Props> = ({
height,
xDomain,
yDomain,
xAxisLabel,
yAxisLabel,
baseLayer: {...INITIAL_PLOT_ENV.baseLayer, table},
})
useMountedEffect(() => dispatch(setTable(table)), [table])
useMountedEffect(() => dispatch(setControlledXDomain(xDomain)), [xDomain])
useMountedEffect(() => dispatch(setControlledYDomain(yDomain)), [yDomain])
useMountedEffect(() => dispatch(setXAxisLabel(xAxisLabel)), [xAxisLabel])
useMountedEffect(() => dispatch(setYAxisLabel(yAxisLabel)), [yAxisLabel])
useMountedEffect(() => dispatch(setDimensions(width, height)), [
width,
height,

View File

@ -9,6 +9,8 @@ export const TICK_PADDING_TOP = 5
export const TICK_CHAR_WIDTH = 7
export const TICK_CHAR_HEIGHT = 10
export const AXIS_LABEL_PADDING_BOTTOM = 15
export {Plot} from 'src/minard/components/Plot'
export {
@ -146,6 +148,8 @@ export interface PlotEnv {
margins: Margins
xTicks: number[]
yTicks: number[]
xAxisLabel: string
yAxisLabel: string
// If the domains have been explicitly passed in to the `Plot` component,
// they will be stored here. Scales and child layers use the `xDomain` and

View File

@ -8,6 +8,8 @@ export type PlotAction =
| ResetAction
| SetControlledXDomainAction
| SetControlledYDomainAction
| SetXAxisLabelAction
| SetYAxisLabelAction
interface RegisterLayerAction {
type: 'REGISTER_LAYER'
@ -91,3 +93,23 @@ export const setControlledYDomain = (
type: 'SET_CONTROLLED_Y_DOMAIN',
payload: {yDomain},
})
interface SetXAxisLabelAction {
type: 'SET_X_AXIS_LABEL'
payload: {xAxisLabel: string}
}
export const setXAxisLabel = (xAxisLabel: string): SetXAxisLabelAction => ({
type: 'SET_X_AXIS_LABEL',
payload: {xAxisLabel},
})
interface SetYAxisLabelAction {
type: 'SET_Y_AXIS_LABEL'
payload: {yAxisLabel: string}
}
export const setYAxisLabel = (yAxisLabel: string): SetYAxisLabelAction => ({
type: 'SET_Y_AXIS_LABEL',
payload: {yAxisLabel},
})

View File

@ -13,6 +13,7 @@ import {
TICK_CHAR_HEIGHT,
TICK_PADDING_RIGHT,
TICK_PADDING_TOP,
AXIS_LABEL_PADDING_BOTTOM,
} from 'src/minard'
import {PlotAction} from 'src/minard/utils/plotEnvActions'
import {getGroupKey} from 'src/minard/utils/getGroupKey'
@ -33,6 +34,8 @@ export const INITIAL_PLOT_ENV: PlotEnv = {
},
xTicks: [],
yTicks: [],
xAxisLabel: '',
yAxisLabel: '',
xDomain: null,
yDomain: null,
baseLayer: {
@ -119,6 +122,26 @@ export const plotEnvReducer = (state: PlotEnv, action: PlotAction): PlotEnv =>
return
}
case 'SET_X_AXIS_LABEL': {
const {xAxisLabel} = action.payload
draftState.xAxisLabel = xAxisLabel
setLayout(draftState)
return
}
case 'SET_Y_AXIS_LABEL': {
const {yAxisLabel} = action.payload
draftState.yAxisLabel = yAxisLabel
setLayout(draftState)
return
}
}
})
@ -230,11 +253,20 @@ const setLayout = (draftState: PlotEnv): void => {
const yTickWidth =
Math.max(...draftState.yTicks.map(t => String(t).length)) * TICK_CHAR_WIDTH
const xAxisLabelHeight = draftState.xAxisLabel
? TICK_CHAR_HEIGHT + AXIS_LABEL_PADDING_BOTTOM
: 0
const yAxisLabelHeight = draftState.yAxisLabel
? TICK_CHAR_HEIGHT + AXIS_LABEL_PADDING_BOTTOM
: 0
const margins = {
top: PLOT_PADDING,
right: PLOT_PADDING,
bottom: TICK_CHAR_HEIGHT + TICK_PADDING_TOP + PLOT_PADDING,
left: yTickWidth + TICK_PADDING_RIGHT + PLOT_PADDING,
bottom:
TICK_CHAR_HEIGHT + TICK_PADDING_TOP + PLOT_PADDING + xAxisLabelHeight,
left: yTickWidth + TICK_PADDING_RIGHT + PLOT_PADDING + yAxisLabelHeight,
}
const innerWidth = width - margins.left - margins.right