PagerankTable: Replace topLevelFilter with NodeType in props (#1103)
The motivation for this change is to make it easier to access the selected Node's `name` prop for #576, in which we plan to show a Card displaying summary stats for the selected node. With only the `topLevelFilter` available, it's trickier than it needs to be to find out a node type's `name`. Test Plan: * Yarn test passes. * Visual/Manual inspection of table doesn't surface any issues. * Updated `it("filter defaults to defaultNodeFilter if available")` to `it("selectedNodeType defaults to defaultNodeType if available")`. * Verified that the above new test is failable in several ways by mangling the tests to test for the wrong node type and mangling the code to set the wrong node type. * Since we factored out 'topLevelFilter' and 'defaultNodeFilter', running `git grep -i topLevelFilter` and `git grep -i defaultNodeFilter` turns up empty, just to make sure those terms aren't hanging around to confuse anybody in the future. * I don't think changing the `prop` parameter warrants any additional tests, as the current tests verify that the prop is passed in correctly. This was at @decentralion's suggestion, following the Contributing Guideline's Kent Beck quote of making the easy change to make the change we were originally after (#576) easier. 🙌
This commit is contained in:
parent
c353efff36
commit
42669cd160
|
@ -20,6 +20,7 @@ import {
|
|||
initialState,
|
||||
} from "./state";
|
||||
import {StaticExplorerAdapterSet} from "./adapters/explorerAdapterSet";
|
||||
import {userNodeType} from "../plugins/github/declaration";
|
||||
|
||||
const credOverviewUrl =
|
||||
"https://discuss.sourcecred.io/t/a-gentle-introduction-to-cred/20";
|
||||
|
@ -91,7 +92,7 @@ export function createApp(
|
|||
const pnd = appState.pagerankNodeDecomposition;
|
||||
pagerankTable = (
|
||||
<PagerankTable
|
||||
defaultNodeFilter={GithubPrefix.user}
|
||||
defaultNodeType={userNodeType}
|
||||
adapters={adapters}
|
||||
weightedTypes={this.state.weightedTypes}
|
||||
onWeightedTypesChange={(weightedTypes) =>
|
||||
|
|
|
@ -4,15 +4,16 @@ import React from "react";
|
|||
import sortBy from "lodash.sortby";
|
||||
import * as NullUtil from "../../util/null";
|
||||
|
||||
import {type NodeAddressT, NodeAddress} from "../../core/graph";
|
||||
import {NodeAddress} from "../../core/graph";
|
||||
import type {PagerankNodeDecomposition} from "../../analysis/pagerankNodeDecomposition";
|
||||
import {DynamicExplorerAdapterSet} from "../adapters/explorerAdapterSet";
|
||||
import type {DynamicExplorerAdapter} from "../adapters/explorerAdapter";
|
||||
import {FALLBACK_NAME} from "../../analysis/fallbackDeclaration";
|
||||
import type {WeightedTypes} from "../../analysis/weights";
|
||||
import {WeightConfig} from "../weights/WeightConfig";
|
||||
|
||||
import {NodeRowList} from "./Node";
|
||||
import {type NodeType} from "../../analysis/types";
|
||||
import {fallbackNodeType} from "../../analysis/fallbackDeclaration";
|
||||
|
||||
type PagerankTableProps = {|
|
||||
+pnd: PagerankNodeDecomposition,
|
||||
|
@ -20,10 +21,10 @@ type PagerankTableProps = {|
|
|||
+weightedTypes: WeightedTypes,
|
||||
+onWeightedTypesChange: (WeightedTypes) => void,
|
||||
+maxEntriesPerList: number,
|
||||
+defaultNodeFilter: ?NodeAddressT,
|
||||
+defaultNodeType: ?NodeType,
|
||||
|};
|
||||
type PagerankTableState = {|
|
||||
topLevelFilter: NodeAddressT,
|
||||
selectedNodeType: NodeType,
|
||||
showWeightConfig: boolean,
|
||||
|};
|
||||
export class PagerankTable extends React.PureComponent<
|
||||
|
@ -32,20 +33,11 @@ export class PagerankTable extends React.PureComponent<
|
|||
> {
|
||||
constructor(props: PagerankTableProps): void {
|
||||
super();
|
||||
const {defaultNodeFilter, adapters} = props;
|
||||
if (defaultNodeFilter != null) {
|
||||
const nodeTypes = adapters.static().nodeTypes();
|
||||
if (!nodeTypes.some((x) => x.prefix === defaultNodeFilter)) {
|
||||
throw new Error(
|
||||
`invalid defaultNodeFilter ${defaultNodeFilter}: doesn't match any type`
|
||||
);
|
||||
}
|
||||
}
|
||||
const topLevelFilter = NullUtil.orElse(
|
||||
props.defaultNodeFilter,
|
||||
NodeAddress.empty
|
||||
const selectedNodeType = NullUtil.orElse(
|
||||
props.defaultNodeType,
|
||||
fallbackNodeType
|
||||
);
|
||||
this.state = {topLevelFilter, showWeightConfig: false};
|
||||
this.state = {selectedNodeType, showWeightConfig: false};
|
||||
}
|
||||
|
||||
renderConfigurationRow() {
|
||||
|
@ -116,9 +108,11 @@ export class PagerankTable extends React.PureComponent<
|
|||
<label>
|
||||
<span>Filter by node type: </span>
|
||||
<select
|
||||
value={this.state.topLevelFilter}
|
||||
value={this.state.selectedNodeType.prefix}
|
||||
onChange={(e) => {
|
||||
this.setState({topLevelFilter: e.target.value});
|
||||
const nodePrefix = e.target.value;
|
||||
const nodeType = adapters.static().typeMatchingNode(nodePrefix);
|
||||
this.setState({selectedNodeType: nodeType});
|
||||
}}
|
||||
>
|
||||
<option value={NodeAddress.empty}>Show all</option>
|
||||
|
@ -138,7 +132,7 @@ export class PagerankTable extends React.PureComponent<
|
|||
if (pnd == null || adapters == null || maxEntriesPerList == null) {
|
||||
throw new Error("Impossible.");
|
||||
}
|
||||
const topLevelFilter = this.state.topLevelFilter;
|
||||
const selectedNodeTypePrefix = this.state.selectedNodeType.prefix;
|
||||
const sharedProps = {pnd, adapters, maxEntriesPerList};
|
||||
return (
|
||||
<table
|
||||
|
@ -161,7 +155,7 @@ export class PagerankTable extends React.PureComponent<
|
|||
<NodeRowList
|
||||
sharedProps={sharedProps}
|
||||
nodes={Array.from(pnd.keys()).filter((node) =>
|
||||
NodeAddress.hasPrefix(node, topLevelFilter)
|
||||
NodeAddress.hasPrefix(node, selectedNodeTypePrefix)
|
||||
)}
|
||||
/>
|
||||
</tbody>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import React from "react";
|
||||
import {shallow} from "enzyme";
|
||||
|
||||
import {NodeAddress, type NodeAddressT} from "../../core/graph";
|
||||
import {NodeAddress} from "../../core/graph";
|
||||
|
||||
import {PagerankTable} from "./Table";
|
||||
import {example, COLUMNS} from "./sharedTestUtils";
|
||||
|
@ -11,17 +11,19 @@ import {NodeRowList} from "./Node";
|
|||
import {WeightConfig} from "../weights/WeightConfig";
|
||||
import {defaultWeightsForAdapter} from "../weights/weights";
|
||||
import {FactorioStaticAdapter} from "../../plugins/demo/explorerAdapter";
|
||||
import {type NodeType} from "../../analysis/types";
|
||||
import {fallbackNodeType} from "../../analysis/fallbackDeclaration";
|
||||
|
||||
require("../../webutil/testUtil").configureEnzyme();
|
||||
describe("explorer/pagerankTable/Table", () => {
|
||||
describe("PagerankTable", () => {
|
||||
async function setup(defaultNodeFilter?: NodeAddressT) {
|
||||
async function setup(defaultNodeType?: NodeType) {
|
||||
const {pnd, adapters, weightedTypes} = await example();
|
||||
const onWeightedTypesChange = jest.fn();
|
||||
const maxEntriesPerList = 321;
|
||||
const element = shallow(
|
||||
<PagerankTable
|
||||
defaultNodeFilter={defaultNodeFilter}
|
||||
defaultNodeType={defaultNodeType}
|
||||
weightedTypes={weightedTypes}
|
||||
onWeightedTypesChange={onWeightedTypesChange}
|
||||
pnd={pnd}
|
||||
|
@ -124,20 +126,20 @@ describe("explorer/pagerankTable/Table", () => {
|
|||
);
|
||||
expect(actualNodes).not.toHaveLength(0);
|
||||
});
|
||||
it("filter defaults to show all if defaultNodeFilter not passed", async () => {
|
||||
it("filter defaults to show all if defaultNodeType not passed", async () => {
|
||||
const {element} = await setup();
|
||||
expect(element.state().topLevelFilter).toEqual(NodeAddress.empty);
|
||||
expect(element.state().selectedNodeType).toEqual(fallbackNodeType);
|
||||
});
|
||||
it("filter defaults to defaultNodeFilter if available", async () => {
|
||||
const filter = NodeAddress.fromParts(["factorio", "inserter"]);
|
||||
const {element} = await setup(filter);
|
||||
expect(element.state().topLevelFilter).toEqual(filter);
|
||||
});
|
||||
it("raises an error if defaultNodeFilter doesn't match any node type", async () => {
|
||||
const badFilter = NodeAddress.fromParts(["doesn't", "exist"]);
|
||||
await expect(setup(badFilter)).rejects.toThrow(
|
||||
"invalid defaultNodeFilter"
|
||||
);
|
||||
it("selectedNodeType defaults to defaultNodeType if available", async () => {
|
||||
const nodeType: NodeType = {
|
||||
name: "testNodeType",
|
||||
pluralName: "testNodeTypes",
|
||||
prefix: NodeAddress.empty,
|
||||
defaultWeight: 1,
|
||||
description: "test type",
|
||||
};
|
||||
const {element} = await setup(nodeType);
|
||||
expect(element.state().selectedNodeType).toEqual(nodeType);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ const commentNodeType = Object.freeze({
|
|||
description: "NodeType for a GitHub comment",
|
||||
});
|
||||
|
||||
const userNodeType = Object.freeze({
|
||||
export const userNodeType = Object.freeze({
|
||||
name: "User",
|
||||
pluralName: "Users",
|
||||
prefix: N.Prefix.user,
|
||||
|
|
Loading…
Reference in New Issue