diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/scale/applyDomain.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/scale/applyDomain.ts index 312f87d4f331..1612f084d978 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/scale/applyDomain.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/parsers/scale/applyDomain.ts @@ -1,21 +1,25 @@ +import { isDateTime } from 'vega-lite/build/src/datetime'; import { Value } from '../../types/VegaLite'; -import { ScaleConfig, D3Scale, TimeScaleConfig } from '../../types/Scale'; +import { ScaleConfig, D3Scale } from '../../types/Scale'; import parseDateTime from '../parseDateTime'; import inferElementTypeFromUnionOfArrayTypes from '../../utils/inferElementTypeFromUnionOfArrayTypes'; -import { isTimeScale } from '../../typeGuards/Scale'; +import { isEveryElementDefined } from '../../typeGuards/Base'; export default function applyDomain( config: ScaleConfig, scale: D3Scale, ) { - const { domain, reverse, type } = config; - if (typeof domain !== 'undefined') { - const processedDomain = reverse ? domain.slice().reverse() : domain; - if (isTimeScale(scale, type)) { - const timeDomain = processedDomain as TimeScaleConfig['domain']; - scale.domain(inferElementTypeFromUnionOfArrayTypes(timeDomain).map(d => parseDateTime(d))); - } else { - scale.domain(processedDomain); + const { domain, reverse } = config; + if (typeof domain !== 'undefined' && domain.length > 0) { + const processedDomain = inferElementTypeFromUnionOfArrayTypes(domain); + + // Only set domain if all items are defined + if (isEveryElementDefined(processedDomain)) { + scale.domain( + (reverse ? processedDomain.slice().reverse() : processedDomain).map(d => + isDateTime(d) ? parseDateTime(d) : d, + ), + ); } } } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/typeGuards/Base.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/typeGuards/Base.ts index c2633141aab1..811f8d921412 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/typeGuards/Base.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/typeGuards/Base.ts @@ -9,3 +9,7 @@ export function isNotArray(maybeArray: T | T[]): maybeArray is T { export function isDefined(value: any): value is T { return typeof value !== 'undefined' && value !== null; } + +export function isEveryElementDefined(array: T[]): array is Exclude[] { + return array.every(isDefined); +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/typeGuards/Scale.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/typeGuards/Scale.ts index 6e4afef49122..c42cc998a07e 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/typeGuards/Scale.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/typeGuards/Scale.ts @@ -10,6 +10,7 @@ import { SymlogScaleConfig, TimeScaleConfig, UtcScaleConfig, + ContinuousD3Scale, } from '../types/Scale'; import { Value, ScaleType } from '../types/VegaLite'; import { timeScaleTypesSet, continuousScaleTypesSet } from '../parsers/scale/scaleCategories'; @@ -50,6 +51,13 @@ export function isD3Scale( return !isCategoricalColorScale(scale); } +export function isContinuousScale( + scale: D3Scale | CategoricalColorScale, + scaleType: ScaleType, +): scale is ContinuousD3Scale { + return scale && continuousScaleTypesSet.has(scaleType); +} + export function isTimeScale( scale: D3Scale | CategoricalColorScale, scaleType: ScaleType, diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Scale.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Scale.ts index a6000684a1bd..3fa26dbb2f06 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Scale.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/src/types/Scale.ts @@ -40,7 +40,7 @@ export interface CombinedScaleConfig /** * domain of the scale */ - domain?: number[] | string[] | boolean[] | DateTime[]; + domain?: (number | undefined | null)[] | string[] | boolean[] | (DateTime | undefined | null)[]; /** * range of the scale */ diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/parsers/scale/createScaleFromScaleConfig.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/parsers/scale/createScaleFromScaleConfig.test.ts index c4692c47479e..779a9a2e82ed 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/parsers/scale/createScaleFromScaleConfig.test.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/parsers/scale/createScaleFromScaleConfig.test.ts @@ -34,6 +34,60 @@ describe('createScaleFromScaleConfig(config)', () => { }); expect(scale(10)).toEqual(0); }); + describe('does not set domain if domain has undefined or null', () => { + describe('undefined', () => { + it('min', () => { + const scale = createScaleFromScaleConfig({ + type: 'linear', + domain: [undefined, 30], + range: [0, 100], + }); + expect(scale(10)).toEqual(1000); + }); + it('max', () => { + const scale = createScaleFromScaleConfig({ + type: 'linear', + domain: [0, undefined], + range: [0, 100], + }); + expect(scale(10)).toEqual(1000); + }); + it('both', () => { + const scale = createScaleFromScaleConfig({ + type: 'linear', + domain: [undefined, undefined], + range: [0, 100], + }); + expect(scale(10)).toEqual(1000); + }); + }); + describe('null', () => { + it('min', () => { + const scale = createScaleFromScaleConfig({ + type: 'linear', + domain: [null, 30], + range: [0, 100], + }); + expect(scale(10)).toEqual(1000); + }); + it('max', () => { + const scale = createScaleFromScaleConfig({ + type: 'linear', + domain: [0, null], + range: [0, 100], + }); + expect(scale(10)).toEqual(1000); + }); + it('both', () => { + const scale = createScaleFromScaleConfig({ + type: 'linear', + domain: [null, null], + range: [0, 100], + }); + expect(scale(10)).toEqual(1000); + }); + }); + }); it('with color scheme as range', () => { getSequentialSchemeRegistry().registerValue( 'test-scheme', diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/typeGuards/Base.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/typeGuards/Base.test.ts index 581ea3c1d66a..ce30c2cbe184 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/typeGuards/Base.test.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/typeGuards/Base.test.ts @@ -1,4 +1,4 @@ -import { isDefined, isArray, isNotArray } from '../../src/typeGuards/Base'; +import { isDefined, isArray, isNotArray, isEveryElementDefined } from '../../src/typeGuards/Base'; describe('type guards: Base', () => { describe('isArray(maybeArray)', () => { @@ -37,4 +37,18 @@ describe('type guards: Base', () => { expect(isDefined(undefined)).toBeFalsy(); }); }); + describe('isEveryElementDefined(array)', () => { + it('returns true and remove undefined from possible return type', () => { + expect(isEveryElementDefined(['a', 'b'])).toBeTruthy(); + expect(isEveryElementDefined([])).toBeTruthy(); + const array: (string | undefined)[] = ['a', 'b']; + if (isEveryElementDefined(array)) { + expect(array.every(a => a.length === 1)).toBeTruthy(); + } + }); + it('returns false otherwise', () => { + expect(isEveryElementDefined([undefined])).toBeFalsy(); + expect(isEveryElementDefined([undefined, 'a'])).toBeFalsy(); + }); + }); }); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/typeGuards/Scale.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/typeGuards/Scale.test.ts index e25eababaff1..dcd2087cda80 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/typeGuards/Scale.test.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-encodable/test/typeGuards/Scale.test.ts @@ -6,6 +6,7 @@ import { isTimeScale, isContinuousScaleConfig, isScaleConfigWithZero, + isContinuousScale, } from '../../src/typeGuards/Scale'; import { HasToString } from '../../src/types/Base'; @@ -43,6 +44,14 @@ describe('type guards', () => { expect(isCategoricalColorScale(scaleLinear())).toBeFalsy(); }); }); + describe('isContinuousScale(scale, type)', () => { + it('returns true if type is one of the time scale types', () => { + expect(isContinuousScale(scaleLinear(), 'linear')).toBeTruthy(); + }); + it('returns false otherwise', () => { + expect(isContinuousScale(scaleOrdinal(), 'ordinal')).toBeFalsy(); + }); + }); describe('isTimeScale(scale, type)', () => { it('returns true if type is one of the time scale types', () => { expect(isTimeScale(scaleTime(), 'time')).toBeTruthy();