From be65a04238f4b138ceff23025a45b9e31305fe53 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Tue, 20 Aug 2019 11:29:54 -0700 Subject: [PATCH] feat: add functions for parsing formatters from encoding (#205) * feat: add function for parsing formatters from encoding * fix: add unit test * feat: add getter * fix: update type --- .../superset-ui-encodeable/package.json | 3 ++- .../parsers/createFormatterFromChannelDef.ts | 14 ++++++++++ .../createFormatterFromFieldTypeAndFormat.ts | 22 +++++++++++++++ .../src/parsers/createGetterFromChannelDef.ts | 17 ++++++++++++ .../src/parsers/fallbackFormatter.ts | 3 +++ .../src/types/ChannelDef.ts | 4 +-- .../createFormatterFromChannelDef.test.ts | 27 +++++++++++++++++++ ...ateFormatterFromFieldTypeAndFormat.test.ts | 16 +++++++++++ .../createGetterFromChannelDef.test.ts | 21 +++++++++++++++ .../test/parsers/fallbackFormatter.test.ts | 22 +++++++++++++++ 10 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createFormatterFromFieldTypeAndFormat.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/fallbackFormatter.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createFormatterFromFieldTypeAndFormat.test.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createGetterFromChannelDef.test.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/fallbackFormatter.test.ts diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/package.json b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/package.json index 8e3e226b43..6be1193bb0 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/package.json +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/package.json @@ -26,7 +26,8 @@ "private": true, "dependencies": { "vega": "^5.4.0", - "vega-lite": "^3.4.0" + "vega-lite": "^3.4.0", + "lodash": "^4.17.15" }, "peerDependencies": { "@superset-ui/time-format": "^0.11.14", diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts new file mode 100644 index 0000000000..78d0005abc --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts @@ -0,0 +1,14 @@ +import { ChannelDef } from '../types/ChannelDef'; +import { isTypedFieldDef } from '../typeGuards/ChannelDef'; +import fallbackFormatter from './fallbackFormatter'; +import createFormatterFromFieldTypeAndFormat from './createFormatterFromFieldTypeAndFormat'; + +export default function createFormatterFromChannelDef(definition: ChannelDef) { + if (isTypedFieldDef(definition)) { + const { type, format = '' } = definition; + + return createFormatterFromFieldTypeAndFormat(type, format); + } + + return fallbackFormatter; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createFormatterFromFieldTypeAndFormat.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createFormatterFromFieldTypeAndFormat.ts new file mode 100644 index 0000000000..8f85830df7 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createFormatterFromFieldTypeAndFormat.ts @@ -0,0 +1,22 @@ +import { getNumberFormatter } from '@superset-ui/number-format'; +import { getTimeFormatter } from '@superset-ui/time-format'; +import { Type } from '../types/VegaLite'; +import { Formatter } from '../types/ChannelDef'; +import fallbackFormatter from './fallbackFormatter'; + +export default function createFormatterFromFieldTypeAndFormat( + type: Type, + format: string, +): Formatter { + if (type === 'quantitative') { + const formatter = getNumberFormatter(format); + + return (value: any) => formatter(value); + } else if (type === 'temporal') { + const formatter = getTimeFormatter(format); + + return (value: any) => formatter(value); + } + + return fallbackFormatter; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts new file mode 100644 index 0000000000..f4955af4fc --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts @@ -0,0 +1,17 @@ +import { get } from 'lodash/fp'; +import identity from '../utils/identity'; +import { ChannelDef } from '../types/ChannelDef'; +import { isValueDef } from '../typeGuards/ChannelDef'; +import { PlainObject } from '../types/Data'; + +export default function createGetterFromChannelDef( + definition: ChannelDef, +): (x?: PlainObject) => any { + if (isValueDef(definition)) { + return () => definition.value; + } else if (typeof definition.field !== 'undefined') { + return get(definition.field); + } + + return identity; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/fallbackFormatter.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/fallbackFormatter.ts new file mode 100644 index 0000000000..77c3bf27a4 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/parsers/fallbackFormatter.ts @@ -0,0 +1,3 @@ +export default function fallbackFormatter(v: any) { + return `${v}`; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/types/ChannelDef.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/types/ChannelDef.ts index 601bf691c8..0b36a5b718 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/types/ChannelDef.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/src/types/ChannelDef.ts @@ -1,11 +1,9 @@ -import { TimeFormatter } from '@superset-ui/time-format'; -import { NumberFormatter } from '@superset-ui/number-format'; import { ValueDef, Value, Type } from './VegaLite'; import { WithScale } from './Scale'; import { WithXAxis, WithYAxis, WithAxis } from './Axis'; import { WithLegend } from './Legend'; -export type Formatter = NumberFormatter | TimeFormatter | ((d: any) => string); +export type Formatter = (d: any) => string; export interface FieldDef { field: string; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts new file mode 100644 index 0000000000..d3c9acf8ed --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts @@ -0,0 +1,27 @@ +import createFormatterFromChannelDef from '../../src/parsers/createFormatterFromChannelDef'; + +describe('createFormatterFromChannelDef(type, format)', () => { + it('handles when format is defined', () => { + const formatter = createFormatterFromChannelDef({ + field: 'lunchTime', + type: 'temporal', + format: '%b %d, %Y', + }); + expect(formatter(new Date(Date.UTC(2019, 5, 20)))).toEqual('Jun 20, 2019'); + }); + it('handles when format is not defined', () => { + const formatter = createFormatterFromChannelDef({ + field: 'lunchTime', + type: 'temporal', + }); + expect(formatter(new Date(Date.UTC(2019, 5, 20)))).toEqual('2019-06-20 00:00:00'); + }); + it('uses fallback for other cases', () => { + const formatter = createFormatterFromChannelDef({ type: 'nominal', field: 'restaurantName' }); + expect(formatter('Lazy Burger')).toEqual('Lazy Burger'); + }); + it('uses fallback for channel definitions without type', () => { + const formatter = createFormatterFromChannelDef({ value: 'Lettuce' }); + expect(formatter('Lazy Burger')).toEqual('Lazy Burger'); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createFormatterFromFieldTypeAndFormat.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createFormatterFromFieldTypeAndFormat.test.ts new file mode 100644 index 0000000000..73f2456fab --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createFormatterFromFieldTypeAndFormat.test.ts @@ -0,0 +1,16 @@ +import createFormatterFromFieldTypeAndFormat from '../../src/parsers/createFormatterFromFieldTypeAndFormat'; + +describe('createFormatterFromFieldTypeAndFormat(type, format)', () => { + it('handles quantitative field type', () => { + const formatter = createFormatterFromFieldTypeAndFormat('quantitative', '.2f'); + expect(formatter(200)).toEqual('200.00'); + }); + it('handles temporal field type', () => { + const formatter = createFormatterFromFieldTypeAndFormat('temporal', '%b %d, %Y'); + expect(formatter(new Date(Date.UTC(2019, 5, 20)))).toEqual('Jun 20, 2019'); + }); + it('uses fallback for other cases', () => { + const formatter = createFormatterFromFieldTypeAndFormat('nominal', ''); + expect(formatter('cat')).toEqual('cat'); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createGetterFromChannelDef.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createGetterFromChannelDef.test.ts new file mode 100644 index 0000000000..468deae693 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/createGetterFromChannelDef.test.ts @@ -0,0 +1,21 @@ +import createGetterFromChannelDef from '../../src/parsers/createGetterFromChannelDef'; + +describe('createGetterFromChannelDef(definition)', () => { + it('handles ValueDef', () => { + const getter = createGetterFromChannelDef({ value: 1 }); + expect(getter()).toBe(1); + }); + it('handleFieldDef', () => { + const getter = createGetterFromChannelDef({ field: 'cost' }); + expect(getter({ cost: 10 })).toBe(10); + }); + it('handleFieldDef with nested field', () => { + const getter = createGetterFromChannelDef({ field: 'fuel.cost' }); + expect(getter({ fuel: { cost: 10 } })).toBe(10); + }); + it('otherwise return identity', () => { + // @ts-ignore + const getter = createGetterFromChannelDef({}); + expect(getter(300)).toBe(300); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/fallbackFormatter.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/fallbackFormatter.test.ts new file mode 100644 index 0000000000..a61b611eb2 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodeable/test/parsers/fallbackFormatter.test.ts @@ -0,0 +1,22 @@ +import fallbackFormatter from '../../src/parsers/fallbackFormatter'; + +describe('fallbackFormatter(v: any)', () => { + it('handles primitive types', () => { + expect(fallbackFormatter(undefined)).toEqual('undefined'); + expect(fallbackFormatter(null)).toEqual('null'); + expect(fallbackFormatter(true)).toEqual('true'); + expect(fallbackFormatter(false)).toEqual('false'); + expect(fallbackFormatter(0)).toEqual('0'); + expect(fallbackFormatter(1)).toEqual('1'); + expect(fallbackFormatter(-1)).toEqual('-1'); + }); + it('handles arrays', () => { + expect(fallbackFormatter([])).toEqual(''); + expect(fallbackFormatter(['def'])).toEqual('def'); + expect(fallbackFormatter(['def', 'ghi'])).toEqual('def,ghi'); + }); + it('handles objects', () => { + expect(fallbackFormatter({})).toEqual('[object Object]'); + expect(fallbackFormatter({ abc: 1 })).toEqual('[object Object]'); + }); +});