@vitessce/vit-s 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +102 -18
- package/dist-tsc/VitS.d.ts.map +1 -1
- package/dist-tsc/VitS.js +2 -2
- package/dist-tsc/VitessceGrid.d.ts.map +1 -1
- package/dist-tsc/VitessceGrid.js +4 -2
- package/dist-tsc/generate-alt-text.d.ts +19 -0
- package/dist-tsc/generate-alt-text.d.ts.map +1 -0
- package/dist-tsc/generate-alt-text.js +48 -0
- package/dist-tsc/generate-alt-text.test.d.ts +2 -0
- package/dist-tsc/generate-alt-text.test.d.ts.map +1 -0
- package/dist-tsc/generate-alt-text.test.js +67 -0
- package/dist-tsc/state/hooks.d.ts +3 -1
- package/dist-tsc/state/hooks.d.ts.map +1 -1
- package/dist-tsc/vitessce-grid-utils.d.ts +3 -2
- package/dist-tsc/vitessce-grid-utils.d.ts.map +1 -1
- package/dist-tsc/vitessce-grid-utils.js +19 -7
- package/package.json +5 -5
- package/src/VitS.js +2 -0
- package/src/VitessceGrid.js +7 -0
- package/src/generate-alt-text.js +51 -0
- package/src/generate-alt-text.test.js +73 -0
- package/src/vitessce-grid-utils.js +19 -7
package/dist/index.js
CHANGED
|
@@ -32233,9 +32233,9 @@ const schemePlasma = [[13, 8, 135], [16, 7, 136], [19, 7, 137], [22, 7, 138], [2
|
|
|
32233
32233
|
function rgbSpline(spline) {
|
|
32234
32234
|
return (colors2) => {
|
|
32235
32235
|
const n = colors2.length;
|
|
32236
|
-
|
|
32237
|
-
|
|
32238
|
-
|
|
32236
|
+
const r2 = new Array(n);
|
|
32237
|
+
const g = new Array(n);
|
|
32238
|
+
const b = new Array(n);
|
|
32239
32239
|
let i;
|
|
32240
32240
|
let color2;
|
|
32241
32241
|
for (i = 0; i < n; ++i) {
|
|
@@ -32244,10 +32244,10 @@ function rgbSpline(spline) {
|
|
|
32244
32244
|
g[i] = color2[1] || 0;
|
|
32245
32245
|
b[i] = color2[2] || 0;
|
|
32246
32246
|
}
|
|
32247
|
-
|
|
32248
|
-
|
|
32249
|
-
|
|
32250
|
-
return (t2) => [
|
|
32247
|
+
const rFunc = spline(r2);
|
|
32248
|
+
const gFunc = spline(g);
|
|
32249
|
+
const bFunc = spline(b);
|
|
32250
|
+
return (t2) => [rFunc(t2), gFunc(t2), bFunc(t2)];
|
|
32251
32251
|
};
|
|
32252
32252
|
}
|
|
32253
32253
|
function basis(values2) {
|
|
@@ -35149,6 +35149,7 @@ const DataType$1 = {
|
|
|
35149
35149
|
const FileType$1 = {
|
|
35150
35150
|
// Joint file types
|
|
35151
35151
|
ANNDATA_ZARR: "anndata.zarr",
|
|
35152
|
+
SPATIALDATA_ZARR: "spatialdata.zarr",
|
|
35152
35153
|
// Atomic file types
|
|
35153
35154
|
OBS_EMBEDDING_CSV: "obsEmbedding.csv",
|
|
35154
35155
|
OBS_SPOTS_CSV: "obsSpots.csv",
|
|
@@ -35171,6 +35172,16 @@ const FileType$1 = {
|
|
|
35171
35172
|
OBS_SEGMENTATIONS_ANNDATA_ZARR: "obsSegmentations.anndata.zarr",
|
|
35172
35173
|
OBS_LABELS_ANNDATA_ZARR: "obsLabels.anndata.zarr",
|
|
35173
35174
|
FEATURE_LABELS_ANNDATA_ZARR: "featureLabels.anndata.zarr",
|
|
35175
|
+
// SpatialData
|
|
35176
|
+
IMAGE_SPATIALDATA_ZARR: "image.spatialdata.zarr",
|
|
35177
|
+
LABELS_SPATIALDATA_ZARR: "labels.spatialdata.zarr",
|
|
35178
|
+
SHAPES_SPATIALDATA_ZARR: "shapes.spatialdata.zarr",
|
|
35179
|
+
OBS_FEATURE_MATRIX_SPATIALDATA_ZARR: "obsFeatureMatrix.spatialdata.zarr",
|
|
35180
|
+
OBS_SETS_SPATIALDATA_ZARR: "obsSets.spatialdata.zarr",
|
|
35181
|
+
OBS_SPOTS_SPATIALDATA_ZARR: "obsSpots.spatialdata.zarr",
|
|
35182
|
+
// TODO:
|
|
35183
|
+
// OBS_POINTS_SPATIALDATA_ZARR: 'obsPoints.spatialdata.zarr',
|
|
35184
|
+
// OBS_LOCATIONS_SPATIALDATA_ZARR: 'obsLocations.spatialdata.zarr',
|
|
35174
35185
|
// MuData
|
|
35175
35186
|
OBS_FEATURE_MATRIX_MUDATA_ZARR: "obsFeatureMatrix.mudata.zarr",
|
|
35176
35187
|
OBS_SETS_MUDATA_ZARR: "obsSets.mudata.zarr",
|
|
@@ -35383,10 +35394,10 @@ const AUTO_INDEPENDENT_COORDINATION_TYPES = [
|
|
|
35383
35394
|
CoordinationType$1.EMBEDDING_OBS_OPACITY
|
|
35384
35395
|
];
|
|
35385
35396
|
const note = "This file is autogenerated by .changeset/post-changelog.mjs.";
|
|
35386
|
-
const version = "3.
|
|
35387
|
-
const date = "2023-09-
|
|
35397
|
+
const version = "3.3.0";
|
|
35398
|
+
const date = "2023-09-28";
|
|
35388
35399
|
const branch = "changeset-release/main";
|
|
35389
|
-
const hash = "
|
|
35400
|
+
const hash = "4b4a99ea";
|
|
35390
35401
|
const META_VERSION = {
|
|
35391
35402
|
note,
|
|
35392
35403
|
version,
|
|
@@ -35632,7 +35643,13 @@ function buildConfigSchema(pluginFileTypes, pluginJointFileTypes, pluginCoordina
|
|
|
35632
35643
|
// Wrap each value schema in z.record()
|
|
35633
35644
|
fromEntries(pluginCoordinationTypes.map((ct) => [
|
|
35634
35645
|
ct.name,
|
|
35635
|
-
z.record(
|
|
35646
|
+
z.record(
|
|
35647
|
+
// For now, assume the key type is string (though it would be
|
|
35648
|
+
// slightly nicer if we could use the coordinationScopeName schema here).
|
|
35649
|
+
// Once https://github.com/colinhacks/zod/issues/2746 gets resolved
|
|
35650
|
+
// then we can try that approach again, but it should not be a big deal.
|
|
35651
|
+
ct.valueSchema.optional()
|
|
35652
|
+
).optional()
|
|
35636
35653
|
]))
|
|
35637
35654
|
).strict().describe("The coordination space stores the values for each scope of each coordination object.").optional(),
|
|
35638
35655
|
layout: z.array(z.object({
|
|
@@ -35740,9 +35757,30 @@ z.object({
|
|
|
35740
35757
|
offsetsUrl: z.string().optional(),
|
|
35741
35758
|
coordinateTransformations: omeCoordinateTransformations.optional()
|
|
35742
35759
|
});
|
|
35743
|
-
z.object({
|
|
35760
|
+
const imageOmeZarrSchema = z.object({
|
|
35744
35761
|
coordinateTransformations: omeCoordinateTransformations.optional()
|
|
35745
35762
|
});
|
|
35763
|
+
imageOmeZarrSchema.extend({
|
|
35764
|
+
path: z.string()
|
|
35765
|
+
});
|
|
35766
|
+
z.object({
|
|
35767
|
+
path: z.string()
|
|
35768
|
+
});
|
|
35769
|
+
z.object({
|
|
35770
|
+
path: z.string()
|
|
35771
|
+
});
|
|
35772
|
+
z.object({
|
|
35773
|
+
path: z.string(),
|
|
35774
|
+
tablePath: z.string().optional().describe("The path to a table which annotates the spots. If available but not specified, the spot identifiers may not be aligned with associated tabular data as expected.")
|
|
35775
|
+
});
|
|
35776
|
+
annDataObsFeatureMatrix.extend({
|
|
35777
|
+
region: z.string().describe("The name of a region to use to filter instances (i.e., rows) in the table").optional()
|
|
35778
|
+
});
|
|
35779
|
+
z.object({
|
|
35780
|
+
region: z.string().describe("The name of a region to use to filter instances (i.e., rows) in the table").optional(),
|
|
35781
|
+
tablePath: z.string().optional().describe("The path to a table which contains the index for the set values."),
|
|
35782
|
+
obsSets: annDataObsSets
|
|
35783
|
+
});
|
|
35746
35784
|
z.object({
|
|
35747
35785
|
obsIndex: z.string(),
|
|
35748
35786
|
obsEmbedding: z.array(z.string()).length(2)
|
|
@@ -43130,9 +43168,9 @@ After: ${JSON.stringify(coordinationValuesWithDefaults)}`);
|
|
|
43130
43168
|
}
|
|
43131
43169
|
return coordinationValuesWithDefaults;
|
|
43132
43170
|
}
|
|
43133
|
-
function createLoaders(datasets, configDescription, fileTypes, coordinationTypes) {
|
|
43171
|
+
function createLoaders(datasets, configDescription, fileTypes, coordinationTypes, stores) {
|
|
43134
43172
|
const result = {};
|
|
43135
|
-
const dataSources =
|
|
43173
|
+
const dataSources = new InternMap([], JSON.stringify);
|
|
43136
43174
|
const defaultCoordinationValues = fromEntries(
|
|
43137
43175
|
coordinationTypes.map((ct) => [ct.name, ct.defaultValue])
|
|
43138
43176
|
);
|
|
@@ -43160,10 +43198,18 @@ function createLoaders(datasets, configDescription, fileTypes, coordinationTypes
|
|
|
43160
43198
|
);
|
|
43161
43199
|
const [DataSourceClass, LoaderClass] = getSourceAndLoaderFromFileType(fileType, fileTypes);
|
|
43162
43200
|
const fileId = url || JSON.stringify(options);
|
|
43163
|
-
|
|
43164
|
-
|
|
43201
|
+
const dataSourceName = DataSourceClass.prototype.constructor.name;
|
|
43202
|
+
const dataSourceKey = [fileId, dataSourceName];
|
|
43203
|
+
if (!dataSources.has(dataSourceKey)) {
|
|
43204
|
+
dataSources.set(dataSourceKey, new DataSourceClass({
|
|
43205
|
+
url,
|
|
43206
|
+
requestInit: requestInit2,
|
|
43207
|
+
// Optionally, pass a Zarrita store to the data source,
|
|
43208
|
+
// if one was mapped to this URL.
|
|
43209
|
+
store: stores == null ? void 0 : stores[url]
|
|
43210
|
+
}));
|
|
43165
43211
|
}
|
|
43166
|
-
const loader = new LoaderClass(dataSources
|
|
43212
|
+
const loader = new LoaderClass(dataSources.get(dataSourceKey), file);
|
|
43167
43213
|
if (datasetLoaders.loaders[dataType]) {
|
|
43168
43214
|
datasetLoaders.loaders[dataType].set(coordinationValuesWithDefaults, loader);
|
|
43169
43215
|
} else {
|
|
@@ -43699,6 +43745,38 @@ const useTitleStyles = makeStyles((theme) => ({
|
|
|
43699
43745
|
}
|
|
43700
43746
|
}
|
|
43701
43747
|
}));
|
|
43748
|
+
function arrayToString(arr) {
|
|
43749
|
+
const arrF = arr.filter((item) => item !== void 0);
|
|
43750
|
+
if (arrF.length === 0) {
|
|
43751
|
+
return "";
|
|
43752
|
+
}
|
|
43753
|
+
if (arrF.length === 1) {
|
|
43754
|
+
return arrF[0];
|
|
43755
|
+
}
|
|
43756
|
+
return `${arr.slice(0, -1).join(", ")} and ${arr.slice(-1)}`;
|
|
43757
|
+
}
|
|
43758
|
+
const ViewTypesToText = /* @__PURE__ */ new Map([
|
|
43759
|
+
[ViewType$1.DESCRIPTION, "description"],
|
|
43760
|
+
[ViewType$1.STATUS, "status view for debugging"],
|
|
43761
|
+
[ViewType$1.SCATTERPLOT, "scatterplot with embeddings"],
|
|
43762
|
+
[ViewType$1.SPATIAL, "spatial view"],
|
|
43763
|
+
[ViewType$1.SPATIAL_BETA, "spatial view"],
|
|
43764
|
+
[ViewType$1.HEATMAP, "cell by gene heatmap"],
|
|
43765
|
+
[ViewType$1.LAYER_CONTROLLER, "layer controller"],
|
|
43766
|
+
[ViewType$1.LAYER_CONTROLLER_BETA, "layer controller"],
|
|
43767
|
+
[ViewType$1.GENOMIC_PROFILES, "genome browser tracks with bar plots"],
|
|
43768
|
+
[ViewType$1.GATING, "scatterplot of gated gene expression data"],
|
|
43769
|
+
[ViewType$1.FEATURE_LIST, "interactive list of features"],
|
|
43770
|
+
[ViewType$1.OBS_SETS, "list of potential observation sets"],
|
|
43771
|
+
[ViewType$1.OBS_SET_SIZES, "sizes of selected observation sets"],
|
|
43772
|
+
[ViewType$1.OBS_SET_FEATURE_VALUE_DISTRIBUTION, "violin plot with values"],
|
|
43773
|
+
[ViewType$1.FEATURE_VALUE_HISTOGRAM, "distribution of values"]
|
|
43774
|
+
]);
|
|
43775
|
+
function getAltText(config2) {
|
|
43776
|
+
const componentList = config2.layout.map((c) => c.component);
|
|
43777
|
+
const altText = `Vitessce grid with ${componentList.length} views, including ${arrayToString(componentList.map((c) => ViewTypesToText.get(c) || "plugin view"))}.`;
|
|
43778
|
+
return altText;
|
|
43779
|
+
}
|
|
43702
43780
|
const padding = 10;
|
|
43703
43781
|
const margin = 5;
|
|
43704
43782
|
function VitessceGrid(props) {
|
|
@@ -43719,6 +43797,7 @@ function VitessceGrid(props) {
|
|
|
43719
43797
|
const [componentWidth] = useClosestVitessceContainerSize(containerRef);
|
|
43720
43798
|
const classes = useVitessceContainerStyles();
|
|
43721
43799
|
const titleClasses = useTitleStyles();
|
|
43800
|
+
const altText = useMemo(() => getAltText(config2), [configKey]);
|
|
43722
43801
|
useEffect(() => {
|
|
43723
43802
|
onResize();
|
|
43724
43803
|
}, [rowHeight, onResize]);
|
|
@@ -43753,9 +43832,12 @@ function VitessceGrid(props) {
|
|
|
43753
43832
|
{
|
|
43754
43833
|
ref: containerRef,
|
|
43755
43834
|
className: clsx(VITESSCE_CONTAINER, classes.vitessceContainer),
|
|
43835
|
+
role: "group",
|
|
43836
|
+
"aria-label": altText,
|
|
43756
43837
|
children: layout2 ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43757
43838
|
VitessceGridLayout,
|
|
43758
43839
|
{
|
|
43840
|
+
role: "group",
|
|
43759
43841
|
layout: layout2,
|
|
43760
43842
|
height: height2,
|
|
43761
43843
|
rowHeight,
|
|
@@ -44079,6 +44161,7 @@ function createGenerateClassName(customPrefix) {
|
|
|
44079
44161
|
function VitS(props) {
|
|
44080
44162
|
const {
|
|
44081
44163
|
config: config2,
|
|
44164
|
+
stores,
|
|
44082
44165
|
rowHeight,
|
|
44083
44166
|
height: height2,
|
|
44084
44167
|
theme,
|
|
@@ -44186,7 +44269,8 @@ function VitS(props) {
|
|
|
44186
44269
|
configOrWarning.datasets,
|
|
44187
44270
|
configOrWarning.description,
|
|
44188
44271
|
fileTypes,
|
|
44189
|
-
coordinationTypes
|
|
44272
|
+
coordinationTypes,
|
|
44273
|
+
stores
|
|
44190
44274
|
);
|
|
44191
44275
|
return createViewConfigStore(loaders, configOrWarning);
|
|
44192
44276
|
}
|
package/dist-tsc/VitS.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VitS.d.ts","sourceRoot":"","sources":["../src/VitS.js"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH;IA7ByB,MAAM,EAApB,MAAM;IAGkB,SAAS,EAAjC,SAAS,GAAC,MAAM;IACF,MAAM,EAApB,MAAM;IACQ,KAAK,EAAnB,MAAM;IAEU,MAAM;IACN,cAAc;IAEd,cAAc;IAEf,cAAc,EAA7B,OAAO;IAIQ,sBAAsB,EAArC,OAAO;IAGY,GAAG,EAAtB,IAAI,GAAC,MAAM;IAGE,SAAS;IACT,SAAS;IACT,cAAc;IACd,iBAAiB;IACX,OAAO,EAA1B,IAAI,GAAC,MAAM;
|
|
1
|
+
{"version":3,"file":"VitS.d.ts","sourceRoot":"","sources":["../src/VitS.js"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH;IA7ByB,MAAM,EAApB,MAAM;IAGkB,SAAS,EAAjC,SAAS,GAAC,MAAM;IACF,MAAM,EAApB,MAAM;IACQ,KAAK,EAAnB,MAAM;IAEU,MAAM;IACN,cAAc;IAEd,cAAc;IAEf,cAAc,EAA7B,OAAO;IAIQ,sBAAsB,EAArC,OAAO;IAGY,GAAG,EAAtB,IAAI,GAAC,MAAM;IAGE,SAAS;IACT,SAAS;IACT,cAAc;IACd,iBAAiB;IACX,OAAO,EAA1B,IAAI,GAAC,MAAM;gBA2LrB"}
|
package/dist-tsc/VitS.js
CHANGED
|
@@ -45,7 +45,7 @@ import { createGenerateClassName } from './mui-utils.js';
|
|
|
45
45
|
* provided by the parent.
|
|
46
46
|
*/
|
|
47
47
|
export function VitS(props) {
|
|
48
|
-
const { config, rowHeight, height, theme, onWarn, onConfigChange, onLoaderChange, validateConfig = true, validateOnConfigChange = false, isBounded = false, uid = null, viewTypes: viewTypesProp, fileTypes: fileTypesProp, jointFileTypes: jointFileTypesProp, coordinationTypes: coordinationTypesProp, warning, } = props;
|
|
48
|
+
const { config, stores, rowHeight, height, theme, onWarn, onConfigChange, onLoaderChange, validateConfig = true, validateOnConfigChange = false, isBounded = false, uid = null, viewTypes: viewTypesProp, fileTypes: fileTypesProp, jointFileTypes: jointFileTypesProp, coordinationTypes: coordinationTypesProp, warning, } = props;
|
|
49
49
|
const viewTypes = useMemo(() => (viewTypesProp || []), [viewTypesProp]);
|
|
50
50
|
const fileTypes = useMemo(() => (fileTypesProp || []), [fileTypesProp]);
|
|
51
51
|
const jointFileTypes = useMemo(() => (jointFileTypesProp || []), [jointFileTypesProp]);
|
|
@@ -135,7 +135,7 @@ export function VitS(props) {
|
|
|
135
135
|
// Initialize the view config and loaders in the global state.
|
|
136
136
|
const createViewConfigStoreClosure = useCallback(() => {
|
|
137
137
|
if (success) {
|
|
138
|
-
const loaders = createLoaders(configOrWarning.datasets, configOrWarning.description, fileTypes, coordinationTypes);
|
|
138
|
+
const loaders = createLoaders(configOrWarning.datasets, configOrWarning.description, fileTypes, coordinationTypes, stores);
|
|
139
139
|
return createViewConfigStore(loaders, configOrWarning);
|
|
140
140
|
}
|
|
141
141
|
// No config found, so clear the loaders.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VitessceGrid.d.ts","sourceRoot":"","sources":["../src/VitessceGrid.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VitessceGrid.d.ts","sourceRoot":"","sources":["../src/VitessceGrid.js"],"names":[],"mappings":"AA4BA;;;;;;;;;;;GAWG;AACH;IATyB,SAAS,EAAvB,MAAM;IACQ,MAAM,EAApB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,MAAM,EAApB,MAAM;IACU,MAAM;IACE,SAAS,EAAjC,gBAAgB;IACQ,SAAS,EAAjC,gBAAgB;IACgB,iBAAiB,EAAjD,wBAAwB;gBA4FlC"}
|
package/dist-tsc/VitessceGrid.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import React, { useEffect, useCallback, } from 'react';
|
|
2
|
+
import React, { useEffect, useCallback, useMemo, } from 'react';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
4
|
import { VITESSCE_CONTAINER } from './classNames.js';
|
|
5
5
|
import { VitessceGridLayout } from './vitessce-grid-layout/index.js';
|
|
@@ -8,6 +8,7 @@ import { useViewConfigStoreApi, useSetViewConfig, useSetLoaders, useEmitGridResi
|
|
|
8
8
|
import { useClosestVitessceContainerSize, } from './hooks.js';
|
|
9
9
|
import { useVitessceContainerStyles } from './shared-mui/container.js';
|
|
10
10
|
import { useTitleStyles } from './title-styles.js';
|
|
11
|
+
import { getAltText } from './generate-alt-text.js';
|
|
11
12
|
const padding = 10;
|
|
12
13
|
const margin = 5;
|
|
13
14
|
/**
|
|
@@ -29,6 +30,7 @@ export default function VitessceGrid(props) {
|
|
|
29
30
|
const [componentWidth] = useClosestVitessceContainerSize(containerRef);
|
|
30
31
|
const classes = useVitessceContainerStyles();
|
|
31
32
|
const titleClasses = useTitleStyles();
|
|
33
|
+
const altText = useMemo(() => getAltText(config), [configKey]);
|
|
32
34
|
// When the row height has changed, publish a GRID_RESIZE event.
|
|
33
35
|
useEffect(() => {
|
|
34
36
|
onResize();
|
|
@@ -59,5 +61,5 @@ export default function VitessceGrid(props) {
|
|
|
59
61
|
setLoaders(newLoaders);
|
|
60
62
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
61
63
|
}, [success, configKey]);
|
|
62
|
-
return (_jsx("div", { ref: containerRef, className: clsx(VITESSCE_CONTAINER, classes.vitessceContainer), children: layout ? (_jsx(VitessceGridLayout, { layout: layout, height: height, rowHeight: rowHeight, theme: theme, viewTypes: viewTypes, draggableHandle: titleClasses.title, margin: margin, padding: padding, onRemoveComponent: removeComponent, onLayoutChange: changeLayoutPostMount, isBounded: isBounded, onResize: onResize, onResizeStop: onResize })) : null }));
|
|
64
|
+
return (_jsx("div", { ref: containerRef, className: clsx(VITESSCE_CONTAINER, classes.vitessceContainer), role: "group", "aria-label": altText, children: layout ? (_jsx(VitessceGridLayout, { role: "group", layout: layout, height: height, rowHeight: rowHeight, theme: theme, viewTypes: viewTypes, draggableHandle: titleClasses.title, margin: margin, padding: padding, onRemoveComponent: removeComponent, onLayoutChange: changeLayoutPostMount, isBounded: isBounded, onResize: onResize, onResizeStop: onResize })) : null }));
|
|
63
65
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Method for converting an array with strings to a natural sentence,
|
|
3
|
+
* concatenating the items with commans, and the last item with 'and'.
|
|
4
|
+
* @param {Array} arr Array with text items.
|
|
5
|
+
* @returns {string} Natural language sentence in the form of 'a, b and c', where a/b/c are items.
|
|
6
|
+
*/
|
|
7
|
+
export function arrayToString(arr: any[]): string;
|
|
8
|
+
/**
|
|
9
|
+
* Method for converting an array with strings to a natural sentence,
|
|
10
|
+
* concatenating the items with commans, and the last item with 'and'.
|
|
11
|
+
* @param {Object} config Vitessce config.
|
|
12
|
+
* @returns {string} Natural language sentence describing config (alt-text).
|
|
13
|
+
*/
|
|
14
|
+
export function getAltText(config: Object): string;
|
|
15
|
+
/**
|
|
16
|
+
* Mapping of ViewType to natural language description of ViewType.
|
|
17
|
+
*/
|
|
18
|
+
export const ViewTypesToText: Map<string, string>;
|
|
19
|
+
//# sourceMappingURL=generate-alt-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-alt-text.d.ts","sourceRoot":"","sources":["../src/generate-alt-text.js"],"names":[],"mappings":"AAEA;;;;;KAKK;AACL,2CAFe,MAAM,CAWpB;AAuBD;;;;;KAKK;AACL,mCAHa,MAAM,GACJ,MAAM,CAMpB;AA/BD;;KAEK;AACL,kDAgBG"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ViewType } from '@vitessce/constants-internal';
|
|
2
|
+
/**
|
|
3
|
+
* Method for converting an array with strings to a natural sentence,
|
|
4
|
+
* concatenating the items with commans, and the last item with 'and'.
|
|
5
|
+
* @param {Array} arr Array with text items.
|
|
6
|
+
* @returns {string} Natural language sentence in the form of 'a, b and c', where a/b/c are items.
|
|
7
|
+
*/
|
|
8
|
+
export function arrayToString(arr) {
|
|
9
|
+
const arrF = arr.filter(item => item !== undefined);
|
|
10
|
+
if (arrF.length === 0) {
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
if (arrF.length === 1) {
|
|
14
|
+
return arrF[0];
|
|
15
|
+
}
|
|
16
|
+
return `${arr.slice(0, -1).join(', ')} and ${arr.slice(-1)}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Mapping of ViewType to natural language description of ViewType.
|
|
20
|
+
*/
|
|
21
|
+
export const ViewTypesToText = new Map([
|
|
22
|
+
[ViewType.DESCRIPTION, 'description'],
|
|
23
|
+
[ViewType.STATUS, 'status view for debugging'],
|
|
24
|
+
[ViewType.SCATTERPLOT, 'scatterplot with embeddings'],
|
|
25
|
+
[ViewType.SPATIAL, 'spatial view'],
|
|
26
|
+
[ViewType.SPATIAL_BETA, 'spatial view'],
|
|
27
|
+
[ViewType.HEATMAP, 'cell by gene heatmap'],
|
|
28
|
+
[ViewType.LAYER_CONTROLLER, 'layer controller'],
|
|
29
|
+
[ViewType.LAYER_CONTROLLER_BETA, 'layer controller'],
|
|
30
|
+
[ViewType.GENOMIC_PROFILES, 'genome browser tracks with bar plots'],
|
|
31
|
+
[ViewType.GATING, 'scatterplot of gated gene expression data'],
|
|
32
|
+
[ViewType.FEATURE_LIST, 'interactive list of features'],
|
|
33
|
+
[ViewType.OBS_SETS, 'list of potential observation sets'],
|
|
34
|
+
[ViewType.OBS_SET_SIZES, 'sizes of selected observation sets'],
|
|
35
|
+
[ViewType.OBS_SET_FEATURE_VALUE_DISTRIBUTION, 'violin plot with values'],
|
|
36
|
+
[ViewType.FEATURE_VALUE_HISTOGRAM, 'distribution of values'],
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Method for converting an array with strings to a natural sentence,
|
|
40
|
+
* concatenating the items with commans, and the last item with 'and'.
|
|
41
|
+
* @param {Object} config Vitessce config.
|
|
42
|
+
* @returns {string} Natural language sentence describing config (alt-text).
|
|
43
|
+
*/
|
|
44
|
+
export function getAltText(config) {
|
|
45
|
+
const componentList = config.layout.map(c => c.component);
|
|
46
|
+
const altText = `Vitessce grid with ${componentList.length} views, including ${arrayToString(componentList.map(c => ViewTypesToText.get(c) || 'plugin view'))}.`;
|
|
47
|
+
return altText;
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-alt-text.test.d.ts","sourceRoot":"","sources":["../src/generate-alt-text.test.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ViewType } from '@vitessce/constants-internal';
|
|
3
|
+
import { arrayToString, ViewTypesToText, getAltText } from './generate-alt-text.js';
|
|
4
|
+
describe('Tests for generating an alt text from the vitessce config', () => {
|
|
5
|
+
describe('Tests for arrayToString', () => {
|
|
6
|
+
it('should return empty string for empty array', () => {
|
|
7
|
+
const arr = [];
|
|
8
|
+
const str = arrayToString(arr);
|
|
9
|
+
expect(str).toEqual('');
|
|
10
|
+
});
|
|
11
|
+
it('should return the element if the araay only has one element', () => {
|
|
12
|
+
const arr = ['one'];
|
|
13
|
+
const str = arrayToString(arr);
|
|
14
|
+
expect(str).toEqual('one');
|
|
15
|
+
});
|
|
16
|
+
it('should return the elements concatenated with "and" when the array has two elements', () => {
|
|
17
|
+
const arr = ['one', 'two'];
|
|
18
|
+
const str = arrayToString(arr);
|
|
19
|
+
expect(str).toEqual('one and two');
|
|
20
|
+
});
|
|
21
|
+
it('should return the elements concatenated with ", " and the last two elements with "and" when the array has more than two elements', () => {
|
|
22
|
+
const arr = ['one', 'two', 'three', 'four'];
|
|
23
|
+
const str = arrayToString(arr);
|
|
24
|
+
expect(str).toEqual('one, two, three and four');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('Tests for (const) ViewTypesToText', () => {
|
|
28
|
+
it('should have a natural language mapping for every defined viewtype', () => {
|
|
29
|
+
expect(ViewTypesToText.length).toEqual(ViewType.length);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('Tests for getAltTest', () => {
|
|
33
|
+
const config = {
|
|
34
|
+
coordinationSpace: {},
|
|
35
|
+
datasets: {},
|
|
36
|
+
description: 'Spatial organization of the somatosensory cortex revealed by osmFISH',
|
|
37
|
+
initStrategy: 'auto',
|
|
38
|
+
layout: [
|
|
39
|
+
{ component: 'description', props: { description: 'Codeluppi et al., Nature Methods 2018: Spatial organization of the somatosensory cortex revealed by osmFISH' }, x: 0, y: 0, w: 2 },
|
|
40
|
+
{ component: 'layerController', x: 0, y: 1, w: 2, h: 4 },
|
|
41
|
+
{ component: 'status', x: 0, y: 5, w: 2, h: 1 },
|
|
42
|
+
{ component: 'spatial', props: { channelNamesVisible: true }, x: 2, y: 0, w: 4 },
|
|
43
|
+
{ component: 'featureList', x: 9, y: 0, w: 3, h: 2 },
|
|
44
|
+
{ component: 'obsSets', x: 9, y: 3, w: 3, h: 2 },
|
|
45
|
+
{ component: 'heatmap', props: { transpose: true }, x: 2, y: 4, w: 5 },
|
|
46
|
+
{ component: 'obsSetFeatureValueDistribution', x: 7, y: 4, w: 5, h: 2 },
|
|
47
|
+
{ component: 'scatterplot', x: 6, y: 0, w: 3, h: 2 },
|
|
48
|
+
{ component: 'scatterplot', x: 6, y: 2, w: 3, h: 2 },
|
|
49
|
+
],
|
|
50
|
+
name: 'Codeluppi et al., Nature Methods 2018',
|
|
51
|
+
uid: 'A',
|
|
52
|
+
version: '1.0.16',
|
|
53
|
+
};
|
|
54
|
+
const altText = getAltText(config);
|
|
55
|
+
it('should return a string', () => {
|
|
56
|
+
expect(typeof altText).toEqual('string');
|
|
57
|
+
});
|
|
58
|
+
it('should contain the number of views', () => {
|
|
59
|
+
const hasNViews = altText.includes('10');
|
|
60
|
+
expect(hasNViews).toEqual(true);
|
|
61
|
+
});
|
|
62
|
+
it('should contain mappings', () => {
|
|
63
|
+
const hasHeatmap = altText.includes('heatmap') && altText.includes('spatial') && altText.includes('description') && altText.includes('scatterplot');
|
|
64
|
+
expect(hasHeatmap).toEqual(true);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -62,7 +62,9 @@ export function useMultiCoordinationScopes(parameter: any, coordinationScopes: a
|
|
|
62
62
|
export function useMultiCoordinationScopesNonNull(parameter: any, coordinationScopes: any): any;
|
|
63
63
|
export function useMultiCoordinationScopesSecondary(parameter: any, byType: any, coordinationScopes: any, coordinationScopesBy: any): any;
|
|
64
64
|
export function useMultiCoordinationScopesSecondaryNonNull(parameter: any, byType: any, coordinationScopes: any, coordinationScopesBy: any): any;
|
|
65
|
-
export function useMultiCoordinationValues(parameter: any, coordinationScopes: any):
|
|
65
|
+
export function useMultiCoordinationValues(parameter: any, coordinationScopes: any): {
|
|
66
|
+
[key: string]: any;
|
|
67
|
+
};
|
|
66
68
|
/**
|
|
67
69
|
* Get a mapping from dataset coordination scopes to dataset UIDs.
|
|
68
70
|
* @param {object} coordinationScopes The coordination scope mapping object for a view.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/state/hooks.js"],"names":[],"mappings":"AAkCA;;;;;;GAMG;AACH,oEAFa,MAAM,GAAC,SAAS,CAoB5B;AAED;;;;;;;GAOG;AACH,mGAFa,MAAM,GAAC,SAAS,CAoB5B;AAED;;;;;GAKG;AACH,6CAJW,MAAM,4BAEJ,MAAM,GAAC,SAAS,CAI5B;AAED;;;;;;GAMG;AACH,+CALW,MAAM,oFAGJ,MAAM,GAAC,SAAS,CAc5B;AA2MD;;;;;;;;;GASG;AACH,mDALW,MAAM,EAAE,sBACR,MAAM,GAEJ,MAAM,CAclB;AAED;;;;;;;;;;;;;;;GAeG;AACH,4CATW,MAAM,EAAE,sBACR,MAAM,SAqChB;AAED,yFAKC;AAED,gGAqBC;AAED,0IA4BC;AAED,iJAkEC;AAED
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/state/hooks.js"],"names":[],"mappings":"AAkCA;;;;;;GAMG;AACH,oEAFa,MAAM,GAAC,SAAS,CAoB5B;AAED;;;;;;;GAOG;AACH,mGAFa,MAAM,GAAC,SAAS,CAoB5B;AAED;;;;;GAKG;AACH,6CAJW,MAAM,4BAEJ,MAAM,GAAC,SAAS,CAI5B;AAED;;;;;;GAMG;AACH,+CALW,MAAM,oFAGJ,MAAM,GAAC,SAAS,CAc5B;AA2MD;;;;;;;;;GASG;AACH,mDALW,MAAM,EAAE,sBACR,MAAM,GAEJ,MAAM,CAclB;AAED;;;;;;;;;;;;;;;GAeG;AACH,4CATW,MAAM,EAAE,sBACR,MAAM,SAqChB;AAED,yFAKC;AAED,gGAqBC;AAED,0IA4BC;AAED,iJAkEC;AAED;;EAmBC;AAED;;;;GAIG;AACH,mDAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;;;;;;;;;GAaG;AACH,mDAXW,MAAM,EAAE,sBACR,MAAM,wBACN,MAAM,UAEN,MAAM,SA6EhB;AAED;;;;;GAKG;AACH,0DAHW,MAAM,GACJ,MAAM,CAiBlB;AAED;;;;;;GAMG;AACH,4DAJW,MAAM,wBACN,MAAM,GACJ,MAAM,CAgBlB;AAED;;;;;;;GAOG;AACH,4DANW,MAAM,EAAE,iDACR,MAAM,eACN,MAAM,iBACN,MAAM,SAuEhB;AAGD;;;;;;;;;;;GAWG;AACH,wDATW,MAAM,EAAE,sBACR,MAAM,wBACN,MAAM,SAYhB;AA6BD;;;;;;;;;;;;;;;GAeG;AACH,qDATW,MAAM,EAAE,sBACR,MAAM,SAkChB;AAED;;;;;GAKG;AACH,8BAHa,MAAM,CAKlB;AAGD;;;;;;;;;;;;GAYG;AACH,2CARW,MAAM,WACN,MAAM,YACN,MAAM,0BACN,MAAM,OAoBhB;AAED;;;;;;;;;;;;GAYG;AACH,2CARW,MAAM,WACN,MAAM,YACN,MAAM,0BACN,MAAM,OAShB;AAED;;;;;GAKG;AACH,6BAHa,MAAM,EAAE,CAKpB;AAED;;;;;GAKG;AACH,+CAEC;AAED;;;;;GAKG;AACH,0EAEC;AAED;;;;;GAKG;AACH,uEAEC;AAED;;;;;GAKG;AACH,4CAEC;AAED;;;;;GAKG;AACH,0CAEC;AAED;;;;;GAKG;AACH,oEAIC;AAED;;;;;GAKG;AACH,qCAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,iDAEC;AAED;;;;;GAKG;AACH,8BAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,0CAEC;AAED;;;;;GAKG;AACH,iDAHa,MAAM,CAKlB;AAED;;;;;GAKG;AACH,6DAOC;AAED;;;;GAIG;AACH,iCAFa,MAAM,CAIlB;AAED;;;;;GAKG;AACH,8CAEC;AAnhCD;;;;+QAA0D;AAC1D,sFAA0D;AAC1D;;;;;EAAgE;AAOhE;;;;+QAAwD;AACxD,qFAAwD;AAmGjD,yFA8FJ;AAOI,0FAFM,MAAM,CAOlB;AAcM,iDAUJ"}
|
|
@@ -3,11 +3,12 @@ export function useRowHeight(config: any, initialRowHeight: any, height: any, ma
|
|
|
3
3
|
* Create a mapping from dataset ID to loader objects by data type.
|
|
4
4
|
* @param {object[]} datasets The datasets array from the view config.
|
|
5
5
|
* @param {string} configDescription The top-level description in the
|
|
6
|
+
* view config.
|
|
6
7
|
* @param {PluginFileType[]} fileTypes
|
|
7
8
|
* @param {PluginCoordinationType[]} coordinationTypes
|
|
8
|
-
*
|
|
9
|
+
* @param {object} stores Optional mapping from URLs to Zarrita stores.
|
|
9
10
|
* @returns {object} Mapping from dataset ID to data type to loader
|
|
10
11
|
* instance.
|
|
11
12
|
*/
|
|
12
|
-
export function createLoaders(datasets: object[], configDescription: string, fileTypes: PluginFileType[], coordinationTypes: PluginCoordinationType[]): object;
|
|
13
|
+
export function createLoaders(datasets: object[], configDescription: string, fileTypes: PluginFileType[], coordinationTypes: PluginCoordinationType[], stores: object): object;
|
|
13
14
|
//# sourceMappingURL=vitessce-grid-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitessce-grid-utils.d.ts","sourceRoot":"","sources":["../src/vitessce-grid-utils.js"],"names":[],"mappings":"AA0CA,gHA6CC;AA4BD
|
|
1
|
+
{"version":3,"file":"vitessce-grid-utils.d.ts","sourceRoot":"","sources":["../src/vitessce-grid-utils.js"],"names":[],"mappings":"AA0CA,gHA6CC;AA4BD;;;;;;;;;;GAUG;AACH,wCATW,MAAM,EAAE,qBACR,MAAM,aAEN,gBAAgB,qBAChB,wBAAwB,UACxB,MAAM,GACJ,MAAM,CA4DlB"}
|
|
@@ -99,15 +99,16 @@ function withDefaults(coordinationValues, dataType, fileType, datasetUid, defaul
|
|
|
99
99
|
* Create a mapping from dataset ID to loader objects by data type.
|
|
100
100
|
* @param {object[]} datasets The datasets array from the view config.
|
|
101
101
|
* @param {string} configDescription The top-level description in the
|
|
102
|
+
* view config.
|
|
102
103
|
* @param {PluginFileType[]} fileTypes
|
|
103
104
|
* @param {PluginCoordinationType[]} coordinationTypes
|
|
104
|
-
*
|
|
105
|
+
* @param {object} stores Optional mapping from URLs to Zarrita stores.
|
|
105
106
|
* @returns {object} Mapping from dataset ID to data type to loader
|
|
106
107
|
* instance.
|
|
107
108
|
*/
|
|
108
|
-
export function createLoaders(datasets, configDescription, fileTypes, coordinationTypes) {
|
|
109
|
+
export function createLoaders(datasets, configDescription, fileTypes, coordinationTypes, stores) {
|
|
109
110
|
const result = {};
|
|
110
|
-
const dataSources =
|
|
111
|
+
const dataSources = new InternMap([], JSON.stringify);
|
|
111
112
|
const defaultCoordinationValues = fromEntries(coordinationTypes.map(ct => ([ct.name, ct.defaultValue])));
|
|
112
113
|
datasets.forEach((dataset) => {
|
|
113
114
|
const datasetLoaders = {
|
|
@@ -120,12 +121,23 @@ export function createLoaders(datasets, configDescription, fileTypes, coordinati
|
|
|
120
121
|
const dataType = getDataTypeFromFileType(fileType, fileTypes);
|
|
121
122
|
const coordinationValuesWithDefaults = withDefaults(coordinationValues, dataType, fileType, dataset.uid, defaultCoordinationValues);
|
|
122
123
|
const [DataSourceClass, LoaderClass] = getSourceAndLoaderFromFileType(fileType, fileTypes);
|
|
123
|
-
// Create _one_ DataSourceClass instance per URL
|
|
124
|
+
// Create _one_ DataSourceClass instance per (URL, DataSource class name) pair.
|
|
125
|
+
// Derived loaders share this object.
|
|
124
126
|
const fileId = url || JSON.stringify(options);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
// The class name might be minified but that should not matter;
|
|
128
|
+
// we just need a string that is unique to the class for the key.
|
|
129
|
+
const dataSourceName = DataSourceClass.prototype.constructor.name;
|
|
130
|
+
const dataSourceKey = [fileId, dataSourceName];
|
|
131
|
+
if (!dataSources.has(dataSourceKey)) {
|
|
132
|
+
dataSources.set(dataSourceKey, new DataSourceClass({
|
|
133
|
+
url,
|
|
134
|
+
requestInit,
|
|
135
|
+
// Optionally, pass a Zarrita store to the data source,
|
|
136
|
+
// if one was mapped to this URL.
|
|
137
|
+
store: stores?.[url],
|
|
138
|
+
}));
|
|
127
139
|
}
|
|
128
|
-
const loader = new LoaderClass(dataSources
|
|
140
|
+
const loader = new LoaderClass(dataSources.get(dataSourceKey), file);
|
|
129
141
|
if (datasetLoaders.loaders[dataType]) {
|
|
130
142
|
datasetLoaders.loaders[dataType].set(coordinationValuesWithDefaults, loader);
|
|
131
143
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/vit-s",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"author": "Gehlenborg Lab",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"uuid": "^9.0.0",
|
|
30
30
|
"zustand": "^3.5.10",
|
|
31
31
|
"react-aria": "^3.28.0",
|
|
32
|
-
"@vitessce/constants-internal": "3.
|
|
33
|
-
"@vitessce/
|
|
34
|
-
"@vitessce/
|
|
35
|
-
"@vitessce/utils": "3.
|
|
32
|
+
"@vitessce/constants-internal": "3.3.0",
|
|
33
|
+
"@vitessce/plugins": "3.3.0",
|
|
34
|
+
"@vitessce/schemas": "3.3.0",
|
|
35
|
+
"@vitessce/utils": "3.3.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@testing-library/jest-dom": "^5.16.4",
|
package/src/VitS.js
CHANGED
|
@@ -63,6 +63,7 @@ import { createGenerateClassName } from './mui-utils.js';
|
|
|
63
63
|
export function VitS(props) {
|
|
64
64
|
const {
|
|
65
65
|
config,
|
|
66
|
+
stores,
|
|
66
67
|
rowHeight,
|
|
67
68
|
height,
|
|
68
69
|
theme,
|
|
@@ -197,6 +198,7 @@ export function VitS(props) {
|
|
|
197
198
|
configOrWarning.description,
|
|
198
199
|
fileTypes,
|
|
199
200
|
coordinationTypes,
|
|
201
|
+
stores,
|
|
200
202
|
);
|
|
201
203
|
return createViewConfigStore(loaders, configOrWarning);
|
|
202
204
|
}
|
package/src/VitessceGrid.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, {
|
|
2
2
|
useEffect,
|
|
3
3
|
useCallback,
|
|
4
|
+
useMemo,
|
|
4
5
|
} from 'react';
|
|
5
6
|
import clsx from 'clsx';
|
|
6
7
|
import { VITESSCE_CONTAINER } from './classNames.js';
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
} from './hooks.js';
|
|
21
22
|
import { useVitessceContainerStyles } from './shared-mui/container.js';
|
|
22
23
|
import { useTitleStyles } from './title-styles.js';
|
|
24
|
+
import { getAltText } from './generate-alt-text.js';
|
|
23
25
|
|
|
24
26
|
const padding = 10;
|
|
25
27
|
const margin = 5;
|
|
@@ -58,6 +60,8 @@ export default function VitessceGrid(props) {
|
|
|
58
60
|
const classes = useVitessceContainerStyles();
|
|
59
61
|
const titleClasses = useTitleStyles();
|
|
60
62
|
|
|
63
|
+
const altText = useMemo(() => getAltText(config), [configKey]);
|
|
64
|
+
|
|
61
65
|
// When the row height has changed, publish a GRID_RESIZE event.
|
|
62
66
|
useEffect(() => {
|
|
63
67
|
onResize();
|
|
@@ -101,9 +105,12 @@ export default function VitessceGrid(props) {
|
|
|
101
105
|
<div
|
|
102
106
|
ref={containerRef}
|
|
103
107
|
className={clsx(VITESSCE_CONTAINER, classes.vitessceContainer)}
|
|
108
|
+
role="group"
|
|
109
|
+
aria-label={altText}
|
|
104
110
|
>
|
|
105
111
|
{layout ? (
|
|
106
112
|
<VitessceGridLayout
|
|
113
|
+
role="group"
|
|
107
114
|
layout={layout}
|
|
108
115
|
height={height}
|
|
109
116
|
rowHeight={rowHeight}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ViewType } from '@vitessce/constants-internal';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Method for converting an array with strings to a natural sentence,
|
|
5
|
+
* concatenating the items with commans, and the last item with 'and'.
|
|
6
|
+
* @param {Array} arr Array with text items.
|
|
7
|
+
* @returns {string} Natural language sentence in the form of 'a, b and c', where a/b/c are items.
|
|
8
|
+
*/
|
|
9
|
+
export function arrayToString(arr) {
|
|
10
|
+
const arrF = arr.filter(item => item !== undefined);
|
|
11
|
+
if (arrF.length === 0) {
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
if (arrF.length === 1) {
|
|
15
|
+
return arrF[0];
|
|
16
|
+
}
|
|
17
|
+
return `${arr.slice(0, -1).join(', ')} and ${arr.slice(-1)}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Mapping of ViewType to natural language description of ViewType.
|
|
22
|
+
*/
|
|
23
|
+
export const ViewTypesToText = new Map([
|
|
24
|
+
[ViewType.DESCRIPTION, 'description'],
|
|
25
|
+
[ViewType.STATUS, 'status view for debugging'],
|
|
26
|
+
[ViewType.SCATTERPLOT, 'scatterplot with embeddings'],
|
|
27
|
+
[ViewType.SPATIAL, 'spatial view'],
|
|
28
|
+
[ViewType.SPATIAL_BETA, 'spatial view'],
|
|
29
|
+
[ViewType.HEATMAP, 'cell by gene heatmap'],
|
|
30
|
+
[ViewType.LAYER_CONTROLLER, 'layer controller'],
|
|
31
|
+
[ViewType.LAYER_CONTROLLER_BETA, 'layer controller'],
|
|
32
|
+
[ViewType.GENOMIC_PROFILES, 'genome browser tracks with bar plots'],
|
|
33
|
+
[ViewType.GATING, 'scatterplot of gated gene expression data'],
|
|
34
|
+
[ViewType.FEATURE_LIST, 'interactive list of features'],
|
|
35
|
+
[ViewType.OBS_SETS, 'list of potential observation sets'],
|
|
36
|
+
[ViewType.OBS_SET_SIZES, 'sizes of selected observation sets'],
|
|
37
|
+
[ViewType.OBS_SET_FEATURE_VALUE_DISTRIBUTION, 'violin plot with values'],
|
|
38
|
+
[ViewType.FEATURE_VALUE_HISTOGRAM, 'distribution of values'],
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Method for converting an array with strings to a natural sentence,
|
|
43
|
+
* concatenating the items with commans, and the last item with 'and'.
|
|
44
|
+
* @param {Object} config Vitessce config.
|
|
45
|
+
* @returns {string} Natural language sentence describing config (alt-text).
|
|
46
|
+
*/
|
|
47
|
+
export function getAltText(config) {
|
|
48
|
+
const componentList = config.layout.map(c => c.component);
|
|
49
|
+
const altText = `Vitessce grid with ${componentList.length} views, including ${arrayToString(componentList.map(c => ViewTypesToText.get(c) || 'plugin view'))}.`;
|
|
50
|
+
return altText;
|
|
51
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ViewType } from '@vitessce/constants-internal';
|
|
3
|
+
import { arrayToString, ViewTypesToText, getAltText } from './generate-alt-text.js';
|
|
4
|
+
|
|
5
|
+
describe('Tests for generating an alt text from the vitessce config', () => {
|
|
6
|
+
describe('Tests for arrayToString', () => {
|
|
7
|
+
it('should return empty string for empty array', () => {
|
|
8
|
+
const arr = [];
|
|
9
|
+
const str = arrayToString(arr);
|
|
10
|
+
expect(str).toEqual('');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should return the element if the araay only has one element', () => {
|
|
14
|
+
const arr = ['one'];
|
|
15
|
+
const str = arrayToString(arr);
|
|
16
|
+
expect(str).toEqual('one');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should return the elements concatenated with "and" when the array has two elements', () => {
|
|
20
|
+
const arr = ['one', 'two'];
|
|
21
|
+
const str = arrayToString(arr);
|
|
22
|
+
expect(str).toEqual('one and two');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should return the elements concatenated with ", " and the last two elements with "and" when the array has more than two elements', () => {
|
|
26
|
+
const arr = ['one', 'two', 'three', 'four'];
|
|
27
|
+
const str = arrayToString(arr);
|
|
28
|
+
expect(str).toEqual('one, two, three and four');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('Tests for (const) ViewTypesToText', () => {
|
|
33
|
+
it('should have a natural language mapping for every defined viewtype', () => {
|
|
34
|
+
expect(ViewTypesToText.length).toEqual(ViewType.length);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('Tests for getAltTest', () => {
|
|
39
|
+
const config = {
|
|
40
|
+
coordinationSpace: {},
|
|
41
|
+
datasets: {},
|
|
42
|
+
description: 'Spatial organization of the somatosensory cortex revealed by osmFISH',
|
|
43
|
+
initStrategy: 'auto',
|
|
44
|
+
layout: [
|
|
45
|
+
{ component: 'description', props: { description: 'Codeluppi et al., Nature Methods 2018: Spatial organization of the somatosensory cortex revealed by osmFISH' }, x: 0, y: 0, w: 2 },
|
|
46
|
+
{ component: 'layerController', x: 0, y: 1, w: 2, h: 4 },
|
|
47
|
+
{ component: 'status', x: 0, y: 5, w: 2, h: 1 },
|
|
48
|
+
{ component: 'spatial', props: { channelNamesVisible: true }, x: 2, y: 0, w: 4 },
|
|
49
|
+
{ component: 'featureList', x: 9, y: 0, w: 3, h: 2 },
|
|
50
|
+
{ component: 'obsSets', x: 9, y: 3, w: 3, h: 2 },
|
|
51
|
+
{ component: 'heatmap', props: { transpose: true }, x: 2, y: 4, w: 5 },
|
|
52
|
+
{ component: 'obsSetFeatureValueDistribution', x: 7, y: 4, w: 5, h: 2 },
|
|
53
|
+
{ component: 'scatterplot', x: 6, y: 0, w: 3, h: 2 },
|
|
54
|
+
{ component: 'scatterplot', x: 6, y: 2, w: 3, h: 2 },
|
|
55
|
+
],
|
|
56
|
+
name: 'Codeluppi et al., Nature Methods 2018',
|
|
57
|
+
uid: 'A',
|
|
58
|
+
version: '1.0.16',
|
|
59
|
+
};
|
|
60
|
+
const altText = getAltText(config);
|
|
61
|
+
it('should return a string', () => {
|
|
62
|
+
expect(typeof altText).toEqual('string');
|
|
63
|
+
});
|
|
64
|
+
it('should contain the number of views', () => {
|
|
65
|
+
const hasNViews = altText.includes('10');
|
|
66
|
+
expect(hasNViews).toEqual(true);
|
|
67
|
+
});
|
|
68
|
+
it('should contain mappings', () => {
|
|
69
|
+
const hasHeatmap = altText.includes('heatmap') && altText.includes('spatial') && altText.includes('description') && altText.includes('scatterplot');
|
|
70
|
+
expect(hasHeatmap).toEqual(true);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -117,15 +117,16 @@ function withDefaults(
|
|
|
117
117
|
* Create a mapping from dataset ID to loader objects by data type.
|
|
118
118
|
* @param {object[]} datasets The datasets array from the view config.
|
|
119
119
|
* @param {string} configDescription The top-level description in the
|
|
120
|
+
* view config.
|
|
120
121
|
* @param {PluginFileType[]} fileTypes
|
|
121
122
|
* @param {PluginCoordinationType[]} coordinationTypes
|
|
122
|
-
*
|
|
123
|
+
* @param {object} stores Optional mapping from URLs to Zarrita stores.
|
|
123
124
|
* @returns {object} Mapping from dataset ID to data type to loader
|
|
124
125
|
* instance.
|
|
125
126
|
*/
|
|
126
|
-
export function createLoaders(datasets, configDescription, fileTypes, coordinationTypes) {
|
|
127
|
+
export function createLoaders(datasets, configDescription, fileTypes, coordinationTypes, stores) {
|
|
127
128
|
const result = {};
|
|
128
|
-
const dataSources =
|
|
129
|
+
const dataSources = new InternMap([], JSON.stringify);
|
|
129
130
|
const defaultCoordinationValues = fromEntries(
|
|
130
131
|
coordinationTypes.map(ct => ([ct.name, ct.defaultValue])),
|
|
131
132
|
);
|
|
@@ -152,12 +153,23 @@ export function createLoaders(datasets, configDescription, fileTypes, coordinati
|
|
|
152
153
|
defaultCoordinationValues,
|
|
153
154
|
);
|
|
154
155
|
const [DataSourceClass, LoaderClass] = getSourceAndLoaderFromFileType(fileType, fileTypes);
|
|
155
|
-
// Create _one_ DataSourceClass instance per URL
|
|
156
|
+
// Create _one_ DataSourceClass instance per (URL, DataSource class name) pair.
|
|
157
|
+
// Derived loaders share this object.
|
|
156
158
|
const fileId = url || JSON.stringify(options);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
// The class name might be minified but that should not matter;
|
|
160
|
+
// we just need a string that is unique to the class for the key.
|
|
161
|
+
const dataSourceName = DataSourceClass.prototype.constructor.name;
|
|
162
|
+
const dataSourceKey = [fileId, dataSourceName];
|
|
163
|
+
if (!dataSources.has(dataSourceKey)) {
|
|
164
|
+
dataSources.set(dataSourceKey, new DataSourceClass({
|
|
165
|
+
url,
|
|
166
|
+
requestInit,
|
|
167
|
+
// Optionally, pass a Zarrita store to the data source,
|
|
168
|
+
// if one was mapped to this URL.
|
|
169
|
+
store: stores?.[url],
|
|
170
|
+
}));
|
|
159
171
|
}
|
|
160
|
-
const loader = new LoaderClass(dataSources
|
|
172
|
+
const loader = new LoaderClass(dataSources.get(dataSourceKey), file);
|
|
161
173
|
if (datasetLoaders.loaders[dataType]) {
|
|
162
174
|
datasetLoaders.loaders[dataType].set(coordinationValuesWithDefaults, loader);
|
|
163
175
|
} else {
|