Skip to content

Commit

Permalink
Convert number-format to TypeScript (apache#75)
Browse files Browse the repository at this point in the history
* Convert number-format to TypeScript

* return string

* unit test 100%

* Allow developer to specify return type for loader

* add doc
  • Loading branch information
kristw authored Jan 15, 2019
1 parent a789498 commit ac24d30
Show file tree
Hide file tree
Showing 21 changed files with 217 additions and 158 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"build": "yarn run build:cjs && yarn run build:esm && yarn run type:dts",
"build:cjs": "NODE_ENV=production beemo babel --extensions=\".js,.jsx,.ts,.tsx\" ./src --out-dir lib/ --minify --workspaces=\"@superset-ui/!(demo|generator-superset)\"",
"build:esm": "NODE_ENV=production beemo babel --extensions=\".js,.jsx,.ts,.tsx\" ./src --out-dir esm/ --esm --minify --workspaces=\"@superset-ui/!(demo|generator-superset)\"",
"type": "NODE_ENV=production beemo typescript --workspaces=\"@superset-ui/(connection|core|color|chart)\" --noEmit",
"type:dts": "NODE_ENV=production beemo typescript --workspaces=\"@superset-ui/(connection|core|color|chart)\" --emitDeclarationOnly",
"type": "NODE_ENV=production beemo typescript --workspaces=\"@superset-ui/(connection|core|color|chart|number-format)\" --noEmit",
"type:dts": "NODE_ENV=production beemo typescript --workspaces=\"@superset-ui/(connection|core|color|chart|number-format)\" --emitDeclarationOnly",
"lint": "beemo create-config prettier && beemo eslint \"./packages/*/{src,test,storybook}/**/*.{js,jsx,ts,tsx}\"",
"lint:fix": "beemo create-config prettier && beemo eslint --fix \"./packages/*/{src,test,storybook}/**/*.{js,jsx,ts,tsx}\"",
"jest": "beemo jest --color --coverage --react",
Expand Down
31 changes: 21 additions & 10 deletions packages/superset-ui-core/src/models/Registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,30 @@ interface ItemWithValue<T> {
}

interface ItemWithLoader<T> {
loader: () => T | Promise<T>;
loader: () => T;
}

export interface RegistryConfig {
name?: string;
overwritePolicy?: OverwritePolicy;
}

export default class Registry<V> {
/**
* Registry class
*
* Can use generic to specify type of item in the registry
* @type V Type of value
* @type W Type of value returned from loader function when using registerLoader().
* W can be either V, Promise<V> or V | Promise<V>
* Set W=V when does not support asynchronous loader.
* By default W is set to V | Promise<V> to support
* both synchronous and asynchronous loaders.
*/
export default class Registry<V, W extends V | Promise<V> = V | Promise<V>> {
name: string;
overwritePolicy: OverwritePolicy;
items: {
[key: string]: ItemWithValue<V> | ItemWithLoader<V>;
[key: string]: ItemWithValue<V> | ItemWithLoader<W>;
};

promises: {
Expand Down Expand Up @@ -70,7 +81,7 @@ export default class Registry<V> {
return this;
}

registerLoader(key: string, loader: () => V | Promise<V>) {
registerLoader(key: string, loader: () => W) {
const item = this.items[key];
const willOverwrite =
this.has(key) && (('loader' in item && item.loader !== loader) || 'value' in item);
Expand All @@ -89,7 +100,7 @@ export default class Registry<V> {
return this;
}

get(key: string): V | Promise<V> | undefined {
get(key: string): V | W | undefined {
const item = this.items[key];
if (item !== undefined) {
if ('loader' in item) {
Expand All @@ -109,7 +120,7 @@ export default class Registry<V> {
}
const item = this.get(key);
if (item !== undefined) {
const newPromise = Promise.resolve(item);
const newPromise = Promise.resolve(item) as Promise<V>;
this.promises[key] = newPromise;

return newPromise;
Expand All @@ -120,7 +131,7 @@ export default class Registry<V> {

getMap() {
return this.keys().reduce<{
[key: string]: V | Promise<V> | undefined;
[key: string]: V | W | undefined;
}>((prev, key) => {
const map = prev;
map[key] = this.get(key);
Expand Down Expand Up @@ -148,22 +159,22 @@ export default class Registry<V> {
return Object.keys(this.items);
}

values(): Array<V | Promise<V> | undefined> {
values(): (V | W | undefined)[] {
return this.keys().map(key => this.get(key));
}

valuesAsPromise(): Promise<V[]> {
return Promise.all(this.keys().map(key => this.getAsPromise(key)));
}

entries(): Array<{ key: string; value: V | Promise<V> | undefined }> {
entries(): { key: string; value: V | W | undefined }[] {
return this.keys().map(key => ({
key,
value: this.get(key),
}));
}

entriesAsPromise(): Promise<Array<{ key: string; value: V }>> {
entriesAsPromise(): Promise<{ key: string; value: V }[]> {
const keys = this.keys();

return Promise.all(keys.map(key => this.getAsPromise(key))).then(values =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ export interface RegistryWithDefaultKeyConfig extends RegistryConfig {
setFirstItemAsDefault?: boolean;
}

export default class RegistryWithDefaultKey<V> extends Registry<V> {
export default class RegistryWithDefaultKey<
V,
W extends V | Promise<V> = V | Promise<V>
> extends Registry<V, W> {
initialDefaultKey?: string;
defaultKey?: string;
setFirstItemAsDefault: boolean;
Expand Down Expand Up @@ -41,7 +44,7 @@ export default class RegistryWithDefaultKey<V> extends Registry<V> {
return this;
}

registerLoader(key: string, loader: () => V | Promise<V>) {
registerLoader(key: string, loader: () => W) {
super.registerLoader(key, loader);
// If there is no default, set as default
if (this.setFirstItemAsDefault && !this.defaultKey) {
Expand Down
1 change: 1 addition & 0 deletions packages/superset-ui-number-format/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"dependencies": {
"@superset-ui/core": "^0.8.0",
"@types/d3-format": "^1.3.0",
"d3-format": "^1.3.2"
}
}
37 changes: 0 additions & 37 deletions packages/superset-ui-number-format/src/NumberFormatter.js

This file was deleted.

53 changes: 53 additions & 0 deletions packages/superset-ui-number-format/src/NumberFormatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { ExtensibleFunction, isRequired } from '@superset-ui/core';
import { NumberFormatFunction } from './types';

export const PREVIEW_VALUE = 12345.432;

export interface NumberFormatterConfig {
id: string;
label?: string;
description?: string;
formatFunc: NumberFormatFunction;
isInvalid?: boolean;
}

export default class NumberFormatter extends ExtensibleFunction {
id: string;
label: string;
description: string;
formatFunc: NumberFormatFunction;
isInvalid: boolean;

constructor(config: NumberFormatterConfig) {
super((value: number) => this.format(value));

const {
id = isRequired('config.id'),
label,
description = '',
formatFunc = isRequired('config.formatFunc'),
isInvalid = false,
} = config;
this.id = id;
this.label = label || id;
this.description = description;
this.formatFunc = formatFunc;
this.isInvalid = isInvalid;
}

format(value: number | null | undefined) {
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: number = PREVIEW_VALUE) {
return `${value} => ${this.format(value)}`;
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { RegistryWithDefaultKey } from '@superset-ui/core';
import createD3NumberFormatter from './factories/createD3NumberFormatter';
import NumberFormats from './NumberFormats';
import NumberFormatter from './NumberFormatter';

export default class NumberFormatterRegistry extends RegistryWithDefaultKey {
export default class NumberFormatterRegistry extends RegistryWithDefaultKey<
NumberFormatter,
NumberFormatter
> {
constructor() {
super({
initialDefaultKey: NumberFormats.SI,
name: 'NumberFormatter',
});
}

get(formatterId) {
const targetFormat = (formatterId || this.defaultKey).trim();
get(formatterId?: string) {
const targetFormat = `${formatterId || this.defaultKey}`.trim();

if (this.has(targetFormat)) {
return super.get(targetFormat);
return super.get(targetFormat) as NumberFormatter;
}

// Create new formatter if does not exist
Expand All @@ -26,7 +30,7 @@ export default class NumberFormatterRegistry extends RegistryWithDefaultKey {
return formatter;
}

format(formatterId, value) {
format(formatterId: string, value: number | null | undefined): string {
return this.get(formatterId)(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const getInstance = makeSingleton(NumberFormatterRegistry);

export default getInstance;

export function getNumberFormatter(format) {
export function getNumberFormatter(format: string) {
return getInstance().get(format);
}

export function formatNumber(format, value) {
export function formatNumber(format: string, value: number | null | undefined) {
return getInstance().format(format, value);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { format as d3Format } from 'd3-format';
import { isRequired } from '@superset-ui/core';
import NumberFormatter from '../NumberFormatter';
import { NumberFormatFunction } from '../types';

export default function createD3NumberFormatter({
description,
formatString = isRequired('formatString'),
label,
} = {}) {
let formatFunc;
export default function createD3NumberFormatter(config: {
description?: string;
formatString: string;
label?: string;
}) {
const { description, formatString = isRequired('config.formatString'), label } = config;

let formatFunc: NumberFormatFunction;
let isInvalid = false;

try {
Expand All @@ -17,12 +20,10 @@ export default function createD3NumberFormatter({
isInvalid = true;
}

const id = formatString;

return new NumberFormatter({
description,
formatFunc,
id,
id: formatString,
isInvalid,
label,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { format as d3Format } from 'd3-format';
import NumberFormatter from '../NumberFormatter';

export default function createSiAtMostNDigitFormatter({ description, n = 3, id, label } = {}) {
export default function createSiAtMostNDigitFormatter(
config: {
description?: string;
n?: number;
id?: string;
label?: string;
} = {},
) {
const { description, n = 3, id, label } = config;
const siFormatter = d3Format(`.${n}s`);

return new NumberFormatter({
Expand Down
2 changes: 2 additions & 0 deletions packages/superset-ui-number-format/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/* eslint-disable-next-line import/prefer-default-export */
export type NumberFormatFunction = (value: number) => string;
Loading

0 comments on commit ac24d30

Please sign in to comment.