Skip to content

Commit

Permalink
Tip label is used for on-hover+click info
Browse files Browse the repository at this point in the history
This generally better reflects the intention of changing the tip label
key, which was previously only reflected in text drawn to the RHS of
tree nodes at certain zoom levels.

The motivating use case is the proliferation of datasets using an
accession for `node.name` but who wish to see node names of another
piece of metadata. This is possible by adding the desired name as a
node_attr and (optionally) setting this as the default tip label.

Note that the order of items in the node-clicked modal has been
slightly altered and is now more consistent across internal branches,
terminal branches & tips
  • Loading branch information
jameshadfield committed Aug 26, 2024
1 parent 3eb6ea8 commit 962a2e2
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 25 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* Any `node_attr` in the tree can be used as a tip label, as well as the special-cases of strain-name and "none".
Previously we only allowed valid colorings.
([#1668](https://github.com/nextstrain/auspice/pull/1668))

* The specified tip label is surfaced more prominently within the the on-hover info boxes & on-click modals.
([PR #1668](https://github.com/nextstrain/auspice/pull/1668))

## version 2.56.1 - 2024/08/22

Expand Down
22 changes: 11 additions & 11 deletions src/components/tree/infoPanels/click.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from "react";
import { isValueValid } from "../../../util/globals";
import { isValueValid, strainSymbol } from "../../../util/globals";
import { infoPanelStyles } from "../../../globalStyles";
import { numericToCalendar } from "../../../util/dateHelpers";
import { getTraitFromNode, getFullAuthorInfoFromNode, getVaccineFromNode,
getAccessionFromNode, getUrlFromNode } from "../../../util/treeMiscHelpers";
import { MutationTable } from "./MutationTable";
import { lhsTreeId} from "../tree";
import { nodeDisplayName } from "./helpers";

export const styles = {
container: {
Expand Down Expand Up @@ -241,9 +242,10 @@ const Trait = ({node, trait, colorings, isTerminal}) => {
* @param {Object} props.colorings
* @param {Object} props.observedMutations
* @param {function} props.geneSortFn
* @param {string|symbol} props.tipLabelKey
* @param {function} props.t
*/
const NodeClickedPanel = ({selectedNode, nodesLhsTree, nodesRhsTree, clearSelectedNode, colorings, observedMutations, geneSortFn, t}) => {
const NodeClickedPanel = ({selectedNode, nodesLhsTree, nodesRhsTree, clearSelectedNode, colorings, observedMutations, geneSortFn, tipLabelKey, t}) => {
if (!selectedNode) return null;
const node = (selectedNode.treeId===lhsTreeId ? nodesLhsTree : nodesRhsTree)?.[selectedNode.idx];
if (!node) {
Expand All @@ -252,25 +254,23 @@ const NodeClickedPanel = ({selectedNode, nodesLhsTree, nodesRhsTree, clearSelect
}
const panelStyle = { ...infoPanelStyles.panel};
panelStyle.maxHeight = "70%";

/* We have `isTerminal` and `isTip` to differentiate between clicking on a branch leading to a tip
* vs clicking on the tip (circle) itself */
const isTerminal = !node.hasChildren;
const isTip = !selectedNode.isBranch;

const title = isTip ?
node.name :
isTerminal ?
`Branch leading to ${node.name}` :
"Internal branch";
const shouldShowNodeName = tipLabelKey!==strainSymbol;

return (
<div style={infoPanelStyles.modalContainer} onClick={() => clearSelectedNode(selectedNode)}>
<div className={"panel"} style={panelStyle} onClick={(e) => stopProp(e)}>
<StrainName>{title}</StrainName>
<StrainName>{nodeDisplayName(t, node, tipLabelKey, !isTip)}</StrainName>
<table>
<tbody>
{!isTip && item(t("Number of terminal tips"), node.fullTipCount)}
{!isTerminal && item(t("Number of terminal tips"), node.fullTipCount)}
{shouldShowNodeName && item(t("Node name"), node.name)}
{isTip && <VaccineInfo node={node} t={t}/>}
<SampleDate isTerminal={isTerminal} node={node} t={t}/>
{!isTip && item("Node name", node.name)}
{isTip && <PublicationInfo node={node} t={t}/>}
{getTraitsToDisplay(node).map((trait) => (
<Trait node={node} trait={trait} colorings={colorings} key={trait} isTerminal={isTerminal}/>
Expand Down
23 changes: 23 additions & 0 deletions src/components/tree/infoPanels/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getTraitFromNode } from "../../../util/treeMiscHelpers";
import { numericToCalendar } from "../../../util/dateHelpers";


/**
* Attempt to display the best name we can for a node, depending on how we are looking at a node.
* The returned string will be rendered on a line of its own, so an empty string will look ok
* Future enhancement: we could examine the coloring metadata (if available) and format the value accordingly
*/
export function nodeDisplayName(t, node, tipLabelKey, branch) {
let tipLabel = getTraitFromNode(node, tipLabelKey)
if (tipLabelKey==='num_date' && tipLabel) tipLabel = numericToCalendar(tipLabel)
const terminal = !node.hasChildren;

if (branch) {
if (terminal) {
return tipLabel ? t("Branch leading to {{tipLabel}}", {tipLabel}) : t("Terminal branch") // hover + click
}
return t("Internal branch") // branch click only
}
/* TIP */
return tipLabel;
}
23 changes: 10 additions & 13 deletions src/components/tree/infoPanels/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { getTipColorAttribute } from "../../../util/colorHelpers";
import { isColorByGenotype, decodeColorByGenotype } from "../../../util/getGenotype";
import { getTraitFromNode, getDivFromNode, getVaccineFromNode,
getFullAuthorInfoFromNode, getTipChanges, getBranchMutations } from "../../../util/treeMiscHelpers";
import { isValueValid } from "../../../util/globals";
import { isValueValid, strainSymbol } from "../../../util/globals";
import { formatDivergence, getIdxOfInViewRootNode } from "../phyloTree/helpers";
import { parseIntervalsOfNsOrGaps } from "./MutationTable";
import { nodeDisplayName } from "./helpers";

export const InfoLine = ({name, value, padBelow=false}) => {
const renderValues = () => {
Expand All @@ -32,12 +33,6 @@ export const InfoLine = ({name, value, padBelow=false}) => {
);
};

const StrainName = ({name}) => (
<div style={infoPanelStyles.tooltipHeading}>
{name}
</div>
);

/**
* A React component to display information about the branch's time & divergence (where applicable)
* @param {Object} props
Expand Down Expand Up @@ -267,14 +262,13 @@ const BranchMutations = ({node, geneSortFn, observedMutations, t}) => {
* @param {Object} props
* @param {Object} props.node branch node which is currently highlighted
*/
const BranchDescendents = ({node, t}) => {
const BranchDescendants = ({node, t, tipLabelKey}) => {
const [name, value] = node.fullTipCount === 1 ?
[t("Branch leading to"), node.name] :
[nodeDisplayName(node, tipLabelKey, true), ""] :
[t("Number of descendants")+":", node.fullTipCount];
return <InfoLine name={name} value={value} padBelow/>;
};


/**
* A React component to show vaccine information, if present
* @param {Object} props
Expand Down Expand Up @@ -399,17 +393,20 @@ const HoverInfoPanel = ({
colorings,
geneSortFn,
observedMutations,
tipLabelKey,
t
}) => {
if (!selectedNode) return null
const node = selectedNode.node.n; // want the redux node, not the phylo node
const idxOfInViewRootNode = getIdxOfInViewRootNode(node);

return (
<Container node={node} panelDims={panelDims}>
{selectedNode.isBranch===false ? (
<>
<StrainName name={node.name}/>
<div style={infoPanelStyles.tooltipHeading}>
{nodeDisplayName(t, node, tipLabelKey, false)}
</div>
{tipLabelKey!==strainSymbol && <InfoLine name="Node name:" value={node.name}/>}
<VaccineInfo node={node} t={t}/>
<TipMutations node={node} t={t}/>
<BranchLength node={node} t={t}/>
Expand All @@ -419,7 +416,7 @@ const HoverInfoPanel = ({
</>
) : (
<>
<BranchDescendents node={node} t={t}/>
<BranchDescendants node={node} t={t} tipLabelKey={tipLabelKey}/>
<BranchMutations node={node} geneSortFn={geneSortFn} observedMutations={observedMutations} t={t}/>
<BranchLength node={node} t={t}/>
<ColorBy node={node} colorBy={colorBy} colorByConfidence={colorByConfidence} colorScale={colorScale} colorings={colorings}/>
Expand Down
2 changes: 2 additions & 0 deletions src/components/tree/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ class Tree extends React.Component {
geneSortFn={this.state.geneSortFn}
observedMutations={this.props.tree.observedMutations}
panelDims={{width: this.props.width, height: this.props.height, spaceBetweenTrees}}
tipLabelKey={this.props.tipLabelKey}
t={t}
/>
<NodeClickedPanel
Expand All @@ -216,6 +217,7 @@ class Tree extends React.Component {
observedMutations={this.props.tree.observedMutations}
colorings={this.props.colorings}
geneSortFn={this.state.geneSortFn}
tipLabelKey={this.props.tipLabelKey}
t={t}
/>
{this.props.showTangle && this.state.tree && this.state.treeToo ? (
Expand Down

0 comments on commit 962a2e2

Please sign in to comment.