@vitessce/vit-s 3.0.0 → 3.0.1

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.
Files changed (105) hide show
  1. package/dist/index.js +28348 -25624
  2. package/dist-tsc/VitS.d.ts +7 -2
  3. package/dist-tsc/VitS.d.ts.map +1 -1
  4. package/dist-tsc/VitS.js +43 -23
  5. package/dist-tsc/VitessceGrid.d.ts.map +1 -1
  6. package/dist-tsc/VitessceGrid.js +14 -11
  7. package/dist-tsc/VitessceGrid.test.js +5 -2
  8. package/dist-tsc/Warning.js +2 -2
  9. package/dist-tsc/data/LoaderResult.d.ts +8 -3
  10. package/dist-tsc/data/LoaderResult.d.ts.map +1 -1
  11. package/dist-tsc/data/LoaderResult.js +5 -0
  12. package/dist-tsc/data-hook-utils.d.ts +3 -7
  13. package/dist-tsc/data-hook-utils.d.ts.map +1 -1
  14. package/dist-tsc/data-hook-utils.js +97 -79
  15. package/dist-tsc/data-hooks.d.ts +12 -16
  16. package/dist-tsc/data-hooks.d.ts.map +1 -1
  17. package/dist-tsc/data-hooks.js +140 -127
  18. package/dist-tsc/errors/DataSourceFetchError.js +1 -1
  19. package/dist-tsc/hooks.d.ts +3 -7
  20. package/dist-tsc/hooks.d.ts.map +1 -1
  21. package/dist-tsc/hooks.js +9 -18
  22. package/dist-tsc/index.d.ts +2 -1
  23. package/dist-tsc/index.js +2 -1
  24. package/dist-tsc/mui-utils.d.ts +2 -0
  25. package/dist-tsc/mui-utils.d.ts.map +1 -0
  26. package/dist-tsc/mui-utils.js +44 -0
  27. package/dist-tsc/shared-plot-options/OptionSelect.js +1 -1
  28. package/dist-tsc/shared-plot-options/styles.js +1 -1
  29. package/dist-tsc/state/hooks.d.ts +27 -1
  30. package/dist-tsc/state/hooks.d.ts.map +1 -1
  31. package/dist-tsc/state/hooks.js +61 -21
  32. package/dist-tsc/view-config-utils.d.ts +1 -0
  33. package/dist-tsc/view-config-utils.d.ts.map +1 -1
  34. package/dist-tsc/view-config-utils.js +7 -1
  35. package/dist-tsc/view-config-utils.test.js +1 -0
  36. package/dist-tsc/vitessce-grid-layout/VitessceGridLayout.test.js +1 -1
  37. package/dist-tsc/vitessce-grid-layout/layout-utils.test.js +1 -0
  38. package/package.json +10 -9
  39. package/src/VitS.js +79 -43
  40. package/src/VitessceGrid.js +16 -10
  41. package/src/VitessceGrid.test.jsx +8 -2
  42. package/src/Warning.js +2 -2
  43. package/src/data/LoaderResult.js +5 -0
  44. package/src/data-hook-utils.js +116 -81
  45. package/src/data-hooks.js +148 -129
  46. package/src/errors/DataSourceFetchError.js +1 -1
  47. package/src/hooks.js +11 -21
  48. package/src/index.js +2 -0
  49. package/src/mui-utils.js +52 -0
  50. package/src/shared-plot-options/OptionSelect.js +1 -1
  51. package/src/shared-plot-options/styles.js +1 -1
  52. package/src/state/hooks.js +66 -21
  53. package/src/view-config-utils.js +8 -0
  54. package/src/view-config-utils.test.jsx +1 -0
  55. package/src/vitessce-grid-layout/VitessceGridLayout.test.jsx +1 -1
  56. package/src/vitessce-grid-layout/layout-utils.test.js +1 -0
  57. package/dist-tsc/schemas/config-0.1.0.schema.json +0 -84
  58. package/dist-tsc/schemas/config-1.0.0.schema.json +0 -733
  59. package/dist-tsc/schemas/config-1.0.1.schema.json +0 -909
  60. package/dist-tsc/schemas/config-1.0.10.schema.json +0 -969
  61. package/dist-tsc/schemas/config-1.0.11.schema.json +0 -990
  62. package/dist-tsc/schemas/config-1.0.12.schema.json +0 -997
  63. package/dist-tsc/schemas/config-1.0.13.schema.json +0 -1013
  64. package/dist-tsc/schemas/config-1.0.14.schema.json +0 -1048
  65. package/dist-tsc/schemas/config-1.0.15.schema.json +0 -1048
  66. package/dist-tsc/schemas/config-1.0.16.schema.json +0 -1089
  67. package/dist-tsc/schemas/config-1.0.2.schema.json +0 -910
  68. package/dist-tsc/schemas/config-1.0.3.schema.json +0 -910
  69. package/dist-tsc/schemas/config-1.0.4.schema.json +0 -949
  70. package/dist-tsc/schemas/config-1.0.5.schema.json +0 -949
  71. package/dist-tsc/schemas/config-1.0.6.schema.json +0 -949
  72. package/dist-tsc/schemas/config-1.0.7.schema.json +0 -949
  73. package/dist-tsc/schemas/config-1.0.8.schema.json +0 -965
  74. package/dist-tsc/schemas/config-1.0.9.schema.json +0 -965
  75. package/dist-tsc/schemas/obsSets.schema.d.ts +0 -179
  76. package/dist-tsc/schemas/obsSets.schema.d.ts.map +0 -1
  77. package/dist-tsc/schemas/obsSets.schema.js +0 -195
  78. package/dist-tsc/schemas/obsSetsTabular.schema.d.ts +0 -57
  79. package/dist-tsc/schemas/obsSetsTabular.schema.d.ts.map +0 -1
  80. package/dist-tsc/schemas/obsSetsTabular.schema.js +0 -38
  81. package/dist-tsc/schemas/raster.schema.d.ts +0 -239
  82. package/dist-tsc/schemas/raster.schema.d.ts.map +0 -1
  83. package/dist-tsc/schemas/raster.schema.js +0 -120
  84. package/src/schemas/config-0.1.0.schema.json +0 -85
  85. package/src/schemas/config-1.0.0.schema.json +0 -733
  86. package/src/schemas/config-1.0.1.schema.json +0 -909
  87. package/src/schemas/config-1.0.10.schema.json +0 -969
  88. package/src/schemas/config-1.0.11.schema.json +0 -990
  89. package/src/schemas/config-1.0.12.schema.json +0 -997
  90. package/src/schemas/config-1.0.13.schema.json +0 -1013
  91. package/src/schemas/config-1.0.14.schema.json +0 -1048
  92. package/src/schemas/config-1.0.15.schema.json +0 -1048
  93. package/src/schemas/config-1.0.16.schema.json +0 -1089
  94. package/src/schemas/config-1.0.2.schema.json +0 -911
  95. package/src/schemas/config-1.0.3.schema.json +0 -911
  96. package/src/schemas/config-1.0.4.schema.json +0 -949
  97. package/src/schemas/config-1.0.5.schema.json +0 -949
  98. package/src/schemas/config-1.0.6.schema.json +0 -950
  99. package/src/schemas/config-1.0.7.schema.json +0 -950
  100. package/src/schemas/config-1.0.8.schema.json +0 -966
  101. package/src/schemas/config-1.0.9.schema.json +0 -965
  102. package/src/schemas/obsSets.schema.js +0 -195
  103. package/src/schemas/obsSetsTabular.schema.js +0 -39
  104. package/src/schemas/raster.schema.js +0 -121
  105. package/src/schemas/schema-schema.sh +0 -34
