@vitessce/vit-s 3.0.0 → 3.1.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 +28430 -25685
- package/dist-tsc/VitS.d.ts +7 -2
- package/dist-tsc/VitS.d.ts.map +1 -1
- package/dist-tsc/VitS.js +43 -23
- package/dist-tsc/VitessceGrid.d.ts.map +1 -1
- package/dist-tsc/VitessceGrid.js +14 -11
- package/dist-tsc/VitessceGrid.test.js +5 -2
- package/dist-tsc/Warning.js +2 -2
- package/dist-tsc/data/LoaderResult.d.ts +8 -3
- package/dist-tsc/data/LoaderResult.d.ts.map +1 -1
- package/dist-tsc/data/LoaderResult.js +5 -0
- package/dist-tsc/data-hook-utils.d.ts +3 -7
- package/dist-tsc/data-hook-utils.d.ts.map +1 -1
- package/dist-tsc/data-hook-utils.js +97 -79
- package/dist-tsc/data-hooks.d.ts +12 -16
- package/dist-tsc/data-hooks.d.ts.map +1 -1
- package/dist-tsc/data-hooks.js +140 -127
- package/dist-tsc/errors/DataSourceFetchError.js +1 -1
- package/dist-tsc/hooks.d.ts +3 -7
- package/dist-tsc/hooks.d.ts.map +1 -1
- package/dist-tsc/hooks.js +12 -18
- package/dist-tsc/index.d.ts +2 -1
- package/dist-tsc/index.js +2 -1
- package/dist-tsc/mui-utils.d.ts +2 -0
- package/dist-tsc/mui-utils.d.ts.map +1 -0
- package/dist-tsc/mui-utils.js +44 -0
- package/dist-tsc/shared-plot-options/OptionSelect.js +1 -1
- package/dist-tsc/shared-plot-options/styles.js +1 -1
- package/dist-tsc/state/hooks.d.ts +27 -1
- package/dist-tsc/state/hooks.d.ts.map +1 -1
- package/dist-tsc/state/hooks.js +61 -21
- package/dist-tsc/view-config-utils.d.ts +1 -0
- package/dist-tsc/view-config-utils.d.ts.map +1 -1
- package/dist-tsc/view-config-utils.js +7 -1
- package/dist-tsc/view-config-utils.test.js +1 -0
- package/dist-tsc/vitessce-grid-layout/VitessceGridLayout.test.js +1 -1
- package/dist-tsc/vitessce-grid-layout/layout-utils.test.js +1 -0
- package/package.json +10 -9
- package/src/VitS.js +79 -43
- package/src/VitessceGrid.js +16 -10
- package/src/VitessceGrid.test.jsx +8 -2
- package/src/Warning.js +2 -2
- package/src/data/LoaderResult.js +5 -0
- package/src/data-hook-utils.js +116 -81
- package/src/data-hooks.js +148 -129
- package/src/errors/DataSourceFetchError.js +1 -1
- package/src/hooks.js +14 -21
- package/src/index.js +2 -0
- package/src/mui-utils.js +52 -0
- package/src/shared-plot-options/OptionSelect.js +1 -1
- package/src/shared-plot-options/styles.js +1 -1
- package/src/state/hooks.js +66 -21
- package/src/view-config-utils.js +8 -0
- package/src/view-config-utils.test.jsx +1 -0
- package/src/vitessce-grid-layout/VitessceGridLayout.test.jsx +1 -1
- package/src/vitessce-grid-layout/layout-utils.test.js +1 -0
- package/dist-tsc/schemas/config-0.1.0.schema.json +0 -84
- package/dist-tsc/schemas/config-1.0.0.schema.json +0 -733
- package/dist-tsc/schemas/config-1.0.1.schema.json +0 -909
- package/dist-tsc/schemas/config-1.0.10.schema.json +0 -969
- package/dist-tsc/schemas/config-1.0.11.schema.json +0 -990
- package/dist-tsc/schemas/config-1.0.12.schema.json +0 -997
- package/dist-tsc/schemas/config-1.0.13.schema.json +0 -1013
- package/dist-tsc/schemas/config-1.0.14.schema.json +0 -1048
- package/dist-tsc/schemas/config-1.0.15.schema.json +0 -1048
- package/dist-tsc/schemas/config-1.0.16.schema.json +0 -1089
- package/dist-tsc/schemas/config-1.0.2.schema.json +0 -910
- package/dist-tsc/schemas/config-1.0.3.schema.json +0 -910
- package/dist-tsc/schemas/config-1.0.4.schema.json +0 -949
- package/dist-tsc/schemas/config-1.0.5.schema.json +0 -949
- package/dist-tsc/schemas/config-1.0.6.schema.json +0 -949
- package/dist-tsc/schemas/config-1.0.7.schema.json +0 -949
- package/dist-tsc/schemas/config-1.0.8.schema.json +0 -965
- package/dist-tsc/schemas/config-1.0.9.schema.json +0 -965
- package/dist-tsc/schemas/obsSets.schema.d.ts +0 -179
- package/dist-tsc/schemas/obsSets.schema.d.ts.map +0 -1
- package/dist-tsc/schemas/obsSets.schema.js +0 -195
- package/dist-tsc/schemas/obsSetsTabular.schema.d.ts +0 -57
- package/dist-tsc/schemas/obsSetsTabular.schema.d.ts.map +0 -1
- package/dist-tsc/schemas/obsSetsTabular.schema.js +0 -38
- package/dist-tsc/schemas/raster.schema.d.ts +0 -239
- package/dist-tsc/schemas/raster.schema.d.ts.map +0 -1
- package/dist-tsc/schemas/raster.schema.js +0 -120
- package/src/schemas/config-0.1.0.schema.json +0 -85
- package/src/schemas/config-1.0.0.schema.json +0 -733
- package/src/schemas/config-1.0.1.schema.json +0 -909
- package/src/schemas/config-1.0.10.schema.json +0 -969
- package/src/schemas/config-1.0.11.schema.json +0 -990
- package/src/schemas/config-1.0.12.schema.json +0 -997
- package/src/schemas/config-1.0.13.schema.json +0 -1013
- package/src/schemas/config-1.0.14.schema.json +0 -1048
- package/src/schemas/config-1.0.15.schema.json +0 -1048
- package/src/schemas/config-1.0.16.schema.json +0 -1089
- package/src/schemas/config-1.0.2.schema.json +0 -911
- package/src/schemas/config-1.0.3.schema.json +0 -911
- package/src/schemas/config-1.0.4.schema.json +0 -949
- package/src/schemas/config-1.0.5.schema.json +0 -949
- package/src/schemas/config-1.0.6.schema.json +0 -950
- package/src/schemas/config-1.0.7.schema.json +0 -950
- package/src/schemas/config-1.0.8.schema.json +0 -966
- package/src/schemas/config-1.0.9.schema.json +0 -965
- package/src/schemas/obsSets.schema.js +0 -195
- package/src/schemas/obsSetsTabular.schema.js +0 -39
- package/src/schemas/raster.schema.js +0 -121
- package/src/schemas/schema-schema.sh +0 -34
package/dist-tsc/state/hooks.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useRef, useCallback, useMemo } from 'react';
|
|
|
3
3
|
import create from 'zustand';
|
|
4
4
|
import createContext from 'zustand/context';
|
|
5
5
|
import shallow from 'zustand/shallow';
|
|
6
|
-
import { isMatch } from 'lodash-es';
|
|
6
|
+
import { isMatch, cloneDeep } from 'lodash-es';
|
|
7
7
|
import { CoordinationType } from '@vitessce/constants-internal';
|
|
8
8
|
import { fromEntries, capitalize } from '@vitessce/utils';
|
|
9
9
|
// Reference: https://github.com/pmndrs/zustand#react-context
|
|
@@ -25,17 +25,19 @@ export const useAuxiliaryStore = useAuxiliaryStoreLocal;
|
|
|
25
25
|
* - https://github.com/pmndrs/zustand#using-subscribe-with-selector
|
|
26
26
|
* @returns {function} The useStore hook.
|
|
27
27
|
*/
|
|
28
|
-
export const createViewConfigStore = () => create(set => ({
|
|
28
|
+
export const createViewConfigStore = (initialLoaders, initialConfig) => create(set => ({
|
|
29
29
|
// State:
|
|
30
30
|
// The viewConfig is an object which must conform to the schema
|
|
31
31
|
// found in src/schemas/config.schema.json.
|
|
32
|
-
viewConfig:
|
|
32
|
+
viewConfig: initialConfig,
|
|
33
|
+
// Store the initial config so that its values can be used for resetting.
|
|
34
|
+
initialViewConfig: cloneDeep(initialConfig),
|
|
33
35
|
// The loaders object is a mapping from dataset ID to
|
|
34
36
|
// data type to loader object instance.
|
|
35
|
-
loaders:
|
|
37
|
+
loaders: initialLoaders,
|
|
36
38
|
// Reducer functions which update the state
|
|
37
39
|
// (although technically also part of state):
|
|
38
|
-
setViewConfig: viewConfig => set({ viewConfig }),
|
|
40
|
+
setViewConfig: viewConfig => set({ viewConfig, initialViewConfig: viewConfig }),
|
|
39
41
|
setLoaders: loaders => set({ loaders }),
|
|
40
42
|
setCoordinationValue: ({ parameter, scope, value }) => set(state => ({
|
|
41
43
|
viewConfig: {
|
|
@@ -157,6 +159,29 @@ const useGridSizeStore = create(set => ({
|
|
|
157
159
|
resizeCount: state.resizeCount + 1,
|
|
158
160
|
})),
|
|
159
161
|
}));
|
|
162
|
+
/**
|
|
163
|
+
* This hook uses the same logic as for the `values` part of
|
|
164
|
+
* the useCoordination hook, with the difference that it
|
|
165
|
+
* gets its values from the _initial_ view config rather
|
|
166
|
+
* than the current view config.
|
|
167
|
+
* @param {string[]} parameters Array of coordination types.
|
|
168
|
+
* @param {object} coordinationScopes Mapping of coordination types
|
|
169
|
+
* to scope names.
|
|
170
|
+
* @returns {object} Object containing all coordination values.
|
|
171
|
+
*/
|
|
172
|
+
export function useInitialCoordination(parameters, coordinationScopes) {
|
|
173
|
+
const values = useViewConfigStore((state) => {
|
|
174
|
+
const { coordinationSpace } = state.initialViewConfig;
|
|
175
|
+
return fromEntries(parameters.map((parameter) => {
|
|
176
|
+
if (coordinationSpace && coordinationSpace[parameter]) {
|
|
177
|
+
const value = coordinationSpace[parameter][coordinationScopes[parameter]];
|
|
178
|
+
return [parameter, value];
|
|
179
|
+
}
|
|
180
|
+
return [parameter, undefined];
|
|
181
|
+
}));
|
|
182
|
+
}, shallow);
|
|
183
|
+
return values;
|
|
184
|
+
}
|
|
160
185
|
/**
|
|
161
186
|
* The useCoordination hook returns both the
|
|
162
187
|
* values and setter functions for the coordination objects
|
|
@@ -388,6 +413,35 @@ export function useAuxiliaryCoordination(parameters, coordinationScopes) {
|
|
|
388
413
|
export function useLoaders() {
|
|
389
414
|
return useViewConfigStore(state => state.loaders);
|
|
390
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Find a specific loader instance for a particular dataset, data type, and view
|
|
418
|
+
* coordination values (mapping from coordination types to coordination values).
|
|
419
|
+
* Uses lodash/isMatch to perform matching against the file definition's
|
|
420
|
+
* coordination value mapping.
|
|
421
|
+
* @param {object} loaders The value returned by useLoaders.
|
|
422
|
+
* @param {string} dataset The dataset UID.
|
|
423
|
+
* @param {string} dataType The data type for the matching file.
|
|
424
|
+
* @param {object} viewCoordinationValues Current coordination values
|
|
425
|
+
* from the view. Match these against a subset of file definition coordination
|
|
426
|
+
* values.
|
|
427
|
+
* @returns The matching loader instance or `null`.
|
|
428
|
+
*/
|
|
429
|
+
export function getMatchingLoader(loaders, dataset, dataType, viewCoordinationValues) {
|
|
430
|
+
if (!loaders[dataset]) {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
const loaderInternMap = loaders[dataset].loaders[dataType];
|
|
434
|
+
if (!loaderInternMap) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
const loaderKeys = Array.from(loaderInternMap.keys());
|
|
438
|
+
const matchingKey = loaderKeys
|
|
439
|
+
.find(fileCoordinationValues => isMatch(fileCoordinationValues, viewCoordinationValues));
|
|
440
|
+
if (!matchingKey) {
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
return loaderInternMap.get(matchingKey);
|
|
444
|
+
}
|
|
391
445
|
/**
|
|
392
446
|
* Find a specific loader instance for a particular dataset, data type, and view
|
|
393
447
|
* coordination values (mapping from coordination types to coordination values).
|
|
@@ -402,28 +456,14 @@ export function useLoaders() {
|
|
|
402
456
|
* @returns The matching loader instance or `null`.
|
|
403
457
|
*/
|
|
404
458
|
export function useMatchingLoader(loaders, dataset, dataType, viewCoordinationValues) {
|
|
405
|
-
return useMemo(() =>
|
|
406
|
-
if (!loaders[dataset]) {
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
const loaderInternMap = loaders[dataset].loaders[dataType];
|
|
410
|
-
if (!loaderInternMap) {
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
|
-
const loaderKeys = Array.from(loaderInternMap.keys());
|
|
414
|
-
const matchingKey = loaderKeys
|
|
415
|
-
.find(fileCoordinationValues => isMatch(fileCoordinationValues, viewCoordinationValues));
|
|
416
|
-
if (!matchingKey) {
|
|
417
|
-
return null;
|
|
418
|
-
}
|
|
419
|
-
return loaderInternMap.get(matchingKey);
|
|
420
|
-
}, [loaders, dataset, dataType, viewCoordinationValues]);
|
|
459
|
+
return useMemo(() => getMatchingLoader(loaders, dataset, dataType, viewCoordinationValues), [loaders, dataset, dataType, viewCoordinationValues]);
|
|
421
460
|
}
|
|
422
461
|
/**
|
|
423
462
|
* Find a specific loader instance for a particular dataset, data type, and view
|
|
424
463
|
* coordination values (mapping from coordination types to coordination values).
|
|
425
464
|
* Uses lodash/isMatch to perform matching against the file definition's
|
|
426
465
|
* coordination value mapping.
|
|
466
|
+
* TODO: can this function be removed?
|
|
427
467
|
* @param {object} loaders The value returned by useLoaders.
|
|
428
468
|
* @param {string} dataset The dataset UID.
|
|
429
469
|
* @param {string} dataType The data type for the matching file.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-config-utils.d.ts","sourceRoot":"","sources":["../src/view-config-utils.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"view-config-utils.d.ts","sourceRoot":"","sources":["../src/view-config-utils.js"],"names":[],"mappings":"AASA,wDAKC;AAED;;;;;;;;GAQG;AACH,6DALW,MAAM,oBACN,MAAM,GAEJ,MAAM,EAAE,CAMpB;AAyND;;;;;;;;;;;;;;GAcG;AACH,mCANW,MAAM,kBACN,qBAAqB,qBACrB,wBAAwB,aACxB,gBAAgB;;;EAY1B"}
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
/* eslint-disable camelcase */
|
|
3
3
|
import { cloneDeep } from 'lodash-es';
|
|
4
4
|
import { fromEntries, getNextScope } from '@vitessce/utils';
|
|
5
|
-
import { AUTO_INDEPENDENT_COORDINATION_TYPES, } from '@vitessce/constants-internal';
|
|
5
|
+
import { AUTO_INDEPENDENT_COORDINATION_TYPES, META_VERSION, } from '@vitessce/constants-internal';
|
|
6
|
+
export function logConfig(config, name) {
|
|
7
|
+
console.groupCollapsed(`🚄 VitS (${META_VERSION.version}) ${name}`);
|
|
8
|
+
console.info(`data:,${JSON.stringify(config)}`);
|
|
9
|
+
console.info(JSON.stringify(config, null, 2));
|
|
10
|
+
console.groupEnd();
|
|
11
|
+
}
|
|
6
12
|
/**
|
|
7
13
|
* Get a list of all unique scope names for a
|
|
8
14
|
* particular coordination type, which exist in
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
/* eslint-disable camelcase */
|
|
3
|
+
import { describe, it, expect } from 'vitest';
|
|
3
4
|
import React from 'react';
|
|
4
5
|
import { z } from '@vitessce/schemas';
|
|
5
6
|
import { PluginViewType, PluginCoordinationType } from '@vitessce/plugins';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
3
|
import '@testing-library/jest-dom';
|
|
3
4
|
import { cleanup, render, screen } from '@testing-library/react';
|
|
4
|
-
import { afterEach } from 'vitest';
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { PluginViewType } from '@vitessce/plugins';
|
|
7
7
|
import { VitessceGridLayout } from './VitessceGridLayout.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/vit-s",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"author": "Gehlenborg Lab",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@material-ui/core": "~4.12.3",
|
|
20
20
|
"@material-ui/icons": "~4.11.2",
|
|
21
|
+
"@tanstack/react-query": "^4.29.12",
|
|
21
22
|
"clsx": "^1.1.1",
|
|
22
23
|
"d3-array": "^2.4.0",
|
|
23
24
|
"fast-deep-equal": "^3.1.3",
|
|
@@ -27,24 +28,24 @@
|
|
|
27
28
|
"react-grid-layout-with-lodash": "^1.3.5",
|
|
28
29
|
"uuid": "^9.0.0",
|
|
29
30
|
"zustand": "^3.5.10",
|
|
30
|
-
"@vitessce/constants-internal": "3.
|
|
31
|
-
"@vitessce/plugins": "3.
|
|
32
|
-
"@vitessce/schemas": "3.
|
|
33
|
-
"@vitessce/utils": "3.
|
|
31
|
+
"@vitessce/constants-internal": "3.1.0",
|
|
32
|
+
"@vitessce/plugins": "3.1.0",
|
|
33
|
+
"@vitessce/schemas": "3.1.0",
|
|
34
|
+
"@vitessce/utils": "3.1.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
37
|
+
"@testing-library/jest-dom": "^5.16.4",
|
|
38
|
+
"@testing-library/react": "^13.3.0",
|
|
36
39
|
"react": "^18.0.0",
|
|
37
40
|
"vite": "^4.3.0",
|
|
38
|
-
"vitest": "^0.
|
|
39
|
-
"@testing-library/jest-dom": "^5.16.4",
|
|
40
|
-
"@testing-library/react": "^13.3.0"
|
|
41
|
+
"vitest": "^0.32.2"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
44
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
44
45
|
},
|
|
45
46
|
"scripts": {
|
|
46
47
|
"bundle": "pnpm exec vite build -c ../../scripts/vite.config.js",
|
|
47
|
-
"test": "pnpm exec vitest --run
|
|
48
|
+
"test": "pnpm exec vitest --run"
|
|
48
49
|
},
|
|
49
50
|
"module": "dist/index.js",
|
|
50
51
|
"exports": {
|
package/src/VitS.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import React, { useEffect, useMemo } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo, useCallback } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ThemeProvider,
|
|
4
4
|
StylesProvider,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
} from '@material-ui/core';
|
|
6
|
+
import {
|
|
7
|
+
QueryClient,
|
|
8
|
+
QueryClientProvider,
|
|
9
|
+
} from '@tanstack/react-query';
|
|
7
10
|
import { isEqual } from 'lodash-es';
|
|
8
|
-
import { META_VERSION } from '@vitessce/constants-internal';
|
|
9
11
|
import { buildConfigSchema, latestConfigSchema } from '@vitessce/schemas';
|
|
10
12
|
import { muiTheme } from './shared-mui/styles.js';
|
|
11
13
|
import {
|
|
@@ -20,14 +22,11 @@ import { Warning } from './Warning.js';
|
|
|
20
22
|
import CallbackPublisher from './CallbackPublisher.js';
|
|
21
23
|
import {
|
|
22
24
|
initialize,
|
|
25
|
+
logConfig,
|
|
23
26
|
} from './view-config-utils.js';
|
|
27
|
+
import { createLoaders } from './vitessce-grid-utils.js';
|
|
28
|
+
import { createGenerateClassName } from './mui-utils.js';
|
|
24
29
|
|
|
25
|
-
function logConfig(config, name) {
|
|
26
|
-
console.groupCollapsed(`🚄 Vitessce (${META_VERSION.version}) ${name}`);
|
|
27
|
-
console.info(`data:,${JSON.stringify(config)}`);
|
|
28
|
-
console.info(JSON.stringify(config, null, 2));
|
|
29
|
-
console.groupEnd();
|
|
30
|
-
}
|
|
31
30
|
|
|
32
31
|
/**
|
|
33
32
|
* The Vitessce component.
|
|
@@ -44,10 +43,14 @@ function logConfig(config, name) {
|
|
|
44
43
|
* updates. Optional.
|
|
45
44
|
* @param {function} props.onLoaderChange A callback for loader
|
|
46
45
|
* updates. Optional.
|
|
46
|
+
* @param {boolean} props.validateConfig Whether to validate or not. Only to be
|
|
47
|
+
* set to false in controlled component situations, where bypassing validation
|
|
48
|
+
* is required for performance, and the parent knows the config
|
|
49
|
+
* is already valid (e.g., it originated from onConfigChange). By default, true.
|
|
47
50
|
* @param {boolean} props.validateOnConfigChange Whether to validate
|
|
48
51
|
* against the view config schema when publishing changes. Use for debugging
|
|
49
52
|
* purposes, as this may have a performance impact. By default, false.
|
|
50
|
-
* @param {
|
|
53
|
+
* @param {null|string} props.uid A unique identifier for this Vitessce instance,
|
|
51
54
|
* for the purpose of avoiding CSS autogenerated class name conflicts. Must be valid as part
|
|
52
55
|
* of a CSS class name string.
|
|
53
56
|
* @param {array} props.viewTypes Plugin view types.
|
|
@@ -66,9 +69,10 @@ export function VitS(props) {
|
|
|
66
69
|
onWarn,
|
|
67
70
|
onConfigChange,
|
|
68
71
|
onLoaderChange,
|
|
72
|
+
validateConfig = true,
|
|
69
73
|
validateOnConfigChange = false,
|
|
70
74
|
isBounded = false,
|
|
71
|
-
uid,
|
|
75
|
+
uid = null,
|
|
72
76
|
viewTypes: viewTypesProp,
|
|
73
77
|
fileTypes: fileTypesProp,
|
|
74
78
|
jointFileTypes: jointFileTypesProp,
|
|
@@ -87,16 +91,15 @@ export function VitS(props) {
|
|
|
87
91
|
[coordinationTypesProp],
|
|
88
92
|
);
|
|
89
93
|
|
|
90
|
-
const generateClassName = useMemo(() => createGenerateClassName(
|
|
91
|
-
disableGlobal: false, // Class names need to be deterministic
|
|
92
|
-
// Avoid conflicts with portal-ui MUI class names,
|
|
93
|
-
// and allow setting a UID.
|
|
94
|
-
productionPrefix: (uid ? `vit${uid}` : 'vit'),
|
|
95
|
-
}), [uid]);
|
|
94
|
+
const generateClassName = useMemo(() => createGenerateClassName(uid), [uid]);
|
|
96
95
|
|
|
97
|
-
const configUid = config?.uid;
|
|
98
96
|
const configVersion = config?.version;
|
|
99
97
|
|
|
98
|
+
// If config.uid exists, then use it for hook dependencies to detect changes
|
|
99
|
+
// (controlled component case). If not, then use the config object itself
|
|
100
|
+
// and assume the un-controlled component case.
|
|
101
|
+
const configKey = config?.uid || config;
|
|
102
|
+
|
|
100
103
|
const pluginSpecificConfigSchema = useMemo(() => buildConfigSchema(
|
|
101
104
|
fileTypes,
|
|
102
105
|
jointFileTypes,
|
|
@@ -114,10 +117,13 @@ export function VitS(props) {
|
|
|
114
117
|
return [warning, false];
|
|
115
118
|
}
|
|
116
119
|
logConfig(config, 'input view config');
|
|
120
|
+
if (!validateConfig) {
|
|
121
|
+
return [config, true];
|
|
122
|
+
}
|
|
117
123
|
const result = latestConfigSchema.safeParse(config);
|
|
118
124
|
if (result.success) {
|
|
119
125
|
const upgradedConfig = result.data;
|
|
120
|
-
logConfig(upgradedConfig, '
|
|
126
|
+
logConfig(upgradedConfig, 'parsed view config');
|
|
121
127
|
// Perform second round of parsing against plugin-specific config schema.
|
|
122
128
|
const pluginSpecificResult = pluginSpecificConfigSchema.safeParse(upgradedConfig);
|
|
123
129
|
// Initialize the view config according to the initStrategy.
|
|
@@ -155,7 +161,17 @@ export function VitS(props) {
|
|
|
155
161
|
unformatted: result.error.message,
|
|
156
162
|
}, result.success];
|
|
157
163
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
158
|
-
}, [
|
|
164
|
+
}, [configKey, configVersion, pluginSpecificConfigSchema, warning]);
|
|
165
|
+
|
|
166
|
+
const queryClient = useMemo(() => new QueryClient({
|
|
167
|
+
// Reference: https://tanstack.com/query/latest/docs/react/guides/window-focus-refetching
|
|
168
|
+
defaultOptions: {
|
|
169
|
+
queries: {
|
|
170
|
+
refetchOnWindowFocus: false,
|
|
171
|
+
retry: 2,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
}), [configKey]);
|
|
159
175
|
|
|
160
176
|
// Emit the upgraded/initialized view config
|
|
161
177
|
// to onConfigChange if necessary.
|
|
@@ -164,32 +180,52 @@ export function VitS(props) {
|
|
|
164
180
|
onConfigChange(configOrWarning);
|
|
165
181
|
}
|
|
166
182
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
167
|
-
}, [success,
|
|
183
|
+
}, [success, configKey, configOrWarning, onConfigChange]);
|
|
184
|
+
|
|
185
|
+
// Initialize the view config and loaders in the global state.
|
|
186
|
+
const createViewConfigStoreClosure = useCallback(() => {
|
|
187
|
+
if (success) {
|
|
188
|
+
const loaders = createLoaders(
|
|
189
|
+
configOrWarning.datasets,
|
|
190
|
+
configOrWarning.description,
|
|
191
|
+
fileTypes,
|
|
192
|
+
coordinationTypes,
|
|
193
|
+
);
|
|
194
|
+
return createViewConfigStore(loaders, configOrWarning);
|
|
195
|
+
}
|
|
196
|
+
// No config found, so clear the loaders.
|
|
197
|
+
return createViewConfigStore(null, null);
|
|
198
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
199
|
+
}, [success, configKey]);
|
|
168
200
|
|
|
169
201
|
return success ? (
|
|
170
202
|
<StylesProvider generateClassName={generateClassName}>
|
|
171
203
|
<ThemeProvider theme={muiTheme[theme]}>
|
|
172
|
-
<
|
|
173
|
-
<
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
204
|
+
<QueryClientProvider client={queryClient}>
|
|
205
|
+
<ViewConfigProvider key={configKey} createStore={createViewConfigStoreClosure}>
|
|
206
|
+
<AuxiliaryProvider createStore={createAuxiliaryStore}>
|
|
207
|
+
<VitessceGrid
|
|
208
|
+
success={success}
|
|
209
|
+
configKey={configKey}
|
|
210
|
+
viewTypes={viewTypes}
|
|
211
|
+
fileTypes={fileTypes}
|
|
212
|
+
coordinationTypes={coordinationTypes}
|
|
213
|
+
config={configOrWarning}
|
|
214
|
+
rowHeight={rowHeight}
|
|
215
|
+
height={height}
|
|
216
|
+
theme={theme}
|
|
217
|
+
isBounded={isBounded}
|
|
218
|
+
/>
|
|
219
|
+
<CallbackPublisher
|
|
220
|
+
onWarn={onWarn}
|
|
221
|
+
onConfigChange={onConfigChange}
|
|
222
|
+
onLoaderChange={onLoaderChange}
|
|
223
|
+
validateOnConfigChange={validateOnConfigChange}
|
|
224
|
+
pluginSpecificConfigSchema={pluginSpecificConfigSchema}
|
|
225
|
+
/>
|
|
226
|
+
</AuxiliaryProvider>
|
|
227
|
+
</ViewConfigProvider>
|
|
228
|
+
</QueryClientProvider>
|
|
193
229
|
</ThemeProvider>
|
|
194
230
|
</StylesProvider>
|
|
195
231
|
) : (
|
package/src/VitessceGrid.js
CHANGED
|
@@ -38,6 +38,8 @@ const margin = 5;
|
|
|
38
38
|
*/
|
|
39
39
|
export default function VitessceGrid(props) {
|
|
40
40
|
const {
|
|
41
|
+
success,
|
|
42
|
+
configKey,
|
|
41
43
|
rowHeight: initialRowHeight,
|
|
42
44
|
config,
|
|
43
45
|
theme,
|
|
@@ -75,28 +77,32 @@ export default function VitessceGrid(props) {
|
|
|
75
77
|
}, [changeLayout, componentWidth]);
|
|
76
78
|
|
|
77
79
|
// Update the view config and loaders in the global state.
|
|
80
|
+
// This effect is needed for the controlled component case in which
|
|
81
|
+
// the store has already been initialized with a view config,
|
|
82
|
+
// and we want to replace it with a new view config.
|
|
78
83
|
useEffect(() => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
let newLoaders = null;
|
|
85
|
+
let newConfig = null;
|
|
86
|
+
if (success) {
|
|
87
|
+
newLoaders = createLoaders(
|
|
82
88
|
config.datasets,
|
|
83
89
|
config.description,
|
|
84
90
|
fileTypes,
|
|
85
91
|
coordinationTypes,
|
|
86
92
|
);
|
|
87
|
-
|
|
88
|
-
} else {
|
|
89
|
-
// No config found, so clear the loaders.
|
|
90
|
-
setLoaders({});
|
|
93
|
+
newConfig = config;
|
|
91
94
|
}
|
|
92
|
-
|
|
95
|
+
setViewConfig(newConfig);
|
|
96
|
+
setLoaders(newLoaders);
|
|
97
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
98
|
+
}, [success, configKey]);
|
|
93
99
|
|
|
94
100
|
return (
|
|
95
101
|
<div
|
|
96
102
|
ref={containerRef}
|
|
97
103
|
className={clsx(VITESSCE_CONTAINER, classes.vitessceContainer)}
|
|
98
104
|
>
|
|
99
|
-
{layout
|
|
105
|
+
{layout ? (
|
|
100
106
|
<VitessceGridLayout
|
|
101
107
|
layout={layout}
|
|
102
108
|
height={height}
|
|
@@ -112,7 +118,7 @@ export default function VitessceGrid(props) {
|
|
|
112
118
|
onResize={onResize}
|
|
113
119
|
onResizeStop={onResize}
|
|
114
120
|
/>
|
|
115
|
-
)}
|
|
121
|
+
) : null}
|
|
116
122
|
</div>
|
|
117
123
|
);
|
|
118
124
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
1
2
|
import '@testing-library/jest-dom';
|
|
2
3
|
import { cleanup, render, screen } from '@testing-library/react';
|
|
3
|
-
import { afterEach } from 'vitest';
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { PluginViewType } from '@vitessce/plugins';
|
|
@@ -33,6 +33,10 @@ describe('VitessceGrid.js', () => {
|
|
|
33
33
|
],
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
function createViewConfigStoreClosure() {
|
|
37
|
+
return createViewConfigStore(null, config);
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
function FakeComponent() {
|
|
37
41
|
return <p>FakeComponent!</p>;
|
|
38
42
|
}
|
|
@@ -40,9 +44,11 @@ describe('VitessceGrid.js', () => {
|
|
|
40
44
|
const fileTypes = [];
|
|
41
45
|
const coordinationTypes = [];
|
|
42
46
|
render(
|
|
43
|
-
<ViewConfigProvider createStore={
|
|
47
|
+
<ViewConfigProvider createStore={createViewConfigStoreClosure}>
|
|
44
48
|
<AuxiliaryProvider createStore={createAuxiliaryStore}>
|
|
45
49
|
<VitessceGrid
|
|
50
|
+
success
|
|
51
|
+
configKey={null}
|
|
46
52
|
config={config}
|
|
47
53
|
viewTypes={viewTypes}
|
|
48
54
|
fileTypes={fileTypes}
|
package/src/Warning.js
CHANGED
|
@@ -20,7 +20,7 @@ const useStyles = makeStyles(theme => ({
|
|
|
20
20
|
row: {
|
|
21
21
|
flexGrow: '1',
|
|
22
22
|
},
|
|
23
|
-
|
|
23
|
+
warningCard: {
|
|
24
24
|
border: `1px solid ${theme.palette.cardBorder}`,
|
|
25
25
|
flex: '1 1 auto',
|
|
26
26
|
minHeight: '1px',
|
|
@@ -50,7 +50,7 @@ export function Warning(props) {
|
|
|
50
50
|
<div className={VITESSCE_CONTAINER}>
|
|
51
51
|
<div className={clsx(classes.warningLayout, classes.containerFluid)}>
|
|
52
52
|
<div className={classes.row}>
|
|
53
|
-
<div className={classes.
|
|
53
|
+
<div className={classes.warningCard}>
|
|
54
54
|
<h1>{title}</h1>
|
|
55
55
|
{preformatted ? (<pre>{preformatted}</pre>) : null}
|
|
56
56
|
<p>{unformatted}</p>
|
package/src/data/LoaderResult.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
export default class LoaderResult {
|
|
3
|
+
/**
|
|
4
|
+
* @param {*} data
|
|
5
|
+
* @param {object[]|string|null} url Single URL or array of { url, name } objects.
|
|
6
|
+
* @param {object} coordinationValues
|
|
7
|
+
*/
|
|
3
8
|
constructor(data, url, coordinationValues = null) {
|
|
4
9
|
this.data = data;
|
|
5
10
|
this.url = url;
|