@vitessce/vit-s 2.0.3 → 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 (215) hide show
  1. package/dist/index.js +44072 -0
  2. package/dist-tsc/CallbackPublisher.d.ts +18 -0
  3. package/dist-tsc/CallbackPublisher.d.ts.map +1 -0
  4. package/dist-tsc/CallbackPublisher.js +62 -0
  5. package/dist-tsc/LoadingIndicator.d.ts +2 -0
  6. package/dist-tsc/LoadingIndicator.d.ts.map +1 -0
  7. package/dist-tsc/LoadingIndicator.js +26 -0
  8. package/dist-tsc/TitleInfo.d.ts +2 -0
  9. package/dist-tsc/TitleInfo.d.ts.map +1 -0
  10. package/dist-tsc/TitleInfo.js +74 -0
  11. package/dist-tsc/VitS.d.ts +50 -0
  12. package/dist-tsc/VitS.d.ts.map +1 -0
  13. package/dist-tsc/VitS.js +139 -0
  14. package/dist-tsc/VitessceGrid.d.ts +23 -0
  15. package/dist-tsc/VitessceGrid.d.ts.map +1 -0
  16. package/dist-tsc/VitessceGrid.js +63 -0
  17. package/dist-tsc/VitessceGrid.test.d.ts +2 -0
  18. package/dist-tsc/VitessceGrid.test.d.ts.map +1 -0
  19. package/dist-tsc/VitessceGrid.test.js +43 -0
  20. package/dist-tsc/Warning.d.ts +2 -0
  21. package/dist-tsc/Warning.d.ts.map +1 -0
  22. package/dist-tsc/Warning.js +45 -0
  23. package/dist-tsc/classNames.d.ts +8 -0
  24. package/dist-tsc/classNames.d.ts.map +1 -0
  25. package/dist-tsc/classNames.js +8 -0
  26. package/dist-tsc/data/AbstractLoader.d.ts +22 -0
  27. package/dist-tsc/data/AbstractLoader.d.ts.map +1 -0
  28. package/dist-tsc/data/AbstractLoader.js +18 -0
  29. package/dist-tsc/data/AbstractTwoStepLoader.d.ts +6 -0
  30. package/dist-tsc/data/AbstractTwoStepLoader.d.ts.map +1 -0
  31. package/dist-tsc/data/AbstractTwoStepLoader.js +7 -0
  32. package/dist-tsc/data/LoaderResult.d.ts +12 -0
  33. package/dist-tsc/data/LoaderResult.d.ts.map +1 -0
  34. package/dist-tsc/data/LoaderResult.js +12 -0
  35. package/dist-tsc/data/index.d.ts +4 -0
  36. package/dist-tsc/data/index.d.ts.map +1 -0
  37. package/dist-tsc/data/index.js +3 -0
  38. package/dist-tsc/data/loader-registry.d.ts +9 -0
  39. package/dist-tsc/data/loader-registry.d.ts.map +1 -0
  40. package/dist-tsc/data/loader-registry.js +27 -0
  41. package/dist-tsc/data-hook-utils.d.ts +65 -0
  42. package/dist-tsc/data-hook-utils.d.ts.map +1 -0
  43. package/dist-tsc/data-hook-utils.js +191 -0
  44. package/dist-tsc/data-hooks.d.ts +73 -0
  45. package/dist-tsc/data-hooks.d.ts.map +1 -0
  46. package/dist-tsc/data-hooks.js +254 -0
  47. package/dist-tsc/errors/AbstractLoaderError.d.ts +10 -0
  48. package/dist-tsc/errors/AbstractLoaderError.d.ts.map +1 -0
  49. package/dist-tsc/errors/AbstractLoaderError.js +13 -0
  50. package/dist-tsc/errors/DataSourceFetchError.d.ts +8 -0
  51. package/dist-tsc/errors/DataSourceFetchError.d.ts.map +1 -0
  52. package/dist-tsc/errors/DataSourceFetchError.js +13 -0
  53. package/dist-tsc/errors/DatasetNotFoundError.d.ts +6 -0
  54. package/dist-tsc/errors/DatasetNotFoundError.d.ts.map +1 -0
  55. package/dist-tsc/errors/DatasetNotFoundError.js +17 -0
  56. package/dist-tsc/errors/LoaderNotFoundError.d.ts +10 -0
  57. package/dist-tsc/errors/LoaderNotFoundError.d.ts.map +1 -0
  58. package/dist-tsc/errors/LoaderNotFoundError.js +17 -0
  59. package/dist-tsc/errors/LoaderValidationError.d.ts +10 -0
  60. package/dist-tsc/errors/LoaderValidationError.d.ts.map +1 -0
  61. package/dist-tsc/errors/LoaderValidationError.js +15 -0
  62. package/dist-tsc/errors/index.d.ts +6 -0
  63. package/dist-tsc/errors/index.d.ts.map +1 -0
  64. package/dist-tsc/errors/index.js +5 -0
  65. package/dist-tsc/hooks.d.ts +76 -0
  66. package/dist-tsc/hooks.d.ts.map +1 -0
  67. package/dist-tsc/hooks.js +262 -0
  68. package/dist-tsc/index.d.ts +12 -0
  69. package/dist-tsc/index.d.ts.map +1 -0
  70. package/dist-tsc/index.js +11 -17
  71. package/dist-tsc/mui-utils.d.ts +2 -0
  72. package/dist-tsc/mui-utils.d.ts.map +1 -0
  73. package/dist-tsc/mui-utils.js +44 -0
  74. package/dist-tsc/plugins.test.fixtures.d.ts +61 -0
  75. package/dist-tsc/plugins.test.fixtures.d.ts.map +1 -0
  76. package/dist-tsc/plugins.test.fixtures.js +76 -0
  77. package/dist-tsc/shared-mui/components.d.ts +2 -0
  78. package/dist-tsc/shared-mui/components.d.ts.map +1 -0
  79. package/dist-tsc/shared-mui/components.js +29 -0
  80. package/dist-tsc/shared-mui/container.d.ts +2 -0
  81. package/dist-tsc/shared-mui/container.d.ts.map +1 -0
  82. package/dist-tsc/shared-mui/container.js +211 -0
  83. package/dist-tsc/shared-mui/styles.d.ts +5 -0
  84. package/dist-tsc/shared-mui/styles.d.ts.map +1 -0
  85. package/dist-tsc/shared-mui/styles.js +109 -0
  86. package/dist-tsc/shared-plot-options/CellColorEncodingOption.d.ts +2 -0
  87. package/dist-tsc/shared-plot-options/CellColorEncodingOption.d.ts.map +1 -0
  88. package/dist-tsc/shared-plot-options/CellColorEncodingOption.js +17 -0
  89. package/dist-tsc/shared-plot-options/OptionSelect.d.ts +2 -0
  90. package/dist-tsc/shared-plot-options/OptionSelect.d.ts.map +1 -0
  91. package/dist-tsc/shared-plot-options/OptionSelect.js +12 -0
  92. package/dist-tsc/shared-plot-options/OptionsContainer.d.ts +2 -0
  93. package/dist-tsc/shared-plot-options/OptionsContainer.d.ts.map +1 -0
  94. package/dist-tsc/shared-plot-options/OptionsContainer.js +9 -0
  95. package/dist-tsc/shared-plot-options/index.d.ts +5 -0
  96. package/dist-tsc/shared-plot-options/index.d.ts.map +1 -0
  97. package/dist-tsc/shared-plot-options/index.js +4 -0
  98. package/dist-tsc/shared-plot-options/styles.d.ts +2 -0
  99. package/dist-tsc/shared-plot-options/styles.d.ts.map +1 -0
  100. package/dist-tsc/shared-plot-options/styles.js +48 -0
  101. package/dist-tsc/state/hooks.d.ts +242 -0
  102. package/dist-tsc/state/hooks.d.ts.map +1 -0
  103. package/dist-tsc/state/hooks.js +615 -0
  104. package/dist-tsc/title-styles.d.ts +2 -0
  105. package/dist-tsc/title-styles.d.ts.map +1 -0
  106. package/dist-tsc/title-styles.js +72 -0
  107. package/dist-tsc/view-config-utils.d.ts +31 -0
  108. package/dist-tsc/view-config-utils.d.ts.map +1 -0
  109. package/dist-tsc/view-config-utils.js +240 -0
  110. package/dist-tsc/view-config-utils.test.d.ts +2 -0
  111. package/dist-tsc/view-config-utils.test.d.ts.map +1 -0
  112. package/dist-tsc/view-config-utils.test.fixtures.d.ts +1049 -0
  113. package/dist-tsc/view-config-utils.test.fixtures.d.ts.map +1 -0
  114. package/dist-tsc/view-config-utils.test.fixtures.js +883 -0
  115. package/dist-tsc/view-config-utils.test.js +210 -0
  116. package/dist-tsc/vitessce-grid-layout/VitessceGridLayout.d.ts +8 -0
  117. package/dist-tsc/vitessce-grid-layout/VitessceGridLayout.d.ts.map +1 -0
  118. package/dist-tsc/vitessce-grid-layout/VitessceGridLayout.js +114 -0
  119. package/dist-tsc/vitessce-grid-layout/VitessceGridLayout.test.d.ts +2 -0
  120. package/dist-tsc/vitessce-grid-layout/VitessceGridLayout.test.d.ts.map +1 -0
  121. package/dist-tsc/vitessce-grid-layout/VitessceGridLayout.test.js +48 -0
  122. package/dist-tsc/vitessce-grid-layout/index.d.ts +2 -0
  123. package/dist-tsc/vitessce-grid-layout/index.d.ts.map +1 -0
  124. package/dist-tsc/vitessce-grid-layout/index.js +1 -0
  125. package/dist-tsc/vitessce-grid-layout/layout-utils.d.ts +15 -0
  126. package/dist-tsc/vitessce-grid-layout/layout-utils.d.ts.map +1 -0
  127. package/dist-tsc/vitessce-grid-layout/layout-utils.js +61 -0
  128. package/dist-tsc/vitessce-grid-layout/layout-utils.test.d.ts +2 -0
  129. package/dist-tsc/vitessce-grid-layout/layout-utils.test.d.ts.map +1 -0
  130. package/dist-tsc/vitessce-grid-layout/layout-utils.test.js +149 -0
  131. package/dist-tsc/vitessce-grid-utils.d.ts +13 -0
  132. package/dist-tsc/vitessce-grid-utils.d.ts.map +1 -0
  133. package/dist-tsc/vitessce-grid-utils.js +141 -0
  134. package/package.json +25 -11
  135. package/src/CallbackPublisher.js +7 -11
  136. package/src/LoadingIndicator.js +1 -2
  137. package/src/TitleInfo.js +12 -85
  138. package/src/VitS.js +143 -85
  139. package/src/VitessceGrid.js +35 -21
  140. package/src/VitessceGrid.test.jsx +23 -8
  141. package/src/Warning.js +4 -4
  142. package/src/data/AbstractLoader.js +2 -25
  143. package/src/data/AbstractTwoStepLoader.js +1 -1
  144. package/src/data/LoaderResult.js +5 -0
  145. package/src/data/index.js +3 -3
  146. package/src/data/loader-registry.js +22 -13
  147. package/src/data-hook-utils.js +119 -86
  148. package/src/data-hooks.js +150 -131
  149. package/src/errors/DataSourceFetchError.js +2 -2
  150. package/src/errors/DatasetNotFoundError.js +1 -1
  151. package/src/errors/LoaderNotFoundError.js +1 -1
  152. package/src/errors/LoaderValidationError.js +1 -1
  153. package/src/errors/index.js +5 -6
  154. package/src/hooks.js +81 -26
  155. package/src/index.js +23 -46
  156. package/src/mui-utils.js +52 -0
  157. package/src/shared-mui/components.js +10 -8
  158. package/src/shared-mui/container.js +1 -1
  159. package/src/shared-mui/styles.js +5 -2
  160. package/src/shared-plot-options/CellColorEncodingOption.js +3 -4
  161. package/src/shared-plot-options/OptionSelect.js +3 -3
  162. package/src/shared-plot-options/OptionsContainer.js +2 -5
  163. package/src/shared-plot-options/index.js +4 -4
  164. package/src/shared-plot-options/styles.js +2 -2
  165. package/src/state/hooks.js +75 -23
  166. package/src/title-styles.js +73 -0
  167. package/src/view-config-utils.js +54 -98
  168. package/src/view-config-utils.test.jsx +223 -0
  169. package/src/vitessce-grid-layout/VitessceGridLayout.js +51 -30
  170. package/src/vitessce-grid-layout/VitessceGridLayout.test.jsx +17 -9
  171. package/src/vitessce-grid-layout/index.js +1 -1
  172. package/src/vitessce-grid-layout/layout-utils.js +1 -1
  173. package/src/vitessce-grid-layout/layout-utils.test.js +2 -1
  174. package/src/vitessce-grid-utils.js +27 -15
  175. package/dist/index.mjs +0 -84801
  176. package/src/component-registry.js +0 -21
  177. package/src/errors/OptionsValidationError.js +0 -25
  178. package/src/file-options-schemas-legacy.js +0 -139
  179. package/src/file-options-schemas-legacy.test.js +0 -138
  180. package/src/file-options-schemas.js +0 -327
  181. package/src/file-options-schemas.test.js +0 -26
  182. package/src/joint-file-types-legacy.js +0 -298
  183. package/src/joint-file-types-legacy.test.js +0 -412
  184. package/src/joint-file-types.js +0 -171
  185. package/src/joint-file-types.test.js +0 -144
  186. package/src/json-schemas.js +0 -9
  187. package/src/plugins.js +0 -186
  188. package/src/plugins.test.js +0 -42
  189. package/src/schemas/config-0.1.0.schema.json +0 -85
  190. package/src/schemas/config-1.0.0.schema.json +0 -733
  191. package/src/schemas/config-1.0.1.schema.json +0 -909
  192. package/src/schemas/config-1.0.10.schema.json +0 -969
  193. package/src/schemas/config-1.0.11.schema.json +0 -990
  194. package/src/schemas/config-1.0.12.schema.json +0 -997
  195. package/src/schemas/config-1.0.13.schema.json +0 -1013
  196. package/src/schemas/config-1.0.14.schema.json +0 -1048
  197. package/src/schemas/config-1.0.15.schema.json +0 -1048
  198. package/src/schemas/config-1.0.16.schema.json +0 -1069
  199. package/src/schemas/config-1.0.16.schema.test.js +0 -32
  200. package/src/schemas/config-1.0.2.schema.json +0 -911
  201. package/src/schemas/config-1.0.3.schema.json +0 -911
  202. package/src/schemas/config-1.0.4.schema.json +0 -949
  203. package/src/schemas/config-1.0.5.schema.json +0 -949
  204. package/src/schemas/config-1.0.6.schema.json +0 -950
  205. package/src/schemas/config-1.0.7.schema.json +0 -950
  206. package/src/schemas/config-1.0.8.schema.json +0 -966
  207. package/src/schemas/config-1.0.9.schema.json +0 -965
  208. package/src/schemas/obsSets.schema.js +0 -195
  209. package/src/schemas/obsSetsTabular.schema.js +0 -39
  210. package/src/schemas/raster.schema.js +0 -121
  211. package/src/schemas/schema-schema.sh +0 -34
  212. package/src/schemas/schema.test-old.js +0 -49
  213. package/src/view-config-upgraders.js +0 -628
  214. package/src/view-config-utils.test.js +0 -95
  215. package/src/view-config-versions.js +0 -68
