@vitessce/vit-s 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +84801 -0
- package/{dist → dist-tsc}/index.js +2 -2
- package/package.json +7 -7
- package/src/CallbackPublisher.js +81 -0
- package/src/LoadingIndicator.js +34 -0
- package/src/TitleInfo.js +240 -0
- package/src/VitS.js +180 -0
- package/src/VitessceGrid.js +110 -0
- package/src/VitessceGrid.test.jsx +48 -0
- package/src/Warning.js +62 -0
- package/{dist → src}/classNames.js +0 -0
- package/src/component-registry.js +21 -0
- package/src/data/AbstractLoader.js +46 -0
- package/{dist → src}/data/AbstractTwoStepLoader.js +5 -4
- package/src/data/LoaderResult.js +8 -0
- package/{dist → src}/data/index.js +0 -0
- package/src/data/loader-registry.js +19 -0
- package/src/data-hook-utils.js +197 -0
- package/src/data-hooks.js +354 -0
- package/src/errors/AbstractLoaderError.js +14 -0
- package/src/errors/DataSourceFetchError.js +15 -0
- package/src/errors/DatasetNotFoundError.js +23 -0
- package/src/errors/LoaderNotFoundError.js +24 -0
- package/src/errors/LoaderValidationError.js +23 -0
- package/src/errors/OptionsValidationError.js +25 -0
- package/{dist → src}/errors/index.js +0 -0
- package/src/file-options-schemas-legacy.js +139 -0
- package/src/file-options-schemas-legacy.test.js +138 -0
- package/src/file-options-schemas.js +327 -0
- package/src/file-options-schemas.test.js +26 -0
- package/src/hooks.js +252 -0
- package/src/index.js +101 -0
- package/src/joint-file-types-legacy.js +298 -0
- package/src/joint-file-types-legacy.test.js +412 -0
- package/src/joint-file-types.js +171 -0
- package/src/joint-file-types.test.js +144 -0
- package/{dist/schemas.js → src/json-schemas.js} +6 -1
- package/{dist → src}/plugins.js +102 -70
- package/src/plugins.test.fixtures.js +78 -0
- package/src/plugins.test.js +42 -0
- package/src/schemas/config-0.1.0.schema.json +85 -0
- package/src/schemas/config-1.0.0.schema.json +733 -0
- package/src/schemas/config-1.0.1.schema.json +909 -0
- package/src/schemas/config-1.0.10.schema.json +969 -0
- package/src/schemas/config-1.0.11.schema.json +990 -0
- package/src/schemas/config-1.0.12.schema.json +997 -0
- package/src/schemas/config-1.0.13.schema.json +1013 -0
- package/src/schemas/config-1.0.14.schema.json +1048 -0
- package/src/schemas/config-1.0.15.schema.json +1048 -0
- package/src/schemas/config-1.0.16.schema.json +1069 -0
- package/src/schemas/config-1.0.16.schema.test.js +32 -0
- package/src/schemas/config-1.0.2.schema.json +911 -0
- package/src/schemas/config-1.0.3.schema.json +911 -0
- package/src/schemas/config-1.0.4.schema.json +949 -0
- package/src/schemas/config-1.0.5.schema.json +949 -0
- package/src/schemas/config-1.0.6.schema.json +950 -0
- package/src/schemas/config-1.0.7.schema.json +950 -0
- package/src/schemas/config-1.0.8.schema.json +966 -0
- package/src/schemas/config-1.0.9.schema.json +965 -0
- package/src/schemas/fixtures/cellSets.good.json +16 -0
- package/src/schemas/fixtures/config-1.0.1.bad.json +1 -0
- package/src/schemas/fixtures/config-1.0.1.bad.message.json +11 -0
- package/src/schemas/fixtures/config-1.0.1.good.json +37 -0
- package/src/schemas/fixtures/config-1.0.2.bad.json +1 -0
- package/src/schemas/fixtures/config-1.0.2.bad.message.json +11 -0
- package/src/schemas/fixtures/config-1.0.2.good.json +38 -0
- package/src/schemas/fixtures/config-1.0.3.bad.json +1 -0
- package/src/schemas/fixtures/config-1.0.3.bad.message.json +11 -0
- package/src/schemas/fixtures/config-1.0.3.good.json +38 -0
- package/src/schemas/fixtures/obsSets.bad.json +1 -0
- package/src/schemas/fixtures/obsSets.bad.message.json +11 -0
- package/src/schemas/fixtures/obsSets.good.json +16 -0
- package/src/schemas/fixtures/obsSetsTabular.bad.json +1 -0
- package/src/schemas/fixtures/obsSetsTabular.bad.message.json +11 -0
- package/src/schemas/fixtures/obsSetsTabular.good.json +26 -0
- package/src/schemas/fixtures/raster.bad.json +1 -0
- package/src/schemas/fixtures/raster.bad.message.json +11 -0
- package/src/schemas/fixtures/raster.good.json +39 -0
- package/src/schemas/obsSets.schema.js +195 -0
- package/src/schemas/obsSetsTabular.schema.js +39 -0
- package/src/schemas/raster.schema.js +121 -0
- package/src/schemas/schema-schema.sh +34 -0
- package/{dist → src}/schemas/schema.test-old.js +0 -1
- package/src/shared-mui/components.js +79 -0
- package/src/shared-mui/container.js +212 -0
- package/src/shared-mui/styles.js +110 -0
- package/src/shared-plot-options/CellColorEncodingOption.js +43 -0
- package/src/shared-plot-options/OptionSelect.js +19 -0
- package/src/shared-plot-options/OptionsContainer.js +26 -0
- package/{dist → src}/shared-plot-options/index.js +0 -0
- package/src/shared-plot-options/styles.js +49 -0
- package/src/state/hooks.js +633 -0
- package/src/view-config-upgraders.js +628 -0
- package/src/view-config-utils.js +315 -0
- package/src/view-config-utils.test.fixtures.js +893 -0
- package/src/view-config-utils.test.js +95 -0
- package/src/view-config-versions.js +68 -0
- package/src/vitessce-grid-layout/VitessceGridLayout.js +158 -0
- package/src/vitessce-grid-layout/VitessceGridLayout.test.jsx +56 -0
- package/{dist → src}/vitessce-grid-layout/index.js +0 -0
- package/src/vitessce-grid-layout/layout-utils.js +76 -0
- package/src/vitessce-grid-layout/layout-utils.test.js +167 -0
- package/src/vitessce-grid-utils.js +160 -0
- package/dist/CallbackPublisher.js +0 -68
- package/dist/LoadingIndicator.js +0 -27
- package/dist/TitleInfo.js +0 -151
- package/dist/VitS.js +0 -112
- package/dist/VitessceGrid.js +0 -59
- package/dist/VitessceGrid.test.js +0 -36
- package/dist/Warning.js +0 -45
- package/dist/component-registry.js +0 -16
- package/dist/data/AbstractLoader.js +0 -35
- package/dist/data/LoaderResult.js +0 -7
- package/dist/data/loader-registry.js +0 -15
- package/dist/data-hook-utils.js +0 -175
- package/dist/data-hooks.js +0 -241
- package/dist/errors/AbstractLoaderError.js +0 -13
- package/dist/errors/DataSourceFetchError.js +0 -13
- package/dist/errors/DatasetNotFoundError.js +0 -17
- package/dist/errors/LoaderNotFoundError.js +0 -17
- package/dist/errors/LoaderValidationError.js +0 -15
- package/dist/errors/OptionsValidationError.js +0 -15
- package/dist/file-options-schemas-legacy.js +0 -136
- package/dist/file-options-schemas-legacy.test.js +0 -126
- package/dist/file-options-schemas.js +0 -322
- package/dist/file-options-schemas.test.js +0 -23
- package/dist/hooks.js +0 -215
- package/dist/joint-file-types-legacy.js +0 -278
- package/dist/joint-file-types-legacy.test.js +0 -400
- package/dist/joint-file-types.js +0 -148
- package/dist/joint-file-types.test.js +0 -139
- package/dist/plugins.test.fixtures.js +0 -76
- package/dist/plugins.test.js +0 -30
- package/dist/schemas/config-0.1.0.schema.json +0 -84
- package/dist/schemas/config-1.0.0.schema.json +0 -733
- package/dist/schemas/config-1.0.1.schema.json +0 -909
- package/dist/schemas/config-1.0.10.schema.json +0 -969
- package/dist/schemas/config-1.0.11.schema.json +0 -990
- package/dist/schemas/config-1.0.12.schema.json +0 -997
- package/dist/schemas/config-1.0.13.schema.json +0 -1013
- package/dist/schemas/config-1.0.14.schema.json +0 -1048
- package/dist/schemas/config-1.0.15.schema.json +0 -1048
- package/dist/schemas/config-1.0.15.schema.test.js +0 -22
- package/dist/schemas/config-1.0.2.schema.json +0 -910
- package/dist/schemas/config-1.0.3.schema.json +0 -910
- package/dist/schemas/config-1.0.4.schema.json +0 -949
- package/dist/schemas/config-1.0.5.schema.json +0 -949
- package/dist/schemas/config-1.0.6.schema.json +0 -949
- package/dist/schemas/config-1.0.7.schema.json +0 -949
- package/dist/schemas/config-1.0.8.schema.json +0 -965
- package/dist/schemas/config-1.0.9.schema.json +0 -965
- package/dist/schemas/obsSets.schema.js +0 -195
- package/dist/schemas/obsSetsTabular.schema.js +0 -38
- package/dist/schemas/raster.schema.js +0 -120
- package/dist/shared-mui/components.js +0 -35
- package/dist/shared-mui/container.js +0 -211
- package/dist/shared-mui/styles.js +0 -73
- package/dist/shared-plot-options/CellColorEncodingOption.js +0 -18
- package/dist/shared-plot-options/OptionSelect.js +0 -12
- package/dist/shared-plot-options/OptionsContainer.js +0 -12
- package/dist/shared-plot-options/styles.js +0 -48
- package/dist/state/hooks.js +0 -549
- package/dist/view-config-upgraders.js +0 -525
- package/dist/view-config-utils.js +0 -286
- package/dist/view-config-utils.test.fixtures.js +0 -765
- package/dist/view-config-utils.test.js +0 -66
- package/dist/view-config-versions.js +0 -47
- package/dist/vitessce-grid-layout/VitessceGridLayout.js +0 -95
- package/dist/vitessce-grid-layout/VitessceGridLayout.test.js +0 -40
- package/dist/vitessce-grid-layout/layout-utils.js +0 -60
- package/dist/vitessce-grid-layout/layout-utils.test.js +0 -146
- package/dist/vitessce-grid-utils.js +0 -140
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import {
|
|
3
|
+
getExistingScopesForCoordinationType,
|
|
4
|
+
initialize,
|
|
5
|
+
} from './view-config-utils';
|
|
6
|
+
import {
|
|
7
|
+
upgradeFrom0_1_0,
|
|
8
|
+
upgradeFrom1_0_0,
|
|
9
|
+
upgradeFrom1_0_15,
|
|
10
|
+
} from './view-config-upgraders';
|
|
11
|
+
import {
|
|
12
|
+
legacyViewConfig0_1_0,
|
|
13
|
+
upgradedLegacyViewConfig0_1_0,
|
|
14
|
+
legacyViewConfig1_0_0,
|
|
15
|
+
upgradedLegacyViewConfig1_0_0,
|
|
16
|
+
initializedViewConfig,
|
|
17
|
+
viewConfig1_0_10,
|
|
18
|
+
missingViewUids,
|
|
19
|
+
implicitPerDatasetCoordinations,
|
|
20
|
+
explicitPerDatasetCoordinations,
|
|
21
|
+
} from './view-config-utils.test.fixtures';
|
|
22
|
+
|
|
23
|
+
describe('src/app/view-config-utils.js', () => {
|
|
24
|
+
describe('getExistingScopesForCoordinationType', () => {
|
|
25
|
+
it('gets all scope names for a particular coordination type', () => {
|
|
26
|
+
const config = {
|
|
27
|
+
coordinationSpace: {
|
|
28
|
+
dataset: {
|
|
29
|
+
A: 'my-dataset-1',
|
|
30
|
+
B: 'my-dataset-2',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
layout: [
|
|
34
|
+
{
|
|
35
|
+
coordinationScopes: {
|
|
36
|
+
dataset: 'A',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
coordinationScopes: {
|
|
41
|
+
dataset: 'C',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
expect(getExistingScopesForCoordinationType(config, 'dataset')).toEqual(['A', 'B', 'C']);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('upgrade', () => {
|
|
51
|
+
it('upgrade view config from v0.1.0 to v1.0.0', () => {
|
|
52
|
+
expect(upgradeFrom0_1_0(legacyViewConfig0_1_0, 'A')).toEqual(upgradedLegacyViewConfig0_1_0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('upgrade view config from v1.0.0 to v1.0.1', () => {
|
|
56
|
+
expect(upgradeFrom1_0_0(legacyViewConfig1_0_0)).toEqual(upgradedLegacyViewConfig1_0_0);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('upgrade view config from v1.0.9 to v1.0.16', () => {
|
|
60
|
+
expect(upgradeFrom1_0_15(implicitPerDatasetCoordinations))
|
|
61
|
+
.toEqual(explicitPerDatasetCoordinations);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('initialize', () => {
|
|
66
|
+
it('initializes coordination space and component coordination scopes when initStrategy is auto', () => {
|
|
67
|
+
expect(initialize(viewConfig1_0_10)).toEqual(initializedViewConfig);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('does not change the result when initialized twice when initStrategy is auto', () => {
|
|
71
|
+
const firstResult = initialize(viewConfig1_0_10);
|
|
72
|
+
expect(firstResult).toEqual(initializedViewConfig);
|
|
73
|
+
const secondResult = initialize(firstResult);
|
|
74
|
+
expect(secondResult).toEqual(initializedViewConfig);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('generates unique ids for the view uid property when missing', () => {
|
|
78
|
+
const withUids = initialize(missingViewUids);
|
|
79
|
+
expect(withUids.layout[0].uid).toEqual('A');
|
|
80
|
+
// Should not overwrite uid when present:
|
|
81
|
+
expect(withUids.layout[1].uid).toEqual('some-umap');
|
|
82
|
+
expect(withUids.layout[2].uid).toEqual('B');
|
|
83
|
+
expect(withUids.layout[3].uid).toEqual('C');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('does not initialize when initStrategy is none', () => {
|
|
87
|
+
const noneInitViewConfig = {
|
|
88
|
+
...upgradedLegacyViewConfig0_1_0,
|
|
89
|
+
initStrategy: 'none',
|
|
90
|
+
};
|
|
91
|
+
expect(initialize(noneInitViewConfig).coordinationSpace)
|
|
92
|
+
.toEqual(noneInitViewConfig.coordinationSpace);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
2
|
+
import Ajv from 'ajv';
|
|
3
|
+
import configSchema0_1_0 from './schemas/config-0.1.0.schema.json';
|
|
4
|
+
import configSchema1_0_0 from './schemas/config-1.0.0.schema.json';
|
|
5
|
+
import configSchema1_0_1 from './schemas/config-1.0.1.schema.json';
|
|
6
|
+
import configSchema1_0_2 from './schemas/config-1.0.2.schema.json';
|
|
7
|
+
import configSchema1_0_3 from './schemas/config-1.0.3.schema.json';
|
|
8
|
+
import configSchema1_0_4 from './schemas/config-1.0.4.schema.json';
|
|
9
|
+
import configSchema1_0_5 from './schemas/config-1.0.5.schema.json';
|
|
10
|
+
import configSchema1_0_6 from './schemas/config-1.0.6.schema.json';
|
|
11
|
+
import configSchema1_0_7 from './schemas/config-1.0.7.schema.json';
|
|
12
|
+
import configSchema1_0_8 from './schemas/config-1.0.8.schema.json';
|
|
13
|
+
import configSchema1_0_9 from './schemas/config-1.0.9.schema.json';
|
|
14
|
+
import configSchema1_0_10 from './schemas/config-1.0.10.schema.json';
|
|
15
|
+
import configSchema1_0_11 from './schemas/config-1.0.11.schema.json';
|
|
16
|
+
import configSchema1_0_12 from './schemas/config-1.0.12.schema.json';
|
|
17
|
+
import configSchema1_0_13 from './schemas/config-1.0.13.schema.json';
|
|
18
|
+
import configSchema1_0_14 from './schemas/config-1.0.14.schema.json';
|
|
19
|
+
import configSchema1_0_15 from './schemas/config-1.0.15.schema.json';
|
|
20
|
+
import configSchema1_0_16 from './schemas/config-1.0.16.schema.json';
|
|
21
|
+
import obsSetsSchema from './schemas/obsSets.schema';
|
|
22
|
+
import rasterSchema from './schemas/raster.schema';
|
|
23
|
+
import {
|
|
24
|
+
upgradeFrom0_1_0,
|
|
25
|
+
upgradeFrom1_0_0,
|
|
26
|
+
upgradeFrom1_0_1,
|
|
27
|
+
upgradeFrom1_0_2,
|
|
28
|
+
upgradeFrom1_0_3,
|
|
29
|
+
upgradeFrom1_0_4,
|
|
30
|
+
upgradeFrom1_0_5,
|
|
31
|
+
upgradeFrom1_0_6,
|
|
32
|
+
upgradeFrom1_0_7,
|
|
33
|
+
upgradeFrom1_0_8,
|
|
34
|
+
upgradeFrom1_0_9,
|
|
35
|
+
upgradeFrom1_0_10,
|
|
36
|
+
upgradeFrom1_0_11,
|
|
37
|
+
upgradeFrom1_0_12,
|
|
38
|
+
upgradeFrom1_0_13,
|
|
39
|
+
upgradeFrom1_0_14,
|
|
40
|
+
upgradeFrom1_0_15,
|
|
41
|
+
} from './view-config-upgraders';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Mapping from view config versions to view config schemas and upgrade functions.
|
|
45
|
+
* Add a new schema and upgrade function here when bumping the view config version.
|
|
46
|
+
* The latest view config version should always have a null value instead of an upgrade function.
|
|
47
|
+
*/
|
|
48
|
+
export const LATEST_VERSION = '1.0.15';
|
|
49
|
+
export const SCHEMA_HANDLERS = {
|
|
50
|
+
'0.1.0': [new Ajv().compile(configSchema0_1_0), upgradeFrom0_1_0],
|
|
51
|
+
'1.0.0': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_0), upgradeFrom1_0_0],
|
|
52
|
+
'1.0.1': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_1), upgradeFrom1_0_1],
|
|
53
|
+
'1.0.2': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_2), upgradeFrom1_0_2],
|
|
54
|
+
'1.0.3': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_3), upgradeFrom1_0_3],
|
|
55
|
+
'1.0.4': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_4), upgradeFrom1_0_4],
|
|
56
|
+
'1.0.5': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_5), upgradeFrom1_0_5],
|
|
57
|
+
'1.0.6': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_6), upgradeFrom1_0_6],
|
|
58
|
+
'1.0.7': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_7), upgradeFrom1_0_7],
|
|
59
|
+
'1.0.8': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_8), upgradeFrom1_0_8],
|
|
60
|
+
'1.0.9': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_9), upgradeFrom1_0_9],
|
|
61
|
+
'1.0.10': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_10), upgradeFrom1_0_10],
|
|
62
|
+
'1.0.11': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_11), upgradeFrom1_0_11],
|
|
63
|
+
'1.0.12': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_12), upgradeFrom1_0_12],
|
|
64
|
+
'1.0.13': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_13), upgradeFrom1_0_13],
|
|
65
|
+
'1.0.14': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_14), upgradeFrom1_0_14],
|
|
66
|
+
'1.0.15': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_15), upgradeFrom1_0_15],
|
|
67
|
+
'1.0.16': [new Ajv().addSchema(obsSetsSchema).addSchema(rasterSchema).compile(configSchema1_0_16), null],
|
|
68
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Responsive, WidthProvider } from 'react-grid-layout-with-lodash';
|
|
3
|
+
import isEqual from 'lodash/isEqual';
|
|
4
|
+
import { getMaxRows, resolveLayout } from './layout-utils';
|
|
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
|
+
getComponent, padding, margin, draggableHandle: draggableHandleClass,
|
|
20
|
+
onResize, onResizeStop, rowHeight, theme, height,
|
|
21
|
+
onRemoveComponent, onLayoutChange: onLayoutChangeProp,
|
|
22
|
+
isBounded,
|
|
23
|
+
} = props;
|
|
24
|
+
|
|
25
|
+
const draggableHandle = `.${draggableHandleClass}`;
|
|
26
|
+
|
|
27
|
+
// If layout changes, update grid components.
|
|
28
|
+
const {
|
|
29
|
+
cols: gridCols, layouts: gridLayouts, breakpoints: gridBreakpoints, components: gridComponents,
|
|
30
|
+
} = resolveLayout(layout);
|
|
31
|
+
|
|
32
|
+
const maxRows = getMaxRows(gridLayouts);
|
|
33
|
+
|
|
34
|
+
// Inline CSS is generally avoided, but this saves the end-user a little work,
|
|
35
|
+
// and prevents class names from getting out of sync.
|
|
36
|
+
const style = (
|
|
37
|
+
<style>
|
|
38
|
+
{`
|
|
39
|
+
${draggableHandle} {
|
|
40
|
+
cursor: grab;
|
|
41
|
+
}
|
|
42
|
+
${draggableHandle}:active {
|
|
43
|
+
cursor: grabbing;
|
|
44
|
+
}
|
|
45
|
+
`}
|
|
46
|
+
</style>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// A bit of hacky feeling stuff to prevent users from stacking elements and forcing the
|
|
50
|
+
// grid boundary to change even when isBounded is set to true. It seems like react-grid-layout
|
|
51
|
+
// should support this through isBounded and maxRows, but neither of these actually works for the
|
|
52
|
+
// edge case where a user drags one element above another and forces the first element downwards.
|
|
53
|
+
//
|
|
54
|
+
// Additionally, react-grid-layout doesn't revert if you don't save a new/changed layouts. If you
|
|
55
|
+
// wish to do this, first you have to save the new layouts and render the grid with it, and then
|
|
56
|
+
// you can revert to the original layouts. Thus, we need one state for the current grid layouts,
|
|
57
|
+
// which gets called on every onLayoutChange. If the grid height is still valid, we then call
|
|
58
|
+
// onValidLayoutChange, otherwise we reset currentGridLayouts to lastValidGridLayouts.
|
|
59
|
+
//
|
|
60
|
+
// See the following GitHub issue for more information.
|
|
61
|
+
// https://github.com/react-grid-layout/react-grid-layout/issues/1104#issuecomment-827785217
|
|
62
|
+
const [currentGridLayouts, setCurrentGridLayouts] = useState(gridLayouts);
|
|
63
|
+
const [lastValidGridLayouts, setLastValidGridLayouts] = useState(gridLayouts);
|
|
64
|
+
|
|
65
|
+
const onValidLayoutChange = (newLayout) => {
|
|
66
|
+
if (newLayout.length === Object.entries(gridComponents).length) {
|
|
67
|
+
const newComponentProps = {};
|
|
68
|
+
newLayout.forEach((nextC) => {
|
|
69
|
+
const id = nextC.i;
|
|
70
|
+
const prevC = gridComponents[id];
|
|
71
|
+
if (prevC) {
|
|
72
|
+
const nextProps = {
|
|
73
|
+
x: nextC.x, y: nextC.y, w: nextC.w, h: nextC.h,
|
|
74
|
+
};
|
|
75
|
+
const prevProps = {
|
|
76
|
+
x: prevC.x, y: prevC.y, w: prevC.w, h: prevC.h,
|
|
77
|
+
};
|
|
78
|
+
if (!isEqual(nextProps, prevProps)) {
|
|
79
|
+
newComponentProps[id] = nextProps;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
if (Object.keys(newComponentProps).length > 0) {
|
|
84
|
+
onLayoutChangeProp(newComponentProps);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const onLayoutChange = (newLayout, allLayouts) => {
|
|
90
|
+
setCurrentGridLayouts(allLayouts);
|
|
91
|
+
if (!isBounded || getMaxRows({ ID: newLayout }) <= maxRows) {
|
|
92
|
+
onValidLayoutChange(newLayout);
|
|
93
|
+
setLastValidGridLayouts(allLayouts);
|
|
94
|
+
} else {
|
|
95
|
+
setCurrentGridLayouts(lastValidGridLayouts);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const saveCurrentLayouts = () => {
|
|
100
|
+
setLastValidGridLayouts(currentGridLayouts);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
const layoutChildren = Object.values(gridComponents).map((v) => {
|
|
105
|
+
const Component = getComponent(v.component);
|
|
106
|
+
|
|
107
|
+
const removeGridComponent = () => {
|
|
108
|
+
onRemoveComponent(v.uid);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div key={v.uid}>
|
|
113
|
+
<Component
|
|
114
|
+
{... v.props}
|
|
115
|
+
uuid={v.uid}
|
|
116
|
+
coordinationScopes={v.coordinationScopes}
|
|
117
|
+
coordinationScopesBy={v.coordinationScopesBy}
|
|
118
|
+
theme={theme}
|
|
119
|
+
removeGridComponent={removeGridComponent}
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
return (currentGridLayouts && gridComponents && gridBreakpoints && gridCols) && (
|
|
125
|
+
<>
|
|
126
|
+
{style}
|
|
127
|
+
<ResponsiveHeightGridLayout
|
|
128
|
+
className="layout"
|
|
129
|
+
cols={gridCols}
|
|
130
|
+
layouts={currentGridLayouts}
|
|
131
|
+
breakpoints={gridBreakpoints}
|
|
132
|
+
height={height}
|
|
133
|
+
rowHeight={
|
|
134
|
+
rowHeight
|
|
135
|
+
|| (
|
|
136
|
+
(window.innerHeight - 2 * padding - (maxRows - 1) * margin)
|
|
137
|
+
/ maxRows
|
|
138
|
+
)}
|
|
139
|
+
containerPadding={[padding, padding]}
|
|
140
|
+
margin={[margin, margin]}
|
|
141
|
+
draggableHandle={draggableHandle}
|
|
142
|
+
onLayoutChange={onLayoutChange}
|
|
143
|
+
isBounded={isBounded}
|
|
144
|
+
onResizeStart={saveCurrentLayouts}
|
|
145
|
+
onDragStart={saveCurrentLayouts}
|
|
146
|
+
onResize={onResize}
|
|
147
|
+
onResizeStop={onResizeStop}
|
|
148
|
+
>
|
|
149
|
+
{layoutChildren}
|
|
150
|
+
</ResponsiveHeightGridLayout>
|
|
151
|
+
</>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
VitessceGridLayout.defaultProps = {
|
|
156
|
+
padding: 10,
|
|
157
|
+
margin: 10,
|
|
158
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
2
|
+
import { cleanup, render, screen, waitForElement } from '@testing-library/react'
|
|
3
|
+
import { afterEach } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import { VitessceGridLayout } from './VitessceGridLayout';
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
cleanup()
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function FakeComponent(props) {
|
|
12
|
+
const { text } = props;
|
|
13
|
+
return <span>{text}</span>;
|
|
14
|
+
}
|
|
15
|
+
const layoutJson = {
|
|
16
|
+
columns: {
|
|
17
|
+
600: [0, 2, 4, 8],
|
|
18
|
+
},
|
|
19
|
+
components: [
|
|
20
|
+
{ component: 'FakeComponent',
|
|
21
|
+
uid: 'fake',
|
|
22
|
+
props: { text: 'Hello World' },
|
|
23
|
+
x: 0, y: 0, w: 2 },
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
describe('VitessceGridLayout.js', () => {
|
|
28
|
+
describe('<VitessceGridLayout />', () => {
|
|
29
|
+
it('mount() works', async () => {
|
|
30
|
+
const { container } = render(<VitessceGridLayout
|
|
31
|
+
layout={layoutJson}
|
|
32
|
+
getComponent={() => FakeComponent}
|
|
33
|
+
draggableHandle=".my-handle"
|
|
34
|
+
/>);
|
|
35
|
+
|
|
36
|
+
expect(await screen.findByText('Hello World'));
|
|
37
|
+
expect(container.querySelectorAll('.react-grid-item span:not(.react-resizable-handle)').length).toEqual(1);
|
|
38
|
+
|
|
39
|
+
const style = container.querySelectorAll('style');
|
|
40
|
+
expect(style.length).toEqual(1);
|
|
41
|
+
expect(style[0].textContent).toContain('.my-handle {');
|
|
42
|
+
expect(style[0].textContent).toContain('.my-handle:active {');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('rowHeight works', () => {
|
|
46
|
+
const { container } = render(<VitessceGridLayout
|
|
47
|
+
layout={layoutJson}
|
|
48
|
+
getComponent={() => FakeComponent}
|
|
49
|
+
draggableHandle=".my-handle"
|
|
50
|
+
rowHeight={123}
|
|
51
|
+
/>);
|
|
52
|
+
|
|
53
|
+
expect(container.querySelector('.react-grid-item').style.height).toEqual('123px');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import range from 'lodash/range';
|
|
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';
|
|
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
|
+
});
|