Skip to content

Commit dacb6f5

Browse files
authored
fix(BaseLayerSwitcher): add onClick callback function props to BaseLayerSwitcher and Zoom (#723)
* fix(BaseLayerSwitcher): an onButtonClick prop, remove defaultProps, refactor some code * fix(Zoom): add onButtonClick prop * chore(release): 1.11.1-beta.0 * chore: split callback functions up * chore(release): 1.11.1-beta.1 * chore: handle null callbacks, update tests * chore(release): 1.11.1-beta.2
1 parent 0c1da2c commit dacb6f5

File tree

7 files changed

+302
-325
lines changed

7 files changed

+302
-325
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "react-spatial",
33
"license": "MIT",
44
"description": "Components to build React map apps.",
5-
"version": "1.11.0",
5+
"version": "1.11.1-beta.2",
66
"dependencies": {
77
"@emotion/react": "^11.11.4",
88
"@emotion/styled": "^11.11.5",

src/components/BaseLayerSwitcher/BaseLayerSwitcher.js

Lines changed: 99 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,24 @@ const propTypes = {
4646
* @param {function} Translation function returning the translated string.
4747
*/
4848
t: PropTypes.func,
49-
};
5049

51-
const defaultProps = {
52-
className: "rs-base-layer-switcher",
53-
altText: "Source not found",
54-
titles: {
55-
button: "Base layers",
56-
openSwitcher: "Open Baselayer-Switcher",
57-
closeSwitcher: "Close Baselayer-Switcher",
58-
},
59-
closeButtonImage: <FaChevronLeft />,
60-
layerImages: undefined,
61-
t: (s) => {
62-
return s;
63-
},
50+
/**
51+
* Callback function on close button click.
52+
* @param {function} Callback function triggered when a switcher button is clicked. Takes the event as argument.
53+
*/
54+
onCloseButtonClick: PropTypes.func,
55+
56+
/**
57+
* Callback function on layer button click.
58+
* @param {function} Callback function triggered when a switcher button is clicked. Takes the event and the layer as arguments.
59+
*/
60+
onLayerButtonClick: PropTypes.func,
61+
62+
/**
63+
* Callback function on main switcher button click.
64+
* @param {function} Callback function triggered when a switcher button is clicked. Takes the event as argument.
65+
*/
66+
onSwitcherButtonClick: PropTypes.func,
6467
};
6568

6669
const getVisibleLayer = (layers) => {
@@ -90,6 +93,31 @@ const getImageStyle = (url) => {
9093
: null;
9194
};
9295

96+
function CloseButton({ onClick, tabIndex, title, children }) {
97+
return (
98+
<div
99+
className="rs-base-layer-switcher-close-btn"
100+
role="button"
101+
onClick={onClick}
102+
onKeyPress={(e) => {
103+
return e.which === 13 && onClick();
104+
}}
105+
tabIndex={tabIndex}
106+
aria-label={title}
107+
title={title}
108+
>
109+
{children}
110+
</div>
111+
);
112+
}
113+
114+
CloseButton.propTypes = {
115+
onClick: PropTypes.func.isRequired,
116+
tabIndex: PropTypes.string.isRequired,
117+
title: PropTypes.string.isRequired,
118+
children: PropTypes.node.isRequired,
119+
};
120+
93121
/**
94122
* The BaseLayerSwitcher component renders a button interface for switching the visible
95123
* [mobility-toolbox-js layer](https://mobility-toolbox-js.geops.io/api/identifiers%20html#ol-layers)
@@ -98,33 +126,26 @@ const getImageStyle = (url) => {
98126

99127
function BaseLayerSwitcher({
100128
layers,
101-
layerImages,
102-
className,
103-
altText,
104-
titles,
105-
closeButtonImage,
106-
t,
129+
layerImages = undefined,
130+
className = "rs-base-layer-switcher",
131+
altText = "Source not found",
132+
titles = {
133+
button: "Base layers",
134+
openSwitcher: "Open Baselayer-Switcher",
135+
closeSwitcher: "Close Baselayer-Switcher",
136+
},
137+
closeButtonImage = <FaChevronLeft />,
138+
onCloseButtonClick = null,
139+
onLayerButtonClick = null,
140+
onSwitcherButtonClick = null,
141+
t = (s) => s,
107142
}) {
108143
const [switcherOpen, setSwitcherOpen] = useState(false);
109144
const [isClosed, setIsClosed] = useState(true);
110145
const [currentLayer, setCurrentLayer] = useState(
111146
getVisibleLayer(layers) || layers[0],
112147
);
113148

114-
useEffect(() => {
115-
// Update the layer selected when a visibility changes.
116-
const olKeys = (layers || []).map((layer) => {
117-
return layer.on("change:visible", (evt) => {
118-
if (evt.target.visible && currentLayer !== evt.target) {
119-
setCurrentLayer(evt.target);
120-
}
121-
});
122-
});
123-
return () => {
124-
unByKey(olKeys);
125-
};
126-
}, [currentLayer, layers]);
127-
128149
/* Images are loaded from props if provided, fallback from layer */
129150
const images = layerImages
130151
? Object.keys(layerImages).map((layerImage) => {
@@ -137,12 +158,17 @@ function BaseLayerSwitcher({
137158
const openClass = switcherOpen ? " rs-open" : "";
138159
const hiddenStyle = switcherOpen && !isClosed ? "visible" : "hidden";
139160

140-
const handleSwitcherClick = () => {
161+
const handleSwitcherClick = (evt) => {
162+
const nextLayer = layers.find((layer) => {
163+
return !layer.visible;
164+
});
165+
const onButtonClick =
166+
layers.length === 2 ? onLayerButtonClick : onSwitcherButtonClick;
167+
if (onButtonClick) {
168+
onButtonClick(evt, nextLayer);
169+
}
141170
if (layers.length === 2) {
142171
/* On only two layer options the opener becomes a layer toggle button */
143-
const nextLayer = layers.find((layer) => {
144-
return !layer.visible;
145-
});
146172
if (currentLayer.setVisible) {
147173
currentLayer.setVisible(false);
148174
} else {
@@ -160,7 +186,10 @@ function BaseLayerSwitcher({
160186
return setSwitcherOpen(true) && setIsClosed(false);
161187
};
162188

163-
const onLayerSelect = (layer) => {
189+
const onLayerSelect = (layer, evt) => {
190+
if (onLayerButtonClick) {
191+
onLayerButtonClick(evt, layer);
192+
}
164193
if (!switcherOpen) {
165194
setSwitcherOpen(true);
166195
return;
@@ -214,30 +243,24 @@ function BaseLayerSwitcher({
214243
};
215244
}, [switcherOpen]);
216245

246+
useEffect(() => {
247+
// Update the layer selected when a visibility changes.
248+
const olKeys = (layers || []).map((layer) => {
249+
return layer.on("change:visible", (evt) => {
250+
if (evt.target.visible && currentLayer !== evt.target) {
251+
setCurrentLayer(evt.target);
252+
}
253+
});
254+
});
255+
return () => {
256+
unByKey(olKeys);
257+
};
258+
}, [currentLayer, layers]);
259+
217260
if (!layers || layers.length < 2 || !currentLayer) {
218261
return null;
219262
}
220263

221-
const toggleBtn = (
222-
<div className="rs-base-layer-switcher-btn-wrapper">
223-
<div
224-
className="rs-base-layer-switcher-close-btn"
225-
role="button"
226-
onClick={() => {
227-
return setSwitcherOpen(false);
228-
}}
229-
onKeyPress={(e) => {
230-
return e.which === 13 && setSwitcherOpen(false);
231-
}}
232-
tabIndex={switcherOpen ? "0" : "-1"}
233-
aria-label={titles.closeSwitcher}
234-
title={titles.closeSwitcher}
235-
>
236-
{closeButtonImage}
237-
</div>
238-
</div>
239-
);
240-
241264
return (
242265
<div className={`${className}${openClass}`}>
243266
<div
@@ -290,12 +313,12 @@ function BaseLayerSwitcher({
290313
role="button"
291314
title={t(layerName)}
292315
aria-label={t(layerName)}
293-
onClick={() => {
294-
return onLayerSelect(layer);
316+
onClick={(evt) => {
317+
return onLayerSelect(layer, evt);
295318
}}
296-
onKeyPress={(e) => {
297-
if (e.which === 13) {
298-
onLayerSelect(layer);
319+
onKeyPress={(evt) => {
320+
if (evt.which === 13) {
321+
onLayerSelect(layer, evt);
299322
}
300323
}}
301324
style={imageStyle}
@@ -311,12 +334,22 @@ function BaseLayerSwitcher({
311334
</div>
312335
);
313336
})}
314-
{toggleBtn}
337+
<CloseButton
338+
onClick={(evt) => {
339+
if (onCloseButtonClick) {
340+
onCloseButtonClick(evt);
341+
}
342+
setSwitcherOpen(false);
343+
}}
344+
tabIndex={switcherOpen ? "0" : "-1"}
345+
title={titles.closeSwitcher}
346+
>
347+
{closeButtonImage}
348+
</CloseButton>
315349
</div>
316350
);
317351
}
318352

319353
BaseLayerSwitcher.propTypes = propTypes;
320-
BaseLayerSwitcher.defaultProps = defaultProps;
321354

322355
export default BaseLayerSwitcher;

src/components/BaseLayerSwitcher/BaseLayerSwitcher.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
transition: 800ms width;
55
overflow: hidden;
66
display: flex;
7+
align-items: center;
78
padding: 2px;
89
pointer-events: none;
910

src/components/BaseLayerSwitcher/__snapshots__/BaseLayerSwitcher.test.js.snap

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,25 +64,23 @@ exports[`BaseLayerSwitcher matches snapshots using default properties. 1`] = `
6464
</span>
6565
</div>
6666
</div>
67-
<div class="rs-base-layer-switcher-btn-wrapper">
68-
<div class="rs-base-layer-switcher-close-btn"
69-
role="button"
70-
tabindex="-1"
71-
aria-label="Close Baselayer-Switcher"
72-
title="Close Baselayer-Switcher"
67+
<div class="rs-base-layer-switcher-close-btn"
68+
role="button"
69+
tabindex="-1"
70+
aria-label="Close Baselayer-Switcher"
71+
title="Close Baselayer-Switcher"
72+
>
73+
<svg stroke="currentColor"
74+
fill="currentColor"
75+
stroke-width="0"
76+
viewbox="0 0 320 512"
77+
height="1em"
78+
width="1em"
79+
xmlns="http://www.w3.org/2000/svg"
7380
>
74-
<svg stroke="currentColor"
75-
fill="currentColor"
76-
stroke-width="0"
77-
viewbox="0 0 320 512"
78-
height="1em"
79-
width="1em"
80-
xmlns="http://www.w3.org/2000/svg"
81-
>
82-
<path d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z">
83-
</path>
84-
</svg>
85-
</div>
81+
<path d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z">
82+
</path>
83+
</svg>
8684
</div>
8785
</div>
8886
`;

src/components/Zoom/Zoom.js

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,18 @@ const propTypes = {
4545
* Display a slider to zoom.
4646
*/
4747
zoomSlider: PropTypes.bool,
48-
};
4948

50-
const defaultProps = {
51-
titles: {
52-
zoomIn: "Zoom in",
53-
zoomOut: "Zoom out",
54-
},
55-
zoomInChildren: <FaPlus focusable={false} />,
56-
zoomOutChildren: <FaMinus focusable={false} />,
57-
zoomSlider: false,
58-
delta: 1,
49+
/**
50+
* Callback function on zoom-in button click.
51+
* @param {function} Callback function triggered when zoom-in button is clicked. Takes the event as argument.
52+
*/
53+
onZoomInButtonClick: PropTypes.func,
54+
55+
/**
56+
* Callback function on zoom-out button click.
57+
* @param {function} Callback function triggered when the zoom-out button is clicked. Takes the event as argument.
58+
*/
59+
onZoomOutButtonClick: PropTypes.func,
5960
};
6061

6162
const updateZoom = (map, delta) => {
@@ -79,32 +80,43 @@ const updateZoom = (map, delta) => {
7980
*/
8081
function Zoom({
8182
map,
82-
titles,
83-
zoomInChildren,
84-
zoomOutChildren,
85-
zoomSlider,
86-
delta,
83+
titles = {
84+
zoomIn: "Zoom in",
85+
zoomOut: "Zoom out",
86+
},
87+
zoomInChildren = <FaPlus focusable={false} />,
88+
zoomOutChildren = <FaMinus focusable={false} />,
89+
zoomSlider = false,
90+
onZoomInButtonClick = null,
91+
onZoomOutButtonClick = null,
92+
delta = 1,
8793
...other
8894
}) {
8995
const ref = useRef();
9096
const [currentZoom, setZoom] = useState();
9197

9298
const zoomIn = useCallback(
9399
(evt) => {
100+
if (onZoomInButtonClick) {
101+
onZoomInButtonClick(evt);
102+
}
94103
if (!evt.which || evt.which === 13) {
95104
updateZoom(map, delta);
96105
}
97106
},
98-
[delta, map],
107+
[delta, map, onZoomInButtonClick],
99108
);
100109

101110
const zoomOut = useCallback(
102111
(evt) => {
112+
if (onZoomOutButtonClick) {
113+
onZoomOutButtonClick(evt);
114+
}
103115
if (!evt.which || evt.which === 13) {
104116
updateZoom(map, -delta);
105117
}
106118
},
107-
[delta, map],
119+
[delta, map, onZoomOutButtonClick],
108120
);
109121

110122
const zoomInDisabled = useMemo(() => {
@@ -175,6 +187,5 @@ function Zoom({
175187
}
176188

177189
Zoom.propTypes = propTypes;
178-
Zoom.defaultProps = defaultProps;
179190

180191
export default React.memo(Zoom);

0 commit comments

Comments
 (0)