Skip to content

Commit 3854db0

Browse files
authored
Maps circle (#18568)
* feat(google-maps): Add Circle component Adds a component to draw a circle onto a Google Map. * feat(google-maps): Add Circle component Update public API for circle component.
1 parent 2b937f1 commit 3854db0

File tree

8 files changed

+556
-2
lines changed

8 files changed

+556
-2
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<map-polyline *ngIf="isPolylineDisplayed" [options]="polylineOptions"></map-polyline>
1717
<map-polygon *ngIf="isPolygonDisplayed" [options]="polygonOptions"></map-polygon>
1818
<map-rectangle *ngIf="isRectangleDisplayed" [options]="rectangleOptions"></map-rectangle>
19+
<map-circle *ngIf="isCircleDisplayed" [options]="circleOptions"></map-circle>
1920
</google-map>
2021

2122
<p><label>Latitude:</label> {{display?.lat}}</p>
@@ -66,4 +67,17 @@
6667
</label>
6768
</div>
6869

70+
<div>
71+
<label for="circle-checkbox">
72+
Toggle Circle
73+
<input type="checkbox" (click)="toggleCircleDisplay()">
74+
</label>
75+
</div>
76+
<div>
77+
<label for="editable-circle-checkbox">
78+
Toggle Editable Circle
79+
<input type="checkbox" [disabled]="!isCircleDisplayed" (click)="toggleEditableCircle()">
80+
</label>
81+
</div>
82+
6983
</div>

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import {Component, ViewChild} from '@angular/core';
1010
import {
11+
MapCircle,
1112
MapInfoWindow,
1213
MapMarker,
1314
MapPolygon,
@@ -28,6 +29,12 @@ const RECTANGLE_BOUNDS: google.maps.LatLngBoundsLiteral = {
2829
south: -5
2930
};
3031

32+
const CIRCLE_CENTER: google.maps.LatLngLiteral = {
33+
lat: 19,
34+
lng: 20
35+
};
36+
const CIRCLE_RADIUS = 500000;
37+
3138
/** Demo Component for @angular/google-maps/map */
3239
@Component({
3340
selector: 'google-map-demo',
@@ -39,6 +46,7 @@ export class GoogleMapDemo {
3946
@ViewChild(MapPolyline) polyline: MapPolyline;
4047
@ViewChild(MapPolygon) polygon: MapPolygon;
4148
@ViewChild(MapRectangle) rectangle: MapRectangle;
49+
@ViewChild(MapCircle) circle: MapCircle;
4250

4351
center = {lat: 24, lng: 12};
4452
markerOptions = {draggable: false};
@@ -54,6 +62,9 @@ export class GoogleMapDemo {
5462
isRectangleDisplayed = false;
5563
rectangleOptions: google.maps
5664
.RectangleOptions = {bounds: RECTANGLE_BOUNDS, strokeColor: 'grey', strokeOpacity: 0.8};
65+
isCircleDisplayed = false;
66+
circleOptions: google.maps.CircleOptions =
67+
{center: CIRCLE_CENTER, radius: CIRCLE_RADIUS, strokeColor: 'grey', strokeOpacity: 0.8};
5768

5869
handleClick(event: google.maps.MouseEvent) {
5970
this.markerPositions.push(event.latLng.toJSON());
@@ -106,4 +117,17 @@ export class GoogleMapDemo {
106117
bounds: this.rectangle.getBounds()
107118
};
108119
}
120+
121+
toggleCircleDisplay() {
122+
this.isCircleDisplayed = !this.isCircleDisplayed;
123+
}
124+
125+
toggleEditableCircle() {
126+
this.circleOptions = {
127+
...this.circleOptions,
128+
editable: !this.circleOptions.editable,
129+
center: this.circle.getCenter(),
130+
radius: this.circle.getRadius(),
131+
};
132+
}
109133
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {NgModule} from '@angular/core';
1010

1111
import {GoogleMap} from './google-map/google-map';
12+
import {MapCircle} from './map-circle/map-circle';
1213
import {MapInfoWindow} from './map-info-window/map-info-window';
1314
import {MapMarker} from './map-marker/map-marker';
1415
import {MapPolygon} from './map-polygon/map-polygon';
@@ -17,10 +18,11 @@ import {MapRectangle} from './map-rectangle/map-rectangle';
1718

1819
const COMPONENTS = [
1920
GoogleMap,
21+
MapCircle,
2022
MapInfoWindow,
2123
MapMarker,
22-
MapPolyline,
2324
MapPolygon,
25+
MapPolyline,
2426
MapRectangle,
2527
];
2628

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

0 commit comments

Comments
 (0)