This repository has been archived by the owner on Nov 3, 2023. It is now read-only.
forked from apache/superset
-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add SMART_NUMBER formatter and make it default (#109)
* feat: implement smart number format * test: add unit tests * refactor: Rename number formats BREAKING CHANGE: NumberFormat.xxx are renamed * feat: Make smart number default formatter * fix: add unit test * refactor: move formatters outside
- Loading branch information
1 parent
8fe9e2f
commit 0cde8a2
Showing
5 changed files
with
217 additions
and
23 deletions.
There are no files selected for viewing
49 changes: 27 additions & 22 deletions
49
...temporary_superset_ui/superset-ui/packages/superset-ui-number-format/src/NumberFormats.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,68 @@ | ||
const DOLLAR = '$,.2f'; | ||
const DOLLAR_CHANGE = '+$,.2f'; | ||
const DOLLAR_SIGNED = '+$,.2f'; | ||
const DOLLAR_ROUND = '$,d'; | ||
const DOLLAR_ROUND_CHANGE = '+$,d'; | ||
const DOLLAR_ROUND_SIGNED = '+$,d'; | ||
|
||
const FLOAT_1_POINT = ',.1f'; | ||
const FLOAT_2_POINT = ',.2f'; | ||
const FLOAT_3_POINT = ',.3f'; | ||
const FLOAT = FLOAT_2_POINT; | ||
|
||
const FLOAT_CHANGE_1_POINT = '+,.1f'; | ||
const FLOAT_CHANGE_2_POINT = '+,.2f'; | ||
const FLOAT_CHANGE_3_POINT = '+,.3f'; | ||
const FLOAT_CHANGE = FLOAT_CHANGE_2_POINT; | ||
const FLOAT_SIGNED_1_POINT = '+,.1f'; | ||
const FLOAT_SIGNED_2_POINT = '+,.2f'; | ||
const FLOAT_SIGNED_3_POINT = '+,.3f'; | ||
const FLOAT_SIGNED = FLOAT_SIGNED_2_POINT; | ||
|
||
const INTEGER = ',d'; | ||
const INTEGER_CHANGE = '+,d'; | ||
const INTEGER_SIGNED = '+,d'; | ||
|
||
const PERCENT_1_POINT = ',.1%'; | ||
const PERCENT_2_POINT = ',.2%'; | ||
const PERCENT_3_POINT = ',.3%'; | ||
const PERCENT = PERCENT_2_POINT; | ||
|
||
const PERCENT_CHANGE_1_POINT = '+,.1%'; | ||
const PERCENT_CHANGE_2_POINT = '+,.2%'; | ||
const PERCENT_CHANGE_3_POINT = '+,.3%'; | ||
const PERCENT_CHANGE = PERCENT_CHANGE_2_POINT; | ||
const PERCENT_SIGNED_1_POINT = '+,.1%'; | ||
const PERCENT_SIGNED_2_POINT = '+,.2%'; | ||
const PERCENT_SIGNED_3_POINT = '+,.3%'; | ||
const PERCENT_SIGNED = PERCENT_SIGNED_2_POINT; | ||
|
||
const SI_1_DIGIT = '.1s'; | ||
const SI_2_DIGIT = '.2s'; | ||
const SI_3_DIGIT = '.3s'; | ||
const SI = SI_3_DIGIT; | ||
|
||
const SMART_NUMBER = 'SMART_NUMBER'; | ||
const SMART_NUMBER_SIGNED = 'SMART_NUMBER_SIGNED'; | ||
|
||
const NumberFormats = { | ||
DOLLAR, | ||
DOLLAR_CHANGE, | ||
DOLLAR_ROUND, | ||
DOLLAR_ROUND_CHANGE, | ||
DOLLAR_ROUND_SIGNED, | ||
DOLLAR_SIGNED, | ||
FLOAT, | ||
FLOAT_1_POINT, | ||
FLOAT_2_POINT, | ||
FLOAT_3_POINT, | ||
FLOAT_CHANGE, | ||
FLOAT_CHANGE_1_POINT, | ||
FLOAT_CHANGE_2_POINT, | ||
FLOAT_CHANGE_3_POINT, | ||
FLOAT_SIGNED, | ||
FLOAT_SIGNED_1_POINT, | ||
FLOAT_SIGNED_2_POINT, | ||
FLOAT_SIGNED_3_POINT, | ||
INTEGER, | ||
INTEGER_CHANGE, | ||
INTEGER_SIGNED, | ||
PERCENT, | ||
PERCENT_1_POINT, | ||
PERCENT_2_POINT, | ||
PERCENT_3_POINT, | ||
PERCENT_CHANGE, | ||
PERCENT_CHANGE_1_POINT, | ||
PERCENT_CHANGE_2_POINT, | ||
PERCENT_CHANGE_3_POINT, | ||
PERCENT_SIGNED, | ||
PERCENT_SIGNED_1_POINT, | ||
PERCENT_SIGNED_2_POINT, | ||
PERCENT_SIGNED_3_POINT, | ||
SI, | ||
SI_1_DIGIT, | ||
SI_2_DIGIT, | ||
SI_3_DIGIT, | ||
SMART_NUMBER, | ||
SMART_NUMBER_SIGNED, | ||
}; | ||
|
||
export default NumberFormats; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
...uperset-ui/packages/superset-ui-number-format/src/factories/createSmartNumberFormatter.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* eslint-disable no-magic-numbers */ | ||
|
||
import { format as d3Format } from 'd3-format'; | ||
import NumberFormatter from '../NumberFormatter'; | ||
import NumberFormats from '../NumberFormats'; | ||
|
||
const siFormatter = d3Format(`.3~s`); | ||
const float2PointFormatter = d3Format(`.2~f`); | ||
const float4PointFormatter = d3Format(`.4~f`); | ||
|
||
export default function createSmartNumberFormatter( | ||
config: { | ||
description?: string; | ||
signed?: boolean; | ||
id?: string; | ||
label?: string; | ||
} = {}, | ||
) { | ||
const { description, signed = false, id, label } = config; | ||
const getSign = signed ? (value: number) => (value > 0 ? '+' : '') : () => ''; | ||
|
||
function formatValue(value: number) { | ||
if (value === 0) { | ||
return '0'; | ||
} | ||
const absoluteValue = Math.abs(value); | ||
if (absoluteValue >= 1000) { | ||
// Normal human being are more familiar | ||
// with billion (B) that giga (G) | ||
return siFormatter(value).replace('G', 'B'); | ||
} else if (absoluteValue >= 1) { | ||
return float2PointFormatter(value); | ||
} else if (absoluteValue >= 0.001) { | ||
return float4PointFormatter(value); | ||
} else if (absoluteValue > 0.000001) { | ||
return `${siFormatter(value * 1000000)}µ`; | ||
} | ||
|
||
return siFormatter(value); | ||
} | ||
|
||
return new NumberFormatter({ | ||
description, | ||
formatFunc: value => `${getSign(value)}${formatValue(value)}`, | ||
id: id || signed ? NumberFormats.SMART_NUMBER_SIGNED : NumberFormats.SMART_NUMBER, | ||
label: label || 'Adaptive formatter', | ||
}); | ||
} |
4 changes: 4 additions & 0 deletions
4
...et_ui/superset-ui/packages/superset-ui-number-format/test/NumberFormatterRegistry.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
...t-ui/packages/superset-ui-number-format/test/factories/createSmartNumberFormatter.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import NumberFormatter from '../../src/NumberFormatter'; | ||
import createSmartNumberFormatter from '../../src/factories/createSmartNumberFormatter'; | ||
|
||
describe('createSmartNumberFormatter(options)', () => { | ||
it('creates an instance of NumberFormatter', () => { | ||
const formatter = createSmartNumberFormatter(); | ||
expect(formatter).toBeInstanceOf(NumberFormatter); | ||
}); | ||
describe('using default options', () => { | ||
const formatter = createSmartNumberFormatter(); | ||
it('formats 0 correctly', () => { | ||
expect(formatter(0)).toBe('0'); | ||
}); | ||
describe('for positive numbers', () => { | ||
it('formats billion with B in stead of G', () => { | ||
expect(formatter(1000000000)).toBe('1B'); | ||
expect(formatter(4560000000)).toBe('4.56B'); | ||
}); | ||
it('formats numbers that are >= 1,000 & <= 1,000,000,000 as SI format with precision 3', () => { | ||
expect(formatter(1000)).toBe('1k'); | ||
expect(formatter(10001)).toBe('10k'); | ||
expect(formatter(10100)).toBe('10.1k'); | ||
expect(formatter(111000000)).toBe('111M'); | ||
}); | ||
it('formats number that are >= 1 & < 1,000 as integer or float with at most 2 decimal points', () => { | ||
expect(formatter(1)).toBe('1'); | ||
expect(formatter(1.0)).toBe('1'); | ||
expect(formatter(10)).toBe('10'); | ||
expect(formatter(10.0)).toBe('10'); | ||
expect(formatter(10.23432)).toBe('10.23'); | ||
expect(formatter(274.2856)).toBe('274.29'); | ||
expect(formatter(999)).toBe('999'); | ||
}); | ||
it('formats numbers that are < 1 & >= 0.001 as float with at most 4 decimal points', () => { | ||
expect(formatter(0.1)).toBe('0.1'); | ||
expect(formatter(0.23)).toBe('0.23'); | ||
expect(formatter(0.699)).toBe('0.699'); | ||
expect(formatter(0.0023)).toBe('0.0023'); | ||
expect(formatter(0.002300001)).toBe('0.0023'); | ||
}); | ||
it('formats numbers that are < 0.001 & >= 0.000001 as micron', () => { | ||
expect(formatter(0.0002300001)).toBe('230µ'); | ||
expect(formatter(0.000023)).toBe('23µ'); | ||
expect(formatter(0.000001)).toBe('1µ'); | ||
}); | ||
it('formats numbers that are less than 0.000001 as SI format with precision 3', () => { | ||
expect(formatter(0.0000001)).toBe('100n'); | ||
}); | ||
}); | ||
describe('for negative numbers', () => { | ||
it('formats billion with B in stead of G', () => { | ||
expect(formatter(-1000000000)).toBe('-1B'); | ||
expect(formatter(-4560000000)).toBe('-4.56B'); | ||
}); | ||
it('formats numbers that are >= 1,000 & <= 1,000,000,000 as SI format with precision 3', () => { | ||
expect(formatter(-1000)).toBe('-1k'); | ||
expect(formatter(-10001)).toBe('-10k'); | ||
expect(formatter(-10100)).toBe('-10.1k'); | ||
expect(formatter(-111000000)).toBe('-111M'); | ||
}); | ||
it('formats number that are >= 1 & < 1,000 as integer or float with at most 2 decimal points', () => { | ||
expect(formatter(-1)).toBe('-1'); | ||
expect(formatter(-1.0)).toBe('-1'); | ||
expect(formatter(-10)).toBe('-10'); | ||
expect(formatter(-10.0)).toBe('-10'); | ||
expect(formatter(-10.23432)).toBe('-10.23'); | ||
expect(formatter(-274.2856)).toBe('-274.29'); | ||
expect(formatter(-999)).toBe('-999'); | ||
}); | ||
it('formats numbers that are < 1 & >= 0.001 as float with at most 4 decimal points', () => { | ||
expect(formatter(-0.1)).toBe('-0.1'); | ||
expect(formatter(-0.23)).toBe('-0.23'); | ||
expect(formatter(-0.699)).toBe('-0.699'); | ||
expect(formatter(-0.0023)).toBe('-0.0023'); | ||
expect(formatter(-0.002300001)).toBe('-0.0023'); | ||
}); | ||
it('formats numbers that are < 0.001 & >= 0.000001 as micron', () => { | ||
expect(formatter(-0.0002300001)).toBe('-230µ'); | ||
expect(formatter(-0.000023)).toBe('-23µ'); | ||
expect(formatter(-0.000001)).toBe('-1µ'); | ||
}); | ||
it('formats numbers that are less than 0.000001 as SI format with precision 3', () => { | ||
expect(formatter(-0.0000001)).toBe('-100n'); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('when options.signed is true, it adds + for positive numbers', () => { | ||
const formatter = createSmartNumberFormatter({ signed: true }); | ||
it('formats 0 correctly', () => { | ||
expect(formatter(0)).toBe('0'); | ||
}); | ||
describe('for positive numbers', () => { | ||
it('formats billion with B in stead of G', () => { | ||
expect(formatter(1000000000)).toBe('+1B'); | ||
expect(formatter(4560000000)).toBe('+4.56B'); | ||
}); | ||
it('formats numbers that are >= 1,000 & <= 1,000,000,000 as SI format with precision 3', () => { | ||
expect(formatter(1000)).toBe('+1k'); | ||
expect(formatter(10001)).toBe('+10k'); | ||
expect(formatter(10100)).toBe('+10.1k'); | ||
expect(formatter(111000000)).toBe('+111M'); | ||
}); | ||
it('formats number that are >= 1 & < 1,000 as integer or float with at most 2 decimal points', () => { | ||
expect(formatter(1)).toBe('+1'); | ||
expect(formatter(1.0)).toBe('+1'); | ||
expect(formatter(10)).toBe('+10'); | ||
expect(formatter(10.0)).toBe('+10'); | ||
expect(formatter(10.23432)).toBe('+10.23'); | ||
expect(formatter(274.2856)).toBe('+274.29'); | ||
expect(formatter(999)).toBe('+999'); | ||
}); | ||
it('formats numbers that are < 1 & >= 0.001 as float with at most 4 decimal points', () => { | ||
expect(formatter(0.1)).toBe('+0.1'); | ||
expect(formatter(0.23)).toBe('+0.23'); | ||
expect(formatter(0.699)).toBe('+0.699'); | ||
expect(formatter(0.0023)).toBe('+0.0023'); | ||
expect(formatter(0.002300001)).toBe('+0.0023'); | ||
}); | ||
it('formats numbers that are < 0.001 & >= 0.000001 as micron', () => { | ||
expect(formatter(0.0002300001)).toBe('+230µ'); | ||
expect(formatter(0.000023)).toBe('+23µ'); | ||
expect(formatter(0.000001)).toBe('+1µ'); | ||
}); | ||
it('formats numbers that are less than 0.000001 as SI format with precision 3', () => { | ||
expect(formatter(0.0000001)).toBe('+100n'); | ||
}); | ||
}); | ||
}); | ||
}); |