@vitessce/vit-s 2.0.3-beta.0 → 3.0.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.
Files changed (277) hide show
  1. package/dist/index.js +41348 -18
  2. package/dist-tsc/CallbackPublisher.d.ts +18 -0
  3. package/dist-tsc/CallbackPublisher.d.ts.map +1 -0
  4. package/{dist → dist-tsc}/CallbackPublisher.js +6 -12
  5. package/dist-tsc/LoadingIndicator.d.ts +2 -0
  6. package/dist-tsc/LoadingIndicator.d.ts.map +1 -0
  7. package/{dist → dist-tsc}/LoadingIndicator.js +1 -2
  8. package/dist-tsc/TitleInfo.d.ts +2 -0
  9. package/dist-tsc/TitleInfo.d.ts.map +1 -0
  10. package/{dist → dist-tsc}/TitleInfo.js +6 -83
  11. package/dist-tsc/VitS.d.ts +45 -0
  12. package/dist-tsc/VitS.d.ts.map +1 -0
  13. package/dist-tsc/VitS.js +119 -0
  14. package/dist-tsc/VitessceGrid.d.ts +23 -0
  15. package/dist-tsc/VitessceGrid.d.ts.map +1 -0
  16. package/{dist → dist-tsc}/VitessceGrid.js +14 -13
  17. package/dist-tsc/VitessceGrid.test.d.ts +2 -0
  18. package/dist-tsc/VitessceGrid.test.d.ts.map +1 -0
  19. package/{dist → dist-tsc}/VitessceGrid.test.js +8 -4
  20. package/dist-tsc/Warning.d.ts +2 -0
  21. package/dist-tsc/Warning.d.ts.map +1 -0
  22. package/{dist → dist-tsc}/Warning.js +2 -2
  23. package/dist-tsc/classNames.d.ts +8 -0
  24. package/dist-tsc/classNames.d.ts.map +1 -0
  25. package/dist-tsc/data/AbstractLoader.d.ts +22 -0
  26. package/dist-tsc/data/AbstractLoader.d.ts.map +1 -0
  27. package/dist-tsc/data/AbstractLoader.js +18 -0
  28. package/dist-tsc/data/AbstractTwoStepLoader.d.ts +6 -0
  29. package/dist-tsc/data/AbstractTwoStepLoader.d.ts.map +1 -0
  30. package/{dist → dist-tsc}/data/AbstractTwoStepLoader.js +1 -1
  31. package/dist-tsc/data/LoaderResult.d.ts +7 -0
  32. package/dist-tsc/data/LoaderResult.d.ts.map +1 -0
  33. package/dist-tsc/data/index.d.ts +4 -0
  34. package/dist-tsc/data/index.d.ts.map +1 -0
  35. package/dist-tsc/data/index.js +3 -0
  36. package/dist-tsc/data/loader-registry.d.ts +9 -0
  37. package/dist-tsc/data/loader-registry.d.ts.map +1 -0
  38. package/dist-tsc/data/loader-registry.js +27 -0
  39. package/dist-tsc/data-hook-utils.d.ts +69 -0
  40. package/dist-tsc/data-hook-utils.d.ts.map +1 -0
  41. package/{dist → dist-tsc}/data-hook-utils.js +4 -6
  42. package/dist-tsc/data-hooks.d.ts +77 -0
  43. package/dist-tsc/data-hooks.d.ts.map +1 -0
  44. package/{dist → dist-tsc}/data-hooks.js +4 -4
  45. package/dist-tsc/errors/AbstractLoaderError.d.ts +10 -0
  46. package/dist-tsc/errors/AbstractLoaderError.d.ts.map +1 -0
  47. package/dist-tsc/errors/DataSourceFetchError.d.ts +8 -0
  48. package/dist-tsc/errors/DataSourceFetchError.d.ts.map +1 -0
  49. package/{dist → dist-tsc}/errors/DataSourceFetchError.js +1 -1
  50. package/dist-tsc/errors/DatasetNotFoundError.d.ts +6 -0
  51. package/dist-tsc/errors/DatasetNotFoundError.d.ts.map +1 -0
  52. package/{dist → dist-tsc}/errors/DatasetNotFoundError.js +1 -1
  53. package/dist-tsc/errors/LoaderNotFoundError.d.ts +10 -0
  54. package/dist-tsc/errors/LoaderNotFoundError.d.ts.map +1 -0
  55. package/{dist → dist-tsc}/errors/LoaderNotFoundError.js +1 -1
  56. package/dist-tsc/errors/LoaderValidationError.d.ts +10 -0
  57. package/dist-tsc/errors/LoaderValidationError.d.ts.map +1 -0
  58. package/{dist → dist-tsc}/errors/LoaderValidationError.js +1 -1
  59. package/dist-tsc/errors/index.d.ts +6 -0
  60. package/dist-tsc/errors/index.d.ts.map +1 -0
  61. package/{dist → dist-tsc}/errors/index.js +5 -6
  62. package/dist-tsc/hooks.d.ts +80 -0
  63. package/dist-tsc/hooks.d.ts.map +1 -0
  64. package/{dist → dist-tsc}/hooks.js +60 -4
  65. package/dist-tsc/index.d.ts +11 -0
  66. package/dist-tsc/index.d.ts.map +1 -0
  67. package/dist-tsc/index.js +11 -0
  68. package/dist-tsc/plugins.test.fixtures.d.ts +61 -0
  69. package/dist-tsc/plugins.test.fixtures.d.ts.map +1 -0
  70. package/{dist → dist-tsc}/schemas/config-1.0.16.schema.json +21 -41
  71. package/dist-tsc/schemas/obsSets.schema.d.ts +179 -0
  72. package/dist-tsc/schemas/obsSets.schema.d.ts.map +1 -0
  73. package/dist-tsc/schemas/obsSetsTabular.schema.d.ts +57 -0
  74. package/dist-tsc/schemas/obsSetsTabular.schema.d.ts.map +1 -0
  75. package/dist-tsc/schemas/raster.schema.d.ts +239 -0
  76. package/dist-tsc/schemas/raster.schema.d.ts.map +1 -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 → dist-tsc}/shared-mui/components.js +2 -8
  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 → dist-tsc}/shared-mui/container.js +1 -1
  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 → dist-tsc}/shared-mui/styles.js +4 -2
  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 → dist-tsc}/shared-plot-options/CellColorEncodingOption.js +3 -4
  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 → dist-tsc}/shared-plot-options/OptionSelect.js +2 -2
  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 → dist-tsc}/shared-plot-options/OptionsContainer.js +2 -5
  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 → dist-tsc}/shared-plot-options/styles.js +1 -1
  101. package/dist-tsc/state/hooks.d.ts +216 -0
  102. package/dist-tsc/state/hooks.d.ts.map +1 -0
  103. package/{dist → dist-tsc}/state/hooks.js +47 -21
  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 +30 -0
  108. package/dist-tsc/view-config-utils.d.ts.map +1 -0
  109. package/{dist → dist-tsc}/view-config-utils.js +32 -84
  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 → dist-tsc}/view-config-utils.test.fixtures.js +118 -0
  115. package/dist-tsc/view-config-utils.test.js +209 -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 → dist-tsc}/vitessce-grid-layout/VitessceGridLayout.js +47 -28
  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 → dist-tsc}/vitessce-grid-layout/VitessceGridLayout.test.js +13 -5
  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 → dist-tsc}/vitessce-grid-layout/layout-utils.js +2 -1
  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 → dist-tsc}/vitessce-grid-layout/layout-utils.test.js +3 -1
  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 → dist-tsc}/vitessce-grid-utils.js +15 -14
  134. package/package.json +24 -11
  135. package/src/CallbackPublisher.js +77 -0
  136. package/src/LoadingIndicator.js +33 -0
  137. package/src/TitleInfo.js +167 -0
  138. package/src/VitS.js +202 -0
  139. package/src/VitessceGrid.js +118 -0
  140. package/src/VitessceGrid.test.jsx +57 -0
  141. package/src/Warning.js +62 -0
  142. package/src/classNames.js +8 -0
  143. package/src/data/AbstractLoader.js +23 -0
  144. package/src/data/AbstractTwoStepLoader.js +8 -0
  145. package/src/data/LoaderResult.js +8 -0
  146. package/src/data/index.js +3 -0
  147. package/src/data/loader-registry.js +28 -0
  148. package/src/data-hook-utils.js +195 -0
  149. package/src/data-hooks.js +354 -0
  150. package/src/errors/AbstractLoaderError.js +14 -0
  151. package/src/errors/DataSourceFetchError.js +15 -0
  152. package/src/errors/DatasetNotFoundError.js +23 -0
  153. package/src/errors/LoaderNotFoundError.js +24 -0
  154. package/src/errors/LoaderValidationError.js +23 -0
  155. package/src/errors/index.js +5 -0
  156. package/src/hooks.js +317 -0
  157. package/src/index.js +76 -0
  158. package/src/plugins.test.fixtures.js +78 -0
  159. package/src/schemas/config-0.1.0.schema.json +85 -0
  160. package/src/schemas/config-1.0.0.schema.json +733 -0
  161. package/src/schemas/config-1.0.1.schema.json +909 -0
  162. package/src/schemas/config-1.0.10.schema.json +969 -0
  163. package/src/schemas/config-1.0.11.schema.json +990 -0
  164. package/src/schemas/config-1.0.12.schema.json +997 -0
  165. package/src/schemas/config-1.0.13.schema.json +1013 -0
  166. package/src/schemas/config-1.0.14.schema.json +1048 -0
  167. package/src/schemas/config-1.0.15.schema.json +1048 -0
  168. package/src/schemas/config-1.0.16.schema.json +1089 -0
  169. package/src/schemas/config-1.0.2.schema.json +911 -0
  170. package/src/schemas/config-1.0.3.schema.json +911 -0
  171. package/src/schemas/config-1.0.4.schema.json +949 -0
  172. package/src/schemas/config-1.0.5.schema.json +949 -0
  173. package/src/schemas/config-1.0.6.schema.json +950 -0
  174. package/src/schemas/config-1.0.7.schema.json +950 -0
  175. package/src/schemas/config-1.0.8.schema.json +966 -0
  176. package/src/schemas/config-1.0.9.schema.json +965 -0
  177. package/src/schemas/fixtures/cellSets.good.json +16 -0
  178. package/src/schemas/fixtures/config-1.0.1.bad.json +1 -0
  179. package/src/schemas/fixtures/config-1.0.1.bad.message.json +11 -0
  180. package/src/schemas/fixtures/config-1.0.1.good.json +37 -0
  181. package/src/schemas/fixtures/config-1.0.2.bad.json +1 -0
  182. package/src/schemas/fixtures/config-1.0.2.bad.message.json +11 -0
  183. package/src/schemas/fixtures/config-1.0.2.good.json +38 -0
  184. package/src/schemas/fixtures/config-1.0.3.bad.json +1 -0
  185. package/src/schemas/fixtures/config-1.0.3.bad.message.json +11 -0
  186. package/src/schemas/fixtures/config-1.0.3.good.json +38 -0
  187. package/src/schemas/fixtures/obsSets.bad.json +1 -0
  188. package/src/schemas/fixtures/obsSets.bad.message.json +11 -0
  189. package/src/schemas/fixtures/obsSets.good.json +16 -0
  190. package/src/schemas/fixtures/obsSetsTabular.bad.json +1 -0
  191. package/src/schemas/fixtures/obsSetsTabular.bad.message.json +11 -0
  192. package/src/schemas/fixtures/obsSetsTabular.good.json +26 -0
  193. package/src/schemas/fixtures/raster.bad.json +1 -0
  194. package/src/schemas/fixtures/raster.bad.message.json +11 -0
  195. package/src/schemas/fixtures/raster.good.json +39 -0
  196. package/src/schemas/obsSets.schema.js +195 -0
  197. package/src/schemas/obsSetsTabular.schema.js +39 -0
  198. package/src/schemas/raster.schema.js +121 -0
  199. package/src/schemas/schema-schema.sh +34 -0
  200. package/src/shared-mui/components.js +81 -0
  201. package/src/shared-mui/container.js +212 -0
  202. package/src/shared-mui/styles.js +113 -0
  203. package/src/shared-plot-options/CellColorEncodingOption.js +42 -0
  204. package/src/shared-plot-options/OptionSelect.js +19 -0
  205. package/src/shared-plot-options/OptionsContainer.js +23 -0
  206. package/src/shared-plot-options/index.js +4 -0
  207. package/src/shared-plot-options/styles.js +49 -0
  208. package/src/state/hooks.js +640 -0
  209. package/src/title-styles.js +73 -0
  210. package/src/view-config-utils.js +263 -0
  211. package/src/view-config-utils.test.fixtures.js +893 -0
  212. package/src/view-config-utils.test.jsx +222 -0
  213. package/src/vitessce-grid-layout/VitessceGridLayout.js +179 -0
  214. package/src/vitessce-grid-layout/VitessceGridLayout.test.jsx +64 -0
  215. package/src/vitessce-grid-layout/index.js +1 -0
  216. package/src/vitessce-grid-layout/layout-utils.js +76 -0
  217. package/src/vitessce-grid-layout/layout-utils.test.js +167 -0
  218. package/src/vitessce-grid-utils.js +172 -0
  219. package/dist/VitS.js +0 -117
  220. package/dist/Vitessce.integration.test.js +0 -36
  221. package/dist/Vitessce.js +0 -101
  222. package/dist/component-registry.js +0 -16
  223. package/dist/data/AbstractLoader.js +0 -35
  224. package/dist/data/index.js +0 -3
  225. package/dist/data/loader-registry.js +0 -15
  226. package/dist/errors/OptionsValidationError.js +0 -15
  227. package/dist/export-utils.js +0 -70
  228. package/dist/export-utils.test.js +0 -66
  229. package/dist/file-options-schemas-legacy.js +0 -136
  230. package/dist/file-options-schemas-legacy.test.js +0 -126
  231. package/dist/file-options-schemas.js +0 -322
  232. package/dist/file-options-schemas.test.js +0 -23
  233. package/dist/joint-file-types-legacy.js +0 -278
  234. package/dist/joint-file-types-legacy.test.js +0 -400
  235. package/dist/joint-file-types.js +0 -148
  236. package/dist/joint-file-types.test.js +0 -139
  237. package/dist/loader-registry.js +0 -98
  238. package/dist/plugins.js +0 -154
  239. package/dist/plugins.test.js +0 -30
  240. package/dist/schemas/PrettyJsonSchema.js +0 -34
  241. package/dist/schemas/config-1.0.15.schema.test.js +0 -22
  242. package/dist/schemas/config-1.0.16.schema.test.js +0 -22
  243. package/dist/schemas/obs-sets-tabular.schema.js +0 -37
  244. package/dist/schemas/obs-sets.schema.js +0 -194
  245. package/dist/schemas/schema.test-old.js +0 -50
  246. package/dist/schemas/schema.test.js +0 -46
  247. package/dist/schemas-extra.js +0 -3
  248. package/dist/schemas.js +0 -4
  249. package/dist/shared-plot-options/index.js +0 -4
  250. package/dist/view-config-upgraders.js +0 -525
  251. package/dist/view-config-utils.test.js +0 -66
  252. package/dist/view-config-versions.js +0 -47
  253. package/dist/vitessce-grid-layout/index.js +0 -1
  254. /package/{dist → dist-tsc}/classNames.js +0 -0
  255. /package/{dist → dist-tsc}/data/LoaderResult.js +0 -0
  256. /package/{dist → dist-tsc}/errors/AbstractLoaderError.js +0 -0
  257. /package/{dist → dist-tsc}/plugins.test.fixtures.js +0 -0
  258. /package/{dist → dist-tsc}/schemas/config-0.1.0.schema.json +0 -0
  259. /package/{dist → dist-tsc}/schemas/config-1.0.0.schema.json +0 -0
  260. /package/{dist → dist-tsc}/schemas/config-1.0.1.schema.json +0 -0
  261. /package/{dist → dist-tsc}/schemas/config-1.0.10.schema.json +0 -0
  262. /package/{dist → dist-tsc}/schemas/config-1.0.11.schema.json +0 -0
  263. /package/{dist → dist-tsc}/schemas/config-1.0.12.schema.json +0 -0
  264. /package/{dist → dist-tsc}/schemas/config-1.0.13.schema.json +0 -0
  265. /package/{dist → dist-tsc}/schemas/config-1.0.14.schema.json +0 -0
  266. /package/{dist → dist-tsc}/schemas/config-1.0.15.schema.json +0 -0
  267. /package/{dist → dist-tsc}/schemas/config-1.0.2.schema.json +0 -0
  268. /package/{dist → dist-tsc}/schemas/config-1.0.3.schema.json +0 -0
  269. /package/{dist → dist-tsc}/schemas/config-1.0.4.schema.json +0 -0
  270. /package/{dist → dist-tsc}/schemas/config-1.0.5.schema.json +0 -0
  271. /package/{dist → dist-tsc}/schemas/config-1.0.6.schema.json +0 -0
  272. /package/{dist → dist-tsc}/schemas/config-1.0.7.schema.json +0 -0
  273. /package/{dist → dist-tsc}/schemas/config-1.0.8.schema.json +0 -0
  274. /package/{dist → dist-tsc}/schemas/config-1.0.9.schema.json +0 -0
  275. /package/{dist → dist-tsc}/schemas/obsSets.schema.js +0 -0
  276. /package/{dist → dist-tsc}/schemas/obsSetsTabular.schema.js +0 -0
  277. /package/{dist → dist-tsc}/schemas/raster.schema.js +0 -0
