Skip to content

Commit

Permalink
Maps circle (angular#18568)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
mbehrlich authored Mar 12, 2020
1 parent 2b937f1 commit 3854db0
Show file tree
Hide file tree
Showing 8 changed files with 556 additions and 2 deletions.
14 changes: 14 additions & 0 deletions src/dev-app/google-map/google-map-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<map-polyline *ngIf="isPolylineDisplayed" [options]="polylineOptions"></map-polyline>
<map-polygon *ngIf="isPolygonDisplayed" [options]="polygonOptions"></map-polygon>
<map-rectangle *ngIf="isRectangleDisplayed" [options]="rectangleOptions"></map-rectangle>
<map-circle *ngIf="isCircleDisplayed" [options]="circleOptions"></map-circle>
</google-map>

<p><label>Latitude:</label> {{display?.lat}}</p>
Expand Down Expand Up @@ -66,4 +67,17 @@
</label>
</div>

<div>
<label for="circle-checkbox">
Toggle Circle
<input type="checkbox" (click)="toggleCircleDisplay()">
</label>
</div>
<div>
<label for="editable-circle-checkbox">
Toggle Editable Circle
<input type="checkbox" [disabled]="!isCircleDisplayed" (click)="toggleEditableCircle()">
</label>
</div>

</div>
24 changes: 24 additions & 0 deletions src/dev-app/google-map/google-map-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import {Component, ViewChild} from '@angular/core';
import {
MapCircle,
MapInfoWindow,
MapMarker,
MapPolygon,
Expand All @@ -28,6 +29,12 @@ const RECTANGLE_BOUNDS: google.maps.LatLngBoundsLiteral = {
south: -5
};

const CIRCLE_CENTER: google.maps.LatLngLiteral = {
lat: 19,
lng: 20
};
const CIRCLE_RADIUS = 500000;

/** Demo Component for @angular/google-maps/map */
@Component({
selector: 'google-map-demo',
Expand All @@ -39,6 +46,7 @@ export class GoogleMapDemo {
@ViewChild(MapPolyline) polyline: MapPolyline;
@ViewChild(MapPolygon) polygon: MapPolygon;
@ViewChild(MapRectangle) rectangle: MapRectangle;
@ViewChild(MapCircle) circle: MapCircle;

center = {lat: 24, lng: 12};
markerOptions = {draggable: false};
Expand All @@ -54,6 +62,9 @@ export class GoogleMapDemo {
isRectangleDisplayed = false;
rectangleOptions: google.maps
.RectangleOptions = {bounds: RECTANGLE_BOUNDS, strokeColor: 'grey', strokeOpacity: 0.8};
isCircleDisplayed = false;
circleOptions: google.maps.CircleOptions =
{center: CIRCLE_CENTER, radius: CIRCLE_RADIUS, strokeColor: 'grey', strokeOpacity: 0.8};

handleClick(event: google.maps.MouseEvent) {
this.markerPositions.push(event.latLng.toJSON());
Expand Down Expand Up @@ -106,4 +117,17 @@ export class GoogleMapDemo {
bounds: this.rectangle.getBounds()
};
}

toggleCircleDisplay() {
this.isCircleDisplayed = !this.isCircleDisplayed;
}

toggleEditableCircle() {
this.circleOptions = {
...this.circleOptions,
editable: !this.circleOptions.editable,
center: this.circle.getCenter(),
radius: this.circle.getRadius(),
};
}
}
4 changes: 3 additions & 1 deletion src/google-maps/google-maps-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import {NgModule} from '@angular/core';

import {GoogleMap} from './google-map/google-map';
import {MapCircle} from './map-circle/map-circle';
import {MapInfoWindow} from './map-info-window/map-info-window';
import {MapMarker} from './map-marker/map-marker';
import {MapPolygon} from './map-polygon/map-polygon';
Expand All @@ -17,10 +18,11 @@ import {MapRectangle} from './map-rectangle/map-rectangle';

const COMPONENTS = [
GoogleMap,
MapCircle,
MapInfoWindow,
MapMarker,
MapPolyline,
MapPolygon,
MapPolyline,
MapRectangle,
];

Expand Down
182 changes: 182 additions & 0 deletions src/google-maps/map-circle/map-circle.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import {Component, ViewChild} from '@angular/core';
import {async, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';

import {DEFAULT_OPTIONS, UpdatedGoogleMap} from '../google-map/google-map';
import {GoogleMapsModule} from '../google-maps-module';
import {
createCircleConstructorSpy,
createCircleSpy,
createMapConstructorSpy,
createMapSpy,
TestingWindow,
} from '../testing/fake-google-map-utils';

import {MapCircle} from './map-circle';

describe('MapCircle', () => {
let mapSpy: jasmine.SpyObj<UpdatedGoogleMap>;
let circleCenter: google.maps.LatLngLiteral;
let circleRadius: number;
let circleOptions: google.maps.CircleOptions;

beforeEach(async(() => {
circleCenter = {lat: 30, lng: 15};
circleRadius = 15;
circleOptions = {
center: circleCenter,
radius: circleRadius,
strokeColor: 'grey',
strokeOpacity: 0.8,
};
TestBed.configureTestingModule({
imports: [GoogleMapsModule],
declarations: [TestApp],
});
}));

beforeEach(() => {
TestBed.compileComponents();

mapSpy = createMapSpy(DEFAULT_OPTIONS);
createMapConstructorSpy(mapSpy).and.callThrough();
});

afterEach(() => {
const testingWindow: TestingWindow = window;
delete testingWindow.google;
});

it('initializes a Google Map Circle', () => {
const circleSpy = createCircleSpy({});
const circleConstructorSpy = createCircleConstructorSpy(circleSpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();

expect(circleConstructorSpy).toHaveBeenCalledWith({center: undefined, radius: undefined});
expect(circleSpy.setMap).toHaveBeenCalledWith(mapSpy);
});

it('sets center and radius from input', () => {
const center: google.maps.LatLngLiteral = {lat: 3, lng: 5};
const radius = 15;
const options: google.maps.CircleOptions = {center, radius};
const circleSpy = createCircleSpy(options);
const circleConstructorSpy = createCircleConstructorSpy(circleSpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
fixture.componentInstance.center = center;
fixture.componentInstance.radius = radius;
fixture.detectChanges();

expect(circleConstructorSpy).toHaveBeenCalledWith(options);
});

it('gives precedence to other inputs over options', () => {
const center: google.maps.LatLngLiteral = {lat: 3, lng: 5};
const radius = 15;
const expectedOptions: google.maps.CircleOptions = {...circleOptions, center, radius};
const circleSpy = createCircleSpy(expectedOptions);
const circleConstructorSpy = createCircleConstructorSpy(circleSpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
fixture.componentInstance.options = circleOptions;
fixture.componentInstance.center = center;
fixture.componentInstance.radius = radius;
fixture.detectChanges();

expect(circleConstructorSpy).toHaveBeenCalledWith(expectedOptions);
});

it('exposes methods that provide information about the Circle', () => {
const circleSpy = createCircleSpy(circleOptions);
createCircleConstructorSpy(circleSpy).and.callThrough();

const fixture = TestBed.createComponent(TestApp);
const circleComponent =
fixture.debugElement.query(By.directive(MapCircle))!.injector.get<MapCircle>(MapCircle);
fixture.detectChanges();

circleComponent.getCenter();
expect(circleSpy.getCenter).toHaveBeenCalled();

circleSpy.getRadius.and.returnValue(10);
expect(circleComponent.getRadius()).toBe(10);

circleSpy.getDraggable.and.returnValue(true);
expect(circleComponent.getDraggable()).toBe(true);

circleSpy.getEditable.and.returnValue(true);
expect(circleComponent.getEditable()).toBe(true);

circleSpy.getVisible.and.returnValue(true);
expect(circleComponent.getVisible()).toBe(true);
});

it('initializes Circle event handlers', () => {
const circleSpy = createCircleSpy(circleOptions);
createCircleConstructorSpy(circleSpy).and.callThrough();

const addSpy = circleSpy.addListener;
const fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();

expect(addSpy).toHaveBeenCalledWith('center_changed', jasmine.any(Function));
expect(addSpy).toHaveBeenCalledWith('click', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('dblclick', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('drag', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('dragend', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('dragstart', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('mousedown', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('mousemove', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('mouseout', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('mouseover', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('mouseup', jasmine.any(Function));
expect(addSpy).not.toHaveBeenCalledWith('radius_changed', jasmine.any(Function));
expect(addSpy).toHaveBeenCalledWith('rightclick', jasmine.any(Function));
});

it('should be able to add an event listener after init', () => {
const circleSpy = createCircleSpy(circleOptions);
createCircleConstructorSpy(circleSpy).and.callThrough();

const addSpy = circleSpy.addListener;
const fixture = TestBed.createComponent(TestApp);
fixture.detectChanges();

expect(addSpy).not.toHaveBeenCalledWith('dragend', jasmine.any(Function));

// Pick an event that isn't bound in the template.
const subscription = fixture.componentInstance.circle.circleDragend.subscribe();
fixture.detectChanges();

expect(addSpy).toHaveBeenCalledWith('dragend', jasmine.any(Function));
subscription.unsubscribe();
});
});

@Component({
selector: 'test-app',
template: `<google-map>
<map-circle [options]="options"
[center]="center"
[radius]="radius"
(centerChanged)="handleCenterChange()"
(circleClick)="handleClick()"
(circleRightclick)="handleRightclick()">
</map-circle>
</google-map>`,
})
class TestApp {
@ViewChild(MapCircle) circle: MapCircle;
options?: google.maps.CircleOptions;
center?: google.maps.LatLngLiteral;
radius?: number;

handleCenterChange() {}

handleClick() {}

handleRightclick() {}
}
Loading

0 comments on commit 3854db0

Please sign in to comment.