Skip to content

Commit

Permalink
Fix inconsistent Sankey behavior (#5286)
Browse files Browse the repository at this point in the history
* added type casting to coerce number string into nuber

* Merge branch 'master' into fix-inconsistent=sankey-behavior

* typed map viz options

* Partially typed what was possible

* reworked data coercion

* improved MapOptionsType types

* readaqueted sankey rows so as to allow strings again
  • Loading branch information
rafawendel authored Jan 13, 2021
1 parent 84c2abe commit 7d33af4
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 70 deletions.
29 changes: 27 additions & 2 deletions viz-lib/src/visualizations/map/getOptions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
import { merge } from "lodash";

const DEFAULT_OPTIONS = {
export type LeafletBaseIconType = "marker" | "rectangle" | "circle" | "rectangle-dot" | "circle-dot" | "doughnut";
export interface MapOptionsType {
latColName: string;
lonColName: string;
classify: any;
groups: Record<string, any>;
mapTileUrl: string;
clusterMarkers: boolean;
customizeMarkers: boolean;
iconShape: LeafletBaseIconType;
iconFont: LeafletBaseIconType;
foregroundColor: string;
backgroundColor: string;
borderColor: string;
bounds: any;
tooltip: {
enabled: boolean;
template: string;
};
popup: {
enabled: boolean;
template: string;
};
}

const DEFAULT_OPTIONS: MapOptionsType = {
latColName: "lat",
lonColName: "lon",
classify: null,
Expand All @@ -24,7 +49,7 @@ const DEFAULT_OPTIONS = {
},
};

export default function getOptions(options: any) {
export default function getOptions(options: MapOptionsType) {
options = merge({}, DEFAULT_OPTIONS, options);
options.mapTileUrl = options.mapTileUrl || DEFAULT_OPTIONS.mapTileUrl;

Expand Down
12 changes: 5 additions & 7 deletions viz-lib/src/visualizations/sankey/Renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import React, { useState, useEffect, useMemo } from "react";
import resizeObserver from "@/services/resizeObserver";
import { RendererPropTypes } from "@/visualizations/prop-types";

import initSankey from "./initSankey";
import { SankeyDataType } from "./index";
import initSankey, { ExtendedSankeyDataType } from "./initSankey";
import "./renderer.less";

export default function Renderer({
data
}: any) {
const [container, setContainer] = useState(null);
export default function Renderer({ data }: { data: SankeyDataType }) {
const [container, setContainer] = useState<null | HTMLDivElement>(null);

const render = useMemo(() => initSankey(data), [data]);
const render = useMemo(() => initSankey(data as ExtendedSankeyDataType), [data]);

useEffect(() => {
if (container) {
Expand All @@ -22,7 +21,6 @@ export default function Renderer({
}
}, [container, render]);

// @ts-expect-error ts-migrate(2322) FIXME: Type 'Dispatch<SetStateAction<null>>' is not assig... Remove this comment to see the full error message
return <div className="sankey-visualization-container" ref={setContainer} />;
}

Expand Down
53 changes: 38 additions & 15 deletions viz-lib/src/visualizations/sankey/d3sankey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,38 @@

import d3 from "d3";

export interface LinkType {
id: number;
name: string;
color: string;
x: number;
y: number;
dx: number;
dy: number;
source: SourceTargetType;
target: SourceTargetType;
}

export type SourceTargetType = {
sourceLinks: Array<LinkType>;
targetLinks: Array<LinkType>;
};

export type NodeType = LinkType & SourceTargetType;
export interface D3SankeyType {
nodeWidth: (...args: any[]) => any;
nodeHeight: (...args: any[]) => any;
nodePadding: (...args: any[]) => any;
nodes: (...args: any[]) => any[];
link: (...args: any[]) => any;
links: (...args: any[]) => any[];
size: (...args: any[]) => any;
layout: (...args: any[]) => any;
relayout: (...args: any[]) => any;
}

export type DType = { sy: number; ty: number; value: number; source: LinkType; target: LinkType } & LinkType;

function center(node: any) {
return node.y + node.dy / 2;
}
Expand All @@ -10,23 +42,21 @@ function value(link: any) {
return link.value;
}

function Sankey() {
function Sankey(): D3SankeyType {
const sankey = {};
let nodeWidth = 24;
let nodePadding = 8;
let size = [1, 1];
let nodes: any = [];
let links: any = [];
let nodes: any[] = [];
let links: any[] = [];

// Populate the sourceLinks and targetLinks for each node.
// Also, if the source and target are not objects, assume they are indices.
function computeNodeLinks() {
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'node' implicitly has an 'any' type.
nodes.forEach(node => {
node.sourceLinks = [];
node.targetLinks = [];
});
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'link' implicitly has an 'any' type.
links.forEach(link => {
let source = link.source;
let target = link.target;
Expand All @@ -39,14 +69,12 @@ function Sankey() {

// Compute the value (size) of each node by summing the associated links.
function computeNodeValues() {
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'node' implicitly has an 'any' type.
nodes.forEach(node => {
node.value = Math.max(d3.sum(node.sourceLinks, value), d3.sum(node.targetLinks, value));
});
}

function moveSinksRight(x: any) {
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'node' implicitly has an 'any' type.
nodes.forEach(node => {
if (!node.sourceLinks.length) {
node.x = x - 1;
Expand All @@ -55,7 +83,6 @@ function Sankey() {
}

function scaleNodeBreadths(kx: any) {
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'node' implicitly has an 'any' type.
nodes.forEach(node => {
node.x *= kx;
});
Expand Down Expand Up @@ -89,7 +116,6 @@ function Sankey() {

moveSinksRight(x);
x = Math.max(
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message
d3.max(nodes, n => n.x),
2
); // get new maximum x value (min 2)
Expand All @@ -98,7 +124,7 @@ function Sankey() {

function computeNodeDepths(iterations: any) {
const nodesByBreadth = d3
// @ts-expect-error ts-migrate(2339) FIXME: Property 'nest' does not exist on type 'typeof imp... Remove this comment to see the full error message
// @ts-expect-error
.nest()
.key((d: any) => d.x)
.sortKeys(d3.ascending)
Expand All @@ -117,7 +143,6 @@ function Sankey() {
});
});

// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'link' implicitly has an 'any' type.
links.forEach(link => {
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
link.dy = link.value * ky;
Expand Down Expand Up @@ -206,12 +231,10 @@ function Sankey() {
}

function computeLinkDepths() {
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'node' implicitly has an 'any' type.
nodes.forEach(node => {
node.sourceLinks.sort(ascendingTargetDepth);
node.targetLinks.sort(ascendingSourceDepth);
});
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'node' implicitly has an 'any' type.
nodes.forEach(node => {
let sy = 0,
ty = 0;
Expand Down Expand Up @@ -289,7 +312,7 @@ function Sankey() {
sankey.link = function() {
let curvature = 0.5;

function link(d: any) {
function link(d: DType) {
const x0 = d.source.x + d.source.dx;
const x1 = d.target.x;
const xi = d3.interpolateNumber(x0, x1);
Expand All @@ -310,7 +333,7 @@ function Sankey() {
return link;
};

return sankey;
return sankey as D3SankeyType;
}

export default Sankey;
16 changes: 14 additions & 2 deletions viz-lib/src/visualizations/sankey/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import Renderer from "./Renderer";
import Editor from "./Editor";
export interface SankeyDataType {
columns: {
name: string;
friendly_name: string;
type: "integer";
}[];

rows: {
value: number;
[name: string]: number | string | null;
}[];
}

export default {
type: "SANKEY",
name: "Sankey",
getOptions: (options: any) => ({
...options
getOptions: (options: {}) => ({
...options,
}),
Renderer,
Editor,
Expand Down
Loading

0 comments on commit 7d33af4

Please sign in to comment.