Skip to content

Commit

Permalink
feat(ui): add heatmap vis type
Browse files Browse the repository at this point in the history
  • Loading branch information
chnn committed May 16, 2019
1 parent 7b2d76c commit efb97e0
Show file tree
Hide file tree
Showing 33 changed files with 868 additions and 164 deletions.
37 changes: 37 additions & 0 deletions dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,12 @@ func UnmarshalViewPropertiesJSON(b []byte) (ViewProperties, error) {
return nil, err
}
vis = hv
case "heatmap":
var hv HeatmapViewProperties
if err := json.Unmarshal(v.B, &hv); err != nil {
return nil, err
}
vis = hv
}
case "empty":
var ev EmptyViewProperties
Expand Down Expand Up @@ -460,6 +466,15 @@ func MarshalViewPropertiesJSON(v ViewProperties) ([]byte, error) {

HistogramViewProperties: vis,
}
case HeatmapViewProperties:
s = struct {
Shape string `json:"shape"`
HeatmapViewProperties
}{
Shape: "chronograf-v2",

HeatmapViewProperties: vis,
}
case MarkdownViewProperties:
s = struct {
Shape string `json:"shape"`
Expand Down Expand Up @@ -602,6 +617,26 @@ type HistogramViewProperties struct {
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
}

// HeatmapViewProperties represents options for heatmap view in Chronograf
type HeatmapViewProperties struct {
Type string `json:"type"`
Queries []DashboardQuery `json:"queries"`
ViewColors []string `json:"colors"`
BinSize int32 `json:"binSize"`
XColumn string `json:"xColumn"`
YColumn string `json:"yColumn"`
XDomain []float64 `json:"xDomain,omitEmpty"`
YDomain []float64 `json:"yDomain,omitEmpty"`
XAxisLabel string `json:"xAxisLabel"`
YAxisLabel string `json:"yAxisLabel"`
XPrefix string `json:"xPrefix"`
XSuffix string `json:"xSuffix"`
YPrefix string `json:"yPrefix"`
YSuffix string `json:"ySuffix"`
Note string `json:"note"`
ShowNoteWhenEmpty bool `json:"showNoteWhenEmpty"`
}

// GaugeViewProperties represents options for gauge view in Chronograf
type GaugeViewProperties struct {
Type string `json:"type"`
Expand Down Expand Up @@ -656,6 +691,7 @@ func (XYViewProperties) viewProperties() {}
func (LinePlusSingleStatProperties) viewProperties() {}
func (SingleStatViewProperties) viewProperties() {}
func (HistogramViewProperties) viewProperties() {}
func (HeatmapViewProperties) viewProperties() {}
func (GaugeViewProperties) viewProperties() {}
func (TableViewProperties) viewProperties() {}
func (MarkdownViewProperties) viewProperties() {}
Expand All @@ -665,6 +701,7 @@ func (v XYViewProperties) GetType() string { return v.Type }
func (v LinePlusSingleStatProperties) GetType() string { return v.Type }
func (v SingleStatViewProperties) GetType() string { return v.Type }
func (v HistogramViewProperties) GetType() string { return v.Type }
func (v HeatmapViewProperties) GetType() string { return v.Type }
func (v GaugeViewProperties) GetType() string { return v.Type }
func (v TableViewProperties) GetType() string { return v.Type }
func (v MarkdownViewProperties) GetType() string { return v.Type }
Expand Down
3 changes: 3 additions & 0 deletions ui/src/shared/components/AutoDomainInput.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.auto-domain-input--custom {
padding-top: 4px;
}
14 changes: 9 additions & 5 deletions ui/src/shared/components/AutoDomainInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ const AutoDomainInput: SFC<AutoDomainInputProps> = ({
const initialMax = domain ? String(domain[1]) : ''

return (
<Form.Element label={label} errorMessage={errorMessage}>
<Form.Element
label={label}
errorMessage={errorMessage}
className="auto-domain-input"
>
<Grid>
<Grid.Row>
<Grid.Column widthXS={Columns.Twelve}>
Expand All @@ -141,16 +145,16 @@ const AutoDomainInput: SFC<AutoDomainInputProps> = ({
</Radio>
</Grid.Column>
</Grid.Row>
<Grid.Row>
{showInputs && (
{showInputs && (
<Grid.Row className="auto-domain-input--custom">
<MinMaxInputs
initialMin={initialMin}
initialMax={initialMax}
onSetMinMax={onSetDomain}
onSetErrorMessage={setErrorMessage}
/>
)}
</Grid.Row>
</Grid.Row>
)}
</Grid>
</Form.Element>
)
Expand Down
23 changes: 6 additions & 17 deletions ui/src/shared/components/ColorSchemeDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Libraries
import React, {SFC, CSSProperties} from 'react'
import React, {SFC} from 'react'

// Components
import {Dropdown, DropdownMenuColors} from 'src/clockface'
import ColorSchemeDropdownItem from 'src/shared/components/ColorSchemeDropdownItem'

// Constants
import {
Expand All @@ -18,14 +19,6 @@ interface Props {
onChange: (colors: Color[]) => void
}

const generateGradientStyle = (colors: Color[]): CSSProperties => {
const [start, mid, stop] = colors.map(color => color.hex)

return {
background: `linear-gradient(to right, ${start} 0%, ${mid} 50%, ${stop} 100%)`,
}
}

const findSelectedScaleID = (colors: Color[]) => {
const key = (colors: Color[]) => colors.map(color => color.hex).join(', ')
const needle = key(colors)
Expand All @@ -44,17 +37,13 @@ const ColorSchemeDropdown: SFC<Props> = ({value, onChange}) => {
selectedID={findSelectedScaleID(value)}
onChange={onChange}
menuColor={DropdownMenuColors.Onyx}
customClass="color-scheme-dropdown"
>
{LINE_COLOR_SCALES.map(({id, name, colors}) => (
<Dropdown.Item key={id} id={id} value={colors}>
<div className="color-scheme-dropdown--item">
<div
className="color-scheme-dropdown--swatches"
style={generateGradientStyle(colors)}
/>
<div className="color-scheme-dropdown--name">{name}</div>
</div>
<ColorSchemeDropdownItem
name={name}
colors={colors.map(c => c.hex)}
/>
</Dropdown.Item>
))}
</Dropdown>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.color-scheme-dropdown--item {
.color-scheme-dropdown-item {
display: flex;
justify-content: flex-start;
align-items: center;
}

.color-scheme-dropdown--swatches {
.color-scheme-dropdown-item--swatches {
width: 100px;
height: 10px;
border-radius: 5px;
Expand Down
31 changes: 31 additions & 0 deletions ui/src/shared/components/ColorSchemeDropdownItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Libraries
import React, {CSSProperties, FunctionComponent} from 'react'

const generateGradientStyle = (colors: string[]): CSSProperties => {
const stops = colors
.map((hex, i) => `${hex} ${Math.round((i / (colors.length - 1)) * 100)}%`)
.join(', ')

return {
background: `linear-gradient(to right, ${stops})`,
}
}

interface Props {
name: string
colors: string[]
}

const ColorSchemeDropdownItem: FunctionComponent<Props> = ({name, colors}) => {
return (
<div className="color-scheme-dropdown-item">
<div
className="color-scheme-dropdown-item--swatches"
style={generateGradientStyle(colors)}
/>
<div className="color-scheme-dropdown-item--name">{name}</div>
</div>
)
}

export default ColorSchemeDropdownItem
39 changes: 27 additions & 12 deletions ui/src/shared/components/FeatureFlag.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
import {PureComponent} from 'react'
import {FunctionComponent} from 'react'

interface Props {
name?: string
export const isFlagEnabled = (flagName: string) => {
return JSON.parse(window.localStorage.featureFlags || {})[flagName] === true
}

export default class extends PureComponent<Props> {
public render() {
if (this.isHidden) {
return null
}
export const toggleFlag = (flagName: string) => {
const featureFlags = JSON.parse(window.localStorage.featureFlags || {})

return this.props.children
}
featureFlags[flagName] = !featureFlags[flagName]

window.localStorage.featureFlags = JSON.stringify(featureFlags)
}

interface Props {
name: string
}

private get isHidden(): boolean {
return process.env.NODE_ENV !== 'development'
const FeatureFlag: FunctionComponent<Props> = ({name, children}) => {
if (!isFlagEnabled(name)) {
return null
}

return children as any
}

export default FeatureFlag

// Expose toggleFlag on the window for convenience
const w = window as any

w.influx = {
toggleFeatureFlag: toggleFlag,
}
107 changes: 107 additions & 0 deletions ui/src/shared/components/HeatmapContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Libraries
import React, {FunctionComponent} from 'react'
import {Config, Table} from '@influxdata/vis'
import {get} from 'lodash'

// Components
import EmptyGraphMessage from 'src/shared/components/EmptyGraphMessage'
import GraphLoadingDots from 'src/shared/components/GraphLoadingDots'

// Utils
import {useVisDomainSettings} from 'src/shared/utils/useVisDomainSettings'
import {getFormatter} from 'src/shared/utils/vis'

// Constants
import {VIS_THEME} from 'src/shared/constants'
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
import {INVALID_DATA_COPY} from 'src/shared/copy/cell'

// Types
import {RemoteDataState, HeatmapView} from 'src/types'

interface Props {
table: Table
loading: RemoteDataState
viewProperties: HeatmapView
children: (config: Config) => JSX.Element
}

const HeatmapContainer: FunctionComponent<Props> = ({
table,
loading,
viewProperties: {
xColumn,
yColumn,
xDomain: storedXDomain,
yDomain: storedYDomain,
xAxisLabel,
yAxisLabel,
xPrefix,
xSuffix,
yPrefix,
ySuffix,
colors: storedColors,
binSize,
},
children,
}) => {
const [xDomain, onSetXDomain, onResetXDomain] = useVisDomainSettings(
storedXDomain,
get(table, ['columns', xColumn, 'data'], [])
)

const [yDomain, onSetYDomain, onResetYDomain] = useVisDomainSettings(
storedYDomain,
get(table, ['columns', yColumn, 'data'], [])
)

const isValidView =
xColumn && yColumn && table.columns[xColumn] && table.columns[yColumn]

if (!isValidView) {
return <EmptyGraphMessage message={INVALID_DATA_COPY} />
}

const colors =
storedColors && storedColors.length
? storedColors
: DEFAULT_LINE_COLORS.map(c => c.hex)

const xFormatter = getFormatter(table.columns[xColumn].type, xPrefix, xSuffix)
const yFormatter = getFormatter(table.columns[yColumn].type, yPrefix, ySuffix)

const config: Config = {
...VIS_THEME,
table,
xAxisLabel,
yAxisLabel,
xDomain,
onSetXDomain,
onResetXDomain,
yDomain,
onSetYDomain,
onResetYDomain,
valueFormatters: {
[xColumn]: xFormatter,
[yColumn]: yFormatter,
},
layers: [
{
type: 'heatmap',
x: xColumn,
y: yColumn,
colors,
binSize,
},
],
}

return (
<div className="vis-plot-container">
{loading === RemoteDataState.Loading && <GraphLoadingDots />}
{children(config)}
</div>
)
}

export default HeatmapContainer
Loading

0 comments on commit efb97e0

Please sign in to comment.