Skip to content

Commit c35144f

Browse files
mbehrlichjelbourn
authored andcommitted
feat(google-maps): Add MapPolygon component (#17936)
Add MapPolygon component to Google Maps, allowing polygons to be added to a Google Map.
1 parent 555037a commit c35144f

File tree

8 files changed

+497
-7
lines changed

8 files changed

+497
-7
lines changed

src/dev-app/google-map/google-map-demo.html

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,40 @@
1414
(mapClick)="clickMarker(marker)"></map-marker>
1515
<map-info-window>Testing 1 2 3</map-info-window>
1616
<map-polyline *ngIf="isPolylineDisplayed" [options]="polylineOptions"></map-polyline>
17+
<map-polygon *ngIf="isPolygonDisplayed" [options]="polygonOptions"></map-polygon>
1718
</google-map>
1819

1920
<p><label>Latitude:</label> {{display?.lat}}</p>
2021
<p><label>Longitude:</label> {{display?.lng}}</p>
2122

2223
<div>
23-
<label for="polyline-checkbox">Toggle Polyline</label>
24-
<input id="polyline-checkbox" type="checkbox" (click)="togglePolylineDisplay()">
24+
<label for="polyline-checkbox">
25+
Toggle Polyline
26+
<input type="checkbox" (click)="togglePolylineDisplay()">
27+
</label>
2528
</div>
2629
<div>
27-
<label for="editable-polyline-checkbox">Toggle Editable Polyline</label>
28-
<input id="editable-polyline-checkbox" type="checkbox" (click)="toggleEditablePolyline()">
30+
<label for="editable-polyline-checkbox">
31+
Toggle Editable Polyline
32+
<input type="checkbox"
33+
[disabled]="!isPolylineDisplayed"
34+
(click)="toggleEditablePolyline()">
35+
</label>
36+
</div>
37+
38+
<div>
39+
<label for="polygon-checkbox">
40+
Toggle Polygon
41+
<input type="checkbox" (click)="togglePolygonDisplay()">
42+
</label>
43+
</div>
44+
<div>
45+
<label for="editable-polygon-checkbox">
46+
Toggle Editable Polygon
47+
<input type="checkbox"
48+
[disabled]="!isPolygonDisplayed"
49+
(click)="toggleEditablePolygon()">
50+
</label>
2951
</div>
3052

3153
</div>

src/dev-app/google-map/google-map-demo.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
*/
88

99
import {Component, ViewChild} from '@angular/core';
10-
import {MapInfoWindow, MapMarker} from '@angular/google-maps';
10+
import {MapInfoWindow, MapMarker, MapPolygon, MapPolyline} from '@angular/google-maps';
1111

1212
const POLYLINE_PATH: google.maps.LatLngLiteral[] =
1313
[{lat: 25, lng: 26}, {lat: 26, lng: 27}, {lat: 30, lng: 34}];
1414

15+
const POLYGON_PATH: google.maps.LatLngLiteral[] =
16+
[{lat: 20, lng: 21}, {lat: 22, lng: 23}, {lat: 24, lng: 25}];
17+
1518
/** Demo Component for @angular/google-maps/map */
1619
@Component({
1720
selector: 'google-map-demo',
@@ -20,6 +23,8 @@ const POLYLINE_PATH: google.maps.LatLngLiteral[] =
2023
})
2124
export class GoogleMapDemo {
2225
@ViewChild(MapInfoWindow) infoWindow: MapInfoWindow;
26+
@ViewChild(MapPolyline) polyline: MapPolyline;
27+
@ViewChild(MapPolygon) polygon: MapPolygon;
2328

2429
center = {lat: 24, lng: 12};
2530
markerOptions = {draggable: false};
@@ -29,6 +34,9 @@ export class GoogleMapDemo {
2934
isPolylineDisplayed = false;
3035
polylineOptions:
3136
google.maps.PolylineOptions = {path: POLYLINE_PATH, strokeColor: 'grey', strokeOpacity: 0.8};
37+
isPolygonDisplayed = false;
38+
polygonOptions:
39+
google.maps.PolygonOptions = {paths: POLYGON_PATH, strokeColor: 'grey', strokeOpacity: 0.8};
3240

3341
handleClick(event: google.maps.MouseEvent) {
3442
this.markerPositions.push(event.latLng.toJSON());
@@ -51,6 +59,22 @@ export class GoogleMapDemo {
5159
}
5260

5361
toggleEditablePolyline() {
54-
this.polylineOptions = {...this.polylineOptions, editable: !this.polylineOptions.editable};
62+
this.polylineOptions = {
63+
...this.polylineOptions,
64+
editable: !this.polylineOptions.editable,
65+
path: this.polyline.getPath()
66+
};
67+
}
68+
69+
togglePolygonDisplay() {
70+
this.isPolygonDisplayed = !this.isPolygonDisplayed;
71+
}
72+
73+
toggleEditablePolygon() {
74+
this.polygonOptions = {
75+
...this.polygonOptions,
76+
editable: !this.polygonOptions.editable,
77+
paths: this.polygon.getPaths()
78+
};
5579
}
5680
}

src/google-maps/google-maps-module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import {NgModule} from '@angular/core';
1111
import {GoogleMap} from './google-map/google-map';
1212
import {MapInfoWindow} from './map-info-window/map-info-window';
1313
import {MapMarker} from './map-marker/map-marker';
14+
import {MapPolygon} from './map-polygon/map-polygon';
1415
import {MapPolyline} from './map-polyline/map-polyline';
1516

1617
const COMPONENTS = [
1718
GoogleMap,
1819
MapInfoWindow,
1920
MapMarker,
2021
MapPolyline,
22+
MapPolygon,
2123
];
2224

2325
@NgModule({
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import {Component, ViewChild} from '@angular/core';
2+
import {async, TestBed} from '@angular/core/testing';
3+
import {By} from '@angular/platform-browser';
4+
5+
import {DEFAULT_OPTIONS, UpdatedGoogleMap} from '../google-map/google-map';
6+
import {GoogleMapsModule} from '../google-maps-module';
7+
import {
8+
createMapConstructorSpy,
9+
createMapSpy,
10+
createPolygonConstructorSpy,
11+
createPolygonSpy,
12+
TestingWindow,
13+
} from '../testing/fake-google-map-utils';
14+
15+
import {MapPolygon} from './map-polygon';
16+
17+
describe('MapPolygon', () => {
18+
let mapSpy: jasmine.SpyObj<UpdatedGoogleMap>;
19+
let polygonPath: google.maps.LatLngLiteral[];
20+
let polygonOptions: google.maps.PolygonOptions;
21+
22+
beforeEach(async(() => {
23+
polygonPath = [{lat: 25, lng: 26}, {lat: 26, lng: 27}, {lat: 30, lng: 34}];
24+
polygonOptions = {paths: polygonPath, strokeColor: 'grey', strokeOpacity: 0.8};
25+
TestBed.configureTestingModule({
26+
imports: [GoogleMapsModule],
27+
declarations: [TestApp],
28+
});
29+
}));
30+
31+
beforeEach(() => {
32+
TestBed.compileComponents();
33+
34+
mapSpy = createMapSpy(DEFAULT_OPTIONS);
35+
createMapConstructorSpy(mapSpy).and.callThrough();
36+
});
37+
38+
afterEach(() => {
39+
const testingWindow: TestingWindow = window;
40+
delete testingWindow.google;
41+
});
42+
43+
it('initializes a Google Map Polygon', () => {
44+
const polygonSpy = createPolygonSpy({});
45+
const polygonConstructorSpy = createPolygonConstructorSpy(polygonSpy).and.callThrough();
46+
47+
const fixture = TestBed.createComponent(TestApp);
48+
fixture.detectChanges();
49+
50+
expect(polygonConstructorSpy).toHaveBeenCalledWith({paths: undefined});
51+
expect(polygonSpy.setMap).toHaveBeenCalledWith(mapSpy);
52+
});
53+
54+
it('sets path from input', () => {
55+
const paths: google.maps.LatLngLiteral[] = [{lat: 3, lng: 5}];
56+
const options: google.maps.PolygonOptions = {paths};
57+
const polygonSpy = createPolygonSpy(options);
58+
const polygonConstructorSpy = createPolygonConstructorSpy(polygonSpy).and.callThrough();
59+
60+
const fixture = TestBed.createComponent(TestApp);
61+
fixture.componentInstance.paths = paths;
62+
fixture.detectChanges();
63+
64+
expect(polygonConstructorSpy).toHaveBeenCalledWith(options);
65+
});
66+
67+
it('gives precedence to path input over options', () => {
68+
const paths: google.maps.LatLngLiteral[] = [{lat: 3, lng: 5}];
69+
const expectedOptions: google.maps.PolygonOptions = {...polygonOptions, paths};
70+
const polygonSpy = createPolygonSpy(expectedOptions);
71+
const polygonConstructorSpy = createPolygonConstructorSpy(polygonSpy).and.callThrough();
72+
73+
const fixture = TestBed.createComponent(TestApp);
74+
fixture.componentInstance.options = polygonOptions;
75+
fixture.componentInstance.paths = paths;
76+
fixture.detectChanges();
77+
78+
expect(polygonConstructorSpy).toHaveBeenCalledWith(expectedOptions);
79+
});
80+
81+
it('exposes methods that provide information about the Polygon', () => {
82+
const polygonSpy = createPolygonSpy(polygonOptions);
83+
createPolygonConstructorSpy(polygonSpy).and.callThrough();
84+
85+
const fixture = TestBed.createComponent(TestApp);
86+
const polygonComponent =
87+
fixture.debugElement.query(By.directive(MapPolygon))!.injector.get<MapPolygon>(MapPolygon);
88+
fixture.detectChanges();
89+
90+
polygonSpy.getDraggable.and.returnValue(true);
91+
expect(polygonComponent.getDraggable()).toBe(true);
92+
93+
polygonSpy.getEditable.and.returnValue(true);
94+
expect(polygonComponent.getEditable()).toBe(true);
95+
96+
polygonComponent.getPath();
97+
expect(polygonSpy.getPath).toHaveBeenCalled();
98+
99+
polygonComponent.getPaths();
100+
expect(polygonSpy.getPaths).toHaveBeenCalled();
101+
102+
polygonSpy.getVisible.and.returnValue(true);
103+
expect(polygonComponent.getVisible()).toBe(true);
104+
});
105+
106+
it('initializes Polygon event handlers', () => {
107+
const polygonSpy = createPolygonSpy(polygonOptions);
108+
createPolygonConstructorSpy(polygonSpy).and.callThrough();
109+
110+
const addSpy = polygonSpy.addListener;
111+
const fixture = TestBed.createComponent(TestApp);
112+
fixture.detectChanges();
113+
114+
expect(addSpy).toHaveBeenCalledWith('click', jasmine.any(Function));
115+
expect(addSpy).not.toHaveBeenCalledWith('dblclick', jasmine.any(Function));
116+
expect(addSpy).not.toHaveBeenCalledWith('drag', jasmine.any(Function));
117+
expect(addSpy).not.toHaveBeenCalledWith('dragend', jasmine.any(Function));
118+
expect(addSpy).not.toHaveBeenCalledWith('dragstart', jasmine.any(Function));
119+
expect(addSpy).not.toHaveBeenCalledWith('mousedown', jasmine.any(Function));
120+
expect(addSpy).not.toHaveBeenCalledWith('mousemove', jasmine.any(Function));
121+
expect(addSpy).not.toHaveBeenCalledWith('mouseout', jasmine.any(Function));
122+
expect(addSpy).not.toHaveBeenCalledWith('mouseover', jasmine.any(Function));
123+
expect(addSpy).not.toHaveBeenCalledWith('mouseup', jasmine.any(Function));
124+
expect(addSpy).toHaveBeenCalledWith('rightclick', jasmine.any(Function));
125+
});
126+
127+
it('should be able to add an event listener after init', () => {
128+
const polygonSpy = createPolygonSpy(polygonOptions);
129+
createPolygonConstructorSpy(polygonSpy).and.callThrough();
130+
131+
const addSpy = polygonSpy.addListener;
132+
const fixture = TestBed.createComponent(TestApp);
133+
fixture.detectChanges();
134+
135+
expect(addSpy).not.toHaveBeenCalledWith('dragend', jasmine.any(Function));
136+
137+
// Pick an event that isn't bound in the template.
138+
const subscription = fixture.componentInstance.polygon.polygonDragend.subscribe();
139+
fixture.detectChanges();
140+
141+
expect(addSpy).toHaveBeenCalledWith('dragend', jasmine.any(Function));
142+
subscription.unsubscribe();
143+
});
144+
});
145+
146+
@Component({
147+
selector: 'test-app',
148+
template: `<google-map>
149+
<map-polygon [options]="options"
150+
[paths]="paths"
151+
(polygonClick)="handleClick()"
152+
(polygonRightclick)="handleRightclick()">
153+
</map-polygon>
154+
</google-map>`,
155+
})
156+
class TestApp {
157+
@ViewChild(MapPolygon) polygon: MapPolygon;
158+
options?: google.maps.PolygonOptions;
159+
paths?: google.maps.LatLngLiteral[];
160+
161+
handleClick() {}
162+
163+
handleRightclick() {}
164+
}

0 commit comments

Comments
 (0)