Compare commits

..
Author SHA1 Message Date
kaloster 012d3b8b9e flakey test 2024-09-12 21:02:42 -04:00
Timmy Huang 151c28d119 chore: Add ESLint no-floating-promise 2024-09-12 13:36:26 -07:00
29 changed files with 841 additions and 758 deletions
+8 -3
View File
@@ -7,6 +7,10 @@ on:
branches:
- main
# For debug - uncomment below to run on all PRs
pull_request:
branches: "*"
env:
JEST_ENV: prod
@@ -28,13 +32,14 @@ jobs:
strategy:
fail-fast: false
matrix:
# note: The `macos-latest` is latest Catalina version, and not Big Sur. So we explicitly ask for Big Sur (`macos-11`)
os: [ubuntu-latest, macos-latest, macos-13]
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11"]
cellxgene_build: [main, latest]
# add anndata pinned version test for subset of matrix configurations,
# in order to reduce matrix cross-product explosion
include:
- python-version: 3.12
- python-version: 3.11
cellxgene_build: latest
# TODO: dynamically use the literal version in requirements.txt,
# to avoid having to update this in manually in the future
@@ -95,7 +100,7 @@ jobs:
# keep same pip pkg versions as in the cxg release
sed -i'' -e 's/-r requirements.txt//' server/requirements-dev.txt
pip install -r server/requirements-dev.txt
pip install --force-reinstall numpy==2.0.1 numba>=0.60.0 pandas flatbuffers==2.0.7
pip install --force-reinstall numpy==2.0.1 numba>=0.60.0 pandas
- name: Install anndata version per matrix variable
run: pip install anndata${{ matrix.anndata_version }}
- name: Install node
-20
View File
@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2017-2023 Chan Zuckerberg Initiative
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+3 -9
View File
@@ -1,4 +1,4 @@
<img src="./docs/cellxgene-logo.png" width="300">
![](https://github.com/chanzuckerberg/cellxgene/raw/main/docs/cellxgene-logo.png)
_an interactive explorer for single-cell transcriptomics data_
@@ -11,7 +11,8 @@ CZ CELLxGENE Annotate (pronounced "cell-by-gene") is an interactive data explore
Whether you need to visualize one thousand cells or one million, CELLxGENE Annotate helps you gain insight into your single-cell data.
<img src="https://github.com/chanzuckerberg/cellxgene/raw/main/docs/images/crossfilter.gif" width="350" height="200" hspace="30"><img src="https://github.com/chanzuckerberg/cellxgene/raw/main/docs/images/category-breakdown.gif" width="350" height="200" hspace="30">
![](https://github.com/chanzuckerberg/cellxgene/raw/main/docs/images/crossfilter.gif)
![](https://github.com/chanzuckerberg/cellxgene/raw/main/docs/images/category-breakdown.gif)
# Getting started
@@ -82,13 +83,6 @@ extension could be directly contributed, which would make it available for a wid
See the [CELLxGENE extensions](https://github.com/chanzuckerberg/cellxgene-documentation/blob/main/community-extensions.md) section of our documentation for examples of community use and CELLxGENE extensions.
### Trademarks
CZ CELLXGENE, CZ CELLXGENE DISCOVER, and CZ CELLXGENE ANNOTATE are trademarks of the Chan Zuckerberg Initiative. All rights reserved.
Use, reuse, modification, and re-distribution of the source code in this repository is subject to the terms of the applicable open source [license](LICENSE.txt). However, that license does not grant permission to use the trademarks without separate, express permission from the Chan Zuckerberg Initiative.
### Security
If you believe you have found a security issue, we would appreciate notification. Please send email to <security@chanzuckerberg.com>.
-2
View File
@@ -1,2 +0,0 @@
## Reporting Security Issues
If you believe you have found a security issue, please responsibly disclose by contacting us at [security@chanzuckerberg.com](mailto:security@chanzuckerberg.com).
+1 -1
View File
@@ -1,4 +1,4 @@
import * as ENV_DEFAULT from "../../../environment.default.json";
import * as ENV_DEFAULT from "Code/cellxgene/environment.default.json";
export const jestEnv = process.env.JEST_ENV || ENV_DEFAULT.JEST_ENV;
export const appUrlBase =
+1 -1
View File
@@ -5,8 +5,8 @@
*/
import { setDefaultOptions } from "expect-puppeteer";
import * as ENV_DEFAULT from "Code/cellxgene/environment.default.json";
import { isDebug, isDev } from "./config";
import * as ENV_DEFAULT from "../../../environment.default.json";
// (thuang): This is the max time a test can take to run.
// Since when debugging, we run slowMo and !headless, this means
+6 -6
View File
@@ -76,7 +76,7 @@ describe("PromiseLimit", () => {
const plimit = new PromiseLimit(1);
let finishOrder = 0;
const callback = () => async () => {
const callback = async () => {
await delay(100);
const result = finishOrder;
finishOrder += 1;
@@ -84,11 +84,11 @@ describe("PromiseLimit", () => {
};
const result = await Promise.all([
plimit.add(callback()),
plimit.priorityAdd(4, callback()),
plimit.priorityAdd(0, callback()),
plimit.priorityAdd(1, callback()),
plimit.priorityAdd(-1, callback()),
plimit.add(callback),
plimit.priorityAdd(4, callback),
plimit.priorityAdd(0, callback),
plimit.priorityAdd(1, callback),
plimit.priorityAdd(-1, callback),
]);
expect(result).toEqual([0, 4, 2, 3, 1]);
+2 -1
View File
@@ -82,13 +82,14 @@ module.exports = {
},
// Can't extend in overrides: https://github.com/eslint/eslint/issues/8813
// "extends": ["plugin:jest/recommended"]
plugins: ["jest"],
plugins: ["jest", "no-floating-promise"],
rules: {
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error",
"jest/prefer-to-have-length": "warn",
"jest/valid-expect": "error",
"no-floating-promise/no-floating-promise": 2,
},
},
],
+774 -645
View File
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -10,8 +10,8 @@
"dev": "npm run build -- configuration/webpack/webpack.config.dev.js",
"e2e": "jest --config __tests__/e2e/e2eJestConfig.json e2e/e2e.test.js",
"e2e-annotations": "jest --config __tests__/e2e/e2eJestConfig.json e2e/e2eAnnotations.test.js",
"fmt": "eslint --fix src __tests__",
"lint": "eslint --fix src __tests__",
"fmt": "eslint --fix __tests__",
"lint": "eslint --fix __tests__",
"prod": "npm run build -- configuration/webpack/webpack.config.prod.js",
"test": "jest --testPathIgnorePatterns e2e",
"prepare": "cd .. && husky install client/.husky"
@@ -92,7 +92,7 @@
"@babel/preset-react": "^7.13.13",
"@babel/register": "^7.13.16",
"@babel/runtime": "^7.13.16",
"@blueprintjs/eslint-plugin": "^0.3.0",
"@blueprintjs/eslint-plugin": "^6.1.4",
"@sentry/webpack-plugin": "^1.15.0",
"babel-jest": "^26.1.0",
"babel-loader": "^8.1.0",
@@ -104,15 +104,16 @@
"codecov": "^3.7.1",
"css-loader": "^5.2.4",
"css-minimizer-webpack-plugin": "^4.0.0",
"eslint": "^7.24.0",
"eslint-config-airbnb": "^18.2.0",
"eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.2.0",
"eslint-plugin-compat": "^4.2.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-jest": "^24.3.5",
"eslint-plugin-jest": "^28.8.3",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-no-floating-promise": "^2.0.0",
"eslint-plugin-react": "^7.23.2",
"eslint-plugin-react-hooks": "^4.0.8",
"expect-puppeteer": "^5.0.0",
@@ -1,8 +1,8 @@
import React from "react";
import * as globals from "../../globals";
const ErrorLoading = ({ displayName, zebra }) => (
<div
function ErrorLoading({ displayName, zebra }) {
return <div
style={{
backgroundColor: zebra ? globals.lightestGrey : "white",
fontStyle: "italic",
@@ -10,6 +10,6 @@ const ErrorLoading = ({ displayName, zebra }) => (
>
<span>{`Failure loading ${displayName}`}</span>
</div>
);
}
export default ErrorLoading;
@@ -5,7 +5,7 @@ import * as d3 from "d3";
import maybeScientific from "../../util/maybeScientific";
import clamp from "../../util/clamp";
const Histogram = ({
function Histogram({
field,
fieldForId,
display,
@@ -18,7 +18,7 @@ const Histogram = ({
isColorBy,
selectionRange,
mini,
}) => {
}) {
const svgRef = useRef(null);
const [brush, setBrush] = useState(null);
@@ -186,6 +186,6 @@ const Histogram = ({
ref={svgRef}
/>
);
};
}
export default Histogram;
@@ -3,12 +3,8 @@ import { Button } from "@blueprintjs/core";
import * as globals from "../../globals";
const StillLoading = ({ zebra, displayName }) =>
/*
Render a loading indicator for the field.
*/
(
<div
function StillLoading({ zebra, displayName }) {
return <div
data-testclass="gene-loading-spinner"
style={{
padding: globals.leftSidebarSectionPadding,
@@ -37,7 +33,6 @@ const StillLoading = ({ zebra, displayName }) =>
</div>
</div>
</div>
)
;
}
export default StillLoading;
@@ -71,8 +71,7 @@ class Category extends React.PureComponent {
const { metadataField, annotations, obsCrossfilter } = this.props;
return (
<>
<AnnoDialog
<AnnoDialog
isActive={
annotations.isAddingNewLabel &&
annotations.categoryAddingNewLabel === metadataField
@@ -105,7 +104,6 @@ class Category extends React.PureComponent {
/>
}
/>
</>
);
}
}
@@ -105,8 +105,7 @@ class AnnoDialogEditCategoryName extends React.PureComponent {
const { metadataField, annotations } = this.props;
return (
<>
<AnnoDialog
<AnnoDialog
isActive={
annotations.isEditingCategoryName &&
annotations.categoryBeingEdited === metadataField
@@ -141,7 +140,6 @@ class AnnoDialogEditCategoryName extends React.PureComponent {
/>
}
/>
</>
);
}
}
@@ -288,11 +288,8 @@ class Category extends React.PureComponent {
export default Category;
const StillLoading = ({ metadataField, checkboxID }) => (
/*
We are still loading this category, so render a "busy" signal.
*/
<div
function StillLoading({ metadataField, checkboxID }) {
return <div
style={{
maxWidth: globals.maxControlsWidth,
}}
@@ -335,8 +332,8 @@ const StillLoading = ({ metadataField, checkboxID }) => (
</div>
</div>
</div>
);
const ErrorLoading = ({ metadataField, error }) => {
}
function ErrorLoading({ metadataField, error }) {
console.error(error); // log error to console as it is unexpected.
return (
<div style={{ marginBottom: 10, marginTop: 4 }}>
@@ -352,7 +349,7 @@ const ErrorLoading = ({ metadataField, error }) => {
</span>
</div>
);
};
}
const CategoryHeader = React.memo(
({
@@ -162,7 +162,7 @@ class CategoryValue extends React.Component {
);
};
shouldComponentUpdate = (nextProps, nextState) => {
shouldComponentUpdate(nextProps, nextState) {
/*
Checks to see if at least one of the following changed:
* world state
+2 -2
View File
@@ -109,7 +109,7 @@ const loadAllEmbeddingCounts = async ({ annoMatrix, available }) => {
}));
};
const EmbeddingChoices = ({ onChange, annoMatrix, layoutChoice }) => {
function EmbeddingChoices({ onChange, annoMatrix, layoutChoice }) {
const { available } = layoutChoice;
const { data, error, isPending } = useAsync({
promiseFn: loadAllEmbeddingCounts,
@@ -149,4 +149,4 @@ const EmbeddingChoices = ({ onChange, annoMatrix, layoutChoice }) => {
);
}
return null;
};
}
+2 -2
View File
@@ -1,7 +1,7 @@
import React from "react";
import icon from "../../images/icon.png";
const Logo = (props) => {
function Logo(props) {
const { size } = props;
return (
<img
@@ -11,6 +11,6 @@ const Logo = (props) => {
alt="CELLxGENE Annotate Logo"
/>
);
};
}
export default Logo;
@@ -53,8 +53,7 @@ class AddGeneToGenesetDialogue extends React.PureComponent {
const { genesToAdd } = this.state;
return (
<>
<AnnoDialog
<AnnoDialog
isActive={genesetsUI.isAddingGenesToGeneset === geneset}
inputProps={{ "data-testid": `${geneset}:create-label-dialog` }}
primaryButtonProps={{
@@ -81,7 +80,6 @@ class AddGeneToGenesetDialogue extends React.PureComponent {
handleSubmit={this.handleAddGeneToGeneSet}
handleCancel={this.disableAddGeneMode}
/>
</>
);
}
}
@@ -125,8 +125,7 @@ class CreateGenesetDialogue extends React.PureComponent {
const { genesetsUI, genesets } = this.props;
return (
<>
<Dialog
<Dialog
icon="tag"
title="Create gene set"
isOpen={genesetsUI.createGenesetModeActive}
@@ -210,7 +209,6 @@ class CreateGenesetDialogue extends React.PureComponent {
</div>
</form>
</Dialog>
</>
);
}
}
@@ -109,8 +109,7 @@ class RenameGeneset extends React.PureComponent {
} = this.props;
return (
<>
<AnnoDialog
<AnnoDialog
isActive={genesetsUI.isEditingGenesetName === originalGenesetName}
inputProps={{
"data-testid": `${genesetsUI.isEditingGenesetName}:rename-geneset-dialog`,
@@ -161,7 +160,6 @@ class RenameGeneset extends React.PureComponent {
handleSubmit={this.renameGeneset}
handleCancel={this.disableEditGenesetNameMode}
/>
</>
);
}
}
+5 -10
View File
@@ -935,7 +935,7 @@ class Graph extends React.Component {
}
}
const ErrorLoading = ({ displayName, error, width, height }) => {
function ErrorLoading({ displayName, error, width, height }) {
console.log(error); // log to console as this is an unepected error
return (
<div
@@ -949,14 +949,10 @@ const ErrorLoading = ({ displayName, error, width, height }) => {
<span>{`Failure loading ${displayName}`}</span>
</div>
);
};
}
const StillLoading = ({ displayName, width, height }) =>
/*
Render a busy/loading indicator
*/
(
<div
function StillLoading({ displayName, width, height }) {
return <div
style={{
position: "fixed",
fontWeight: 500,
@@ -976,7 +972,6 @@ const StillLoading = ({ displayName, width, height }) =>
<span style={{ fontStyle: "italic" }}>Loading {displayName}</span>
</div>
</div>
)
;
}
export default Graph;
@@ -167,7 +167,7 @@ class CentroidLabels extends PureComponent {
}
}
const Label = ({
function Label({
label,
dilatedValue,
coords,
@@ -177,7 +177,7 @@ const Label = ({
displayLabel,
onMouseEnter,
onMouseOut,
}) => {
}) {
/*
Render a label at a given coordinate.
*/
@@ -215,4 +215,4 @@ const Label = ({
</text>
</g>
);
};
}
+1 -3
View File
@@ -159,8 +159,7 @@ export default class LabelInput extends React.PureComponent {
};
const { queryResults } = this.state;
return (
<>
<Suggest
<Suggest
fill
inputValueRenderer={(i) => i.target}
items={queryResults}
@@ -172,7 +171,6 @@ export default class LabelInput extends React.PureComponent {
inputProps={inputProps}
onKeyDown={this.handleKeyDown}
/>
</>
);
}
}
+2 -2
View File
@@ -35,11 +35,11 @@ export default class MiniHistogram extends React.PureComponent {
}
};
componentDidMount = () => {
componentDidMount() {
this.drawHistogram();
};
componentDidUpdate = (prevProps) => {
componentDidUpdate(prevProps) {
const { obsOrVarContinuousFieldDisplayName, bins } = this.props;
if (
prevProps.obsOrVarContinuousFieldDisplayName !==
@@ -42,12 +42,12 @@ export default class MiniStackedBar extends React.PureComponent {
}
};
componentDidUpdate = (prevProps) => {
componentDidUpdate(prevProps) {
const { occupancy } = this.props;
if (occupancy !== prevProps.occupancy) this.drawStacks();
};
componentDidMount = () => {
componentDidMount() {
this.drawStacks();
};
+1 -1
View File
@@ -32,7 +32,7 @@ const SECOND_HALF_INNER_STYLE = {
right: 0,
};
export default (props) => {
export default function(props) {
const { children, isGenesetDescription, tooltipAddendum = "" } = props;
// Truncate only support a single child with a text child
+1 -1
View File
@@ -1,6 +1,6 @@
import { Colors } from "@blueprintjs/core";
import ENV_DEFAULT from "Code/cellxgene/environment.default.json";
import { dispatchNetworkErrorMessageToUser } from "./util/actionHelpers";
import ENV_DEFAULT from "../../environment.default.json";
/* overflow category values are created using this string */
export const overflowCategoryLabel = ": all other labels";