package/src/VitS.js CHANGED
@@ -1,36 +1,32 @@
1
- /* eslint-disable camelcase */
2
- import React, { useEffect, useMemo } from 'react';
1
+ import React, { useEffect, useMemo, useCallback } from 'react';
3
2
  import {
4
3
  ThemeProvider,
5
4
  StylesProvider,
6
- createGenerateClassName,
7
- } from '@material-ui/core/styles';
8
- import isEqual from 'lodash/isEqual';
9
- import { META_VERSION } from '@vitessce/constants-internal';
10
- import { muiTheme } from './shared-mui/styles';
5
+ } from '@material-ui/core';
6
+ import {
7
+ QueryClient,
8
+ QueryClientProvider,
9
+ } from '@tanstack/react-query';
10
+ import { isEqual } from 'lodash-es';
11
+ import { buildConfigSchema, latestConfigSchema } from '@vitessce/schemas';
12
+ import { muiTheme } from './shared-mui/styles.js';
11
13
  import {
12
14
  ViewConfigProvider,
13
15
  createViewConfigStore,
14
16
  AuxiliaryProvider,
15
17
  createAuxiliaryStore,
16
- } from './state/hooks';
18
+ } from './state/hooks.js';
17
19
 
18
- import VitessceGrid from './VitessceGrid';
19
- import { Warning } from './Warning';
20
- import CallbackPublisher from './CallbackPublisher';
21
- import { getComponent } from './component-registry';
20
+ import VitessceGrid from './VitessceGrid.js';
21
+ import { Warning } from './Warning.js';
22
+ import CallbackPublisher from './CallbackPublisher.js';
22
23
  import {
23
- checkTypes,
24
24
  initialize,
25
- upgradeAndValidate,
26
- } from './view-config-utils';
25
+ logConfig,
26
+ } from './view-config-utils.js';
27
+ import { createLoaders } from './vitessce-grid-utils.js';
28
+ import { createGenerateClassName } from './mui-utils.js';
27
29
 
