diff --git a/common/functions/axisConfig.js b/common/functions/axisConfig.js new file mode 100644 index 0000000000000..80c1e40e24ddb --- /dev/null +++ b/common/functions/axisConfig.js @@ -0,0 +1,26 @@ +export const axisConfig = () => ({ + name: 'axisConfig', + aliases: [], + type: 'axisConfig', + context: { + types: ['datatable'], + }, + help: 'Configure axis of a visualization', + args: { + show: { + types: ['boolean'], + help: 'Show the axis labels?', + default: true, + }, + position: { + types: ['string'], + help: 'Position of the axis labels', + }, + }, + fn: (context, args) => { + return { + type: 'axisConfig', + ...args, + }; + }, +}); diff --git a/common/functions/index.js b/common/functions/index.js index 5c0be87521b65..3e79189a57fea 100644 --- a/common/functions/index.js +++ b/common/functions/index.js @@ -1,5 +1,6 @@ import { alterColumn } from './alterColumn'; -import { asFn } from './as.js'; +import { asFn } from './as'; +import { axisConfig } from './axisConfig'; import { compare } from './compare'; import { containerStyle } from './containerStyle'; import { context } from './context'; @@ -43,6 +44,7 @@ import { timefilterControl } from './timefilterControl'; export const commonFunctions = [ alterColumn, asFn, + axisConfig, containerStyle, compare, columns, diff --git a/common/functions/plot.js b/common/functions/plot.js index 8bc2d31745d4f..2e3bda1befae7 100644 --- a/common/functions/plot.js +++ b/common/functions/plot.js @@ -1,6 +1,7 @@ import { groupBy, get, set, map, sortBy } from 'lodash'; import keyBy from 'lodash.keyby'; import chroma from 'chroma-js'; +import { getType } from '../lib/get_type'; export const plot = () => ({ name: 'plot', @@ -38,14 +39,12 @@ export const plot = () => ({ default: 'nw', }, yaxis: { - types: ['boolean'], - help: 'Show the y-axis?', - default: true, + types: ['boolean', 'axisConfig'], + help: 'Axis configuration, or false to disable', }, xaxis: { - types: ['boolean'], - help: 'Show the x-axis?', - default: true, + types: ['boolean', 'axisConfig'], + help: 'Axis configuration, or false to disable', }, }, fn: (context, args) => { @@ -68,6 +67,7 @@ export const plot = () => ({ barWidth: get(seriesStyle, 'bars'), fill: 1, align: 'center', + horizontal: get(seriesStyle, 'horizontalBars', false), }, // This is here intentionally even though it is the default. // We use the `size` plugins for this and if the user says they want points @@ -154,6 +154,23 @@ export const plot = () => ({ return args.legend; } + function axisConfig(name, argValue) { + if (getType(argValue) === 'axisConfig') { + // first value is used as the default + const acceptedPositions = name === 'x' ? ['bottom', 'top'] : ['left', 'right']; + + const config = { show: true }; + config.position = acceptedPositions.includes(argValue.position) + ? argValue.position + : acceptedPositions[0]; + return config; + } + + if (getType(argValue) === 'boolean') return { show: argValue }; + + return { show: true }; + } + const result = { type: 'render', as: 'plot', @@ -182,7 +199,7 @@ export const plot = () => ({ }, }, xaxis: { - show: args.xaxis, + ...axisConfig('x', args.xaxis), ticks: get(context.columns, 'x.type') === 'string' ? map(ticks.x.hash, (position, name) => [position, name]) @@ -190,7 +207,7 @@ export const plot = () => ({ mode: get(context.columns, 'x.type') === 'date' ? 'time' : undefined, }, yaxis: { - show: args.yaxis, + ...axisConfig('y', args.yaxis), ticks: get(context.columns, 'y.type') === 'string' ? map(ticks.y.hash, (position, name) => [position, name]) diff --git a/common/functions/repeatImage/index.js b/common/functions/repeatImage/index.js index ba8413fe40643..3b8cc80044055 100644 --- a/common/functions/repeatImage/index.js +++ b/common/functions/repeatImage/index.js @@ -31,7 +31,6 @@ export const repeatImage = () => ({ }, }, fn: (count, args) => { - console.log('WTF', count, args); return { type: 'render', as: 'repeatImage', diff --git a/common/functions/seriesStyle.js b/common/functions/seriesStyle.js index 9feaec39ca8e6..cb9e3c66c8c29 100644 --- a/common/functions/seriesStyle.js +++ b/common/functions/seriesStyle.js @@ -50,6 +50,12 @@ export const seriesStyle = () => ({ help: 'Should we stack the series? This is the stack "id". Series with the same stack id will be stacked together', }, + horizontalBars: { + types: ['null', 'boolean'], + displayName: 'Horizontal Bars Orientation', + help: 'Sets the orientation of bars in the chart to horizontal', + default: false, + }, }, fn: (context, args) => ({ type: name, ...args }), }); diff --git a/public/expression_types/arg_types/axis_config/axis_config.less b/public/expression_types/arg_types/axis_config/axis_config.less new file mode 100644 index 0000000000000..90815bcbe492e --- /dev/null +++ b/public/expression_types/arg_types/axis_config/axis_config.less @@ -0,0 +1,16 @@ +@import (reference) "../../../style/variables"; + +.canvas__argtype--axis_config--disabled { + color: @mediumGrey; + font-size: 0.8em; +} + +.canvas__argtype--axis_config--configure { + .form-group { + display: flex; + + label { + padding: @spacingXS @spacingS; + } + } +} diff --git a/public/expression_types/arg_types/axis_config/extended_template.js b/public/expression_types/arg_types/axis_config/extended_template.js new file mode 100644 index 0000000000000..03e1b29d34689 --- /dev/null +++ b/public/expression_types/arg_types/axis_config/extended_template.js @@ -0,0 +1,70 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { get } from 'lodash'; +import { set } from 'object-path-immutable'; +import { LabeledInput } from '../../../components/labeled_input'; + +const defaultExpression = { + type: 'expression', + chain: [ + { + type: 'function', + function: 'axisConfig', + arguments: {}, + }, + ], +}; + +export class ExtendedTemplate extends React.PureComponent { + static propTypes = { + onValueChange: PropTypes.func.isRequired, + argValue: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.shape({ + chain: PropTypes.array, + }).isRequired, + ]), + typeInstance: PropTypes.object.isRequired, + }; + + // TODO: this should be in a helper, it's the same code from container_style + getArgValue = (name, alt) => { + return get(this.props.argValue, ['chain', 0, 'arguments', name, 0], alt); + }; + + // TODO: this should be in a helper, it's the same code from container_style + setArgValue = name => ev => { + const val = ev.target.value; + const { argValue, onValueChange } = this.props; + const oldVal = typeof argValue === 'boolean' ? defaultExpression : argValue; + const newValue = set(oldVal, ['chain', 0, 'arguments', name, 0], val); + onValueChange(newValue); + }; + + render() { + const isDisabled = typeof this.props.argValue === 'boolean' && this.props.argValue === false; + + if (isDisabled) + return