PagerankTable filter defaults to GitHub users (#653)

We humans tend to find information about humans more interesting than
information about commits or pulls. The UI should accomodate this by
defaulting to displaying GitHub user nodes in the cred explorer.

This is implemented as a new nullable argument to the PageRankTable. If
not present, then the filter defaults to showing all nodes. If the
default filter is present but doesn't match any available type, an error
is thrown.

Test plan: The new behavior is tested. Also, I checked it in the UI and
it works.

Closes #651
This commit is contained in:
Dandelion Mané 2018-08-15 11:44:44 -07:00 committed by GitHub
parent e3a4d1f2b9
commit f100cd02cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 6 deletions

View File

@ -9,6 +9,7 @@ import BrowserLocalStore from "../browserLocalStore";
import {PagerankTable} from "./pagerankTable/Table"; import {PagerankTable} from "./pagerankTable/Table";
import {WeightConfig} from "./WeightConfig"; import {WeightConfig} from "./WeightConfig";
import RepositorySelect from "./RepositorySelect"; import RepositorySelect from "./RepositorySelect";
import {_Prefix as GithubPrefix} from "../../plugins/github/nodes";
import { import {
createStateTransitionMachine, createStateTransitionMachine,
type AppState, type AppState,
@ -71,6 +72,7 @@ export function createApp(
const pnd = appState.substate.pagerankNodeDecomposition; const pnd = appState.substate.pagerankNodeDecomposition;
pagerankTable = ( pagerankTable = (
<PagerankTable <PagerankTable
defaultNodeFilter={GithubPrefix.userlike}
adapters={adapters} adapters={adapters}
pnd={pnd} pnd={pnd}
maxEntriesPerList={100} maxEntriesPerList={100}

View File

@ -2,6 +2,7 @@
import React from "react"; import React from "react";
import sortBy from "lodash.sortby"; import sortBy from "lodash.sortby";
import * as NullUtil from "../../../util/null";
import {type NodeAddressT, NodeAddress} from "../../../core/graph"; import {type NodeAddressT, NodeAddress} from "../../../core/graph";
import type {PagerankNodeDecomposition} from "../../../core/attribution/pagerankNodeDecomposition"; import type {PagerankNodeDecomposition} from "../../../core/attribution/pagerankNodeDecomposition";
@ -15,15 +16,29 @@ type PagerankTableProps = {|
+pnd: PagerankNodeDecomposition, +pnd: PagerankNodeDecomposition,
+adapters: DynamicAdapterSet, +adapters: DynamicAdapterSet,
+maxEntriesPerList: number, +maxEntriesPerList: number,
+defaultNodeFilter: ?NodeAddressT,
|}; |};
type PagerankTableState = {|topLevelFilter: NodeAddressT|}; type PagerankTableState = {|topLevelFilter: NodeAddressT|};
export class PagerankTable extends React.PureComponent< export class PagerankTable extends React.PureComponent<
PagerankTableProps, PagerankTableProps,
PagerankTableState PagerankTableState
> { > {
constructor() { constructor(props: PagerankTableProps): void {
super(); super();
this.state = {topLevelFilter: NodeAddress.empty}; 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
);
this.state = {topLevelFilter};
} }
render() { render() {

View File

@ -3,7 +3,7 @@
import React from "react"; import React from "react";
import {shallow} from "enzyme"; import {shallow} from "enzyme";
import {NodeAddress} from "../../../core/graph"; import {NodeAddress, type NodeAddressT} from "../../../core/graph";
import {PagerankTable} from "./Table"; import {PagerankTable} from "./Table";
import {example, COLUMNS} from "./sharedTestUtils"; import {example, COLUMNS} from "./sharedTestUtils";
@ -24,7 +24,12 @@ describe("app/credExplorer/pagerankTable/Table", () => {
it("renders thead column order properly", async () => { it("renders thead column order properly", async () => {
const {pnd, adapters} = await example(); const {pnd, adapters} = await example();
const element = shallow( const element = shallow(
<PagerankTable pnd={pnd} adapters={adapters} maxEntriesPerList={1} /> <PagerankTable
defaultNodeFilter={null}
pnd={pnd}
adapters={adapters}
maxEntriesPerList={1}
/>
); );
const th = element.find("thead th"); const th = element.find("thead th");
const columnNames = th.map((t) => t.text()); const columnNames = th.map((t) => t.text());
@ -32,10 +37,15 @@ describe("app/credExplorer/pagerankTable/Table", () => {
}); });
describe("has a filter select", () => { describe("has a filter select", () => {
async function setup() { async function setup(defaultNodeFilter?: NodeAddressT) {
const {pnd, adapters} = await example(); const {pnd, adapters} = await example();
const element = shallow( const element = shallow(
<PagerankTable pnd={pnd} adapters={adapters} maxEntriesPerList={1} /> <PagerankTable
defaultNodeFilter={defaultNodeFilter}
pnd={pnd}
adapters={adapters}
maxEntriesPerList={1}
/>
); );
const label = element.find("label"); const label = element.find("label");
const options = label.find("option"); const options = label.find("option");
@ -74,6 +84,21 @@ describe("app/credExplorer/pagerankTable/Table", () => {
); );
expect(actualNodes).not.toHaveLength(0); expect(actualNodes).not.toHaveLength(0);
}); });
it("filter defaults to show all if defaultNodeFilter not passed", async () => {
const {element} = await setup();
expect(element.state().topLevelFilter).toEqual(NodeAddress.empty);
});
it("filter defaults to defaultNodeFilter if available", async () => {
const filter = NodeAddress.fromParts(["foo", "a"]);
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"
);
});
}); });
describe("creates a NodeRowList", () => { describe("creates a NodeRowList", () => {
@ -82,6 +107,7 @@ describe("app/credExplorer/pagerankTable/Table", () => {
const maxEntriesPerList = 1; const maxEntriesPerList = 1;
const element = shallow( const element = shallow(
<PagerankTable <PagerankTable
defaultNodeFilter={null}
pnd={pnd} pnd={pnd}
adapters={adapters} adapters={adapters}
maxEntriesPerList={maxEntriesPerList} maxEntriesPerList={maxEntriesPerList}