refactor categorical controls state (#1549)

* refactor categorical controls state

* lint

* fix race condition in tests

* fix typo

* add missing update on subset

* remove obsolete code

* update jest and puppeteer major version; update all minors

* update when label changes

* remove lint from tests; increase timeouts in e2e tests

* changes in response to PR review

* lint

* more PR comment changes

* more PR comment fixes

* lint

* more PR comment resolutions
This commit is contained in:
Bruce Martin
2020-06-10 12:56:38 -07:00
committed by GitHub
parent f063708b33
commit bc7a712048
25 changed files with 5097 additions and 3510 deletions
+34 -47
View File
@@ -1,11 +1,17 @@
import { ControlsHelpers as CH } from "../util/stateManager";
const CategoricalSelection = (
state,
action,
nextSharedState,
prevSharedState
) => {
/*
State is an object, with a key for each categorical annotation, and a
value which is a Map of label->t/f, reflecting selection state for the label.
Label state default (if missing) is up to the component, but typically true.
{
"louvain": Map(),
...
}
*/
const CategoricalSelection = (state, action, nextSharedState) => {
switch (action.type) {
case "initial data load complete (universe exists)":
case "set World to current selection":
@@ -13,11 +19,7 @@ const CategoricalSelection = (
case "set clip quantiles": {
const { world } = nextSharedState;
const newState = CH.createCategoricalSelection(
world,
CH.selectableCategoryNames(
world.schema,
CH.maxCategoryItems(prevSharedState.config)
)
CH.selectableCategoryNames(world.schema)
);
return newState;
}
@@ -30,13 +32,12 @@ const CategoricalSelection = (
const { world } = nextSharedState;
const names = CH.selectableCategoryNames(
world.schema,
CH.maxCategoryItems(prevSharedState.config),
dataframe.colIndex.labels()
);
if (names.length === 0) return state;
return {
...state,
...CH.createCategoricalSelection(world, names),
...CH.createCategoricalSelection(names),
};
}
@@ -44,16 +45,12 @@ const CategoricalSelection = (
/*
Set the specific category in this field to false
*/
const newCategoryValueSelected = Array.from(
state[action.metadataField].categoryValueSelected
);
newCategoryValueSelected[action.categoryIndex] = true;
const { metadataField, label } = action;
const newSelected = new Map(state[metadataField]);
newSelected.set(label, true);
const newCategoricalSelection = {
...state,
[action.metadataField]: {
...state[action.metadataField],
categoryValueSelected: newCategoryValueSelected,
},
[action.metadataField]: newSelected,
};
return newCategoricalSelection;
}
@@ -62,16 +59,12 @@ const CategoricalSelection = (
/*
Set the specific category in this field to false
*/
const newCategoryValueSelected = Array.from(
state[action.metadataField].categoryValueSelected
);
newCategoryValueSelected[action.categoryIndex] = false;
const { metadataField, label } = action;
const newSelected = new Map(state[metadataField]);
newSelected.set(label, false);
const newCategoricalSelection = {
...state,
[action.metadataField]: {
...state[action.metadataField],
categoryValueSelected: newCategoryValueSelected,
},
[action.metadataField]: newSelected,
};
return newCategoricalSelection;
}
@@ -80,15 +73,13 @@ const CategoricalSelection = (
/*
set all categories in this field to false.
*/
const { metadataField, labels } = action;
const { selected } = state[metadataField];
const newSelected = new Map(selected);
labels.forEach((label) => newSelected.set(label, false));
const newCategoricalSelection = {
...state,
[action.metadataField]: {
...state[action.metadataField],
categorySelected: false,
categoryValueSelected: Array.from(
state[action.metadataField].categoryValueSelected
).fill(false),
},
[action.metadataField]: newSelected,
};
return newCategoricalSelection;
}
@@ -97,25 +88,22 @@ const CategoricalSelection = (
/*
set all categories in this field to true.
*/
const { metadataField, labels } = action;
const { selected } = state[metadataField];
const newSelected = new Map(selected);
labels.forEach((label) => newSelected.set(label, true));
const newCategoricalSelection = {
...state,
[action.metadataField]: {
...state[action.metadataField],
categorySelected: true,
categoryValueSelected: Array.from(
state[action.metadataField].categoryValueSelected
).fill(true),
},
[action.metadataField]: newSelected,
};
return newCategoricalSelection;
}
case "annotation: create category": {
const { world } = nextSharedState;
const name = action.data;
return {
...state,
...CH.createCategoricalSelection(world, [name]),
...CH.createCategoricalSelection([name]),
};
}
@@ -138,12 +126,11 @@ const CategoricalSelection = (
case "annotation: label edited":
case "annotation: delete label": {
/* need to rebuild the state for this annotation */
const { world } = nextSharedState;
const name = action.metadataField;
const { [name]: _, ...partialState } = state;
return {
...partialState,
...CH.createCategoricalSelection(world, [name]),
...CH.createCategoricalSelection([name]),
};
}
+3 -13
View File
@@ -26,14 +26,7 @@ const centroidLabels = (state = initialState, action, sharedNextState) => {
...state,
labels:
!!colorAccessor && showLabels && !!categoricalSelection[colorAccessor]
? calcCentroid(
world.obsAnnotations,
world.obsLayout,
colorAccessor,
layoutChoice.currentDimNames,
categoricalSelection,
world.schema.annotations.obsByName
)
? calcCentroid(world, colorAccessor, layoutChoice.currentDimNames)
: [],
};
@@ -52,12 +45,9 @@ const centroidLabels = (state = initialState, action, sharedNextState) => {
return {
...state,
labels: calcCentroid(
world.obsAnnotations,
world.obsLayout,
world,
colorAccessor,
layoutChoice.currentDimNames,
categoricalSelection,
world.schema.annotations.obsByName
layoutChoice.currentDimNames
),
showLabels,
};
+3 -4
View File
@@ -260,10 +260,9 @@ const CrossfilterReducerBase = (
case "categorical metadata filter select":
case "categorical metadata filter deselect": {
const { categoricalSelection } = nextSharedState;
const cat = categoricalSelection[action.metadataField];
const { categoryValues, categoryValueSelected } = cat;
const values = categoryValues.filter((v, i) => categoryValueSelected[i]);
const { labels, metadataField } = action;
const selected = nextSharedState.categoricalSelection[metadataField];
const values = labels.filter((label) => selected.get(label) ?? true);
return state.select(obsAnnoDimensionName(action.metadataField), {
mode: "exact",
values,
+2 -6
View File
@@ -3,12 +3,8 @@ const initialState = {
categoryField: "",
};
const pointDialation = (state = initialState, action, sharedNextState) => {
const { categoricalSelection } = sharedNextState;
const { metadataField, categoryIndex } = action;
const categoryField =
action.categoryField ||
categoricalSelection?.[metadataField]?.categoryValues[categoryIndex];
const pointDialation = (state = initialState, action) => {
const { metadataField, label: categoryField } = action;
switch (action.type) {
case "category value mouse hover start":