28
- function logConfig(config, name) {
29
- console.groupCollapsed(`🚄 Vitessce (${META_VERSION.version}) ${name}`);
30
- console.info(`data:,${JSON.stringify(config)}`);
31
- console.info(JSON.stringify(config, null, 2));
32
- console.groupEnd();
33
- }
34
30
 
35
31
  /**
36
32
  * The Vitessce component.
@@ -38,7 +34,7 @@ function logConfig(config, name) {
38
34
  * @param {object} props.config A Vitessce view config.
39
35
  * If the config is valid, the VitessceGrid will be rendered as a child.
40
36
  * If the config is invalid, a Warning will be rendered instead.
41
- * @param {number} props.rowHeight Row height for grid layout. Optional.
37
+ * @param {undefined|number} props.rowHeight Row height for grid layout. Optional.
42
38
  * @param {number} props.height Total height for grid layout. Optional.
43
39
  * @param {string} props.theme The theme, used for styling as
44
40
  * light or dark. Optional. By default, "dark"
@@ -47,12 +43,22 @@ function logConfig(config, name) {
47
43
  * updates. Optional.
48
44
  * @param {function} props.onLoaderChange A callback for loader
49
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.
50
50
  * @param {boolean} props.validateOnConfigChange Whether to validate
51
51
  * against the view config schema when publishing changes. Use for debugging
52
52
  * purposes, as this may have a performance impact. By default, false.
53
- * @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,
54
54
  * for the purpose of avoiding CSS autogenerated class name conflicts. Must be valid as part
55
55
  * of a CSS class name string.
56
+ * @param {array} props.viewTypes Plugin view types.
57
+ * @param {array} props.fileTypes Plugin file types.
58
+ * @param {array} props.jointFileTypes Plugin joint file types.
59
+ * @param {array} props.coordinationTypes Plugin coordination types.
60
+ * @param {null|object} props.warning A warning to render within the Vitessce grid,
61
+ * provided by the parent.
56
62
  */
