Skip to content

Commit

Permalink
feat: add additional columns to Static Legend for stacked line layer (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
TCL735 authored Apr 29, 2021
1 parent 8e3c1de commit f2704fb
Show file tree
Hide file tree
Showing 13 changed files with 351 additions and 142 deletions.
2 changes: 1 addition & 1 deletion giraffe/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@influxdata/giraffe",
"version": "2.8.2",
"version": "2.9.0",
"main": "dist/index.js",
"module": "src/index.js",
"license": "MIT",
Expand Down
8 changes: 7 additions & 1 deletion giraffe/src/components/StaticLegendBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,17 @@ export const StaticLegendBox: FunctionComponent<StaticLegendBoxProps> = props =>
const layerConfig = configOverride.layers[staticLegendOverride.layer]
const valueColumnKey = layerConfig[staticLegendOverride.valueAxis]

const position = Array.isArray(
(spec as LineLayerSpec).stackedDomainValueColumn
)
? 'stacked'
: 'overlaid'
const legendData = convertLineSpec(
staticLegendOverride,
spec as LineLayerSpec,
columnFormatter,
valueColumnKey
valueColumnKey,
position
)
const {
legendBackgroundColor: backgroundColor,
Expand Down
25 changes: 22 additions & 3 deletions giraffe/src/transforms/line.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import {
CumulativeValuesByTime,
DomainLabel,
LatestIndexMap,
LineData,
LineLayerSpec,
LinePosition,
NumericColumnData,
Table,
} from '../types'
import {FILL, VALUE} from '../constants/columnKeys'
import {FILL, TIME, VALUE} from '../constants/columnKeys'
import {createGroupIDColumn, getNominalColorScale} from './'
import {getDomainDataFromLines} from '../utils/lineData'
import {isDefined} from '../utils/isDefined'

export const mapCumulativeValuesToTimeRange = (
timesCol: NumericColumnData,
Expand Down Expand Up @@ -73,6 +75,7 @@ export const lineTransform = (
const fillScale = getNominalColorScale(fillColumnMap, colors)
const lineData: LineData = {}
let stackedValuesByTime: CumulativeValuesByTime = {}
const latestIndices: LatestIndexMap = {}

if (position === 'stacked') {
if (yColumnKey === VALUE) {
Expand Down Expand Up @@ -114,6 +117,23 @@ export const lineTransform = (
lineData[groupID].xs.push(x)
lineData[groupID].ys.push(y)

// remember the latest (most recent) index for each group
if (!isDefined(latestIndices[groupID])) {
latestIndices[groupID] = i
} else if (yColumnKey === TIME) {
if (
y > yCol[latestIndices[groupID]] ||
!isDefined(yCol[latestIndices[groupID]])
) {
latestIndices[groupID] = i
}
} else if (
x > xCol[latestIndices[groupID]] ||
!isDefined(xCol[latestIndices[groupID]])
) {
latestIndices[groupID] = i
}

if (x < xMin) {
xMin = x
}
Expand All @@ -139,7 +159,6 @@ export const lineTransform = (
)
}

//todo: look at stackdomainvalue column for stacked data for static legend
return {
type: 'line',
inputTable,
Expand All @@ -152,7 +171,7 @@ export const lineTransform = (
xColumnType: table.getColumnType(xColumnKey),
yColumnType: table.getColumnType(yColumnKey),
scales: {fill: fillScale},
columnGroupMaps: {fill: fillColumnMap},
columnGroupMaps: {fill: fillColumnMap, latestIndices},
stackedDomainValueColumn,
}
}
10 changes: 10 additions & 0 deletions giraffe/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ export interface LineLayerSpec {
}
columnGroupMaps: {
fill: ColumnGroupMap
latestIndices: LatestIndexMap
}
stackedDomainValueColumn?: NumericColumnData
}
Expand Down Expand Up @@ -807,6 +808,15 @@ export interface ColumnGroupMap {
mappings: Array<{[columnKey: string]: any}>
}

export interface LatestIndexMap {
// each Column in a Table contains a single array even when there are multiple series
// for example, a line graph has multiple lines (series), but each Column has only one array
// Static Legend needs to know where each line (series) ends, to render the "latest"
// - "latest" is the timestamp with the highest value in each line (series)
// - Keep a map of the highest timestamp's index for each series
[columnKey: string]: number
}

export type LineData = {
[groupID: number]: {
xs: number[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@ import {newTable} from '../newTable'
import {LayerTypes} from '../../types'
import memoizeOne from 'memoize-one'

interface SampleTableOptions {
include_negative?: boolean
all_negative?: boolean
decimalPlaces?: number
maxValue?: number
numberOfRecords?: number
recordsPerLine?: number
plotType?: string
}

const getRandomNumber = (
max: number,
decimalPlaces: number,
Expand All @@ -28,55 +18,6 @@ export const COLUMN_KEY = 'cpu'
export const POINT_KEY = 'disk'
export const HOST_KEY = 'host'

export const createSampleTable = (options: SampleTableOptions) => {
const {
include_negative = false,
all_negative = false,
decimalPlaces = 2,
maxValue = 100,
numberOfRecords = 20,
recordsPerLine = 5,
plotType = 'line',
} = options

const now = Date.now()
const TIME_COL = []
const VALUE_COL = []
const CPU_COL = []
const SYMBOL_COL = []
const DISK_COL = []
const HOST_COL = []

for (let i = 0; i < numberOfRecords; i += 1) {
let num = getRandomNumber(maxValue, decimalPlaces)
if (include_negative) {
num = all_negative
? Math.abs(num) * -1
: getRandomNumber(maxValue, decimalPlaces, true)
}
VALUE_COL.push(num)
CPU_COL.push(`${COLUMN_KEY}${Math.floor(i / recordsPerLine)}`)
TIME_COL.push(now + (i % recordsPerLine) * 1000 * 60)
if (plotType === LayerTypes.Scatter) {
SYMBOL_COL.push(i % 2)
DISK_COL.push(`disk-${i % recordsPerLine}`)
HOST_COL.push(`host-${i % 2}`)
}
}
const table = newTable(numberOfRecords)
.addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL)
.addColumn('_value', 'system', 'number', VALUE_COL)

if (plotType === LayerTypes.Scatter) {
return table
.addColumn(POINT_KEY, 'string', 'string', DISK_COL)
.addColumn('__symbol', 'string', 'string', SYMBOL_COL)
.addColumn(HOST_KEY, 'string', 'string', HOST_COL)
}
return table.addColumn(COLUMN_KEY, 'string', 'string', CPU_COL)
}

const now = Date.now()
const defaultNumberOfRecords = 80
const defaultRecordsPerLine = 20

Expand All @@ -100,6 +41,7 @@ interface RandomFillColumns {
export const getRandomTable = memoizeOne(
(
maxValue: number,
includeNegative: boolean,
numberOfRecords: number = defaultNumberOfRecords,
recordsPerLine: number = defaultRecordsPerLine,
fillColumns?: ColumnsArg,
Expand All @@ -112,9 +54,10 @@ export const getRandomTable = memoizeOne(
columnNames: [],
columnValues: [],
} as RandomFillColumns
const now = Date.now()

for (let i = 0; i < numberOfRecords; i += 1) {
valueColumn.push(getRandomNumber(maxValue, 2))
valueColumn.push(getRandomNumber(maxValue, 2, includeNegative))
cpuColumn.push(`cpu${Math.floor(i / recordsPerLine)}`)
timeColumn.push(now + (i % recordsPerLine) * 1000 * 60)
}
Expand Down Expand Up @@ -173,3 +116,61 @@ export const getRandomTable = memoizeOne(
return table
}
)

export interface SampleTableOptions {
include_negative?: boolean
all_negative?: boolean
decimalPlaces?: number
maxValue?: number
numberOfRecords?: number
recordsPerLine?: number
plotType?: string
}

export const createSampleTable = (options: SampleTableOptions) => {
const {
include_negative = false,
all_negative = false,
decimalPlaces = 2,
maxValue = 100,
numberOfRecords = 20,
recordsPerLine = 5,
plotType = 'line',
} = options

const now = Date.now()
const TIME_COL = []
const VALUE_COL = []
const CPU_COL = []
const SYMBOL_COL = []
const DISK_COL = []
const HOST_COL = []

for (let i = 0; i < numberOfRecords; i += 1) {
let num = getRandomNumber(maxValue, decimalPlaces)
if (include_negative) {
num = all_negative
? Math.abs(num) * -1
: getRandomNumber(maxValue, decimalPlaces, true)
}
VALUE_COL.push(num)
CPU_COL.push(`${COLUMN_KEY}${Math.floor(i / recordsPerLine)}`)
TIME_COL.push(now + (i % recordsPerLine) * 1000 * 60)
if (plotType === LayerTypes.Scatter) {
SYMBOL_COL.push(i % 2)
DISK_COL.push(`disk-${i % recordsPerLine}`)
HOST_COL.push(`host-${i % 2}`)
}
}
const table = newTable(numberOfRecords)
.addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL)
.addColumn('_value', 'system', 'number', VALUE_COL)

if (plotType === LayerTypes.Scatter) {
return table
.addColumn(POINT_KEY, 'string', 'string', DISK_COL)
.addColumn('__symbol', 'string', 'string', SYMBOL_COL)
.addColumn(HOST_KEY, 'string', 'string', HOST_COL)
}
return table.addColumn(COLUMN_KEY, 'string', 'string', CPU_COL)
}
77 changes: 77 additions & 0 deletions giraffe/src/utils/legend/sort.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {getDataSortOrder} from './sort'
import {getRandomTable} from '../fixtures/randomTable'
import {lineTransform} from '../../transforms/line'
import {NINETEEN_EIGHTY_FOUR} from '../../constants/colorSchemes'
import {DomainLabel} from '../../types'

describe('getDataSortOrder', () => {
const xColKey = '_time'
const yColKey = '_value'
const maxValue = 100
const numberOfRecords = 200
const recordsPerLine = 10
const fillColKeys = ['cpu', 'host', 'machine']
const table = getRandomTable(
maxValue,
true,
numberOfRecords,
recordsPerLine,
fillColKeys
)

it('leaves overlaid line graphs unsorted', () => {
const lineOption = 'overlaid'
const lineSpec = lineTransform(
table,
xColKey,
yColKey,
fillColKeys,
NINETEEN_EIGHTY_FOUR,
lineOption
)

const latestIndices = Object.values(lineSpec.columnGroupMaps.latestIndices)
const sortOrder = getDataSortOrder(
lineSpec.lineData,
latestIndices,
lineOption,
DomainLabel.Y
)
expect(latestIndices).toEqual(sortOrder)
})

it('sorts stacked line graphs in descending order', () => {
const lineOption = 'stacked'
const lineSpec = lineTransform(
table,
xColKey,
yColKey,
fillColKeys,
NINETEEN_EIGHTY_FOUR,
lineOption
)
const {stackedDomainValueColumn} = lineSpec

const latestIndices = Object.values(lineSpec.columnGroupMaps.latestIndices)
const sortOrder = getDataSortOrder(
lineSpec.lineData,
latestIndices,
lineOption,
DomainLabel.Y
)
expect(sortOrder.length).toBeGreaterThanOrEqual(2)
sortOrder.forEach((columnIndex, index) => {
if (index < sortOrder.length - 1) {
expect(
stackedDomainValueColumn[columnIndex] >
stackedDomainValueColumn[columnIndex + 1]
)
} else {
expect(
stackedDomainValueColumn[columnIndex] <
stackedDomainValueColumn[columnIndex + 1]
)
}
})
})
})
31 changes: 31 additions & 0 deletions giraffe/src/utils/legend/sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {DomainLabel, LineData, LinePosition} from '../../types'

export const getDataSortOrder = (
lineData: LineData,
rowIndices: number[],
position: LinePosition,
domainLabel?: DomainLabel
): number[] => {
if (!position || position === 'overlaid') {
return rowIndices
}

const domainLabelName = domainLabel || DomainLabel.Y

const numberMap = {}
const numericalValues = Object.keys(lineData).reduce(
(combinedNumericalValues, id) =>
combinedNumericalValues.concat(lineData[id][domainLabelName]),
[]
)
const sortable = []
rowIndices.forEach(rowIndex => {
if (!numberMap[numericalValues[rowIndex]]) {
numberMap[numericalValues[rowIndex]] = []
}
numberMap[numericalValues[rowIndex]].push(rowIndex)
sortable.push(numericalValues[rowIndex])
})
sortable.sort((first, second) => second - first)
return sortable.map(numericalValue => numberMap[numericalValue].shift())
}
Loading

0 comments on commit f2704fb

Please sign in to comment.