diff --git a/client/__tests__/e2e/config.js b/client/__tests__/e2e/config.js
index d4748edd..80078f8b 100644
--- a/client/__tests__/e2e/config.js
+++ b/client/__tests__/e2e/config.js
@@ -1,9 +1,9 @@
-export const jest_env = process.env.JEST_ENV;
+export const jestEnv = process.env.JEST_ENV;
export const appPort = process.env.CXG_SERVER_PORT;
export const appUrlBase =
process.env.CXG_URL_BASE || `http://localhost:${appPort}`;
-export const DEV = jest_env === "dev";
-export const DEBUG = jest_env === "debug";
+export const DEV = jestEnv === "dev";
+export const DEBUG = jestEnv === "debug";
export const DATASET = "pbmc3k";
if (DEBUG) jest.setTimeout(100000);
diff --git a/client/__tests__/e2e/e2e.test.js b/client/__tests__/e2e/e2e.test.js
index 226d37f1..76e1b4d0 100644
--- a/client/__tests__/e2e/e2e.test.js
+++ b/client/__tests__/e2e/e2e.test.js
@@ -7,7 +7,10 @@ import { appUrlBase, DATASET } from "./config";
import { setupTestBrowser } from "./testBrowser";
import { datasets } from "./data";
-let browser, page, utils, cxgActions;
+let browser;
+let page;
+let utils;
+let cxgActions;
const data = datasets[DATASET];
beforeAll(async () => {
@@ -34,9 +37,9 @@ describe("did launch", () => {
try {
await utils.clickOn("tos-cookies-accept", { timeout: 500 });
} catch {
- console.warn("No terms of service footer detected.")
+ console.warn("No terms of service footer detected.");
}
- page.waitFor(50); // give the footer a chance to disappear
+ page.waitFor(50); // give the footer a chance to disappear
const result = await page.$("[data-testid='tos-cookies-accept']");
expect(result).toBeNull();
});
@@ -130,7 +133,7 @@ describe("gene entry", () => {
testGenes
);
expect(allHistograms).toEqual(expect.arrayContaining(testGenes));
- expect(allHistograms.length).toEqual(testGenes.length);
+ expect(allHistograms).toHaveLength(testGenes.length);
});
});
@@ -144,7 +147,7 @@ describe("differential expression", () => {
expect(allHistograms).toEqual(
expect.arrayContaining(data.diffexp["gene-results"])
);
- expect(allHistograms.length).toEqual(data.diffexp["gene-results"].length);
+ expect(allHistograms).toHaveLength(data.diffexp["gene-results"].length);
});
});
diff --git a/client/__tests__/e2e/e2eAnnotations.test.js b/client/__tests__/e2e/e2eAnnotations.test.js
index 6da0c42e..f0041529 100644
--- a/client/__tests__/e2e/e2eAnnotations.test.js
+++ b/client/__tests__/e2e/e2eAnnotations.test.js
@@ -5,7 +5,10 @@ import { appUrlBase, DATASET } from "./config";
import { setupTestBrowser } from "./testBrowser";
import { datasets } from "./data";
-let browser, page, utils, actions;
+let browser;
+let page;
+let utils;
+let actions;
const data = datasets[DATASET];
beforeAll(async () => {
diff --git a/client/__tests__/e2e/feature.test.js b/client/__tests__/e2e/feature.test.js
index 0fed000a..9e7adc11 100644
--- a/client/__tests__/e2e/feature.test.js
+++ b/client/__tests__/e2e/feature.test.js
@@ -12,9 +12,13 @@ import { appUrlBase, DEBUG, DEV, DATASET } from "./config";
import { puppeteerUtils, cellxgeneActions } from "./puppeteerUtils";
import { datasets } from "./data";
-let browser, page, utils, cxgActions, spy;
+let browser;
+let page;
+let utils;
+let cxgActions;
+let spy;
const browserViewport = { width: 1280, height: 960 };
-let data = datasets[DATASET].features;
+const data = datasets[DATASET].features;
if (DEBUG) jest.setTimeout(100000);
if (DEV) jest.setTimeout(10000);
diff --git a/client/__tests__/util/promiseLimit.test.js b/client/__tests__/util/promiseLimit.test.js
index fc6832f6..659e2f23 100644
--- a/client/__tests__/util/promiseLimit.test.js
+++ b/client/__tests__/util/promiseLimit.test.js
@@ -1,4 +1,4 @@
-import { PromiseLimit } from "../../src/util/promiseLimit";
+import PromiseLimit from "../../src/util/promiseLimit";
import { range } from "../../src/util/range";
const delay = (t) => new Promise((resolve, reject) => setTimeout(resolve, t));
diff --git a/client/configuration/webpack/cspHashPlugin.js b/client/configuration/webpack/cspHashPlugin.js
index d56d2b32..99afd46f 100644
--- a/client/configuration/webpack/cspHashPlugin.js
+++ b/client/configuration/webpack/cspHashPlugin.js
@@ -54,7 +54,7 @@ class CspHashPlugin {
.createHash("sha256")
.update(str, "utf8")
.digest("base64");
- return "sha256-" + hash;
+ return `sha256-${hash}`;
}
}
diff --git a/client/src/actions/index.js b/client/src/actions/index.js
index 19ff498d..0bfffd51 100644
--- a/client/src/actions/index.js
+++ b/client/src/actions/index.js
@@ -7,7 +7,7 @@ import {
doBinaryRequest,
dispatchNetworkErrorMessageToUser,
} from "../util/actionHelpers";
-import { PromiseLimit } from "../util/promiseLimit";
+import PromiseLimit from "../util/promiseLimit";
import { requestReembed, reembedResetWorldToUniverse } from "./reembed";
import { loadUserColorConfig } from "../util/stateManager/colorHelpers";
diff --git a/client/src/components/autosave/filenameDialog.js b/client/src/components/autosave/filenameDialog.js
index e25792d1..c2f02e64 100644
--- a/client/src/components/autosave/filenameDialog.js
+++ b/client/src/components/autosave/filenameDialog.js
@@ -18,7 +18,7 @@ import {
saveInProgress: state.autosave?.saveInProgress ?? false,
lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations,
error: state.autosave?.error,
- writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false,
+ writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
}))
class FilenameDialog extends React.Component {
constructor(props) {
diff --git a/client/src/components/autosave/index.js b/client/src/components/autosave/index.js
index bffd1740..d77dcc65 100644
--- a/client/src/components/autosave/index.js
+++ b/client/src/components/autosave/index.js
@@ -1,6 +1,5 @@
import React from "react";
import { connect } from "react-redux";
-import * as globals from "../../globals";
import actions from "../../actions";
import FilenameDialog from "./filenameDialog";
@@ -11,7 +10,7 @@ import FilenameDialog from "./filenameDialog";
saveInProgress: state.autosave?.saveInProgress ?? false,
lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations,
error: state.autosave?.error,
- writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false,
+ writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
initialDataLoadComplete: state.autosave?.initialDataLoadComplete,
}))
class Autosave extends React.Component {
diff --git a/client/src/components/categorical/category/index.js b/client/src/components/categorical/category/index.js
index e3c6b239..dfd5e296 100644
--- a/client/src/components/categorical/category/index.js
+++ b/client/src/components/categorical/category/index.js
@@ -64,7 +64,7 @@ class Category extends React.Component {
} else if (categoryCount.selectedCatCount < categoryCount.totalCatCount) {
/* to be explicit... */
this.checkbox.indeterminate = true;
- this.setState({ isChecked: false });
+ this.setState({ isChecked: false }); // eslint-disable-line react/no-did-update-set-state
}
}
}
@@ -77,14 +77,15 @@ class Category extends React.Component {
});
};
- toggleAll() {
- const { dispatch, metadataField } = this.props;
- dispatch({
- type: "categorical metadata filter all of these",
- metadataField,
- });
- this.setState({ isChecked: true });
- }
+ handleCategoryClick = () => {
+ const { annotations, metadataField, onExpansionChange } = this.props;
+ const editingCategory =
+ annotations.isEditingCategoryName &&
+ annotations.categoryBeingEdited === metadataField;
+ if (!editingCategory) {
+ onExpansionChange(metadataField);
+ }
+ };
toggleNone() {
const { dispatch, metadataField } = this.props;
@@ -95,6 +96,15 @@ class Category extends React.Component {
this.setState({ isChecked: false });
}
+ toggleAll() {
+ const { dispatch, metadataField } = this.props;
+ dispatch({
+ type: "categorical metadata filter all of these",
+ metadataField,
+ });
+ this.setState({ isChecked: true });
+ }
+
handleToggleAllClick() {
const { isChecked } = this.state;
// || this.checkbox.indeterminate === false
@@ -115,6 +125,8 @@ class Category extends React.Component {
globals.categoryDisplayStringMaxLength
);
+ const checkboxID = `category-select-${metadataField}`;
+
return (
-
diff --git a/client/src/index.js b/client/src/index.js
index b4655afa..7f2de221 100644
--- a/client/src/index.js
+++ b/client/src/index.js
@@ -1,5 +1,4 @@
// jshint esversion: 6
-/* eslint-disable no-console */
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
diff --git a/client/src/reducers/colors.js b/client/src/reducers/colors.js
index aa10d077..a7c566fe 100644
--- a/client/src/reducers/colors.js
+++ b/client/src/reducers/colors.js
@@ -150,7 +150,7 @@ const ColorsReducer = (
case "annotation: delete label": {
const { world } = nextSharedState;
const { colorMode, colorAccessor } = state;
- const { metadataField, colors } = action;
+ const { metadataField } = action;
if (
colorMode !== "color by categorical metadata" ||
colorAccessor !== metadataField
diff --git a/client/src/reducers/layoutChoice.js b/client/src/reducers/layoutChoice.js
index 12486bc0..fa04f6f7 100644
--- a/client/src/reducers/layoutChoice.js
+++ b/client/src/reducers/layoutChoice.js
@@ -49,7 +49,7 @@ const LayoutChoice = (
}
case "reembed: add reembedding": {
- const name = action.schema.name;
+ const { name } = action.schema;
const available = Array.from(new Set(state.available).add(name));
return {
...state,
diff --git a/client/src/reducers/ontology.js b/client/src/reducers/ontology.js
index 504d26ff..828bc88a 100644
--- a/client/src/reducers/ontology.js
+++ b/client/src/reducers/ontology.js
@@ -9,11 +9,12 @@ const Ontology = (
) => {
switch (action.type) {
case "configuration load complete": {
- /* eslint-disable camelcase */
const enabled =
- action.config?.parameters?.annotations_cell_ontology_enabled ?? false;
- const terms = action.config?.parameters?.annotations_cell_ontology_terms;
- /* eslint-enable camelcase */
+ action.config?.parameters?.["annotations_cell_ontology_enabled"] ??
+ false;
+ const terms =
+ action.config?.parameters?.["annotations_cell_ontology_terms"];
+
const termSet = new Set(terms);
return {
...state,
diff --git a/client/src/reducers/world.js b/client/src/reducers/world.js
index 0e9685ad..6a62df26 100644
--- a/client/src/reducers/world.js
+++ b/client/src/reducers/world.js
@@ -41,8 +41,8 @@ const WorldReducer = (
const { dim } = action;
// we don't clip anything except for varData and obsAnnotations
- let unclipped = state.unclipped;
- if (dim == "varData" || dim == "obsAnnotations") {
+ let { unclipped } = state;
+ if (dim === "varData" || dim === "obsAnnotations") {
unclipped = {
...unclipped,
[dim]: universe[dim].clone(),
@@ -298,7 +298,7 @@ const WorldReducer = (
const { obsLayout: origObsLayout, schema: origSchema } = state;
const { embedding, schema: embeddingSchema } = action;
- const { dims, name } = embeddingSchema;
+ const { dims } = embeddingSchema;
let obsLayout = origObsLayout;
let schema = origSchema;
diff --git a/client/src/util/dataframe/labelIndex.js b/client/src/util/dataframe/labelIndex.js
index 0b01adda..79f69802 100644
--- a/client/src/util/dataframe/labelIndex.js
+++ b/client/src/util/dataframe/labelIndex.js
@@ -1,3 +1,4 @@
+/* eslint-disable max-classes-per-file */
/**
Label indexing - map a label to & from an integer offset. See Dataframe
for how this is used.
@@ -23,7 +24,6 @@ function extent(tarr) {
return [min, max];
}
-/* eslint-disable class-methods-use-this */
class IdentityInt32Index {
/*
identity/noop index, with small assumptions that labels are int32
@@ -41,11 +41,13 @@ class IdentityInt32Index {
return k;
}
+ // eslint-disable-next-line class-methods-use-this
getOffset(i) {
// label to offset
return i;
}
+ // eslint-disable-next-line class-methods-use-this
getLabel(i) {
// offset to label
return i;
@@ -93,9 +95,6 @@ class IdentityInt32Index {
return this.__promote(labelArray);
}
}
-/* eslint-enable class-methods-use-this */
-
-/* eslint-disable class-methods-use-this */
class DenseInt32Index {
/*
DenseInt32Index indexes integer labels, and uses Int32Array typed arrays
@@ -177,9 +176,7 @@ class DenseInt32Index {
return this.__promote(labelArray);
}
}
-/* eslint-enable class-methods-use-this */
-/* eslint-disable class-methods-use-this */
class KeyIndex {
/*
KeyIndex indexes arbitrary JS primitive types, and uses a Map()
@@ -223,6 +220,7 @@ class KeyIndex {
return this.rindex.length;
}
+ // eslint-disable-next-line class-methods-use-this
subsetLabels(labelArray) {
return new KeyIndex(labelArray);
}
diff --git a/client/src/util/promiseLimit.js b/client/src/util/promiseLimit.js
index ad502fbc..1b1731b3 100644
--- a/client/src/util/promiseLimit.js
+++ b/client/src/util/promiseLimit.js
@@ -13,7 +13,7 @@ return Promise.all([
plimit.add(() => fetch('/baz'))
])
*/
-export class PromiseLimit {
+export default class PromiseLimit {
constructor(maxConcurrency) {
this.queue = new Set();
this.maxConcurrency = maxConcurrency;
diff --git a/client/src/util/significantDigits.js b/client/src/util/significantDigits.js
index 6d3e005e..21fbf83f 100644
--- a/client/src/util/significantDigits.js
+++ b/client/src/util/significantDigits.js
@@ -5,6 +5,6 @@
export default (n) => {
return n
.toExponential()
- .replace(/e[\+\-0-9]*$/, "")
+ .replace(/e[+\-0-9]*$/, "")
.replace(/^0\.?0*|\./, "").length;
};
diff --git a/client/src/util/typedCrossfilter/crossfilter.js b/client/src/util/typedCrossfilter/crossfilter.js
index f328d3a8..3ee99386 100644
--- a/client/src/util/typedCrossfilter/crossfilter.js
+++ b/client/src/util/typedCrossfilter/crossfilter.js
@@ -1,3 +1,4 @@
+// eslint-disable-next-line max-classes-per-file
import PositiveIntervals from "./positiveIntervals";
import BitArray from "./bitArray";
import {
@@ -409,7 +410,7 @@ class ImmutableScalarDimension extends _ImmutableBaseDimension {
this.index = makeSortIndex(array);
}
- /* eslint-disable class-methods-use-this */
+ // eslint-disable-next-line class-methods-use-this
_createValueArray(data, mapf, array) {
// create dimension value array
const len = data.length;
@@ -419,7 +420,6 @@ class ImmutableScalarDimension extends _ImmutableBaseDimension {
}
return larray;
}
- /* eslint-enable class-methods-use-this */
select(spec) {
const { mode } = spec;
@@ -466,7 +466,7 @@ class ImmutableScalarDimension extends _ImmutableBaseDimension {
const ranges = [];
const r = [
lowerBoundIndirect(value, index, lo, 0, value.length),
- !!inclusive
+ inclusive
? upperBoundIndirect(value, index, hi, 0, value.length)
: lowerBoundIndirect(value, index, hi, 0, value.length),
];
@@ -514,11 +514,10 @@ class ImmutableEnumDimension extends ImmutableScalarDimension {
});
}
- /* eslint-disable class-methods-use-this */
+ // eslint-disable-next-line class-methods-use-this
selectRange() {
throw new Error("range selection unsupported on Enumerated dimension");
}
- /* eslint-enable class-methods-use-this */
}
class ImmutableSpatialDimension extends _ImmutableBaseDimension {