Skip to content

Commit 887624a

Browse files
authored
Merge pull request #1863: Tree component refactoring
2 parents 556d4fa + 4658803 commit 887624a

File tree

7 files changed

+133
-63
lines changed

7 files changed

+133
-63
lines changed

src/components/tree/index.js src/components/tree/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { connect } from "react-redux";
22
import UnconnectedTree from "./tree";
3+
import { RootState } from "../../store";
34

4-
const Tree = connect((state) => ({
5+
const Tree = connect((state: RootState) => ({
56
tree: state.tree,
67
treeToo: state.treeToo,
78
selectedNode: state.controls.selectedNode,

src/components/tree/tree.js

+23-21
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,24 @@ class Tree extends React.Component {
3535
tree: null,
3636
treeToo: null
3737
};
38+
3839
/* bind callbacks */
3940
this.clearSelectedNode = callbacks.clearSelectedNode.bind(this);
40-
// this.handleIconClickHOF = callbacks.handleIconClickHOF.bind(this);
41-
this.redrawTree = () => {
42-
this.props.dispatch(updateVisibleTipsAndBranchThicknesses({
43-
root: [0, 0]
44-
}));
45-
};
46-
/* pressing the escape key should dismiss an info modal (if one exists) */
47-
this.handlekeydownEvent = (event) => {
48-
if (event.key==="Escape" && this.props.selectedNode) {
49-
this.clearSelectedNode(this.props.selectedNode);
50-
}
51-
};
5241
}
42+
43+
redrawTree = () => {
44+
this.props.dispatch(updateVisibleTipsAndBranchThicknesses({
45+
root: [0, 0]
46+
}));
47+
}
48+
49+
/* pressing the escape key should dismiss an info modal (if one exists) */
50+
handlekeydownEvent = (event) => {
51+
if (event.key==="Escape" && this.props.selectedNode) {
52+
this.clearSelectedNode(this.props.selectedNode);
53+
}
54+
}
55+
5356
setUpAndRenderTreeToo(props, newState) {
5457
/* this.setState(newState) will be run sometime after this returns */
5558
/* modifies newState in place */
@@ -59,6 +62,7 @@ class Tree extends React.Component {
5962
}
6063
renderTree(this, false, newState.treeToo, props);
6164
}
65+
6266
componentDidMount() {
6367
document.addEventListener('keyup', this.handlekeydownEvent);
6468
if (this.props.tree.loaded) {
@@ -72,6 +76,7 @@ class Tree extends React.Component {
7276
this.setState(newState); /* this will trigger an unnecessary CDU :( */
7377
}
7478
}
79+
7580
componentDidUpdate(prevProps) {
7681
let newState = {};
7782
let rightTreeUpdated = false;
@@ -110,16 +115,13 @@ class Tree extends React.Component {
110115
}
111116

112117
getStyles = () => {
113-
const activeResetTreeButton = this.props.tree.idxOfInViewRootNode !== 0 ||
114-
this.props.treeToo.idxOfInViewRootNode !== 0;
115-
116118
const filteredTree = !!this.props.tree.idxOfFilteredRoot &&
117119
this.props.tree.idxOfInViewRootNode !== this.props.tree.idxOfFilteredRoot;
118120
const filteredTreeToo = !!this.props.treeToo.idxOfFilteredRoot &&
119121
this.props.treeToo.idxOfInViewRootNode !== this.props.treeToo.idxOfFilteredRoot;
120122
const activeZoomButton = filteredTree || filteredTreeToo;
121123

122-
const treeIsZoomed = this.props.tree.idxOfInViewRootNode !== 0 ||
124+
const anyTreeZoomed = this.props.tree.idxOfInViewRootNode !== 0 ||
123125
this.props.treeToo.idxOfInViewRootNode !== 0;
124126

125127
return {
@@ -133,8 +135,8 @@ class Tree extends React.Component {
133135
zIndex: 100,
134136
display: "inline-block",
135137
marginLeft: 4,
136-
cursor: activeResetTreeButton ? "pointer" : "auto",
137-
color: activeResetTreeButton ? darkGrey : lightGrey
138+
cursor: anyTreeZoomed ? "pointer" : "auto",
139+
color: anyTreeZoomed ? darkGrey : lightGrey
138140
},
139141
zoomToSelectedButton: {
140142
zIndex: 100,
@@ -146,9 +148,9 @@ class Tree extends React.Component {
146148
zoomOutButton: {
147149
zIndex: 100,
148150
display: "inline-block",
149-
cursor: treeIsZoomed ? "pointer" : "auto",
150-
color: treeIsZoomed ? darkGrey : lightGrey,
151-
pointerEvents: treeIsZoomed ? "auto" : "none",
151+
cursor: anyTreeZoomed ? "pointer" : "auto",
152+
color: anyTreeZoomed ? darkGrey : lightGrey,
153+
pointerEvents: anyTreeZoomed ? "auto" : "none",
152154
marginRight: "4px"
153155
}
154156
};

src/reducers/controls.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,24 @@ import { calcBrowserDimensionsInitialState } from "./browserDimensions";
1212
import { doesColorByHaveConfidence } from "../actions/recomputeReduxState";
1313
import { hasMultipleGridPanels } from "../actions/panelDisplay";
1414

15+
type Layout = "rect" | "radial" | "unrooted" | "clock" | "scatter"
16+
17+
interface Defaults {
18+
distanceMeasure: string
19+
layout: Layout
20+
geoResolution: string
21+
filters: Record<string, any>
22+
filtersInFooter: string[]
23+
colorBy: string
24+
selectedBranchLabel: string
25+
tipLabelKey: typeof strainSymbol
26+
showTransmissionLines: boolean
27+
sidebarOpen?: boolean
28+
}
29+
1530
export interface BasicControlsState {
31+
defaults: Defaults
32+
layout: Layout
1633
panelsAvailable: string[]
1734
panelsToDisplay: string[]
1835
showTreeToo: boolean
@@ -40,7 +57,7 @@ export interface ControlsState extends BasicControlsState, MeasurementsControlSt
4057
at any time, e.g. if we want to revert things (e.g. on dataset change)
4158
*/
4259
export const getDefaultControlsState = () => {
43-
const defaults: Partial<ControlsState> = {
60+
const defaults: Defaults = {
4461
distanceMeasure: defaultDistanceMeasure,
4562
layout: defaultLayout,
4663
geoResolution: defaultGeoResolution,

src/reducers/index.js

-30
This file was deleted.

src/reducers/index.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { combineReducers } from "redux";
2+
import metadata from "./metadata";
3+
import tree from "./tree";
4+
import frequencies from "./frequencies";
5+
import entropy from "./entropy";
6+
import controls, { ControlsState } from "./controls";
7+
import browserDimensions from "./browserDimensions";
8+
import notifications from "./notifications";
9+
import narrative, { NarrativeState } from "./narrative";
10+
import treeToo from "./treeToo";
11+
import general from "./general";
12+
import jsonCache from "./jsonCache";
13+
import measurements from "./measurements";
14+
15+
interface RootState {
16+
metadata: ReturnType<typeof metadata>
17+
tree: ReturnType<typeof tree>
18+
frequencies: ReturnType<typeof frequencies>
19+
controls: ControlsState
20+
entropy: ReturnType<typeof entropy>
21+
browserDimensions: ReturnType<typeof browserDimensions>
22+
notifications: ReturnType<typeof notifications>
23+
narrative: NarrativeState
24+
treeToo: ReturnType<typeof treeToo>
25+
general: ReturnType<typeof general>
26+
jsonCache: ReturnType<typeof jsonCache>
27+
measurements: ReturnType<typeof measurements>
28+
}
29+
30+
const rootReducer = combineReducers<RootState>({
31+
metadata,
32+
tree,
33+
frequencies,
34+
controls,
35+
entropy,
36+
browserDimensions,
37+
notifications,
38+
narrative,
39+
treeToo,
40+
general,
41+
jsonCache,
42+
measurements
43+
});
44+
45+
export default rootReducer;

src/reducers/narrative.js src/reducers/narrative.ts

+44-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,47 @@
11
import * as types from "../actions/types";
2+
import { AnyAction } from 'redux';
23

3-
const narrative = (state = {
4+
export interface NarrativeState {
5+
loaded: boolean
6+
/**
7+
* array of paragraphs (aka blocks)
8+
*/
9+
blocks: { __html: string }[] | null
10+
11+
/**
12+
* which block is currently "in view"
13+
*/
14+
blockIdx?: number
15+
16+
/**
17+
* the pathname of the _narrative_
18+
*/
19+
pathname?: string
20+
21+
display: boolean
22+
title?: string
23+
}
24+
25+
const defaultState: NarrativeState = {
426
loaded: false,
5-
blocks: null, /* array of paragraphs (aka blocks) */
6-
blockIdx: undefined, /* which block is currently "in view" */
7-
pathname: undefined, /* the pathname of the _narrative_ */
27+
blocks: null,
28+
blockIdx: undefined,
29+
pathname: undefined,
830
display: false,
931
title: undefined
10-
}, action) => {
32+
};
33+
34+
const narrative = (
35+
state: NarrativeState = defaultState,
36+
action: AnyAction,
37+
): NarrativeState => {
1138
switch (action.type) {
1239
case types.DATA_INVALID:
13-
return Object.assign({}, state, {
40+
return {
41+
...state,
1442
loaded: false,
15-
display: false
16-
});
43+
display: false,
44+
};
1745
case types.CLEAN_START:
1846
if (action.narrative) {
1947
const blocks = action.narrative;
@@ -29,12 +57,18 @@ const narrative = (state = {
2957
return state;
3058
case types.URL_QUERY_CHANGE_WITH_COMPUTED_STATE:
3159
if (Object.prototype.hasOwnProperty.call(action.query, "n")) {
32-
return Object.assign({}, state, {blockIdx: action.query.n});
60+
return {
61+
...state,
62+
blockIdx: action.query.n,
63+
};
3364
}
3465
return state;
3566
case types.TOGGLE_NARRATIVE:
3667
if (state.loaded) {
37-
return Object.assign({}, state, {display: action.narrativeOn});
68+
return {
69+
...state,
70+
display: action.narrativeOn,
71+
};
3872
}
3973
console.warn("Attempted to toggle narrative that was not loaded");
4074
return state;

src/store.ts

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ if (process.env.NODE_ENV !== 'production' && module.hot) {
4848
}
4949

5050
// Infer types from the store.
51+
// This is more clearly defined in src/reducers/index.ts but exported here.
5152
export type RootState = ReturnType<typeof store.getState>
5253
export type AppDispatch = typeof store.dispatch
5354

0 commit comments

Comments
 (0)