Skip to content

Commit

Permalink
Merge pull request #2317 from terrestris/split-digitize-button
Browse files Browse the repository at this point in the history
Add SelectFeaturesButton
  • Loading branch information
simonseyock committed Sep 13, 2021
2 parents c6cf1b7 + 5c5d614 commit 9722a4c
Show file tree
Hide file tree
Showing 6 changed files with 363 additions and 35 deletions.
55 changes: 39 additions & 16 deletions src/Button/DrawButton/DrawButton.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import userEvent from '@testing-library/user-event';

import OlView from 'ol/View';
import OlMap from 'ol/Map';
import { clickMap, doubleClickMap, renderInContext } from '../../Util/rtlTestUtils';
import { clickMap, doubleClickMap, renderInMapContext } from '../../Util/rtlTestUtils';
import LineString from 'ol/geom/LineString';
import Polygon from 'ol/geom/Polygon';
import VectorLayer from 'ol/layer/Vector';
Expand Down Expand Up @@ -33,7 +33,7 @@ describe('<DrawButton />', () => {
});

it('can be rendered', () => {
const { container } = renderInContext(map, <DrawButton drawType={'Point'} />);
const { container } = renderInMapContext(map, <DrawButton drawType={'Point'} />);

const button = within(container).getByRole('button');
expect(button).toBeVisible();
Expand All @@ -42,7 +42,7 @@ describe('<DrawButton />', () => {

describe('#Drawing', () => {
it('draws points', () => {
renderInContext(map, <DrawButton drawType={'Point'} />);
renderInMapContext(map, <DrawButton drawType={'Point'} />);

const button = screen.getByRole('button');

Expand All @@ -59,8 +59,8 @@ describe('<DrawButton />', () => {
expect(feature.getGeometry().getType()).toBe('Point');
});

it('draws lines', () => {
renderInContext(map, <DrawButton drawType={'LineString'} />);
it('draws lines', () => {
renderInMapContext(map, <DrawButton drawType={'LineString'} />);

const button = screen.getByRole('button');

Expand All @@ -82,7 +82,7 @@ describe('<DrawButton />', () => {
});

it('draws polygons', () => {
renderInContext(map, <DrawButton drawType={'Polygon'} />);
renderInMapContext(map, <DrawButton drawType={'Polygon'} />);

const button = screen.getByRole('button');

Expand Down Expand Up @@ -111,7 +111,7 @@ describe('<DrawButton />', () => {
});

it('draws labels', async () => {
renderInContext(map, <DrawButton drawType={'Text'} />);
renderInMapContext(map, <DrawButton drawType={'Text'} />);

const button = screen.getByRole('button');

Expand Down Expand Up @@ -143,7 +143,7 @@ describe('<DrawButton />', () => {
});

it('aborts drawing labels', async () => {
renderInContext(map, <DrawButton drawType={'Text'} />);
renderInMapContext(map, <DrawButton drawType={'Text'} />);

const button = screen.getByRole('button');

Expand All @@ -169,8 +169,8 @@ describe('<DrawButton />', () => {
expect(digitizeLayer.getSource().getFeatures()).toHaveLength(0);
});

it('draws circles', () => {
renderInContext(map, <DrawButton drawType={'Circle'} />);
it('draws circles', async () => {
renderInMapContext(map, <DrawButton drawType={'Circle'} />);

const button = screen.getByRole('button');

Expand All @@ -189,8 +189,8 @@ describe('<DrawButton />', () => {
expect(feature.getGeometry().getType()).toBe('Circle');
});

it('draws rectangles', () => {
renderInContext(map, <DrawButton drawType={'Rectangle'} />);
it('draws rectangles', async () => {
renderInMapContext(map, <DrawButton drawType={'Rectangle'} />);

const button = screen.getByRole('button');

Expand All @@ -215,7 +215,7 @@ describe('<DrawButton />', () => {
});

it('toggles off', () => {
renderInContext(map, <DrawButton drawType={'Point'} />);
renderInMapContext(map, <DrawButton drawType={'Point'} />);

const button = screen.getByRole('button');

Expand Down Expand Up @@ -246,7 +246,7 @@ describe('<DrawButton />', () => {
const startSpy = jest.fn();
const endSpy = jest.fn();

renderInContext(map, <DrawButton drawType={'Polygon'} onDrawStart={startSpy} onDrawEnd={endSpy}/>);
renderInMapContext(map, <DrawButton drawType={'Polygon'} onDrawStart={startSpy} onDrawEnd={endSpy}/>);

const button = screen.getByRole('button');

Expand Down Expand Up @@ -277,7 +277,7 @@ describe('<DrawButton />', () => {
});

it('multiple draw buttons use the same digitize layer', () => {
renderInContext(map, <>
renderInMapContext(map, <>
<DrawButton drawType={'Point'}>Point 1</DrawButton>
<DrawButton drawType={'Point'}>Point 2</DrawButton>
</>);
Expand Down Expand Up @@ -306,7 +306,7 @@ describe('<DrawButton />', () => {

map.addLayer(layer);

renderInContext(map, <DrawButton drawType={'Point'} digitizeLayer={layer} />);
renderInMapContext(map, <DrawButton drawType={'Point'} digitizeLayer={layer} />);

const button = screen.getByRole('button');

Expand All @@ -320,5 +320,28 @@ describe('<DrawButton />', () => {

expect(defaultDigitizeLayer.getSource().getFeatures()).toHaveLength(0);
});

it('can change the type', () => {
const { rerenderInMapContext } = renderInMapContext(map, <DrawButton drawType={'Point'} />);

const button = screen.getByRole('button');

userEvent.click(button);

clickMap(map, 100, 100);

const digitizeLayer = DigitizeUtil.getDigitizeLayer(map);

expect(digitizeLayer.getSource().getFeatures()).toHaveLength(1);
expect(digitizeLayer.getSource().getFeatures()[0].getGeometry().getType()).toBe('Point');

rerenderInMapContext(<DrawButton drawType={'LineString'} />);

clickMap(map, 120, 120);
doubleClickMap(map, 140, 140);

expect(digitizeLayer.getSource().getFeatures()).toHaveLength(2);
expect(digitizeLayer.getSource().getFeatures()[1].getGeometry().getType()).toBe('LineString');
});
});
});
76 changes: 76 additions & 0 deletions src/Button/SelectFeaturesButton/SelectFeaturesButton.example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
This demonstrates the use of the SelectFeaturesButton.

```jsx
import { useEffect, useState } from 'react';

import OlMap from 'ol/Map';
import OlView from 'ol/View';
import OlLayerTile from 'ol/layer/Tile';
import OlSourceOsm from 'ol/source/OSM';
import OlVectorLayer from 'ol/layer/Vector';
import OlVectorSource from 'ol/source/Vector';
import OlFormatGeoJSON from 'ol/format/GeoJSON';
import { fromLonLat } from 'ol/proj';

import MapContext from '@terrestris/react-geo/Context/MapContext/MapContext'
import MapComponent from '@terrestris/react-geo/Map/MapComponent/MapComponent';
import SelectFeaturesButton from '@terrestris/react-geo/Button/SelectFeaturesButton/SelectFeaturesButton';

import federalStates from '../../../assets/federal-states-ger.json';

const format = new OlFormatGeoJSON();
const features = format.readFeatures(federalStates);

const SelectFeaturesButtonExample = () => {

const [map, setMap] = useState();
const [layer, setLayer] = useState();

useEffect(() => {
const layer = new OlVectorLayer({
source: new OlVectorSource({
features
})
});

setLayer(layer);

setMap(new OlMap({
layers: [
new OlLayerTile({
name: 'OSM',
source: new OlSourceOsm()
}),
layer
],
view: new OlView({
center: fromLonLat([8, 50]),
zoom: 4
})
}));
}, [])

if (!map) {
return null;
}

return (
<div>
<MapContext.Provider value={map}>
<MapComponent
map={map}
style={{
height: '400px'
}}
/>

<SelectFeaturesButton layers={[layer]}>
Select feature
</SelectFeaturesButton>
</MapContext.Provider>
</div>
);
}

<SelectFeaturesButtonExample />
```
85 changes: 85 additions & 0 deletions src/Button/SelectFeaturesButton/SelectFeaturesButton.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import * as React from 'react';
import { screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import OlMap from 'ol/Map';
import OlView from 'ol/View';
import OlFeature from 'ol/Feature';
import OlPoint from 'ol/geom/Point';
import OlVectorLayer from 'ol/layer/Vector';
import OlVectorSource from 'ol/source/Vector';
import { SelectEvent as OlSelectEvent } from 'ol/interaction/Select';

import DrawButton from '../DrawButton/DrawButton';
import { clickMap, renderInMapContext } from '../../Util/rtlTestUtils';
import SelectFeaturesButton from './SelectFeaturesButton';

describe('<SelectFeaturesButton />', () => {

const coord = [829729, 6708850];
let map: OlMap;
let layer: OlVectorLayer<OlVectorSource<OlPoint>>;
let feature: OlFeature<OlPoint>;

beforeEach(() => {
feature = new OlFeature(new OlPoint(coord));

layer = new OlVectorLayer({
source: new OlVectorSource({
features: [feature]
})
});

map = new OlMap({
view: new OlView({
center: coord,
zoom: 10
}),
controls: [],
layers: [
layer
]
});
});

describe('#Basics', () => {

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

it('can be rendered', () => {
const { container } = renderInMapContext(map, <SelectFeaturesButton layers={[layer]} />);

const button = within(container).getByRole('button');
expect(button).toBeVisible();
});
});

describe('#Selection', () => {
it('calls the listener', async () => {
const selectSpy = jest.fn();

renderInMapContext(map, <SelectFeaturesButton layers={[layer]} onFeatureSelect={selectSpy} />);

map.renderSync();

const button = screen.getByRole('button');
userEvent.click(button);

const mock = jest.spyOn(map, 'forEachFeatureAtPixel').mockImplementation((pixel, callback) => {
if (pixel[0] === 200 && pixel[1] === 200) {
callback(feature, layer, null);
}
});

await clickMap(map, 200, 200);

expect(selectSpy).toBeCalled();
const event: OlSelectEvent = selectSpy.mock.calls[0][0];
expect(event.selected).toEqual([feature]);

mock.mockRestore();
});
});
});

0 comments on commit 9722a4c

Please sign in to comment.