Skip to content

Commit c246a1c

Browse files
jelbournmbehrlich
andauthored
Merge google-maps-next branch into master (#18485)
* feat(google-maps): Add MapPolygon component (#17936) Add MapPolygon component to Google Maps, allowing polygons to be added to a Google Map. * Maps rectangle (#18184) Add rectangle component to @angular/google-maps that allows rectangles to be overlayed over the Google map. * refactor(google-maps): Update new components with NgZone fix. Add NgZone fix to new components, map-rectangle and map-polygon. Co-authored-by: mbehrlich <[email protected]>
1 parent d1a6ea7 commit c246a1c

File tree

11 files changed

+995
-11
lines changed

11 files changed

+995
-11
lines changed

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

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,56 @@
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>
18+
<map-rectangle *ngIf="isRectangleDisplayed" [options]="rectangleOptions"></map-rectangle>
1719
</google-map>
1820

1921
<p><label>Latitude:</label> {{display?.lat}}</p>
2022
<p><label>Longitude:</label> {{display?.lng}}</p>
2123

2224
<div>
23-
<label for="polyline-checkbox">Toggle Polyline</label>
24-
<input id="polyline-checkbox" type="checkbox" (click)="togglePolylineDisplay()">
25+
<label for="polyline-checkbox">
26+
Toggle Polyline
27+
<input type="checkbox" (click)="togglePolylineDisplay()">
28+
</label>
2529
</div>
2630
<div>
27-
<label for="editable-polyline-checkbox">Toggle Editable Polyline</label>
28-
<input id="editable-polyline-checkbox" type="checkbox" (click)="toggleEditablePolyline()">
31+
<label for="editable-polyline-checkbox">
32+
Toggle Editable Polyline
33+
<input type="checkbox"
34+
[disabled]="!isPolylineDisplayed"
35+
(click)="toggleEditablePolyline()">
36+
</label>
37+
</div>
38+
39+
<div>
40+
<label for="polygon-checkbox">
41+
Toggle Polygon
42+
<input type="checkbox" (click)="togglePolygonDisplay()">
43+
</label>
44+
</div>
45+
<div>
46+
<label for="editable-polygon-checkbox">
47+
Toggle Editable Polygon
48+
<input type="checkbox"
49+
[disabled]="!isPolygonDisplayed"
50+
(click)="toggleEditablePolygon()">
51+
</label>
52+
</div>
53+
54+
<div>
55+
<label for="rectangle-checkbox">
56+
Toggle Rectangle
57+
<input type="checkbox" (click)="toggleRectangleDisplay()">
58+
</label>
59+
</div>
60+
<div>
61+
<label for="editable-rectangle-checkbox">
62+
Toggle Editable Rectangle
63+
<input type="checkbox"
64+
[disabled]="!isRectangleDisplayed"
65+
(click)="toggleEditableRectangle()">
66+
</label>
2967
</div>
3068

3169
</div>

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

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

99
import {Component, ViewChild} from '@angular/core';
10-
import {MapInfoWindow, MapMarker} from '@angular/google-maps';
10+
import {
11+
MapInfoWindow,
12+
MapMarker,
13+
MapPolygon,
14+
MapPolyline,
15+
MapRectangle
16+
} from '@angular/google-maps';
1117

1218
const POLYLINE_PATH: google.maps.LatLngLiteral[] =
1319
[{lat: 25, lng: 26}, {lat: 26, lng: 27}, {lat: 30, lng: 34}];
1420

21+
const POLYGON_PATH: google.maps.LatLngLiteral[] =
22+
[{lat: 20, lng: 21}, {lat: 22, lng: 23}, {lat: 24, lng: 25}];
23+
24+
const RECTANGLE_BOUNDS: google.maps.LatLngBoundsLiteral = {
25+
east: 30,
26+
north: 15,
27+
west: 10,
28+
south: -5
29+
};
30+
1531
/** Demo Component for @angular/google-maps/map */
1632
@Component({
1733
selector: 'google-map-demo',
@@ -20,6 +36,9 @@ const POLYLINE_PATH: google.maps.LatLngLiteral[] =
2036
})
2137
export class GoogleMapDemo {
2238
@ViewChild(MapInfoWindow) infoWindow: MapInfoWindow;
39+
@ViewChild(MapPolyline) polyline: MapPolyline;
40+
@ViewChild(MapPolygon) polygon: MapPolygon;
41+
@ViewChild(MapRectangle) rectangle: MapRectangle;
2342

2443
center = {lat: 24, lng: 12};
2544
markerOptions = {draggable: false};
@@ -29,6 +48,12 @@ export class GoogleMapDemo {
2948
isPolylineDisplayed = false;
3049
polylineOptions:
3150
google.maps.PolylineOptions = {path: POLYLINE_PATH, strokeColor: 'grey', strokeOpacity: 0.8};
51+
isPolygonDisplayed = false;
52+
polygonOptions:
53+
google.maps.PolygonOptions = {paths: POLYGON_PATH, strokeColor: 'grey', strokeOpacity: 0.8};
54+
isRectangleDisplayed = false;
55+
rectangleOptions: google.maps
56+
.RectangleOptions = {bounds: RECTANGLE_BOUNDS, strokeColor: 'grey', strokeOpacity: 0.8};
3257

3358
handleClick(event: google.maps.MouseEvent) {
3459
this.markerPositions.push(event.latLng.toJSON());
@@ -51,6 +76,34 @@ export class GoogleMapDemo {
5176
}
5277

5378
toggleEditablePolyline() {
54-
this.polylineOptions = {...this.polylineOptions, editable: !this.polylineOptions.editable};
79+
this.polylineOptions = {
80+
...this.polylineOptions,
81+
editable: !this.polylineOptions.editable,
82+
path: this.polyline.getPath()
83+
};
84+
}
85+
86+
togglePolygonDisplay() {
87+
this.isPolygonDisplayed = !this.isPolygonDisplayed;
88+
}
89+
90+
toggleEditablePolygon() {
91+
this.polygonOptions = {
92+
...this.polygonOptions,
93+
editable: !this.polygonOptions.editable,
94+
paths: this.polygon.getPaths()
95+
};
96+
}
97+
98+
toggleRectangleDisplay() {
99+
this.isRectangleDisplayed = !this.isRectangleDisplayed;
100+
}
101+
102+
toggleEditableRectangle() {
103+
this.rectangleOptions = {
104+
...this.rectangleOptions,
105+
editable: !this.rectangleOptions.editable,
106+
bounds: this.rectangle.getBounds()
107+
};
55108
}
56109
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ 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';
16+
import {MapRectangle} from './map-rectangle/map-rectangle';
1517

1618
const COMPONENTS = [
1719
GoogleMap,
1820
MapInfoWindow,
1921
MapMarker,
2022
MapPolyline,
23+
MapPolygon,
24+
MapRectangle,
2125
];
2226

2327
@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)