-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add @superset-ui/number-format package (#31)
* feat: Add number-format package
- Loading branch information
1 parent
50cb761
commit 3d12b0f
Showing
16 changed files
with
498 additions
and
1 deletion.
There are no files selected for viewing
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
57 changes: 57 additions & 0 deletions
57
.../temporary_superset_ui/superset-ui/packages/superset-ui-number-format/README.md
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,57 @@ | ||
## @superset-ui/number-format | ||
|
||
[![Version](https://img.shields.io/npm/v/@superset-ui/number-format.svg?style=flat)](https://img.shields.io/npm/v/@superset-ui/number-format.svg?style=flat) | ||
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui.svg?path=packages%2Fsuperset-ui-number-format&style=flat-square)](https://david-dm.org/apache-superset/superset-ui?path=packages/superset-ui-number-format) | ||
|
||
Description | ||
|
||
#### Example usage | ||
|
||
Functions `getNumberFormatter` and `formatNumber` should be used instead of calling `d3.format` directly. | ||
|
||
```js | ||
import { getNumberFormatter } from '@superset-ui/number-format'; | ||
const formatter = getNumberFormatter('.2f'); | ||
console.log(formatter(1000)); | ||
``` | ||
|
||
or | ||
|
||
```js | ||
import { formatNumber } from '@superset-ui/number-format'; | ||
console.log(formatNumber('.2f', 1000)); | ||
``` | ||
|
||
It is powered by a registry to support registration of custom formatting, with fallback to `d3.format` and handle error for invalid format string. | ||
|
||
```js | ||
import { getNumberFormatterRegistry, formatNumber, NumberFormatter } from '@superset-ui/number-format'; | ||
|
||
getNumberFormatterRegistry().registerValue('my_format', new NumberFormatter({ | ||
id: 'my_format', | ||
formatFunc: v => `my special format of ${v}` | ||
}); | ||
|
||
console.log(formatNumber('my_format', 1000)); | ||
// prints 'my special format of 1000' | ||
``` | ||
It also define constants for common d3 formats. See the full list of formats in [NumberFormats.js](https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-number-format/src/NumberFormats.js). | ||
```js | ||
import { NumberFormats } from '@superset-ui-number-format'; | ||
|
||
NumberFormats.PERCENT // ,.2% | ||
NumberFormats.PERCENT_3_POINT // ,.3% | ||
``` | ||
#### API | ||
`fn(args)` | ||
- Do something | ||
### Development | ||
`@data-ui/build-config` is used to manage the build configuration for this package including babel | ||
builds, jest testing, eslint, and prettier. |
33 changes: 33 additions & 0 deletions
33
...rontend/temporary_superset_ui/superset-ui/packages/superset-ui-number-format/package.json
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,33 @@ | ||
{ | ||
"name": "@superset-ui/number-format", | ||
"version": "0.0.0", | ||
"description": "Superset UI number format", | ||
"sideEffects": false, | ||
"main": "lib/index.js", | ||
"module": "esm/index.js", | ||
"files": [ | ||
"esm", | ||
"lib" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/apache-superset/superset-ui.git" | ||
}, | ||
"keywords": [ | ||
"superset" | ||
], | ||
"author": "Superset", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/apache-superset/superset-ui/issues" | ||
}, | ||
"homepage": "https://github.com/apache-superset/superset-ui#readme", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"dependencies": { | ||
"@superset-ui/core": "^0.6.0", | ||
"d3-format": "^1.3.2", | ||
"lodash": "^4.17.11" | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
...temporary_superset_ui/superset-ui/packages/superset-ui-number-format/src/NumberFormats.js
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,31 @@ | ||
export const DOLLAR = '$,.2f'; | ||
export const DOLLAR_CHANGE = '+$,.2f'; | ||
export const DOLLAR_ROUND = '$,d'; | ||
export const DOLLAR_ROUND_CHANGE = '+$,d'; | ||
|
||
export const FLOAT_1_POINT = ',.1f'; | ||
export const FLOAT_2_POINT = ',.2f'; | ||
export const FLOAT_3_POINT = ',.3f'; | ||
export const FLOAT = FLOAT_2_POINT; | ||
|
||
export const FLOAT_CHANGE_1_POINT = '+,.1f'; | ||
export const FLOAT_CHANGE_2_POINT = '+,.2f'; | ||
export const FLOAT_CHANGE_3_POINT = '+,.3f'; | ||
export const FLOAT_CHANGE = FLOAT_CHANGE_2_POINT; | ||
|
||
export const INTEGER = ',d'; | ||
export const INTEGER_CHANGE = '+,d'; | ||
|
||
export const PERCENT_1_POINT = ',.1%'; | ||
export const PERCENT_2_POINT = ',.2%'; | ||
export const PERCENT_3_POINT = ',.3%'; | ||
export const PERCENT = PERCENT_2_POINT; | ||
|
||
export const PERCENT_CHANGE_1_POINT = '+,.1%'; | ||
export const PERCENT_CHANGE_2_POINT = '+,.2%'; | ||
export const PERCENT_CHANGE_3_POINT = '+,.3%'; | ||
export const PERCENT_CHANGE = PERCENT_CHANGE_2_POINT; | ||
|
||
export const SI_1_DIGIT = '.1s'; | ||
export const SI_2_DIGIT = '.2s'; | ||
export const SI_3_DIGIT = '.3s'; |
35 changes: 35 additions & 0 deletions
35
...mporary_superset_ui/superset-ui/packages/superset-ui-number-format/src/NumberFormatter.js
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,35 @@ | ||
import { ExtensibleFunction, isRequired } from '@superset-ui/core'; | ||
|
||
export const PREVIEW_VALUE = 12345.432; | ||
|
||
export default class NumberFormatter extends ExtensibleFunction { | ||
constructor({ | ||
id = isRequired('config.id'), | ||
label, | ||
description = '', | ||
formatFunc = isRequired('config.formatFunc'), | ||
} = {}) { | ||
super((...args) => this.format(...args)); | ||
|
||
this.id = id; | ||
this.label = label || id; | ||
this.description = description; | ||
this.formatFunc = formatFunc; | ||
} | ||
|
||
format(value) { | ||
if (value === null || value === undefined || Number.isNaN(value)) { | ||
return value; | ||
} else if (value === Number.POSITIVE_INFINITY) { | ||
return '∞'; | ||
} else if (value === Number.NEGATIVE_INFINITY) { | ||
return '-∞'; | ||
} | ||
|
||
return this.formatFunc(value); | ||
} | ||
|
||
preview(value = PREVIEW_VALUE) { | ||
return `${value} => ${this.format(value)}`; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
...superset_ui/superset-ui/packages/superset-ui-number-format/src/NumberFormatterRegistry.js
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,32 @@ | ||
import { RegistryWithDefaultKey } from '@superset-ui/core'; | ||
import D3Formatter from './formatters/D3Formatter'; | ||
import { SI_3_DIGIT } from './NumberFormats'; | ||
|
||
const DEFAULT_FORMAT = SI_3_DIGIT; | ||
|
||
export default class NumberFormatterRegistry extends RegistryWithDefaultKey { | ||
constructor() { | ||
super({ | ||
initialDefaultKey: DEFAULT_FORMAT, | ||
name: 'NumberFormatter', | ||
}); | ||
} | ||
|
||
get(formatterId) { | ||
const targetFormat = formatterId || this.defaultKey; | ||
|
||
if (this.has(targetFormat)) { | ||
return super.get(targetFormat); | ||
} | ||
|
||
// Create new formatter if does not exist | ||
const formatter = new D3Formatter(targetFormat); | ||
this.registerValue(targetFormat, formatter); | ||
|
||
return formatter; | ||
} | ||
|
||
format(formatterId, value) { | ||
return this.get(formatterId)(value); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
...ui/superset-ui/packages/superset-ui-number-format/src/NumberFormatterRegistrySingleton.js
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,14 @@ | ||
import { makeSingleton } from '@superset-ui/core'; | ||
import NumberFormatterRegistry from './NumberFormatterRegistry'; | ||
|
||
const getInstance = makeSingleton(NumberFormatterRegistry); | ||
|
||
export default getInstance; | ||
|
||
export function getNumberFormatter(format) { | ||
return getInstance().get(format); | ||
} | ||
|
||
export function formatNumber(format, value) { | ||
return getInstance().format(format, value); | ||
} |
40 changes: 40 additions & 0 deletions
40
..._superset_ui/superset-ui/packages/superset-ui-number-format/src/formatters/D3Formatter.js
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,40 @@ | ||
import isString from 'lodash/isString'; | ||
import { format as d3Format } from 'd3-format'; | ||
import { isRequired } from '@superset-ui/core'; | ||
import NumberFormatter from '../NumberFormatter'; | ||
|
||
export default class D3Formatter extends NumberFormatter { | ||
/** | ||
* Pass only the D3 format string to constructor | ||
* | ||
* new D3Formatter('.2f'); | ||
* | ||
* or accompany it with human-readable label and description | ||
* | ||
* new D3Formatter({ | ||
* id: '.2f', | ||
* label: 'Float with 2 decimal points', | ||
* description: 'lorem ipsum dolor sit amet', | ||
* }); | ||
* | ||
* @param {String|Object} configOrFormatString | ||
*/ | ||
constructor(configOrFormatString = isRequired('configOrFormatString')) { | ||
const config = isString(configOrFormatString) | ||
? { id: configOrFormatString } | ||
: configOrFormatString; | ||
|
||
let formatFunc; | ||
let isInvalid = false; | ||
|
||
try { | ||
formatFunc = d3Format(config.id); | ||
} catch (e) { | ||
formatFunc = () => `Invalid format: ${config.id}`; | ||
isInvalid = true; | ||
} | ||
|
||
super({ ...config, formatFunc }); | ||
this.isInvalid = isInvalid; | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
.../superset-ui/packages/superset-ui-number-format/src/formatters/SiAtMostNDigitFormatter.js
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,19 @@ | ||
import { format as d3Format } from 'd3-format'; | ||
import NumberFormatter from '../NumberFormatter'; | ||
|
||
export default class SiAtMostNDigitFormatter extends NumberFormatter { | ||
constructor(n = 3) { | ||
const siFormatter = d3Format(`.${n}s`); | ||
|
||
super({ | ||
formatFunc: value => { | ||
const si = siFormatter(value); | ||
|
||
// Removing trailing `.00` if any | ||
return si.slice(-1) < 'A' ? parseFloat(si).toString() : si; | ||
}, | ||
id: `si_at_most_${n}_digit`, | ||
label: `SI with at most ${n} significant digits`, | ||
}); | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
...rontend/temporary_superset_ui/superset-ui/packages/superset-ui-number-format/src/index.js
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,10 @@ | ||
import * as NumberFormats from './NumberFormats'; | ||
|
||
export { | ||
default as getNumberFormatterRegistry, | ||
formatNumber, | ||
getNumberFormatter, | ||
} from './NumberFormatterRegistrySingleton'; | ||
|
||
export { default as NumberFormatter, PREVIEW_VALUE } from './NumberFormatter'; | ||
export { NumberFormats }; |
64 changes: 64 additions & 0 deletions
64
...y_superset_ui/superset-ui/packages/superset-ui-number-format/test/NumberFormatter.test.js
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,64 @@ | ||
import NumberFormatter from '../src/NumberFormatter'; | ||
|
||
describe('NumberFormatter', () => { | ||
describe('new NumberFormatter(config)', () => { | ||
it('requires config.id', () => { | ||
expect(() => new NumberFormatter()).toThrow(); | ||
}); | ||
it('requires config.formatFunc', () => { | ||
expect( | ||
() => | ||
new NumberFormatter({ | ||
id: 'my_format', | ||
}), | ||
).toThrow(); | ||
}); | ||
}); | ||
describe('formatter is also a format function itself', () => { | ||
const formatter = new NumberFormatter({ | ||
id: 'fixed_3', | ||
formatFunc: value => value.toFixed(3), | ||
}); | ||
it('returns formatted value', () => { | ||
expect(formatter(12345.67)).toEqual('12345.670'); | ||
}); | ||
it('formatter(value) is the same with formatter.format(value)', () => { | ||
const value = 12345.67; | ||
expect(formatter(value)).toEqual(formatter.format(value)); | ||
}); | ||
}); | ||
describe('.format(value)', () => { | ||
const formatter = new NumberFormatter({ | ||
id: 'fixed_3', | ||
formatFunc: value => value.toFixed(3), | ||
}); | ||
it('handles null', () => { | ||
expect(formatter.format(null)).toBeNull(); | ||
}); | ||
it('handles undefined', () => { | ||
expect(formatter.format(undefined)).toBeUndefined(); | ||
}); | ||
it('handles NaN', () => { | ||
expect(formatter.format(NaN)).toBeNaN(); | ||
}); | ||
it('handles positive and negative infinity', () => { | ||
expect(formatter.format(Number.POSITIVE_INFINITY)).toEqual('∞'); | ||
expect(formatter.format(Number.NEGATIVE_INFINITY)).toEqual('-∞'); | ||
}); | ||
it('otherwise returns formatted value', () => { | ||
expect(formatter.format(12345.67)).toEqual('12345.670'); | ||
}); | ||
}); | ||
describe('.preview(value)', () => { | ||
const formatter = new NumberFormatter({ | ||
id: 'fixed_2', | ||
formatFunc: value => value.toFixed(2), | ||
}); | ||
it('returns string comparing value before and after formatting', () => { | ||
expect(formatter.preview(100)).toEqual('100 => 100.00'); | ||
}); | ||
it('uses the default preview value if not specified', () => { | ||
expect(formatter.preview()).toEqual('12345.432 => 12345.43'); | ||
}); | ||
}); | ||
}); |
32 changes: 32 additions & 0 deletions
32
...et_ui/superset-ui/packages/superset-ui-number-format/test/NumberFormatterRegistry.test.js
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,32 @@ | ||
import NumberFormatterRegistry from '../src/NumberFormatterRegistry'; | ||
import NumberFormatter from '../src/NumberFormatter'; | ||
|
||
describe('NumberFormatterRegistry', () => { | ||
let registry; | ||
beforeEach(() => { | ||
registry = new NumberFormatterRegistry(); | ||
}); | ||
describe('.get(format)', () => { | ||
it('creates and returns a new formatter if does not exist', () => { | ||
const formatter = registry.get('.2f'); | ||
expect(formatter).toBeInstanceOf(NumberFormatter); | ||
expect(formatter.format(100)).toEqual('100.00'); | ||
}); | ||
it('returns an existing formatter if already exists', () => { | ||
const formatter = registry.get('.2f'); | ||
const formatter2 = registry.get('.2f'); | ||
expect(formatter).toBe(formatter2); | ||
}); | ||
it('falls back to default format if format is not specified', () => { | ||
registry.setDefaultKey('.1f'); | ||
const formatter = registry.get(); | ||
expect(formatter.format(100)).toEqual('100.0'); | ||
}); | ||
}); | ||
describe('.format(format, value)', () => { | ||
it('return the value with the specified format', () => { | ||
expect(registry.format('.2f', 100)).toEqual('100.00'); | ||
expect(registry.format(',d', 100)).toEqual('100'); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.