From 7a1e0f42a5b1f6aee81a77eda7be7454b60b3bd5 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Mon, 25 Jan 2021 12:02:10 +0100 Subject: [PATCH] fix: removed bar grouping feature --- giraffe/README.md | 18 ++- giraffe/src/components/GaugeMini.tsx | 43 ++------ giraffe/src/components/GaugeMiniLayer.tsx | 4 +- .../LatestMultipleValueTransform.tsx | 104 ++++++------------ giraffe/src/components/SizedTable.tsx | 5 - giraffe/src/constants/gaugeMiniStyles.ts | 4 +- giraffe/src/types/index.ts | 18 +-- giraffe/src/utils/gaugeMiniThemeNormalize.ts | 49 +-------- stories/src/gaugeMini.stories.tsx | 8 +- 9 files changed, 62 insertions(+), 191 deletions(-) diff --git a/giraffe/README.md b/giraffe/README.md index 60185deb..9bff8d4a 100644 --- a/giraffe/README.md +++ b/giraffe/README.md @@ -669,15 +669,9 @@ TableGraphLayerConfig uses the `fluxResponse` property from `config` as the data All values (excluding **type**) are Optional and their defaults is defined by theme `GAUGE_MINI_THEME_BULLET_DARK` - - **type**: _'gauge mini'. **Required**._ Specifies that this LayerConfig is a gauge mini layer. + gauge mini creates one bar per unique __field_ value - - **barsDefinitions** _{groupByColumns; bars;}_ _(types of properties based on selected style)_ - - Object style: - - **groupByColumns** _{ [key: string]: true }_ bar for each unique combination of given columns values. _(example: `{cpu: true, _field: true}`)_ - - **bars** _{ barDef: { [key in keyof T]: string }, label?: string }[]_ where _barDef_ contains values for specific bar columns and label for this bar. - - _or_ Array style: - - **groupByColumns** _string[]_ bar for each unique combination of given columns values. _(example: `['cpu', '_field', ]`)_ - - **bars** _{ barDef: string[], label?: string }[]_ where _barDef_ contains values for specific bar columns and label for this bar. Each barDef value belongs to key grom groupByColumns with same index. + - **type**: _'gauge mini'. **Required**._ Specifies that this LayerConfig is a gauge mini layer. - **mode** _'progress' | 'bullet'._ - `'bullet'` backgroud bar is colored and value bar has always secondary color @@ -729,6 +723,8 @@ TableGraphLayerConfig uses the `fluxResponse` property from `config` as the data - **labelMainFontColor** _string_ Main label color. Bar labels + - **labelBarsEnabled** _boolean_ Bar labels shown if true + - **labelBarsFontSize** _number_ Bar labels font size - **labelBarsFontColor** _string_ Bar labels font color @@ -764,7 +760,7 @@ TableGraphLayerConfig uses the `fluxResponse` property from `config` as the data - **isEnforced**: _boolean. Optional. Defaults to false when not included._ Indicates whether the number of decimal places ("**digits**") will be enforced. When **isEnforced** is falsy or omitted, **digits** will be locked to 2 for stat values with a decimal and 0 for stat values that are integers, and the **digits** option will be ignored. - **digits**: _number. Optional. Defaults to 0 when not included. Maximum 10._ When **digits** is a non-integer number, the decimal portion is ignored. Represents the number of decimal places to display in the stat value. Displayed stat value is subject to rounding. - - example ```valueFormater: (num: number) => `${num.toFixed(0)}%` ``` for _value=23.213_ will show text value _23%_. + - example ```valueFormater: (num: number) => `${((num || 0) * 100).toFixed(0)}%` ``` for _value=0.23213_ will show text value _23%_. **Precreated themes** - `GAUGE_MINI_THEME_BULLET_DARK` @@ -773,7 +769,6 @@ TableGraphLayerConfig uses the `fluxResponse` property from `config` as the data type: 'gauge mini', mode: 'bullet', textMode: 'follow', - barsDefinitions: {groupByColumns: ["_field"]}, valueHeight: 18, gaugeHeight: 25, @@ -795,6 +790,7 @@ TableGraphLayerConfig uses the `fluxResponse` property from `config` as the data labelMainFontSize: 13, labelMainFontColor: InfluxColors.Ghost, + labelBarsEnabled: false, labelBarsFontSize: 11, labelBarsFontColor: InfluxColors.Forge, @@ -816,7 +812,6 @@ TableGraphLayerConfig uses the `fluxResponse` property from `config` as the data type: 'gauge mini', mode: 'progress', textMode: 'follow', - barsDefinitions: {groupByColumns: ['_field']}, valueHeight: 20, gaugeHeight: 20, @@ -836,6 +831,7 @@ TableGraphLayerConfig uses the `fluxResponse` property from `config` as the data labelMainFontSize: 13, labelMainFontColor: InfluxColors.Ghost, + labelBarsEnabled: false, labelBarsFontSize: 11, labelBarsFontColor: InfluxColors.Forge, diff --git a/giraffe/src/components/GaugeMini.tsx b/giraffe/src/components/GaugeMini.tsx index f9d43db6..843d9cc5 100644 --- a/giraffe/src/components/GaugeMini.tsx +++ b/giraffe/src/components/GaugeMini.tsx @@ -5,6 +5,8 @@ import {scaleLinear} from 'd3-scale' // Types import {GaugeMiniColors, GaugeMiniLayerConfig} from '../types' +import {GroupedData} from './LatestMultipleValueTransform' + import { gaugeMiniNormalizeThemeMemoized, GaugeMiniThemeNormalized, @@ -13,28 +15,10 @@ import { interface Props { width: number height: number - values: {colsMString: string; value: number}[] + values: GroupedData theme: Required } -// todo: move into gauge utils -/** create merged string for given column string values. String is same for all columns with same values and unique for different ones */ -export const createColsMString = ( - groupedBy: T, - col: {[key in keyof T]: string} -): string => { - const columns = Object.keys(groupedBy) - .filter(x => groupedBy[x]) - .sort() - const columnValues = columns.map(x => col[x]) - /** - * replacing - with -- will ensures that rows - * { a: '0-1', b: '2' } and { a: '0', b: '1-2' } - * will not have same string (0-1-2 instead they will be 0--1-2 and 0-1--2) - */ - return columnValues.map(x => x.split('-').join('--')).join('-') -} - const barCssClass = 'gauge-mini-bar' //#region svg helpers @@ -473,6 +457,7 @@ export const GaugeMini: FunctionComponent = ({ labelMain, labelMainFontSize, labelMainFontColor, + labelBarsEnabled, labelBarsFontColor, labelBarsFontSize, colors, @@ -485,21 +470,11 @@ export const GaugeMini: FunctionComponent = ({ const barLabelWidth = Math.max(...barLabelsWidth) || 0 const barWidth = width - sidePaddings * 2 - barLabelWidth const maxBarHeight = Math.max(gaugeHeight, valueHeight) - const allBarsHeight = values.length * (maxBarHeight + barPaddings) - - const barsDefinitions = theme.barsDefinitions + const allBarsHeight = + Object.keys(values).length * (maxBarHeight + barPaddings) // create unified barsDefinition - const labelMapping: any = {} - barsDefinitions?.bars?.forEach(x => { - if (!x.label) { - return - } - const mstring = createColsMString(barsDefinitions.groupByColumns, x.barDef) - labelMapping[mstring] = x.label - }) - const [autocenterToken, setAutocenterToken] = useState(Date.now()) useEffect(() => { setAutocenterToken(Date.now()) @@ -507,7 +482,6 @@ export const GaugeMini: FunctionComponent = ({ width, height, barLabelWidth, - barsDefinitions, valueHeight, gaugeHeight, barPaddings, @@ -536,11 +510,10 @@ export const GaugeMini: FunctionComponent = ({ {labelMain} )} - {values.map(({colsMString, value}, i) => { + {Object.entries(values).map(([group, value], i) => { const y = 0 + i * (maxBarHeight + barPaddings) - const label = labelMapping?.[colsMString] - const textCenter = y + maxBarHeight / 2 + const label = labelBarsEnabled ? group : '' // todo: no rerender ? const onRectChanged = (r: DOMRect) => { diff --git a/giraffe/src/components/GaugeMiniLayer.tsx b/giraffe/src/components/GaugeMiniLayer.tsx index a6eb7bc7..d88f9d47 100644 --- a/giraffe/src/components/GaugeMiniLayer.tsx +++ b/giraffe/src/components/GaugeMiniLayer.tsx @@ -7,12 +7,14 @@ import {GaugeMiniLayerConfig} from '../types' import {GaugeMini} from './GaugeMini' import {GAUGE_MINI_THEME_BULLET_DARK} from '../constants/gaugeMiniStyles' +import {GroupedData} from './LatestMultipleValueTransform' interface Props { - values: {colsMString: string; value: number}[] + values: GroupedData theme: GaugeMiniLayerConfig } +// todo: move gauge mini here export const GaugeMiniLayer: FunctionComponent = (props: Props) => { const {theme, values} = props const themeOrDefault: Required = { diff --git a/giraffe/src/components/LatestMultipleValueTransform.tsx b/giraffe/src/components/LatestMultipleValueTransform.tsx index fc36890d..a5ce197a 100644 --- a/giraffe/src/components/LatestMultipleValueTransform.tsx +++ b/giraffe/src/components/LatestMultipleValueTransform.tsx @@ -1,34 +1,10 @@ // Libraries import React, {useMemo, FunctionComponent} from 'react' import {Table} from '../types' -import {createColsMString} from './GaugeMini' -interface SelectedColumns { - [key: string]: true -} - -export const getLatestValuesGrouped = ( - table: Table, - columnsObj: SelectedColumns -) => { - const columns = Object.keys(columnsObj).sort() - - columns.forEach(x => { - if (table.getColumnType(x) !== 'string') { - throw new Error( - `Data can be grouped only by string columns. But column ${x} is typeof ${table.getColumnType( - x - )}` - ) - } - }) - - const valueColumn = table.getColumn('_value', 'number') as number[] - - if (!valueColumn.length) { - return [] - } +export type GroupedData = {[key: string]: number} +const getTimeColumn = (table: Table) => { // Fallback to `_stop` column if `_time` column missing otherwise return empty array. let timeColData: number[] = [] @@ -41,73 +17,61 @@ export const getLatestValuesGrouped = ( return [] } - const groupColsData = columns.map(k => - table.getColumn(k, 'string') - ) as string[][] + return timeColData +} - const result: {[key: string]: number} = {} +export const getLatestValuesGrouped = ( + table: Table, + groupColumnKey: string +): GroupedData => { + const valueCol = table.getColumn('_value', 'number') + const groupCol = table.getColumn(groupColumnKey) - timeColData - // merge time with it's index - .map((time, index) => ({time, index})) - // remove entries without time - .filter(({time}) => time) - // todo: sort time complexity too high ... replace with another solution - // from low time to high time (last is last) - .sort(({time: t1}, {time: t2}) => t1 - t2) - // get relevant data from index (we don't need time anymore) - .map(({index}) => ({ - value: valueColumn[index], - groupRow: groupColsData.map(x => x[index]), - })) - // remove invalid data - .filter(({value}) => Number.isFinite(value) && typeof value === 'number') - // create result - .forEach(({value, groupRow}) => { - const grupObj = {} - groupRow.forEach((x, i) => (grupObj[columns[i]] = x)) - const strKey = createColsMString(columnsObj, grupObj) - // data is inserted from last to first so latest data is first - result[strKey] = value - }) + if (!valueCol.length) { + return {} + } - return result + return Object.fromEntries( + getTimeColumn(table) + // merge time with it's index + .map((time, index) => ({time, index})) + // remove entries without time + .filter(({time}) => time) + // todo: sort time complexity too high ... replace with linear solution + // from low time to high time (last is last) + .sort(({time: t1}, {time: t2}) => t1 - t2) + // get relevant data from index (we don't need time anymore) + .map(({index}) => [groupCol?.[index] ?? '', valueCol[index]] as const) + // remove invalid data + .filter( + ([_, value]) => typeof value === 'number' && Number.isFinite(value) + ) + ) } interface Props { table: Table - columns: SelectedColumns - children: (latestValue: {colsMString: string; value: number}[]) => JSX.Element + children: (latestValue: GroupedData) => JSX.Element quiet?: boolean } // todo: can return string ? export const LatestMultipleValueTransform: FunctionComponent = ({ table, - columns, quiet = false, children, }) => { - const latestValues = useMemo(() => getLatestValuesGrouped(table, columns), [ + const latestValues = useMemo(() => getLatestValuesGrouped(table, '_field'), [ table, ]) - if (latestValues.length === 0 && quiet) { - return null - } - - if (latestValues.length === 0) { - return ( + if (Object.keys(latestValues).length === 0) { + return quiet ? null : (

