diff --git a/CHANGELOG.md b/CHANGELOG.md index a05235375..49320fc99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ # Changelog -## version 2.48.0 - 2023/08/31 +* Allow the tip label key to be defined in the JSON via `display_defaults.tip_label`, in addition to being settable via a URL query. +See [the docs](https://docs.nextstrain.org/projects/auspice/en/stable/advanced-functionality/view-settings.html) or [PR #1668](https://github.com/nextstrain/auspice/pull/1668) for more. +## version 2.48.0 - 2023/08/31 * Root sequence data may be inlined in the main dataset JSON ([Auspice PR #1688](https://github.com/nextstrain/auspice/pull/1688) and [Augur PR #1295](https://github.com/nextstrain/augur/pull/1295)). @@ -32,8 +34,6 @@ detailed in [augur PR #1281](https://github.com/nextstrain/augur/pull/1281). * Dependencies updated. See [PR #1669](https://github.com/nextstrain/auspice/pull/1669) for more. ## version 2.46.0 - 2023/05/15 - - * Fixed a bug where narratives with multiple datasets that had measurements panels would error when switching datasets. ([#1603](https://github.com/nextstrain/auspice/issues/1603)) _There have been a number of internal changes with this release, most of which should not result in any different behavior while using Auspice._ diff --git a/docs/advanced-functionality/view-settings.rst b/docs/advanced-functionality/view-settings.rst index 3750ce740..eabb3c280 100644 --- a/docs/advanced-functionality/view-settings.rst +++ b/docs/advanced-functionality/view-settings.rst @@ -17,6 +17,7 @@ Auspice has some hardcoded defaults, largely for historical reasons. Each of the - Default geographic resolution is "country", if available. - Default colouring is "country", if available. - Default branch labelling is "clade", if available. +- Default tip labelling is the sample / strain name (`node.name`) Dataset (JSON) configurable defaults ------------------------------------ @@ -36,6 +37,8 @@ These are exported as the (optional) property of the dataset JSON ``meta.display +---------------------------+-----------------------------------------------------------------------+----------------------------------------------------+ | ``layout`` | Tree layout | "rect", "radial", "unrooted", "clock" or "scatter" | +---------------------------+-----------------------------------------------------------------------+----------------------------------------------------+ +| ``tip_label`` | What attribute (in 'node_attrs') to use as tip (node) labels | "country" | ++---------------------------+-----------------------------------------------------------------------+----------------------------------------------------+ | ``branch_label`` | Which set of branch labels are to be displayed | "aa", "lineage" | +---------------------------+-----------------------------------------------------------------------+----------------------------------------------------+ | ``sidebar`` | Should the sidebar start open or closed? | "open" or "closed" | @@ -103,6 +106,8 @@ URL queries are the part of the URL coming after the ``?`` character, and typica +----------------------+-----------------------------------------------------------+---------------------------------------------------+ | ``s`` | Selected strain | ``s=1_0199_PF`` | +----------------------+-----------------------------------------------------------+---------------------------------------------------+ +| ``tl`` | Tip label to display | ``tl=country`` | ++----------------------+-----------------------------------------------------------+---------------------------------------------------+ | ``branchLabel`` | Branch labels to display | ``branchLabel=aa`` | +----------------------+-----------------------------------------------------------+---------------------------------------------------+ | ``showBranchLabels`` | Force all branch labels to be displayed | ``showBranchLabels=all`` | diff --git a/src/actions/recomputeReduxState.js b/src/actions/recomputeReduxState.js index e1168b1e7..cc00d3547 100644 --- a/src/actions/recomputeReduxState.js +++ b/src/actions/recomputeReduxState.js @@ -22,6 +22,7 @@ import { isColorByGenotype, decodeColorByGenotype, encodeColorByGenotype, decode import { getTraitFromNode, getDivFromNode, collectGenotypeStates } from "../util/treeMiscHelpers"; import { collectAvailableTipLabelOptions } from "../components/controls/choose-tip-label"; import { hasMultipleGridPanels } from "./panelDisplay"; +import { strainSymbolUrlString } from "../middleware/changeURL"; export const doesColorByHaveConfidence = (controlsState, colorBy) => controlsState.coloringsPresentOnTreeWithConfidence.has(colorBy); @@ -89,7 +90,7 @@ const modifyStateViaURLQuery = (state, query) => { state["panelLayout"] = query.p; } if (query.tl) { - state["tipLabelKey"] = query.tl; + state["tipLabelKey"] = query.tl===strainSymbolUrlString ? strainSymbol : query.tl; } if (query.dmin) { state["dateMin"] = query.dmin; @@ -238,8 +239,8 @@ const modifyStateViaMetadata = (state, metadata, genomeMap) => { state.filters[strainSymbol] = []; state.filters[genotypeSymbol] = []; // this doesn't necessitate that mutations are defined if (metadata.displayDefaults) { - const keysToCheckFor = ["geoResolution", "colorBy", "distanceMeasure", "layout", "mapTriplicate", "selectedBranchLabel", 'sidebar', "showTransmissionLines", "normalizeFrequencies"]; - const expectedTypes = ["string", "string", "string", "string", "boolean", "string", 'string', "boolean" , "boolean"]; + const keysToCheckFor = ["geoResolution", "colorBy", "distanceMeasure", "layout", "mapTriplicate", "selectedBranchLabel", "tipLabelKey", 'sidebar', "showTransmissionLines", "normalizeFrequencies"]; + const expectedTypes = ["string", "string", "string", "string", "boolean", "string", 'string', 'string', "boolean" , "boolean"]; for (let i = 0; i < keysToCheckFor.length; i += 1) { if (Object.hasOwnProperty.call(metadata.displayDefaults, keysToCheckFor[i])) { @@ -552,10 +553,21 @@ const checkAndCorrectErrorsInState = (state, metadata, genomeMap, query, tree, v state.defaults.selectedBranchLabel = "none"; } - /* check tip label is valid. We use the function which generates the options for the dropdown here */ - if (!collectAvailableTipLabelOptions(metadata.colorings).map((o) => o.value).includes(state.tipLabelKey)) { - console.error("Can't set selected tip label to ", state.tipLabelKey); - state.tipLabelKey = strainSymbol; + /* check tip label is valid. We use the function which generates the options for the dropdown here. + * state.defaults.tipLabelKey is set by the JSON's display_defaults (default: strainSymbol) + * state.tipLabelKey is first set the JSON and then overridden via the URL query (default: state.defaults.tipLabelKey) + */ + const validTipLabels = collectAvailableTipLabelOptions(metadata.colorings).map((o) => o.value); + if (!validTipLabels.includes(state.defaults.tipLabelKey)) { + console.error("Invalid JSON-defined tip label:", state.defaults.tipLabelKey); + state.defaults.tipLabelKey = strainSymbol; + } + if (!validTipLabels.includes(state.tipLabelKey)) { + if (query.tl) { + console.error("Invalid URL-defined tip label:", state.tipLabelKey); + delete query.tl; + } + state.tipLabelKey = state.defaults.tipLabelKey; } /* temporalConfidence */ @@ -745,6 +757,7 @@ const createMetadataStateFromJSON = (json) => { geo_resolution: "geoResolution", distance_measure: "distanceMeasure", branch_label: "selectedBranchLabel", + tip_label: "tipLabelKey", map_triplicate: "mapTriplicate", layout: "layout", language: "language", diff --git a/src/middleware/changeURL.js b/src/middleware/changeURL.js index 516d5d793..7e08befa9 100644 --- a/src/middleware/changeURL.js +++ b/src/middleware/changeURL.js @@ -5,6 +5,8 @@ import { shouldDisplayTemporalConfidence } from "../reducers/controls"; import { genotypeSymbol, nucleotide_gene, strainSymbol } from "../util/globals"; import { encodeGenotypeFilters, decodeColorByGenotype, isColorByGenotype } from "../util/getGenotype"; +export const strainSymbolUrlString = "__strain__"; + /** * This middleware acts to keep the app state and the URL query state in sync by * intercepting actions and updating the URL accordingly. Thus, in theory, this @@ -167,7 +169,9 @@ export const changeURLMiddleware = (store) => (next) => (action) => { break; } case types.CHANGE_TIP_LABEL_KEY: { - query.tl = action.key===strainSymbol ? undefined : action.key; + query.tl = action.key===state.controls.defaults.tipLabelKey ? undefined : + action.key===strainSymbol ? strainSymbolUrlString : + action.key; break; } case types.CHANGE_DATES_VISIBILITY_THICKNESS: { diff --git a/src/reducers/controls.js b/src/reducers/controls.js index ca186171a..0691f3cba 100644 --- a/src/reducers/controls.js +++ b/src/reducers/controls.js @@ -24,6 +24,7 @@ export const getDefaultControlsState = () => { filtersInFooter: [], colorBy: defaultColorBy, selectedBranchLabel: "none", + tipLabelKey: strainSymbol, showTransmissionLines: true }; // a default sidebarOpen status is only set via JSON, URL query @@ -78,7 +79,7 @@ export const getDefaultControlsState = () => { panelsAvailable: [], panelsToDisplay: [], panelLayout: calcBrowserDimensionsInitialState().width > twoColumnBreakpoint ? "grid" : "full", - tipLabelKey: strainSymbol, + tipLabelKey: defaults.tipLabelKey, showTreeToo: undefined, showTangle: false, zoomMin: undefined,