run prettier(2.0.5) (#1438)

This commit is contained in:
Severiano Badajoz
2020-05-04 10:26:42 -07:00
committed by GitHub
parent b255e32548
commit 6cccc41c0f
118 changed files with 1979 additions and 1584 deletions

View File

@@ -1,7 +1,6 @@
import { strict as assert } from "assert";
export const cellxgeneActions = (page, utils) => ({
async drag(testId, start, end, lasso = false) {
const layout = await utils.waitByID(testId);
const elBox = await layout.boxModel();
@@ -31,22 +30,29 @@ export const cellxgeneActions = (page, utils) => ({
},
async getAllHistograms(testclass, testIds) {
const histTestIds = testIds.map(tid => `histogram-${tid}`);
const histTestIds = testIds.map((tid) => `histogram-${tid}`);
// these load asynchronously, so we need to wait for each histogram individually
await utils.waitForAllByIds(histTestIds);
const allHistograms = await utils.getAllByClass(testclass);
return allHistograms.map(hist => hist.replace(/^histogram-/, ""));
return allHistograms.map((hist) => hist.replace(/^histogram-/, ""));
},
async getAllCategoriesAndCounts(category) {
await utils.waitByClass("categorical-row");
return page.$$eval(
`[data-testid="category-${category}"] [data-testclass='categorical-row']`,
rows => Object.fromEntries(rows.map(row => {
const cat = row.querySelector("[data-testclass='categorical-value']").innerText;
const count = row.querySelector("[data-testclass='categorical-value-count']").innerText;
return [cat, count];
}))
(rows) =>
Object.fromEntries(
rows.map((row) => {
const cat = row.querySelector(
"[data-testclass='categorical-value']"
).innerText;
const count = row.querySelector(
"[data-testclass='categorical-value-count']"
).innerText;
return [cat, count];
})
)
);
},
@@ -60,12 +66,14 @@ export const cellxgeneActions = (page, utils) => ({
await utils.waitByID(checkboxId);
const checkedPseudoclass = await page.$eval(
`[data-testid='${checkboxId}']`,
el => el.matches(":checked")
(el) => el.matches(":checked")
);
if (!checkedPseudoclass) await utils.clickOn(checkboxId);
try {
const categoryRow = await utils.waitByID(`${category}:category-expand`);
const isExpanded = await categoryRow.$("[data-testclass='category-expand-is-expanded']");
const isExpanded = await categoryRow.$(
"[data-testclass='category-expand-is-expanded']"
);
if (isExpanded) await utils.clickOn(`${category}:category-expand`);
} catch {}
},
@@ -75,14 +83,22 @@ export const cellxgeneActions = (page, utils) => ({
const size = await el.boxModel();
return {
x: Math.floor(size.width * xAsPercent),
y: Math.floor(size.height * yAsPercent)
}
y: Math.floor(size.height * yAsPercent),
};
},
async calcDragCoordinates(testId, coordinateAsPercent) {
return {
start: await this.calcCoordinate(testId, coordinateAsPercent.x1, coordinateAsPercent.y1),
end: await this.calcCoordinate(testId, coordinateAsPercent.x2, coordinateAsPercent.y2)
start: await this.calcCoordinate(
testId,
coordinateAsPercent.x1,
coordinateAsPercent.y1
),
end: await this.calcCoordinate(
testId,
coordinateAsPercent.x2,
coordinateAsPercent.y2
),
};
},
@@ -97,7 +113,9 @@ export const cellxgeneActions = (page, utils) => ({
async expandCategory(category) {
const expand = await utils.waitByID(`${category}:category-expand`);
const notExpanded = await expand.$("[data-testclass='category-expand-is-not-expanded']");
const notExpanded = await expand.$(
"[data-testclass='category-expand-is-not-expanded']"
);
if (notExpanded) await utils.clickOn(`${category}:category-expand`);
},
@@ -117,7 +135,10 @@ export const cellxgeneActions = (page, utils) => ({
async renameCategory(oldCatgoryName, newCategoryName) {
await utils.clickOn(`${oldCatgoryName}:see-actions`);
await utils.clickOn(`${oldCatgoryName}:edit-category-mode`);
await utils.clearInputAndTypeInto(`${oldCatgoryName}:edit-category-name-text`, newCategoryName);
await utils.clearInputAndTypeInto(
`${oldCatgoryName}:edit-category-name-text`,
newCategoryName
);
await utils.clickOn(`${oldCatgoryName}:submit-category-edit`);
},
@@ -136,7 +157,7 @@ export const cellxgeneActions = (page, utils) => ({
async deleteLabel(categoryName, labelName) {
await this.expandCategory(categoryName);
await utils.clickOn(`${categoryName}:${labelName}:see-actions`);
await utils.clickOn( `${categoryName}:${labelName}:delete-label`);
await utils.clickOn(`${categoryName}:${labelName}:delete-label`);
},
async renameLabel(categoryName, oldLabelName, newLabelName) {
@@ -153,17 +174,23 @@ export const cellxgeneActions = (page, utils) => ({
async addGeneToSearch(geneName) {
await utils.typeInto("gene-search", geneName);
await page.keyboard.press("Enter");
await page.waitForSelector(
`[data-testid='histogram-${geneName}']`
);
await page.waitForSelector(`[data-testid='histogram-${geneName}']`);
},
async subset(coordinatesAsPercent) {
// In order to deselect the selection after the subset, make sure we have some clear part
// of the scatterplot we can click on
assert(coordinatesAsPercent.x2 < 0.99 || coordinatesAsPercent.y2 < 0.99);
const lassoSelection = await this.calcDragCoordinates( "layout-graph", coordinatesAsPercent);
await this.drag("layout-graph", lassoSelection.start, lassoSelection.end, true );
const lassoSelection = await this.calcDragCoordinates(
"layout-graph",
coordinatesAsPercent
);
await this.drag(
"layout-graph",
lassoSelection.start,
lassoSelection.end,
true
);
await utils.clickOn("subset-button");
const clearCoordinate = await this.calcCoordinate(
"layout-graph",
@@ -174,7 +201,9 @@ export const cellxgeneActions = (page, utils) => ({
},
async setSellSet(cellSet, cellSetNum) {
for (const selection of cellSet.filter(sel => sel.kind === "categorical")) {
for (const selection of cellSet.filter(
(sel) => sel.kind === "categorical"
)) {
await this.selectCategory(selection.metadata, selection.values, true);
}
await this.cellSet(cellSetNum);
@@ -190,7 +219,5 @@ export const cellxgeneActions = (page, utils) => ({
await utils.clickOn("section-bulk-add");
await utils.typeInto("input-bulk-add", geneNames.join(","));
await page.keyboard.press("Enter");
}
},
});

View File

@@ -1,6 +1,7 @@
export const jest_env = 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 appUrlBase =
process.env.CXG_URL_BASE || `http://localhost:${appPort}`;
export const DEV = jest_env === "dev";
export const DEBUG = jest_env === "debug";
export const DATASET = "pbmc3k";

View File

@@ -4,7 +4,7 @@ export const datasets = {
dataframe: {
nObs: "2638",
nVar: "1838",
type: "float32"
type: "float32",
},
categorical: {
louvain: {
@@ -15,47 +15,47 @@ export const datasets = {
"Dendritic cells": "37",
"FCGR3A+ Monocytes": "150",
Megakaryocytes: "15",
"NK cells": "154"
}
"NK cells": "154",
},
},
continuous: {
n_genes: "int32",
percent_mito: "float32",
n_counts: "float32"
n_counts: "float32",
},
cellsets: {
lasso: [
{
"coordinates-as-percent": { x1: 0.1, y1: 0.25, x2: 0.7, y2: 0.75 },
count: "1181"
}
count: "1181",
},
],
categorical: [
{
metadata: "louvain",
values: ["B cells", "Megakaryocytes"],
count: "357"
}
count: "357",
},
],
continuous: [
{
metadata: "n_genes",
"coordinates-as-percent": { x1: 0.25, y1: 0.5, x2: 0.55, y2: 0.5 },
count: "1537"
}
]
count: "1537",
},
],
},
diffexp: {
cellset1: [
{ kind: "categorical", metadata: "louvain", values: ["B cells"] }
{ kind: "categorical", metadata: "louvain", values: ["B cells"] },
],
cellset2: [
{
kind: "categorical",
metadata: "louvain",
values: ["CD4 T cells", "NK cells"]
}
values: ["CD4 T cells", "NK cells"],
},
],
"gene-results": [
"HLA-DRB1",
@@ -67,21 +67,21 @@ export const datasets = {
"HLA-DQB1",
"MS4A1",
"IL32",
"CD37"
]
"CD37",
],
},
genes: {
bulkadd: ["S100A8", "FCGR3A", "LGALS2", "GSTP1"],
search: "ACD"
search: "ACD",
},
subset: {
cellset1: [
{
kind: "categorical",
metadata: "louvain",
values: ["B cells", "Megakaryocytes"]
}
values: ["B cells", "Megakaryocytes"],
},
],
count: "357",
categorical: {
@@ -93,27 +93,27 @@ export const datasets = {
"Dendritic cells": "0",
"FCGR3A+ Monocytes": "0",
Megakaryocytes: "15",
"NK cells": "0"
}
"NK cells": "0",
},
},
lasso: {
"coordinates-as-percent": { x1: 0.25, y1: 0.05, x2: 0.75, y2: 0.55 },
count: "329"
}
count: "329",
},
},
scatter: {
genes: { x: "S100A8", y: "FCGR3A" }
genes: { x: "S100A8", y: "FCGR3A" },
},
pan: {
"coordinates-as-percent": { x1: 0.75, y1: 0.75, x2: 0.35, y2: 0.35 }
"coordinates-as-percent": { x1: 0.75, y1: 0.75, x2: 0.35, y2: 0.35 },
},
features: {
panzoom: {
lasso: {
"coordinates-as-percent": { x1: 0.3, y1: 0.3, x2: 0.5, y2: 0.5 },
count: "24"
}
}
count: "24",
},
},
},
categoryLabel: {
lasso: {
@@ -122,17 +122,17 @@ export const datasets = {
newCount: {
bySubsetConfig: {
false: "600",
true: "591"
}
}
true: "591",
},
},
},
annotationsFromFile: {
count: {
bySubsetConfig: {
false: "1161",
true: "856"
}
}
true: "856",
},
},
},
clip: {
min: "30",
@@ -141,7 +141,7 @@ export const datasets = {
gene: "S100A8",
"coordinates-as-percent": { x1: 0.25, y1: 0.5, x2: 0.55, y2: 0.5 },
count: "386",
"gene-cell-count": "416"
}
}
"gene-cell-count": "416",
},
},
};

View File

@@ -24,7 +24,9 @@ afterAll(() => {
describe("did launch", () => {
test("page launched", async () => {
const element = await utils.getOneElementInnerHTML("[data-testid='header']");
const element = await utils.getOneElementInnerHTML(
"[data-testid='header']"
);
expect(element).toBe(data.title);
});
});
@@ -32,7 +34,9 @@ describe("did launch", () => {
describe("metadata loads", () => {
test("categories and values from dataset appear", async () => {
for (const label in data.categorical) {
const categoryName = await utils.getOneElementInnerText(`[data-testid="category-${label}"]`);
const categoryName = await utils.getOneElementInnerText(
`[data-testid="category-${label}"]`
);
expect(categoryName).toMatch(label);
await utils.clickOn(`${label}:category-expand`);
const categories = await cxgActions.getAllCategoriesAndCounts(label);
@@ -50,7 +54,6 @@ describe("metadata loads", () => {
await utils.waitByID(`histogram-${label}`);
}
});
});
describe("cell selection", () => {
@@ -81,7 +84,9 @@ describe("cell selection", () => {
await utils.clickOn(`${cellset.metadata}:category-expand`);
await utils.clickOn(`${cellset.metadata}:category-select`);
for (const val of cellset.values) {
await utils.clickOn(`categorical-value-select-${cellset.metadata}-${val}`);
await utils.clickOn(
`categorical-value-select-${cellset.metadata}-${val}`
);
}
const cellCount = await cxgActions.cellSet(1);
expect(cellCount).toBe(cellset.count);
@@ -103,12 +108,16 @@ describe("cell selection", () => {
});
describe("gene entry", () => {
test("search for single gene", async () => cxgActions.addGeneToSearch(data.genes.search));
test("search for single gene", async () =>
cxgActions.addGeneToSearch(data.genes.search));
test("bulk add genes", async () => {
const testGenes = data.genes.bulkadd;
await cxgActions.bulkAddGenes(testGenes);
const allHistograms = await cxgActions.getAllHistograms("histogram-user-gene", testGenes);
const allHistograms = await cxgActions.getAllHistograms(
"histogram-user-gene",
testGenes
);
expect(allHistograms).toEqual(expect.arrayContaining(testGenes));
expect(allHistograms.length).toEqual(testGenes.length);
});
@@ -172,11 +181,19 @@ describe("subset", () => {
const userDefinedGenes = data.genes.bulkadd;
const diffExpGenes = data.diffexp["gene-results"];
await cxgActions.bulkAddGenes(userDefinedGenes);
const userDefinedHistograms = await cxgActions.getAllHistograms("histogram-user-gene", userDefinedGenes);
expect(userDefinedHistograms).toEqual(expect.arrayContaining(userDefinedGenes));
await cxgActions.subset({x1: 0.15, y1: 0.10, x2: 0.98, y2: 0.98});
const userDefinedHistograms = await cxgActions.getAllHistograms(
"histogram-user-gene",
userDefinedGenes
);
expect(userDefinedHistograms).toEqual(
expect.arrayContaining(userDefinedGenes)
);
await cxgActions.subset({ x1: 0.15, y1: 0.1, x2: 0.98, y2: 0.98 });
await cxgActions.runDiffExp(data.diffexp.cellset1, data.diffexp.cellset2);
const diffExpHistograms = await cxgActions.getAllHistograms("histogram-diffexp", diffExpGenes);
const diffExpHistograms = await cxgActions.getAllHistograms(
"histogram-diffexp",
diffExpGenes
);
expect(diffExpHistograms).toEqual(expect.arrayContaining(diffExpGenes));
await utils.clickOn("reset-subset-button");
const expected = [].concat(userDefinedGenes, diffExpGenes);
@@ -184,26 +201,38 @@ describe("subset", () => {
"histogram-user-gene",
expected
);
expect(userDefinedHistogramsAfterSubset).toEqual(expect.arrayContaining(expected));
expect(userDefinedHistogramsAfterSubset).toEqual(
expect.arrayContaining(expected)
);
});
test("subset selection appends the top diff exp genes to user defined genes", async () => {
const userDefinedGenes = data.genes.bulkadd;
const diffExpGenes = data.diffexp["gene-results"];
await cxgActions.bulkAddGenes(userDefinedGenes);
const userDefinedHistograms = await cxgActions.getAllHistograms("histogram-user-gene", userDefinedGenes);
expect(userDefinedHistograms).toEqual(expect.arrayContaining(userDefinedGenes));
await cxgActions.subset({x1: 0.15, y1: 0.10, x2: 0.98, y2: 0.98});
const userDefinedHistograms = await cxgActions.getAllHistograms(
"histogram-user-gene",
userDefinedGenes
);
expect(userDefinedHistograms).toEqual(
expect.arrayContaining(userDefinedGenes)
);
await cxgActions.subset({ x1: 0.15, y1: 0.1, x2: 0.98, y2: 0.98 });
await cxgActions.runDiffExp(data.diffexp.cellset1, data.diffexp.cellset2);
const diffExpHistograms = await cxgActions.getAllHistograms("histogram-diffexp", diffExpGenes);
const diffExpHistograms = await cxgActions.getAllHistograms(
"histogram-diffexp",
diffExpGenes
);
expect(diffExpHistograms).toEqual(expect.arrayContaining(diffExpGenes));
await cxgActions.subset({x1: 0.16, y1: 0.11, x2: 0.97, y2: 0.97});
await cxgActions.subset({ x1: 0.16, y1: 0.11, x2: 0.97, y2: 0.97 });
const expected = [].concat(userDefinedGenes, diffExpGenes);
const userDefinedHistogramsAfterSubset = await cxgActions.getAllHistograms(
"histogram-user-gene",
expected
);
expect(userDefinedHistogramsAfterSubset).toEqual(expect.arrayContaining(expected));
expect(userDefinedHistogramsAfterSubset).toEqual(
expect.arrayContaining(expected)
);
});
});

View File

@@ -13,14 +13,13 @@ beforeAll(async () => {
});
afterAll(() => {
if (browser !== undefined) browser.close()
if (browser !== undefined) browser.close();
});
describe.each([
{withSubset: true, tag: "subset"},
{withSubset: false, tag: "whole"}
{ withSubset: true, tag: "subset" },
{ withSubset: false, tag: "whole" },
])("annotations", (config) => {
const perTestCategoryName = "per-test-category";
const perTestLabelName = "per-test-label";
@@ -31,7 +30,8 @@ describe.each([
// setup the test fixtures
await actions.createCategory(perTestCategoryName);
await actions.createLabel(perTestCategoryName, perTestLabelName);
if (config.withSubset) await actions.subset({x1: 0.10, y1: 0.10, x2: 0.80, y2: 0.80});
if (config.withSubset)
await actions.subset({ x1: 0.1, y1: 0.1, x2: 0.8, y2: 0.8 });
await utils.waitByClass("autosave-complete");
});
@@ -74,7 +74,11 @@ describe.each([
test("rename a label", async () => {
const newLabelName = "my-cool-new-label";
await assertLabelDoesNotExist(perTestCategoryName, newLabelName);
await actions.renameLabel(perTestCategoryName, perTestLabelName, newLabelName);
await actions.renameLabel(
perTestCategoryName,
perTestLabelName,
newLabelName
);
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
await assertLabelExists(perTestCategoryName, newLabelName);
});
@@ -83,8 +87,10 @@ describe.each([
const categoryName = "cluster-test";
const labelName = "four";
await actions.expandCategory(categoryName);
const result = await utils.waitByID(`categorical-value-count-${categoryName}-${labelName}`);
expect(await result.evaluate(node => node.innerText)).toBe(
const result = await utils.waitByID(
`categorical-value-count-${categoryName}-${labelName}`
);
expect(await result.evaluate((node) => node.innerText)).toBe(
data.annotationsFromFile.count.bySubsetConfig[config.withSubset]
);
});
@@ -101,11 +107,17 @@ describe.each([
lassoSelection.end,
true
);
await utils.waitByID("lasso-element", {visible: true});
await utils.clickOn(`${perTestCategoryName}:${perTestLabelName}:see-actions`);
await utils.clickOn(`${perTestCategoryName}:${perTestLabelName}:add-current-selection-to-this-label`);
const result = await utils.waitByID(`categorical-value-count-${perTestCategoryName}-${perTestLabelName}`);
expect(await result.evaluate(node => node.innerText)).toBe(
await utils.waitByID("lasso-element", { visible: true });
await utils.clickOn(
`${perTestCategoryName}:${perTestLabelName}:see-actions`
);
await utils.clickOn(
`${perTestCategoryName}:${perTestLabelName}:add-current-selection-to-this-label`
);
const result = await utils.waitByID(
`categorical-value-count-${perTestCategoryName}-${perTestLabelName}`
);
expect(await result.evaluate((node) => node.innerText)).toBe(
data.categoryLabel.newCount.bySubsetConfig[config.withSubset]
);
});
@@ -170,7 +182,11 @@ describe.each([
test("undo/redo label rename", async () => {
const newLabelName = `label-renamed-undo-${config.tag}`;
await assertLabelDoesNotExist(perTestCategoryName, newLabelName);
await actions.renameLabel(perTestCategoryName, perTestLabelName, newLabelName);
await actions.renameLabel(
perTestCategoryName,
perTestLabelName,
newLabelName
);
await assertLabelExists(perTestCategoryName, newLabelName);
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
await utils.clickOn("undo");
@@ -183,14 +199,16 @@ describe.each([
async function assertCategoryExists(categoryName) {
const handle = await utils.waitByID(`${categoryName}:category-expand`);
const result = await handle.evaluate(node => node.innerText);
const result = await handle.evaluate((node) => node.innerText);
// slice beginning and end of category name result to account for truncation of long names
expect(result.slice(0, 10)).toBe(categoryName.slice(0, 10));
expect(result.slice(-10)).toBe(categoryName.slice(-10));
}
async function assertCategoryDoesNotExist(categoryName) {
const result = await page.$(`[data-testid='${categoryName}:category-expand']`);
const result = await page.$(
`[data-testid='${categoryName}:category-expand']`
);
expect(result).toBeNull();
}
@@ -198,13 +216,17 @@ describe.each([
const category = await utils.waitByID(`${categoryName}:category-expand`);
expect(category).not.toBeNull();
await actions.expandCategory(categoryName);
const previous = await utils.waitByID(`categorical-value-${categoryName}-${labelName}`);
expect(await previous.evaluate(node => node.innerText)).toBe(labelName);
const previous = await utils.waitByID(
`categorical-value-${categoryName}-${labelName}`
);
expect(await previous.evaluate((node) => node.innerText)).toBe(labelName);
}
async function assertLabelDoesNotExist(categoryName, labelName) {
await actions.expandCategory(categoryName);
const result = await page.$(`[data-testid='categorical-value-${categoryName}-${labelName}']`);
const result = await page.$(
`[data-testid='categorical-value-${categoryName}-${labelName}']`
);
expect(result).toBeNull();
}
@@ -212,10 +234,10 @@ describe.each([
try {
const category = await page.waitForSelector(
`[data-testid='${categoryName}:category-expand']`,
{timeout: 200}
{ timeout: 200 }
);
if (category !== null) return await actions.deleteCategory(categoryName);
} catch {}
return null
return null;
}
});

View File

@@ -1,9 +1,5 @@
{
"preset": "jest-puppeteer",
"testMatch": [
"**/__tests__/**/?(*.)(spec|test).js?(x)"
],
"setupFiles": [
"../setupMissingGlobals.js"
]
"testMatch": ["**/__tests__/**/?(*.)(spec|test).js?(x)"],
"setupFiles": ["../setupMissingGlobals.js"]
}

View File

@@ -29,9 +29,9 @@ beforeAll(async () => {
page = await browser.newPage();
await page.setViewport(browserViewport);
if (DEV || DEBUG) {
page.on("console", msg => console.log(`PAGE LOG: ${msg.text()}`));
page.on("console", (msg) => console.log(`PAGE LOG: ${msg.text()}`));
}
page.on("pageerror", err => {
page.on("pageerror", (err) => {
throw new Error(`Console error: ${err}`);
});
utils = puppeteerUtils(page);

View File

@@ -1,4 +1,4 @@
export const puppeteerUtils = page => ({
export const puppeteerUtils = (page) => ({
async waitByID(testId, props = {}) {
return page.waitForSelector(`[data-testid='${testId}']`, props);
},
@@ -9,13 +9,13 @@ export const puppeteerUtils = page => ({
async waitForAllByIds(testIds) {
await Promise.all(
testIds.map(testId => page.waitForSelector(`[data-testid='${testId}']`))
testIds.map((testId) => page.waitForSelector(`[data-testid='${testId}']`))
);
},
async getAllByClass(testClass) {
return page.$$eval(`[data-testclass=${testClass}]`, eles =>
eles.map(ele => ele.dataset.testid)
return page.$$eval(`[data-testclass=${testClass}]`, (eles) =>
eles.map((ele) => ele.dataset.testid)
);
},
@@ -52,18 +52,18 @@ export const puppeteerUtils = page => ({
async getOneElementInnerHTML(selector) {
await page.waitForSelector(selector);
return page.$eval(selector, el => el.innerHTML);
return page.$eval(selector, (el) => el.innerHTML);
},
async getOneElementInnerText(selector) {
await page.waitForSelector(selector);
return page.$eval(selector, el => el.innerText);
return page.$eval(selector, (el) => el.innerText);
},
async getElementCoordinates(testid) {
return page.$eval(`[data-testid='${testid}']`, elem => {
return page.$eval(`[data-testid='${testid}']`, (elem) => {
const { left, top } = elem.getBoundingClientRect();
return [left, top];
});
}
},
});

View File

@@ -7,41 +7,48 @@ export async function setupTestBrowser() {
const browserViewport = { width: 1280, height: 960 };
const browserParams = DEV
? {
headless: false,
slowMo: 5,
args: [ `--window-size=${browserViewport.width},${browserViewport.height}`]
}
headless: false,
slowMo: 5,
args: [
`--window-size=${browserViewport.width},${browserViewport.height}`,
],
}
: DEBUG
? {
? {
headless: false,
slowMo: 100,
devtools: true,
args: [ `--window-size=${browserViewport.width + 560},${browserViewport.height}`]
args: [
`--window-size=${browserViewport.width + 560},${
browserViewport.height
}`,
],
}
: {
args: [ `--window-size=${browserViewport.width},${browserViewport.height}`]
: {
args: [
`--window-size=${browserViewport.width},${browserViewport.height}`,
],
};
const browser = await puppeteer.launch(browserParams);
const page = await browser.pages().then(pages => pages[0]);
const page = await browser.pages().then((pages) => pages[0]);
await page.setViewport(browserViewport);
if (DEV || DEBUG) {
page.on("console", async msg => {
page.on("console", async (msg) => {
// If there is a console.error but an error is not thrown, this will ensure the test fails
if (msg.type() === "error") {
const errorMsgText = await Promise.all(
// TODO can we do this without internal properties?
msg.args().map(arg => arg._remoteObject.description)
msg.args().map((arg) => arg._remoteObject.description)
);
throw new Error(`Console error: ${errorMsgText}`);
}
console.log(`PAGE LOG: ${msg.text()}`);
});
}
page.on("pageerror", err => {
page.on("pageerror", (err) => {
throw new Error(`Console error: ${err}`);
});
const utils = puppeteerUtils(page);
const cxgActions = cellxgeneActions(page, utils);
return [browser, page, utils, cxgActions];
}

View File

@@ -26,7 +26,7 @@ describe("cascade", () => {
expect(nextSharedState).toStrictEqual({});
expect(prevSharedState).toBe(topLevelState);
return 0;
}
},
],
[
"bar",
@@ -36,8 +36,8 @@ describe("cascade", () => {
expect(nextSharedState).toStrictEqual({ foo: 0 });
expect(prevSharedState).toBe(topLevelState);
return 99;
}
]
},
],
]);
const nextState = reducer(topLevelState, topLevelAction);

View File

@@ -23,7 +23,7 @@ describe("create", () => {
describe("undo", () => {
test("expected state modifications", () => {
const initialState = { a: 0, b: 1000 };
const reducer = state => {
const reducer = (state) => {
return { a: state.a + 1, b: state.b + 1 };
};
const undoableReducer = undoable(reducer, ["a"]);
@@ -43,7 +43,7 @@ describe("undo", () => {
describe("redo", () => {
const initialState = { a: 0, b: 1000 };
const reducer = state => {
const reducer = (state) => {
return { a: state.a + 1, b: state.b + 1 };
};
let UR;
@@ -58,7 +58,7 @@ describe("redo", () => {
// verify undo->redo reverts state.
const s2 = UR(UR(s1, { type: "@@undoable/undo" }), {
type: "@@undoable/redo"
type: "@@undoable/redo",
});
expect(s2).toMatchObject({ a: 1, b: 1001 });

View File

@@ -13,16 +13,16 @@ describe("rangeEncodeIndices", () => {
expect(rangeEncodeIndices([1, 9, 432], 10, true)).toMatchObject([
1,
9,
432
432,
]);
expect(rangeEncodeIndices([1, 9, 432], 10, false)).toMatchObject([
1,
9,
432
432,
]);
expect(rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, true)).toMatchObject(
[[0, 3], [9, 10], 432]
);
expect(
rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, true)
).toMatchObject([[0, 3], [9, 10], 432]);
expect(
rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, false)
).toMatchObject([[0, 3], [9, 10], 432]);
@@ -34,7 +34,10 @@ describe("rangeEncodeIndices", () => {
).toMatchObject([[3, 7], [10, 12], 99]);
expect(
rangeEncodeIndices([3, 4, 5, 6, 7, 10, 11, 12], 3, false)
).toMatchObject([[3, 7], [10, 12]]);
).toMatchObject([
[3, 7],
[10, 12],
]);
expect(
rangeEncodeIndices([0, 3, 4, 5, 6, 7, 10, 11, 12], 3, false)
).toMatchObject([0, [3, 7], [10, 12]]);

View File

@@ -31,7 +31,7 @@ describe("centroid", () => {
...Universe.addObsLayout(
universe,
Universe.matrixFBSToDataframe(REST.layoutObs)
)
),
};
world = World.createWorldFromEntireUniverse(universe);
@@ -61,10 +61,10 @@ describe("centroid", () => {
// This expected result assumes that all cells belong in all categorical values inside of sample response
const expectedResult = [
quantile([0.5], world.obsLayout.col("umap_0").asArray())[0],
quantile([0.5], world.obsLayout.col("umap_1").asArray())[0]
quantile([0.5], world.obsLayout.col("umap_1").asArray())[0],
];
centroidResult.forEach(coordinate => {
centroidResult.forEach((coordinate) => {
expect(coordinate).toEqual(expectedResult);
});
});
@@ -86,10 +86,10 @@ describe("centroid", () => {
// This expected result assumes that all cells belong in all categorical values inside of sample response
const expectedResult = [
quantile([0.5], world.obsLayout.col("umap_0").asArray())[0],
quantile([0.5], world.obsLayout.col("umap_1").asArray())[0]
quantile([0.5], world.obsLayout.col("umap_1").asArray())[0],
];
centroidResult.forEach(coordinate => {
centroidResult.forEach((coordinate) => {
expect(coordinate).toEqual(expectedResult);
});
});

View File

@@ -53,7 +53,7 @@ describe("simple data access", () => {
[4, 2],
[
new Float64Array([0.0, Number.NaN, Number.POSITIVE_INFINITY, 3.14159]),
["red", "blue", "green", "nan"]
["red", "blue", "green", "nan"],
],
new Dataframe.DenseInt32Index([3, 2, 1, 0]),
new Dataframe.KeyIndex(["numbers", "colors"])
@@ -136,7 +136,7 @@ describe("dataframe subsetting", () => {
new Int32Array([0, 1, 2]),
["A", "B", "C"],
new Float32Array([4.4, 5.5, 6.6]),
["red", "green", "blue"]
["red", "green", "blue"],
],
null,
new Dataframe.KeyIndex(["int32", "string", "float32", "colors"])
@@ -258,7 +258,7 @@ describe("dataframe subsetting", () => {
new Int32Array([0, 1, 2]),
["A", "B", "C"],
new Float32Array([4.4, 5.5, 6.6]),
["red", "green", "blue"]
["red", "green", "blue"],
],
new Dataframe.DenseInt32Index([2, 4, 6]),
new Dataframe.KeyIndex(["int32", "string", "float32", "colors"])
@@ -283,7 +283,7 @@ describe("dataframe factories", () => {
[
new Array(3).fill(0),
new Int16Array(3).fill(99),
new Float64Array(3).fill(1.1)
new Float64Array(3).fill(1.1),
]
);
@@ -323,7 +323,7 @@ describe("dataframe factories", () => {
[2, 2],
[
["red", "blue"],
[true, false]
[true, false],
],
null,
new Dataframe.KeyIndex(["colors", "bools"])
@@ -346,7 +346,7 @@ describe("dataframe factories", () => {
[2, 2],
[
["red", "blue"],
[true, false]
[true, false],
],
null,
new Dataframe.DenseInt32Index([74, 75])
@@ -371,7 +371,7 @@ describe("dataframe factories", () => {
[2, 2],
[
["red", "blue"],
[true, false]
[true, false],
],
null,
new Dataframe.DenseInt32Index([74, 75])
@@ -396,7 +396,7 @@ describe("dataframe factories", () => {
[2, 2],
[
["red", "blue"],
[true, false]
[true, false],
],
null,
null
@@ -421,7 +421,7 @@ describe("dataframe factories", () => {
[2, 2],
[
["red", "blue"],
[true, false]
[true, false],
],
null,
null
@@ -479,7 +479,7 @@ describe("dataframe factories", () => {
[
["red", "blue"],
[true, false],
[1, 0]
[1, 0],
],
null,
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
@@ -553,7 +553,7 @@ describe("dataframe factories", () => {
[
["red", "blue"],
[true, false],
[1, 0]
[1, 0],
],
null,
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
@@ -595,7 +595,7 @@ describe("dataframe factories", () => {
[
["red", "blue"],
[true, false],
[1, 0]
[1, 0],
],
null,
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
@@ -618,7 +618,7 @@ describe("dataframe factories", () => {
[
["red", "blue"],
[true, false],
[1, 0]
[1, 0],
],
null,
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
@@ -641,7 +641,7 @@ describe("dataframe factories", () => {
[
["red", "blue"],
[true, false],
[1, 0]
[1, 0],
],
null,
null
@@ -665,7 +665,7 @@ describe("dataframe factories", () => {
[
["red", "blue"],
[true, false],
[1, 0]
[1, 0],
],
null,
null
@@ -689,7 +689,7 @@ describe("dataframe factories", () => {
[
["red", "blue"],
[true, false],
[1, 0]
[1, 0],
],
null,
new Dataframe.DenseInt32Index([102, 101, 100])
@@ -715,7 +715,7 @@ describe("dataframe factories", () => {
[
new Array(3).fill(0),
new Int16Array(3).fill(99),
new Float64Array(3).fill(1.1)
new Float64Array(3).fill(1.1),
]
);
const dfB = dfA.mapColumns((col, idx) => {
@@ -760,7 +760,7 @@ describe("dataframe factories", () => {
[2, 2],
[
[true, false],
[1, 0]
[1, 0],
],
null,
new Dataframe.KeyIndex(["A", "B"])
@@ -781,7 +781,7 @@ describe("dataframe col", () => {
[2, 2],
[
[true, false],
[1, 0]
[1, 0],
],
null,
new Dataframe.KeyIndex(["A", "B"])

View File

@@ -14,7 +14,7 @@ describe("Dataframe column histogram", () => {
new Map([
["n1", new Map([["c1", 1]])],
["n2", new Map([["c2", 1]])],
["n3", new Map([["c3", 1]])]
["n3", new Map([["c3", 1]])],
])
);
// memoized?
@@ -31,7 +31,11 @@ describe("Dataframe column histogram", () => {
const h1 = df.col("value").histogram(3, [0, 2], df.col("name"));
expect(h1).toMatchObject(
new Map([["n1", [1, 0, 0]], ["n2", [0, 1, 0]], ["n3", [0, 0, 1]]])
new Map([
["n1", [1, 0, 0]],
["n2", [0, 1, 0]],
["n3", [0, 0, 1]],
])
);
// memoized?
expect(df.col("value").histogram(3, [0, 2], df.col("name"))).toMatchObject(
@@ -48,7 +52,13 @@ describe("Dataframe column histogram", () => {
);
const h1 = df.col("cat").histogram();
expect(h1).toMatchObject(new Map([["c1", 1], ["c2", 1], ["c3", 1]]));
expect(h1).toMatchObject(
new Map([
["c1", 1],
["c2", 1],
["c3", 1],
])
);
// memoized?
expect(df.col("value").histogram(3, [0, 2])).toMatchObject(h1);
});
@@ -87,7 +97,7 @@ describe("Dataframe column histogram", () => {
0,
0,
0,
2
2,
]);
});
});

View File

@@ -13,7 +13,7 @@ describe("Dataframe column summary", () => {
categorical: true,
categories: [],
categoryCounts: new Map(),
numCategories: 0
numCategories: 0,
})
);
});
@@ -27,7 +27,7 @@ describe("Dataframe column summary", () => {
[true],
new Float32Array([39.3]),
new Int32Array([99]),
[1]
[1],
],
null,
new Dataframe.KeyIndex([
@@ -36,7 +36,7 @@ describe("Dataframe column summary", () => {
"nameBoolean",
"nameFloat32",
"nameInt32",
"nameCategorical"
"nameCategorical",
])
);
@@ -45,7 +45,7 @@ describe("Dataframe column summary", () => {
categorical: true,
categories: ["n1"],
categoryCounts: new Map([["n1", 1]]),
numCategories: 1
numCategories: 1,
})
);
expect(df.icol(1).summarize()).toEqual(
@@ -53,7 +53,7 @@ describe("Dataframe column summary", () => {
categorical: true,
categories: ["hi"],
categoryCounts: new Map([["hi", 1]]),
numCategories: 1
numCategories: 1,
})
);
expect(df.icol(2).summarize()).toEqual(
@@ -61,7 +61,7 @@ describe("Dataframe column summary", () => {
categorical: true,
categories: [true],
categoryCounts: new Map([[true, 1]]),
numCategories: 1
numCategories: 1,
})
);
expect(df.icol(3).summarize()).toEqual(
@@ -71,7 +71,7 @@ describe("Dataframe column summary", () => {
max: float32Conversion(39.3),
nan: 0,
ninf: 0,
pinf: 0
pinf: 0,
})
);
expect(df.icol(4).summarize()).toEqual(
@@ -81,7 +81,7 @@ describe("Dataframe column summary", () => {
max: 99,
nan: 0,
ninf: 0,
pinf: 0
pinf: 0,
})
);
expect(df.icol(5).summarize()).toEqual(
@@ -89,7 +89,7 @@ describe("Dataframe column summary", () => {
categorical: true,
categories: [1],
categoryCounts: new Map([[1, 1]]),
numCategories: 1
numCategories: 1,
})
);
});
@@ -103,7 +103,7 @@ describe("Dataframe column summary", () => {
[false, true, true],
new Float32Array([39.3, 39.3, 0]),
new Int32Array([99, 99, 99]),
[1, false, "0"]
[1, false, "0"],
],
null,
new Dataframe.KeyIndex([
@@ -112,7 +112,7 @@ describe("Dataframe column summary", () => {
"nameBoolean",
"nameFloat32",
"nameInt32",
"nameCategorical"
"nameCategorical",
])
);
@@ -120,24 +120,34 @@ describe("Dataframe column summary", () => {
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining(["n0", "n1", "n2"]),
categoryCounts: new Map([["n0", 1], ["n1", 1], ["n2", 1]]),
numCategories: 3
categoryCounts: new Map([
["n0", 1],
["n1", 1],
["n2", 1],
]),
numCategories: 3,
})
);
expect(df.icol(1).summarize()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining(["hi", "bye"]),
categoryCounts: new Map([["hi", 2], ["bye", 1]]),
numCategories: 2
categoryCounts: new Map([
["hi", 2],
["bye", 1],
]),
numCategories: 2,
})
);
expect(df.icol(2).summarize()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([true, false]),
categoryCounts: new Map([[true, 2], [false, 1]]),
numCategories: 2
categoryCounts: new Map([
[true, 2],
[false, 1],
]),
numCategories: 2,
})
);
expect(df.icol(3).summarize()).toEqual(
@@ -147,7 +157,7 @@ describe("Dataframe column summary", () => {
max: float32Conversion(39.3),
nan: 0,
ninf: 0,
pinf: 0
pinf: 0,
})
);
expect(df.icol(4).summarize()).toEqual(
@@ -157,15 +167,19 @@ describe("Dataframe column summary", () => {
max: 99,
nan: 0,
ninf: 0,
pinf: 0
pinf: 0,
})
);
expect(df.icol(5).summarize()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([1, false, "0"]),
categoryCounts: new Map([[1, 1], [false, 1], ["0", 1]]),
numCategories: 3
categoryCounts: new Map([
[1, 1],
[false, 1],
["0", 1],
]),
numCategories: 3,
})
);
});
@@ -181,10 +195,10 @@ describe("Dataframe column summary", () => {
39.3,
Number.NEGATIVE_INFINITY,
Number.NaN,
Number.POSITIVE_INFINITY
Number.POSITIVE_INFINITY,
]),
new Int32Array([99, 99, 99, 99]),
[1, false, "0", "0"]
[1, false, "0", "0"],
],
null,
new Dataframe.KeyIndex([
@@ -193,7 +207,7 @@ describe("Dataframe column summary", () => {
"nameBoolean",
"nameFloat32",
"nameInt32",
"nameCategorical"
"nameCategorical",
])
);
@@ -201,24 +215,34 @@ describe("Dataframe column summary", () => {
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining(["n0", "n1", "n2"]),
categoryCounts: new Map([["n0", 1], ["n1", 1], ["n2", 2]]),
numCategories: 3
categoryCounts: new Map([
["n0", 1],
["n1", 1],
["n2", 2],
]),
numCategories: 3,
})
);
expect(df.icol(1).summarize()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining(["hi", "bye"]),
categoryCounts: new Map([["hi", 2], ["bye", 1]]),
numCategories: 2
categoryCounts: new Map([
["hi", 2],
["bye", 1],
]),
numCategories: 2,
})
);
expect(df.icol(2).summarize()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([true, false]),
categoryCounts: new Map([[true, 2], [false, 1]]),
numCategories: 2
categoryCounts: new Map([
[true, 2],
[false, 1],
]),
numCategories: 2,
})
);
expect(df.icol(3).summarize()).toEqual(
@@ -228,7 +252,7 @@ describe("Dataframe column summary", () => {
max: float32Conversion(39.3),
nan: 1,
ninf: 1,
pinf: 1
pinf: 1,
})
);
expect(df.icol(4).summarize()).toEqual(
@@ -238,15 +262,19 @@ describe("Dataframe column summary", () => {
max: 99,
nan: 0,
ninf: 0,
pinf: 0
pinf: 0,
})
);
expect(df.icol(5).summarize()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([1, false, "0"]),
categoryCounts: new Map([[1, 1], [false, 1], ["0", 1]]),
numCategories: 3
categoryCounts: new Map([
[1, 1],
[false, 1],
["0", 1],
]),
numCategories: 3,
})
);
});

View File

@@ -6,7 +6,7 @@ import {
obsAnnoDimensionName,
diffexpDimensionName,
userDefinedDimensionName,
makeContinuousDimensionName
makeContinuousDimensionName,
} from "../../src/util/nameCreators";
describe("nameCreators", () => {
@@ -14,25 +14,25 @@ describe("nameCreators", () => {
layoutDimensionName,
obsAnnoDimensionName,
diffexpDimensionName,
userDefinedDimensionName
userDefinedDimensionName,
];
test("check for namespace isolation", () => {
const foo = "foo";
nameCreators.forEach(fn => expect(fn(foo)).not.toBe(foo));
nameCreators.forEach((fn) => expect(fn(foo)).not.toBe(foo));
const bar = "bar";
nameCreators.forEach(fn => expect(fn(foo)).not.toBe(fn(bar)));
nameCreators.forEach((fn) => expect(fn(foo)).not.toBe(fn(bar)));
nameCreators.forEach(fn => {
const allOtherFn = nameCreators.filter(elmnt => elmnt !== fn);
allOtherFn.forEach(otherFn => expect(fn(foo)).not.toBe(otherFn(foo)));
nameCreators.forEach((fn) => {
const allOtherFn = nameCreators.filter((elmnt) => elmnt !== fn);
allOtherFn.forEach((otherFn) => expect(fn(foo)).not.toBe(otherFn(foo)));
});
});
test("check for legal keys", () => {
/* need namespace creators to return strings only */
nameCreators.forEach(fn => expect(fn("X")).toMatch(/X/));
nameCreators.forEach((fn) => expect(fn("X")).toMatch(/X/));
});
});

View File

@@ -1,73 +1,73 @@
import { PromiseLimit } from "../../src/util/promiseLimit";
import { range } from "../../src/util/range";
const delay = t => new Promise((resolve, reject) => setTimeout(resolve, t));
const delay = (t) => new Promise((resolve, reject) => setTimeout(resolve, t));
describe("PromiseLimit", () => {
test("simple evaluation, concurrency 1", async () => {
const plimit = new PromiseLimit(1);
const result = await Promise.all([
plimit.add(() => Promise.resolve(1)),
plimit.add(() => Promise.resolve(2)),
plimit.add(() => Promise.resolve(3)),
plimit.add(() => Promise.resolve(4))
]);
expect(result).toEqual([1, 2, 3, 4]);
});
test("simple evaluation, concurrency 1", async () => {
const plimit = new PromiseLimit(1);
const result = await Promise.all([
plimit.add(() => Promise.resolve(1)),
plimit.add(() => Promise.resolve(2)),
plimit.add(() => Promise.resolve(3)),
plimit.add(() => Promise.resolve(4)),
]);
expect(result).toEqual([1, 2, 3, 4]);
});
test("simple evaluation, concurrency > 1", async () => {
const plimit = new PromiseLimit(100);
const result = await Promise.all([
plimit.add(() => Promise.resolve(1)),
plimit.add(() => Promise.resolve(2)),
plimit.add(() => Promise.resolve(3)),
plimit.add(() => Promise.resolve(4))
]);
expect(result).toEqual([1, 2, 3, 4]);
});
test("simple evaluation, concurrency > 1", async () => {
const plimit = new PromiseLimit(100);
const result = await Promise.all([
plimit.add(() => Promise.resolve(1)),
plimit.add(() => Promise.resolve(2)),
plimit.add(() => Promise.resolve(3)),
plimit.add(() => Promise.resolve(4)),
]);
expect(result).toEqual([1, 2, 3, 4]);
});
test("eval in order of insertion", async () => {
const plimit = new PromiseLimit(100);
let counter = 0;
const result = await Promise.all([
plimit.add(() => Promise.resolve((counter += 1))),
plimit.add(() => Promise.resolve((counter += 1))),
plimit.add(() => Promise.resolve((counter += 1))),
plimit.add(() => Promise.resolve((counter += 1)))
]);
expect(result).toEqual([1, 2, 3, 4]);
});
test("eval in order of insertion", async () => {
const plimit = new PromiseLimit(100);
let counter = 0;
const result = await Promise.all([
plimit.add(() => Promise.resolve((counter += 1))),
plimit.add(() => Promise.resolve((counter += 1))),
plimit.add(() => Promise.resolve((counter += 1))),
plimit.add(() => Promise.resolve((counter += 1))),
]);
expect(result).toEqual([1, 2, 3, 4]);
});
test("obeys concurrency limit", async () => {
const plimit = new PromiseLimit(2);
let running = 0;
let maxRunning = 0;
test("obeys concurrency limit", async () => {
const plimit = new PromiseLimit(2);
let running = 0;
let maxRunning = 0;
const cbfn = async i => {
running = running + 1;
maxRunning = running > maxRunning ? running : maxRunning;
await delay(100);
running = running - 1;
};
const cbfn = async (i) => {
running = running + 1;
maxRunning = running > maxRunning ? running : maxRunning;
await delay(100);
running = running - 1;
};
const result = await Promise.all(
range(10).map(i => plimit.add(() => cbfn(i)))
);
expect(maxRunning).toEqual(2);
});
const result = await Promise.all(
range(10).map((i) => plimit.add(() => cbfn(i)))
);
expect(maxRunning).toEqual(2);
});
test("rejection", async () => {
const plimit = new PromiseLimit(2);
const result = await Promise.all([
plimit.add(() => Promise.resolve("OK")),
plimit.add(() => Promise.reject("not OK")).catch(e => e),
plimit.add(() => Promise.resolve("OK")),
plimit
.add(() => {
throw new Error("not OK");
})
.catch(e => e.message)
]);
expect(result).toEqual(["OK", "not OK", "OK", "not OK"]);
});
test("rejection", async () => {
const plimit = new PromiseLimit(2);
const result = await Promise.all([
plimit.add(() => Promise.resolve("OK")),
plimit.add(() => Promise.reject("not OK")).catch((e) => e),
plimit.add(() => Promise.resolve("OK")),
plimit
.add(() => {
throw new Error("not OK");
})
.catch((e) => e.message),
]);
expect(result).toEqual(["OK", "not OK", "OK", "not OK"]);
});
});

View File

@@ -1,29 +1,29 @@
import quantile from "../../src/util/quantile";
describe("quantile", () => {
test("single q", () => {
const arr = new Float32Array([9, 3, 5, 6, 0]);
expect(quantile([1.0], arr)).toMatchObject([9]);
expect(quantile([0.9], arr)).toMatchObject([9]);
expect(quantile([0.8], arr)).toMatchObject([9]);
expect(quantile([0.7], arr)).toMatchObject([6]);
expect(quantile([0.6], arr)).toMatchObject([6]);
expect(quantile([0.5], arr)).toMatchObject([5]);
expect(quantile([0.4], arr)).toMatchObject([5]);
expect(quantile([0.3], arr)).toMatchObject([3]);
expect(quantile([0.2], arr)).toMatchObject([3]);
expect(quantile([0.1], arr)).toMatchObject([0]);
expect(quantile([0], arr)).toMatchObject([0]);
});
test("single q", () => {
const arr = new Float32Array([9, 3, 5, 6, 0]);
expect(quantile([1.0], arr)).toMatchObject([9]);
expect(quantile([0.9], arr)).toMatchObject([9]);
expect(quantile([0.8], arr)).toMatchObject([9]);
expect(quantile([0.7], arr)).toMatchObject([6]);
expect(quantile([0.6], arr)).toMatchObject([6]);
expect(quantile([0.5], arr)).toMatchObject([5]);
expect(quantile([0.4], arr)).toMatchObject([5]);
expect(quantile([0.3], arr)).toMatchObject([3]);
expect(quantile([0.2], arr)).toMatchObject([3]);
expect(quantile([0.1], arr)).toMatchObject([0]);
expect(quantile([0], arr)).toMatchObject([0]);
});
test("multi q", () => {
const arr = new Float32Array([9, 3, 5, 6, 0]);
expect(quantile([0, 0.25, 0.5, 0.75, 1.0], arr)).toMatchObject([
0,
3,
5,
6,
9
]);
});
test("multi q", () => {
const arr = new Float32Array([9, 3, 5, 6, 0]);
expect(quantile([0, 0.25, 0.5, 0.75, 1.0], arr)).toMatchObject([
0,
3,
5,
6,
9,
]);
});
});

View File

@@ -1,50 +1,48 @@
import { range, rangeFill, linspace } from "../../src/util/range";
describe("range", () => {
test("no defaults", () => {
expect(range(0, 3, 1)).toMatchObject([0, 1, 2]);
});
test("no defaults", () => {
expect(range(0, 3, 1)).toMatchObject([0, 1, 2]);
});
test("range(stop)", () => {
expect(range(3)).toMatchObject([0, 1, 2]);
expect(range(0)).toMatchObject([]);
expect(range(1)).toMatchObject([0]);
});
test("range(stop)", () => {
expect(range(3)).toMatchObject([0, 1, 2]);
expect(range(0)).toMatchObject([]);
expect(range(1)).toMatchObject([0]);
});
test("range(start,stop)", () => {
expect(range(0, 0)).toMatchObject([]);
expect(range(0, 2)).toMatchObject([0, 1]);
expect(range(4, 8)).toMatchObject([4, 5, 6, 7]);
});
test("range(start,stop)", () => {
expect(range(0, 0)).toMatchObject([]);
expect(range(0, 2)).toMatchObject([0, 1]);
expect(range(4, 8)).toMatchObject([4, 5, 6, 7]);
});
test("range(start, stop, step", () => {
expect(range(4, 0, -1)).toMatchObject([4, 3, 2, 1]);
expect(range(0, 4, 2)).toMatchObject([0, 2]);
});
test("range(start, stop, step", () => {
expect(range(4, 0, -1)).toMatchObject([4, 3, 2, 1]);
expect(range(0, 4, 2)).toMatchObject([0, 2]);
});
});
describe("rangefill", () => {
test("rangeFill(arr)", () => {
expect(rangeFill(new Int32Array(3))).toMatchObject(
new Int32Array([0, 1, 2])
);
});
test("rangeFill(arr, start)", () => {
expect(rangeFill(new Int32Array(2), 1)).toMatchObject(
new Int32Array([1, 2])
);
});
test("rangeFill(arr, start, step)", () => {
expect(rangeFill(new Int32Array(3), 2, -1)).toMatchObject(
new Int32Array([2, 1, 0])
);
});
test("rangeFill(arr)", () => {
expect(rangeFill(new Int32Array(3))).toMatchObject(
new Int32Array([0, 1, 2])
);
});
test("rangeFill(arr, start)", () => {
expect(rangeFill(new Int32Array(2), 1)).toMatchObject(
new Int32Array([1, 2])
);
});
test("rangeFill(arr, start, step)", () => {
expect(rangeFill(new Int32Array(3), 2, -1)).toMatchObject(
new Int32Array([2, 1, 0])
);
});
});
describe("linspace", () => {
test("linspace(arr, start, step)", () => {
expect(linspace(0.0, 2.0, 5)).toMatchObject(
[0.0, 0.5, 1.0, 1.5, 2.0]
);
});
test("linspace(arr, start, step)", () => {
expect(linspace(0.0, 2.0, 5)).toMatchObject([0.0, 0.5, 1.0, 1.5, 2.0]);
});
});

View File

@@ -5,20 +5,23 @@ import { subsetAndResetGeneLists } from "../../../src/util/stateManager/controls
import * as globals from "../../../src/globals";
describe("controls helpers", () => {
test("subsetAndResetGeneLists", () => {
const geneList = [...Array(150).keys()].map(() =>
Math.random().toString(36).substring(2, 6) // random string of 4 characters
const geneList = [...Array(150).keys()].map(
() => Math.random().toString(36).substring(2, 6) // random string of 4 characters
);
const state = {
userDefinedGenes: geneList.slice(0, 20),
diffexpGenes: geneList.slice(20),
};
const [newUserDefinedGenes, newDiffExpGenes] = subsetAndResetGeneLists(state);
const [newUserDefinedGenes, newDiffExpGenes] = subsetAndResetGeneLists(
state
);
expect(globals.maxUserDefinedGenes).toBeLessThan(globals.maxGenes);
expect(geneList.length).toBeGreaterThan(globals.maxGenes);
expect(newUserDefinedGenes).toHaveLength(globals.maxGenes);
expect(newUserDefinedGenes).toStrictEqual(geneList.slice(0, globals.maxGenes));
expect(newUserDefinedGenes).toStrictEqual(
geneList.slice(0, globals.maxGenes)
);
expect(newDiffExpGenes).toStrictEqual([]);
});
});

View File

@@ -3,32 +3,32 @@ test FBS encode/decode API
*/
import { Dataframe, KeyIndex } from "../../../src/util/dataframe";
import {
decodeMatrixFBS,
encodeMatrixFBS
decodeMatrixFBS,
encodeMatrixFBS,
} from "../../../src/util/stateManager/matrix";
describe("encode/decode", () => {
test("round trip", () => {
const columns = [
["red", "green", "blue"],
new Int32Array(3).fill(0),
new Uint32Array(3).fill(1),
new Float32Array(3).fill(2)
];
test("round trip", () => {
const columns = [
["red", "green", "blue"],
new Int32Array(3).fill(0),
new Uint32Array(3).fill(1),
new Float32Array(3).fill(2),
];
const dfNoColIdx = new Dataframe([3, 4], columns);
const dfA = decodeMatrixFBS(encodeMatrixFBS(dfNoColIdx));
expect([dfA.nRows, dfA.nCols]).toEqual(dfNoColIdx.dims);
expect(dfA.colIdx).toBeNull();
expect(dfA.rowIdx).toBeNull();
expect(dfA.columns).toEqual(columns);
const dfNoColIdx = new Dataframe([3, 4], columns);
const dfA = decodeMatrixFBS(encodeMatrixFBS(dfNoColIdx));
expect([dfA.nRows, dfA.nCols]).toEqual(dfNoColIdx.dims);
expect(dfA.colIdx).toBeNull();
expect(dfA.rowIdx).toBeNull();
expect(dfA.columns).toEqual(columns);
const colIndex = new KeyIndex(["a", "b", "c", "d"]);
const dfWithColIdx = new Dataframe([3, 4], columns, null, colIndex);
const dfB = decodeMatrixFBS(encodeMatrixFBS(dfWithColIdx));
expect([dfB.nRows, dfB.nCols]).toEqual(dfWithColIdx.dims);
expect(dfB.colIdx).toEqual(colIndex.keys());
expect(dfB.rowIdx).toBeNull();
expect(dfB.columns).toEqual(columns);
});
const colIndex = new KeyIndex(["a", "b", "c", "d"]);
const dfWithColIdx = new Dataframe([3, 4], columns, null, colIndex);
const dfB = decodeMatrixFBS(encodeMatrixFBS(dfWithColIdx));
expect([dfB.nRows, dfB.nCols]).toEqual(dfWithColIdx.dims);
expect(dfB.colIdx).toEqual(colIndex.keys());
expect(dfB.rowIdx).toBeNull();
expect(dfB.columns).toEqual(columns);
});
});

View File

@@ -17,13 +17,13 @@ const aConfigResponse = {
{ method: "POST", path: "/cluster/", available: false },
{ method: "POST", path: "/layout/", available: false },
{ method: "POST", path: "/diffexp/", available: false },
{ method: "POST", path: "/saveLocal/", available: false }
{ method: "POST", path: "/saveLocal/", available: false },
],
displayNames: {
engine: "the little engine that could",
dataset: "all your zeros are mine"
}
}
dataset: "all your zeros are mine",
},
},
};
const aSchemaResponse = {
@@ -31,7 +31,7 @@ const aSchemaResponse = {
dataframe: {
nObs,
nVar,
type: "float32"
type: "float32",
},
annotations: {
obs: {
@@ -44,9 +44,9 @@ const aSchemaResponse = {
{
name: "field4",
type: "categorical",
categories: field4Categories
}
]
categories: field4Categories,
},
],
},
var: {
index: "name",
@@ -58,46 +58,46 @@ const aSchemaResponse = {
{
name: "fieldD",
type: "categorical",
categories: fieldDCategories
}
]
}
categories: fieldDCategories,
},
],
},
},
layout: {
obs: [{ name: "umap", type: "float32", dims: ["umap_0", "umap_1"] }],
var: []
}
}
var: [],
},
},
};
const anAnnotationsObsJSONResponse = {
names: ["name", "field1", "field2", "field3", "field4"],
data: _()
.range(nObs)
.map(idx => [
.map((idx) => [
idx,
`obs${idx}`,
2 * idx,
idx + 0.0133,
!!(idx & 1),
field4Categories[idx % field4Categories.length]
field4Categories[idx % field4Categories.length],
])
.value()
.value(),
};
const anAnnotationsVarJSONResponse = {
names: ["fieldA", "fieldB", "fieldC", "fieldD", "name"],
data: _()
.range(nVar)
.map(idx => [
.map((idx) => [
idx,
10 * idx,
idx + 2.90143,
!!(idx & 1),
fieldDCategories[idx % fieldDCategories.length],
`var${idx}`
`var${idx}`,
])
.value()
.value(),
};
function encodeTypedArray(builder, uType, uData) {
@@ -120,7 +120,7 @@ function encodeMatrix(columns, colIndex = undefined) {
*/
const utf8Encoder = new TextEncoder("utf-8");
const builder = new flatbuffers.Builder(1024);
const cols = _.map(columns, carr => {
const cols = _.map(columns, (carr) => {
let uType;
let tarr;
if (_.every(carr, _.isNumber)) {
@@ -178,7 +178,7 @@ const anAnnotationsVarFBSResponse = (() => {
const aLayoutFBSResponse = (() => {
const coords = [
new Float32Array(nObs).fill(Math.random()),
new Float32Array(nObs).fill(Math.random())
new Float32Array(nObs).fill(Math.random()),
];
return encodeMatrix(coords, ["umap_0", "umap_1"]);
})();
@@ -187,8 +187,8 @@ const aDataObsResponse = {
var: [2, 4, 29],
obs: _()
.range(nObs)
.map(idx => [idx, Math.random(), Math.random(), Math.random()])
.value()
.map((idx) => [idx, Math.random(), Math.random(), Math.random()])
.value(),
};
export {
@@ -197,5 +197,5 @@ export {
anAnnotationsVarFBSResponse as annotationsVar,
anAnnotationsObsFBSResponse as annotationsObs,
aSchemaResponse as schema,
aConfigResponse as config
aConfigResponse as config,
};

View File

@@ -43,7 +43,7 @@ describe("createUniverseFromResponse", () => {
obsAnnotations: expect.any(Dataframe.Dataframe),
varAnnotations: expect.any(Dataframe.Dataframe),
obsLayout: expect.any(Dataframe.Dataframe),
varData: expect.any(Dataframe.Dataframe)
varData: expect.any(Dataframe.Dataframe),
})
);
@@ -60,7 +60,7 @@ describe("createUniverseFromResponse", () => {
...Universe.addObsLayout(
universe,
Universe.matrixFBSToDataframe(REST.layoutObs)
)
),
};
expect(universe).toMatchObject(
@@ -71,13 +71,13 @@ describe("createUniverseFromResponse", () => {
obsAnnotations: expect.any(Dataframe.Dataframe),
varAnnotations: expect.any(Dataframe.Dataframe),
obsLayout: expect.any(Dataframe.Dataframe),
varData: expect.any(Dataframe.Dataframe)
varData: expect.any(Dataframe.Dataframe),
})
);
expect(universe.obsAnnotations.dims).toEqual([
nObs,
REST.schema.schema.annotations.obs.columns.length
REST.schema.schema.annotations.obs.columns.length,
]);
expect(universe.obsLayout.dims).toEqual([nObs, 2]);
expect(universe.obsLayout.colIndex.keys()).toEqual(
@@ -85,7 +85,7 @@ describe("createUniverseFromResponse", () => {
);
expect(universe.varAnnotations.dims).toEqual([
nVar,
REST.schema.schema.annotations.var.columns.length
REST.schema.schema.annotations.var.columns.length,
]);
expect(universe.varData.isEmpty()).toBeTruthy();
});

View File

@@ -7,7 +7,7 @@ import { DimTypes } from "../../../src/util/typedCrossfilter/crossfilter";
import * as REST from "./sampleResponses";
import {
obsAnnoDimensionName,
layoutDimensionName
layoutDimensionName,
} from "../../../src/util/nameCreators";
/*
@@ -35,7 +35,7 @@ const defaultBigBang = () => {
...Universe.addObsLayout(
universe,
Universe.matrixFBSToDataframe(REST.layoutObs)
)
),
};
/* create world */
@@ -50,7 +50,7 @@ const defaultBigBang = () => {
return {
universe,
world,
crossfilter
crossfilter,
};
};
@@ -80,8 +80,8 @@ describe("createWorldFromEntireUniverse", () => {
clipQuantiles: { min: 0, max: 1 },
unclipped: {
obsAnnotations: expect.any(Dataframe.Dataframe),
varData: expect.any(Dataframe.Dataframe)
}
varData: expect.any(Dataframe.Dataframe),
},
})
);
});
@@ -92,7 +92,7 @@ describe("createWorldFromCurrentSelection", () => {
const {
universe,
world: originalWorld,
crossfilter: originalCrossfilter
crossfilter: originalCrossfilter,
} = defaultBigBang();
/* mock a selection */
@@ -100,7 +100,7 @@ describe("createWorldFromCurrentSelection", () => {
.select(obsAnnoDimensionName("field1"), { mode: "range", lo: 0, hi: 5 })
.select(obsAnnoDimensionName("field3"), {
mode: "exact",
values: [false]
values: [false],
});
/* create the world from the selection */
@@ -124,7 +124,7 @@ describe("createWorldFromCurrentSelection", () => {
};
const matchingIndices = _()
.range(universe.nObs)
.filter(idx => matchFilter(universe.obsAnnotations, idx))
.filter((idx) => matchFilter(universe.obsAnnotations, idx))
.value();
expect(world).toMatchObject(
@@ -139,8 +139,8 @@ describe("createWorldFromCurrentSelection", () => {
varData: expect.any(Dataframe.Dataframe),
unclipped: {
obsAnnotations: expect.any(Dataframe.Dataframe),
varData: expect.any(Dataframe.Dataframe)
}
varData: expect.any(Dataframe.Dataframe),
},
})
);
@@ -170,7 +170,7 @@ describe("createObsDimensionMap", () => {
const { crossfilter } = defaultBigBang();
const annotationNames = _.map(
REST.schema.schema.annotations.obs.columns,
c => c.name
(c) => c.name
);
const obsIndexColName = REST.schema.schema.annotations.obs.index;
const schemaByObsName = _.keyBy(
@@ -178,7 +178,7 @@ describe("createObsDimensionMap", () => {
"name"
);
expect(crossfilter).toBeDefined();
annotationNames.forEach(name => {
annotationNames.forEach((name) => {
const dim = crossfilter.dimensions[obsAnnoDimensionName(name)];
if (name === obsIndexColName) {
expect(dim).toBeUndefined();

View File

@@ -187,7 +187,7 @@ describe("fillBySelection", () => {
describe("wide bitarray", () => {
test.each([9, 30, 31, 32, 33, 54, 63, 64, 65, 127, 128, 129])(
"more than %d dimensions",
ndim => {
(ndim) => {
/* ensure we move across the uint boundary correctly */
const ba = new BitArray(defaultTestLength);

View File

@@ -11,7 +11,7 @@ const someData = [
type: "tab",
productIDs: ["001"],
coords: [0, 0],
nonFinite: 0.0
nonFinite: 0.0,
},
{
date: "2011-11-14T16:20:19Z",
@@ -21,7 +21,7 @@ const someData = [
type: "tab",
productIDs: ["001", "005"],
coords: [0.4, 0.4],
nonFinite: Number.NaN
nonFinite: Number.NaN,
},
{
date: "2011-11-14T16:28:54Z",
@@ -31,7 +31,7 @@ const someData = [
type: "visa",
productIDs: ["004", "005"],
coords: [0.3, 0.1],
nonFinite: Number.POSITIVE_INFINITY
nonFinite: Number.POSITIVE_INFINITY,
},
{
date: "2011-11-14T16:30:43Z",
@@ -41,7 +41,7 @@ const someData = [
type: "tab",
productIDs: ["001", "002"],
coords: [0.392, 0.1],
nonFinite: Number.NEGATIVE_INFINITY
nonFinite: Number.NEGATIVE_INFINITY,
},
{
date: "2011-11-14T16:48:46Z",
@@ -51,7 +51,7 @@ const someData = [
type: "tab",
productIDs: ["005"],
coords: [0.7, 0.0482],
nonFinite: 1.0
nonFinite: 1.0,
},
{
date: "2011-11-14T16:53:41Z",
@@ -61,7 +61,7 @@ const someData = [
type: "tab",
productIDs: ["001", "004", "005"],
coords: [0.9999, 1.0],
nonFinite: Number.NaN
nonFinite: Number.NaN,
},
{
date: "2011-11-14T16:54:06Z",
@@ -71,7 +71,7 @@ const someData = [
type: "cash",
productIDs: ["001", "002", "003", "004", "005"],
coords: [0.384, 0.6938],
nonFinite: 99.0
nonFinite: 99.0,
},
{
date: "2011-11-14T16:58:03Z",
@@ -81,7 +81,7 @@ const someData = [
type: "tab",
productIDs: ["001"],
coords: [0.4822, 0.482],
nonFinite: Number.NaN
nonFinite: Number.NaN,
},
{
date: "2011-11-14T17:07:21Z",
@@ -91,7 +91,7 @@ const someData = [
type: "tab",
productIDs: ["004", "005"],
coords: [0.2234, 0],
nonFinite: Number.NaN
nonFinite: Number.NaN,
},
{
date: "2011-11-14T17:22:59Z",
@@ -101,7 +101,7 @@ const someData = [
type: "tab",
productIDs: ["001", "002", "004", "005"],
coords: [0.382, 0.38485],
nonFinite: -1
nonFinite: -1,
},
{
date: "2011-11-14T17:25:45Z",
@@ -111,7 +111,7 @@ const someData = [
type: "cash",
productIDs: ["002"],
coords: [0.998, 0.8472],
nonFinite: 0.0
nonFinite: 0.0,
},
{
date: "2011-11-14T17:29:52Z",
@@ -121,8 +121,8 @@ const someData = [
type: "visa",
productIDs: ["004"],
coords: [0.8273, 0.3384],
nonFinite: 0.0
}
nonFinite: 0.0,
},
];
let payments = null;
@@ -248,16 +248,21 @@ describe("ImmutableTypedCrossfilter", () => {
test("none", () => {
expect(p.select("quantity", { mode: "none" }).countSelected()).toEqual(0);
});
test.each([[[]], [[2]], [[2, 1]], [[9, 82]], [[0, 1]]])("exact: %p", v =>
test.each([[[]], [[2]], [[2, 1]], [[9, 82]], [[0, 1]]])("exact: %p", (v) =>
expect(
p.select("quantity", { mode: "exact", values: v }).countSelected()
).toEqual(_.filter(someData, d => v.includes(d.quantity)).length)
).toEqual(_.filter(someData, (d) => v.includes(d.quantity)).length)
);
test.each([[0, 1], [1, 2], [0, 99], [99, 100000]])("range %p", (lo, hi) =>
test.each([
[0, 1],
[1, 2],
[0, 99],
[99, 100000],
])("range %p", (lo, hi) =>
expect(
p.select("quantity", { mode: "range", lo, hi }).countSelected()
).toEqual(
_.filter(someData, d => d.quantity >= lo && d.quantity < hi).length
_.filter(someData, (d) => d.quantity >= lo && d.quantity < hi).length
)
);
test("bad mode", () => {
@@ -284,11 +289,11 @@ describe("ImmutableTypedCrossfilter", () => {
[["tab"]],
[["visa"]],
[["visa", "tab"]],
[["cash", "tab", "visa"]]
])("exact: %p", v =>
[["cash", "tab", "visa"]],
])("exact: %p", (v) =>
expect(
p.select("type", { mode: "exact", values: v }).countSelected()
).toEqual(_.filter(someData, d => v.includes(d.type)).length)
).toEqual(_.filter(someData, (d) => v.includes(d.type)).length)
);
test("range", () => {
expect(() => p.select("type", { mode: "range", lo: 0, hi: 9 })).toThrow(
@@ -303,8 +308,8 @@ describe("ImmutableTypedCrossfilter", () => {
describe("spatial dimension", () => {
let p;
beforeEach(() => {
const X = someData.map(r => r.coords[0]);
const Y = someData.map(r => r.coords[1]);
const X = someData.map((r) => r.coords[0]);
const Y = someData.map((r) => r.coords[1]);
p = payments.addDimension("coords", "spatial", X, Y);
});
@@ -316,34 +321,76 @@ describe("ImmutableTypedCrossfilter", () => {
test("none", () => {
expect(p.select("coords", { mode: "none" }).countSelected()).toEqual(0);
});
test.each([[0, 0, 1, 1], [0, 0, 0.5, 0.5], [0.5, 0.5, 1, 1]])(
"within-rect %d %d %d %d",
(minX, minY, maxX, maxY) => {
expect(
p
.select("coords", { mode: "within-rect", minX, minY, maxX, maxY })
.allSelected()
).toEqual(
_.filter(someData, d => {
const [x, y] = d.coords;
return minX <= x && x < maxX && minY <= y && y < maxY;
})
);
}
);
test.each([
[0, 0, 1, 1],
[0, 0, 0.5, 0.5],
[0.5, 0.5, 1, 1],
])("within-rect %d %d %d %d", (minX, minY, maxX, maxY) => {
expect(
p
.select("coords", { mode: "within-rect", minX, minY, maxX, maxY })
.allSelected()
).toEqual(
_.filter(someData, (d) => {
const [x, y] = d.coords;
return minX <= x && x < maxX && minY <= y && y < maxY;
})
);
});
test.each([
[
[[0, 0], [0, 1], [1, 1], [1, 0]],
[true, true, true, true, true, false, true, true, true, true, true, true]
[
[0, 0],
[0, 1],
[1, 1],
[1, 0],
],
[
true,
true,
true,
true,
true,
false,
true,
true,
true,
true,
true,
true,
],
],
[
[[0, 0], [0, 0.5], [0.5, 0.5], [0.5, 0]],
[true, true, true, true, false, false, false, true, true, true, false, false]
]
[
[0, 0],
[0, 0.5],
[0.5, 0.5],
[0.5, 0],
],
[
true,
true,
true,
true,
false,
false,
false,
true,
true,
true,
false,
false,
],
],
])("within-polygon %p", (polygon, expected) => {
expect(p.select("coords", { mode: "within-polygon", polygon }).allSelected())
.toEqual(_.zip(someData, expected).filter(x => x[1]).map(x => x[0]));
expect(
p.select("coords", { mode: "within-polygon", polygon }).allSelected()
).toEqual(
_.zip(someData, expected)
.filter((x) => x[1])
.map((x) => x[0])
);
});
});
@@ -381,7 +428,7 @@ describe("ImmutableTypedCrossfilter", () => {
p
.select("nonFinite", {
mode: "exact",
values: [Number.POSITIVE_INFINITY]
values: [Number.POSITIVE_INFINITY],
})
.countSelected()
).toEqual(1);
@@ -389,7 +436,7 @@ describe("ImmutableTypedCrossfilter", () => {
p
.select("nonFinite", {
mode: "exact",
values: [Number.NEGATIVE_INFINITY]
values: [Number.NEGATIVE_INFINITY],
})
.countSelected()
).toEqual(1);
@@ -402,7 +449,7 @@ describe("ImmutableTypedCrossfilter", () => {
p
.select("nonFinite", {
mode: "exact",
values: [Number.POSITIVE_INFINITY, 0, 1, 99]
values: [Number.POSITIVE_INFINITY, 0, 1, 99],
})
.countSelected()
).toEqual(6);
@@ -414,7 +461,7 @@ describe("ImmutableTypedCrossfilter", () => {
.select("nonFinite", {
mode: "range",
lo: 0,
hi: Number.POSITIVE_INFINITY
hi: Number.POSITIVE_INFINITY,
})
.countSelected()
).toEqual(5);
@@ -423,7 +470,7 @@ describe("ImmutableTypedCrossfilter", () => {
.select("nonFinite", {
mode: "range",
lo: 0,
hi: Number.NaN
hi: Number.NaN,
})
.countSelected()
).toEqual(6);
@@ -432,7 +479,7 @@ describe("ImmutableTypedCrossfilter", () => {
.select("nonFinite", {
mode: "range",
lo: Number.NEGATIVE_INFINITY,
hi: Number.POSITIVE_INFINITY
hi: Number.POSITIVE_INFINITY,
})
.countSelected()
).toEqual(7);

View File

@@ -10,15 +10,30 @@ describe("canonicalize", () => {
test("simple, already correct", () => {
expect(PositiveIntervals.canonicalize([[0, 1]])).toEqual([[0, 1]]);
expect(PositiveIntervals.canonicalize([[0, 1], [2, 3]])).toEqual([
expect(
PositiveIntervals.canonicalize([
[0, 1],
[2, 3],
])
).toEqual([
[0, 1],
[2, 3]
[2, 3],
]);
});
test("non-canonical, need to be canonicalized", () => {
expect(PositiveIntervals.canonicalize([[0, 1], [1, 2]])).toEqual([[0, 2]]);
expect(PositiveIntervals.canonicalize([[1, 2], [2, 3]])).toEqual([[1, 3]]);
expect(
PositiveIntervals.canonicalize([
[0, 1],
[1, 2],
])
).toEqual([[0, 2]]);
expect(
PositiveIntervals.canonicalize([
[1, 2],
[2, 3],
])
).toEqual([[1, 3]]);
});
});
@@ -26,14 +41,30 @@ describe("union", () => {
test("empty range", () => {
expect(PositiveIntervals.union([], [])).toEqual([]);
expect(PositiveIntervals.union([], [[1, 2]])).toEqual([[1, 2]]);
expect(PositiveIntervals.union([], [[1, 2], [3, 4]])).toEqual([
expect(
PositiveIntervals.union(
[],
[
[1, 2],
[3, 4],
]
)
).toEqual([
[1, 2],
[3, 4]
[3, 4],
]);
expect(PositiveIntervals.union([[3, 4]], [])).toEqual([[3, 4]]);
expect(PositiveIntervals.union([[1, 2], [3, 4]], [])).toEqual([
expect(
PositiveIntervals.union(
[
[1, 2],
[3, 4],
],
[]
)
).toEqual([
[1, 2],
[3, 4]
[3, 4],
]);
expect(PositiveIntervals.union([[3, 3]], [])).toEqual([[3, 3]]);
expect(PositiveIntervals.union([], [[3, 3]])).toEqual([[3, 3]]);
@@ -44,17 +75,37 @@ describe("union", () => {
expect(PositiveIntervals.union([[2, 3]], [[1, 2]])).toEqual([[1, 3]]);
expect(PositiveIntervals.union([[1, 2]], [[3, 4]])).toEqual([
[1, 2],
[3, 4]
[3, 4],
]);
expect(
PositiveIntervals.union([[1, 2], [3, 4]], [[6, 7], [19, 40]])
).toEqual([[1, 2], [3, 4], [6, 7], [19, 40]]);
expect(PositiveIntervals.union([[1, 4]], [[1, 1], [3, 4]])).toEqual([
[1, 4]
PositiveIntervals.union(
[
[1, 2],
[3, 4],
],
[
[6, 7],
[19, 40],
]
)
).toEqual([
[1, 2],
[3, 4],
[6, 7],
[19, 40],
]);
expect(
PositiveIntervals.union(
[[1, 4]],
[
[1, 1],
[3, 4],
]
)
).toEqual([[1, 4]]);
expect(PositiveIntervals.union([[3, 3]], [[4, 4]])).toEqual([
[3, 3],
[4, 4]
[4, 4],
]);
});
});
@@ -70,34 +121,43 @@ describe("intersection", () => {
expect(PositiveIntervals.intersection([[1, 2]], [[2, 3]])).toEqual([]);
expect(PositiveIntervals.intersection([[2, 3]], [[1, 2]])).toEqual([]);
expect(PositiveIntervals.intersection([[1, 10]], [[1, 10]])).toEqual([
[1, 10]
[1, 10],
]);
expect(PositiveIntervals.intersection([[1, 10]], [[2, 8]])).toEqual([
[2, 8]
[2, 8],
]);
expect(PositiveIntervals.intersection([[2, 8]], [[1, 10]])).toEqual([
[2, 8]
[2, 8],
]);
expect(PositiveIntervals.intersection([[1, 10]], [[2, 12]])).toEqual([
[2, 10]
[2, 10],
]);
expect(PositiveIntervals.intersection([[2, 12]], [[1, 10]])).toEqual([
[2, 10]
[2, 10],
]);
expect(PositiveIntervals.intersection([[1, 10]], [[1, 8]])).toEqual([
[1, 8]
[1, 8],
]);
expect(PositiveIntervals.intersection([[1, 8]], [[1, 10]])).toEqual([
[1, 8]
[1, 8],
]);
expect(PositiveIntervals.intersection([[1, 10]], [[1, 2], [6, 9]])).toEqual(
[[1, 2], [6, 9]]
);
expect(PositiveIntervals.intersection([[0, 2638]], [[1363, 2638]])).toEqual(
[[1363, 2638]]
);
expect(
PositiveIntervals.intersection(
[[1, 10]],
[
[1, 2],
[6, 9],
]
)
).toEqual([
[1, 2],
[6, 9],
]);
expect(
PositiveIntervals.intersection([[0, 2638]], [[1363, 2638]])
).toEqual([[1363, 2638]]);
expect(PositiveIntervals.intersection([[1, 2]], [[1, 2]])).toEqual([
[1, 2]
[1, 2],
]);
});
});
@@ -110,32 +170,66 @@ describe("difference", () => {
});
test("simple", () => {
expect(PositiveIntervals.difference([[1, 2], [3, 4]], [])).toEqual([
expect(
PositiveIntervals.difference(
[
[1, 2],
[3, 4],
],
[]
)
).toEqual([
[1, 2],
[3, 4]
]);
expect(PositiveIntervals.difference([[1, 2], [3, 10]], [[5, 10]])).toEqual([
[1, 2],
[3, 5]
]);
expect(PositiveIntervals.difference([[1, 2], [3, 10]], [[0, 5]])).toEqual([
[5, 10]
[3, 4],
]);
expect(
PositiveIntervals.difference([[0, 2638]], [[0, 1363], [2055, 2638]])
PositiveIntervals.difference(
[
[1, 2],
[3, 10],
],
[[5, 10]]
)
).toEqual([
[1, 2],
[3, 5],
]);
expect(
PositiveIntervals.difference(
[
[1, 2],
[3, 10],
],
[[0, 5]]
)
).toEqual([[5, 10]]);
expect(
PositiveIntervals.difference(
[[0, 2638]],
[
[0, 1363],
[2055, 2638],
]
)
).toEqual([[1363, 2055]]);
expect(
PositiveIntervals.difference([[0, 1363], [2055, 2638]], [[0, 2638]])
PositiveIntervals.difference(
[
[0, 1363],
[2055, 2638],
],
[[0, 2638]]
)
).toEqual([]);
expect(PositiveIntervals.difference([[0, 10]], [[0, 1]])).toEqual([
[1, 10]
[1, 10],
]);
expect(PositiveIntervals.difference([[0, 10]], [[1, 2]])).toEqual([
[0, 1],
[2, 10]
[2, 10],
]);
expect(PositiveIntervals.difference([[0, 10]], [[9, 10]])).toEqual([
[0, 9]
[0, 9],
]);
});
});

View File

@@ -4,7 +4,7 @@ import {
lowerBound,
upperBound,
lowerBoundIndirect,
upperBoundIndirect
upperBoundIndirect,
} from "../../../src/util/typedCrossfilter/sort";
/*
@@ -40,7 +40,7 @@ describe("sortArray", () => {
["a", "b", "0", "1"],
[0, "a", true, null, undefined, 3.1415],
fillRand(new Array(1000)),
["a", NaN, null, pInf]
["a", NaN, null, pInf],
].map((val, idx) =>
test(`JS vals ${idx}`, () => {
expect(sortArray(val)).toMatchObject(val.sort());
@@ -49,7 +49,7 @@ describe("sortArray", () => {
});
describe("finite numbers", () => {
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type =>
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map((Type) =>
test(Type.name, () => {
expect(sortArray(Type.from([6, 5, 4, 3, 2, 1, 0]))).toMatchObject(
Type.from([0, 1, 2, 3, 4, 5, 6])
@@ -131,7 +131,7 @@ describe("sortArray", () => {
describe("sortIndex", () => {
describe("finite numbers", () => {
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type =>
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map((Type) =>
test(Type.name, () => {
const source1 = Type.from([6, 5, 4, 3, 2, 1, 0]);
const index1 = fillRange(new Uint32Array(source1.length));

View File

@@ -1,6 +1,6 @@
import {
sliceByIndex,
makeSortIndex
makeSortIndex,
} from "../../../src/util/typedCrossfilter/util";
import { rangeFill as fillRange } from "../../../src/util/range";

View File

@@ -3,7 +3,7 @@ module.exports = {
cacheDirectory: true,
presets: [
["modern-browsers", { loose: true, modules: false }],
"@babel/preset-react"
"@babel/preset-react",
],
plugins: [
"@babel/plugin-proposal-function-bind",
@@ -11,6 +11,6 @@ module.exports = {
["@babel/plugin-proposal-class-properties", { loose: true }],
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator"
]
"@babel/plugin-proposal-nullish-coalescing-operator",
],
};

View File

@@ -2,7 +2,7 @@ module.exports = {
babelrc: false,
presets: [
["modern-browsers", { loose: true, modules: false }],
"@babel/preset-react"
"@babel/preset-react",
],
plugins: [
"@babel/plugin-proposal-function-bind",
@@ -12,6 +12,6 @@ module.exports = {
"@babel/plugin-transform-react-constant-elements",
"@babel/plugin-transform-runtime",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator"
]
"@babel/plugin-proposal-nullish-coalescing-operator",
],
};

View File

@@ -9,8 +9,8 @@ module.exports = {
sourceType: "module",
ecmaFeatures: {
jsx: true,
generators: true
}
generators: true,
},
},
rules: {
"no-magic-numbers": "off",
@@ -26,7 +26,7 @@ module.exports = {
"operator-linebreak": [
"error",
"after",
{ overrides: { "?": "before", ":": "before" } }
{ overrides: { "?": "before", ":": "before" } },
],
"no-console": "off",
"spaced-comment": ["error", "always", { exceptions: ["*"] }],
@@ -35,13 +35,13 @@ module.exports = {
"react/prop-types": [0],
"space-before-function-paren": "off",
"function-paren-newline": "off",
"prefer-destructuring": ["error", { object: true, array: false }]
"prefer-destructuring": ["error", { object: true, array: false }],
},
overrides: [
{
files: ["**/*.test.js"],
env: {
jest: true // now **/*.test.js files' env has both es6 *and* jest
jest: true, // now **/*.test.js files' env has both es6 *and* jest
},
// Can't extend in overrides: https://github.com/eslint/eslint/issues/8813
// "extends": ["plugin:jest/recommended"]
@@ -51,8 +51,8 @@ module.exports = {
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error",
"jest/prefer-to-have-length": "warn",
"jest/valid-expect": "error"
}
}
]
"jest/valid-expect": "error",
},
},
],
};

View File

@@ -18,7 +18,7 @@ module.exports = {
path: path.resolve("build"),
pathinfo: true,
filename: "static/js/bundle.js",
publicPath: "/"
publicPath: "/",
},
module: {
rules: [
@@ -26,7 +26,7 @@ module.exports = {
test: /\.js$/,
include: src,
loader: "babel-loader",
options: babelOptions
options: babelOptions,
},
{
test: /\.css$/,
@@ -34,29 +34,29 @@ module.exports = {
exclude: [path.resolve(src, "index.css")],
loader: [
{
loader: "style-loader"
loader: "style-loader",
},
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[name]__[local]___[hash:base64:5]"
}
}
}
]
localIdentName: "[name]__[local]___[hash:base64:5]",
},
},
},
],
},
{
test: /index\.css$/,
include: [path.resolve(src, "index.css")],
loader: [
{
loader: "style-loader"
loader: "style-loader",
},
{
loader: "css-loader"
}
]
loader: "css-loader",
},
],
},
{ test: /\.json$/, include: [src, nodeModules], loader: "json-loader" },
@@ -64,14 +64,14 @@ module.exports = {
test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2|otf)$/i,
loader: "file-loader",
include: [nodeModules, fonts],
query: { name: "static/assets/[name].[ext]" }
}
]
query: { name: "static/assets/[name].[ext]" },
},
],
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: path.resolve("index.html")
template: path.resolve("index.html"),
}),
new FaviconsWebpackPlugin({
logo: "./favicon.png",
@@ -84,16 +84,18 @@ module.exports = {
coast: false,
firefox: false,
windows: false,
yandex: false
}
}
yandex: false,
},
},
}),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
__REACT_DEVTOOLS_GLOBAL_HOOK__: "({ isDisabled: true })"
__REACT_DEVTOOLS_GLOBAL_HOOK__: "({ isDisabled: true })",
}),
new webpack.DefinePlugin({
"process.env.CXG_SERVER_PORT": JSON.stringify(process.env.CXG_SERVER_PORT)
})
]
"process.env.CXG_SERVER_PORT": JSON.stringify(
process.env.CXG_SERVER_PORT
),
}),
],
};

View File

@@ -1,12 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>cell&times;gene</title>
<style>
html, body, p, h1, h2, h3, h4, h5, h6, span, button, input, label, text, div {
font-family: 'Roboto Condensed','Helvetica Neue','Helvetica','Arial',sans-serif;
html,
body,
p,
h1,
h2,
h3,
h4,
h5,
h6,
span,
button,
input,
label,
text,
div {
font-family: "Roboto Condensed", "Helvetica Neue", "Helvetica", "Arial",
sans-serif;
font-size: 14px;
}
body {
@@ -20,9 +35,12 @@
</style>
</head>
<body>
<noscript>If you're seeing this message, that means <strong>JavaScript has been disabled on your browser</strong>, please <strong>enable JS</strong> to make this app work.</noscript>
<noscript
>If you're seeing this message, that means
<strong>JavaScript has been disabled on your browser</strong>, please
<strong>enable JS</strong> to make this app work.</noscript
>
<div id="root"></div>
</body>
</html>

View File

@@ -1,60 +1,61 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>cell&times;gene</title>
<style>
html,
body,
p,
h1,
h2,
h3,
h4,
h5,
h6,
span,
button,
input,
label,
text,
div {
font-family: "Roboto Condensed", "Helvetica Neue", "Helvetica",
"Arial", sans-serif;
font-size: 14px;
}
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>cell&times;gene</title>
<style>
html,
body,
p,
h1,
h2,
h3,
h4,
h5,
h6,
span,
button,
input,
label,
text,
div {
font-family: "Roboto Condensed", "Helvetica Neue", "Helvetica", "Arial",
sans-serif;
font-size: 14px;
}
body {
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}
</style>
</head>
<body>
<script type="text/javascript">
window.CELLXGENE = {};
window.CELLXGENE.API = {
prefix: window.location.href + "api/",
version: "v0.2/"
};
</script>
<noscript
>If you're seeing this message, that means
<strong>JavaScript has been disabled on your browser</strong>,
please <strong>enable JS</strong> to make this app work.
</noscript>
* {
box-sizing: border-box;
}
</style>
</head>
<body>
<script type="text/javascript">
window.CELLXGENE = {};
window.CELLXGENE.API = {
prefix: window.location.href + "api/",
version: "v0.2/",
};
</script>
<noscript
>If you're seeing this message, that means
<strong>JavaScript has been disabled on your browser</strong>, please
<strong>enable JS</strong> to make this app work.
</noscript>
<div id="root"></div>
{% for script in SCRIPTS %}
<script type="text/javascript" src="{{script | safe}}"></script>
{% endfor %}
{% for ils in INLINE_SCRIPTS %}
<script type="text/javascript">{% include ils %}</script>
{% endfor %}
</body>
<div id="root"></div>
{% for script in SCRIPTS %}
<script type="text/javascript" src="{{script | safe}}"></script>
{% endfor %} {% for ils in INLINE_SCRIPTS %}
<script type="text/javascript">
{% include ils %}
</script>
{% endfor %}
</body>
</html>

View File

@@ -21,7 +21,7 @@ compiler.plugin("invalid", () => {
console.log("Compiling...");
});
compiler.plugin("done", stats => {
compiler.plugin("done", (stats) => {
utils.formatStats(stats, CLIENT_PORT);
});
@@ -33,7 +33,7 @@ app.use(historyApiFallback({ verbose: false }));
app.use(
require("webpack-dev-middleware")(compiler, {
logLevel: "warn",
publicPath: config.output.publicPath
publicPath: config.output.publicPath,
})
);
@@ -43,7 +43,7 @@ app.get("*", (req, res) => {
res.sendFile(path.resolve("index.html"));
});
app.listen(CLIENT_PORT, err => {
app.listen(CLIENT_PORT, (err) => {
if (err) {
console.log(err);
return;

View File

@@ -36,10 +36,10 @@ var formatStats = (stats, port) => {
var json = stats.toJson();
var formattedErrors = json.errors.map(
message => "Error in " + formatMessage(message)
(message) => "Error in " + formatMessage(message)
);
var formattedWarnings = json.warnings.map(
message => "Warning in " + formatMessage(message)
(message) => "Warning in " + formatMessage(message)
);
if (hasErrors) {
@@ -48,7 +48,7 @@ var formatStats = (stats, port) => {
if (formattedErrors.some(isLikelyASyntaxError)) {
formattedErrors = formattedErrors.filter(isLikelyASyntaxError);
}
formattedErrors.forEach(message => {
formattedErrors.forEach((message) => {
console.log(message);
console.log();
});
@@ -58,7 +58,7 @@ var formatStats = (stats, port) => {
if (hasWarnings) {
console.log(chalk.yellow("Compiled with warnings."));
console.log();
formattedWarnings.forEach(message => {
formattedWarnings.forEach((message) => {
console.log(message);
console.log();
});

View File

@@ -24,15 +24,17 @@ async function obsAnnotationFetchAndLoad(dispatch, schema) {
const plimit = new PromiseLimit(5);
return Promise.all(
columns.map(col =>
columns.map((col) =>
plimit.add(() =>
fetchBinary(`annotations/obs?annotation-name=${encodeURIComponent(col.name)}`)
.then(buffer => Universe.matrixFBSToDataframe(buffer))
.then(df =>
fetchBinary(
`annotations/obs?annotation-name=${encodeURIComponent(col.name)}`
)
.then((buffer) => Universe.matrixFBSToDataframe(buffer))
.then((df) =>
dispatch({
type: "universe: column load success",
dim: "obsAnnotations",
dataframe: df
dataframe: df,
})
)
)
@@ -48,14 +50,14 @@ async function varAnnotationFetchAndLoad(dispatch, schema) {
const index = varAnnotations.index ?? false;
const names = index ? [index] : [];
return Promise.all(
names.map(name =>
names.map((name) =>
fetchBinary(`annotations/var?annotation-name=${encodeURIComponent(name)}`)
.then(buffer => Universe.matrixFBSToDataframe(buffer))
.then(df =>
.then((buffer) => Universe.matrixFBSToDataframe(buffer))
.then((df) =>
dispatch({
type: "universe: column load success",
dim: "varAnnotations",
dataframe: df
dataframe: df,
})
)
)
@@ -71,17 +73,18 @@ function layoutFetchAndLoad(dispatch, schema) {
const plimit = new PromiseLimit(5);
return Promise.all(
embNames.map(e =>
embNames.map((e) =>
plimit.add(() =>
fetchBinary(`layout/obs?layout-name=${encodeURIComponent(e)}`)
.then(buffer => Universe.matrixFBSToDataframe(buffer))
fetchBinary(
`layout/obs?layout-name=${encodeURIComponent(e)}`
).then((buffer) => Universe.matrixFBSToDataframe(buffer))
)
)
).then(dfs =>
).then((dfs) =>
dispatch({
type: "universe: column load success",
dim: "obsLayout",
dataframe: Dataframe.Dataframe.empty().withColsFromAll(dfs)
dataframe: Dataframe.Dataframe.empty().withColsFromAll(dfs),
})
);
}
@@ -90,13 +93,12 @@ function layoutFetchAndLoad(dispatch, schema) {
return promise fetching user-configured colors
*/
async function userColorsFetchAndLoad(dispatch) {
return fetchJson("colors")
.then(response =>
dispatch({
type: "universe: user color load success",
userColors: loadUserColorConfig(response)
})
);
return fetchJson("colors").then((response) =>
dispatch({
type: "universe: user color load success",
userColors: loadUserColorConfig(response),
})
);
}
/*
@@ -185,11 +187,15 @@ async function _doRequestExpressionData(dispatch, getState, genes) {
/* helper for this function only */
const fetchData = async (geneNames) => {
const query = geneNames
.map(g => `var:${dubEncURIComponent(varIndexName)}=${dubEncURIComponent(g)}`)
.map(
(g) =>
`var:${dubEncURIComponent(varIndexName)}=${dubEncURIComponent(g)}`
)
.join("&");
// TODO: why convert to an Object and not a Dataframe?
return fetchBinary(`data/var?${query}`)
.then(buffer => Universe.convertDataFBStoObject(universe, buffer));
return fetchBinary(`data/var?${query}`).then((buffer) =>
Universe.convertDataFBStoObject(universe, buffer)
);
};
/* preload data already in cache */
@@ -351,7 +357,7 @@ const requestDifferentialExpression = (set1, set2, num_genes = 10) => async (
*/
const plimit = new PromiseLimit(5);
await Promise.all(
topNGenes.map(gene =>
topNGenes.map((gene) =>
plimit.add(() => _doRequestExpressionData(dispatch, getState, [gene]))
)
);
@@ -432,11 +438,15 @@ const saveObsAnnotations = () => async (dispatch, getState) => {
};
function fetchJson(pathAndQuery) {
return doJsonRequest(`${globals.API.prefix}${globals.API.version}${pathAndQuery}`);
return doJsonRequest(
`${globals.API.prefix}${globals.API.version}${pathAndQuery}`
);
}
function fetchBinary(pathAndQuery) {
return doBinaryRequest(`${globals.API.prefix}${globals.API.version}${pathAndQuery}`);
return doBinaryRequest(
`${globals.API.prefix}${globals.API.version}${pathAndQuery}`
);
}
export default {

View File

@@ -3,7 +3,7 @@ import { Universe } from "../util/stateManager";
import {
postNetworkErrorToast,
postAsyncSuccessToast,
postAsyncFailureToast
postAsyncFailureToast,
} from "../components/framework/toasters";
function abortableFetch(request, opts, timeout = 0) {
@@ -18,7 +18,7 @@ function abortableFetch(request, opts, timeout = 0) {
setTimeout(() => controller.abort(), timeout);
}
return fetch(request, { ...opts, signal });
}
},
};
}
@@ -38,19 +38,19 @@ async function doReembedFetch(dispatch, getState) {
method: "PUT",
headers: new Headers({
Accept: "application/octet-stream",
"Content-Type": "application/json"
"Content-Type": "application/json",
}),
body: JSON.stringify({
method: "umap",
filter: { obs: { index: cells } }
filter: { obs: { index: cells } },
}),
credentials: "include"
credentials: "include",
},
60000 // 1 minute timeout
);
dispatch({
type: "reembed: request start",
abortableFetch: af
abortableFetch: af,
});
const res = await af.ready();
@@ -82,17 +82,17 @@ export function requestReembed() {
const buffer = await res.arrayBuffer();
const df = Universe.matrixFBSToDataframe(buffer);
dispatch({
type: "reembed: request completed"
type: "reembed: request completed",
});
dispatch({
type: "reembed: add reembedding",
embedding: df,
schema
schema,
});
postAsyncSuccessToast("Re-embedding has completed.");
} catch (error) {
dispatch({
type: "reembed: request aborted"
type: "reembed: request aborted",
});
if (error.name === "AbortError") {
postAsyncFailureToast("Re-embedding calculation was aborted.");
@@ -108,6 +108,6 @@ export function reembedResetWorldToUniverse(dispatch, getState) {
const { reembedController } = getState();
if (reembedController.pendingFetch) reembedController.pendingFetch.abort();
dispatch({
type: "reembed: clear all reembeddings"
type: "reembed: clear all reembeddings",
});
}

View File

@@ -14,10 +14,10 @@ import TermsOfServicePrompt from "./termsPrompt";
import actions from "../actions";
@connect(state => ({
@connect((state) => ({
loading: state.controls.loading,
error: state.controls.error,
graphRenderCounter: state.controls.graphRenderCounter
graphRenderCounter: state.controls.graphRenderCounter,
}))
class App extends React.Component {
constructor(props) {
@@ -40,16 +40,16 @@ class App extends React.Component {
type: "window resize",
data: {
height: window.innerHeight,
width: window.innerWidth
}
width: window.innerWidth,
},
});
});
dispatch({
type: "window resize",
data: {
height: window.innerHeight,
width: window.innerWidth
}
width: window.innerWidth,
},
});
}
@@ -70,7 +70,7 @@ class App extends React.Component {
position: "fixed",
fontWeight: 500,
top: window.innerHeight / 2,
left: window.innerWidth / 2 - 50
left: window.innerWidth / 2 - 50,
}}
>
loading cellxgene
@@ -82,7 +82,7 @@ class App extends React.Component {
position: "fixed",
fontWeight: 500,
top: window.innerHeight / 2,
left: window.innerWidth / 2 - 50
left: window.innerWidth / 2 - 50,
}}
>
error loading

View File

@@ -7,10 +7,10 @@ import {
InputGroup,
Dialog,
Classes,
Colors
Colors,
} from "@blueprintjs/core";
@connect(state => ({
@connect((state) => ({
universe: state.universe,
idhash: state.config?.parameters?.["annotations-user-data-idhash"] ?? null,
annotations: state.annotations,
@@ -18,13 +18,13 @@ 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) {
super(props);
this.state = {
filenameText: ""
filenameText: "",
};
}
@@ -36,7 +36,7 @@ class FilenameDialog extends React.Component {
dispatch({
type: "set annotations collection name",
data: filenameText
data: filenameText,
});
};
@@ -71,7 +71,7 @@ class FilenameDialog extends React.Component {
fontStyle: "italic",
fontSize: 12,
marginTop: 5,
color: Colors.ORANGE3
color: Colors.ORANGE3,
}}
>
Name cannot be blank
@@ -84,7 +84,7 @@ class FilenameDialog extends React.Component {
fontStyle: "italic",
fontSize: 12,
marginTop: 5,
color: Colors.ORANGE3
color: Colors.ORANGE3,
}}
>
Only alphanumeric and underscore allowed
@@ -108,7 +108,7 @@ class FilenameDialog extends React.Component {
onClose={this.dismissFilenameDialog}
>
<form
onSubmit={e => {
onSubmit={(e) => {
e.preventDefault();
this.handleCreateFilename();
}}
@@ -120,7 +120,9 @@ class FilenameDialog extends React.Component {
autoFocus
value={filenameText}
intent={this.filenameError(filenameText) ? "warning" : "none"}
onChange={e => this.setState({ filenameText: e.target.value })}
onChange={(e) =>
this.setState({ filenameText: e.target.value })
}
leftIcon="tag"
/>
<p
@@ -129,7 +131,7 @@ class FilenameDialog extends React.Component {
visibility: this.filenameError(filenameText)
? "visible"
: "hidden",
color: Colors.ORANGE3
color: Colors.ORANGE3,
}}
>
{this.filenameErrorMessage(filenameText)}

View File

@@ -4,7 +4,7 @@ import * as globals from "../../globals";
import actions from "../../actions";
import FilenameDialog from "./filenameDialog";
@connect(state => ({
@connect((state) => ({
universe: state.universe,
annotations: state.annotations,
obsAnnotations: state.universe.obsAnnotations,
@@ -12,13 +12,13 @@ import FilenameDialog from "./filenameDialog";
lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations,
error: state.autosave?.error,
writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false,
initialDataLoadComplete: state.autosave?.initialDataLoadComplete
initialDataLoadComplete: state.autosave?.initialDataLoadComplete,
}))
class Autosave extends React.Component {
constructor(props) {
super(props);
this.state = {
timer: null
timer: null,
};
}
@@ -64,22 +64,26 @@ class Autosave extends React.Component {
}
render() {
const { writableCategoriesEnabled, saveInProgress, initialDataLoadComplete } = this.props;
const {
writableCategoriesEnabled,
saveInProgress,
initialDataLoadComplete,
} = this.props;
return writableCategoriesEnabled ? (
<div
id="autosave"
data-testclass={
!initialDataLoadComplete
? "autosave-init"
: (this.needToSave() || saveInProgress)
? "autosave-incomplete"
: "autosave-complete"
: this.needToSave() || saveInProgress
? "autosave-incomplete"
: "autosave-complete"
}
style={{
position: "fixed",
display: "inherit",
right: globals.leftSidebarWidth + 5,
bottom: 5
bottom: 5,
}}
>
{this.statusMessage()}

View File

@@ -2,11 +2,11 @@ import React from "react";
import { connect } from "react-redux";
import { Button, Tooltip, Dialog, Classes, Colors } from "@blueprintjs/core";
@connect(state => ({
@connect((state) => ({
colorAccessor: state.colors.colorAccessor,
categoricalSelection: state.categoricalSelection,
annotations: state.annotations,
universe: state.universe
universe: state.universe,
}))
class AnnoDialog extends React.PureComponent {
constructor(props) {
@@ -30,13 +30,13 @@ class AnnoDialog extends React.PureComponent {
primaryButtonText,
secondaryButtonText,
handleSecondaryButtonSubmit,
primaryButtonProps
primaryButtonProps,
} = this.props;
return (
<Dialog icon="tag" title={title} isOpen={isActive} onClose={handleCancel}>
<form
onSubmit={e => {
onSubmit={(e) => {
e.preventDefault();
}}
>
@@ -48,7 +48,7 @@ class AnnoDialog extends React.PureComponent {
style={{
marginTop: 7,
visibility: validationError ? "visible" : "hidden",
color: Colors.ORANGE3
color: Colors.ORANGE3,
}}
>
{errorMessage}

View File

@@ -3,11 +3,11 @@ import { connect } from "react-redux";
import { Button, MenuItem } from "@blueprintjs/core";
import { Select } from "@blueprintjs/select";
@connect(state => ({
@connect((state) => ({
colorAccessor: state.colors.colorAccessor,
categoricalSelection: state.categoricalSelection,
annotations: state.annotations,
universe: state.universe
universe: state.universe,
}))
class DuplicateCategorySelect extends React.PureComponent {
constructor(props) {
@@ -19,7 +19,7 @@ class DuplicateCategorySelect extends React.PureComponent {
const {
allCategoryNames,
categoryToDuplicate,
handleModalDuplicateCategorySelection
handleModalDuplicateCategorySelection,
} = this.props;
return (
<div>
@@ -37,7 +37,7 @@ class DuplicateCategorySelect extends React.PureComponent {
return <MenuItem onClick={handleClick} key={d} text={d} />;
}}
noResults={<MenuItem disabled text="No results." />}
onItemSelect={d => {
onItemSelect={(d) => {
handleModalDuplicateCategorySelection(d);
}}
>

View File

@@ -6,11 +6,11 @@ import {
MenuItem,
Popover,
Position,
PopoverInteractionKind
PopoverInteractionKind,
} from "@blueprintjs/core";
@connect(state => ({
annotations: state.annotations
@connect((state) => ({
annotations: state.annotations,
}))
class AnnoMenuCategory extends React.PureComponent {
constructor(props) {
@@ -22,7 +22,7 @@ class AnnoMenuCategory extends React.PureComponent {
const { dispatch, metadataField } = this.props;
dispatch({
type: "annotation: activate add new label mode",
data: metadataField
data: metadataField,
});
};
@@ -31,7 +31,7 @@ class AnnoMenuCategory extends React.PureComponent {
dispatch({
type: "annotation: activate category edit mode",
data: metadataField
data: metadataField,
});
};
@@ -39,7 +39,7 @@ class AnnoMenuCategory extends React.PureComponent {
const { dispatch, metadataField } = this.props;
dispatch({
type: "annotation: delete category",
metadataField
metadataField,
});
};
@@ -50,7 +50,7 @@ class AnnoMenuCategory extends React.PureComponent {
isUserAnno,
createText,
editText,
deleteText
deleteText,
} = this.props;
return (

View File

@@ -31,7 +31,7 @@ export default class LabelInput extends React.PureComponent {
const queryResults = this.filterLabels(query);
this.state = {
query,
queryResults
queryResults,
};
}
@@ -42,7 +42,7 @@ export default class LabelInput extends React.PureComponent {
const queryResults = this.filterLabels(query);
this.setState({
query,
queryResults
queryResults,
});
const { onChange } = this.props;
@@ -57,7 +57,7 @@ export default class LabelInput extends React.PureComponent {
if (target !== query && onSelect) onSelect(target, event);
};
handleKeyDown = e => {
handleKeyDown = (e) => {
/*
prevent these events from propagating to containing form/dialog
and causing further side effects (eg, closing dialog, submitting
@@ -72,7 +72,7 @@ export default class LabelInput extends React.PureComponent {
}
};
handleChange = e => {
handleChange = (e) => {
const { onChange } = this.props;
if (onChange) onChange(e.target.value);
};
@@ -112,16 +112,18 @@ export default class LabelInput extends React.PureComponent {
/* empty query is wildcard */
if (query === "") {
return labelSuggestions.slice(0, LabelInput.QueryResultLimit).map(l => ({
target: l,
score: -10000
}));
return labelSuggestions
.slice(0, LabelInput.QueryResultLimit)
.map((l) => ({
target: l,
score: -10000,
}));
}
/* else, do a fuzzy query */
const options = {
limit: LabelInput.QueryResultLimit,
threshold: -10000 // don't return bad results
threshold: -10000, // don't return bad results
};
let queryResults = fuzzysort.go(query, labelSuggestions, options);
/* exact match will always be first in list */
@@ -149,18 +151,18 @@ export default class LabelInput extends React.PureComponent {
const popoverProps = {
minimal: true,
...props.popoverProps
...props.popoverProps,
};
const inputProps = {
...props.inputProps,
autoFocus: false
autoFocus: false,
};
const { queryResults } = this.state;
return (
<>
<Suggest
fill
inputValueRenderer={i => i.target}
inputValueRenderer={(i) => i.target}
items={queryResults}
itemRenderer={this.renderLabelSuggestion}
onItemSelect={this.handleItemSelect}

View File

@@ -4,60 +4,60 @@ import { Colors } from "@blueprintjs/core";
import { AnnotationsHelpers } from "../../util/stateManager";
export function isLabelErroneous(label, metadataField, ontology, schema) {
/*
/*
return false if this is a LEGAL/acceptable category name or NULL/empty string,
or return an error type.
*/
/* allow empty string */
if (label === "") return false;
/* allow empty string */
if (label === "") return false;
/* check for label syntax errors, but allow terms in ontology */
const termInOntology = ontology?.termSet.has(label) ?? false;
const error = AnnotationsHelpers.annotationNameIsErroneous(label);
if (error && !termInOntology) return error;
/* check for label syntax errors, but allow terms in ontology */
const termInOntology = ontology?.termSet.has(label) ?? false;
const error = AnnotationsHelpers.annotationNameIsErroneous(label);
if (error && !termInOntology) return error;
/* disallow duplicates */
const { obsByName } = schema.annotations;
if (obsByName[metadataField].categories.indexOf(label) !== -1)
return "duplicate";
/* disallow duplicates */
const { obsByName } = schema.annotations;
if (obsByName[metadataField].categories.indexOf(label) !== -1)
return "duplicate";
/* otherwise, no error */
return false;
/* otherwise, no error */
return false;
}
/* all other errors - map code to human error message */
const errorMessageMap = {
"empty-string": "Blank names not allowed",
duplicate: "Name must be unique",
"trim-spaces": "Leading and trailing spaces not allowed",
"illegal-characters":
"Only alphanumeric and special characters (-_.) allowed",
"multi-space-run": "Multiple consecutive spaces not allowed"
"empty-string": "Blank names not allowed",
duplicate: "Name must be unique",
"trim-spaces": "Leading and trailing spaces not allowed",
"illegal-characters":
"Only alphanumeric and special characters (-_.) allowed",
"multi-space-run": "Multiple consecutive spaces not allowed",
};
export function labelPrompt(err, prolog, epilog) {
let errPrompt = null;
if (err) {
let errMsg = errorMessageMap[err] ?? "error";
errMsg = errMsg[0].toLowerCase() + errMsg.slice(1);
errPrompt = (
<span
style={{
marginTop: 7,
color: Colors.ORANGE3
}}
>
{errMsg}
</span>
);
}
return (
<span>
{prolog}
{err ? " - " : null}
{errPrompt}
{epilog}
</span>
);
let errPrompt = null;
if (err) {
let errMsg = errorMessageMap[err] ?? "error";
errMsg = errMsg[0].toLowerCase() + errMsg.slice(1);
errPrompt = (
<span
style={{
marginTop: 7,
color: Colors.ORANGE3,
}}
>
{errMsg}
</span>
);
}
return (
<span>
{prolog}
{err ? " - " : null}
{errPrompt}
{epilog}
</span>
);
}

View File

@@ -33,7 +33,7 @@ const setupParallelCoordinates = (width, height, margin) => {
return {
svg,
ctx
ctx,
};
};

View File

@@ -13,7 +13,7 @@ export const innerHeight = height - 2;
export const devicePixelRatio = window.devicePixelRatio || 1;
export const createDimensions = data => {
export const createDimensions = (data) => {
const newArr = [];
_.each(data, (value, key) => {
if (value.range) {
@@ -21,12 +21,12 @@ export const createDimensions = data => {
key /* room for confusion: lodash calls this key, it's also the name of the property parallel coords code is looking for */,
type: {
within: (d, extent, dim) =>
extent[0] <= dim.scale(d) && dim.scale(d) <= extent[1]
extent[0] <= dim.scale(d) && dim.scale(d) <= extent[1],
},
scale: d3
.scaleSqrt()
.range([innerHeight, 0])
.domain([0, value.range.max])
.domain([0, value.range.max]),
});
}
});

View File

@@ -38,12 +38,12 @@ const continuous = (selectorId, colorscale, colorAccessor) => {
.range([1, legendheight - margin.top - margin.bottom])
.domain([
colorscale.domain()[1],
colorscale.domain()[0]
colorscale.domain()[0],
]); /* we flip this to make viridis colors dark if high in the color scale */
// image data hackery based on http://bl.ocks.org/mbostock/048d21cf747371b11884f75ad896e5a5
const image = ctx.createImageData(1, legendheight);
d3.range(legendheight).forEach(i => {
d3.range(legendheight).forEach((i) => {
const c = d3.rgb(colorscale(legendscale.invert(i)));
image.data[4 * i] = c.r;
image.data[4 * i + 1] = c.g;
@@ -62,11 +62,7 @@ const continuous = (selectorId, colorscale, colorAccessor) => {
});
*/
const legendaxis = d3
.axisRight()
.scale(legendscale)
.tickSize(6)
.ticks(8);
const legendaxis = d3.axisRight().scale(legendscale).tickSize(6).ticks(8);
const svg = d3
.select(selectorId)
@@ -98,10 +94,10 @@ const continuous = (selectorId, colorscale, colorAccessor) => {
.text(colorAccessor);
};
@connect(state => ({
@connect((state) => ({
colorAccessor: state.colors.colorAccessor,
colorScale: state.colors.scale,
responsive: state.responsive
responsive: state.responsive,
}))
class ContinuousLegend extends React.Component {
constructor(props) {
@@ -118,9 +114,7 @@ class ContinuousLegend extends React.Component {
prevProps.responsive.width !== responsive.width
) {
/* always remove it, if it's not continuous we don't put it back. */
d3.select("#continuous_legend")
.selectAll("*")
.remove();
d3.select("#continuous_legend").selectAll("*").remove();
}
if (colorAccessor && colorScale && colorScale.range) {
@@ -144,7 +138,7 @@ class ContinuousLegend extends React.Component {
position: "fixed",
display: colorAccessor ? "inherit" : "none",
right: globals.leftSidebarWidth,
top: responsive.height / 2
top: responsive.height / 2,
}}
/>
);

View File

@@ -3,7 +3,7 @@ import React from "react";
import styles from "./container.css";
const Container = props => {
const Container = (props) => {
const { children } = props;
return <div className={styles.container}>{children}</div>;
};

View File

@@ -1,7 +1,7 @@
import React from "react";
import * as globals from "../../globals";
const Logo = props => {
const Logo = (props) => {
const { size } = props;
return (
<svg width={size} height={size} viewBox="0 0 48 48" fill="none">

View File

@@ -11,7 +11,7 @@ import {
Button,
FormGroup,
InputGroup,
ControlGroup
ControlGroup,
} from "@blueprintjs/core";
import { Suggest } from "@blueprintjs/select";
import HistogramBrush from "../brushableHistogram";
@@ -19,7 +19,7 @@ import * as globals from "../../globals";
import actions from "../../actions";
import {
postUserErrorToast,
keepAroundErrorToast
keepAroundErrorToast,
} from "../framework/toasters";
import { memoize } from "../../util/dataframe/util";
@@ -40,7 +40,7 @@ const renderGene = (fuzzySortResult, { handleClick, modifiers }) => {
// See https://github.com/chanzuckerberg/cellxgene/issues/483
// label={gene.n_counts}
key={geneName}
onClick={g =>
onClick={(g) =>
/* this fires when user clicks a menu item */
handleClick(g)
}
@@ -53,17 +53,17 @@ const filterGenes = (query, genes) =>
/* fires on load, once, and then for each character typed into the input */
fuzzysort.go(query, genes, {
limit: 5,
threshold: -10000 // don't return bad results
threshold: -10000, // don't return bad results
});
@connect(state => {
@connect((state) => {
return {
obsAnnotations: state.world?.obsAnnotations,
userDefinedGenes: state.controls.userDefinedGenes,
userDefinedGenesLoading: state.controls.userDefinedGenesLoading,
world: state.world,
colorAccessor: state.colors.colorAccessor,
differential: state.differential
differential: state.differential,
};
})
class GeneExpression extends React.Component {
@@ -72,11 +72,11 @@ class GeneExpression extends React.Component {
this.state = {
bulkAdd: "",
tab: "autosuggest",
activeItem: null
activeItem: null,
};
}
_genesToUpper = listGenes => {
_genesToUpper = (listGenes) => {
// Has to be a Map to preserve index
const upperGenes = new Map();
for (let i = 0, { length } = listGenes; i < length; i += 1) {
@@ -87,7 +87,7 @@ class GeneExpression extends React.Component {
};
// eslint-disable-next-line react/sort-comp
_memoGenesToUpper = memoize(this._genesToUpper, arr => arr);
_memoGenesToUpper = memoize(this._genesToUpper, (arr) => arr);
handleBulkAddClick = () => {
const { world, dispatch, userDefinedGenes } = this.props;
@@ -115,7 +115,7 @@ class GeneExpression extends React.Component {
dispatch({ type: "bulk user defined gene start" });
Promise.all(
[...upperGenes.keys()].map(upperGene => {
[...upperGenes.keys()].map((upperGene) => {
if (upperUserDefinedGenes.get(upperGene) !== undefined) {
return keepAroundErrorToast("That gene already exists");
}
@@ -204,7 +204,7 @@ class GeneExpression extends React.Component {
world,
userDefinedGenes,
userDefinedGenesLoading,
differential
differential,
} = this.props;
const varIndexName = world?.schema?.annotations?.var?.index;
const varIndex = world?.varAnnotations?.col(varIndexName)?.asArray();
@@ -218,7 +218,7 @@ class GeneExpression extends React.Component {
<div>
<div
style={{
padding: globals.leftSidebarSectionPadding
padding: globals.leftSidebarSectionPadding,
}}
>
<Button
@@ -250,7 +250,7 @@ class GeneExpression extends React.Component {
<ControlGroup
style={{
paddingLeft: globals.leftSidebarSectionPadding,
paddingBottom: globals.leftSidebarSectionPadding
paddingBottom: globals.leftSidebarSectionPadding,
}}
>
<Suggest
@@ -261,7 +261,7 @@ class GeneExpression extends React.Component {
userDefinedGenesLoading ? () => true : () => false
}
noResults={<MenuItem disabled text="No matching genes." />}
onItemSelect={g => {
onItemSelect={(g) => {
/* this happens on 'enter' */
this.handleClick(g);
}}
@@ -271,7 +271,9 @@ class GeneExpression extends React.Component {
return "";
}}
itemListPredicate={filterGenes}
onActiveItemChange={item => this.setState({ activeItem: item })}
onActiveItemChange={(item) =>
this.setState({ activeItem: item })
}
itemRenderer={renderGene.bind(this)}
items={varIndex || ["No genes"]}
popoverProps={{ minimal: true }}
@@ -289,7 +291,7 @@ class GeneExpression extends React.Component {
{tab === "bulkadd" ? (
<div style={{ paddingLeft: globals.leftSidebarSectionPadding }}>
<form
onSubmit={e => {
onSubmit={(e) => {
e.preventDefault();
this.handleBulkAddClick();
}}
@@ -300,7 +302,7 @@ class GeneExpression extends React.Component {
>
<ControlGroup>
<InputGroup
onChange={e => {
onChange={(e) => {
this.setState({ bulkAdd: e.target.value });
}}
id="text-input-bulk-add"

View File

@@ -1,6 +1,6 @@
import { glPointFlags, glPointSize } from "../../util/glHelpers";
export default function(regl) {
export default function (regl) {
return regl({
vert: `
precision mediump float;
@@ -54,18 +54,18 @@ export default function(regl) {
attributes: {
position: regl.prop("position"),
color: regl.prop("color"),
flag: regl.prop("flag")
flag: regl.prop("flag"),
},
uniforms: {
distance: regl.prop("distance"),
projView: regl.prop("projView"),
nPoints: regl.prop("nPoints"),
minViewportDimension: regl.prop("minViewportDimension")
minViewportDimension: regl.prop("minViewportDimension"),
},
count: regl.prop("count"),
primitive: "points"
primitive: "points",
});
}

View File

@@ -34,12 +34,12 @@ function createProjectionTF(viewportWidth, viewportHeight) {
const minDim = Math.min(viewportWidth, heightMinusGutter);
const aspectScale = [
(fractionToUse * minDim) / viewportWidth,
(fractionToUse * minDim) / viewportHeight
(fractionToUse * minDim) / viewportHeight,
];
const m = mat3.create();
mat3.fromTranslation(m, [
0,
-topGutterSizePx / viewportHeight / aspectScale[1]
-topGutterSizePx / viewportHeight / aspectScale[1],
]);
mat3.scale(m, m, aspectScale);
return m;
@@ -73,7 +73,7 @@ function renderThrottle(callback) {
};
}
@connect(state => ({
@connect((state) => ({
universe: state.universe,
world: state.world,
crossfilter: state.crossfilter,
@@ -85,7 +85,7 @@ function renderThrottle(callback) {
centroidLabels: state.centroidLabels,
graphInteractionMode: state.controls.graphInteractionMode,
colorAccessor: state.colors.colorAccessor,
pointDilation: state.pointDilation
pointDilation: state.pointDilation,
}))
class Graph extends React.Component {
computePointPositions = memoize((X, Y, modelTF) => {
@@ -102,7 +102,7 @@ class Graph extends React.Component {
return positions;
});
computePointColors = memoize(rgb => {
computePointColors = memoize((rgb) => {
/*
compute webgl colors for each point
*/
@@ -186,13 +186,13 @@ class Graph extends React.Component {
positions: null,
colors: null,
sizes: null,
flags: null
flags: null,
};
this.state = {
toolSVG: null,
tool: null,
container: null,
cameraRender: 0
cameraRender: 0,
};
}
@@ -234,7 +234,7 @@ class Graph extends React.Component {
camera,
modelTF,
modelInvTF: mat3.invert([], modelTF),
projectionTF
projectionTF,
});
}
@@ -250,7 +250,7 @@ class Graph extends React.Component {
layoutChoice,
graphInteractionMode,
pointDilation,
colorAccessor
colorAccessor,
} = this.props;
const { regl, toolSVG, camera, modelTF } = this.state;
let stateChanges = {};
@@ -274,7 +274,7 @@ class Graph extends React.Component {
needsRepaint = true;
stateChanges = {
...stateChanges,
projectionTF
projectionTF,
};
}
@@ -333,7 +333,7 @@ class Graph extends React.Component {
// If the window size has changed we want to recreate all SVGs
stateChanges = {
...stateChanges,
...this.createToolSVG()
...this.createToolSVG(),
};
} else if (
(responsive.height && responsive.width && !toolSVG) ||
@@ -345,7 +345,7 @@ class Graph extends React.Component {
// If lasso/zoom is switched
stateChanges = {
...stateChanges,
...this.createToolSVG()
...this.createToolSVG(),
};
}
@@ -369,12 +369,12 @@ class Graph extends React.Component {
}
}
handleCanvasEvent = e => {
handleCanvasEvent = (e) => {
const { camera, projectionTF } = this.state;
if (e.type !== "wheel") e.preventDefault();
if (camera.handleEvent(e, projectionTF)) {
this.renderCanvas();
this.setState(state => {
this.setState((state) => {
return { ...state, updateOverlay: !state.updateOverlay };
});
}
@@ -389,9 +389,7 @@ class Graph extends React.Component {
/* clear out whatever was on the div, even if nothing, but usually the brushes etc */
d3.select("#lasso-layer")
.selectAll(".lasso-group")
.remove();
d3.select("#lasso-layer").selectAll(".lasso-group").remove();
// Don't render or recreate toolSVG if currently in zoom mode
if (graphInteractionMode !== "select") {
@@ -443,7 +441,7 @@ class Graph extends React.Component {
*/
const screenCoords = [
this.mapPointToScreen(currentSelection.brushCoords.northwest),
this.mapPointToScreen(currentSelection.brushCoords.southeast)
this.mapPointToScreen(currentSelection.brushCoords.southeast),
];
if (!toolCurrentSelection) {
/* tool is not selected, so just move the brush */
@@ -480,7 +478,7 @@ class Graph extends React.Component {
/*
if there is a current selection, make sure the lasso tool matches
*/
const polygon = currentSelection.polygon.map(p =>
const polygon = currentSelection.polygon.map((p) =>
this.mapPointToScreen(p)
);
tool.move(polygon);
@@ -551,7 +549,7 @@ class Graph extends React.Component {
),
Math.round(
-((xy[1] + 1) / 2 - 1) * (responsive.height - this.graphPaddingTop)
)
),
];
return pin;
}
@@ -571,12 +569,12 @@ class Graph extends React.Component {
const s = d3.event.selection;
const brushCoords = {
northwest: this.mapScreenToPoint([s[0][0], s[0][1]]),
southeast: this.mapScreenToPoint([s[1][0], s[1][1]])
southeast: this.mapScreenToPoint([s[1][0], s[1][1]]),
};
dispatch({
type: "graph brush change",
brushCoords
brushCoords,
});
}
@@ -601,15 +599,15 @@ class Graph extends React.Component {
if (s) {
const brushCoords = {
northwest: this.mapScreenToPoint(s[0]),
southeast: this.mapScreenToPoint(s[1])
southeast: this.mapScreenToPoint(s[1]),
};
dispatch({
type: "graph brush end",
brushCoords
brushCoords,
});
} else {
dispatch({
type: "graph brush deselect"
type: "graph brush deselect",
});
}
}
@@ -617,14 +615,14 @@ class Graph extends React.Component {
handleBrushDeselectAction() {
const { dispatch } = this.props;
dispatch({
type: "graph brush deselect"
type: "graph brush deselect",
});
}
handleLassoStart() {
const { dispatch } = this.props;
dispatch({
type: "graph lasso start"
type: "graph lasso start",
});
}
@@ -642,7 +640,7 @@ class Graph extends React.Component {
} else {
dispatch({
type: "graph lasso end",
polygon: polygon.map(xy => this.mapScreenToPoint(xy)) // transform the polygon
polygon: polygon.map((xy) => this.mapScreenToPoint(xy)), // transform the polygon
});
}
}
@@ -667,7 +665,7 @@ class Graph extends React.Component {
const { dispatch } = this.props;
dispatch({
type: "change opacity deselected cells in 2d graph background",
data: e.target.value
data: e.target.value,
});
}
@@ -688,7 +686,7 @@ class Graph extends React.Component {
regl.poll();
regl.clear({
depth: 1,
color: [1, 1, 1, 1]
color: [1, 1, 1, 1],
});
drawPoints({
distance: camera.distance(),
@@ -698,7 +696,7 @@ class Graph extends React.Component {
count: this.count,
projView,
nPoints: universe.nObs,
minViewportDimension: Math.min(width || 800, height || 600)
minViewportDimension: Math.min(width || 800, height || 600),
});
regl._gl.flush();
}
@@ -711,7 +709,7 @@ class Graph extends React.Component {
pointBuffer,
flagBuffer,
camera,
projectionTF
projectionTF,
} = this.state;
this.renderPoints(
regl,
@@ -737,7 +735,7 @@ class Graph extends React.Component {
zIndex: -9999,
position: "fixed",
top: this.graphPaddingTop,
right: globals.leftSidebarWidth
right: globals.leftSidebarWidth,
}}
>
<div id="graphAttachPoint">
@@ -774,7 +772,7 @@ class Graph extends React.Component {
width={responsive.width - this.graphPaddingRightLeft}
height={responsive.height - this.graphPaddingTop}
data-testid="layout-graph"
ref={canvas => {
ref={(canvas) => {
this.reglCanvas = canvas;
}}
onMouseDown={this.handleCanvasEvent}

View File

@@ -6,14 +6,14 @@ import { connect } from "react-redux";
import { categoryLabelDisplayStringLongLength } from "../../../globals";
export default
@connect(state => ({
@connect((state) => ({
colorAccessor: state.colors.colorAccessor,
dilatedValue: state.pointDilation.categoryField,
labels: state.centroidLabels.labels
labels: state.centroidLabels.labels,
}))
class CentroidLabels extends PureComponent {
// Check to see if centroids have either just been displayed or removed from the overlay
componentDidUpdate = prevProps => {
componentDidUpdate = (prevProps) => {
const { labels, overlayToggled } = this.props;
const prevSize = prevProps.labels.size;
const { size } = labels;
@@ -33,7 +33,7 @@ class CentroidLabels extends PureComponent {
inverseTransform,
dilatedValue,
dispatch,
colorAccessor
colorAccessor,
} = this.props;
const labelSVGS = [];
@@ -74,20 +74,20 @@ class CentroidLabels extends PureComponent {
fontSize,
fontWeight,
fill: "black",
userSelect: "none"
userSelect: "none",
}}
onMouseEnter={e =>
onMouseEnter={(e) =>
dispatch({
type: "category value mouse hover start",
metadataField: colorAccessor,
categoryField: e.target.getAttribute("data-label")
categoryField: e.target.getAttribute("data-label"),
})
}
onMouseOut={e =>
onMouseOut={(e) =>
dispatch({
type: "category value mouse hover end",
metadataField: colorAccessor,
categoryField: e.target.getAttribute("data-label")
categoryField: e.target.getAttribute("data-label"),
})
}
pointerEvents="visiblePainted"

View File

@@ -4,8 +4,8 @@ import { connect } from "react-redux";
import styles from "../graph.css";
export default
@connect(state => ({
responsive: state.responsive
@connect((state) => ({
responsive: state.responsive,
}))
class GraphOverlayLayer extends PureComponent {
/*
@@ -18,11 +18,11 @@ class GraphOverlayLayer extends PureComponent {
super(props);
this.state = {
display: {}
display: {},
};
}
matrixToTransformString = m => {
matrixToTransformString = (m) => {
/*
Translates the gl-matrix mat3 to SVG matrix transform style
@@ -34,13 +34,13 @@ class GraphOverlayLayer extends PureComponent {
return `matrix(${m[0]} ${m[1]} ${m[3]} ${m[4]} ${m[6]} ${m[7]})`;
};
reverseMatrixScaleTransformString = m => {
reverseMatrixScaleTransformString = (m) => {
return `matrix(${1 / m[0]} 0 0 ${1 / m[4]} 0 0)`;
};
// This is passed to all children, should be called when an overlay's display state is toggled along with the overlay name and its new display state in boolean form
overlayToggled = (overlay, displaying) => {
this.setState(state => {
this.setState((state) => {
return { ...state, display: { ...state.display, [overlay]: displaying } };
});
};
@@ -54,13 +54,13 @@ class GraphOverlayLayer extends PureComponent {
graphPaddingRightLeft,
graphPaddingTop,
children,
handleCanvasEvent
handleCanvasEvent,
} = this.props;
if (!cameraTF) return null;
const { display } = this.state;
const displaying = Object.values(display).some(value => value); // check to see if at least one overlay is currently displayed
const displaying = Object.values(display).some((value) => value); // check to see if at least one overlay is currently displayed
const inverseTransform = `${this.reverseMatrixScaleTransformString(
modelTF
@@ -68,15 +68,15 @@ class GraphOverlayLayer extends PureComponent {
cameraTF
)} ${this.reverseMatrixScaleTransformString(
projectionTF
)} scale(1 2) scale(1 ${1 /
-(responsive.height - graphPaddingTop)}) scale(2 1) scale(${1 /
(responsive.width - graphPaddingRightLeft)} 1)`;
)} scale(1 2) scale(1 ${
1 / -(responsive.height - graphPaddingTop)
}) scale(2 1) scale(${1 / (responsive.width - graphPaddingRightLeft)} 1)`;
// Copy the children passed with the overlay and add the inverse transform and onDisplayChange props
const newChildren = React.Children.map(children, child =>
const newChildren = React.Children.map(children, (child) =>
cloneElement(child, {
inverseTransform,
overlayToggled: this.overlayToggled
overlayToggled: this.overlayToggled,
})
);
@@ -88,15 +88,16 @@ class GraphOverlayLayer extends PureComponent {
pointerEvents="none"
style={{
zIndex: 99,
backgroundColor: displaying ? "rgba(255, 255, 255, 0.55)" : ""
backgroundColor: displaying ? "rgba(255, 255, 255, 0.55)" : "",
}}
onMouseMove={handleCanvasEvent}
onWheel={handleCanvasEvent}
>
<g
id="canvas-transformation-group-x"
transform={`scale(${responsive.width -
graphPaddingRightLeft} 1) scale(.5 1) translate(1 0)`}
transform={`scale(${
responsive.width - graphPaddingRightLeft
} 1) scale(.5 1) translate(1 0)`}
>
<g
id="canvas-transformation-group-y"

View File

@@ -5,8 +5,8 @@ import * as d3 from "d3";
const Lasso = () => {
const dispatch = d3.dispatch("start", "end", "cancel");
const polygonToPath = polygon =>
`M${polygon.map(d => d.join(",")).join("L")}`;
const polygonToPath = (polygon) =>
`M${polygon.map((d) => d.join(",")).join("L")}`;
const distance = (pt1, pt2) =>
Math.sqrt((pt2[0] - pt1[0]) ** 2 + (pt2[1] - pt1[1]) ** 2);
@@ -14,7 +14,7 @@ const Lasso = () => {
// distance last point has to be to first point before it auto closes when mouse is released
const closeDistance = 75;
const lasso = svg => {
const lasso = (svg) => {
let lassoPolygon;
let lassoPath;
let closePath;
@@ -55,10 +55,7 @@ const Lasso = () => {
distance(lassoPolygon[0], lassoPolygon[lassoPolygon.length - 1]) <
closeDistance
) {
closePath
.attr("x1", point[0])
.attr("y1", point[1])
.attr("opacity", 1);
closePath.attr("x1", point[0]).attr("y1", point[1]).attr("opacity", 1);
} else {
closePath.attr("opacity", 0);
}
@@ -116,7 +113,7 @@ const Lasso = () => {
}
};
lasso.move = polygon => {
lasso.move = (polygon) => {
if (polygon !== lassoPolygon || polygon.length !== lassoPolygon.length) {
lasso.reset();

View File

@@ -24,7 +24,7 @@ export default (
.brush()
.extent([
[0, 0],
[responsive.width - graphPaddingRight, responsive.height]
[responsive.width - graphPaddingRight, responsive.height],
])
.on("start", handleStartAction)
.on("brush", handleDragAction)

View File

@@ -4,12 +4,12 @@ import { connect } from "react-redux";
import * as globals from "../../globals";
import Logo from "../framework/logo";
@connect(state => ({
@connect((state) => ({
responsive: state.responsive,
datasetTitle: state.config?.displayNames?.dataset ?? "",
aboutURL: state.config?.links?.["about-dataset"],
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
scatterplotYYaccessor: state.controls.scatterplotYYaccessor
scatterplotYYaccessor: state.controls.scatterplotYYaccessor,
}))
class LeftSideBar extends React.Component {
render() {
@@ -35,7 +35,7 @@ class LeftSideBar extends React.Component {
width: globals.leftSidebarWidth - paddingToAvoidScrollBar,
position: "absolute",
backgroundColor: "white",
zIndex: 8888
zIndex: 8888,
/* x y blur spread color */
// boxShadow: "-5px -1px 4px 2px rgba(225,225,225,0.4)"
}}
@@ -49,7 +49,7 @@ class LeftSideBar extends React.Component {
fontWeight: "bold",
marginLeft: 5,
color: globals.logoColor,
userSelect: "none"
userSelect: "none",
}}
>
cell
@@ -58,7 +58,7 @@ class LeftSideBar extends React.Component {
position: "relative",
top: 1,
fontWeight: 300,
fontSize: 24
fontSize: 24,
}}
>
×
@@ -76,11 +76,17 @@ class LeftSideBar extends React.Component {
marginLeft: "7px",
height: "1.2em",
overflow: "hidden",
wordBreak: "break-all"
wordBreak: "break-all",
}}
title={datasetTitle}
>
{aboutURL ? <a href={aboutURL} target="_blank">{displayTitle}</a> : displayTitle}
{aboutURL ? (
<a href={aboutURL} target="_blank">
{displayTitle}
</a>
) : (
displayTitle
)}
</div>
</div>
);

View File

@@ -12,7 +12,7 @@ class CellSetButton extends React.PureComponent {
differential,
crossfilter,
dispatch,
eitherCellSetOneOrTwo
eitherCellSetOneOrTwo,
} = this.props;
// Reducer and components assume that value will be null if
@@ -25,7 +25,7 @@ class CellSetButton extends React.PureComponent {
/* diffexp needs to be cleared before we store a new set */
dispatch({
type: `store current cell selection as differential set ${eitherCellSetOneOrTwo}`,
data: set
data: set,
});
}
}

View File

@@ -6,7 +6,7 @@ import {
Popover,
NumericInput,
Icon,
Tooltip
Tooltip,
} from "@blueprintjs/core";
import { tooltipHoverOpenDelay } from "../../globals";
@@ -21,7 +21,7 @@ function Clip(props) {
isClipDisabled,
handleClipOnKeyPress,
handleClipPercentileMaxValueChange,
handleClipPercentileMinValueChange
handleClipPercentileMinValueChange,
} = props;
const clipMin =
@@ -37,7 +37,7 @@ function Clip(props) {
<div
className="bp3-button-group"
style={{
marginRight: 10
marginRight: 10,
}}
>
<Popover
@@ -52,7 +52,7 @@ function Clip(props) {
data-testid="visualization-settings"
className={`bp3-button bp3-icon-timeline-bar-chart ${activeClipClass}`}
style={{
cursor: "pointer"
cursor: "pointer",
}}
/>
</Tooltip>
@@ -67,7 +67,7 @@ function Clip(props) {
justifyContent: "flex-start",
alignItems: "flex-start",
flexDirection: "column",
padding: 10
padding: 10,
}}
>
<div>Clip all continuous values to percentile range</div>
@@ -77,7 +77,7 @@ function Clip(props) {
justifyContent: "space-between",
alignItems: "center",
paddingTop: 5,
paddingBottom: 5
paddingBottom: 5,
}}
>
<NumericInput
@@ -121,7 +121,7 @@ function Clip(props) {
style={{
cursor: "pointer",
marginRight: 5,
marginLeft: 5
marginLeft: 5,
}}
onClick={handleClipCommit}
>

View File

@@ -7,26 +7,26 @@ import {
ButtonGroup,
AnchorButton,
Tooltip,
Position
Position,
} from "@blueprintjs/core";
import * as globals from "../../globals";
import actions from "../../actions";
import CellSetButton from "./cellSetButtons";
@connect(state => ({
@connect((state) => ({
config: state.config,
crossfilter: state.crossfilter,
differential: state.differential,
celllist1: state.differential?.celllist1,
celllist2: state.differential?.celllist2,
diffexpMayBeSlow: state.config?.parameters?.["diffexp-may-be-slow"] ?? false,
diffexpCellcountMax: state.config?.limits?.diffexp_cellcount_max
diffexpCellcountMax: state.config?.limits?.diffexp_cellcount_max,
}))
class DiffexpButtons extends React.Component {
constructor(props) {
super(props);
this.state = {
userDismissedPopover: false
userDismissedPopover: false,
};
}
@@ -46,16 +46,16 @@ class DiffexpButtons extends React.Component {
const { dispatch, differential } = this.props;
dispatch({
type: "clear differential expression",
diffExp: differential.diffExp
diffExp: differential.diffExp,
});
dispatch({
type: "clear scatterplot"
type: "clear scatterplot",
});
};
handlePopoverDismiss = () => {
this.setState({
userDismissedPopover: true
userDismissedPopover: true,
});
};
@@ -124,7 +124,7 @@ class DiffexpButtons extends React.Component {
alignItems: "flex-end",
flexDirection: "column",
padding: 10,
maxWidth: 310
maxWidth: 310,
}}
>
<p>

View File

@@ -7,26 +7,26 @@ import {
Radio,
RadioGroup,
Tooltip,
Position
Position,
} from "@blueprintjs/core";
import { connect } from "react-redux";
import * as globals from "../../globals";
import { World } from "../../util/stateManager";
import actions from "../../actions";
@connect(state => ({
@connect((state) => ({
universe: state.universe,
world: state.world,
layoutChoice: state.layoutChoice,
reembedController: state.reembedController,
enableReembedding: state.config?.parameters?.["enable-reembedding"] ?? false
enableReembedding: state.config?.parameters?.["enable-reembedding"] ?? false,
}))
class Embedding extends React.PureComponent {
handleLayoutChoiceChange = e => {
handleLayoutChoiceChange = (e) => {
const { dispatch } = this.props;
dispatch({
type: "set layout choice",
layoutChoice: e.currentTarget.value
layoutChoice: e.currentTarget.value,
});
};
@@ -36,7 +36,7 @@ class Embedding extends React.PureComponent {
world,
universe,
dispatch,
reembedController
reembedController,
} = this.props;
if (!enableReembedding) return null;
@@ -70,7 +70,7 @@ class Embedding extends React.PureComponent {
return (
<ButtonGroup
style={{
marginRight: 10
marginRight: 10,
}}
>
<Popover
@@ -85,7 +85,7 @@ class Embedding extends React.PureComponent {
data-testid="layout-choice"
icon="heatmap"
style={{
cursor: "pointer"
cursor: "pointer",
}}
/>
</Tooltip>
@@ -98,7 +98,7 @@ class Embedding extends React.PureComponent {
justifyContent: "flex-start",
alignItems: "flex-start",
flexDirection: "column",
padding: 10
padding: 10,
}}
>
<RadioGroup
@@ -106,7 +106,7 @@ class Embedding extends React.PureComponent {
onChange={this.handleLayoutChoiceChange}
selectedValue={layoutChoice.current}
>
{layoutChoice.available.map(name => (
{layoutChoice.available.map((name) => (
<Radio label={name} value={name} key={name} />
))}
</RadioGroup>

View File

@@ -11,7 +11,7 @@ import Subset from "./subset";
import UndoRedoReset from "./undoRedo";
import DiffexpButtons from "./diffexpButtons";
@connect(state => ({
@connect((state) => ({
universe: state.universe,
world: state.world,
crossfilter: state.crossfilter,
@@ -34,7 +34,7 @@ import DiffexpButtons from "./diffexpButtons";
diffexpMayBeSlow: state.config?.parameters?.["diffexp-may-be-slow"] ?? false,
showCentroidLabels: state.centroidLabels.showLabels,
tosURL: state.config?.parameters?.about_legal_tos,
privacyURL: state.config?.parameters?.about_legal_privacy
privacyURL: state.config?.parameters?.about_legal_privacy,
}))
class MenuBar extends React.Component {
static isValidDigitKeyEvent(e) {
@@ -64,7 +64,7 @@ class MenuBar extends React.Component {
constructor(props) {
super(props);
this.state = {
pendingClipPercentiles: null
pendingClipPercentiles: null,
};
}
@@ -90,7 +90,7 @@ class MenuBar extends React.Component {
return isDisabled;
};
handleClipOnKeyPress = e => {
handleClipOnKeyPress = (e) => {
/*
allow only numbers, plus other critical keys which
may be required to make a number
@@ -100,7 +100,7 @@ class MenuBar extends React.Component {
}
};
handleClipPercentileMinValueChange = v => {
handleClipPercentileMinValueChange = (v) => {
/*
Ignore anything that isn't a legit number
*/
@@ -116,11 +116,11 @@ class MenuBar extends React.Component {
if (v > 100) v = 100;
const clipPercentileMin = Math.round(v); // paranoia
this.setState({
pendingClipPercentiles: { clipPercentileMin, clipPercentileMax }
pendingClipPercentiles: { clipPercentileMin, clipPercentileMax },
});
};
handleClipPercentileMaxValueChange = v => {
handleClipPercentileMaxValueChange = (v) => {
/*
Ignore anything that isn't a legit number
*/
@@ -137,7 +137,7 @@ class MenuBar extends React.Component {
const clipPercentileMax = Math.round(v); // paranoia
this.setState({
pendingClipPercentiles: { clipPercentileMin, clipPercentileMax }
pendingClipPercentiles: { clipPercentileMin, clipPercentileMax },
});
};
@@ -149,14 +149,14 @@ class MenuBar extends React.Component {
const max = clipPercentileMax / 100;
dispatch({
type: "set clip quantiles",
clipQuantiles: { min, max }
clipQuantiles: { min, max },
});
};
handleClipOpening = () => {
const { clipPercentileMin, clipPercentileMax } = this.props;
this.setState({
pendingClipPercentiles: { clipPercentileMin, clipPercentileMax }
pendingClipPercentiles: { clipPercentileMin, clipPercentileMax },
});
};
@@ -169,7 +169,7 @@ class MenuBar extends React.Component {
dispatch({
type: "show centroid labels for category",
showLabels: !showCentroidLabels
showLabels: !showCentroidLabels,
});
};
@@ -200,7 +200,7 @@ class MenuBar extends React.Component {
aboutLink,
showCentroidLabels,
privacyURL,
tosURL
tosURL,
} = this.props;
const { pendingClipPercentiles } = this.state;
@@ -221,10 +221,10 @@ class MenuBar extends React.Component {
position: "fixed",
right: globals.leftSidebarWidth + 8,
top: 8,
display: "flex"
display: "flex",
}}
>
{disableDiffexp ? null : <DiffexpButtons/>}
{disableDiffexp ? null : <DiffexpButtons />}
<Subset
subsetPossible={this.subsetPossible()}
subsetResetPossible={this.subsetResetPossible()}
@@ -251,11 +251,11 @@ class MenuBar extends React.Component {
onClick={() => {
dispatch({
type: "change graph interaction mode",
data: "select"
data: "select",
});
}}
style={{
cursor: "pointer"
cursor: "pointer",
}}
/>
</Tooltip>
@@ -272,11 +272,11 @@ class MenuBar extends React.Component {
onClick={() => {
dispatch({
type: "change graph interaction mode",
data: "zoom"
data: "zoom",
});
}}
style={{
cursor: "pointer"
cursor: "pointer",
}}
/>
</Tooltip>
@@ -294,7 +294,7 @@ class MenuBar extends React.Component {
active={showCentroidLabels}
intent={showCentroidLabels ? "primary" : "none"}
style={{
marginRight: 10
marginRight: 10,
}}
/>
</Tooltip>

View File

@@ -47,16 +47,16 @@ function InformationMenu(props) {
}`}
/>
<MenuItem text="MIT License" />
{tosURL ? <MenuItem
href={tosURL}
target="_blank"
text="Terms of Service"
/> : null }
{privacyURL ? <MenuItem
href={privacyURL}
target="_blank"
text="Privacy Policy"
/> : null }
{tosURL ? (
<MenuItem href={tosURL} target="_blank" text="Terms of Service" />
) : null}
{privacyURL ? (
<MenuItem
href={privacyURL}
target="_blank"
text="Privacy Policy"
/>
) : null}
</Menu>
}
position={Position.BOTTOM_RIGHT}
@@ -65,7 +65,7 @@ function InformationMenu(props) {
type="button"
className="bp3-button bp3-icon-info-sign"
style={{
cursor: "pointer"
cursor: "pointer",
}}
/>
</Popover>

View File

@@ -7,7 +7,7 @@ function Subset(props) {
subsetPossible,
subsetResetPossible,
handleSubset,
handleSubsetReset
handleSubsetReset,
} = props;
return (

View File

@@ -4,11 +4,7 @@ import { AnchorButton, Tooltip } from "@blueprintjs/core";
import { tooltipHoverOpenDelay } from "../../globals";
function InformationMenu(props) {
const {
undoDisabled,
redoDisabled,
dispatch
} = props;
const { undoDisabled, redoDisabled, dispatch } = props;
return (
<div style={{ marginRight: 10 }} className="bp3-button-group">
<Tooltip
@@ -24,7 +20,7 @@ function InformationMenu(props) {
dispatch({ type: "@@undoable/undo" });
}}
style={{
cursor: "pointer"
cursor: "pointer",
}}
data-testid="undo"
/>
@@ -42,7 +38,7 @@ function InformationMenu(props) {
dispatch({ type: "@@undoable/redo" });
}}
style={{
cursor: "pointer"
cursor: "pointer",
}}
data-testid="redo"
/>

View File

@@ -5,10 +5,10 @@ import Continuous from "../continuous/continuous";
import GeneExpression from "../geneExpression";
import * as globals from "../../globals";
@connect(state => ({
@connect((state) => ({
responsive: state.responsive,
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
scatterplotYYaccessor: state.controls.scatterplotYYaccessor
scatterplotYYaccessor: state.controls.scatterplotYYaccessor,
}))
class RightSidebar extends React.Component {
render() {
@@ -21,7 +21,7 @@ class RightSidebar extends React.Component {
right: 0,
backgroundColor: "white",
/* x y blur spread color */
borderLeft: `1px solid ${globals.lightGrey}`
borderLeft: `1px solid ${globals.lightGrey}`,
}}
>
<div
@@ -29,7 +29,7 @@ class RightSidebar extends React.Component {
height: responsive.height,
width: globals.leftSidebarWidth,
overflowY: "auto",
overflowX: "hidden"
overflowX: "hidden",
}}
>
<GeneExpression />

View File

@@ -1,6 +1,6 @@
import { glPointFlags, glPointSize } from "../../util/glHelpers";
export default function(regl) {
export default function (regl) {
return regl({
vert: `
precision mediump float;
@@ -52,17 +52,17 @@ export default function(regl) {
attributes: {
position: regl.prop("position"),
color: regl.prop("color"),
flag: regl.prop("flag")
flag: regl.prop("flag"),
},
uniforms: {
projection: regl.prop("projection"),
nPoints: regl.prop("nPoints"),
minViewportDimension: regl.prop("minViewportDimension")
minViewportDimension: regl.prop("minViewportDimension"),
},
count: regl.prop("count"),
primitive: "points"
primitive: "points",
});
}

View File

@@ -22,7 +22,7 @@ function createProjectionTF(viewportWidth, viewportHeight) {
return mat3.projection(m, viewportWidth, viewportHeight);
}
@connect(state => {
@connect((state) => {
const { world, crossfilter, universe } = state;
const { scatterplotXXaccessor, scatterplotYYaccessor } = state.controls;
const expressionX = scatterplotXXaccessor
@@ -54,7 +54,7 @@ function createProjectionTF(viewportWidth, viewportHeight) {
crossfilter,
responsive: state.responsive
responsive: state.responsive,
};
})
class Scatterplot extends React.PureComponent {
@@ -67,7 +67,7 @@ class Scatterplot extends React.PureComponent {
return positions;
});
computePointColors = memoize(rgb => {
computePointColors = memoize((rgb) => {
/*
compute webgl colors for each point
*/
@@ -135,11 +135,11 @@ class Scatterplot extends React.PureComponent {
colors: null,
flags: null,
xScale: null,
yScale: null
yScale: null,
};
this.state = {
svg: null,
minimized: null
minimized: null,
};
}
@@ -184,7 +184,7 @@ class Scatterplot extends React.PureComponent {
colorBuffer,
svg,
drawPoints,
projectionTF
projectionTF,
});
}
@@ -198,7 +198,7 @@ class Scatterplot extends React.PureComponent {
expressionY,
colorRGB,
colorAccessor,
pointDilation
pointDilation,
} = this.props;
const {
regl,
@@ -207,7 +207,7 @@ class Scatterplot extends React.PureComponent {
flagBuffer,
svg,
drawPoints,
projectionTF
projectionTF,
} = this.state;
if (
@@ -284,7 +284,7 @@ class Scatterplot extends React.PureComponent {
return {
xScale,
yScale
yScale,
};
}
@@ -294,15 +294,9 @@ class Scatterplot extends React.PureComponent {
// the axes are much cleaner and easier now. No need to rotate and orient
// the axis, just call axisBottom, axisLeft etc.
const xAxis = d3
.axisBottom()
.ticks(7)
.scale(xScale);
const xAxis = d3.axisBottom().ticks(7).scale(xScale);
const yAxis = d3
.axisLeft()
.ticks(7)
.scale(yScale);
const yAxis = d3.axisLeft().ticks(7).scale(yScale);
// adding axes is also simpler now, just translate x-axis to (0,height)
// and it's alread defined to be a bottom axis.
@@ -356,7 +350,7 @@ class Scatterplot extends React.PureComponent {
regl.poll();
regl.clear({
depth: 1,
color: [1, 1, 1, 1]
color: [1, 1, 1, 1],
});
drawPoints({
flag: flagBuffer,
@@ -368,7 +362,7 @@ class Scatterplot extends React.PureComponent {
minViewportDimension: Math.min(
cvWidth - globals.leftSidebarWidth || width,
cvHeight || height
)
),
});
regl._gl.flush();
}
@@ -387,7 +381,7 @@ class Scatterplot extends React.PureComponent {
padding: "0px 20px 20px 0px",
backgroundColor: "white",
/* x y blur spread color */
boxShadow: "0px 0px 6px 2px rgba(153,153,153,0.4)"
boxShadow: "0px 0px 6px 2px rgba(153,153,153,0.4)",
}}
id="scatterplot_wrapper"
>
@@ -395,7 +389,7 @@ class Scatterplot extends React.PureComponent {
style={{
position: "absolute",
right: 5,
top: 5
top: 5,
}}
>
<Button
@@ -413,7 +407,7 @@ class Scatterplot extends React.PureComponent {
data-testid="clear-scatterplot"
onClick={() =>
dispatch({
type: "clear scatterplot"
type: "clear scatterplot",
})
}
>
@@ -425,7 +419,7 @@ class Scatterplot extends React.PureComponent {
id="scatterplot"
style={{
width: `${width + margin.left + margin.right}px`,
height: `${height + margin.top + margin.bottom}px`
height: `${height + margin.top + margin.bottom}px`,
}}
>
<canvas
@@ -434,9 +428,9 @@ class Scatterplot extends React.PureComponent {
data-testid="scatterplot"
style={{
marginLeft: margin.left,
marginTop: margin.top
marginTop: margin.top,
}}
ref={canvas => {
ref={(canvas) => {
this.reglCanvas = canvas;
}}
/>

View File

@@ -19,7 +19,7 @@ const setupScatterplot = (width, height, margin) => {
.attr("transform", `translate(${margin.left},${margin.top})`);
return {
svg
svg,
};
};

View File

@@ -1,5 +1,5 @@
# Third-party fonts used in cellxgene
| Font Family | License at time of download | URL |
|-------------|-----------------------------|-----|
| Roboto Conensed | Apache v2.0 | https://fonts.google.com/specimen/Roboto+Condensed |
| Font Family | License at time of download | URL |
| --------------- | --------------------------- | -------------------------------------------------- |
| Roboto Conensed | Apache v2.0 | https://fonts.google.com/specimen/Roboto+Condensed |

View File

@@ -9,22 +9,22 @@ namespace mangling on these definitions. See webpack config for specifics.
@import "~@blueprintjs/select/lib/css/blueprint-select.css";
@font-face {
font-family: 'Roboto Condensed';
font-style: italic;
src: url('./fonts/RobotoCondensed-Italic.ttf') format('truetype');
font-weight: 400;
font-family: "Roboto Condensed";
font-style: italic;
src: url("./fonts/RobotoCondensed-Italic.ttf") format("truetype");
font-weight: 400;
}
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
src: url('./fonts/RobotoCondensed-Regular.ttf') format('truetype');
font-weight: 400;
font-family: "Roboto Condensed";
font-style: normal;
src: url("./fonts/RobotoCondensed-Regular.ttf") format("truetype");
font-weight: 400;
}
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
src: url('./fonts/RobotoCondensed-Bold.ttf') format('truetype');
font-weight: 700;
font-family: "Roboto Condensed";
font-style: normal;
src: url("./fonts/RobotoCondensed-Bold.ttf") format("truetype");
font-weight: 700;
}

View File

@@ -25,7 +25,7 @@ const Annotations = (
isEditingLabelName: false,
categoryBeingEdited: null,
categoryAddingNewLabel: null,
labelEditable: { category: null, label: null }
labelEditable: { category: null, label: null },
},
action
) => {
@@ -40,7 +40,7 @@ const Annotations = (
return {
...state,
dataCollectionNameIsReadOnly,
dataCollectionName
dataCollectionName,
};
}
@@ -50,7 +50,7 @@ const Annotations = (
}
return {
...state,
dataCollectionName: action.data
dataCollectionName: action.data,
};
}
@@ -59,7 +59,7 @@ const Annotations = (
return {
...state,
isAddingNewLabel: true,
categoryAddingNewLabel: action.data
categoryAddingNewLabel: action.data,
};
}
@@ -67,7 +67,7 @@ const Annotations = (
return {
...state,
isAddingNewLabel: false,
categoryAddingNewLabel: null
categoryAddingNewLabel: null,
};
}
@@ -75,7 +75,7 @@ const Annotations = (
return {
...state,
isEditingCategoryName: true,
categoryBeingEdited: action.data
categoryBeingEdited: action.data,
};
}
@@ -83,7 +83,7 @@ const Annotations = (
return {
...state,
isEditingCategoryName: false,
categoryBeingEdited: null
categoryBeingEdited: null,
};
}
@@ -94,8 +94,8 @@ const Annotations = (
isEditingLabelName: true,
labelEditable: {
category: action.metadataField,
label: action.categoryIndex
}
label: action.categoryIndex,
},
};
}
@@ -103,7 +103,7 @@ const Annotations = (
return {
...state,
isEditingLabelName: false,
labelEditable: { category: null, label: null }
labelEditable: { category: null, label: null },
};
}

View File

@@ -1,55 +1,55 @@
const Autosave = (
state = {
saveInProgress: false,
error: false,
lastSavedObsAnnotations: null,
initialDataLoadComplete: false
},
action,
nextSharedState
state = {
saveInProgress: false,
error: false,
lastSavedObsAnnotations: null,
initialDataLoadComplete: false,
},
action,
nextSharedState
) => {
switch (action.type) {
case "initial data load complete (universe exists)": {
/* don't save on init */
const { universe } = nextSharedState;
return {
...state,
error: false,
saveInProgress: false,
lastSavedObsAnnotations: universe.obsAnnotations,
initialDataLoadComplete: true,
};
}
switch (action.type) {
case "initial data load complete (universe exists)": {
/* don't save on init */
const { universe } = nextSharedState;
return {
...state,
error: false,
saveInProgress: false,
lastSavedObsAnnotations: universe.obsAnnotations,
initialDataLoadComplete: true,
};
}
case "writable obs annotations - save started": {
return {
...state,
saveInProgress: true
};
}
case "writable obs annotations - save started": {
return {
...state,
saveInProgress: true,
};
}
case "writable obs annotations - save error": {
const { message } = action;
return {
...state,
error: message,
saveInProgress: false
};
}
case "writable obs annotations - save error": {
const { message } = action;
return {
...state,
error: message,
saveInProgress: false,
};
}
case "writable obs annotations - save complete": {
const lastSavedObsAnnotations = action.obsAnnotations;
return {
...state,
saveInProgress: false,
error: false,
lastSavedObsAnnotations
};
}
case "writable obs annotations - save complete": {
const lastSavedObsAnnotations = action.obsAnnotations;
return {
...state,
saveInProgress: false,
error: false,
lastSavedObsAnnotations,
};
}
default:
return { ...state };
}
default:
return { ...state };
}
};
export default Autosave;

View File

@@ -36,7 +36,7 @@ const CategoricalSelection = (
if (names.length === 0) return state;
return {
...state,
...CH.createCategoricalSelection(world, names)
...CH.createCategoricalSelection(world, names),
};
}
@@ -52,8 +52,8 @@ const CategoricalSelection = (
...state,
[action.metadataField]: {
...state[action.metadataField],
categoryValueSelected: newCategoryValueSelected
}
categoryValueSelected: newCategoryValueSelected,
},
};
return newCategoricalSelection;
}
@@ -70,8 +70,8 @@ const CategoricalSelection = (
...state,
[action.metadataField]: {
...state[action.metadataField],
categoryValueSelected: newCategoryValueSelected
}
categoryValueSelected: newCategoryValueSelected,
},
};
return newCategoricalSelection;
}
@@ -87,8 +87,8 @@ const CategoricalSelection = (
categorySelected: false,
categoryValueSelected: Array.from(
state[action.metadataField].categoryValueSelected
).fill(false)
}
).fill(false),
},
};
return newCategoricalSelection;
}
@@ -104,8 +104,8 @@ const CategoricalSelection = (
categorySelected: true,
categoryValueSelected: Array.from(
state[action.metadataField].categoryValueSelected
).fill(true)
}
).fill(true),
},
};
return newCategoricalSelection;
}
@@ -115,7 +115,7 @@ const CategoricalSelection = (
const name = action.data;
return {
...state,
...CH.createCategoricalSelection(world, [name])
...CH.createCategoricalSelection(world, [name]),
};
}
@@ -143,7 +143,7 @@ const CategoricalSelection = (
const { [name]: _, ...partialState } = state;
return {
...partialState,
...CH.createCategoricalSelection(world, [name])
...CH.createCategoricalSelection(world, [name]),
};
}

View File

@@ -2,7 +2,7 @@ import calcCentroid from "../util/centroid";
const initialState = {
labels: [],
showLabels: false
showLabels: false,
};
const centroidLabels = (state = initialState, action, sharedNextState) => {
@@ -10,7 +10,7 @@ const centroidLabels = (state = initialState, action, sharedNextState) => {
world,
layoutChoice,
categoricalSelection,
colors: { colorAccessor }
colors: { colorAccessor },
} = sharedNextState;
const showLabels = action.showLabels ?? state.showLabels;
@@ -34,7 +34,7 @@ const centroidLabels = (state = initialState, action, sharedNextState) => {
categoricalSelection,
world.schema.annotations.obsByName
)
: []
: [],
};
case "color by categorical metadata":
@@ -45,7 +45,7 @@ const centroidLabels = (state = initialState, action, sharedNextState) => {
return {
...state,
labels: [],
showLabels
showLabels,
};
}
@@ -59,7 +59,7 @@ const centroidLabels = (state = initialState, action, sharedNextState) => {
categoricalSelection,
world.schema.annotations.obsByName
),
showLabels
showLabels,
};
case "color by continuous metadata":

View File

@@ -5,7 +5,7 @@ const ColorsReducer = (
colorMode: null,
colorAccessor: null,
rgb: null,
scale: null
scale: null,
},
action,
nextSharedState,
@@ -23,7 +23,7 @@ const ColorsReducer = (
colorAccessor,
colorMode,
rgb,
scale
scale,
};
}
@@ -31,7 +31,7 @@ const ColorsReducer = (
const { userColors } = action;
return {
...state,
userColors
userColors,
};
}
@@ -47,7 +47,7 @@ const ColorsReducer = (
return {
...state,
rgb,
scale
scale,
};
}
@@ -73,7 +73,7 @@ const ColorsReducer = (
return {
...state,
rgb,
scale
scale,
};
}
@@ -85,14 +85,14 @@ const ColorsReducer = (
/* else reset */
return {
...state,
...ColorHelpers.resetColors(prevSharedState.world)
...ColorHelpers.resetColors(prevSharedState.world),
};
}
case "reset colorscale": {
return {
...state,
...ColorHelpers.resetColors(prevSharedState.world)
...ColorHelpers.resetColors(prevSharedState.world),
};
}
@@ -107,13 +107,18 @@ const ColorsReducer = (
const colorMode = !resetCurrent ? action.type : null;
const colorAccessor = !resetCurrent ? action.colorAccessor : null;
const { rgb, scale } = ColorHelpers.createColors(world, colorMode, colorAccessor, colors.userColors);
const { rgb, scale } = ColorHelpers.createColors(
world,
colorMode,
colorAccessor,
colors.userColors
);
return {
...state,
colorMode,
colorAccessor,
rgb,
scale
scale,
};
}
@@ -136,7 +141,7 @@ const ColorsReducer = (
colorMode,
colorAccessor,
rgb,
scale
scale,
};
}
@@ -153,7 +158,11 @@ const ColorsReducer = (
return state;
/* else, we need to rebuild colors as labels have changed! */
const { rgb, scale } = ColorHelpers.createColors(world, colorMode, colorAccessor);
const { rgb, scale } = ColorHelpers.createColors(
world,
colorMode,
colorAccessor
);
return { ...state, rgb, scale };
}

View File

@@ -3,7 +3,7 @@ const Config = (
state = {
displayNames: null,
features: null,
parameters: null
parameters: null,
},
action
) => {
@@ -12,19 +12,19 @@ const Config = (
return {
...state,
loading: true,
error: null
error: null,
};
case "configuration load complete":
return {
...state,
loading: false,
error: null,
...action.config
...action.config,
};
case "initial data load error":
return {
...state,
error: action.error
error: action.error,
};
default:
return state;

View File

@@ -6,7 +6,7 @@ const Differential = (
loading: null,
error: null,
celllist1: null,
celllist2: null
celllist2: null,
},
action
) => {
@@ -15,37 +15,37 @@ const Differential = (
return {
...state,
loading: true,
error: null
error: null,
};
case "request differential expression success":
return {
...state,
error: null,
loading: false,
diffExp: action.data
diffExp: action.data,
};
case "request differential expression error":
return {
...state,
loading: false,
error: action.data
error: action.data,
};
case "store current cell selection as differential set 1":
return {
...state,
celllist1: action.data
celllist1: action.data,
};
case "store current cell selection as differential set 2":
return {
...state,
celllist2: action.data
celllist2: action.data,
};
case "clear differential expression":
return {
...state,
diffExp: null,
celllist1: null,
celllist2: null
celllist2: null,
};
case "reset World to eq Universe":
case "set World to current selection":
@@ -53,7 +53,7 @@ const Differential = (
...state,
diffExp: null,
celllist1: null,
celllist2: null
celllist2: null,
};
default:
return state;

View File

@@ -1,7 +1,7 @@
const GraphSelection = (
state = {
tool: "lasso", // what selection tool mode (lasso, brush, ...)
selection: { mode: "all" } // current selection, which is tool specific
selection: { mode: "all" }, // current selection, which is tool specific
},
action
) => {
@@ -12,8 +12,8 @@ const GraphSelection = (
return {
...state,
selection: {
mode: "all"
}
mode: "all",
},
};
}
@@ -24,8 +24,8 @@ const GraphSelection = (
...state,
selection: {
mode: "within-rect",
brushCoords
}
brushCoords,
},
};
}
@@ -35,8 +35,8 @@ const GraphSelection = (
...state,
selection: {
mode: "within-polygon",
polygon
}
polygon,
},
};
}
@@ -47,8 +47,8 @@ const GraphSelection = (
return {
...state,
selection: {
mode: "all"
}
mode: "all",
},
};
}

View File

@@ -9,14 +9,14 @@ about commonly used names. Preferentially, pick in the following order:
*/
function bestDefaultLayout(layouts) {
const preferredNames = ["umap", "tsne", "pca"];
const idx = preferredNames.findIndex(name => layouts.indexOf(name) !== -1);
const idx = preferredNames.findIndex((name) => layouts.indexOf(name) !== -1);
if (idx !== -1) return preferredNames[idx];
return layouts[0];
}
function setToDefaultLayout(world) {
const { schema } = world;
const available = schema.layout.obs.map(v => v.name).sort();
const available = schema.layout.obs.map((v) => v.name).sort();
const current = bestDefaultLayout(available);
const currentDimNames = schema.layout.obsByName[current].dims;
return { available, current, currentDimNames };
@@ -26,7 +26,7 @@ const LayoutChoice = (
state = {
available: [], // all available choices
current: undefined, // name of the current layout, eg, 'umap'
currentDimNames: [] // dimension name
currentDimNames: [], // dimension name
},
action,
nextSharedState
@@ -37,7 +37,7 @@ const LayoutChoice = (
const { universe } = nextSharedState;
return {
...state,
...setToDefaultLayout(universe)
...setToDefaultLayout(universe),
};
}
@@ -53,7 +53,7 @@ const LayoutChoice = (
const available = Array.from(new Set(state.available).add(name));
return {
...state,
available
available,
};
}
@@ -64,12 +64,12 @@ const LayoutChoice = (
if (dflt.available.includes(current)) {
return {
...state,
available: dflt.available
available: dflt.available,
};
}
return {
...state,
...dflt
...dflt,
};
}

View File

@@ -3,7 +3,7 @@ const Ontology = (
enabled: false, // are ontology terms enabled?
terms: null, // an array of term names, eg, ['cell', 'lung cell', ...]
termSet: null, // a Set object containing all terms, for fast lookup
loading: true
loading: true,
},
action
) => {
@@ -20,7 +20,7 @@ const Ontology = (
loading: false,
enabled,
terms,
termSet
termSet,
};
}
default: {

View File

@@ -1,6 +1,6 @@
const initialState = {
metadataField: "",
categoryField: ""
categoryField: "",
};
const pointDialation = (state = initialState, action, sharedNextState) => {
@@ -15,7 +15,7 @@ const pointDialation = (state = initialState, action, sharedNextState) => {
return {
...state,
metadataField,
categoryField
categoryField,
};
case "category value mouse hover end":

View File

@@ -3,7 +3,7 @@ controller state is not part of the undo/redo history
*/
export const reembedController = (
state = {
pendingFetch: null
pendingFetch: null,
},
action
) => {
@@ -11,7 +11,7 @@ export const reembedController = (
case "reembed: request start": {
return {
...state,
pendingFetch: action.abortableFetch
pendingFetch: action.abortableFetch,
};
}
case "reembed: request aborted":
@@ -19,7 +19,7 @@ export const reembedController = (
case "reembed: request completed": {
return {
...state,
pendingFetch: null
pendingFetch: null,
};
}
default: {
@@ -33,7 +33,7 @@ actual reembedding data is part of the undo/redo history
*/
export const reembedding = (
state = {
reembeddings: new Map()
reembeddings: new Map(),
},
action
) => {
@@ -47,14 +47,14 @@ export const reembedding = (
reembeddings: new Map(reembeddings).set(name, {
name,
schema,
embedding
})
embedding,
}),
};
}
case "reembed: clear all reembeddings": {
return {
...state,
reembeddings: new Map()
reembeddings: new Map(),
};
}
default: {

View File

@@ -8,7 +8,7 @@ which improves Reset UI performance.
*/
const ResetCacheReducer = (
state = {
crossfilter: null
crossfilter: null,
},
action,
nextSharedState
@@ -19,7 +19,7 @@ const ResetCacheReducer = (
const { crossfilter } = nextSharedState;
return {
...state,
crossfilter
crossfilter,
};
}
default: {

View File

@@ -2,7 +2,7 @@
const Responsive = (
state = {
width: null,
height: null
height: null,
},
action
) => {
@@ -11,7 +11,7 @@ const Responsive = (
return {
...state,
width: action.data.width,
height: action.data.height
height: action.data.height,
};
default:
return state;

View File

@@ -80,7 +80,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
const past = currentState[pastKey];
const future = currentState[futureKey];
if (past.length === 0) return currentState;
const currentUndoableState = Object.entries(currentState).filter(kv =>
const currentUndoableState = Object.entries(currentState).filter((kv) =>
undoableKeysSet.has(kv[0])
);
const newPast = [...past];
@@ -91,7 +91,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
...fromEntries(newState),
[pastKey]: newPast,
[futureKey]: newFuture,
[pendingKey]: null
[pendingKey]: null,
};
return nextState;
}
@@ -103,7 +103,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
const past = currentState[pastKey] || [];
const future = currentState[futureKey] || [];
if (future.length === 0) return currentState;
const currentUndoableState = Object.entries(currentState).filter(kv =>
const currentUndoableState = Object.entries(currentState).filter((kv) =>
undoableKeysSet.has(kv[0])
);
const newFuture = [...future];
@@ -114,7 +114,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
...fromEntries(newState),
[pastKey]: newPast,
[futureKey]: newFuture,
[pendingKey]: null
[pendingKey]: null,
};
return nextState;
}
@@ -128,7 +128,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
[pastKey]: [],
[futureKey]: [],
[filterStateKey]: {},
[pendingKey]: null
[pendingKey]: null,
};
}
@@ -145,7 +145,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
[pastKey]: past,
[futureKey]: future,
[filterStateKey]: filterState,
[pendingKey]: pending
[pendingKey]: pending,
};
}
@@ -154,7 +154,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
*/
function save(currentState, action, filterState) {
const past = currentState[pastKey] || [];
const currentUndoableState = Object.entries(currentState).filter(kv =>
const currentUndoableState = Object.entries(currentState).filter((kv) =>
undoableKeysSet.has(kv[0])
);
const res = reducer(currentState, action);
@@ -164,7 +164,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
[pastKey]: newPast,
[futureKey]: [],
[filterStateKey]: filterState,
[pendingKey]: null
[pendingKey]: null,
};
return nextState;
}
@@ -173,12 +173,12 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
Save current state as pending history change. No other side effects.
*/
function stashPending(currentState) {
const currentUndoableState = Object.entries(currentState).filter(kv =>
const currentUndoableState = Object.entries(currentState).filter((kv) =>
undoableKeysSet.has(kv[0])
);
return {
...currentState,
[pendingKey]: currentUndoableState
[pendingKey]: currentUndoableState,
};
}
@@ -188,7 +188,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
function cancelPending(currentState) {
return {
...currentState,
[pendingKey]: null
[pendingKey]: null,
};
}
@@ -203,7 +203,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
...currentState,
[pastKey]: newPast,
[futureKey]: [],
[pendingKey]: null
[pendingKey]: null,
};
return nextState;
}
@@ -213,7 +213,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
[pastKey]: [],
[futureKey]: [],
[filterStateKey]: {},
[pendingKey]: null
[pendingKey]: null,
},
action
) => {
@@ -241,7 +241,7 @@ const Undoable = (reducer, undoableKeys, options = {}) => {
);
const {
[filterActionKey]: filterAction,
[filterStateKey]: filterStateUpdate
[filterStateKey]: filterStateUpdate,
} = actionFilterResp;
const nextFilterState = { ...currentFilterState, ...filterStateUpdate };

View File

@@ -49,7 +49,7 @@ const skipOnActions = new Set([
"annotation: disable category edit mode",
"annotation: activate edit label mode",
"annotation: cancel edit label mode",
"set annotations collection name"
"set annotations collection name",
]);
/*
@@ -101,7 +101,7 @@ const saveOnActions = new Set([
"annotation: label edited",
"annotation: label current cell selection",
"annotation: delete label",
"annotation: category edited"
"annotation: category edited",
]);
/**
@@ -115,26 +115,26 @@ See graph definition for the transitions that use each.
Signature: (fsm, transition, reducerState, reducerAction) => undoableAction
*/
const stashPending = fsm => ({
const stashPending = (fsm) => ({
[actionKey]: "stashPending",
[stateKey]: { fsm }
[stateKey]: { fsm },
});
const cancelPending = () => ({
[actionKey]: "cancelPending",
[stateKey]: { fsm: null }
[stateKey]: { fsm: null },
});
const applyPending = () => ({
[actionKey]: "applyPending",
[stateKey]: { fsm: null }
[stateKey]: { fsm: null },
});
const skip = (fsm, transition) => ({
[actionKey]: "skip",
[stateKey]: { fsm: transition.to !== "done" ? fsm : null }
[stateKey]: { fsm: transition.to !== "done" ? fsm : null },
});
const clear = () => ({ [actionKey]: "clear", [stateKey]: { fsm: null } });
const save = (fsm, transition) => ({
[actionKey]: "save",
[stateKey]: { fsm: transition.to !== "done" ? fsm : null }
[stateKey]: { fsm: transition.to !== "done" ? fsm : null },
});
/*
@@ -171,11 +171,11 @@ Basic approach:
* only implement complex state machines where absolutely required (eg,
multi-event seleciton and the like)
*/
const actionFilter = debug => (state, action, prevFilterState) => {
const actionFilter = (debug) => (state, action, prevFilterState) => {
const actionType = action.type;
const filterState = {
...prevFilterState,
prevAction: action
prevAction: action,
};
if (skipOnActions.has(actionType)) {
return { [actionKey]: "skip", [stateKey]: filterState };
@@ -246,7 +246,7 @@ const debug = false;
const undoableConfig = {
debug,
historyLimit: 50, // maximum history size
actionFilter: actionFilter(debug)
actionFilter: actionFilter(debug),
};
/*
@@ -258,9 +258,9 @@ if (debug) {
Confirm no intersection between the various trivial rejection action filters
*/
if (
new Set([...skipOnActions].filter(x => clearOnActions.has(x))).size > 0 ||
new Set([...skipOnActions].filter(x => saveOnActions.has(x))).size > 0 ||
new Set([...clearOnActions].filter(x => saveOnActions.has(x))).size > 0
new Set([...skipOnActions].filter((x) => clearOnActions.has(x))).size > 0 ||
new Set([...skipOnActions].filter((x) => saveOnActions.has(x))).size > 0 ||
new Set([...clearOnActions].filter((x) => saveOnActions.has(x))).size > 0
) {
console.error(
"Undoable misconfiguration - action filters have redundant events"
@@ -275,10 +275,10 @@ if (debug) {
const trivialFilters = new Set([
...skipOnActions,
...clearOnActions,
...saveOnActions
...saveOnActions,
]);
const trivialOverlapWithFsm = new Set(
[...trivialFilters].filter(x => seedFsm.events.has(x))
[...trivialFilters].filter((x) => seedFsm.events.has(x))
);
if (trivialOverlapWithFsm.size > 0) {
console.error(

View File

@@ -32,13 +32,13 @@ const createFsmTransitions = (
event: "graph brush start",
from: "init",
to: "graph brush in progress",
action: stashPending
action: stashPending,
},
{
event: "graph brush cancel",
from: "graph brush in progress",
to: "done",
action: applyPending
action: applyPending,
},
{
event: "graph brush deselect",
@@ -48,13 +48,13 @@ const createFsmTransitions = (
action: (fsm, transition, data) =>
data.state.graphSelection.selection.mode === "all"
? cancelPending()
: applyPending()
: applyPending(),
},
{
event: "graph brush end",
from: "graph brush in progress",
to: "done",
action: applyPending
action: applyPending,
},
/* graph selection lasso */
@@ -62,13 +62,13 @@ const createFsmTransitions = (
event: "graph lasso start",
from: "init",
to: "graph lasso in progress",
action: stashPending
action: stashPending,
},
{
event: "graph lasso cancel",
from: "graph lasso in progress",
to: "done",
action: applyPending
action: applyPending,
},
{
event: "graph lasso deselect",
@@ -78,13 +78,13 @@ const createFsmTransitions = (
action: (fsm, transition, data) =>
data.state.graphSelection.selection.mode === "all"
? cancelPending()
: applyPending()
: applyPending(),
},
{
event: "graph lasso end",
from: "graph lasso in progress",
to: "done",
action: applyPending
action: applyPending,
},
/* Continuous metadata histogram brush selection */
@@ -92,19 +92,19 @@ const createFsmTransitions = (
event: "continuous metadata histogram start",
from: "init",
to: "continuous histo select in progress",
action: stashPending
action: stashPending,
},
{
event: "continuous metadata histogram cancel",
from: "continuous histo select in progress",
to: "done",
action: cancelPending
action: cancelPending,
},
{
event: "continuous metadata histogram end",
from: "continuous histo select in progress",
to: "done",
action: applyPending
action: applyPending,
},
/* Single gene request by user */
@@ -112,25 +112,25 @@ const createFsmTransitions = (
event: "single user defined gene start",
from: "init",
to: "single user gene request in progress",
action: stashPending
action: stashPending,
},
{
event: "request user defined gene error",
from: "single user gene request in progress",
to: "single user gene error in progress",
action: skip
action: skip,
},
{
event: "single user defined gene error",
from: "single user gene error in progress",
to: "done",
action: cancelPending
action: cancelPending,
},
{
event: "single user defined gene complete",
from: "single user gene request in progress",
to: "done",
action: applyPending
action: applyPending,
},
/* Bulk gene request by user */
@@ -138,25 +138,25 @@ const createFsmTransitions = (
event: "bulk user defined gene start",
from: "init",
to: "bulk user gene request in progress",
action: stashPending
action: stashPending,
},
{
event: "request user defined gene error",
from: "bulk user gene request in progress",
to: "bulk user gene request error in progress",
action: skip
action: skip,
},
{
event: "bulk user defined gene error",
from: "bulk user gene request error in progress",
to: "done",
action: cancelPending
action: cancelPending,
},
{
event: "bulk user defined gene complete",
from: "bulk user gene request in progress",
to: "done",
action: applyPending
action: applyPending,
},
/* Compute Differential Expression button user action */
@@ -164,19 +164,19 @@ const createFsmTransitions = (
event: "request differential expression started",
from: "init",
to: "diffexp in progress",
action: stashPending
action: stashPending,
},
{
event: "request user defined gene error",
from: "diffexp in progress",
to: "done",
action: cancelPending
action: cancelPending,
},
{
event: "request differential expression success",
from: "diffexp in progress",
to: "done",
action: applyPending
action: applyPending,
},
/* Clear Differential Expression button user action */
@@ -184,13 +184,13 @@ const createFsmTransitions = (
event: "clear differential expression",
from: "init",
to: "CDE Button in progress",
action: stashPending
action: stashPending,
},
{
event: "clear scatterplot",
from: "CDE Button in progress",
to: "done",
action: applyPending
action: applyPending,
},
/* clear scatter plot button (eg, on scatterplot view) */
@@ -198,8 +198,8 @@ const createFsmTransitions = (
event: "clear scatterplot",
from: "init",
to: "done",
action: save
}
action: save,
},
];
};

View File

@@ -2,12 +2,12 @@ import { unassignedCategoryLabel } from "../globals";
import {
addObsAnnotations,
addVarAnnotations,
addObsLayout
addObsLayout,
} from "../util/stateManager/universe";
import {
World,
ControlsHelpers,
AnnotationsHelpers
AnnotationsHelpers,
} from "../util/stateManager";
const Universe = (state = null, action, nextSharedState, prevSharedState) => {
@@ -23,19 +23,19 @@ const Universe = (state = null, action, nextSharedState, prevSharedState) => {
case "obsAnnotations": {
return {
...state,
...addObsAnnotations(state, dataframe)
...addObsAnnotations(state, dataframe),
};
}
case "varAnnotations": {
return {
...state,
...addVarAnnotations(state, dataframe)
...addVarAnnotations(state, dataframe),
};
}
case "obsLayout": {
return {
...state,
...addObsLayout(state, dataframe)
...addObsLayout(state, dataframe),
};
}
default: {
@@ -62,8 +62,10 @@ const Universe = (state = null, action, nextSharedState, prevSharedState) => {
const { userDefinedGenes, diffexpGenes } = prevSharedState;
const allTheGenesWeNeed = [
...new Set(
[userDefinedGenes, diffexpGenes, Object.keys(action.expressionData)].filter(ele => ele).flat()
)
[userDefinedGenes, diffexpGenes, Object.keys(action.expressionData)]
.filter((ele) => ele)
.flat()
),
];
varData = ControlsHelpers.pruneVarDataCache(varData, allTheGenesWeNeed);
return { ...state, varData };
@@ -98,7 +100,7 @@ const Universe = (state = null, action, nextSharedState, prevSharedState) => {
categoryToDuplicate,
name,
{
writable: true
writable: true,
}
);
/* if we are duplicating a non-writable annotation, it may not have an unassigned category */
@@ -114,7 +116,7 @@ const Universe = (state = null, action, nextSharedState, prevSharedState) => {
name,
categories,
type: "categorical",
writable: true
writable: true,
});
data = new Array(state.nObs).fill(unassignedCategoryLabel);
}
@@ -134,7 +136,7 @@ const Universe = (state = null, action, nextSharedState, prevSharedState) => {
const colSchema = {
...state.schema.annotations.obsByName[name],
name: newName
name: newName,
};
const schema = AnnotationsHelpers.addObsAnnoSchema(
AnnotationsHelpers.removeObsAnnoSchema(state.schema, name),

View File

@@ -2,11 +2,11 @@ import { unassignedCategoryLabel } from "../globals";
import {
World,
ControlsHelpers,
AnnotationsHelpers
AnnotationsHelpers,
} from "../util/stateManager";
import {
addObsLayout,
removeObsLayout
removeObsLayout,
} from "../util/stateManager/schemaHelpers";
import clip from "../util/clip";
import quantile from "../util/quantile";
@@ -45,14 +45,14 @@ const WorldReducer = (
if (dim == "varData" || dim == "obsAnnotations") {
unclipped = {
...unclipped,
[dim]: universe[dim].clone()
[dim]: universe[dim].clone(),
};
}
return {
...state,
schema: universe.schema,
[dim]: universe[dim].clone(),
unclipped
unclipped,
};
}
@@ -113,9 +113,9 @@ const WorldReducer = (
const allTheGenesWeNeed = [
...new Set(
[userDefinedGenes, diffexpGenes, Object.keys(action.expressionData)]
.filter(ele => ele)
.filter((ele) => ele)
.flat()
)
),
];
unclippedVarData = ControlsHelpers.pruneVarDataCache(
unclippedVarData,
@@ -130,14 +130,14 @@ const WorldReducer = (
let clippedVarData = state.varData;
const keysToDrop = clippedVarData.colIndex
.keys()
.filter(k => !unclippedVarData.hasCol(k));
.filter((k) => !unclippedVarData.hasCol(k));
const keysToAdd = unclippedVarData.colIndex
.keys()
.filter(k => !clippedVarData.hasCol(k));
keysToDrop.forEach(k => {
.filter((k) => !clippedVarData.hasCol(k));
keysToDrop.forEach((k) => {
clippedVarData = clippedVarData.dropCol(k);
});
keysToAdd.forEach(k => {
keysToAdd.forEach((k) => {
const data = unclippedVarData.col(k).asArray();
const q = [state.clipQuantiles.min, state.clipQuantiles.max];
const [qMinVal, qMaxVal] = quantile(q, data);
@@ -154,8 +154,8 @@ const WorldReducer = (
varData: clippedVarData,
unclipped: {
...state.unclipped,
varData: unclippedVarData
}
varData: unclippedVarData,
},
};
}
@@ -188,7 +188,7 @@ const WorldReducer = (
name,
newAnnotation,
state.unclipped.obsAnnotations.rowIndex
)
),
};
return { ...state, schema, obsAnnotations, unclipped };
}
@@ -201,7 +201,7 @@ const WorldReducer = (
const obsAnnotations = state.obsAnnotations.renameCol(name, newName);
const unclipped = {
...state.unclipped,
obsAnnotations: state.unclipped.obsAnnotations.renameCol(name, newName)
obsAnnotations: state.unclipped.obsAnnotations.renameCol(name, newName),
};
return { ...state, schema, obsAnnotations, unclipped };
}
@@ -213,7 +213,7 @@ const WorldReducer = (
const obsAnnotations = state.obsAnnotations.dropCol(name);
const unclipped = {
...state.unclipped,
obsAnnotations: state.unclipped.obsAnnotations.dropCol(name)
obsAnnotations: state.unclipped.obsAnnotations.dropCol(name),
};
return { ...state, schema, obsAnnotations, unclipped };
}
@@ -233,7 +233,7 @@ const WorldReducer = (
crossfilter,
metadataField,
newLabelText
)
),
};
}
return { ...state, schema };
@@ -253,7 +253,7 @@ const WorldReducer = (
metadataField,
oldLabelName,
newLabelName
)
),
};
const obsAnnotations = state.obsAnnotations.replaceColData(
metadataField,
@@ -274,7 +274,7 @@ const WorldReducer = (
metadataField,
label,
unassignedCategoryLabel
)
),
};
const obsAnnotations = state.obsAnnotations.replaceColData(
metadataField,
@@ -288,7 +288,7 @@ const WorldReducer = (
const { crossfilter } = prevSharedState;
return {
...state,
...setLabelOnCurrentSelection(state, crossfilter, metadataField, label)
...setLabelOnCurrentSelection(state, crossfilter, metadataField, label),
};
}
@@ -306,14 +306,14 @@ const WorldReducer = (
const embedingLabels = embedding.colIndex.keys();
const labels = {
[embedingLabels[0]]: dims[0],
[embedingLabels[1]]: dims[1]
[embedingLabels[1]]: dims[1],
};
obsLayout = obsLayout.withColsFrom(embedding, labels);
schema = addObsLayout(schema, embeddingSchema);
return {
...state,
obsLayout,
schema
schema,
};
}
@@ -334,7 +334,7 @@ const WorldReducer = (
return {
...state,
obsLayout,
schema
schema,
};
}
@@ -357,7 +357,7 @@ function setLabelOnCurrentSelection(world, crossfilter, metadataField, label) {
metadataField,
mask,
label
)
),
};
const obsAnnotations = world.obsAnnotations.replaceColData(
metadataField,

View File

@@ -20,7 +20,7 @@ class Camera {
this.prevEvent = {
clientX: 0,
clientY: 0,
type: 0
type: 0,
};
this.canvas = canvas;
this.viewMatrix = mat3.create();
@@ -43,11 +43,11 @@ class Camera {
const m = this.viewMatrix;
const dyRange = [
-panBound - (m[7] + 1) / m[4],
panBound - (m[7] - 1) / m[4]
panBound - (m[7] - 1) / m[4],
];
const dxRange = [
-panBound - (m[6] + 1) / m[0],
panBound - (m[6] - 1) / m[0]
panBound - (m[6] - 1) / m[0],
];
const dxClamped = clamp(dx, dxRange);

View File

@@ -24,7 +24,7 @@ const catLabelSort = (isUserAnno, values) => {
const ints = [];
const unassignedOrNaN = [];
values.forEach(v => {
values.forEach((v) => {
if (isUserAnno && v === globals.unassignedCategoryLabel) {
unassignedOrNaN.push(v);
} else if (String(v).toLowerCase() === "nan") {

View File

@@ -70,7 +70,7 @@ const getCoordinatesByLabel = (
hasFinite: false,
xCoordinates: new Float32Array(numInLabel),
yCoordinates: new Float32Array(numInLabel),
length: 0
length: 0,
};
coordsByCategoryLabel.set(label, coords);
}

View File

@@ -11,15 +11,15 @@ If `setTo` is not undefined, values outside the [lower, upper] range will be set
*/
export default function clip(arr, lower, upper, setTo) {
const lowerSet = setTo === undefined ? lower : setTo;
const upperSet = setTo === undefined ? upper : setTo;
for (let i = 0, l = arr.length; i < l; i += 1) {
const v = arr[i];
if (v < lower) {
arr[i] = lowerSet;
} else if (v > upper) {
arr[i] = upperSet;
}
}
return arr;
const lowerSet = setTo === undefined ? lower : setTo;
const upperSet = setTo === undefined ? upper : setTo;
for (let i = 0, l = arr.length; i < l; i += 1) {
const v = arr[i];
if (v < lower) {
arr[i] = lowerSet;
} else if (v > upper) {
arr[i] = upperSet;
}
}
return arr;
}

View File

@@ -5,17 +5,17 @@ import {
isTypedArray,
isArrayOrTypedArray,
callOnceLazy,
memoize
memoize,
} from "./util";
import {
summarizeContinuous,
summarizeCategorical as _summarizeCategorical
summarizeCategorical as _summarizeCategorical,
} from "./summarize";
import {
histogramCategorical as _histogramCategorical,
hashCategorical,
histogramContinuous,
hashContinuous
hashContinuous,
} from "./histogram";
/*
@@ -140,7 +140,7 @@ class Dataframe {
if (!Array.isArray(columnarData)) {
throw new TypeError("Dataframe constructor requires array of columns");
}
if (!columnarData.every(c => isArrayOrTypedArray(c))) {
if (!columnarData.every((c) => isArrayOrTypedArray(c))) {
throw new TypeError("Dataframe columns must all be Array or TypedArray");
}
if (!isLabelIndex(rowIndex)) {
@@ -153,7 +153,7 @@ class Dataframe {
/* check for expected dimensionality / size */
if (
nCols !== columnarData.length ||
!columnarData.every(c => c.length === nRows)
!columnarData.every((c) => c.length === nRows)
) {
throw new RangeError(
"Dataframe dimension does not match provided data shape"
@@ -261,7 +261,7 @@ class Dataframe {
Create histogram bins for this column. Memoized.
*/
const _memoHistoCat = memoize(_histogramCategorical, hashCategorical);
const histogramCategorical = by => _memoHistoCat(get, by);
const histogramCategorical = (by) => _memoHistoCat(get, by);
let histogram = null;
if (isTypedArray(column)) {
const mFn = memoize(histogramContinuous, hashContinuous);
@@ -293,7 +293,7 @@ class Dataframe {
*/
const {
getOffset: getRowByOffset,
getLabel: getRowByLabel
getLabel: getRowByLabel,
} = this.rowIndex;
this.__columnsAccessor = this.__columns.map((column, idx) => {
if (accessors[idx]) {
@@ -423,7 +423,7 @@ class Dataframe {
// otherwise, bulid a new dataframe combining columns from both
const srcOffsets = srcLabels.map(l => dataframe.colIndex.getOffset(l));
const srcOffsets = srcLabels.map((l) => dataframe.colIndex.getOffset(l));
// check for label collisions
if (dstLabels.some(this.hasCol, this)) {
@@ -435,12 +435,12 @@ class Dataframe {
const { rowIndex } = this;
const columns = [
...this.__columns,
...srcOffsets.map(i => dataframe.__columns[i])
...srcOffsets.map((i) => dataframe.__columns[i]),
];
const colIndex = this.colIndex.withLabels(dstLabels);
const columnsAccessor = [
...this.__columnsAccessor,
...srcOffsets.map(i => dataframe.__columnsAccessor[i])
...srcOffsets.map((i) => dataframe.__columnsAccessor[i]),
];
return new this.constructor(
@@ -604,7 +604,7 @@ class Dataframe {
/* subset rows */
if (rowOffsets) {
columns = columns.map(col => {
columns = columns.map((col) => {
const newCol = new col.constructor(rowOffsets.length);
for (let i = 0, l = rowOffsets.length; i < l; i += 1) {
newCol[i] = col[rowOffsets[i]];
@@ -630,7 +630,7 @@ class Dataframe {
if (!labels) {
return null;
}
return labels.map(label => {
return labels.map((label) => {
const off = index.getOffset(label);
if (off === undefined) {
throw new RangeError(`unknown label: ${label}`);

View File

@@ -59,7 +59,7 @@ export function summarizeContinuous(col) {
nan,
pinf,
ninf,
percentiles
percentiles,
};
}
@@ -77,6 +77,6 @@ export function summarizeCategorical(col) {
categorical: true,
categories: [...categoryCounts.keys()],
categoryCounts,
numCategories: categoryCounts.size
numCategories: categoryCounts.size,
};
}

Some files were not shown because too many files have changed in this diff Show More