57
63
  export function VitS(props) {
58
64
  const {
@@ -63,18 +69,43 @@ export function VitS(props) {
63
69
  onWarn,
64
70
  onConfigChange,
65
71
  onLoaderChange,
72
+ validateConfig = true,
66
73
  validateOnConfigChange = false,
67
- onConfigUpgrade,
68
74
  isBounded = false,
69
- uid,
75
+ uid = null,
76
+ viewTypes: viewTypesProp,
77
+ fileTypes: fileTypesProp,
78
+ jointFileTypes: jointFileTypesProp,
79
+ coordinationTypes: coordinationTypesProp,
80
+ warning,
70
81
  } = props;
71
82
 
72
- const generateClassName = useMemo(() => createGenerateClassName({
73
- disableGlobal: false, // Class names need to be deterministic
74
- // Avoid conflicts with portal-ui MUI class names,
75
- // and allow setting a UID.
76
- productionPrefix: (uid ? `vit${uid}` : 'vit'),
77
- }), [uid]);
83
+ const viewTypes = useMemo(() => (viewTypesProp || []), [viewTypesProp]);
84
+ const fileTypes = useMemo(() => (fileTypesProp || []), [fileTypesProp]);
85
+ const jointFileTypes = useMemo(
86
+ () => (jointFileTypesProp || []),
87
+ [jointFileTypesProp],
88
+ );
89
+ const coordinationTypes = useMemo(
90
+ () => (coordinationTypesProp || []),
91
+ [coordinationTypesProp],
92
+ );
93
+
94
+ const generateClassName = useMemo(() => createGenerateClassName(uid), [uid]);
95
+
96
+ const configVersion = config?.version;
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
+
103
+ const pluginSpecificConfigSchema = useMemo(() => buildConfigSchema(
104
+ fileTypes,
105
+ jointFileTypes,
106
+ coordinationTypes,
107
+ viewTypes,
108
+ ), [viewTypes, fileTypes, jointFileTypes, coordinationTypes]);
78
109
 
79
110
  // Process the view config and memoize the result:
80
111
  // - Validate.
@@ -82,46 +113,35 @@ export function VitS(props) {
82
113
  // - Validate after upgrade, if legacy schema.
83
114
  // - Initialize (based on initStrategy).
84
115
  const [configOrWarning, success] = useMemo(() => {
85
- // If the config value is undefined, show a warning message.
86
- if (!config) {
87
- return [
88
- {
89
- title: 'No such dataset',
90
- unformatted: 'The dataset configuration could not be found.',
91
- },
92
- false,
93
- ];
94
- }
95
- // If the view config is missing a version, show a warning message.
96
- if (!config.version) {
97
- return [
98
- {
99
- title: 'Missing version',
100
- unformatted:
101
- 'The dataset configuration is missing a version, preventing validation.',
102
- },
103
- false,
104
- ];
116
+ if (warning) {
117
+ return [warning, false];
105
118
  }
106
119
  logConfig(config, 'input view config');
107
- // Check if this is a "legacy" view config.
108
- const [upgradedConfig, upgradeSuccess] = upgradeAndValidate(
109
- config,
110
- onConfigUpgrade,
111
- );
112
- if (upgradeSuccess) {
113
- logConfig(upgradedConfig, 'upgraded view config');
120
+ if (!validateConfig) {
121
+ return [config, true];
122
+ }
123
+ const result = latestConfigSchema.safeParse(config);
124
+ if (result.success) {
125
+ const upgradedConfig = result.data;
126
+ logConfig(upgradedConfig, 'parsed view config');
127
+ // Perform second round of parsing against plugin-specific config schema.
128
+ const pluginSpecificResult = pluginSpecificConfigSchema.safeParse(upgradedConfig);
114
129
  // Initialize the view config according to the initStrategy.
115
- const [typeCheckSuccess, typeCheckMessage] = checkTypes(upgradedConfig);
116
- if (typeCheckSuccess) {
130
+ if (pluginSpecificResult.success) {
117
131
  try {
118
- const initializedConfig = initialize(upgradedConfig);
132
+ const upgradedConfigWithValidPlugins = pluginSpecificResult.data;
133
+ const initializedConfig = initialize(
134
+ upgradedConfigWithValidPlugins,
135
+ jointFileTypes,
136
+ coordinationTypes,
137
+ viewTypes,
138
+ );
119
139
  logConfig(initializedConfig, 'initialized view config');
120
140
  return [initializedConfig, true];
121
141
  } catch (e) {
122
142
  return [
123
143
  {
124
- title: 'View config initialization failed.',
144
+ title: 'Config initialization failed.',
125
145
  unformatted: e.message,
126
146
  },
127
147
  false,
@@ -130,14 +150,28 @@ export function VitS(props) {
130
150
  }
131
151
  return [
132
152
  {
133
- title: 'View config checks failed.',
134
- unformatted: typeCheckMessage,
153
+ title: 'Config validation failed on second pass.',
154
+ unformatted: pluginSpecificResult.error.message,
135
155
  },
136
156
  false,
137
157
  ];
138
158
  }
139
- return [upgradedConfig, false];
140
- }, [config, onConfigUpgrade]);
159
+ return [{
160
+ title: 'Config validation failed on first pass.',
161
+ unformatted: result.error.message,
162
+ }, result.success];
163
+ // eslint-disable-next-line react-hooks/exhaustive-deps
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]);
141
175
 
142
176
  // Emit the upgraded/initialized view config
143
177
  // to onConfigChange if necessary.
@@ -145,29 +179,53 @@ export function VitS(props) {
145
179
  if (success && !isEqual(configOrWarning, config) && onConfigChange) {
146
180
  onConfigChange(configOrWarning);
147
181
  }
148
- }, [success, config, configOrWarning, onConfigChange]);
182
+ // eslint-disable-next-line react-hooks/exhaustive-deps
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]);
149
200
 
150
201
  return success ? (
151
202
  <StylesProvider generateClassName={generateClassName}>
152
203
  <ThemeProvider theme={muiTheme[theme]}>
153
- <ViewConfigProvider createStore={createViewConfigStore}>
154
- <AuxiliaryProvider createStore={createAuxiliaryStore}>
155
- <VitessceGrid
156
- config={configOrWarning}
157
- getComponent={getComponent}
158
- rowHeight={rowHeight}
159
- height={height}
160
- theme={theme}
161
- isBounded={isBounded}
162
- />
163
- <CallbackPublisher
164
- onWarn={onWarn}
165
- onConfigChange={onConfigChange}
166
- onLoaderChange={onLoaderChange}
167
- validateOnConfigChange={validateOnConfigChange}
168
- />
169
- </AuxiliaryProvider>
170
- </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>
171
229
  </ThemeProvider>
172
230
  </StylesProvider>
173
231
  ) : (
@@ -3,9 +3,9 @@ import React, {
3
3
  useCallback,
4
4
  } from 'react';
5
5
  import clsx from 'clsx';
6
- import { VITESSCE_CONTAINER } from './classNames';
7
- import { VitessceGridLayout } from './vitessce-grid-layout';
8
- import { useRowHeight, createLoaders } from './vitessce-grid-utils';
6
+ import { VITESSCE_CONTAINER } from './classNames.js';
7
+ import { VitessceGridLayout } from './vitessce-grid-layout/index.js';
8
+ import { useRowHeight, createLoaders } from './vitessce-grid-utils.js';
9
9
  import {
10
10
  useViewConfigStoreApi,
11
11
  useSetViewConfig,
@@ -14,12 +14,12 @@ import {
14
14
  useRemoveComponent,
15
15
  useChangeLayout,
16
16
  useLayout,
17
- } from './state/hooks';
17
+ } from './state/hooks.js';
18
18
  import {
19
19
  useClosestVitessceContainerSize,
20
- } from './hooks';
21
- import { useVitessceContainerStyles } from './shared-mui/container';
22
- import { useTitleStyles } from './TitleInfo';
20
+ } from './hooks.js';
21
+ import { useVitessceContainerStyles } from './shared-mui/container.js';
22
+ import { useTitleStyles } from './title-styles.js';
23
23
 
24
24
  const padding = 10;
25
25
  const margin = 5;
@@ -29,20 +29,25 @@ const margin = 5;
29
29
  * @param {object} props
30
30
  * @param {number} props.rowHeight The height of each grid row. Optional.
31
31
  * @param {object} props.config The view config.
32
- * @param {function} props.getComponent A function that maps component names to their
33
- * React counterparts.
34
32
  * @param {string} props.theme The theme name.
35
33
  * @param {number} props.height Total height for grid. Optional.
36
34
  * @param {function} props.onWarn A callback for warning messages. Optional.
35
+ * @param {PluginViewType[]} props.viewTypes
36
+ * @param {PluginFileType[]} props.fileTypes
37
+ * @param {PluginCoordinationType[]} props.coordinationTypes
37
38
  */
38
39
  export default function VitessceGrid(props) {
39
40
  const {
41
+ success,
42
+ configKey,
40
43
  rowHeight: initialRowHeight,
41
44
  config,
42
- getComponent,
43
45
  theme,
44
46
  height,
45
47
  isBounded,
48
+ viewTypes,
49
+ fileTypes,
50
+ coordinationTypes,
46
51
  } = props;
47
52
 
48
53
  const [rowHeight, containerRef] = useRowHeight(config, initialRowHeight, height, margin, padding);
@@ -72,29 +77,38 @@ export default function VitessceGrid(props) {
72
77
  }, [changeLayout, componentWidth]);
73
78
 
74
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.
75
83
  useEffect(() => {
76
- if (config) {
77
- setViewConfig(config);
78
- const loaders = createLoaders(config.datasets, config.description);
79
- setLoaders(loaders);
80
- } else {
81
- // No config found, so clear the loaders.
82
- setLoaders({});
84
+ let newLoaders = null;
85
+ let newConfig = null;
86
+ if (success) {
87
+ newLoaders = createLoaders(
88
+ config.datasets,
89
+ config.description,
90
+ fileTypes,
91
+ coordinationTypes,
92
+ );
93
+ newConfig = config;
83
94
  }
84
- }, [config, setViewConfig, setLoaders]);
95
+ setViewConfig(newConfig);
96
+ setLoaders(newLoaders);
97
+ // eslint-disable-next-line react-hooks/exhaustive-deps
98
+ }, [success, configKey]);
85
99
 
86
100
  return (
87
101
  <div
88
102
  ref={containerRef}
89
103
  className={clsx(VITESSCE_CONTAINER, classes.vitessceContainer)}
90
104
  >
91
- {layout && (
105
+ {layout ? (
92
106
  <VitessceGridLayout
93
107
  layout={layout}
94
108
  height={height}
95
109
  rowHeight={rowHeight}
96
110
  theme={theme}
97
- getComponent={getComponent}
111
+ viewTypes={viewTypes}
98
112
  draggableHandle={titleClasses.title}
99
113
  margin={margin}
100
114
  padding={padding}
@@ -104,7 +118,7 @@ export default function VitessceGrid(props) {
104
118
  onResize={onResize}
105
119
  onResizeStop={onResize}
106
120
  />
107
- )}
121
+ ) : null}
108
122
  </div>
109
123
  );
110
124
  }
@@ -1,15 +1,17 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
1
2
  import '@testing-library/jest-dom';
2
- import { cleanup, render, screen } from '@testing-library/react'
3
- import { afterEach } from 'vitest'
3
+ import { cleanup, render, screen } from '@testing-library/react';
4
4
 
5
- import VitessceGrid from './VitessceGrid';
5
+ import React from 'react';
6
+ import { PluginViewType } from '@vitessce/plugins';
7
+ import VitessceGrid from './VitessceGrid.js';
6
8
  import {
7
9
  ViewConfigProvider, createViewConfigStore,
8
10
  AuxiliaryProvider, createAuxiliaryStore,
9
- } from './state/hooks';
11
+ } from './state/hooks.js';
10
12
 
11
13
  afterEach(() => {
12
- cleanup()
14
+ cleanup();
13
15
  });
14
16
 
15
17
  describe('VitessceGrid.js', () => {
@@ -31,14 +33,27 @@ describe('VitessceGrid.js', () => {
31
33
  ],
32
34
  };
33
35
 
36
+ function createViewConfigStoreClosure() {
37
+ return createViewConfigStore(null, config);
38
+ }
39
+
34
40
  function FakeComponent() {
35
41
  return <p>FakeComponent!</p>;
36
42
  }
37
- const getComponent = () => FakeComponent;
43
+ const viewTypes = [new PluginViewType('FakeComponent', FakeComponent, [])];
44
+ const fileTypes = [];
45
+ const coordinationTypes = [];
38
46
  render(
39
- <ViewConfigProvider createStore={createViewConfigStore}>
47
+ <ViewConfigProvider createStore={createViewConfigStoreClosure}>
40
48
  <AuxiliaryProvider createStore={createAuxiliaryStore}>
41
- <VitessceGrid config={config} getComponent={getComponent} />
49
+ <VitessceGrid
50
+ success
51
+ configKey={null}
52
+ config={config}
53
+ viewTypes={viewTypes}
54
+ fileTypes={fileTypes}
55
+ coordinationTypes={coordinationTypes}
56
+ />
42
57
  </AuxiliaryProvider>
43
58
  </ViewConfigProvider>,
44
59
  );
package/src/Warning.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import clsx from 'clsx';
2
- import { makeStyles } from '@material-ui/core/styles';
3
- import { VITESSCE_CONTAINER } from './classNames';
2
+ import { makeStyles } from '@material-ui/core';
3
+ import { VITESSCE_CONTAINER } from './classNames.js';
4
4
 
5
5
  const useStyles = makeStyles(theme => ({
6
6
  warningLayout: {
@@ -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,7 +1,3 @@
1
- import Ajv from 'ajv';
2
- import { OptionsValidationError } from '../errors/index';
3
- import { emptySchema } from '../file-options-schemas';
4
-
5
1
  /**
6
2
  * A loader ancestor class containing a default constructor
7
3
  * and a stub for the required load() method.
@@ -18,29 +14,10 @@ export default class AbstractLoader {
18
14
  this.requestInit = requestInit;
19
15
  this.options = options;
20
16
  this.coordinationValues = coordinationValues;
21
- this.optionsSchema = emptySchema;
22
- }
23
-
24
- validateOptions() {
25
- const { optionsSchema, options } = this;
26
- const validate = new Ajv().compile(optionsSchema);
27
- const valid = validate(options || null);
28
- if (!valid) {
29
- return [false, validate.errors];
30
- }
31
- return [true, null];
32
17
  }
33
18
 
19
+ // eslint-disable-next-line class-methods-use-this
34
20
  load() {
35
- const {
36
- fileType, url, options,
37
- } = this;
38
- const [optionsAreValid, optionsFailureReason] = this.validateOptions();
39
- if (!optionsAreValid) {
40
- return Promise.reject(
41
- new OptionsValidationError(fileType, url, options, optionsFailureReason),
42
- );
43
- }
44
- return Promise.resolve(optionsAreValid);
21
+ return Promise.resolve(true);
45
22
  }
46
23
  }
@@ -1,4 +1,4 @@
1
- import AbstractLoader from './AbstractLoader';
1
+ import AbstractLoader from './AbstractLoader.js';
2
2
 
3
3
  export default class AbstractTwoStepLoader extends AbstractLoader {
4
4
  constructor(dataSource, params) {
@@ -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;
package/src/data/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { default as AbstractLoader } from './AbstractLoader';
2
- export { default as AbstractTwoStepLoader } from './AbstractTwoStepLoader';
3
- export { default as LoaderResult } from './LoaderResult';
1
+ export { default as AbstractLoader } from './AbstractLoader.js';
2
+ export { default as AbstractTwoStepLoader } from './AbstractTwoStepLoader.js';
3
+ export { default as LoaderResult } from './LoaderResult.js';
@@ -1,19 +1,28 @@
1
- import {
2
- getLoaderClassesForPluginFileType,
3
- } from '../plugins';
4
-
5
- export const fileTypeToLoaderAndSource = {};
6
-
7
- export function getSourceAndLoaderFromFileType(type) {
8
- if (fileTypeToLoaderAndSource[type]) {
9
- return fileTypeToLoaderAndSource[type];
10
- }
11
- const pluginFileType = getLoaderClassesForPluginFileType(type);
12
- if (pluginFileType) {
13
- return pluginFileType;
1
+ /**
2
+ * Get the data source and data loader class.
3
+ * @param {string} type The fileType name.
4
+ * @param {PluginFileType[]} fileTypes The array of file types.
5
+ * @returns {array} [dataSourceClass, dataLoaderClass].
6
+ */
7
+ export function getSourceAndLoaderFromFileType(type, fileTypes) {
8
+ if (Array.isArray(fileTypes)) {
9
+ const matchingFileType = fileTypes.find(ft => ft.name === type);
10
+ if (matchingFileType) {
11
+ return matchingFileType.getSourceAndLoader();
12
+ }
14
13
  }
15
14
  // Fallback to JSON.
16
15
  // TODO(monorepo)
17
16
  // return [JsonSource, JsonLoader];
18
17
  return [null, null];
19
18
  }
19
+
20
+ export function getDataTypeFromFileType(type, fileTypes) {
21
+ if (Array.isArray(fileTypes)) {
22
+ const matchingFileType = fileTypes.find(ft => ft.name === type);
23
+ if (matchingFileType) {
24
+ return matchingFileType.dataType;
25
+ }
26
+ }
27
+ return null;
28
+ }