@@ -0,0 +1,222 @@
1
+ /* eslint-disable camelcase */
2
+ import React from 'react';
3
+ import { z } from '@vitessce/schemas';
4
+ import { PluginViewType, PluginCoordinationType } from '@vitessce/plugins';
5
+ import {
6
+ getExistingScopesForCoordinationType,
7
+ initialize,
8
+ } from './view-config-utils.js';
9
+
10
+ function FakeComponent(props) {
11
+ const { text } = props;
12
+ return <span>{text}</span>;
13
+ }
14
+
15
+ const jointFileTypes = [];
16
+ const coordinationTypes = [
17
+ new PluginCoordinationType('dataset', null, z.string().nullable()),
18
+ new PluginCoordinationType('embeddingTargetX', 0, z.number()),
19
+ new PluginCoordinationType('embeddingTargetY', 0, z.number()),
20
+ new PluginCoordinationType('embeddingZoom', 3, z.number()),
21
+ new PluginCoordinationType('embeddingType', null, z.string().nullable()),
22
+ ];
23
+ const viewTypes = [
24
+ new PluginViewType('description', FakeComponent, ['dataset']),
25
+ new PluginViewType('scatterplot', FakeComponent, ['dataset', 'embeddingType', 'embeddingZoom', 'embeddingTargetX', 'embeddingTargetY']),
26
+ ];
27
+
28
+ describe('src/app/view-config-utils.js', () => {
29
+ describe('getExistingScopesForCoordinationType', () => {
30
+ it('gets all scope names for a particular coordination type', () => {
31
+ const config = {
32
+ coordinationSpace: {
33
+ dataset: {
34
+ A: 'my-dataset-1',
35
+ B: 'my-dataset-2',
36
+ },
37
+ },
38
+ layout: [
39
+ {
40
+ coordinationScopes: {
41
+ dataset: 'A',
42
+ },
43
+ },
44
+ {
45
+ coordinationScopes: {
46
+ dataset: 'C',
47
+ },
48
+ },
49
+ ],
50
+ };
51
+ expect(getExistingScopesForCoordinationType(config, 'dataset')).toEqual(['A', 'B', 'C']);
52
+ });
53
+ });
54
+
55
+ describe('initialize', () => {
56
+ it('initializes coordination space and component coordination scopes when initStrategy is auto', () => {
57
+ const preInitializationConfig = {
58
+ version: '1.0.16',
59
+ name: 'My config name',
60
+ description: 'My config description',
61
+ initStrategy: 'auto',
62
+ coordinationSpace: {
63
+ embeddingTargetX: {
64
+ A: 0,
65
+ },
66
+ embeddingTargetY: {
67
+ A: 0,
68
+ },
69
+ embeddingType: {
70
+ 't-SNE': 't-SNE',
71
+ },
72
+ },
73
+ datasets: [
74
+ {
75
+ files: [],
76
+ name: 'A',
77
+ uid: 'A',
78
+ },
79
+ ],
80
+ layout: [
81
+ {
82
+ component: 'description',
83
+ coordinationScopes: {},
84
+ h: 2,
85
+ w: 3,
86
+ x: 9,
87
+ y: 0,
88
+ },
89
+ {
90
+ component: 'scatterplot',
91
+ coordinationScopes: {
92
+ embeddingTargetX: 'A',
93
+ embeddingTargetY: 'A',
94
+ embeddingType: 't-SNE',
95
+ },
96
+ h: 4,
97
+ w: 5,
98
+ x: 0,
99
+ y: 2,
100
+ },
101
+ ],
102
+ };
103
+
104
+ const initializedViewConfig = {
105
+ version: '1.0.16',
106
+ uid: 'A',
107
+ name: 'My config name',
108
+ description: 'My config description',
109
+ initStrategy: 'auto',
110
+ coordinationSpace: {
111
+ dataset: {
112
+ A: 'A',
113
+ },
114
+ embeddingTargetX: {
115
+ A: 0,
116
+ },
117
+ embeddingTargetY: {
118
+ A: 0,
119
+ },
120
+ embeddingType: {
121
+ 't-SNE': 't-SNE',
122
+ },
123
+ embeddingZoom: {
124
+ A: 3,
125
+ },
126
+ },
127
+ datasets: [
128
+ {
129
+ files: [],
130
+ name: 'A',
131
+ uid: 'A',
132
+ },
133
+ ],
134
+ layout: [
135
+ {
136
+ component: 'description',
137
+ coordinationScopes: {
138
+ dataset: 'A',
139
+ },
140
+ h: 2,
141
+ uid: 'A',
142
+ w: 3,
143
+ x: 9,
144
+ y: 0,
145
+ },
146
+ {
147
+ component: 'scatterplot',
148
+ coordinationScopes: {
149
+ dataset: 'A',
150
+ embeddingTargetX: 'A',
151
+ embeddingTargetY: 'A',
152
+ embeddingType: 't-SNE',
153
+ embeddingZoom: 'A',
154
+ },
155
+ h: 4,
156
+ uid: 'B',
157
+ w: 5,
158
+ x: 0,
159
+ y: 2,
160
+ },
161
+ ],
162
+ };
163
+
164
+ expect(initialize(preInitializationConfig, jointFileTypes, coordinationTypes, viewTypes))
165
+ .toEqual(initializedViewConfig);
166
+ });
167
+
168
+ it('does not initialize when initStrategy is none', () => {
169
+ const preInitializationConfig = {
170
+ version: '1.0.16',
171
+ name: 'My config name',
172
+ description: 'My config description',
173
+ initStrategy: 'none', // initStrategy is none
174
+ coordinationSpace: {
175
+ embeddingTargetX: {
176
+ A: 0,
177
+ },
178
+ embeddingTargetY: {
179
+ A: 0,
180
+ },
181
+ embeddingType: {
182
+ 't-SNE': 't-SNE',
183
+ },
184
+ },
185
+ datasets: [
186
+ {
187
+ files: [],
188
+ name: 'A',
189
+ uid: 'A',
190
+ },
191
+ ],
192
+ layout: [
193
+ {
194
+ component: 'description',
195
+ coordinationScopes: {},
196
+ h: 2,
197
+ w: 3,
198
+ x: 9,
199
+ y: 0,
200
+ },
201
+ {
202
+ component: 'scatterplot',
203
+ coordinationScopes: {
204
+ embeddingTargetX: 'A',
205
+ embeddingTargetY: 'A',
206
+ embeddingType: 't-SNE',
207
+ },
208
+ h: 4,
209
+ w: 5,
210
+ x: 0,
211
+ y: 2,
212
+ },
213
+ ],
214
+ };
215
+ expect(
216
+ initialize(
217
+ preInitializationConfig, jointFileTypes, coordinationTypes, viewTypes,
218
+ ).coordinationSpace,
219
+ ).toEqual(preInitializationConfig.coordinationSpace);
220
+ });
221
+ });
222
+ });
@@ -0,0 +1,179 @@
1
+ import React, { useState, useMemo, useCallback } from 'react';
2
+ import { Responsive, WidthProvider } from 'react-grid-layout-with-lodash';
3
+ import { isEqual } from 'lodash-es';
4
+ import { getMaxRows, resolveLayout } from './layout-utils.js';
5
+
6
+ const ResponsiveGridLayout = WidthProvider(Responsive);
7
+
8
+ class ResponsiveHeightGridLayout extends ResponsiveGridLayout {
9
+ componentDidUpdate(prevProps) {
10
+ if (this.props.height !== prevProps.height) {
11
+ this.onWindowResize();
12
+ }
13
+ }
14
+ }
15
+
16
+ export function VitessceGridLayout(props) {
17
+ const {
18
+ layout,
19
+ viewTypes, padding, margin: marginProp, draggableHandle: draggableHandleClass,
20
+ onResize, onResizeStop, rowHeight, theme, height,
21
+ onRemoveComponent, onLayoutChange: onLayoutChangeProp,
22
+ isBounded,
23
+ } = props;
24
+
25
+ const getComponent = useCallback((viewType) => {
26
+ if (Array.isArray(viewTypes)) {
27
+ const matchingViewType = viewTypes.find(v => v.name === viewType);
28
+ if (matchingViewType) {
29
+ return matchingViewType.component;
30
+ }
31
+ }
32
+ return () => null;
33
+ }, [viewTypes]);
34
+
35
+ const draggableHandle = `.${draggableHandleClass}`;
36
+
37
+ // If layout changes, update grid components.
38
+ const {
39
+ cols: gridCols, layouts: gridLayouts, breakpoints: gridBreakpoints, components: gridComponents,
40
+ } = useMemo(() => resolveLayout(layout), [layout]);
41
+
42
+ const containerPadding = useMemo(() => ([padding, padding]), [padding]);
43
+ const margin = useMemo(() => ([marginProp, marginProp]), [marginProp]);
44
+
45
+ const maxRows = getMaxRows(gridLayouts);
46
+
47
+ // Inline CSS is generally avoided, but this saves the end-user a little work,
48
+ // and prevents class names from getting out of sync.
49
+ const style = (
50
+ <style>
51
+ {`
52
+ ${draggableHandle} {
53
+ cursor: grab;
54
+ }
55
+ ${draggableHandle}:active {
56
+ cursor: grabbing;
57
+ }
58
+ `}
59
+ </style>
60
+ );
61
+
62
+ // A bit of hacky feeling stuff to prevent users from stacking elements and forcing the
63
+ // grid boundary to change even when isBounded is set to true. It seems like react-grid-layout
64
+ // should support this through isBounded and maxRows, but neither of these actually works for the
65
+ // edge case where a user drags one element above another and forces the first element downwards.
66
+ //
67
+ // Additionally, react-grid-layout doesn't revert if you don't save a new/changed layouts. If you
68
+ // wish to do this, first you have to save the new layouts and render the grid with it, and then
69
+ // you can revert to the original layouts. Thus, we need one state for a temporary grid layout,
70
+ // which gets called on every onLayoutChange. If the grid height is still valid, we then call
71
+ // onValidLayoutChange, otherwise we call onValidLayoutChange with lastValidGridLayouts.
72
+ //
73
+ // See the following GitHub issue for more information.
74
+ // https://github.com/react-grid-layout/react-grid-layout/issues/1104#issuecomment-827785217
75
+ const [tempGridLayouts, setTempGridLayouts] = useState(null);
76
+ const [lastValidGridLayouts, setLastValidGridLayouts] = useState(gridLayouts);
77
+
78
+ const onValidLayoutChange = useCallback((newLayout) => {
79
+ if (newLayout.length === Object.entries(gridComponents).length) {
80
+ const newComponentProps = {};
81
+ newLayout.forEach((nextC) => {
82
+ const id = nextC.i;
83
+ const prevC = gridComponents[id];
84
+ if (prevC) {
85
+ const nextProps = {
86
+ x: nextC.x, y: nextC.y, w: nextC.w, h: nextC.h,
87
+ };
88
+ const prevProps = {
89
+ x: prevC.x, y: prevC.y, w: prevC.w, h: prevC.h,
90
+ };
91
+ if (!isEqual(nextProps, prevProps)) {
92
+ newComponentProps[id] = nextProps;
93
+ }
94
+ }
95
+ });
96
+ if (Object.keys(newComponentProps).length > 0) {
97
+ onLayoutChangeProp(newComponentProps);
98
+ }
99
+ }
100
+ }, [gridComponents, onLayoutChangeProp]);
101
+
102
+ const onLayoutChange = useCallback((newLayout, allLayouts) => {
103
+ // We first need to set the new layout to the potentially-invalid allLayouts
104
+ // (see comments above about react-grid-layout limitations).
105
+ setTempGridLayouts(allLayouts);
106
+ // Then we wait 50ms and validate/set the new layout.
107
+ setTimeout(() => {
108
+ if (!isBounded || getMaxRows({ ID: newLayout }) <= maxRows) {
109
+ // Good, new layout was valid with respect to isBounded, so set in parent.
110
+ onValidLayoutChange(newLayout);
111
+ setLastValidGridLayouts(allLayouts);
112
+ } else {
113
+ // Bad, new layout was not valid with respect to isBounded.
114
+ onValidLayoutChange(lastValidGridLayouts);
115
+ }
116
+ setTempGridLayouts(null);
117
+ }, 50);
118
+ }, [isBounded, lastValidGridLayouts, maxRows, onValidLayoutChange]);
119
+
120
+ const saveCurrentLayouts = useCallback(() => {
121
+ setLastValidGridLayouts(gridLayouts);
122
+ }, [gridLayouts]);
123
+
124
+ const layoutChildren = useMemo(() => Object.values(gridComponents).map((v) => {
125
+ const Component = getComponent(v.component);
126
+
127
+ const removeGridComponent = () => {
128
+ onRemoveComponent(v.uid);
129
+ };
130
+
131
+ return (
132
+ <div key={v.uid}>
133
+ <Component
134
+ {... v.props}
135
+ uuid={v.uid}
136
+ coordinationScopes={v.coordinationScopes}
137
+ coordinationScopesBy={v.coordinationScopesBy}
138
+ theme={theme}
139
+ removeGridComponent={removeGridComponent}
140
+ />
141
+ </div>
142
+ );
143
+ }), [gridComponents, getComponent, onRemoveComponent, theme]);
144
+
145
+ return (gridLayouts && gridComponents && gridBreakpoints && gridCols) && (
146
+ <>
147
+ {style}
148
+ <ResponsiveHeightGridLayout
149
+ className="layout"
150
+ cols={gridCols}
151
+ layouts={tempGridLayouts || gridLayouts}
152
+ breakpoints={gridBreakpoints}
153
+ height={height}
154
+ rowHeight={
155
+ rowHeight
156
+ || (
157
+ (window.innerHeight - 2 * padding - (maxRows - 1) * margin)
158
+ / maxRows
159
+ )}
160
+ containerPadding={containerPadding}
161
+ margin={margin}
162
+ draggableHandle={draggableHandle}
163
+ onLayoutChange={onLayoutChange}
164
+ isBounded={isBounded}
165
+ onResizeStart={saveCurrentLayouts}
166
+ onDragStart={saveCurrentLayouts}
167
+ onResize={onResize}
168
+ onResizeStop={onResizeStop}
169
+ >
170
+ {layoutChildren}
171
+ </ResponsiveHeightGridLayout>
172
+ </>
173
+ );
174
+ }
175
+
176
+ VitessceGridLayout.defaultProps = {
177
+ padding: 10,
178
+ margin: 10,
179
+ };
@@ -0,0 +1,64 @@
1
+ import '@testing-library/jest-dom';
2
+ import { cleanup, render, screen } from '@testing-library/react';
3
+ import { afterEach } from 'vitest';
4
+
5
+ import React from 'react';
6
+ import { PluginViewType } from '@vitessce/plugins';
7
+ import { VitessceGridLayout } from './VitessceGridLayout.js';
8
+
9
+ afterEach(() => {
10
+ cleanup();
11
+ });
12
+
13
+ function FakeComponent(props) {
14
+ const { text } = props;
15
+ return <span>{text}</span>;
16
+ }
17
+ const layoutJson = {
18
+ columns: {
19
+ 600: [0, 2, 4, 8],
20
+ },
21
+ components: [
22
+ {
23
+ component: 'FakeComponent',
24
+ uid: 'fake',
25
+ props: { text: 'Hello World' },
26
+ x: 0,
27
+ y: 0,
28
+ w: 2,
29
+ },
30
+ ],
31
+ };
32
+
33
+ describe('VitessceGridLayout.js', () => {
34
+ describe('<VitessceGridLayout />', () => {
35
+ it('mount() works', async () => {
36
+ const viewTypes = [new PluginViewType('FakeComponent', FakeComponent, [])];
37
+ const { container } = render(<VitessceGridLayout
38
+ layout={layoutJson}
39
+ viewTypes={viewTypes}
40
+ draggableHandle=".my-handle"
41
+ />);
42
+
43
+ expect(await screen.findByText('Hello World'));
44
+ expect(container.querySelectorAll('.react-grid-item span:not(.react-resizable-handle)').length).toEqual(1);
45
+
46
+ const style = container.querySelectorAll('style');
47
+ expect(style.length).toEqual(1);
48
+ expect(style[0].textContent).toContain('.my-handle {');
49
+ expect(style[0].textContent).toContain('.my-handle:active {');
50
+ });
51
+
52
+ it('rowHeight works', () => {
53
+ const viewTypes = [new PluginViewType('FakeComponent', FakeComponent, [])];
54
+ const { container } = render(<VitessceGridLayout
55
+ layout={layoutJson}
56
+ viewTypes={viewTypes}
57
+ draggableHandle=".my-handle"
58
+ rowHeight={123}
59
+ />);
60
+
61
+ expect(container.querySelector('.react-grid-item').style.height).toEqual('123px');
62
+ });
63
+ });
64
+ });
@@ -0,0 +1 @@
1
+ export { VitessceGridLayout } from './VitessceGridLayout.js';
@@ -0,0 +1,76 @@
1
+ import { range } from 'lodash-es';
2
+
3
+ function sum(a) {
4
+ return a.reduce((x, y) => x + y, 0);
5
+ }
6
+
7
+ export function makeGridLayout(colXs, colLayout) {
8
+ const colWs = [];
9
+ for (let i = 0; i < colXs.length; i++) { // eslint-disable-line no-plusplus
10
+ colWs.push(colXs[i + 1] - colXs[i]);
11
+ }
12
+ return Object.entries(colLayout).map(([id, spec]) => ({
13
+ i: id,
14
+ y: spec.y,
15
+ h: spec.h || 1,
16
+ x: colXs[spec.x],
17
+ w: sum(colWs.slice(spec.x, spec.x + (spec.w || 1))),
18
+ }));
19
+ }
20
+
21
+ export function getMaxRows(layouts) {
22
+ return Math.max(
23
+ ...Object.values(layouts).map(
24
+ layout => Math.max(
25
+ ...layout.map(xywh => xywh.y + xywh.h),
26
+ ),
27
+ ),
28
+ );
29
+ }
30
+
31
+ export function resolveLayout(layout) {
32
+ const cols = {};
33
+ const layouts = {};
34
+ const breakpoints = {};
35
+ const components = {};
36
+ const positions = {};
37
+
38
+ (('components' in layout) ? layout.components : layout).forEach(
39
+ (def) => {
40
+ const id = def.uid;
41
+ components[id] = {
42
+ uid: def.uid,
43
+ component: def.component,
44
+ props: def.props || {},
45
+ coordinationScopes: def.coordinationScopes || {},
46
+ coordinationScopesBy: def.coordinationScopesBy || {},
47
+ };
48
+ positions[id] = {
49
+ id, x: def.x, y: def.y, w: def.w, h: def.h,
50
+ };
51
+ },
52
+ );
53
+
54
+ if ('components' in layout) {
55
+ Object.entries(layout.columns).forEach(
56
+ ([width, columnXs]) => {
57
+ cols[width] = columnXs[columnXs.length - 1];
58
+ layouts[width] = makeGridLayout(columnXs, positions);
59
+ breakpoints[width] = width;
60
+ },
61
+ );
62
+ } else {
63
+ // static layout
64
+ const id = 'ID';
65
+ const columnCount = 12;
66
+ cols[id] = columnCount;
67
+ layouts[id] = makeGridLayout(range(columnCount + 1), positions);
68
+ breakpoints[id] = 1000;
69
+ // Default has different numbers of columns at different widths,
70
+ // so we do need to override that to ensure the same number of columns,
71
+ // regardless of window width.
72
+ }
73
+ return {
74
+ cols, layouts, breakpoints, components,
75
+ };
76
+ }
@@ -0,0 +1,167 @@
1
+ import {
2
+ makeGridLayout, getMaxRows, resolveLayout,
3
+ } from './layout-utils.js';
4
+
5
+ describe('layout-utils.js', () => {
6
+ describe('makeGridLayout', () => {
7
+ it('applies columnXs and makes list from object', () => {
8
+ const columnXs = [0, 4, 8, 12];
9
+ const layout = {
10
+ bigLeft: { x: 0, y: 0, w: 2 },
11
+ smallRight: { x: 2, y: 0 },
12
+ smallLeft: { x: 0, y: 1 },
13
+ bigRight: { x: 1, y: 1, w: 2 },
14
+ wholeRow: { x: 0, y: 2, w: 3 },
15
+ };
16
+ const gridLayout = makeGridLayout(columnXs, layout);
17
+ expect(gridLayout).toEqual(
18
+ /* Disable eslint so it's easier to copy-and-paste from test results. */
19
+ /* eslint-disable quote-props */
20
+ /* eslint-disable quotes */
21
+ [
22
+ {
23
+ "h": 1,
24
+ "i": "bigLeft",
25
+ "w": 8,
26
+ "x": 0,
27
+ "y": 0,
28
+ },
29
+ {
30
+ "h": 1,
31
+ "i": "smallRight",
32
+ "w": 4,
33
+ "x": 8,
34
+ "y": 0,
35
+ },
36
+ {
37
+ "h": 1,
38
+ "i": "smallLeft",
39
+ "w": 4,
40
+ "x": 0,
41
+ "y": 1,
42
+ },
43
+ {
44
+ "h": 1,
45
+ "i": "bigRight",
46
+ "w": 8,
47
+ "x": 4,
48
+ "y": 1,
49
+ },
50
+ {
51
+ "h": 1,
52
+ "i": "wholeRow",
53
+ "w": 12,
54
+ "x": 0,
55
+ "y": 2,
56
+ },
57
+ ],
58
+ /* eslint-enable */
59
+ );
60
+ });
61
+ });
62
+
63
+ describe('getMaxRows', () => {
64
+ it('works', () => {
65
+ const columnXs = [0, 4, 8, 12];
66
+ const layout = {
67
+ bigLeft: { x: 0, y: 0, w: 2 },
68
+ smallRight: { x: 2, y: 0 },
69
+ smallLeft: { x: 0, y: 1 },
70
+ bigRight: { x: 1, y: 1, w: 2 },
71
+ wholeRow: { x: 0, y: 2, w: 3 },
72
+ };
73
+ const gridLayout = makeGridLayout(columnXs, layout);
74
+ expect(getMaxRows([gridLayout])).toEqual(3);
75
+ });
76
+ });
77
+
78
+ describe('resolveLayout', () => {
79
+ const componentsSpec = [
80
+ {
81
+ uid: 'i0', component: 'NoProps', x: 0, y: 0,
82
+ },
83
+ {
84
+ uid: 'i1', component: 'HasProps', props: { foo: 'bar' }, x: 1, y: 1, w: 1, h: 1,
85
+ },
86
+ ];
87
+ const expectedComponents = {
88
+ i0: {
89
+ uid: 'i0',
90
+ component: 'NoProps',
91
+ coordinationScopes: {},
92
+ coordinationScopesBy: {},
93
+ props: {},
94
+ },
95
+ i1: {
96
+ uid: 'i1',
97
+ component: 'HasProps',
98
+ coordinationScopes: {},
99
+ coordinationScopesBy: {},
100
+ props: {
101
+ foo: 'bar',
102
+ },
103
+ },
104
+ };
105
+
106
+ it('handles responsive', () => {
107
+ const {
108
+ cols, layouts, breakpoints, components,
109
+ } = resolveLayout({
110
+ columns: {
111
+ 1000: [0, 3, 9, 12],
112
+ 800: [0, 4, 8, 12],
113
+ },
114
+ components: componentsSpec,
115
+ });
116
+ expect(cols).toEqual({ 800: 12, 1000: 12 });
117
+ expect(layouts).toEqual(
118
+ {
119
+ 800: [
120
+ {
121
+ h: 1, i: 'i0', w: 4, x: 0, y: 0,
122
+ },
123
+ {
124
+ h: 1, i: 'i1', w: 4, x: 4, y: 1,
125
+ },
126
+ ],
127
+ 1000: [
128
+ {
129
+ h: 1, i: 'i0', w: 3, x: 0, y: 0,
130
+ },
131
+ {
132
+ h: 1, i: 'i1', w: 6, x: 3, y: 1,
133
+ },
134
+ ],
135
+ },
136
+ );
137
+ expect(breakpoints).toEqual({
138
+ 800: '800',
139
+ 1000: '1000',
140
+ });
141
+ expect(components).toEqual(expectedComponents);
142
+ });
143
+
144
+ it('handles static', () => {
145
+ const {
146
+ cols, layouts, breakpoints, components,
147
+ } = resolveLayout(
148
+ componentsSpec,
149
+ );
150
+ expect(cols).toEqual({ ID: 12 });
151
+ expect(layouts).toEqual(
152
+ {
153
+ ID: [
154
+ {
155
+ h: 1, i: 'i0', w: 1, x: 0, y: 0,
156
+ },
157
+ {
158
+ h: 1, i: 'i1', w: 1, x: 1, y: 1,
159
+ },
160
+ ],
161
+ },
162
+ );
163
+ expect(breakpoints).toEqual({ ID: 1000 });
164
+ expect(components).toEqual(expectedComponents);
165
+ });
166
+ });
167
+ });