@eeacms/volto-bise-policy 1.3.11 → 1.3.13
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/CHANGELOG.md +37 -1
- package/package.json +2 -1
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyCard.jsx +404 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyCard.test.jsx +366 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyExplorerView.jsx +192 -91
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyExplorerView.test.jsx +205 -58
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyMap.jsx +329 -178
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyMap.test.jsx +243 -123
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyMapLazy.jsx +61 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyMapLazy.test.jsx +89 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyResults.jsx +128 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyResults.test.jsx +160 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/FacetPopover.jsx +180 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/FacetPopover.test.jsx +125 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/FilterPanel.jsx +171 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/FilterPanel.test.jsx +181 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/Pagination.jsx +81 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/filters.js +350 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/filters.test.js +399 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/hooks.js +28 -4
- package/src/components/manage/Blocks/CaseStudyExplorer/hooks.test.js +73 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/messages.js +138 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/styles.less +1157 -317
- package/src/components/manage/Blocks/CaseStudyExplorer/testUtils.js +44 -46
- package/src/components/manage/Blocks/CaseStudyExplorer/utils.js +68 -184
- package/src/components/manage/Blocks/CaseStudyExplorer/utils.test.js +91 -158
- package/src/index.js +39 -0
- package/src/openlayers.js +51 -0
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyFilters.jsx +0 -383
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyFilters.test.jsx +0 -90
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyListing.jsx +0 -342
- package/src/components/manage/Blocks/CaseStudyExplorer/CaseStudyListing.test.jsx +0 -196
- package/src/components/manage/Blocks/CaseStudyExplorer/FeatureDisplay.jsx +0 -24
- package/src/components/manage/Blocks/CaseStudyExplorer/FeatureDisplay.test.jsx +0 -49
- package/src/components/manage/Blocks/CaseStudyExplorer/FeatureInteraction.jsx +0 -100
- package/src/components/manage/Blocks/CaseStudyExplorer/FeatureInteraction.test.jsx +0 -189
- package/src/components/manage/Blocks/CaseStudyExplorer/InfoOverlay.jsx +0 -111
- package/src/components/manage/Blocks/CaseStudyExplorer/InfoOverlay.test.jsx +0 -159
- package/src/components/manage/Blocks/CaseStudyExplorer/styles.less_old +0 -201
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { withOpenLayers } from '@eeacms/volto-openlayers-map';
|
|
3
|
-
import { useMapContext } from '@eeacms/volto-openlayers-map/api';
|
|
4
|
-
import { scrollToElement, zoomMapToFeatures } from './utils';
|
|
5
|
-
|
|
6
|
-
export const useStyles = ({ ol }) => {
|
|
7
|
-
const selected = React.useMemo(
|
|
8
|
-
() =>
|
|
9
|
-
new ol.style.Style({
|
|
10
|
-
image: new ol.style.Circle({
|
|
11
|
-
radius: 12,
|
|
12
|
-
fill: new ol.style.Fill({
|
|
13
|
-
color: '#ccc',
|
|
14
|
-
}),
|
|
15
|
-
stroke: new ol.style.Stroke({
|
|
16
|
-
color: '#fff',
|
|
17
|
-
width: 0,
|
|
18
|
-
}),
|
|
19
|
-
}),
|
|
20
|
-
}),
|
|
21
|
-
[ol.style],
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const selectStyle = React.useCallback(() => {
|
|
25
|
-
selected.image_.getFill().setColor('#0A99FF');
|
|
26
|
-
return selected;
|
|
27
|
-
}, [selected]);
|
|
28
|
-
|
|
29
|
-
return { selected, selectStyle };
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
function FeatureInteraction({ onFeatureSelect, hideFilters, ol }) {
|
|
33
|
-
const { map } = useMapContext();
|
|
34
|
-
const { selectStyle } = useStyles({ ol });
|
|
35
|
-
|
|
36
|
-
// Keep callbacks current without recreating the OpenLayers interaction.
|
|
37
|
-
const onFeatureSelectRef = React.useRef(onFeatureSelect);
|
|
38
|
-
onFeatureSelectRef.current = onFeatureSelect;
|
|
39
|
-
const hideFiltersRef = React.useRef(hideFilters);
|
|
40
|
-
hideFiltersRef.current = hideFilters;
|
|
41
|
-
const olRef = React.useRef(ol);
|
|
42
|
-
olRef.current = ol;
|
|
43
|
-
|
|
44
|
-
// Select stores the selected rendered cluster feature and applies its style
|
|
45
|
-
// directly to that feature. Recreating it after selectedCase changes clears
|
|
46
|
-
// that collection and restores the feature's original style.
|
|
47
|
-
const selectRef = React.useRef(null);
|
|
48
|
-
if (!selectRef.current) {
|
|
49
|
-
selectRef.current = new ol.interaction.Select({
|
|
50
|
-
condition: ol.condition.click,
|
|
51
|
-
style: hideFilters ? null : selectStyle,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
const select = selectRef.current;
|
|
55
|
-
|
|
56
|
-
React.useEffect(() => {
|
|
57
|
-
if (!map) return;
|
|
58
|
-
|
|
59
|
-
const onSelect = (e) => {
|
|
60
|
-
const features = e.target.getFeatures().getArray();
|
|
61
|
-
|
|
62
|
-
features.forEach((feature) => {
|
|
63
|
-
const subfeatures = feature.values_.features;
|
|
64
|
-
if (subfeatures.length === 1) {
|
|
65
|
-
const selectedFeature = subfeatures[0].values_;
|
|
66
|
-
if (hideFiltersRef.current) {
|
|
67
|
-
const url = window.location.origin + selectedFeature.path;
|
|
68
|
-
// window.open(url);
|
|
69
|
-
window.location.href = url;
|
|
70
|
-
}
|
|
71
|
-
onFeatureSelectRef.current(selectedFeature);
|
|
72
|
-
scrollToElement('ol-map-container');
|
|
73
|
-
} else {
|
|
74
|
-
onFeatureSelectRef.current(null);
|
|
75
|
-
zoomMapToFeatures({ map, features: subfeatures, ol: olRef.current });
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
select.on('select', onSelect);
|
|
81
|
-
map.addInteraction(select);
|
|
82
|
-
|
|
83
|
-
const onPointerMove = (e) => {
|
|
84
|
-
const pixel = map.getEventPixel(e.originalEvent);
|
|
85
|
-
const hit = map.hasFeatureAtPixel(pixel);
|
|
86
|
-
map.getViewport().style.cursor = hit ? 'pointer' : '';
|
|
87
|
-
};
|
|
88
|
-
map.on('pointermove', onPointerMove);
|
|
89
|
-
|
|
90
|
-
return () => {
|
|
91
|
-
select.un('select', onSelect);
|
|
92
|
-
map.un('pointermove', onPointerMove);
|
|
93
|
-
map.removeInteraction(select);
|
|
94
|
-
};
|
|
95
|
-
}, [map, select]);
|
|
96
|
-
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export default withOpenLayers(FeatureInteraction);
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render } from '@testing-library/react';
|
|
3
|
-
import FeatureInteraction from './FeatureInteraction';
|
|
4
|
-
import { scrollToElement, zoomMapToFeatures } from './utils';
|
|
5
|
-
import { createMockOl } from './testUtils';
|
|
6
|
-
|
|
7
|
-
jest.mock('@eeacms/volto-openlayers-map', () => ({
|
|
8
|
-
withOpenLayers: (Comp) => Comp,
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
const addInteraction = jest.fn();
|
|
12
|
-
const removeInteraction = jest.fn();
|
|
13
|
-
const onMap = jest.fn();
|
|
14
|
-
const getEventPixel = jest.fn(() => [10, 20]);
|
|
15
|
-
const hasFeatureAtPixel = jest.fn(() => true);
|
|
16
|
-
const getViewport = jest.fn(() => ({ style: {} }));
|
|
17
|
-
|
|
18
|
-
jest.mock('@eeacms/volto-openlayers-map/api', () => ({
|
|
19
|
-
useMapContext: () => ({
|
|
20
|
-
map: mockMap,
|
|
21
|
-
}),
|
|
22
|
-
}));
|
|
23
|
-
|
|
24
|
-
jest.mock('./utils', () => ({
|
|
25
|
-
scrollToElement: jest.fn(),
|
|
26
|
-
zoomMapToFeatures: jest.fn(),
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
let mockSelectCb;
|
|
30
|
-
const mockOl = createMockOl({
|
|
31
|
-
interaction: {
|
|
32
|
-
Select: jest.fn(() => ({
|
|
33
|
-
on: jest.fn((evt, cb) => {
|
|
34
|
-
if (evt === 'select') {
|
|
35
|
-
mockSelectCb = cb;
|
|
36
|
-
}
|
|
37
|
-
}),
|
|
38
|
-
un: jest.fn(),
|
|
39
|
-
})),
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const mockMap = {
|
|
44
|
-
addInteraction,
|
|
45
|
-
removeInteraction,
|
|
46
|
-
on: onMap,
|
|
47
|
-
un: jest.fn(),
|
|
48
|
-
getEventPixel,
|
|
49
|
-
hasFeatureAtPixel,
|
|
50
|
-
getViewport,
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
function triggerSelectEvent(subfeatures) {
|
|
54
|
-
const feature = { values_: { features: subfeatures } };
|
|
55
|
-
const mockE = {
|
|
56
|
-
target: {
|
|
57
|
-
getFeatures: () => ({
|
|
58
|
-
getArray: () => [feature],
|
|
59
|
-
}),
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
mockSelectCb(mockE);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
describe('FeatureInteraction', () => {
|
|
66
|
-
beforeEach(() => {
|
|
67
|
-
jest.clearAllMocks();
|
|
68
|
-
mockSelectCb = null;
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('configures the selection style when filters are visible', () => {
|
|
72
|
-
render(
|
|
73
|
-
<FeatureInteraction
|
|
74
|
-
onFeatureSelect={jest.fn()}
|
|
75
|
-
hideFilters={false}
|
|
76
|
-
selectedCase={null}
|
|
77
|
-
ol={mockOl}
|
|
78
|
-
/>,
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
const options = mockOl.interaction.Select.mock.calls[0][0];
|
|
82
|
-
expect(options.style).toEqual(expect.any(Function));
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('keeps one Select interaction across rerenders', () => {
|
|
86
|
-
const { rerender } = render(
|
|
87
|
-
<FeatureInteraction
|
|
88
|
-
onFeatureSelect={jest.fn()}
|
|
89
|
-
hideFilters={false}
|
|
90
|
-
selectedCase={null}
|
|
91
|
-
ol={mockOl}
|
|
92
|
-
/>,
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
rerender(
|
|
96
|
-
<FeatureInteraction
|
|
97
|
-
onFeatureSelect={jest.fn()}
|
|
98
|
-
hideFilters={false}
|
|
99
|
-
selectedCase={{ path: '/selected' }}
|
|
100
|
-
ol={{ ...mockOl }}
|
|
101
|
-
/>,
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
expect(mockOl.interaction.Select).toHaveBeenCalledTimes(1);
|
|
105
|
-
expect(addInteraction).toHaveBeenCalledTimes(1);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('selects a single feature and calls onFeatureSelect + scrollToElement (hideFilters=false)', () => {
|
|
109
|
-
const onFeatureSelect = jest.fn();
|
|
110
|
-
const singleFeature = {
|
|
111
|
-
values_: { path: '/path1', geometry: {}, foo: 'bar' },
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
render(
|
|
115
|
-
<FeatureInteraction
|
|
116
|
-
onFeatureSelect={onFeatureSelect}
|
|
117
|
-
hideFilters={false}
|
|
118
|
-
selectedCase={null}
|
|
119
|
-
ol={mockOl}
|
|
120
|
-
/>,
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
triggerSelectEvent([singleFeature]);
|
|
124
|
-
|
|
125
|
-
expect(onFeatureSelect).toHaveBeenCalledWith(singleFeature.values_);
|
|
126
|
-
expect(scrollToElement).toHaveBeenCalledWith('ol-map-container');
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('selects a single feature and changes window.location when hideFilters=true', () => {
|
|
130
|
-
const onFeatureSelect = jest.fn();
|
|
131
|
-
const singleFeature = { values_: { path: '/go-here', geometry: {} } };
|
|
132
|
-
delete window.location;
|
|
133
|
-
window.location = { href: '' };
|
|
134
|
-
|
|
135
|
-
render(
|
|
136
|
-
<FeatureInteraction
|
|
137
|
-
onFeatureSelect={onFeatureSelect}
|
|
138
|
-
hideFilters={true}
|
|
139
|
-
selectedCase={null}
|
|
140
|
-
ol={mockOl}
|
|
141
|
-
/>,
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
triggerSelectEvent([singleFeature]);
|
|
145
|
-
|
|
146
|
-
expect(window.location.href).toContain('/go-here');
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('selects multiple features and calls zoomMapToFeatures', () => {
|
|
150
|
-
const onFeatureSelect = jest.fn();
|
|
151
|
-
const subfeatures = [
|
|
152
|
-
{ values_: { path: '/f1' } },
|
|
153
|
-
{ values_: { path: '/f2' } },
|
|
154
|
-
];
|
|
155
|
-
|
|
156
|
-
render(
|
|
157
|
-
<FeatureInteraction
|
|
158
|
-
onFeatureSelect={onFeatureSelect}
|
|
159
|
-
hideFilters={false}
|
|
160
|
-
selectedCase={null}
|
|
161
|
-
ol={mockOl}
|
|
162
|
-
/>,
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
triggerSelectEvent(subfeatures);
|
|
166
|
-
|
|
167
|
-
expect(onFeatureSelect).toHaveBeenCalledWith(null);
|
|
168
|
-
expect(zoomMapToFeatures).toHaveBeenCalledWith({
|
|
169
|
-
map: mockMap,
|
|
170
|
-
features: subfeatures,
|
|
171
|
-
ol: mockOl,
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('adds and removes interaction on mount/unmount', () => {
|
|
176
|
-
const { unmount } = render(
|
|
177
|
-
<FeatureInteraction
|
|
178
|
-
onFeatureSelect={jest.fn()}
|
|
179
|
-
hideFilters={false}
|
|
180
|
-
selectedCase={null}
|
|
181
|
-
ol={mockOl}
|
|
182
|
-
/>,
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
expect(addInteraction).toHaveBeenCalled();
|
|
186
|
-
unmount();
|
|
187
|
-
expect(removeInteraction).toHaveBeenCalled();
|
|
188
|
-
});
|
|
189
|
-
});
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { useMapContext } from '@eeacms/volto-openlayers-map/api';
|
|
3
|
-
import { withOpenLayers } from '@eeacms/volto-openlayers-map';
|
|
4
|
-
import FeatureDisplay from './FeatureDisplay';
|
|
5
|
-
import { usePrevious } from '@plone/volto/helpers/Utils/usePrevious';
|
|
6
|
-
|
|
7
|
-
function InfoOverlay({
|
|
8
|
-
selectedFeature,
|
|
9
|
-
onFeatureSelect,
|
|
10
|
-
layerId,
|
|
11
|
-
hideFilters,
|
|
12
|
-
ol,
|
|
13
|
-
}) {
|
|
14
|
-
const { map } = useMapContext();
|
|
15
|
-
const [tooltip, setTooltipRef] = React.useState();
|
|
16
|
-
const [showTooltip, setShowTooltip] = React.useState();
|
|
17
|
-
const overlayRef = React.useRef(null);
|
|
18
|
-
|
|
19
|
-
// `ol` is a fresh object literal on every render (see withOpenLayers) and
|
|
20
|
-
// `onFeatureSelect`/`hideFilters` can change identity; keep them in refs so
|
|
21
|
-
// the overlay and its map handler are created only once per mount.
|
|
22
|
-
const olRef = React.useRef(ol);
|
|
23
|
-
olRef.current = ol;
|
|
24
|
-
const onFeatureSelectRef = React.useRef(onFeatureSelect);
|
|
25
|
-
onFeatureSelectRef.current = onFeatureSelect;
|
|
26
|
-
const hideFiltersRef = React.useRef(hideFilters);
|
|
27
|
-
hideFiltersRef.current = hideFilters;
|
|
28
|
-
|
|
29
|
-
const prevLayerId = usePrevious(layerId);
|
|
30
|
-
|
|
31
|
-
React.useEffect(() => {
|
|
32
|
-
if (prevLayerId && layerId !== prevLayerId) {
|
|
33
|
-
setShowTooltip(false);
|
|
34
|
-
}
|
|
35
|
-
}, [layerId, prevLayerId]);
|
|
36
|
-
|
|
37
|
-
React.useEffect(() => {
|
|
38
|
-
if (!(map && tooltip)) return;
|
|
39
|
-
|
|
40
|
-
const overlay = new olRef.current.Overlay({
|
|
41
|
-
element: tooltip,
|
|
42
|
-
positioning: 'bottom-center',
|
|
43
|
-
offset: [0, -10],
|
|
44
|
-
stopEvent: false,
|
|
45
|
-
});
|
|
46
|
-
overlayRef.current = overlay;
|
|
47
|
-
map.addOverlay(overlay);
|
|
48
|
-
|
|
49
|
-
function handler(evt) {
|
|
50
|
-
const { pixel, target } = evt;
|
|
51
|
-
const features = target.getFeaturesAtPixel(pixel);
|
|
52
|
-
const popupOverlay = overlay.element; // document.getElementById('popup-overlay');
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
features.length &&
|
|
56
|
-
!hideFiltersRef.current // && !isCluster(features)
|
|
57
|
-
) {
|
|
58
|
-
setShowTooltip(true);
|
|
59
|
-
} else {
|
|
60
|
-
// handle a click in an overlay popup
|
|
61
|
-
if (evt.originalEvent.target.tagName === 'A') return;
|
|
62
|
-
setShowTooltip(false);
|
|
63
|
-
popupOverlay.style.display = 'none';
|
|
64
|
-
onFeatureSelectRef.current(null);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
map.on('click', handler);
|
|
69
|
-
|
|
70
|
-
return () => {
|
|
71
|
-
map.un('click', handler);
|
|
72
|
-
map.removeOverlay(overlay);
|
|
73
|
-
overlayRef.current = null;
|
|
74
|
-
};
|
|
75
|
-
}, [map, tooltip]);
|
|
76
|
-
|
|
77
|
-
// Anchor the popup to the selected case study and show it above the marker.
|
|
78
|
-
// Selecting a cluster (null feature) or clicking the map clears the popup.
|
|
79
|
-
React.useEffect(() => {
|
|
80
|
-
const overlay = overlayRef.current;
|
|
81
|
-
if (!overlay) return;
|
|
82
|
-
|
|
83
|
-
const coordinates = selectedFeature?.geometry?.flatCoordinates;
|
|
84
|
-
if (coordinates) {
|
|
85
|
-
overlay.setPosition(coordinates);
|
|
86
|
-
setShowTooltip(true);
|
|
87
|
-
} else {
|
|
88
|
-
overlay.setPosition(undefined);
|
|
89
|
-
setShowTooltip(false);
|
|
90
|
-
}
|
|
91
|
-
}, [selectedFeature, tooltip]);
|
|
92
|
-
|
|
93
|
-
const [isClient, setIsClient] = React.useState(false);
|
|
94
|
-
React.useEffect(() => setIsClient(true), []);
|
|
95
|
-
|
|
96
|
-
return isClient ? (
|
|
97
|
-
<div
|
|
98
|
-
id="popup-overlay"
|
|
99
|
-
style={{
|
|
100
|
-
position: 'absolute',
|
|
101
|
-
zIndex: 1,
|
|
102
|
-
visibility: showTooltip ? 'visible' : 'hidden',
|
|
103
|
-
}}
|
|
104
|
-
ref={setTooltipRef}
|
|
105
|
-
>
|
|
106
|
-
{selectedFeature ? <FeatureDisplay feature={selectedFeature} /> : null}
|
|
107
|
-
</div>
|
|
108
|
-
) : null;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export default withOpenLayers(InfoOverlay);
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render } from '@testing-library/react';
|
|
3
|
-
import InfoOverlay from './InfoOverlay';
|
|
4
|
-
import '@testing-library/jest-dom';
|
|
5
|
-
|
|
6
|
-
jest.mock('@eeacms/volto-openlayers-map', () => ({
|
|
7
|
-
withOpenLayers: (Comp) => Comp,
|
|
8
|
-
}));
|
|
9
|
-
|
|
10
|
-
const onMap = jest.fn();
|
|
11
|
-
const unMap = jest.fn();
|
|
12
|
-
const addOverlay = jest.fn();
|
|
13
|
-
const removeOverlay = jest.fn();
|
|
14
|
-
jest.mock('@eeacms/volto-openlayers-map/api', () => ({
|
|
15
|
-
useMapContext: () => ({
|
|
16
|
-
map: mockMap,
|
|
17
|
-
}),
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
jest.mock('./FeatureDisplay', () => () => (
|
|
21
|
-
<div data-testid="feature-display" />
|
|
22
|
-
));
|
|
23
|
-
|
|
24
|
-
const mockAddOverlayInstance = {};
|
|
25
|
-
jest.mock('ol/Overlay', () => {
|
|
26
|
-
return jest.fn().mockImplementation((config) => {
|
|
27
|
-
return { ...mockAddOverlayInstance, element: config.element };
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const mockOl = {
|
|
32
|
-
Overlay: jest.fn((config) => ({
|
|
33
|
-
element: config.element,
|
|
34
|
-
setPosition: jest.fn(),
|
|
35
|
-
})),
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const mockMap = {
|
|
39
|
-
on: onMap,
|
|
40
|
-
un: unMap,
|
|
41
|
-
addOverlay,
|
|
42
|
-
removeOverlay,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
jest.mock('@plone/volto/helpers/Utils/usePrevious', () => ({
|
|
46
|
-
usePrevious: (val) => undefined,
|
|
47
|
-
}));
|
|
48
|
-
|
|
49
|
-
const defaultProps = {
|
|
50
|
-
layerId: 'layer1',
|
|
51
|
-
hideFilters: false,
|
|
52
|
-
ol: mockOl,
|
|
53
|
-
onFeatureSelect: jest.fn(),
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
describe('InfoOverlay', () => {
|
|
57
|
-
let overlayElement;
|
|
58
|
-
beforeEach(() => {
|
|
59
|
-
jest.clearAllMocks();
|
|
60
|
-
overlayElement = document.createElement('div');
|
|
61
|
-
overlayElement.id = 'popup-overlay';
|
|
62
|
-
document.body.appendChild(overlayElement);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
afterEach(() => {
|
|
66
|
-
document.body.innerHTML = '';
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('renders FeatureDisplay when selectedFeature is provided', () => {
|
|
70
|
-
const { getByTestId } = render(
|
|
71
|
-
<InfoOverlay {...defaultProps} selectedFeature={{ id: 1 }} />,
|
|
72
|
-
);
|
|
73
|
-
expect(getByTestId('feature-display')).toBeInTheDocument();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('does not render FeatureDisplay when selectedFeature is null', () => {
|
|
77
|
-
const { queryByTestId } = render(
|
|
78
|
-
<InfoOverlay {...defaultProps} selectedFeature={null} />,
|
|
79
|
-
);
|
|
80
|
-
expect(queryByTestId('feature-display')).toBeNull();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('attaches and detaches click handler on map', () => {
|
|
84
|
-
const { unmount } = render(
|
|
85
|
-
<InfoOverlay {...defaultProps} selectedFeature={null} />,
|
|
86
|
-
);
|
|
87
|
-
expect(addOverlay).toHaveBeenCalled();
|
|
88
|
-
expect(onMap).toHaveBeenCalledWith('click', expect.any(Function));
|
|
89
|
-
|
|
90
|
-
unmount();
|
|
91
|
-
expect(unMap).toHaveBeenCalledWith('click', expect.any(Function));
|
|
92
|
-
expect(removeOverlay).toHaveBeenCalled();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('click handler hides tooltip when no features found', () => {
|
|
96
|
-
const onFeatureSelect = jest.fn();
|
|
97
|
-
render(
|
|
98
|
-
<InfoOverlay
|
|
99
|
-
{...defaultProps}
|
|
100
|
-
selectedFeature={{}}
|
|
101
|
-
onFeatureSelect={onFeatureSelect}
|
|
102
|
-
/>,
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
const handler = onMap.mock.calls.find(([event]) => event === 'click')[1];
|
|
106
|
-
|
|
107
|
-
const evt = {
|
|
108
|
-
pixel: [0, 0],
|
|
109
|
-
target: { getFeaturesAtPixel: () => [] },
|
|
110
|
-
originalEvent: { target: { tagName: 'DIV' } },
|
|
111
|
-
};
|
|
112
|
-
handler(evt);
|
|
113
|
-
|
|
114
|
-
expect(onFeatureSelect).toHaveBeenCalledWith(null);
|
|
115
|
-
const overlay = mockOl.Overlay.mock.results[0].value;
|
|
116
|
-
expect(overlay.element.style.display).toBe('none');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('ignores click when target is a link (A tag)', () => {
|
|
120
|
-
const onFeatureSelect = jest.fn();
|
|
121
|
-
render(
|
|
122
|
-
<InfoOverlay
|
|
123
|
-
{...defaultProps}
|
|
124
|
-
selectedFeature={{}}
|
|
125
|
-
onFeatureSelect={onFeatureSelect}
|
|
126
|
-
/>,
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const handler = onMap.mock.calls.find(([event]) => event === 'click')[1];
|
|
130
|
-
|
|
131
|
-
const evt = {
|
|
132
|
-
pixel: [0, 0],
|
|
133
|
-
target: { getFeaturesAtPixel: () => [] },
|
|
134
|
-
originalEvent: { target: { tagName: 'A' } },
|
|
135
|
-
};
|
|
136
|
-
handler(evt);
|
|
137
|
-
|
|
138
|
-
expect(onFeatureSelect).not.toHaveBeenCalled();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('positions the overlay over the selected feature', () => {
|
|
142
|
-
render(
|
|
143
|
-
<InfoOverlay
|
|
144
|
-
{...defaultProps}
|
|
145
|
-
selectedFeature={{ geometry: { flatCoordinates: [10, 20] } }}
|
|
146
|
-
/>,
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
const overlay = mockOl.Overlay.mock.results[0].value;
|
|
150
|
-
expect(overlay.setPosition).toHaveBeenCalledWith([10, 20]);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('clears the overlay position when no feature is selected', () => {
|
|
154
|
-
render(<InfoOverlay {...defaultProps} selectedFeature={null} />);
|
|
155
|
-
|
|
156
|
-
const overlay = mockOl.Overlay.mock.results[0].value;
|
|
157
|
-
expect(overlay.setPosition).toHaveBeenCalledWith(undefined);
|
|
158
|
-
});
|
|
159
|
-
});
|