Skip to content
This repository has been archived by the owner on Dec 10, 2021. It is now read-only.

Commit

Permalink
feat: add type and typeguards
Browse files Browse the repository at this point in the history
  • Loading branch information
kristw committed Aug 13, 2019
1 parent 7074046 commit cc9c369
Show file tree
Hide file tree
Showing 20 changed files with 421 additions and 0 deletions.
9 changes: 9 additions & 0 deletions packages/superset-ui-encodeable/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,14 @@
"homepage": "https://github.com/apache-superset/superset-ui#readme",
"publishConfig": {
"access": "public"
},
"private": true,
"dependencies": {
"vega": "^5.4.0",
"vega-lite": "^3.4.0"
},
"peerDependencies": {
"@superset-ui/time-format": "^0.11.14",
"@superset-ui/number-format": "^0.11.14"
}
}
6 changes: 6 additions & 0 deletions packages/superset-ui-encodeable/src/typeGuards/Axis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Axis } from '../types/Axis';

// eslint-disable-next-line import/prefer-default-export
export function isAxis(axis: Axis | null | undefined | false): axis is Axis {
return axis !== false && axis !== null && axis !== undefined;
}
11 changes: 11 additions & 0 deletions packages/superset-ui-encodeable/src/typeGuards/Base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export function isArray<T>(maybeArray: T | T[]): maybeArray is T[] {
return Array.isArray(maybeArray);
}

export function isNotArray<T>(maybeArray: T | T[]): maybeArray is T {
return !Array.isArray(maybeArray);
}

export function isDefined<T>(value: any): value is T {
return typeof value !== 'undefined' && value !== null;
}
45 changes: 45 additions & 0 deletions packages/superset-ui-encodeable/src/typeGuards/ChannelDef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Value, ValueDef } from '../types/Inherit';
import {
ChannelDef,
NonValueDef,
FieldDef,
TypedFieldDef,
PositionFieldDef,
ScaleFieldDef,
} from '../types/ChannelDef';

export function isValueDef<Output extends Value>(
channelDef: ChannelDef<Output>,
): channelDef is ValueDef<Output> {
return channelDef && 'value' in channelDef;
}

export function isNonValueDef<Output extends Value>(
channelDef: ChannelDef<Output>,
): channelDef is NonValueDef<Output> {
return channelDef && !('value' in channelDef);
}

export function isFieldDef<Output extends Value>(
channelDef: ChannelDef<Output>,
): channelDef is FieldDef {
return channelDef && 'field' in channelDef && !!channelDef.field;
}

export function isTypedFieldDef<Output extends Value>(
channelDef: ChannelDef<Output>,
): channelDef is TypedFieldDef {
return isFieldDef(channelDef) && 'type' in channelDef && !!channelDef.type;
}

export function isScaleFieldDef<Output extends Value>(
channelDef: ChannelDef<Output>,
): channelDef is ScaleFieldDef<Output> {
return channelDef && 'scale' in channelDef;
}

export function isPositionFieldDef<Output extends Value>(
channelDef: ChannelDef<Output>,
): channelDef is PositionFieldDef<Output> {
return channelDef && 'axis' in channelDef;
}
56 changes: 56 additions & 0 deletions packages/superset-ui-encodeable/src/types/Axis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/** See https://vega.github.io/vega-lite/docs/axis.html */

import { DateTime } from './Inherit';

export type AxisOrient = 'top' | 'bottom' | 'left' | 'right';

export type LabelOverlapStrategy = 'auto' | 'flat' | 'rotate';

export interface CoreAxis {
format?: string;
labelAngle: number;
/**
* Indicates if the first and last axis labels should be aligned flush with the scale range.
* Flush alignment for a horizontal axis will left-align the first label and right-align the last label.
* For vertical axes, bottom and top text baselines are applied instead.
* If this property is a number, it also indicates the number of pixels by which to offset the first and last labels;
* for example, a value of 2 will flush-align the first and last labels
* and also push them 2 pixels outward from the center of the axis.
* The additional adjustment can sometimes help the labels better visually group with corresponding axis ticks. */
labelFlush?: boolean | number;
labelOverlap: LabelOverlapStrategy;
/** The padding, in pixels, between axis and text labels. */
labelPadding: number;
orient: AxisOrient;
tickCount: number;
tickSize?: number;
title?: string | boolean;
/** Explicitly set the visible axis tick values. */
values?: string[] | number[] | boolean[] | DateTime[];
}

