Skip to content

Commit

Permalink
Begin implementation of typing-based data filtering
Browse files Browse the repository at this point in the history
See nextstrain#1200 for the motivation
for a typing-based filter query box. This commit starts the implementation
which is functional but incomplete.

Implements the star
  • Loading branch information
jameshadfield committed Oct 26, 2020
1 parent f2b68a0 commit e2fa24b
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/components/controls/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import SearchStrains from "./search";
import ToggleTangle from "./toggle-tangle";
import Language from "./language";
import { SidebarHeader, ControlsContainer } from "./styles";

import FilterData from "./filter";

function Controls({mapOn, frequenciesOn}) {
const { t } = useTranslation();
Expand All @@ -33,6 +33,10 @@ function Controls({mapOn, frequenciesOn}) {
<SidebarHeader>{t("sidebar:Color By")}</SidebarHeader>
<ColorBy />

<SidebarHeader>{t("sidebar:Filter Data")}</SidebarHeader>
<FilterData />


<SidebarHeader>{t("sidebar:Tree Options")}</SidebarHeader>
<ChooseLayout />
<ChooseMetric />
Expand Down
78 changes: 78 additions & 0 deletions src/components/controls/filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from "react";
import { connect } from "react-redux";
import Select from "react-select/lib/Select";
import { controlsWidth, isValueValid } from "../../util/globals";
import { applyFilter } from "../../actions/tree";

/**
* <FilterData> is a (keyboard)-typing based search box intended to
* allow users to filter samples. The filtering rules are not implemented
* in this component, but are useful to spell out: we take the union of
* entries within each category and then take the intersection of those unions.
*/
@connect((state) => {
return {
activeFilters: state.controls.filters,
totalStateCounts: state.tree.totalStateCounts
};
})
class FilterData extends React.Component {
constructor(props) {
super(props);
}

getStyles() {
return {
base: {
width: controlsWidth,
marginBottom: 0,
fontSize: 14
}
};
}
makeOptions = () => {
/**
* The <Select> component needs an array of options to display (and search across). We compute this
* by looping across each filter and calculating all valid options for each. This function runs
* each time a filter is toggled on / off.
*/
const options = [];
Object.keys(this.props.activeFilters)
.forEach((filterName) => {
Array.from(this.props.totalStateCounts[filterName].keys())
.filter((itemName) => isValueValid(itemName)) // remove invalid values present across the tree
.filter((itemName) => !this.props.activeFilters[filterName].includes(itemName)) // remove already enabled filters
.sort() // filters are sorted alphabetically - probably not necessary for a select component
.forEach((itemName) => {
options.push({
label: `${filterName}${itemName}`,
value: [filterName, itemName]
});
});
});
return options;
}
selectionMade = (sel) => {
this.props.dispatch(applyFilter("add", sel.value[0], sel.value[1]));
}
render() {
const styles = this.getStyles();
return (
<div style={styles.base}>
<Select
name="filterQueryBox"
placeholder="Type filter query here..."
value={undefined}
arrowRenderer={null}
options={this.makeOptions()}
clearable={false}
searchable
multi={false}
onChange={this.selectionMade}
/>
</div>
);
}
}

export default FilterData;

0 comments on commit e2fa24b

Please sign in to comment.