Skip to content

Commit

Permalink
Merge pull request #3765 from terrestris/refactor-scale-combo
Browse files Browse the repository at this point in the history
Refactor `ScaleCombo` to function component
  • Loading branch information
dnlkoch authored Feb 28, 2024
2 parents bd3b10b + 570f666 commit 70d5041
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 456 deletions.
72 changes: 30 additions & 42 deletions src/Field/ScaleCombo/ScaleCombo.example.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,46 @@ This is a example containing a map component and a scale combo

```jsx
import ScaleCombo from '@terrestris/react-geo/dist/Field/ScaleCombo/ScaleCombo';
import MapComponent from '@terrestris/react-util/dist/Components/MapComponent/MapComponent';
import MapContext from '@terrestris/react-util/dist/Context/MapContext/MapContext';
import OlLayerTile from 'ol/layer/Tile';
import OlMap from 'ol/Map';
import { fromLonLat } from 'ol/proj';
import OlSourceOSM from 'ol/source/OSM';
import OlView from 'ol/View';
import * as React from 'react';

class ScaleComboExample extends React.Component {
const ScaleComboExample = () => {

constructor(props) {

super(props);

this.mapDivId = `map-${Math.random()}`;

this.map = new OlMap({
layers: [
new OlLayerTile({
name: 'OSM',
source: new OlSourceOSM()
})
],
view: new OlView({
center: fromLonLat([37.40570, 8.81566]),
zoom: 4
const map = new OlMap({
layers: [
new OlLayerTile({
name: 'OSM',
source: new OlSourceOSM()
})
});
}

componentDidMount() {
this.map.setTarget(this.mapDivId);
}

render() {
return (
<div>
<div className="example-block">
<label>ScaleCombo:<br />
<ScaleCombo
map={this.map}
/>
</label>
</div>
<div
id={this.mapDivId}
style={{
height: '400px'
}}
/>
],
view: new OlView({
center: fromLonLat([37.40570, 8.81566]),
constrainResolution: true,
zoom: 4
})
});

return (
<MapContext.Provider value={map}>
<div className="example-block">
<label>ScaleCombo:<br />
<ScaleCombo />
</label>
</div>
);
}
<MapComponent
map={map}
style={{
height: '400px'
}}
/>
</MapContext.Provider>
);
}

<ScaleComboExample />
Expand Down
220 changes: 81 additions & 139 deletions src/Field/ScaleCombo/ScaleCombo.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,182 +1,124 @@
import MapUtil from '@terrestris/ol-util/dist/MapUtil/MapUtil';
import { renderInMapContext } from '@terrestris/react-util/dist/Util/rtlTestUtils';
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';

import TestUtil from '../../Util/TestUtil';
import ScaleCombo from './ScaleCombo';

describe('<ScaleCombo />', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('is defined', () => {
expect(ScaleCombo).not.toBeUndefined();
expect(ScaleCombo).toBeDefined();
});

it('can be rendered', () => {
const map = TestUtil.createMap();
const wrapper = TestUtil.mountComponent(ScaleCombo, {
map
});
expect(wrapper).not.toBeUndefined();
const { container } = render(<ScaleCombo />);
expect(container).toBeVisible();
});

it('passes style prop', () => {
it('calls onZoomLevelSelect when a scale is selected', async () => {
const onZoomLevelSelect = jest.fn();
const map = TestUtil.createMap();
const props = {
map,
style: {
backgroundColor: 'yellow'
}
};
const wrapper = TestUtil.mountComponent(ScaleCombo, props);
expect(wrapper.getDOMNode()).toHaveStyle('backgroundColor: yellow');
renderInMapContext(map,
<ScaleCombo
onZoomLevelSelect={onZoomLevelSelect}
scales={[100, 200]}
open
/>
);

const entry = screen.getByText('1:100');

await userEvent.click(entry);

expect(onZoomLevelSelect).toHaveBeenCalledWith('100');
TestUtil.removeMap(map);
});

describe('#getOptionsFromMap', () => {
it('is defined', () => {
const map = TestUtil.createMap();
const wrapper = TestUtil.mountComponent(ScaleCombo, {
map
});
const instance = wrapper.instance() as ScaleCombo;
expect(instance.getOptionsFromMap).not.toBeUndefined();
it('creates options array from given map with resolutions', () => {
const testResolutions = [560, 280, 140, 70, 28];
const map = TestUtil.createMap({
resolutions: testResolutions
});

it('creates options array from resolutions set on the map', () => {
const map = TestUtil.createMap();
renderInMapContext(map, (
<ScaleCombo
open
/>
));

const getOptionsFromMapSpy = jest.spyOn(ScaleCombo.prototype, 'getOptionsFromMap');
const options = screen.getAllByText((text, element) => element?.tagName === 'DIV' && /1:/.test(text));

TestUtil.mountComponent(ScaleCombo, {
map
});

expect(getOptionsFromMapSpy).toHaveBeenCalledTimes(1);
getOptionsFromMapSpy.mockRestore();

TestUtil.removeMap(map);
});
expect(options).toHaveLength(testResolutions.length);
TestUtil.removeMap(map);
});

it('creates options array from given map without resolutions', () => {
const map = TestUtil.createMap();
const wrapper = TestUtil.mountComponent(ScaleCombo, {
scales: [],
map: map
});

// Reset the scales array, as getOptionsFromMap() will be called in
// constructor.
wrapper.setState({scales: []});
const instance = wrapper.instance() as ScaleCombo;
const scales = instance.getOptionsFromMap();
expect(scales).toBeInstanceOf(Array);

TestUtil.removeMap(map);
it('creates options array from given map with filtered resolutions', () => {
const testResolutions = [560, 280, 140, 70, 28, 19, 15, 14, 13, 9];
const map = TestUtil.createMap({
resolutions: testResolutions
});

it('creates options array from given map with resolutions', () => {
const testResolutions = [560, 280, 140, 70, 28];
const map = TestUtil.createMap({
resolutions: testResolutions
});
const wrapper = TestUtil.mountComponent(ScaleCombo, {
scales: [],
map: map
});

// Reset the scales array, as getOptionsFromMap() will be called in
// constructor.
wrapper.setState({scales: []});

const instance = wrapper.instance() as ScaleCombo;
const scales = instance.getOptionsFromMap();

expect(scales).toBeInstanceOf(Array);
expect(scales).toHaveLength(testResolutions.length);

let testResolution = testResolutions[testResolutions.length - 1];
const roundScale = (Math.round(MapUtil.getScaleForResolution(testResolution ,'m')!));
const resolutionsFilter = (res: number) => {
return res >= 19 || res <= 13;
};

expect(scales[0]).toBe(roundScale);
const expectedLength = testResolutions.filter(resolutionsFilter).length;

TestUtil.removeMap(map);
});
renderInMapContext(map, (
<ScaleCombo
open
resolutionsFilter={resolutionsFilter}
/>
));

it('creates options array from given map with filtered resolutions', () => {
const testResolutions = [560, 280, 140, 70, 28, 19, 15, 14, 13, 9];
const map = TestUtil.createMap({
resolutions: testResolutions
});
const options = screen.getAllByText((text, element) => element?.tagName === 'DIV' && /1:/.test(text));

// eslint-disable-next-line
const resolutionsFilter = (res: number) => {
return res >= 19 || res <= 13;
};

const expectedLength = testResolutions.filter(resolutionsFilter).length;
expect(options).toHaveLength(expectedLength);
TestUtil.removeMap(map);
});

const wrapper = TestUtil.mountComponent(ScaleCombo, {
map: map,
scales: [],
resolutionsFilter
});
it('zooms the map to the clicked scale', async () => {
const map = TestUtil.createMap();

// Reset the scales array, as getOptionsFromMap() will be called in
// constructor.
wrapper.setState({scales: []});
renderInMapContext(map,
<ScaleCombo
scales={[100, 200, 300, 400, 500]}
open
/>
);

const instance = wrapper.instance() as ScaleCombo;
const scales = instance.getOptionsFromMap();
expect(scales).toBeInstanceOf(Array);
expect(scales).toHaveLength(expectedLength);
expect(map.getView().getResolution()).toBeCloseTo(1);

const roundScale = MapUtil.roundScale(MapUtil.getScaleForResolution(
testResolutions[testResolutions.length - 2] ,'m')!);
const entry = screen.getByText('1:300');

expect(scales[1]).toBe(roundScale);
await userEvent.click(entry);

TestUtil.removeMap(map);
});
expect(map.getView().getResolution()).toBeCloseTo(0.08);
TestUtil.removeMap(map);
});

describe('#determineOptionKeyForZoomLevel', () => {
it('is defined', () => {
const map = TestUtil.createMap();
const wrapper = TestUtil.mountComponent(ScaleCombo, {
map
});
const instance = wrapper.instance() as ScaleCombo;
expect(instance.determineOptionKeyForZoomLevel).not.toBeUndefined();
});
it('sets the correct scale on map zoom', () => {
const map = TestUtil.createMap();

it('returns "undefied" for erronous zoom level or if exceeds number of valid zoom levels ', () => {
const map = TestUtil.createMap();
const scaleArray = [100, 200, 300];
const wrapper = TestUtil.mountComponent(ScaleCombo, {
map,
scales: scaleArray
});
renderInMapContext(map,
<ScaleCombo
scales={[100, 200, 300, 400, 500]}
/>
);

let component = wrapper.instance() as ScaleCombo;
expect(component.determineOptionKeyForZoomLevel(undefined)).toBeUndefined();
expect(component.determineOptionKeyForZoomLevel(17.123)).toBeUndefined();
expect(component.determineOptionKeyForZoomLevel(scaleArray.length)).toBeUndefined();
expect(map.getView().getResolution()).toBeCloseTo(1);

TestUtil.removeMap(map);
act(() => {
map.getView().setZoom(4);
});

it('returns matching key for zoom level', () => {
const map = TestUtil.createMap();
const scaleArray = [100, 200, 300];
const wrapper = TestUtil.mountComponent(ScaleCombo, {
map,
scales: scaleArray
});
const index = 1;
let component = wrapper.instance() as ScaleCombo;
expect(component.determineOptionKeyForZoomLevel(index)).toBe(scaleArray[index].toString());

TestUtil.removeMap(map);
});
const entry = screen.getByText('1:100');

expect(entry).toBeVisible();
});

});
Loading

0 comments on commit 70d5041

Please sign in to comment.