Skip to content

Commit

Permalink
improve type handling for non-string annotation data (#465)
Browse files Browse the repository at this point in the history
* improve type handling for non-string annotation data

* improve clarity of code
  • Loading branch information
Bruce Martin authored and csweaver committed Nov 26, 2018
1 parent 2b50946 commit 138d309
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 159 deletions.
51 changes: 6 additions & 45 deletions client/src/components/categorical/categorical.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,13 @@ import { connect } from "react-redux";
import * as globals from "../../globals";
import Category from "./category";

/* Cap the max number of displayed categories */
const truncateCategories = options => {
const numOptions = _.size(options);
if (numOptions <= globals.maxCategoricalOptionsToDisplay) {
return options;
}
return _(options)
.map((v, k) => ({ name: k, val: v }))
.sortBy("val")
.slice(numOptions - globals.maxCategoricalOptionsToDisplay)
.transform((r, v) => {
r[v.name] = v.val;
}, {})
.value();
};

@connect(state => ({
ranges: _.get(state.controls.world, "summary.obs", null),
categorySelectionLimit: _.get(
state.config,
"parameters.max-category-items",
globals.configDefaults.parameters["max-category-items"]
)
categoricalSelectionState: state.controls.categoricalSelectionState
}))
class Categories extends React.Component {
render() {
const { ranges, categorySelectionLimit } = this.props;
if (!ranges) return null;
const { categoricalSelectionState } = this.props;
if (!categoricalSelectionState) return null;

return (
<div
Expand All @@ -47,27 +26,9 @@ class Categories extends React.Component {
>
Categorical Metadata
</p>
{_.map(ranges, (value, key) => {
const isColorField = key.includes("color") || key.includes("Color");
const isSelectableCategory =
value.options &&
!isColorField &&
key !== "name" &&
value.numOptions < categorySelectionLimit;

if (isSelectableCategory) {
const categoryOptions = truncateCategories(value.options);
return (
<Category
key={key}
metadataField={key}
values={categoryOptions}
isTruncated={categoryOptions !== value.options}
/>
);
}
return undefined;
})}
{_.map(categoricalSelectionState, (catState, catName) => (
<Category key={catName} metadataField={catName} />
))}
</div>
);
}
Expand Down
72 changes: 34 additions & 38 deletions client/src/components/categorical/category.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,15 @@ import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import { FaChevronRight, FaChevronDown } from "react-icons/fa";
import memoize from "memoize-one";
import { Button, Tooltip, Position } from "@blueprintjs/core";
import { Button, Tooltip } from "@blueprintjs/core";

import * as globals from "../../globals";
import Value from "./value";
import alphabeticallySortedValues from "./util";

const countCategories = (values, optsAsBools) =>
_.reduce(
values,
(r, v, k) => {
r.total += 1;
if (optsAsBools[k]) {
r.on += 1;
}
return r;
},
{ total: 0, on: 0 }
);

@connect(state => ({
colorAccessor: state.controls.colorAccessor,
categoricalAsBooleansMap: state.controls.categoricalAsBooleansMap
categoricalSelectionState: state.controls.categoricalSelectionState
}))
class Category extends React.Component {
constructor(props) {
Expand All @@ -33,24 +19,30 @@ class Category extends React.Component {
isChecked: true,
isExpanded: false
};
this.countCategories = memoize((values, optsAsBools) =>
countCategories(values, optsAsBools)
);
}

componentDidUpdate() {
const { categoricalAsBooleansMap, metadataField, values } = this.props;
const categoryCount = this.countCategories(
values,
categoricalAsBooleansMap[metadataField]
);
if (categoryCount.on === categoryCount.total) {
const { categoricalSelectionState, metadataField } = this.props;
const cat = categoricalSelectionState[metadataField];
const categoryCount = {
// total number of options in this category
totalOptionCount: cat.numOptions,
// number of selected options in this category
selectedOptionCount: _.reduce(
cat.optionSelected,
(res, cond) => (cond ? res + 1 : res),
0
)
};
if (categoryCount.selectedOptionCount === categoryCount.totalOptionCount) {
/* everything is on, so not indeterminate */
this.checkbox.indeterminate = false;
} else if (categoryCount.on === 0) {
} else if (categoryCount.selectedOptionCount === 0) {
/* nothing is on, so no */
this.checkbox.indeterminate = false;
} else if (categoryCount.on < categoryCount.total) {
} else if (
categoryCount.selectedOptionCount < categoryCount.totalOptionCount
) {
/* to be explicit... */
this.checkbox.indeterminate = true;
}
Expand All @@ -74,11 +66,10 @@ class Category extends React.Component {
}

toggleNone() {
const { dispatch, metadataField, value } = this.props;
const { dispatch, metadataField } = this.props;
dispatch({
type: "categorical metadata filter none of these",
metadataField,
value
metadataField
});
this.setState({ isChecked: false });
}
Expand All @@ -94,26 +85,31 @@ class Category extends React.Component {
}

renderCategoryItems() {
const { values, metadataField } = this.props;
return _.map(alphabeticallySortedValues(values), (v, i) => (
const { categoricalSelectionState, metadataField } = this.props;

const cat = categoricalSelectionState[metadataField];
const optTuples = alphabeticallySortedValues([...cat.optionIndex]);
return _.map(optTuples, (tuple, i) => (
<Value
key={v}
key={tuple[1]}
metadataField={metadataField}
count={values[v]}
value={v}
optionIndex={tuple[1]}
i={i}
/>
));
}

render() {
const { isExpanded, isChecked } = this.state;
const { metadataField, colorAccessor, isTruncated } = this.props;
const {
metadataField,
colorAccessor,
categoricalSelectionState
} = this.props;
const { isTruncated } = categoricalSelectionState[metadataField];
return (
<div
style={{
// display: "flex",
// alignItems: "baseline",
maxWidth: globals.maxControlsWidth
}}
>
Expand Down
10 changes: 7 additions & 3 deletions client/src/components/categorical/util.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// jshint esversion: 6

// values is [ [optVal, optIdx], ...]
// index is range array
// return sorted index
export default values =>
Object.keys(values).sort((a, b) => {
const textA = a.toUpperCase();
const textB = b.toUpperCase();
values.sort((a, b) => {
const textA = String(a[0]).toUpperCase();
const textB = String(b[0]).toUpperCase();
return textA < textB ? -1 : textA > textB ? 1 : 0;
});
26 changes: 15 additions & 11 deletions client/src/components/categorical/value.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,49 @@ import React from "react";
import _ from "lodash";

@connect(state => ({
categoricalAsBooleansMap: state.controls.categoricalAsBooleansMap,
categoricalSelectionState: state.controls.categoricalSelectionState,
colorScale: state.controls.colorScale,
colorAccessor: state.controls.colorAccessor,
schema: _.get(state.controls.world, "schema", null)
}))
class CategoryValue extends React.Component {
toggleOff() {
const { dispatch, metadataField, value } = this.props;
const { dispatch, metadataField, optionIndex } = this.props;
dispatch({
type: "categorical metadata filter deselect",
metadataField,
value
optionIndex
});
}

toggleOn() {
const { dispatch, metadataField, value } = this.props;
const { dispatch, metadataField, optionIndex } = this.props;
dispatch({
type: "categorical metadata filter select",
metadataField,
value
optionIndex
});
}

render() {
const {
categoricalAsBooleansMap,
categoricalSelectionState,
metadataField,
count,
value,
optionIndex,
colorAccessor,
colorScale,
i,
schema
} = this.props;

if (!categoricalAsBooleansMap) return null;
if (!categoricalSelectionState) return null;

const category = categoricalSelectionState[metadataField];
const selected = category.optionSelected[optionIndex];
const count = category.optionCount[optionIndex];
const value = category.optionValue[optionIndex];
const displayString = String(category.optionValue[optionIndex]).valueOf();

const selected = categoricalAsBooleansMap[metadataField][value];
/* this is the color scale, so add swatches below */
const c = metadataField === colorAccessor;
let categories = null;
Expand Down Expand Up @@ -78,7 +82,7 @@ class CategoryValue extends React.Component {
type="checkbox"
/>
<span className="bp3-control-indicator" />
{value}
{displayString}
</label>
</div>
<span>
Expand Down
Loading

0 comments on commit 138d309

Please sign in to comment.