Factor out NodeTypeConfig (#763)

This factors `NodeTypeConfig` out of the `WeightConfig` component. The
scope for a `NodeTypeConfig` is that it configures a single node type.
Right now it just renders a single `WeightSlider`, but I like factoring
out both for consistency with the `EdgeTypeConfig` (see #749) and
because I expect we may want to add more complexity later.

Test plan: The new component has some tests, also I manually tested the
frontend.
This commit is contained in:
Dandelion Mané 2018-09-04 12:52:53 -07:00 committed by GitHub
parent 44407b5520
commit 4cd45c77fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 108 additions and 38 deletions

View File

@ -6,9 +6,14 @@ import sortBy from "lodash.sortby";
import {type EdgeEvaluator} from "../../core/attribution/pagerank"; import {type EdgeEvaluator} from "../../core/attribution/pagerank";
import {byEdgeType, byNodeType} from "./edgeWeights"; import {byEdgeType, byNodeType} from "./edgeWeights";
import {defaultStaticAdapters} from "../adapters/defaultPlugins"; import {defaultStaticAdapters} from "../adapters/defaultPlugins";
import type {NodeType, EdgeType} from "../adapters/pluginAdapter"; import type {EdgeType} from "../adapters/pluginAdapter";
import {WeightSlider} from "./weights/WeightSlider"; import {WeightSlider} from "./weights/WeightSlider";
import {DirectionalitySlider} from "./weights/DirectionalitySlider"; import {DirectionalitySlider} from "./weights/DirectionalitySlider";
import {
NodeTypeConfig,
defaultWeightedNodeType,
type WeightedNodeType,
} from "./weights/NodeTypeConfig";
type Props = {| type Props = {|
+onChange: (EdgeEvaluator) => void, +onChange: (EdgeEvaluator) => void,
@ -28,19 +33,9 @@ const defaultEdgeWeights = (): EdgeWeights => {
return result; return result;
}; };
type NodeWeights = WeightedNodeType[];
type WeightedNodeType = {|+type: NodeType, +weight: number|};
const defaultNodeWeights = (): NodeWeights => {
const result = [];
for (const type of defaultStaticAdapters().nodeTypes()) {
result.push({type, weight: type.defaultWeight});
}
return result;
};
type State = { type State = {
edgeWeights: EdgeWeights, edgeWeights: EdgeWeights,
nodeWeights: NodeWeights, nodeWeights: $ReadOnlyArray<WeightedNodeType>,
expanded: boolean, expanded: boolean,
}; };
@ -49,7 +44,9 @@ export class WeightConfig extends React.Component<Props, State> {
super(props); super(props);
this.state = { this.state = {
edgeWeights: defaultEdgeWeights(), edgeWeights: defaultEdgeWeights(),
nodeWeights: defaultNodeWeights(), nodeWeights: defaultStaticAdapters()
.nodeTypes()
.map(defaultWeightedNodeType),
expanded: false, expanded: false,
}; };
} }
@ -175,36 +172,30 @@ class EdgeConfig extends React.Component<{
} }
class NodeConfig extends React.Component<{ class NodeConfig extends React.Component<{
nodeWeights: NodeWeights, nodeWeights: $ReadOnlyArray<WeightedNodeType>,
onChange: (NodeWeights) => void, onChange: ($ReadOnlyArray<WeightedNodeType>) => void,
}> { }> {
render() { _renderControls() {
const sortedWeights = sortBy( return sortBy(this.props.nodeWeights, ({type}) => type.prefix).map(
this.props.nodeWeights, (weightedType) => {
({type}) => type.prefix const onChange = (newType) => {
);
const controls = sortedWeights.map(({type, weight}) => {
const onChange = (value) => {
const nodeWeights = this.props.nodeWeights.filter( const nodeWeights = this.props.nodeWeights.filter(
(x) => x.type.prefix !== type.prefix (x) => x.type.prefix !== weightedType.type.prefix
); );
nodeWeights.push({type, weight: value}); nodeWeights.push(newType);
this.props.onChange(nodeWeights); this.props.onChange(nodeWeights);
}; };
return ( return (
<WeightSlider <NodeTypeConfig weightedType={weightedType} onChange={onChange} />
key={type.prefix}
weight={weight}
name={type.name}
onChange={onChange}
/>
); );
}); }
);
}
render() {
return ( return (
<div> <div>
<h2>Node weights</h2> <h2>Node weights</h2>
{controls} {this._renderControls()}
</div> </div>
); );
} }

View File

@ -0,0 +1,34 @@
// @flow
import React from "react";
import {WeightSlider} from "./WeightSlider";
import {type NodeType} from "../../adapters/pluginAdapter";
export type WeightedNodeType = {|+type: NodeType, +weight: number|};
export function defaultWeightedNodeType(type: NodeType): WeightedNodeType {
return {
type,
weight: type.defaultWeight,
};
}
export class NodeTypeConfig extends React.Component<{
+weightedType: WeightedNodeType,
+onChange: (WeightedNodeType) => void,
}> {
render() {
return (
<WeightSlider
name={this.props.weightedType.type.name}
weight={this.props.weightedType.weight}
onChange={(weight) => {
this.props.onChange({
...this.props.weightedType,
weight,
});
}}
/>
);
}
}

View File

@ -0,0 +1,45 @@
// @flow
import React from "react";
import {shallow} from "enzyme";
import {WeightSlider} from "./WeightSlider";
import {defaultWeightedNodeType, NodeTypeConfig} from "./NodeTypeConfig";
import {inserterNodeType} from "../../adapters/demoAdapters";
require("../../testUtil").configureEnzyme();
describe("app/credExplorer/weights/NodeTypeConfig", () => {
describe("defaultWeightedNodeType", () => {
it("sets default weight as specified in type", () => {
const wnt = defaultWeightedNodeType(inserterNodeType);
expect(wnt.weight).toEqual(wnt.type.defaultWeight);
});
});
describe("NodeTypeConfig", () => {
function example() {
const onChange = jest.fn();
const wnt = {
type: inserterNodeType,
weight: 0.125,
};
const element = shallow(
<NodeTypeConfig onChange={onChange} weightedType={wnt} />
);
const slider = element.find(WeightSlider);
return {onChange, wnt, slider};
}
it("sets up the weight slider", () => {
const {wnt, slider} = example();
expect(slider.props().name).toBe(wnt.type.name);
expect(slider.props().weight).toBe(wnt.weight);
});
it("weight slider onChange works", () => {
const {wnt, slider, onChange} = example();
slider.props().onChange(9);
const updated = {...wnt, weight: 9};
expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange.mock.calls[0][0]).toEqual(updated);
});
});
});