diff --git a/__mocks__/maplibre-gl.js b/__mocks__/maplibre-gl.js
index 8686089825..716db4b665 100644
--- a/__mocks__/maplibre-gl.js
+++ b/__mocks__/maplibre-gl.js
@@ -4,6 +4,8 @@ const { LngLat, NavigationControl } = require('maplibre-gl');
class MockMap extends EventEmitter {
addControl = jest.fn();
removeControl = jest.fn();
+ zoomIn = jest.fn();
+ zoomOut = jest.fn();
}
const MockMapInstance = new MockMap();
diff --git a/res/css/_components.scss b/res/css/_components.scss
index 09a5fd6e14..79efb3e89b 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -13,6 +13,7 @@
@import "./components/views/location/_Marker.scss";
@import "./components/views/location/_ShareDialogButtons.scss";
@import "./components/views/location/_ShareType.scss";
+@import "./components/views/location/_ZoomButtons.scss";
@import "./components/views/spaces/_QuickThemeSwitcher.scss";
@import "./structures/_AutoHideScrollbar.scss";
@import "./structures/_BackdropPanel.scss";
diff --git a/res/css/components/views/location/_ZoomButtons.scss b/res/css/components/views/location/_ZoomButtons.scss
new file mode 100644
index 0000000000..59d52477f9
--- /dev/null
+++ b/res/css/components/views/location/_ZoomButtons.scss
@@ -0,0 +1,45 @@
+/*
+Copyright 2022 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+.mx_ZoomButtons {
+ position: absolute;
+ bottom: $spacing-32;
+ right: $spacing-24;
+}
+
+.mx_ZoomButtons_button {
+ @mixin ButtonResetDefault;
+
+ margin-top: $spacing-8;
+ border-radius: 4px;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+
+ height: 24px;
+ width: 24px;
+
+ background: $background;
+ box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.25);
+}
+
+.mx_ZoomButtons_icon {
+ height: 10px;
+ width: 10px;
+
+ color: $primary-content;
+}
diff --git a/res/img/element-icons/minus-button.svg b/res/img/element-icons/minus-button.svg
index ca61c23b76..6e7ea87c0b 100644
--- a/res/img/element-icons/minus-button.svg
+++ b/res/img/element-icons/minus-button.svg
@@ -1,3 +1,3 @@
diff --git a/res/img/element-icons/plus-button.svg b/res/img/element-icons/plus-button.svg
index cbc25c4553..9a14c85ee5 100644
--- a/res/img/element-icons/plus-button.svg
+++ b/res/img/element-icons/plus-button.svg
@@ -1,3 +1,3 @@
diff --git a/src/components/views/location/ZoomButtons.tsx b/src/components/views/location/ZoomButtons.tsx
new file mode 100644
index 0000000000..80d2883ad6
--- /dev/null
+++ b/src/components/views/location/ZoomButtons.tsx
@@ -0,0 +1,58 @@
+/*
+Copyright 2022 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import maplibregl from 'maplibre-gl';
+
+import { _t } from '../../../languageHandler';
+import AccessibleButton from '../elements/AccessibleButton';
+import { Icon as PlusIcon } from '../../../../res/img/element-icons/plus-button.svg';
+import { Icon as MinusIcon } from '../../../../res/img/element-icons/minus-button.svg';
+
+interface Props {
+ map: maplibregl.Map;
+}
+
+const ZoomButtons: React.FC = ({ map }) => {
+ const onZoomIn = () => {
+ map.zoomIn();
+ };
+
+ const onZoomOut = () => {
+ map.zoomOut();
+ };
+
+ return
+
+
+
+
+
+
+
;
+};
+
+export default ZoomButtons;
diff --git a/test/components/views/location/ZoomButtons-test.tsx b/test/components/views/location/ZoomButtons-test.tsx
new file mode 100644
index 0000000000..62f488f43c
--- /dev/null
+++ b/test/components/views/location/ZoomButtons-test.tsx
@@ -0,0 +1,63 @@
+/*
+Copyright 2022 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import { mount } from 'enzyme';
+import maplibregl from 'maplibre-gl';
+import { act } from 'react-dom/test-utils';
+
+import ZoomButtons from '../../../../src/components/views/location/ZoomButtons';
+import { findByTestId } from '../../../test-utils';
+
+describe('', () => {
+ const mockMap = new maplibregl.Map();
+ const defaultProps = {
+ map: mockMap,
+ };
+ const getComponent = (props = {}) =>
+ mount();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders buttons', () => {
+ const component = getComponent();
+ expect(component).toMatchSnapshot();
+ });
+
+ it('calls map zoom in on zoom in click', () => {
+ const component = getComponent();
+
+ act(() => {
+ findByTestId(component, 'map-zoom-in-button').at(0).simulate('click');
+ });
+
+ expect(mockMap.zoomIn).toHaveBeenCalled();
+ expect(component).toBeTruthy();
+ });
+
+ it('calls map zoom out on zoom out click', () => {
+ const component = getComponent();
+
+ act(() => {
+ findByTestId(component, 'map-zoom-out-button').at(0).simulate('click');
+ });
+
+ expect(mockMap.zoomOut).toHaveBeenCalled();
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/test/components/views/location/__snapshots__/ZoomButtons-test.tsx.snap b/test/components/views/location/__snapshots__/ZoomButtons-test.tsx.snap
new file mode 100644
index 0000000000..ba5a0b4699
--- /dev/null
+++ b/test/components/views/location/__snapshots__/ZoomButtons-test.tsx.snap
@@ -0,0 +1,71 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` renders buttons 1`] = `
+
+