Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign panel sections in sidebar #1704

Merged
merged 14 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

* Redesigned the panel sections in the sidebar. ([#1704](https://github.com/nextstrain/auspice/pull/1704))
* Moved panel visibility toggles to the header.
* Added the ability to show/hide panel options.
* customisation: Renamed `sidebarTheme.unselectedBackground` to a more generic name, `sidebarTheme.alternateBackground`, while keeping backwards compatibility. ([#1704](https://github.com/nextstrain/auspice/pull/1704/commits/1ad4376bb3be9787181110d47fe8c5f26a207896))

## version 2.50.0 - 2023/10/30


Expand Down
34 changes: 18 additions & 16 deletions docs/customise-client/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,27 @@ For instance, here is the customisation used by nextstrain.org:
"font-family": "Lato, Helvetica Neue, Helvetica, sans-serif",
"selectedColor": "#5097BA",
"unselectedColor": "#333",
"unselectedBackground": "#888"
"alternateBackground": "#888"
}
}

+--------------------------+------------------------------+----------------------------------------------------+
| Properties | CSS string of | Description |
+==========================+==============================+====================================================+
| selectedColor | color | Text color of selected text / button text |
+--------------------------+------------------------------+----------------------------------------------------+
| unselectedColor | color | Text color of unselected text / button text |
+--------------------------+------------------------------+----------------------------------------------------+
| color | color | Text color of all other text |
+--------------------------+------------------------------+----------------------------------------------------+
| unselectedBackground | color | Background color of unselected toggle |
+--------------------------+------------------------------+----------------------------------------------------+
| font-family | font | Font used throughout the sidebar |
+--------------------------+------------------------------+----------------------------------------------------+
| background | color | Background color of the entire sidebar |
+--------------------------+------------------------------+----------------------------------------------------+
+-----------------------------------+---------------+------------------------------------------------------------------------------+
| Properties | CSS string of | Description |
+===================================+===============+==============================================================================+
| selectedColor | color | Text color of selected text / button text |
+-----------------------------------+---------------+------------------------------------------------------------------------------+
| unselectedColor | color | Text color of unselected text / button text |
+-----------------------------------+---------------+------------------------------------------------------------------------------+
| color | color | Text color of all other text |
+-----------------------------------+---------------+------------------------------------------------------------------------------+
| unselectedBackground (deprecated) | color | Old key for ``alternateBackground`` |
+-----------------------------------+---------------+------------------------------------------------------------------------------+
| alternateBackground | color | Background color of some elements (unselected toggle, panel section borders) |
+-----------------------------------+---------------+------------------------------------------------------------------------------+
| font-family | font | Font used throughout the sidebar |
+-----------------------------------+---------------+------------------------------------------------------------------------------+
| background | color | Background color of the entire sidebar |
+-----------------------------------+---------------+------------------------------------------------------------------------------+

Components
----------
Expand Down
26 changes: 0 additions & 26 deletions src/components/controls/annotatedHeader.tsx

This file was deleted.

42 changes: 42 additions & 0 deletions src/components/controls/annotatedTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react";
import { useSelector } from "react-redux";
import { FaInfoCircle } from "react-icons/fa";
import {TitleAndIconContainer, StyledTooltip, HeaderIconContainer, HeaderTitle} from "./styles";
import { RootState } from "../../store";

/** Title to display for the control. */
export type Title = string;

/** Informational tooltip element to display on hover. */
export type Tooltip = JSX.Element;

type Props = {
title: Title
tooltip?: Tooltip
}

/**
* A title and tooltip to be shown in a control header.
* The tooltip is not shown on mobile.
*/
export const AnnotatedTitle = ({title, tooltip=undefined}: Props) => {
const mobile = useSelector((state: RootState) => state.general.mobileDisplay);

return (
<TitleAndIconContainer>
<HeaderTitle>{title}</HeaderTitle>
{tooltip && !mobile && (
<>
<HeaderIconContainer data-tip data-for={title}
// Don't allow any parent onClick callbacks to run.
onClick={(event) => event.stopPropagation()}>
<FaInfoCircle/>
</HeaderIconContainer>
<StyledTooltip place="bottom" type="dark" effect="solid" id={title}>
victorlin marked this conversation as resolved.
Show resolved Hide resolved
{tooltip}
</StyledTooltip>
</>
)}
</TitleAndIconContainer>
);
};
4 changes: 2 additions & 2 deletions src/components/controls/choose-dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import { connect } from "react-redux";
import { withTranslation } from "react-i18next";
import ChooseDatasetSelect from "./choose-dataset-select";
import { AnnotatedHeader } from "./annotatedHeader";
import { ControlHeader } from "./controlHeader";

// const DroppedFiles = withTheme((props) => {
// /* TODO: this shouldn't be in the auspice src, rather injected as an extension when needed */
Expand Down Expand Up @@ -54,7 +54,7 @@ class ChooseDataset extends React.Component {

return (
<>
<AnnotatedHeader title={t("sidebar:Dataset")} />
<ControlHeader title={t("sidebar:Dataset")} />
{options.map((option, optionIdx) => (
<ChooseDatasetSelect
key={displayedDataset[optionIdx]}
Expand Down
21 changes: 21 additions & 0 deletions src/components/controls/controlHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react";
import { HeaderContainer } from "./styles";
import { AnnotatedTitle, Title, Tooltip } from "./annotatedTitle";

type Props = {
title: Title
tooltip?: Tooltip
}

victorlin marked this conversation as resolved.
Show resolved Hide resolved
/**
* A header used by all non-panel controls, containing an informative title.
*/
export const ControlHeader = ({title, tooltip=undefined }: Props) => {
return (
<HeaderContainer>
<AnnotatedTitle
title={title}
tooltip={tooltip} />
</HeaderContainer>
);
};
127 changes: 79 additions & 48 deletions src/components/controls/controls.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react";
import { useSelector } from "react-redux";
import { useTranslation } from 'react-i18next';

import ColorBy, {ColorByInfo} from "./color-by";
Expand All @@ -16,84 +17,114 @@ import GeoResolution from "./geo-resolution";
import TransmissionLines from './transmission-lines';
import NormalizeFrequencies from "./frequency-normalization";
import AnimationOptions from "./animation-options";
import PanelToggles from "./panel-toggles";
import { PanelSection } from "./panelSection";
import ToggleTangle from "./toggle-tangle";
import Language from "./language";
import { ControlsContainer } from "./styles";
import FilterData, {FilterInfo} from "./filter";
import {TreeOptionsInfo, MapOptionsInfo, AnimationOptionsInfo, PanelOptionsInfo,
ExplodeTreeInfo, FrequencyInfo, MeasurementsOptionsInfo} from "./miscInfoText";
import { AnnotatedHeader } from "./annotatedHeader";
import {TreeInfo, MapInfo, AnimationOptionsInfo, PanelLayoutInfo,
ExplodeTreeInfo, EntropyInfo, FrequencyInfo, MeasurementsInfo} from "./miscInfoText";
import { ControlHeader } from "./controlHeader";
import MeasurementsOptions from "./measurementsOptions";
import { RootState } from "../../store";

type Props = {
treeOn: boolean
mapOn: boolean
frequenciesOn: boolean
measurementsOn: boolean
}

function Controls({ treeOn, mapOn, frequenciesOn, measurementsOn }: Props) {
function Controls() {
const { t } = useTranslation();

const panelsAvailable = useSelector((state: RootState) => state.controls.panelsAvailable);
const panelsToDisplay = useSelector((state: RootState) => state.controls.panelsToDisplay);
const showTreeToo = useSelector((state: RootState) => state.controls.showTreeToo);
const canTogglePanelLayout = useSelector((state: RootState) => state.controls.canTogglePanelLayout);

return (
<ControlsContainer>
<ChooseDataset />

<AnnotatedHeader title={t("sidebar:Date Range")} tooltip={DateRangeInfo}/>
<ControlHeader title={t("sidebar:Date Range")} tooltip={DateRangeInfo}/>
<DateRangeInputs />
<AnimationControls />

<AnnotatedHeader title={t("sidebar:Color By")} tooltip={ColorByInfo}/>
<ControlHeader title={t("sidebar:Color By")} tooltip={ColorByInfo}/>
<ColorBy />

<AnnotatedHeader title={t("sidebar:Filter Data")} tooltip={FilterInfo}/>
<FilterData measurementsOn={measurementsOn} />

{treeOn &&
<span>
<AnnotatedHeader title={t("sidebar:Tree Options")} tooltip={TreeOptionsInfo}/>
<ChooseLayout />
<ChooseMetric />
<ChooseBranchLabelling />
<ChooseTipLabel />
<ChooseSecondTree />
<ChooseExplodeAttr tooltip={ExplodeTreeInfo} />
<ToggleTangle />
</span>
<ControlHeader title={t("sidebar:Filter Data")} tooltip={FilterInfo}/>
<FilterData measurementsOn={panelsToDisplay.includes("measurements")} />

<span style={{ paddingTop: "10px" }} />

{panelsAvailable.includes("tree") &&
<PanelSection
panel="tree"
title={t("sidebar:Tree")}
tooltip={TreeInfo}
options={<>
<ChooseLayout />
<ChooseMetric />
<ChooseBranchLabelling />
<ChooseTipLabel />
<ChooseSecondTree />
<ChooseExplodeAttr tooltip={ExplodeTreeInfo} />
<ToggleTangle />
</>}
/>
}

{panelsAvailable.includes("measurements") &&
<PanelSection
panel="measurements"
title={t("sidebar:Measurements")}
tooltip={MeasurementsInfo}
options={<MeasurementsOptions />}
/>
}

{measurementsOn &&
<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Measurements Options")} tooltip={MeasurementsOptionsInfo}/>
<MeasurementsOptions />
</span>
{/* Prevent the map from being toggled on when a second tree is visible.
It is hidden by logic elsewhere.
*/}
{panelsAvailable.includes("map") && !showTreeToo &&
victorlin marked this conversation as resolved.
Show resolved Hide resolved
<PanelSection
panel="map"
title={t("sidebar:Map")}
tooltip={MapInfo}
options={<>
<GeoResolution />
<TransmissionLines />
</>}
/>
}

{mapOn &&
<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Map Options")} tooltip={MapOptionsInfo}/>
<GeoResolution />
<TransmissionLines />
</span>
{panelsAvailable.includes("entropy") &&
<PanelSection
panel="entropy"
title={t("sidebar:Entropy")}
tooltip={EntropyInfo}
/>
}

{frequenciesOn &&
<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Frequency Options")} tooltip={FrequencyInfo}/>
<NormalizeFrequencies />
</span>
{panelsAvailable.includes("frequencies") &&
<PanelSection
panel="frequencies"
title={t("sidebar:Frequency")}
tooltip={FrequencyInfo}
options={<NormalizeFrequencies />}
/>
}

<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Animation Options")} tooltip={AnimationOptionsInfo}/>
<ControlHeader title={t("sidebar:Animation Options")} tooltip={AnimationOptionsInfo}/>
<AnimationOptions />
</span>

{canTogglePanelLayout &&
<>
<span style={{ paddingTop: "10px" }} />
<ControlHeader title={t("sidebar:Display")} tooltip={PanelLayoutInfo} />
<PanelLayout />
</>
}

<span style={{ paddingTop: "10px" }} />
<AnnotatedHeader title={t("sidebar:Panel Options")} tooltip={PanelOptionsInfo}/>
<PanelLayout />
<PanelToggles />
<ControlHeader title={t("sidebar:Language")}/>
<Language />
victorlin marked this conversation as resolved.
Show resolved Hide resolved
</ControlsContainer>
);
Expand Down
35 changes: 13 additions & 22 deletions src/components/controls/language.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import React from "react";
import { connect } from "react-redux";
import { withTranslation } from "react-i18next";
import i18n from "i18next";

import { controlsWidth } from "../../util/globals";
import { analyticsControlsEvent } from "../../util/googleAnalytics";
import { SidebarSubtitle } from "./styles";
import { CHANGE_LANGUAGE } from "../../actions/types";
import CustomSelect from "./customSelect";

Expand Down Expand Up @@ -62,29 +60,22 @@ class Language extends React.Component {
}

render() {
const { t } = this.props;
const selectOptions = this.getlanguageOptions();
return (
<>
<SidebarSubtitle spaceAbove>
{t("sidebar:Language")}
</SidebarSubtitle>
<div style={{paddingBottom: 100, width: controlsWidth, fontSize: 14}}>
<CustomSelect
name="selectLanguage"
id="selectLanguage"
value={selectOptions.filter(({value}) => value === this.props.language)}
options={selectOptions}
isClearable={false}
isSearchable={false}
isMulti={false}
onChange={(opt) => {this.changeLanguage(opt.value);}}
/>
</div>
</>
<div style={{paddingBottom: 100, width: controlsWidth, fontSize: 14}}>
<CustomSelect
name="selectLanguage"
id="selectLanguage"
value={selectOptions.filter(({value}) => value === this.props.language)}
options={selectOptions}
isClearable={false}
isSearchable={false}
isMulti={false}
onChange={(opt) => {this.changeLanguage(opt.value);}}
/>
</div>
);
}
}
const WithTranslation = withTranslation()(Language);

export default WithTranslation;
export default Language;
Loading