export type Axis = Partial<CoreAxis>;

export interface XAxis extends Axis {
orient?: 'top' | 'bottom';
labelAngle?: number;
labelOverlap?: LabelOverlapStrategy;
}

export interface WithXAxis {
axis?: XAxis | boolean;
}

export interface YAxis extends Axis {
orient?: 'left' | 'right';
labelAngle?: 0;
labelOverlap?: 'auto' | 'flat';
}

export interface WithYAxis {
axis?: YAxis;
}

export interface WithAxis {
axis?: XAxis | YAxis;
}
3 changes: 3 additions & 0 deletions packages/superset-ui-encodeable/src/types/Base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type Unarray<T> = T extends Array<infer U> ? U : T;

export type MayBeArray<T> = T | T[];
29 changes: 29 additions & 0 deletions packages/superset-ui-encodeable/src/types/Channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { XFieldDef, YFieldDef, MarkPropChannelDef, TextChannelDef } from './ChannelDef';
import { Value } from './Inherit';

/** Possible input for a channel */
export type ChannelInput = number | string | boolean | null | Date | undefined;

/**
* Define all channel types and mapping to channel definition grammar
*/
export interface ChannelTypeToDefMap<Output extends Value = Value> {
/** position on x-axis */
X: XFieldDef<Output>;
/** position on y-axis */
Y: YFieldDef<Output>;
/** position on x-axis but as a range, e.g., bar chart or heat map */
XBand: XFieldDef<Output>;
/** position on y-axis but as a range, e.g., bar chart or heat map */
YBand: YFieldDef<Output>;
/** numeric attributes of the mark, e.g., size, opacity */
Numeric: MarkPropChannelDef<Output>;
/** categorical attributes of the mark, e.g., color, visibility, shape */
Category: MarkPropChannelDef<Output>;
/** color of the mark */
Color: MarkPropChannelDef<Output>;
/** plain text, e.g., tooltip, key */
Text: TextChannelDef<Output>;
}

export type ChannelType = keyof ChannelTypeToDefMap;
50 changes: 50 additions & 0 deletions packages/superset-ui-encodeable/src/types/ChannelDef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { TimeFormatter } from '@superset-ui/time-format';
import { NumberFormatter } from '@superset-ui/number-format';
import { ValueDef, Value, Type } from './Inherit';
import { WithScale } from './Scale';
import { WithXAxis, WithYAxis, WithAxis } from './Axis';
import { WithLegend } from './Legend';

export type Formatter = NumberFormatter | TimeFormatter | ((d: any) => string);

export interface FieldDef {
field: string;
format?: string;
title?: string;
}

export interface TypedFieldDef extends FieldDef {
type: Type;
}

export type TextFieldDef = FieldDef;

export type ScaleFieldDef<Output extends Value = Value> = TypedFieldDef & WithScale<Output>;

export type MarkPropFieldDef<Output extends Value = Value> = ScaleFieldDef<Output> & WithLegend;

// PositionFieldDef is { field: 'fieldName', scale: xxx, axis: xxx }

type PositionFieldDefBase<Output extends Value = Value> = ScaleFieldDef<Output>;

export type XFieldDef<Output extends Value = Value> = PositionFieldDefBase<Output> & WithXAxis;

export type YFieldDef<Output extends Value = Value> = PositionFieldDefBase<Output> & WithYAxis;

export type PositionFieldDef<Output extends Value = Value> = ScaleFieldDef<Output> & WithAxis;

export type MarkPropChannelDef<Output extends Value = Value> =
| MarkPropFieldDef<Output>
| ValueDef<Output>;

