From b728c9831f996b1f9a4669dbd47744cd43468060 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 14 Aug 2019 16:08:48 -0700 Subject: [PATCH 1/4] feat: add function for parsing formatters from encoding --- .../parsers/createFormatterFromChannelDef.ts | 15 ++++++++++++ .../createFormatterFromFieldTypeAndFormat.ts | 22 ++++++++++++++++++ .../src/parsers/fallbackFormatter.ts | 3 +++ .../src/types/ChannelDef.ts | 4 +--- .../createFormatterFromChannelDef.test.ts | 23 +++++++++++++++++++ ...ateFormatterFromFieldTypeAndFormat.test.ts | 16 +++++++++++++ .../test/parsers/fallbackFormatter.test.ts | 22 ++++++++++++++++++ 7 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts create mode 100644 packages/superset-ui-encodeable/src/parsers/createFormatterFromFieldTypeAndFormat.ts create mode 100644 packages/superset-ui-encodeable/src/parsers/fallbackFormatter.ts create mode 100644 packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts create mode 100644 packages/superset-ui-encodeable/test/parsers/createFormatterFromFieldTypeAndFormat.test.ts create mode 100644 packages/superset-ui-encodeable/test/parsers/fallbackFormatter.test.ts diff --git a/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts b/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts new file mode 100644 index 0000000000..cf002cc3d4 --- /dev/null +++ b/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts @@ -0,0 +1,15 @@ +import { ChannelDef } from '../types/ChannelDef'; +import { isTypedFieldDef } from '../typeGuards/ChannelDef'; +import fallbackFormatter from './fallbackFormatter'; +import createFormatterFromFieldTypeAndFormat from './createFormatterFromFieldTypeAndFormat'; +import { Value } from '../types/VegaLite'; + +export default function createFormatterFromChannelDef(definition: ChannelDef) { + if (isTypedFieldDef(definition)) { + const { type, format = '' } = definition; + + return createFormatterFromFieldTypeAndFormat(type, format); + } + + return fallbackFormatter; +} diff --git a/packages/superset-ui-encodeable/src/parsers/createFormatterFromFieldTypeAndFormat.ts b/packages/superset-ui-encodeable/src/parsers/createFormatterFromFieldTypeAndFormat.ts new file mode 100644 index 0000000000..8f85830df7 --- /dev/null +++ b/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/packages/superset-ui-encodeable/src/parsers/fallbackFormatter.ts b/packages/superset-ui-encodeable/src/parsers/fallbackFormatter.ts new file mode 100644 index 0000000000..77c3bf27a4 --- /dev/null +++ b/packages/superset-ui-encodeable/src/parsers/fallbackFormatter.ts @@ -0,0 +1,3 @@ +export default function fallbackFormatter(v: any) { + return `${v}`; +} diff --git a/packages/superset-ui-encodeable/src/types/ChannelDef.ts b/packages/superset-ui-encodeable/src/types/ChannelDef.ts index 601bf691c8..0b36a5b718 100644 --- a/packages/superset-ui-encodeable/src/types/ChannelDef.ts +++ b/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/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts b/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts new file mode 100644 index 0000000000..b53a1885cb --- /dev/null +++ b/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts @@ -0,0 +1,23 @@ +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'); + }); +}); diff --git a/packages/superset-ui-encodeable/test/parsers/createFormatterFromFieldTypeAndFormat.test.ts b/packages/superset-ui-encodeable/test/parsers/createFormatterFromFieldTypeAndFormat.test.ts new file mode 100644 index 0000000000..73f2456fab --- /dev/null +++ b/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/packages/superset-ui-encodeable/test/parsers/fallbackFormatter.test.ts b/packages/superset-ui-encodeable/test/parsers/fallbackFormatter.test.ts new file mode 100644 index 0000000000..a61b611eb2 --- /dev/null +++ b/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]'); + }); +}); From 339be01bdeeba157719b29e9aaa09b859df127ae Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 14 Aug 2019 16:10:14 -0700 Subject: [PATCH 2/4] fix: add unit test --- .../test/parsers/createFormatterFromChannelDef.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts b/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts index b53a1885cb..d3c9acf8ed 100644 --- a/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts +++ b/packages/superset-ui-encodeable/test/parsers/createFormatterFromChannelDef.test.ts @@ -20,4 +20,8 @@ describe('createFormatterFromChannelDef(type, format)', () => { 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'); + }); }); From 666bd614e98b6764d81d51509b3c1ce8178cb88a Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 15 Aug 2019 16:18:20 -0700 Subject: [PATCH 3/4] feat: add getter --- packages/superset-ui-encodeable/package.json | 3 ++- .../parsers/createFormatterFromChannelDef.ts | 3 +-- .../src/parsers/createGetterFromChannelDef.ts | 14 +++++++++++++ .../createGetterFromChannelDef.test.ts | 21 +++++++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts create mode 100644 packages/superset-ui-encodeable/test/parsers/createGetterFromChannelDef.test.ts diff --git a/packages/superset-ui-encodeable/package.json b/packages/superset-ui-encodeable/package.json index 8e3e226b43..6be1193bb0 100644 --- a/packages/superset-ui-encodeable/package.json +++ b/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/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts b/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts index cf002cc3d4..78d0005abc 100644 --- a/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts +++ b/packages/superset-ui-encodeable/src/parsers/createFormatterFromChannelDef.ts @@ -2,9 +2,8 @@ import { ChannelDef } from '../types/ChannelDef'; import { isTypedFieldDef } from '../typeGuards/ChannelDef'; import fallbackFormatter from './fallbackFormatter'; import createFormatterFromFieldTypeAndFormat from './createFormatterFromFieldTypeAndFormat'; -import { Value } from '../types/VegaLite'; -export default function createFormatterFromChannelDef(definition: ChannelDef) { +export default function createFormatterFromChannelDef(definition: ChannelDef) { if (isTypedFieldDef(definition)) { const { type, format = '' } = definition; diff --git a/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts b/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts new file mode 100644 index 0000000000..7c76066ca0 --- /dev/null +++ b/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts @@ -0,0 +1,14 @@ +import { get } from 'lodash/fp'; +import identity from '../utils/identity'; +import { ChannelDef } from '../types/ChannelDef'; +import { isValueDef } from '../typeGuards/ChannelDef'; + +export default function createGetterFromChannelDef(definition: ChannelDef): (x?: any) => any { + if (isValueDef(definition)) { + return () => definition.value; + } else if (typeof definition.field !== 'undefined') { + return get(definition.field); + } + + return identity; +} diff --git a/packages/superset-ui-encodeable/test/parsers/createGetterFromChannelDef.test.ts b/packages/superset-ui-encodeable/test/parsers/createGetterFromChannelDef.test.ts new file mode 100644 index 0000000000..468deae693 --- /dev/null +++ b/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); + }); +}); From 24e5a14450d469dd7dddfbb8cabf28d44d5a35cb Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Mon, 19 Aug 2019 11:42:13 -0700 Subject: [PATCH 4/4] fix: update type --- .../src/parsers/createGetterFromChannelDef.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts b/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts index 7c76066ca0..f4955af4fc 100644 --- a/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts +++ b/packages/superset-ui-encodeable/src/parsers/createGetterFromChannelDef.ts @@ -2,8 +2,11 @@ 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?: any) => any { +export default function createGetterFromChannelDef( + definition: ChannelDef, +): (x?: PlainObject) => any { if (isValueDef(definition)) { return () => definition.value; } else if (typeof definition.field !== 'undefined') {