Merge pull request #3060 from influxdata/moar-colors-n-stuff
Introduce Customizable Line Graph Colorspull/10616/head
commit
f179936d7d
|
@ -3,6 +3,13 @@
|
|||
### Features
|
||||
|
||||
1. [#2526](https://github.com/influxdata/chronograf/pull/2526): Add support for RS256/JWKS verification, support for id_token parsing (as in ADFS)
|
||||
1. [#3060](https://github.com/influxdata/chronograf/pull/3060): Add ability to set a color palette for Line, Stacked, Step-Plot, and Bar graphs
|
||||
|
||||
### UI Improvements
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
## v1.4.3.0 [unreleased]
|
||||
1. [#3080](https://github.com/influxdata/chronograf/pull/3080): Add tabular data visualization option with features
|
||||
1. [#3103](https://github.com/influxdata/chronograf/pull/3103): Add ability to clone dashboards
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ func HasCorrectAxes(c *chronograf.DashboardCell) error {
|
|||
// HasCorrectColors verifies that the format of each color is correct
|
||||
func HasCorrectColors(c *chronograf.DashboardCell) error {
|
||||
for _, color := range c.CellColors {
|
||||
if !oneOf(color.Type, "max", "min", "threshold", "text", "background") {
|
||||
if !oneOf(color.Type, "max", "min", "threshold", "text", "background", "scale") {
|
||||
return chronograf.ErrInvalidColorType
|
||||
}
|
||||
if len(color.Hex) != 7 {
|
||||
|
|
|
@ -116,6 +116,7 @@
|
|||
"axios": "^0.13.1",
|
||||
"bignumber.js": "^4.0.2",
|
||||
"calculate-size": "^1.1.1",
|
||||
"chroma-js": "^1.3.6",
|
||||
"classnames": "^2.2.3",
|
||||
"dygraphs": "2.1.0",
|
||||
"eslint-plugin-babel": "^4.1.2",
|
||||
|
@ -147,4 +148,4 @@
|
|||
"rome": "^2.1.22",
|
||||
"uuid": "^3.2.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,3 +57,10 @@ export const updateTableOptions = tableOptions => ({
|
|||
tableOptions,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateLineColors = lineColors => ({
|
||||
type: 'UPDATE_LINE_COLORS',
|
||||
payload: {
|
||||
lineColors,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -7,6 +7,7 @@ import OptIn from 'shared/components/OptIn'
|
|||
import Input from 'src/dashboards/components/DisplayOptionsInput'
|
||||
import {Tabber, Tab} from 'src/dashboards/components/Tabber'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import LineGraphColorSelector from 'src/shared/components/LineGraphColorSelector'
|
||||
|
||||
import {
|
||||
AXES_SCALE_OPTIONS,
|
||||
|
@ -102,6 +103,7 @@ class AxesOptions extends Component {
|
|||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<LineGraphColorSelector />
|
||||
<div className="form-group col-sm-6">
|
||||
<label htmlFor="min">Min</label>
|
||||
<OptIn
|
||||
|
|
|
@ -24,7 +24,8 @@ import {
|
|||
import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames'
|
||||
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
||||
import {AUTO_GROUP_BY} from 'src/shared/constants'
|
||||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
|
||||
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
class CellEditorOverlay extends Component {
|
||||
constructor(props) {
|
||||
|
@ -107,7 +108,7 @@ class CellEditorOverlay extends Component {
|
|||
|
||||
handleSaveCell = () => {
|
||||
const {queriesWorkingDraft, staticLegend} = this.state
|
||||
const {cell, thresholdsListColors, gaugeColors} = this.props
|
||||
const {cell, thresholdsListColors, gaugeColors, lineColors} = this.props
|
||||
|
||||
const queries = queriesWorkingDraft.map(q => {
|
||||
const timeRange = q.range || {upper: null, lower: ':dashboardTime:'}
|
||||
|
@ -120,20 +121,12 @@ class CellEditorOverlay extends Component {
|
|||
}
|
||||
})
|
||||
|
||||
let colors = []
|
||||
|
||||
switch (cell.type) {
|
||||
case 'gauge': {
|
||||
colors = stringifyColorValues(gaugeColors)
|
||||
break
|
||||
}
|
||||
case 'single-stat':
|
||||
case 'line-plus-single-stat':
|
||||
case 'table': {
|
||||
colors = stringifyColorValues(thresholdsListColors)
|
||||
break
|
||||
}
|
||||
}
|
||||
const colors = getCellTypeColors({
|
||||
cellType: cell.type,
|
||||
gaugeColors,
|
||||
thresholdsListColors,
|
||||
lineColors,
|
||||
})
|
||||
|
||||
this.props.onSave({
|
||||
...cell,
|
||||
|
@ -390,8 +383,9 @@ CellEditorOverlay.propTypes = {
|
|||
dashboardID: string.isRequired,
|
||||
sources: arrayOf(shape()),
|
||||
thresholdsListType: string.isRequired,
|
||||
thresholdsListColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
gaugeColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
thresholdsListColors: colorsNumberSchema.isRequired,
|
||||
gaugeColors: colorsNumberSchema.isRequired,
|
||||
lineColors: colorsStringSchema.isRequired,
|
||||
}
|
||||
|
||||
CEOBottom.propTypes = {
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
updateGaugeColors,
|
||||
updateAxes,
|
||||
} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
import {colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
class GaugeOptions extends Component {
|
||||
handleAddThreshold = () => {
|
||||
|
@ -219,18 +220,10 @@ class GaugeOptions extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, number, shape, string} = PropTypes
|
||||
const {func, shape} = PropTypes
|
||||
|
||||
GaugeOptions.propTypes = {
|
||||
gaugeColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
gaugeColors: colorsNumberSchema,
|
||||
handleUpdateGaugeColors: func.isRequired,
|
||||
handleUpdateAxes: func.isRequired,
|
||||
axes: shape({}).isRequired,
|
||||
|
|
|
@ -6,7 +6,8 @@ import RefreshingGraph from 'src/shared/components/RefreshingGraph'
|
|||
import buildQueries from 'utils/buildQueriesForGraphs'
|
||||
import VisualizationName from 'src/dashboards/components/VisualizationName'
|
||||
|
||||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
|
||||
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
const DashVisualization = (
|
||||
{
|
||||
|
@ -14,6 +15,7 @@ const DashVisualization = (
|
|||
type,
|
||||
templates,
|
||||
timeRange,
|
||||
lineColors,
|
||||
autoRefresh,
|
||||
gaugeColors,
|
||||
queryConfigs,
|
||||
|
@ -26,14 +28,19 @@ const DashVisualization = (
|
|||
},
|
||||
{source: {links: {proxy}}}
|
||||
) => {
|
||||
const colors = type === 'gauge' ? gaugeColors : thresholdsListColors
|
||||
const colors = getCellTypeColors({
|
||||
cellType: type,
|
||||
gaugeColors,
|
||||
thresholdsListColors,
|
||||
lineColors,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="graph">
|
||||
<VisualizationName />
|
||||
<div className="graph-container">
|
||||
<RefreshingGraph
|
||||
colors={stringifyColorValues(colors)}
|
||||
colors={colors}
|
||||
axes={axes}
|
||||
type={type}
|
||||
tableOptions={tableOptions}
|
||||
|
@ -69,24 +76,9 @@ DashVisualization.propTypes = {
|
|||
}),
|
||||
tableOptions: shape({}),
|
||||
resizerTopHeight: number,
|
||||
thresholdsListColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
gaugeColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
thresholdsListColors: colorsNumberSchema,
|
||||
gaugeColors: colorsNumberSchema,
|
||||
lineColors: colorsStringSchema,
|
||||
staticLegend: bool,
|
||||
setDataLabels: func,
|
||||
}
|
||||
|
@ -103,11 +95,13 @@ const mapStateToProps = ({
|
|||
cellEditorOverlay: {
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
lineColors,
|
||||
cell: {type, axes, tableOptions},
|
||||
},
|
||||
}) => ({
|
||||
gaugeColors,
|
||||
thresholdsListColors,
|
||||
lineColors,
|
||||
type,
|
||||
axes,
|
||||
tableOptions,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph'
|
||||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
|
||||
export const initializeOptions = cellType => {
|
||||
switch (cellType) {
|
||||
|
@ -18,3 +19,33 @@ export const AXES_SCALE_OPTIONS = {
|
|||
|
||||
export const TOOLTIP_Y_VALUE_FORMAT =
|
||||
'<p><strong>K/M/B</strong> = Thousand / Million / Billion<br/><strong>K/M/G</strong> = Kilo / Mega / Giga </p>'
|
||||
|
||||
export const getCellTypeColors = ({
|
||||
cellType,
|
||||
gaugeColors,
|
||||
thresholdsListColors,
|
||||
lineColors,
|
||||
}) => {
|
||||
let colors = []
|
||||
|
||||
switch (cellType) {
|
||||
case 'gauge': {
|
||||
colors = stringifyColorValues(gaugeColors)
|
||||
break
|
||||
}
|
||||
case 'single-stat':
|
||||
case 'line-plus-single-stat':
|
||||
case 'table': {
|
||||
colors = stringifyColorValues(thresholdsListColors)
|
||||
break
|
||||
}
|
||||
case 'bar':
|
||||
case 'line':
|
||||
case 'line-stacked':
|
||||
case 'line-stepplot': {
|
||||
colors = stringifyColorValues(lineColors)
|
||||
}
|
||||
}
|
||||
|
||||
return colors
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import {
|
|||
import {presentationButtonDispatcher} from 'shared/dispatchers'
|
||||
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants'
|
||||
import {notifyDashboardNotFound} from 'shared/copy/notifications'
|
||||
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
const FORMAT_INFLUXQL = 'influxql'
|
||||
const defaultTimeRange = {
|
||||
|
@ -283,6 +284,7 @@ class DashboardPage extends Component {
|
|||
showTemplateControlBar,
|
||||
dashboard,
|
||||
dashboards,
|
||||
lineColors,
|
||||
gaugeColors,
|
||||
autoRefresh,
|
||||
selectedCell,
|
||||
|
@ -399,6 +401,7 @@ class DashboardPage extends Component {
|
|||
thresholdsListType={thresholdsListType}
|
||||
thresholdsListColors={thresholdsListColors}
|
||||
gaugeColors={gaugeColors}
|
||||
lineColors={lineColors}
|
||||
/>
|
||||
) : null}
|
||||
<DashboardHeader
|
||||
|
@ -530,8 +533,9 @@ DashboardPage.propTypes = {
|
|||
handleDismissEditingAnnotation: func.isRequired,
|
||||
selectedCell: shape({}),
|
||||
thresholdsListType: string.isRequired,
|
||||
thresholdsListColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
gaugeColors: arrayOf(shape({}).isRequired).isRequired,
|
||||
thresholdsListColors: colorsNumberSchema.isRequired,
|
||||
gaugeColors: colorsNumberSchema.isRequired,
|
||||
lineColors: colorsStringSchema.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, {params: {dashboardID}}) => {
|
||||
|
@ -549,6 +553,7 @@ const mapStateToProps = (state, {params: {dashboardID}}) => {
|
|||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
lineColors,
|
||||
},
|
||||
} = state
|
||||
|
||||
|
@ -579,6 +584,7 @@ const mapStateToProps = (state, {params: {dashboardID}}) => {
|
|||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
lineColors,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,11 @@ import {
|
|||
getThresholdsListType,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
import {
|
||||
DEFAULT_LINE_COLORS,
|
||||
validateLineColors,
|
||||
} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
import {initializeOptions} from 'src/dashboards/constants/cellEditor'
|
||||
|
||||
export const initialState = {
|
||||
|
@ -16,6 +21,7 @@ export const initialState = {
|
|||
thresholdsListType: THRESHOLD_TYPE_TEXT,
|
||||
thresholdsListColors: DEFAULT_THRESHOLDS_LIST_COLORS,
|
||||
gaugeColors: DEFAULT_GAUGE_COLORS,
|
||||
lineColors: DEFAULT_LINE_COLORS,
|
||||
}
|
||||
|
||||
export default function cellEditorOverlay(state = initialState, action) {
|
||||
|
@ -36,12 +42,15 @@ export default function cellEditorOverlay(state = initialState, action) {
|
|||
initializeOptions('table')
|
||||
)
|
||||
|
||||
const lineColors = validateLineColors(colors)
|
||||
|
||||
return {
|
||||
...state,
|
||||
cell: {...cell, tableOptions},
|
||||
thresholdsListType,
|
||||
thresholdsListColors,
|
||||
gaugeColors,
|
||||
lineColors,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,6 +110,12 @@ export default function cellEditorOverlay(state = initialState, action) {
|
|||
|
||||
return {...state, cell}
|
||||
}
|
||||
|
||||
case 'UPDATE_LINE_COLORS': {
|
||||
const {lineColors} = action.payload
|
||||
|
||||
return {...state, lineColors}
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
|||
|
||||
import Table from './Table'
|
||||
import RefreshingGraph from 'shared/components/RefreshingGraph'
|
||||
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
const VisView = ({
|
||||
axes,
|
||||
|
@ -37,6 +38,7 @@ const VisView = ({
|
|||
|
||||
return (
|
||||
<RefreshingGraph
|
||||
colors={DEFAULT_LINE_COLORS}
|
||||
axes={axes}
|
||||
type={cellType}
|
||||
queries={queries}
|
||||
|
|
|
@ -8,6 +8,8 @@ import underlayCallback from 'src/kapacitor/helpers/ruleGraphUnderlay'
|
|||
|
||||
const RefreshingLineGraph = AutoRefresh(LineGraph)
|
||||
|
||||
import {LINE_COLORS_RULE_GRAPH} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
const {shape, string, func} = PropTypes
|
||||
const RuleGraph = ({
|
||||
query,
|
||||
|
@ -20,7 +22,6 @@ const RuleGraph = ({
|
|||
const autoRefreshMs = 30000
|
||||
const queryText = buildInfluxQLQuery({lower}, query)
|
||||
const queries = [{host: source.links.proxy, text: queryText}]
|
||||
const kapacitorLineColors = ['#4ED8A0']
|
||||
|
||||
if (!queryText) {
|
||||
return (
|
||||
|
@ -47,7 +48,7 @@ const RuleGraph = ({
|
|||
isGraphFilled={false}
|
||||
ruleValues={rule.values}
|
||||
autoRefresh={autoRefreshMs}
|
||||
overrideLineColors={kapacitorLineColors}
|
||||
colors={LINE_COLORS_RULE_GRAPH}
|
||||
underlayCallback={underlayCallback(rule)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -99,7 +99,7 @@ ColorDropdown.propTypes = {
|
|||
shape({
|
||||
hex: string.isRequired,
|
||||
name: string.isRequired,
|
||||
})
|
||||
}).isRequired
|
||||
).isRequired,
|
||||
stretchToFit: bool,
|
||||
disabled: bool,
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import uuid from 'uuid'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
import {LINE_COLOR_SCALES} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
class ColorScaleDropdown extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
expanded: false,
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleMenu = () => {
|
||||
const {disabled} = this.props
|
||||
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
this.setState({expanded: !this.state.expanded})
|
||||
}
|
||||
|
||||
handleClickOutside = () => {
|
||||
this.setState({expanded: false})
|
||||
}
|
||||
|
||||
handleDropdownClick = colorScale => () => {
|
||||
this.props.onChoose(colorScale)
|
||||
this.setState({expanded: false})
|
||||
}
|
||||
|
||||
generateGradientStyle = colors => ({
|
||||
background: `linear-gradient(to right, ${colors[0].hex} 0%,${
|
||||
colors[1].hex
|
||||
} 50%,${colors[2].hex} 100%)`,
|
||||
})
|
||||
|
||||
render() {
|
||||
const {expanded} = this.state
|
||||
const {selected, disabled, stretchToFit} = this.props
|
||||
|
||||
const dropdownClassNames = classnames('color-dropdown', {
|
||||
open: expanded,
|
||||
'color-dropdown--stretch': stretchToFit,
|
||||
})
|
||||
const toggleClassNames = classnames(
|
||||
'btn btn-sm btn-default color-dropdown--toggle',
|
||||
{active: expanded, 'color-dropdown__disabled': disabled}
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={dropdownClassNames}>
|
||||
<div
|
||||
className={toggleClassNames}
|
||||
onClick={this.handleToggleMenu}
|
||||
disabled={disabled}
|
||||
>
|
||||
<div
|
||||
className="color-dropdown--swatches"
|
||||
style={this.generateGradientStyle(selected)}
|
||||
/>
|
||||
<div className="color-dropdown--name">{selected[0].name}</div>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
{expanded ? (
|
||||
<div className="color-dropdown--menu">
|
||||
<FancyScrollbar autoHide={false} autoHeight={true}>
|
||||
{LINE_COLOR_SCALES.map(colorScale => (
|
||||
<div
|
||||
className={
|
||||
colorScale.name === selected[0].name
|
||||
? 'color-dropdown--item active'
|
||||
: 'color-dropdown--item'
|
||||
}
|
||||
key={uuid.v4()}
|
||||
onClick={this.handleDropdownClick(colorScale)}
|
||||
>
|
||||
<div
|
||||
className="color-dropdown--swatches"
|
||||
style={this.generateGradientStyle(colorScale.colors)}
|
||||
/>
|
||||
<span className="color-dropdown--name">
|
||||
{colorScale.name}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
ColorScaleDropdown.propTypes = {
|
||||
selected: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
}).isRequired
|
||||
).isRequired,
|
||||
onChoose: func.isRequired,
|
||||
stretchToFit: bool,
|
||||
disabled: bool,
|
||||
}
|
||||
|
||||
export default OnClickOutside(ColorScaleDropdown)
|
|
@ -23,11 +23,17 @@ import {
|
|||
LABEL_WIDTH,
|
||||
CHAR_PIXELS,
|
||||
barPlotter,
|
||||
hasherino,
|
||||
highlightSeriesOpts,
|
||||
} from 'src/shared/graphs/helpers'
|
||||
|
||||
import {
|
||||
DEFAULT_LINE_COLORS,
|
||||
getLineColorsHexes,
|
||||
} from 'src/shared/constants/graphColorPalettes'
|
||||
const {LINEAR, LOG, BASE_10, BASE_2} = AXES_SCALE_OPTIONS
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class Dygraph extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
@ -53,7 +59,7 @@ class Dygraph extends Component {
|
|||
fillGraph,
|
||||
logscale: y.scale === LOG,
|
||||
colors: this.getLineColors(),
|
||||
series: this.hashColorDygraphSeries(),
|
||||
series: this.colorDygraphSeries(),
|
||||
plugins: [new Dygraphs.Plugins.Crosshair({direction: 'vertical'})],
|
||||
axes: {
|
||||
y: {
|
||||
|
@ -149,7 +155,7 @@ class Dygraph extends Component {
|
|||
},
|
||||
},
|
||||
colors: this.getLineColors(),
|
||||
series: this.hashColorDygraphSeries(),
|
||||
series: this.colorDygraphSeries(),
|
||||
plotter: isBarGraph ? barPlotter : null,
|
||||
drawCallback: this.annotationsRef.heartbeat,
|
||||
}
|
||||
|
@ -189,6 +195,33 @@ class Dygraph extends Component {
|
|||
onZoom(this.formatTimeRange(lower), this.formatTimeRange(upper))
|
||||
}
|
||||
|
||||
colorDygraphSeries = () => {
|
||||
const {dygraphSeries, children, colors, overrideLineColors} = this.props
|
||||
const numSeries = Object.keys(dygraphSeries).length
|
||||
|
||||
let lineColors = getLineColorsHexes(colors, numSeries)
|
||||
|
||||
if (React.children && React.children.count(children)) {
|
||||
// If graph is line-plus-single-stat then reserve colors for single stat
|
||||
lineColors = getLineColorsHexes(DEFAULT_LINE_COLORS, numSeries)
|
||||
}
|
||||
|
||||
if (overrideLineColors) {
|
||||
lineColors = getLineColorsHexes(overrideLineColors, numSeries)
|
||||
}
|
||||
|
||||
const coloredDygraphSeries = {}
|
||||
|
||||
for (const seriesName in dygraphSeries) {
|
||||
const series = dygraphSeries[seriesName]
|
||||
const color = lineColors[Object.keys(dygraphSeries).indexOf(seriesName)]
|
||||
|
||||
coloredDygraphSeries[seriesName] = {...series, color}
|
||||
}
|
||||
|
||||
return coloredDygraphSeries
|
||||
}
|
||||
|
||||
eventToTimestamp = ({pageX: pxBetweenMouseAndPage}) => {
|
||||
const {left: pxBetweenGraphAndPage} = this.graphRef.getBoundingClientRect()
|
||||
const graphXCoordinate = pxBetweenMouseAndPage - pxBetweenGraphAndPage
|
||||
|
@ -213,21 +246,6 @@ class Dygraph extends Component {
|
|||
this.setState({isHoveringThisGraph: false})
|
||||
}
|
||||
|
||||
hashColorDygraphSeries = () => {
|
||||
const {dygraphSeries} = this.props
|
||||
const colors = this.getLineColors()
|
||||
const hashColorDygraphSeries = {}
|
||||
|
||||
for (const seriesName in dygraphSeries) {
|
||||
const series = dygraphSeries[seriesName]
|
||||
const hashIndex = hasherino(seriesName, colors.length)
|
||||
const color = colors[hashIndex]
|
||||
hashColorDygraphSeries[seriesName] = {...series, color}
|
||||
}
|
||||
|
||||
return hashColorDygraphSeries
|
||||
}
|
||||
|
||||
handleHideLegend = e => {
|
||||
const {top, bottom, left, right} = this.graphRef.getBoundingClientRect()
|
||||
|
||||
|
@ -363,7 +381,7 @@ class Dygraph extends Component {
|
|||
/>
|
||||
{staticLegend && (
|
||||
<StaticLegend
|
||||
dygraphSeries={this.hashColorDygraphSeries()}
|
||||
dygraphSeries={this.colorDygraphSeries()}
|
||||
dygraph={this.dygraph}
|
||||
handleReceiveStaticLegendHeight={
|
||||
this.handleReceiveStaticLegendHeight
|
||||
|
@ -420,7 +438,12 @@ Dygraph.propTypes = {
|
|||
isGraphFilled: bool,
|
||||
isBarGraph: bool,
|
||||
staticLegend: bool,
|
||||
overrideLineColors: array,
|
||||
overrideLineColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
dygraphSeries: shape({}).isRequired,
|
||||
ruleValues: shape({
|
||||
operator: string,
|
||||
|
@ -437,6 +460,7 @@ Dygraph.propTypes = {
|
|||
onZoom: func,
|
||||
mode: string,
|
||||
children: node,
|
||||
colors: colorsStringSchema.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({annotations: {mode}}) => ({
|
||||
|
|
|
@ -10,6 +10,8 @@ import {
|
|||
MIN_THRESHOLDS,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class Gauge extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
@ -325,21 +327,13 @@ class Gauge extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {arrayOf, number, shape, string} = PropTypes
|
||||
const {number, string} = PropTypes
|
||||
|
||||
Gauge.propTypes = {
|
||||
width: string.isRequired,
|
||||
height: string.isRequired,
|
||||
gaugePosition: number.isRequired,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
).isRequired,
|
||||
colors: colorsStringSchema.isRequired,
|
||||
prefix: string.isRequired,
|
||||
suffix: string.isRequired,
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import {DEFAULT_GAUGE_COLORS} from 'src/shared/constants/thresholds'
|
|||
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
|
||||
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'src/shared/constants'
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class GaugeChart extends PureComponent {
|
||||
render() {
|
||||
const {
|
||||
|
@ -82,15 +84,7 @@ GaugeChart.propTypes = {
|
|||
cellHeight: number,
|
||||
resizerTopHeight: number,
|
||||
resizeCoords: shape(),
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
prefix: string.isRequired,
|
||||
suffix: string.isRequired,
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import {IS_STATIC_LEGEND} from 'src/shared/constants'
|
|||
|
||||
import _ from 'lodash'
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
const getSource = (cell, source, sources, defaultSource) => {
|
||||
const s = _.get(cell, ['queries', '0', 'source'], null)
|
||||
if (!s) {
|
||||
|
@ -134,15 +136,7 @@ const propTypes = {
|
|||
i: string.isRequired,
|
||||
name: string.isRequired,
|
||||
type: string.isRequired,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
}).isRequired,
|
||||
templates: arrayOf(shape()),
|
||||
host: string,
|
||||
|
|
|
@ -6,7 +6,7 @@ import shallowCompare from 'react-addons-shallow-compare'
|
|||
import SingleStat from 'src/shared/components/SingleStat'
|
||||
import timeSeriesToDygraph from 'utils/timeSeriesTransformers'
|
||||
|
||||
import {SINGLE_STAT_LINE_COLORS} from 'src/shared/graphs/helpers'
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class LineGraph extends Component {
|
||||
constructor(props) {
|
||||
|
@ -85,10 +85,6 @@ class LineGraph extends Component {
|
|||
connectSeparatedPoints: true,
|
||||
}
|
||||
|
||||
const lineColors = showSingleStat
|
||||
? SINGLE_STAT_LINE_COLORS
|
||||
: overrideLineColors
|
||||
|
||||
const containerStyle = {
|
||||
width: 'calc(100% - 32px)',
|
||||
height: 'calc(100% - 16px)',
|
||||
|
@ -118,7 +114,8 @@ class LineGraph extends Component {
|
|||
resizeCoords={resizeCoords}
|
||||
dygraphSeries={dygraphSeries}
|
||||
setResolution={setResolution}
|
||||
overrideLineColors={lineColors}
|
||||
colors={colors}
|
||||
overrideLineColors={overrideLineColors}
|
||||
containerStyle={containerStyle}
|
||||
staticLegend={staticLegend}
|
||||
isGraphFilled={showSingleStat ? false : isGraphFilled}
|
||||
|
@ -201,15 +198,7 @@ LineGraph.propTypes = {
|
|||
resizeCoords: shape(),
|
||||
queries: arrayOf(shape({}).isRequired).isRequired,
|
||||
data: arrayOf(shape({}).isRequired).isRequired,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
}
|
||||
|
||||
export default LineGraph
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import ColorScaleDropdown from 'shared/components/ColorScaleDropdown'
|
||||
|
||||
import {updateLineColors} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class LineGraphColorSelector extends Component {
|
||||
handleSelectColors = colorScale => {
|
||||
const {handleUpdateLineColors} = this.props
|
||||
const {colors} = colorScale
|
||||
|
||||
handleUpdateLineColors(colors)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {lineColors} = this.props
|
||||
|
||||
return (
|
||||
<div className="form-group col-xs-12">
|
||||
<label>Line Colors</label>
|
||||
<ColorScaleDropdown
|
||||
onChoose={this.handleSelectColors}
|
||||
stretchToFit={true}
|
||||
selected={lineColors}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {func} = PropTypes
|
||||
|
||||
LineGraphColorSelector.propTypes = {
|
||||
lineColors: colorsStringSchema.isRequired,
|
||||
handleUpdateLineColors: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({cellEditorOverlay: {lineColors}}) => ({
|
||||
lineColors,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleUpdateLineColors: bindActionCreators(updateLineColors, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(
|
||||
LineGraphColorSelector
|
||||
)
|
|
@ -9,6 +9,8 @@ import SingleStat from 'shared/components/SingleStat'
|
|||
import GaugeChart from 'shared/components/GaugeChart'
|
||||
import TableGraph from 'shared/components/TableGraph'
|
||||
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
const RefreshingLineGraph = AutoRefresh(LineGraph)
|
||||
const RefreshingSingleStat = AutoRefresh(SingleStat)
|
||||
const RefreshingGaugeChart = AutoRefresh(GaugeChart)
|
||||
|
@ -154,15 +156,7 @@ RefreshingGraph.propTypes = {
|
|||
onZoom: func,
|
||||
resizeCoords: shape(),
|
||||
grabDataForDownload: func,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
cellID: string,
|
||||
inView: bool,
|
||||
tableOptions: shape({}),
|
||||
|
|
|
@ -6,6 +6,7 @@ import lastValues from 'shared/parsing/lastValues'
|
|||
import {SMALL_CELL_HEIGHT} from 'shared/graphs/helpers'
|
||||
import {DYGRAPH_CONTAINER_V_MARGIN} from 'shared/constants'
|
||||
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class SingleStat extends PureComponent {
|
||||
render() {
|
||||
|
@ -78,15 +79,7 @@ SingleStat.propTypes = {
|
|||
data: arrayOf(shape()).isRequired,
|
||||
isFetchingInitially: bool,
|
||||
cellHeight: number,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
prefix: string,
|
||||
suffix: string,
|
||||
lineGraph: bool,
|
||||
|
|
|
@ -86,10 +86,6 @@ class StaticLegend extends Component {
|
|||
key={uuid.v4()}
|
||||
onMouseDown={this.handleClick(i)}
|
||||
>
|
||||
<div
|
||||
className="static-legend--dot"
|
||||
style={{backgroundColor: colors[i]}}
|
||||
/>
|
||||
<span style={{color: colors[i]}}>{removeMeasurement(v)}</span>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from 'src/shared/constants/tableGraph'
|
||||
|
||||
import {generateThresholdsListHexs} from 'shared/constants/colorOperations'
|
||||
import {colorsStringSchema} from 'shared/schemas'
|
||||
|
||||
class TableGraph extends Component {
|
||||
constructor(props) {
|
||||
|
@ -432,15 +433,7 @@ TableGraph.propTypes = {
|
|||
}),
|
||||
hoverTime: string,
|
||||
onSetHoverTime: func,
|
||||
colors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
colors: colorsStringSchema,
|
||||
setDataLabels: func,
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import Threshold from 'src/dashboards/components/Threshold'
|
|||
import ColorDropdown from 'shared/components/ColorDropdown'
|
||||
|
||||
import {updateThresholdsListColors} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
import {colorsNumberSchema} from 'shared/schemas'
|
||||
|
||||
import {
|
||||
THRESHOLD_COLORS,
|
||||
|
@ -166,22 +167,14 @@ class ThresholdsList extends Component {
|
|||
)
|
||||
}
|
||||
}
|
||||
const {arrayOf, bool, func, number, shape, string} = PropTypes
|
||||
const {bool, func, string} = PropTypes
|
||||
|
||||
ThresholdsList.defaultProps = {
|
||||
showListHeading: false,
|
||||
}
|
||||
ThresholdsList.propTypes = {
|
||||
thresholdsListType: string.isRequired,
|
||||
thresholdsListColors: arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
thresholdsListColors: colorsNumberSchema.isRequired,
|
||||
handleUpdateThresholdsListColors: func.isRequired,
|
||||
onResetFocus: func.isRequired,
|
||||
showListHeading: bool,
|
||||
|
|
|
@ -1,36 +1,21 @@
|
|||
import _ from 'lodash'
|
||||
import chroma from 'chroma-js'
|
||||
|
||||
import {
|
||||
THRESHOLD_COLORS,
|
||||
THRESHOLD_TYPE_BASE,
|
||||
THRESHOLD_TYPE_TEXT,
|
||||
} from 'shared/constants/thresholds'
|
||||
|
||||
const hexToRgb = hex => {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16),
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
const averageRgbValues = valuesObject => {
|
||||
const {r, g, b} = valuesObject
|
||||
return (r + g + b) / 3
|
||||
}
|
||||
|
||||
const trueNeutralGrey = 128
|
||||
const luminanceThreshold = 0.5
|
||||
|
||||
const getLegibleTextColor = bgColorHex => {
|
||||
const averageBackground = averageRgbValues(hexToRgb(bgColorHex))
|
||||
const isBackgroundLight = averageBackground > trueNeutralGrey
|
||||
|
||||
const darkText = '#292933'
|
||||
const lightText = '#ffffff'
|
||||
|
||||
return isBackgroundLight ? darkText : lightText
|
||||
return chroma(bgColorHex).luminance() < luminanceThreshold
|
||||
? darkText
|
||||
: lightText
|
||||
}
|
||||
|
||||
const findNearestCrossedThreshold = (colors, lastValue) => {
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
import chroma from 'chroma-js'
|
||||
|
||||
const COLOR_TYPE_SCALE = 'scale'
|
||||
|
||||
// Color Palettes
|
||||
export const LINE_COLORS_A = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#31C0F6',
|
||||
id: '0',
|
||||
name: 'Nineteen Eighty Four',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#A500A5',
|
||||
id: '0',
|
||||
name: 'Nineteen Eighty Four',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#FF7E27',
|
||||
id: '0',
|
||||
name: 'Nineteen Eighty Four',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_B = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#74D495',
|
||||
id: '1',
|
||||
name: 'Atlantis',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#3F3FBA',
|
||||
id: '1',
|
||||
name: 'Atlantis',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#FF4D9E',
|
||||
id: '1',
|
||||
name: 'Atlantis',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_C = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#8F8AF4',
|
||||
id: '1',
|
||||
name: 'Do Androids Dream of Electric Sheep?',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#A51414',
|
||||
id: '1',
|
||||
name: 'Do Androids Dream of Electric Sheep?',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#F4CF31',
|
||||
id: '1',
|
||||
name: 'Do Androids Dream of Electric Sheep?',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_D = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#FD7A5D',
|
||||
id: '1',
|
||||
name: 'Delorean',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#5F1CF2',
|
||||
id: '1',
|
||||
name: 'Delorean',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#4CE09A',
|
||||
id: '1',
|
||||
name: 'Delorean',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_E = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#FDC44F',
|
||||
id: '1',
|
||||
name: 'Cthulhu',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#007C76',
|
||||
id: '1',
|
||||
name: 'Cthulhu',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#8983FF',
|
||||
id: '1',
|
||||
name: 'Cthulhu',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_F = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#DA6FF1',
|
||||
id: '1',
|
||||
name: 'Ectoplasm',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#00717A',
|
||||
id: '1',
|
||||
name: 'Ectoplasm',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#ACFF76',
|
||||
id: '1',
|
||||
name: 'Ectoplasm',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_G = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#F6F6F8',
|
||||
id: '1',
|
||||
name: 'T-Max 400 Film',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#A4A8B6',
|
||||
id: '1',
|
||||
name: 'T-Max 400 Film',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#545667',
|
||||
id: '1',
|
||||
name: 'T-Max 400 Film',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const LINE_COLORS_RULE_GRAPH = [
|
||||
{
|
||||
type: COLOR_TYPE_SCALE,
|
||||
hex: '#4ED8A0',
|
||||
id: '1',
|
||||
name: 'Rainforest',
|
||||
value: '0',
|
||||
},
|
||||
]
|
||||
|
||||
export const DEFAULT_LINE_COLORS = LINE_COLORS_A
|
||||
|
||||
export const LINE_COLOR_SCALES = [
|
||||
LINE_COLORS_A,
|
||||
LINE_COLORS_B,
|
||||
LINE_COLORS_C,
|
||||
LINE_COLORS_D,
|
||||
LINE_COLORS_E,
|
||||
LINE_COLORS_F,
|
||||
LINE_COLORS_G,
|
||||
].map(colorScale => {
|
||||
const name = colorScale[0].name
|
||||
const colors = colorScale
|
||||
const id = colorScale[0].id
|
||||
|
||||
return {name, colors, id}
|
||||
})
|
||||
|
||||
export const validateLineColors = colors => {
|
||||
if (!colors || colors.length === 0) {
|
||||
return DEFAULT_LINE_COLORS
|
||||
}
|
||||
|
||||
const testColorsTypes =
|
||||
colors.filter(color => color.type === COLOR_TYPE_SCALE).length ===
|
||||
colors.length
|
||||
|
||||
return testColorsTypes ? colors : DEFAULT_LINE_COLORS
|
||||
}
|
||||
|
||||
export const getLineColorsHexes = (colors, numSeries) => {
|
||||
const validatedColors = validateLineColors(colors) // ensures safe defaults
|
||||
const colorsHexArray = validatedColors.map(color => color.hex)
|
||||
|
||||
if (numSeries === 1 || numSeries === 0) {
|
||||
return [colorsHexArray[0]]
|
||||
}
|
||||
if (numSeries === 2) {
|
||||
return [colorsHexArray[0], colorsHexArray[1]]
|
||||
}
|
||||
return chroma
|
||||
.scale(colorsHexArray)
|
||||
.mode('lch')
|
||||
.colors(numSeries)
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import PropTypes from 'prop-types'
|
||||
|
||||
const {shape, string} = PropTypes
|
||||
const {arrayOf, number, shape, string} = PropTypes
|
||||
|
||||
export const annotation = shape({
|
||||
id: string.isRequired,
|
||||
|
@ -9,3 +9,23 @@ export const annotation = shape({
|
|||
text: string.isRequired,
|
||||
type: string.isRequired,
|
||||
})
|
||||
|
||||
export const colorsStringSchema = arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: string.isRequired,
|
||||
}).isRequired
|
||||
)
|
||||
|
||||
export const colorsNumberSchema = arrayOf(
|
||||
shape({
|
||||
type: string.isRequired,
|
||||
hex: string.isRequired,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
value: number.isRequired,
|
||||
}).isRequired
|
||||
)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
export const fixtureStatusPageCells = [
|
||||
{
|
||||
i: 'alerts-bar-graph',
|
||||
|
@ -8,6 +10,7 @@ export const fixtureStatusPageCells = [
|
|||
h: 4,
|
||||
legend: {},
|
||||
name: 'Alert Events per Day – Last 30 Days',
|
||||
colors: DEFAULT_LINE_COLORS,
|
||||
queries: [
|
||||
{
|
||||
query:
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
*/
|
||||
|
||||
$color-dropdown--circle: 14px;
|
||||
$color-dropdown--bar: 104px;
|
||||
$color-dropdown--bar-height: 10px;
|
||||
$color-dropdown--left-padding: 11px;
|
||||
$color-dropdown--name-padding: 20px;
|
||||
|
||||
.color-dropdown {
|
||||
width: 140px;
|
||||
|
@ -31,11 +35,11 @@ $color-dropdown--circle: 14px;
|
|||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
z-index: 5;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 5px 0.6px fade-out($g0-obsidian, 0.7);
|
||||
@include gradient-h($g0-obsidian,$g2-kevlar);
|
||||
@include gradient-h($g0-obsidian, $g2-kevlar);
|
||||
}
|
||||
.color-dropdown--item {
|
||||
@include no-user-select();
|
||||
|
@ -43,9 +47,7 @@ $color-dropdown--circle: 14px;
|
|||
height: 28px;
|
||||
position: relative;
|
||||
color: $g11-sidewalk;
|
||||
transition:
|
||||
color 0.25s ease,
|
||||
background-color 0.25s ease;
|
||||
transition: color 0.25s ease, background-color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: $g4-onyx;
|
||||
|
@ -67,6 +69,7 @@ $color-dropdown--circle: 14px;
|
|||
}
|
||||
}
|
||||
.color-dropdown--swatch,
|
||||
.color-dropdown--swatches,
|
||||
.color-dropdown--name {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
|
@ -76,21 +79,35 @@ $color-dropdown--circle: 14px;
|
|||
width: $color-dropdown--circle;
|
||||
height: $color-dropdown--circle;
|
||||
border-radius: 50%;
|
||||
left: 11px;
|
||||
left: $color-dropdown--left-padding;
|
||||
}
|
||||
.color-dropdown--swatches {
|
||||
width: $color-dropdown--bar;
|
||||
height: $color-dropdown--bar-height;
|
||||
border-radius: $color-dropdown--bar-height / 2;
|
||||
left: $color-dropdown--left-padding;
|
||||
}
|
||||
.color-dropdown--name {
|
||||
text-align: left;
|
||||
right: 11px;
|
||||
left: 34px;
|
||||
right: $color-dropdown--name-padding;
|
||||
left: $color-dropdown--circle + $color-dropdown--name-padding;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-transform: capitalize;
|
||||
|
||||
.color-dropdown--swatches + & {
|
||||
left: $color-dropdown--bar + $color-dropdown--name-padding;
|
||||
}
|
||||
}
|
||||
.color-dropdown .color-dropdown--menu .fancy-scroll--container .fancy-scroll--track-v .fancy-scroll--thumb-v {
|
||||
@include gradient-v($g9-mountain,$g7-graphite);
|
||||
.color-dropdown
|
||||
.color-dropdown--menu
|
||||
.fancy-scroll--container
|
||||
.fancy-scroll--track-v
|
||||
.fancy-scroll--thumb-v {
|
||||
@include gradient-v($g9-mountain, $g7-graphite);
|
||||
}
|
||||
.color-dropdown--toggle.color-dropdown__disabled {
|
||||
color: $g7-graphite;
|
||||
|
|
|
@ -16,17 +16,9 @@
|
|||
flex-wrap: wrap;
|
||||
max-height: 50%;
|
||||
overflow: auto;
|
||||
@include custom-scrollbar($g3-castle,$g6-smoke);
|
||||
}
|
||||
.static-legend--dot {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 4px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: $g20-white;
|
||||
@include custom-scrollbar($g3-castle, $g6-smoke);
|
||||
}
|
||||
|
||||
.static-legend--item,
|
||||
.static-legend--single {
|
||||
height: 22px;
|
||||
|
@ -43,8 +35,7 @@
|
|||
.static-legend--item {
|
||||
transition: background-color 0.25s ease, color 0.25s ease;
|
||||
|
||||
span,
|
||||
.static-legend--dot {
|
||||
span {
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
@ -53,8 +44,7 @@
|
|||
cursor: pointer;
|
||||
background-color: $g6-smoke;
|
||||
|
||||
span,
|
||||
.static-legend--dot {
|
||||
span {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
@ -62,16 +52,14 @@
|
|||
background-color: $g1-raven;
|
||||
font-style: italic;
|
||||
|
||||
span,
|
||||
.static-legend--dot {
|
||||
span {
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $g2-kevlar;
|
||||
|
||||
span,
|
||||
.static-legend--dot {
|
||||
span {
|
||||
opacity: 0.65;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
updateThresholdsListColors,
|
||||
updateThresholdsListType,
|
||||
updateGaugeColors,
|
||||
updateLineColors,
|
||||
updateAxes,
|
||||
} from 'src/dashboards/actions/cellEditorOverlay'
|
||||
import {DEFAULT_TABLE_OPTIONS} from 'src/shared/constants/tableGraph'
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
validateThresholdsListColors,
|
||||
getThresholdsListType,
|
||||
} from 'shared/constants/thresholds'
|
||||
import {validateLineColors} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
const defaultCellType = 'line'
|
||||
const defaultCellName = 'defaultCell'
|
||||
|
@ -45,6 +47,7 @@ const defaultThresholdsListColors = validateThresholdsListColors(
|
|||
defaultThresholdsListType
|
||||
)
|
||||
const defaultGaugeColors = validateGaugeColors(defaultCell.colors)
|
||||
const defaultLineColors = validateLineColors(defaultCell.colors)
|
||||
|
||||
describe('Dashboards.Reducers.cellEditorOverlay', () => {
|
||||
it('should show cell editor overlay', () => {
|
||||
|
@ -117,4 +120,11 @@ describe('Dashboards.Reducers.cellEditorOverlay', () => {
|
|||
|
||||
expect(actual.cell.axes).toBe(expected)
|
||||
})
|
||||
|
||||
it('should update the cell line graph colors', () => {
|
||||
const actual = reducer(initialState, updateLineColors(defaultLineColors))
|
||||
const expected = defaultLineColors
|
||||
|
||||
expect(actual.lineColors).toBe(expected)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1686,6 +1686,10 @@ chownr@^1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
|
||||
|
||||
chroma-js@^1.3.6:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/chroma-js/-/chroma-js-1.3.6.tgz#22dd7220ef6b55dcfcb8ef92982baaf55dced45d"
|
||||
|
||||
ci-info@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.2.tgz#03561259db48d0474c8bdc90f5b47b068b6bbfb4"
|
||||
|
|
Loading…
Reference in New Issue