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:
parent
e3a4d1f2b9
commit
f100cd02cc
|
@ -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}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in New Issue