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
The axis is disabled
; + + const positions = { + xaxis: ['bottom', 'top'], + yaxis: ['left', 'right'], + }; + const argName = this.props.typeInstance.name; + const position = this.getArgValue('position', positions[argName][0]); + + return ( +
+ +
+ ); + } +} diff --git a/public/expression_types/arg_types/axis_config/index.js b/public/expression_types/arg_types/axis_config/index.js new file mode 100644 index 0000000000000..b7dabb68b0797 --- /dev/null +++ b/public/expression_types/arg_types/axis_config/index.js @@ -0,0 +1,11 @@ +import { SimpleTemplate } from './simple_template'; +import { ExtendedTemplate } from './extended_template'; +import './axis_config.less'; + +export const axisConfig = () => ({ + name: 'axisConfig', + displayName: 'Axis Config', + help: 'Visualization axis configuration', + simpleTemplate: SimpleTemplate, + template: ExtendedTemplate, +}); diff --git a/public/expression_types/arg_types/axis_config/simple_template.js b/public/expression_types/arg_types/axis_config/simple_template.js new file mode 100644 index 0000000000000..9298a6a81fafc --- /dev/null +++ b/public/expression_types/arg_types/axis_config/simple_template.js @@ -0,0 +1,16 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Toggle } from '../../../components/toggle'; + +const isEnabled = argValue => typeof argValue !== 'boolean' || argValue !== false; + +export const SimpleTemplate = ({ onValueChange, argValue }) => ( +
+ +
+); + +SimpleTemplate.propTypes = { + onValueChange: PropTypes.func.isRequired, + argValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), +}; diff --git a/public/expression_types/arg_types/container_style/container_style.js b/public/expression_types/arg_types/container_style/container_style.js index 2d54587330439..e0f4e1a6f79e7 100644 --- a/public/expression_types/arg_types/container_style/container_style.js +++ b/public/expression_types/arg_types/container_style/container_style.js @@ -7,6 +7,7 @@ import { extendedTemplate } from './extended_template'; import './container_style.less'; const wrap = Component => + // TODO: this should be in a helper withHandlers({ getArgValue: ({ argValue }) => (name, alt) => { const args = get(argValue, 'chain.0.arguments', {}); diff --git a/public/expression_types/arg_types/index.js b/public/expression_types/arg_types/index.js index cc15588094f83..7a1a4982a954f 100644 --- a/public/expression_types/arg_types/index.js +++ b/public/expression_types/arg_types/index.js @@ -1,3 +1,4 @@ +import { axisConfig } from './axis_config'; import { containerStyle } from './container_style'; import { checkbox } from './checkbox'; import { datacolumn } from './datacolumn'; @@ -13,6 +14,7 @@ import { string } from './string'; import { font } from './font'; export const argTypeSpecs = [ + axisConfig, containerStyle, checkbox, datacolumn, diff --git a/public/expression_types/arg_types/select.js b/public/expression_types/arg_types/select.js index 86096c0b4b32e..7b1a2aa981128 100644 --- a/public/expression_types/arg_types/select.js +++ b/public/expression_types/arg_types/select.js @@ -2,7 +2,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormControl } from 'react-bootstrap'; -const getArgValueString = argValue => (typeof argValue === 'string' ? argValue : argValue.value); +const getArgValueString = argValue => { + if (typeof argValue === 'object' && argValue !== null) return argValue.value; + return String(argValue); +}; const SelectArgInput = ({ typeInstance, onValueChange, argValue }) => { const choices = typeInstance.options.choices; diff --git a/public/expression_types/views/plot.js b/public/expression_types/views/plot.js index 04ffe2c7b0dcc..db2dcfa1ac52e 100644 --- a/public/expression_types/views/plot.js +++ b/public/expression_types/views/plot.js @@ -23,16 +23,14 @@ export const plot = () => ({ { name: 'xaxis', displayName: 'X-Axis', - help: 'Enable or disable the x-axis', - argType: 'checkbox', - default: 'true', + help: 'Configure or disable the x-axis', + argType: 'axisConfig', }, { name: 'yaxis', displayName: 'Y-Axis', - help: 'Enable or disable the Y-axis', - argType: 'checkbox', - default: 'true', + help: 'Configure or disable the Y-axis', + argType: 'axisConfig', }, { name: 'font',