export type TextChannelDef<Output extends Value = Value> = TextFieldDef | ValueDef<Output>;

export type NonValueDef<Output extends Value = Value> =
| XFieldDef<Output>
| YFieldDef<Output>
| MarkPropFieldDef<Output>
| TextFieldDef;

export type ChannelDef<Output extends Value = Value> = NonValueDef<Output> | ValueDef<Output>;

export type ExtractChannelOutput<Def> = Def extends ChannelDef<infer Output> ? Output : never;
5 changes: 5 additions & 0 deletions packages/superset-ui-encodeable/src/types/Data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type PlainObject<Key extends string = string, Value extends any = any> = {
[key in Key]: Value;
};

export type Dataset<T extends string = string> = Partial<PlainObject<T>>[];
6 changes: 6 additions & 0 deletions packages/superset-ui-encodeable/src/types/Inherit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Types directly imported from vega-lite

export { ValueDef, Value } from 'vega-lite/build/src/channeldef';
export { DateTime } from 'vega-lite/build/src/datetime';
export { SchemeParams, ScaleType } from 'vega-lite/build/src/scale';
export { Type } from 'vega-lite/build/src/type';
5 changes: 5 additions & 0 deletions packages/superset-ui-encodeable/src/types/Legend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type Legend = boolean | null;

export interface WithLegend {
legend?: Legend;
}
18 changes: 18 additions & 0 deletions packages/superset-ui-encodeable/src/types/Scale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Value, DateTime, ScaleType, SchemeParams } from './Inherit';

export interface Scale<Output extends Value = Value> {
type?: ScaleType;
domain?: number[] | string[] | boolean[] | DateTime[];
paddingInner?: number;
paddingOuter?: number;
range?: Output[];
clamp?: boolean;
nice?: boolean;
scheme?: string | SchemeParams;
/** vega-lite does not have this */
namespace?: string;
}

export interface WithScale<Output extends Value = Value> {
scale?: Scale<Output>;
}
3 changes: 3 additions & 0 deletions packages/superset-ui-encodeable/src/utils/identity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function identity<T>(x: T) {
return x;
}
11 changes: 11 additions & 0 deletions packages/superset-ui-encodeable/src/utils/isDisabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function isDisabled(
config:
| {
[key: string]: any;
}
| boolean
| null
| undefined,
) {
return config === false || config === null;
}
13 changes: 13 additions & 0 deletions packages/superset-ui-encodeable/src/utils/isEnabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import isDisabled from './isDisabled';

export default function isEnabled(
config:
| {
[key: string]: any;
}
| boolean
| null
| undefined,
) {
return !isDisabled(config);
}
40 changes: 40 additions & 0 deletions packages/superset-ui-encodeable/test/typeGuards/Base.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { isDefined, isArray, isNotArray } from '../../src/typeGuards/Base';

describe('type guards: Base', () => {
describe('isArray<T>(maybeArray)', () => {
it('returns true and converts to type T[] if is array', () => {
const x: string | string[] = ['abc'];
if (isArray(x)) {
// x is now known to be an array
expect(x[0]).toEqual('abc');
}
});
it('returns false if not', () => {
expect(isArray('abc')).toBeFalsy();
});
});
describe('isNotArray<T>(maybeArray)', () => {
it('returns true and converts to type T if not array', () => {
const x: string | string[] = 'abc';
if (isNotArray(x)) {
// x is now known to be a string
expect(x.startsWith('a')).toBeTruthy();
}
});
it('returns false if is array', () => {
expect(isNotArray(['def'])).toBeFalsy();
});
});
describe('isDefined<T>(value)', () => {
it('returns true and converts to type T if value is defined', () => {
const x: any = 'abc';
if (isDefined<string>(x)) {
expect(x.startsWith('a')).toBeTruthy();
}
});
it('returns false if not defined', () => {
expect(isDefined(null)).toBeFalsy();
expect(isDefined(undefined)).toBeFalsy();
});
});
});
Loading

0 comments on commit cc9c369

Please sign in to comment.