From 53a6b9a5009f4d1879dafa1dab886726462fc1e5 Mon Sep 17 00:00:00 2001 From: Christopher Henn Date: Mon, 24 Jun 2019 11:54:06 -0700 Subject: [PATCH] fix(ui): enable selecting more columns in line visualizations Closes #14153 --- ui/src/shared/components/ScatterContainer.tsx | 10 +- ui/src/shared/components/XYContainer.tsx | 8 +- ui/src/shared/utils/vis.ts | 96 +++++++++++++++---- ui/src/timeMachine/selectors/index.ts | 83 ++++------------ 4 files changed, 108 insertions(+), 89 deletions(-) diff --git a/ui/src/shared/components/ScatterContainer.tsx b/ui/src/shared/components/ScatterContainer.tsx index e3fa64c460c..e5bfbe34932 100644 --- a/ui/src/shared/components/ScatterContainer.tsx +++ b/ui/src/shared/components/ScatterContainer.tsx @@ -8,7 +8,11 @@ import GraphLoadingDots from 'src/shared/components/GraphLoadingDots' // Utils import {useVisDomainSettings} from 'src/shared/utils/useVisDomainSettings' -import {getFormatter, chooseXColumn, chooseYColumn} from 'src/shared/utils/vis' +import { + getFormatter, + defaultXColumn, + defaultYColumn, +} from 'src/shared/utils/vis' // Constants import {VIS_THEME} from 'src/shared/constants' @@ -47,8 +51,8 @@ const ScatterContainer: FunctionComponent = ({ const fillColumns = storedFill || [] const symbolColumns = storedSymbol || [] - const xColumn = storedXColumn || chooseXColumn(table) - const yColumn = storedYColumn || chooseYColumn(table) + const xColumn = storedXColumn || defaultXColumn(table) + const yColumn = storedYColumn || defaultYColumn(table) const columnKeys = table.columnKeys diff --git a/ui/src/shared/components/XYContainer.tsx b/ui/src/shared/components/XYContainer.tsx index 9c44c822f9a..161fab81968 100644 --- a/ui/src/shared/components/XYContainer.tsx +++ b/ui/src/shared/components/XYContainer.tsx @@ -13,8 +13,8 @@ import { geomToInterpolation, filterNoisyColumns, parseBounds, - chooseXColumn, - chooseYColumn, + defaultXColumn, + defaultYColumn, } from 'src/shared/utils/vis' // Constants @@ -59,8 +59,8 @@ const XYContainer: FunctionComponent = ({ const storedXDomain = useMemo(() => parseBounds(xBounds), [xBounds]) const storedYDomain = useMemo(() => parseBounds(yBounds), [yBounds]) - const xColumn = storedXColumn || chooseXColumn(table) - const yColumn = storedYColumn || chooseYColumn(table) + const xColumn = storedXColumn || defaultXColumn(table) + const yColumn = storedYColumn || defaultYColumn(table) const columnKeys = table.columnKeys diff --git a/ui/src/shared/utils/vis.ts b/ui/src/shared/utils/vis.ts index aa0f9780f80..e1ee7c87305 100644 --- a/ui/src/shared/utils/vis.ts +++ b/ui/src/shared/utils/vis.ts @@ -105,32 +105,94 @@ export const extent = (xs: number[]): [number, number] | null => { return [low, high] } -export const chooseXColumn = (table: Table): string | null => { - const columnKeys = new Set(table.columnKeys) +export const checkResultsLength = (giraffeResult: FromFluxResult): boolean => { + return get(giraffeResult, 'table.length', 0) > 0 +} + +export const getNumericColumns = (table: Table): string[] => { + const numericColumnKeys = table.columnKeys.filter(k => { + if (k === 'result' || k === 'table') { + return false + } + + const columnType = table.getColumnType(k) + + return columnType === 'time' || columnType === 'number' + }) - if (columnKeys.has('_time')) { - return '_time' + return numericColumnKeys +} + +export const getGroupableColumns = (table: Table): string[] => { + const invalidGroupColumns = new Set(['_value', '_time', 'table']) + const groupableColumns = table.columnKeys.filter( + name => !invalidGroupColumns.has(name) + ) + + return groupableColumns +} + +/* + Previously we would automatically select an x and y column setting for an + `XYView` based on the current Flux response. We then added support for an + explicit x and y column setting by adding `xColumn` and `yColumn` fields to + the `XYView`. + + We did not migrate existing views when adding the fields, so the fields are + considered optional. Thus to resolve the correct x and y column selections + for an `XYView`, we need to: + + 1. Use the `xColumn` and `yColumn` fields if they exist + 2. Fall back to automatically selecting and x and y column if not + + A `null` result from this function indicates that no valid selection could be + made. +*/ +export const defaultXColumn = ( + table: Table, + preferredColumnKey?: string +): string | null => { + const validColumnKeys = getNumericColumns(table) + + if (validColumnKeys.includes(preferredColumnKey)) { + return preferredColumnKey } - if (columnKeys.has('_stop')) { - return '_stop' + for (const key of ['_time', '_stop', '_start']) { + if (validColumnKeys.includes(key)) { + return key + } } - if (columnKeys.has('_start')) { - return '_start' + if (validColumnKeys.length) { + return validColumnKeys[0] } return null } -export const chooseYColumn = (table: Table): string | null => { - return table.columnKeys.find( - k => - k.startsWith('_value') && - (table.getColumnType(k) === 'number' || table.getColumnType(k) === 'time') - ) -} +/* + See `defaultXColumn`. +*/ +export const defaultYColumn = ( + table: Table, + preferredColumnKey?: string +): string | null => { + const validColumnKeys = getNumericColumns(table) + + if (validColumnKeys.includes(preferredColumnKey)) { + return preferredColumnKey + } -export const checkResultsLength = (giraffeResult: FromFluxResult): boolean => { - return get(giraffeResult, 'table.length', 0) > 0 + for (const key of validColumnKeys) { + if (key.startsWith('_value')) { + return key + } + } + + if (validColumnKeys.length) { + return validColumnKeys[0] + } + + return null } diff --git a/ui/src/timeMachine/selectors/index.ts b/ui/src/timeMachine/selectors/index.ts index 7b4855f8c1e..5918f1725f0 100644 --- a/ui/src/timeMachine/selectors/index.ts +++ b/ui/src/timeMachine/selectors/index.ts @@ -5,7 +5,12 @@ import {fromFlux, Table} from '@influxdata/giraffe' // Utils import {parseResponse} from 'src/shared/parsing/flux/response' -import {chooseYColumn, chooseXColumn} from 'src/shared/utils/vis' +import { + defaultXColumn, + defaultYColumn, + getNumericColumns as getNumericColumnsUtil, + getGroupableColumns as getGroupableColumnsUtil, +} from 'src/shared/utils/vis' // Types import { @@ -47,23 +52,7 @@ export const getVisTable = ( return {table, fluxGroupKeyUnion} } -const getNumericColumnsMemoized = memoizeOne( - (table: Table): string[] => { - const columnKeys = table.columnKeys - return columnKeys.reduce((numericColumns, key) => { - const columnType = table.getColumnType(key) - const columnName = table.getColumnName(key) - if ( - (columnType === 'number' || columnType === 'time') && - columnName !== 'result' && - columnName !== 'table' - ) { - numericColumns.push(columnName) - } - return numericColumns - }, []) - } -) +const getNumericColumnsMemoized = memoizeOne(getNumericColumnsUtil) export const getNumericColumns = (state: AppState): string[] => { const {table} = getVisTable(state) @@ -71,16 +60,7 @@ export const getNumericColumns = (state: AppState): string[] => { return getNumericColumnsMemoized(table) } -const getGroupableColumnsMemoized = memoizeOne( - (table: Table): string[] => { - const invalidGroupColumns = new Set(['_value', '_time', 'table']) - const groupableColumns = table.columnKeys.filter( - name => !invalidGroupColumns.has(name) - ) - - return groupableColumns - } -) +const getGroupableColumnsMemoized = memoizeOne(getGroupableColumnsUtil) export const getGroupableColumns = (state: AppState): string[] => { const {table} = getVisTable(state) @@ -88,51 +68,24 @@ export const getGroupableColumns = (state: AppState): string[] => { return getGroupableColumnsMemoized(table) } -const selectXYColumn = ( - validColumns: string[], - preference: string, - defaultSelection: string -): string => { - if (preference && validColumns.includes(preference)) { - return preference - } - - return defaultSelection -} - -const getXColumnSelectionMemoized = memoizeOne(selectXYColumn) - -const getYColumnSelectionMemoized = memoizeOne(selectXYColumn) - export const getXColumnSelection = (state: AppState): string => { - const validXColumns = getNumericColumns(state) - - const preference = get(getActiveTimeMachine(state), 'view.properties.xColumn') - const {table} = getVisTable(state) - const defaultSelection = chooseXColumn(table) - - return getXColumnSelectionMemoized( - validXColumns, - preference, - defaultSelection + const preferredXColumnKey = get( + getActiveTimeMachine(state), + 'view.properties.xColumn' ) + + return defaultXColumn(table, preferredXColumnKey) } export const getYColumnSelection = (state: AppState): string => { - const validYColumns = getNumericColumns(state) - - const preference = get(getActiveTimeMachine(state), 'view.properties.yColumn') - const {table} = getVisTable(state) - - const defaultSelection = chooseYColumn(table) - - return getYColumnSelectionMemoized( - validYColumns, - preference, - defaultSelection + const preferredYColumnKey = get( + getActiveTimeMachine(state), + 'view.properties.yColumn' ) + + return defaultYColumn(table, preferredYColumnKey) } const getGroupableColumnSelection = (