No latest value found

) } - const entries = Object.keys(latestValues).map(x => ({ - colsMString: x, - value: latestValues[x], - })) - - return children(entries) + return children(latestValues) } diff --git a/giraffe/src/components/SizedTable.tsx b/giraffe/src/components/SizedTable.tsx index e422192a..7beddfd8 100644 --- a/giraffe/src/components/SizedTable.tsx +++ b/giraffe/src/components/SizedTable.tsx @@ -17,7 +17,6 @@ import {TableGraphLayer} from './TableGraphLayer' import {usePlotEnv} from '../utils/usePlotEnv' import {LatestMultipleValueTransform} from './LatestMultipleValueTransform' -import {getGaugeMiniBarsDefinitions} from '../utils/gaugeMiniThemeNormalize' interface Props { config: SizedConfig @@ -87,10 +86,6 @@ export const SizedTable: FunctionComponent = ({ {latestValues => ( = { type: 'gauge mini', mode: 'bullet', textMode: 'follow', - barsDefinitions: {groupByColumns: ['_field']}, valueHeight: 18, gaugeHeight: 25, @@ -27,6 +26,7 @@ export const GAUGE_MINI_THEME_BULLET_DARK: Required = { labelMainFontSize: 13, labelMainFontColor: InfluxColors.Ghost, + labelBarsEnabled: false, labelBarsFontSize: 11, labelBarsFontColor: InfluxColors.Forge, @@ -46,7 +46,6 @@ export const GAUGE_MINI_THEME_PROGRESS_DARK: Required = { type: 'gauge mini', mode: 'progress', textMode: 'follow', - barsDefinitions: {groupByColumns: ['_field']}, valueHeight: 20, gaugeHeight: 20, @@ -66,6 +65,7 @@ export const GAUGE_MINI_THEME_PROGRESS_DARK: Required = { labelMainFontSize: 13, labelMainFontColor: InfluxColors.Ghost, + labelBarsEnabled: false, labelBarsFontSize: 11, labelBarsFontColor: InfluxColors.Forge, diff --git a/giraffe/src/types/index.ts b/giraffe/src/types/index.ts index b8baaf6d..72c76d84 100644 --- a/giraffe/src/types/index.ts +++ b/giraffe/src/types/index.ts @@ -290,25 +290,8 @@ export type GaugeMiniColors = { thresholds?: ColorHexValue[] } -export interface GaugeMiniBarsDefinitions { - /** Defines which columns choose as unique bar indentificator. */ - groupByColumns: T - // todo: allow regex ? - /** Give label for given unique column values */ - bars?: {barDef: {[key in keyof T]: string}; label?: string}[] -} - -export interface GaugeMiniBarsDefinitionsArr { - groupByColumns: string[] - bars?: {barDef: string[]; label?: string}[] -} - export interface GaugeMiniLayerConfig { type: 'gauge mini' - /** Defines which columns choose as unique bar indentificator. Also bar labels can be defined here. */ - barsDefinitions: - | GaugeMiniBarsDefinitionsArr - | GaugeMiniBarsDefinitions<{[key: string]: true}> mode?: 'progress' | 'bullet' textMode?: 'follow' | 'left' @@ -327,6 +310,7 @@ export interface GaugeMiniLayerConfig { labelMainFontSize?: number labelMainFontColor?: string + labelBarsEnabled?: boolean labelBarsFontSize?: number labelBarsFontColor?: string diff --git a/giraffe/src/utils/gaugeMiniThemeNormalize.ts b/giraffe/src/utils/gaugeMiniThemeNormalize.ts index 5a1dd663..3b6572cb 100644 --- a/giraffe/src/utils/gaugeMiniThemeNormalize.ts +++ b/giraffe/src/utils/gaugeMiniThemeNormalize.ts @@ -1,22 +1,15 @@ +import {useMemo} from 'react' import {FormatStatValueOptions, formatStatValue} from './formatStatValue' -import { - GaugeMiniBarsDefinitions, - GaugeMiniColors, - GaugeMiniBarsDefinitionsArr, -} from '../types' +import {GaugeMiniColors} from '../types' import {GaugeMiniLayerConfig} from '..' import {color as d3Color} from 'd3-color' import {range} from 'd3-array' -import {useMemo} from 'react' export const throwReturn = (msg: string): T => { throw new Error(msg) } -type TBarsDefinitions = GaugeMiniBarsDefinitions<{[key: string]: true}> - type RestrictedTypesProperties = { - barsDefinitions: TBarsDefinitions colors?: GaugeMiniColors gaugeMiniColors?: GaugeMiniColors valueFormater?: (value: number) => string @@ -38,36 +31,6 @@ const getFormater = ( ? formater : (value: number) => formatStatValue(value, formater) -const isBarsDefinitionsArrayStyle = ( - barsDefinitions: GaugeMiniLayerConfig['barsDefinitions'] -): barsDefinitions is GaugeMiniBarsDefinitionsArr => { - return Array.isArray(barsDefinitions.groupByColumns) -} - -const getBarsDefinitions = ( - barsDefinitions: GaugeMiniLayerConfig['barsDefinitions'] -): TBarsDefinitions => { - if (!isBarsDefinitionsArrayStyle(barsDefinitions)) { - return barsDefinitions - } - - const {groupByColumns, bars} = barsDefinitions - - return { - groupByColumns: groupByColumns.reduce( - (obj, prop) => ((obj[prop] = true), obj), - {} as {[key: string]: true} - ), - bars: bars?.map(x => ({ - barDef: x.barDef.reduce( - (obj, prop, i) => ((obj[prop] = groupByColumns[i]), obj), - {} as Required['bars'][number]['barDef'] - ), - label: x.label, - })), - } -} - const getAxesSteps = ( axesSteps: number | 'thresholds' | undefined | number[], colors: GaugeMiniColors @@ -140,11 +103,6 @@ const getColors = ( export const gaugeMiniNormalizeThemeMemoized = ( theme: Required ): Required => { - const barsDefinitions = useMemo( - () => getBarsDefinitions(theme.barsDefinitions), - [theme.barsDefinitions] - ) - const colors = useMemo(() => getColors(theme.gaugeMiniColors), [ theme.gaugeMiniColors, ]) @@ -156,7 +114,6 @@ export const gaugeMiniNormalizeThemeMemoized = ( return { ...theme, - barsDefinitions, colors, gaugeMiniColors: colors, valueFormater: getFormater(theme.valueFormater), @@ -165,5 +122,3 @@ export const gaugeMiniNormalizeThemeMemoized = ( axesFormater: getFormater(theme.axesFormater), } } - -export const getGaugeMiniBarsDefinitions = getBarsDefinitions diff --git a/stories/src/gaugeMini.stories.tsx b/stories/src/gaugeMini.stories.tsx index f44d6b46..fd34be73 100644 --- a/stories/src/gaugeMini.stories.tsx +++ b/stories/src/gaugeMini.stories.tsx @@ -120,9 +120,6 @@ const editableLayer = ( knobGroups.basics ), numberOfBars: number('number of bars', 1, {}, knobGroups.basics), - barsDefinitions: { - groupByColumns: {_field: true}, - }, valueHeight: number('valueHeight', theme.valueHeight, {}, knobGroups.sizing), gaugeHeight: number('gaugeHeight', theme.gaugeHeight, {}, knobGroups.sizing), @@ -215,6 +212,11 @@ const editableLayer = ( knobGroups.labels ), + labelBarsEnabled: boolean( + 'labelBarsEnabled', + theme.labelBarsEnabled, + knobGroups.labels + ), labelBarsFontSize: number( 'labelBarsFontSize', theme.labelBarsFontSize,