@@ -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: null,
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: null,
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,3 +1,4 @@
1
+ export function logConfig(config: any, name: any): void;
1
2
  /**
2
3
  * Get a list of all unique scope names for a
3
4
  * particular coordination type, which exist in
@@ -1 +1 @@
1
- {"version":3,"file":"view-config-utils.d.ts","sourceRoot":"","sources":["../src/view-config-utils.js"],"names":[],"mappings":"AAQA;;;;;;;;GAQG;AACH,6DALW,MAAM,oBACN,MAAM,GAEJ,MAAM,EAAE,CAMpB;AAyND;;;;;;;;;;;;;;GAcG;AACH,mCANW,MAAM,kBACN,qBAAqB,qBACrB,wBAAwB,aACxB,gBAAgB;;;EAY1B"}
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';
@@ -1,3 +1,4 @@
1
+ import { describe, it, expect } from 'vitest';
1
2
  import { makeGridLayout, getMaxRows, resolveLayout, } from './layout-utils.js';
2
3
  describe('layout-utils.js', () => {
3
4
  describe('makeGridLayout', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/vit-s",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
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.0.0",
31
- "@vitessce/plugins": "3.0.0",
32
- "@vitessce/schemas": "3.0.0",
33
- "@vitessce/utils": "3.0.0"
31
+ "@vitessce/constants-internal": "3.0.1",
32
+ "@vitessce/plugins": "3.0.1",
33
+ "@vitessce/schemas": "3.0.1",
34
+ "@vitessce/utils": "3.0.1"
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.23.4",
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 -r ../../ --dir ."
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
- createGenerateClassName,
6
- } from '@material-ui/core/es/styles/index.js';
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 {undefined|string} props.uid A unique identifier for this Vitessce instance,
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, 'upgraded view config');
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
- }, [configUid, configVersion, pluginSpecificConfigSchema, warning]);
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, configUid, configOrWarning, onConfigChange]);
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
- <ViewConfigProvider createStore={createViewConfigStore}>
173
- <AuxiliaryProvider createStore={createAuxiliaryStore}>
174
- <VitessceGrid
175
- viewTypes={viewTypes}
176
- fileTypes={fileTypes}
177
- coordinationTypes={coordinationTypes}
178
- config={configOrWarning}
179
- rowHeight={rowHeight}
180
- height={height}
181
- theme={theme}
182
- isBounded={isBounded}
183
- />
184
- <CallbackPublisher
185
- onWarn={onWarn}
186
- onConfigChange={onConfigChange}
187
- onLoaderChange={onLoaderChange}
188
- validateOnConfigChange={validateOnConfigChange}
189
- pluginSpecificConfigSchema={pluginSpecificConfigSchema}
190
- />
191
- </AuxiliaryProvider>
192
- </ViewConfigProvider>
204
+ <QueryClientProvider client={queryClient}>
205
+ <ViewConfigProvider 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
  ) : (
@@ -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
- if (config) {
80
- setViewConfig(config);
81
- const loaders = createLoaders(
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
- setLoaders(loaders);
88
- } else {
89
- // No config found, so clear the loaders.
90
- setLoaders({});
93
+ newConfig = config;
91
94
  }
92
- }, [config, setViewConfig, setLoaders, fileTypes, coordinationTypes]);
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={createViewConfigStore}>
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
- card: {
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.card}>
53
+ <div className={classes.warningCard}>
54
54
  <h1>{title}</h1>
55
55
  {preformatted ? (<pre>{preformatted}</pre>) : null}
56
56
  <p>{unformatted}</p>
@@ -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;