diff --git a/CHANGELOG.md b/CHANGELOG.md index 010ad2e9cd2..90645dca4a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## UNRELEASED + +### Features + +1. [14059](https://github.com/influxdata/influxdb/pull/14059): Enable formatting line graph y ticks with binary prefix + +### Bug Fixes + +### UI Improvements + ## v2.0.0-alpha.11 [2019-05-31] 1. [14031](https://github.com/influxdata/influxdb/pull/14031): Correctly check if columnKeys include xColumn in heatmap diff --git a/ui/package-lock.json b/ui/package-lock.json index f426f3c3aaa..1399f3c5b12 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1033,7 +1033,7 @@ "version": "github:influxdata/influxdb2-js#4bb7981498a2649391fbebdcaababafbf304f642", "from": "github:influxdata/influxdb2-js#dev", "requires": { - "axios": "^0.18.0" + "axios": "^0.19.0" } }, "@influxdata/influxdb-templates": { @@ -5894,8 +5894,7 @@ "version": "2.1.1", "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5919,15 +5918,13 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5944,22 +5941,19 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -6090,8 +6084,7 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6105,7 +6098,6 @@ "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6122,7 +6114,6 @@ "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6131,15 +6122,13 @@ "version": "0.0.8", "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.4.tgz", "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -6160,7 +6149,6 @@ "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6249,8 +6237,7 @@ "version": "1.0.1", "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6264,7 +6251,6 @@ "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6360,8 +6346,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6403,7 +6388,6 @@ "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6425,7 +6409,6 @@ "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6474,15 +6457,13 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", - "dev": true, - "optional": true + "dev": true } } }, diff --git a/ui/src/dashboards/constants/cellEditor.ts b/ui/src/dashboards/constants/cellEditor.ts index bea02f42979..a29f699612a 100644 --- a/ui/src/dashboards/constants/cellEditor.ts +++ b/ui/src/dashboards/constants/cellEditor.ts @@ -1,8 +1,8 @@ -import {Base, Scale} from 'src/types' +import {Scale} from 'src/types' export const AXES_SCALE_OPTIONS = { LINEAR: Scale.Linear, LOG: Scale.Log, - BASE_2: Base.Two, - BASE_10: Base.Ten, + BASE_2: '2', + BASE_10: '10', } diff --git a/ui/src/dashboards/resources.ts b/ui/src/dashboards/resources.ts index 65ec52e53a4..1e70e23fbe5 100644 --- a/ui/src/dashboards/resources.ts +++ b/ui/src/dashboards/resources.ts @@ -13,7 +13,6 @@ import { TimeRange, QueryConfig, Scale, - Base, } from 'src/types' export const dashboard: Dashboard = { @@ -133,7 +132,7 @@ export const axes: Axes = { label: '', prefix: '', suffix: '', - base: Base.Ten, + base: '10', scale: Scale.Linear, }, y: { @@ -141,7 +140,7 @@ export const axes: Axes = { label: '', prefix: '', suffix: '', - base: Base.Ten, + base: '10', scale: Scale.Linear, }, } diff --git a/ui/src/shared/components/XYContainer.tsx b/ui/src/shared/components/XYContainer.tsx index 07a3b60a415..958b01e4810 100644 --- a/ui/src/shared/components/XYContainer.tsx +++ b/ui/src/shared/components/XYContainer.tsx @@ -46,6 +46,7 @@ const XYContainer: FunctionComponent = ({ prefix: yTickPrefix, suffix: yTickSuffix, bounds: yBounds, + base: yTickBase, }, }, }, @@ -94,7 +95,8 @@ const XYContainer: FunctionComponent = ({ const yFormatter = getFormatter( table.getColumnType(yColumn), yTickPrefix, - yTickSuffix + yTickSuffix, + yTickBase ) const config: Config = { diff --git a/ui/src/shared/utils/formatNumber.test.ts b/ui/src/shared/utils/formatNumber.test.ts new file mode 100644 index 00000000000..f30ea10809c --- /dev/null +++ b/ui/src/shared/utils/formatNumber.test.ts @@ -0,0 +1,28 @@ +import {formatNumber} from 'src/shared/utils/formatNumber' + +describe('formatNumber', () => { + test('can format numbers with no unit prefixes', () => { + expect(formatNumber(123456789.123456789)).toEqual('123456789.1235') + }) + + test('can format numbers with SI unit prefixes', () => { + expect(formatNumber(123456, '10')).toEqual('123.5k') + expect(formatNumber(123456789.123456789, '10')).toEqual('123.5M') + expect(formatNumber(12345678912345.123456789, '10')).toEqual('12.35T') + }) + + test('can format numbers with binary unit prefixes', () => { + expect(formatNumber(2 ** 10, '2')).toEqual('1K') + expect(formatNumber(2 ** 20, '2')).toEqual('1M') + expect(formatNumber(2 ** 30, '2')).toEqual('1G') + }) + + test('can format negative numbers with a binary unit prefix', () => { + expect(formatNumber(0 - 2 ** 30, '2')).toEqual('-1G') + }) + + test('formats small numbers without unit prefixes', () => { + expect(formatNumber(0.551249, '2')).toEqual('0.5512') + expect(formatNumber(0.551249, '10')).toEqual('0.5512') + }) +}) diff --git a/ui/src/shared/utils/formatNumber.ts b/ui/src/shared/utils/formatNumber.ts new file mode 100644 index 00000000000..1c7d78a1e4d --- /dev/null +++ b/ui/src/shared/utils/formatNumber.ts @@ -0,0 +1,33 @@ +// Libraries +import {format} from 'd3-format' + +// Types +import {Base} from 'src/types' + +const MAX_DECIMALS = 4 // We should eventually make this configurable + +const formatRaw = format(`.${MAX_DECIMALS}~f`) // e.g. "0.032" + +const formatSIPrefix = format(`.${MAX_DECIMALS}~s`) // e.g. "2.452M" + +const BINARY_PREFIXES = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] + +const formatBinaryPrefix = (t: number): string => { + const i = Math.floor(Math.log(Math.abs(t)) / Math.log(2 ** 10)) + + return `${formatRaw(t / 1024 ** i)}${BINARY_PREFIXES[i]}` +} + +export const formatNumber = (t: number, base: Base = ''): string => { + const isSmallNumber = t >= -1 && t <= 1 + + if (base === '2' && !isSmallNumber) { + return formatBinaryPrefix(t) + } + + if (base === '10' && !isSmallNumber) { + return formatSIPrefix(t) + } + + return formatRaw(t) +} diff --git a/ui/src/shared/utils/mocks/resourceToTemplate.ts b/ui/src/shared/utils/mocks/resourceToTemplate.ts index 8e56f2124d8..c17dbdbbbe4 100644 --- a/ui/src/shared/utils/mocks/resourceToTemplate.ts +++ b/ui/src/shared/utils/mocks/resourceToTemplate.ts @@ -7,7 +7,6 @@ import { XYViewGeom, Scale, QueryEditMode, - Base, Label, TaskStatus, } from 'src/types' @@ -64,7 +63,7 @@ export const myView: View = { label: '', prefix: '', suffix: '', - base: Base.Ten, + base: '10', scale: Scale.Linear, }, y: { @@ -72,7 +71,7 @@ export const myView: View = { label: '', prefix: '', suffix: '', - base: Base.Ten, + base: '10', scale: Scale.Linear, }, }, diff --git a/ui/src/shared/utils/view.ts b/ui/src/shared/utils/view.ts index ecd1f697e2b..5344a809d31 100644 --- a/ui/src/shared/utils/view.ts +++ b/ui/src/shared/utils/view.ts @@ -64,7 +64,7 @@ function defaultLineViewProperties() { label: '', prefix: '', suffix: '', - base: Base.Ten, + base: '10' as Base, scale: Scale.Linear, }, y: { @@ -72,7 +72,7 @@ function defaultLineViewProperties() { label: '', prefix: '', suffix: '', - base: Base.Ten, + base: '10' as Base, scale: Scale.Linear, }, }, diff --git a/ui/src/shared/utils/vis.ts b/ui/src/shared/utils/vis.ts index a9c81d388d9..b36d60d0b8b 100644 --- a/ui/src/shared/utils/vis.ts +++ b/ui/src/shared/utils/vis.ts @@ -1,23 +1,11 @@ // Libraries -import {format} from 'd3-format' import {Table, ColumnType, LineInterpolation} from '@influxdata/vis' -// Types -import {XYViewGeom, Axis} from 'src/types' - -const MAX_DECIMALS = 3 - -const formatSmallNumber = format(`.${MAX_DECIMALS}~f`) // e.g. "0.032" - -const formatLargeNumber = format(`.${MAX_DECIMALS}~s`) // e.g. "2.452M" +// Utils +import {formatNumber} from 'src/shared/utils/formatNumber' -export const formatNumber = (t: number): string => { - if (t >= -1 && t <= 1) { - return formatSmallNumber(t) - } - - return formatLargeNumber(t) -} +// Types +import {XYViewGeom, Axis, Base} from 'src/types' /* A geom may be stored as "line", "step", "monotoneX", "bar", or "stacked", but @@ -47,10 +35,11 @@ export const geomToInterpolation = (geom: XYViewGeom): LineInterpolation => { export const getFormatter = ( columnType: ColumnType, prefix: string = '', - suffix: string = '' + suffix: string = '', + base: Base = '' ): null | ((x: any) => string) => { return columnType === 'number' - ? x => `${prefix}${formatNumber(x)}${suffix}` + ? x => `${prefix}${formatNumber(x, base)}${suffix}` : null } diff --git a/ui/src/timeMachine/components/view_options/LineOptions.tsx b/ui/src/timeMachine/components/view_options/LineOptions.tsx index eef4284ee64..ff8f8ea6b48 100644 --- a/ui/src/timeMachine/components/view_options/LineOptions.tsx +++ b/ui/src/timeMachine/components/view_options/LineOptions.tsx @@ -9,6 +9,7 @@ import YAxisTitle from 'src/timeMachine/components/view_options/YAxisTitle' import AxisAffixes from 'src/timeMachine/components/view_options/AxisAffixes' import ColorSelector from 'src/timeMachine/components/view_options/ColorSelector' import AutoDomainInput from 'src/shared/components/AutoDomainInput' +import YAxisBase from 'src/timeMachine/components/view_options/YAxisBase' // Actions import { @@ -17,6 +18,7 @@ import { setAxisPrefix, setAxisSuffix, setYAxisBounds, + setYAxisBase, setGeom, } from 'src/timeMachine/actions' @@ -40,6 +42,7 @@ interface DispatchProps { onUpdateAxisPrefix: typeof setAxisPrefix onUpdateAxisSuffix: typeof setAxisSuffix onUpdateYAxisBounds: typeof setYAxisBounds + onUpdateYAxisBase: typeof setYAxisBase onUpdateColors: typeof setColors onSetGeom: typeof setGeom } @@ -50,7 +53,7 @@ class LineOptions extends PureComponent { public render() { const { axes: { - y: {label, prefix, suffix}, + y: {label, prefix, suffix, base}, }, colors, geom, @@ -58,6 +61,7 @@ class LineOptions extends PureComponent { onUpdateYAxisLabel, onUpdateAxisPrefix, onUpdateAxisSuffix, + onUpdateYAxisBase, onSetGeom, } = this.props @@ -76,6 +80,7 @@ class LineOptions extends PureComponent {
Y Axis
+ { return ( - + - Raw + None - K/M/B + SI - K/M/G + Binary diff --git a/ui/src/types/dashboards.ts b/ui/src/types/dashboards.ts index 6374672995d..c4904272dbb 100644 --- a/ui/src/types/dashboards.ts +++ b/ui/src/types/dashboards.ts @@ -12,10 +12,10 @@ export enum Scale { Log = 'log', } -export enum Base { - Two = '2', - Ten = '10', -} +export type Base = + | '' // Do not format using a prefix + | '2' // Format using a binary prefix + | '10' // Format using a decimal/SI prefix export interface Axis { label: string