Skip to content

Commit

Permalink
feat: Add types and type guards for encodeable (apache#201)
Browse files Browse the repository at this point in the history
* feat: scaffold superset-ui-encodeable

* feat: add type and typeguards

* fix: remove unused

* docs: update comments

* fix: address comments
  • Loading branch information
kristw authored and zhaoyongjie committed Nov 26, 2021
1 parent a7a12c7 commit cb9e617
Show file tree
Hide file tree
Showing 21 changed files with 482 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## @superset-ui/encodeable

[![Version](https://img.shields.io/npm/v/@superset-ui/encodeable.svg?style=flat)](https://img.shields.io/npm/v/@superset-ui/encodeable.svg?style=flat)
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui.svg?path=packages%2Fsuperset-ui-encodeable&style=flat-square)](https://david-dm.org/apache-superset/superset-ui?path=packages/superset-ui-encodeable)

Description

#### Example usage

```js
import { xxx } from '@superset-ui/encodeable';
```

#### API

`fn(args)`

- Do something

### Development

`@data-ui/build-config` is used to manage the build configuration for this package including babel
builds, jest testing, eslint, and prettier.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@superset-ui/encodeable",
"version": "0.0.0",
"description": "Superset UI encodeable",
"sideEffects": false,
"main": "lib/index.js",
"module": "esm/index.js",
"files": [
"esm",
"lib"
],
"repository": {
"type": "git",
"url": "git+https://github.com/apache-superset/superset-ui.git"
},
"keywords": ["superset"],
"author": "Superset",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/apache-superset/superset-ui/issues"
},
"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"
}
}
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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Value, ValueDef } from '../types/VegaLite';
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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/** See https://vega.github.io/vega-lite/docs/axis.html */

import { DateTime } from './VegaLite';

/** Axis orientation */
export type AxisOrient = 'top' | 'bottom' | 'left' | 'right';

/** Strategy for handling label overlap */
export type LabelOverlapStrategy = 'auto' | 'flat' | 'rotate';

export interface CoreAxis {
/** Tick label format */
format?: string;
/** Angle to rotate the tick labels */
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;
/** Strategy for handling label overlap */
labelOverlap: LabelOverlapStrategy;
/** The padding, in pixels, between axis and text labels. */
labelPadding: number;
/** Axis orientation */
orient: AxisOrient;
/** Estimated number of desired ticks */
tickCount: number;
/** Tick length */
tickSize?: number;
/** Axis title */
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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** Extract generic type from array */
export type Unarray<T> = T extends Array<infer U> ? U : T;

/** T or an array of T */
export type MayBeArray<T> = T | T[];
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { XFieldDef, YFieldDef, MarkPropChannelDef, TextChannelDef } from './ChannelDef';
import { Value } from './VegaLite';

/** 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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
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 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 ChannelDef<Output extends Value = Value> =
| ValueDef<Output>
| XFieldDef<Output>
| YFieldDef<Output>
| MarkPropFieldDef<Output>
| TextFieldDef;

/** Channel definitions that are not constant value */
export type NonValueDef<Output extends Value = Value> = Exclude<
ChannelDef<Output>,
ValueDef<Output>
>;

/** Pattern for extracting output type from channel definition */
export type ExtractChannelOutput<Def> = Def extends ChannelDef<infer Output> ? Output : never;
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>>[];
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type Legend = boolean | null;

export interface WithLegend {
legend?: Legend;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Value, DateTime, ScaleType, SchemeParams } from './VegaLite';

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

export interface WithScale<Output extends Value = Value> {
scale?: Scale<Output>;
}
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';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function identity<T>(x: T) {
return x;
}
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;
}
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);
}
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 cb9e617

Please sign in to comment.