Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions __mocks__/azure-maps-control.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
class DataSource {
add = jest.fn()
clear = jest.fn()
remove = jest.fn()
importDataFromUrl = jest.fn()
setOptions = jest.fn()
}

module.exports = {
Map: jest.fn(() => ({
controls: {
Expand Down Expand Up @@ -109,13 +117,11 @@ module.exports = {
}))
},
source: {
DataSource: jest.fn(() => ({
add: jest.fn(),
clear: jest.fn(),
remove: jest.fn(),
importDataFromUrl: jest.fn(),
setOptions: jest.fn()
}))
DataSource,
VectorTileSource: jest.fn((id, options) => ({
getId: jest.fn(() => id),
getOptions: jest.fn(() => options)
}))
},
Shape: jest.fn(() => ({
setCoordinates: jest.fn(),
Expand Down
Binary file modified assets/coverage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 15 additions & 6 deletions src/components/AzureMapFeature/useFeature.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect } from 'react'
import { DataSourceType, IAzureMapFeature, ShapeType, FeatureType } from '../../types'
import { useCheckRef } from '../../hooks/useCheckRef'
import atlas from 'azure-maps-control'

export const useFeature = (
{ setCoords, setProperties }: IAzureMapFeature,
Expand All @@ -10,17 +11,25 @@ export const useFeature = (
) => {
// Simple feature's usecases and methods
useCheckRef<DataSourceType, FeatureType>(dataSourceRef, featureRef, (dref, fref) => {
dref.add(fref)
return () => {
dref.remove(fref)
if(dref instanceof atlas.source.DataSource){
dref.add(fref)
return () => {
dref.remove(fref)
}
} else if (dataSourceRef instanceof atlas.source.VectorTileSource) {
console.error(`Unable to add Feature(${fref.id}) to VectorTileSource(${dataSourceRef.getId()}): AzureMapFeature has to be a child of AzureMapDataSourceProvider`)
}
})

// Shape's usecases and methods
useCheckRef<DataSourceType, ShapeType>(dataSourceRef, shapeRef, (dref, sref) => {
dref.add(sref)
return () => {
dref.remove(sref)
if(dref instanceof atlas.source.DataSource){
dref.add(sref)
return () => {
dref.remove(sref)
}
} else if (dataSourceRef instanceof atlas.source.VectorTileSource) {
console.error(`Unable to add Shape(${sref.getId()}) to VectorTileSource(${dataSourceRef.getId()}): AzureMapFeature has to be a child of AzureMapDataSourceProvider`)
}
})

Expand Down
20 changes: 13 additions & 7 deletions src/contexts/AzureMapDataSourceContext.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useContext } from 'react'
import { renderHook } from '@testing-library/react-hooks'
import { Map } from 'azure-maps-control'
import atlas, { Map } from 'azure-maps-control'
import React from 'react'
import { AzureMapsContext } from '../contexts/AzureMapContext'
import {
Expand Down Expand Up @@ -65,30 +65,36 @@ describe('AzureMapDataSourceProvider tests', () => {
const { result } = renderHook(() => useContextConsumer(), {
wrapper: wrapWithDataSourceContext({ id: 'id', dataFromUrl: 'dataFromUrl' })
})
expect(result.current.dataSourceRef?.importDataFromUrl).toHaveBeenCalledWith('dataFromUrl')
expect(result.current.dataSourceRef).toBeInstanceOf(atlas.source.DataSource)
expect((result.current.dataSourceRef as atlas.source.DataSource).importDataFromUrl).toHaveBeenCalledWith('dataFromUrl')
})

it('should call add collection if collection was not falsy', () => {
const { result } = renderHook(() => useContextConsumer(), {
wrapper: wrapWithDataSourceContext({ id: 'id', collection: [] })
})
expect(result.current.dataSourceRef?.add).toHaveBeenCalledWith([])
expect(result.current.dataSourceRef?.clear).toHaveBeenCalledWith()
expect(result.current.dataSourceRef).toBeInstanceOf(atlas.source.DataSource)
const dataSourceRef = result.current.dataSourceRef as atlas.source.DataSource
expect(dataSourceRef.add).toHaveBeenCalledWith([])
expect(dataSourceRef.clear).toHaveBeenCalledWith()
})

it('should call add collection and clear method if collection was changed', () => {
const { result, rerender } = renderHook(() => useContextConsumer(), {
wrapper: wrapWithDataSourceContext({ id: 'id', collection: [] })
})
rerender({})
expect(result.current.dataSourceRef?.add).toHaveBeenCalledTimes(2)
expect(result.current.dataSourceRef?.clear).toHaveBeenCalledTimes(1)
expect(result.current.dataSourceRef).toBeInstanceOf(atlas.source.DataSource)
const dataSourceRef = result.current.dataSourceRef as atlas.source.DataSource
expect(dataSourceRef.add).toHaveBeenCalledTimes(2)
expect(dataSourceRef.clear).toHaveBeenCalledTimes(1)
})

it('should call setOptions and clear method if options was changed', () => {
const { result, rerender } = renderHook(() => useContextConsumer(), {
wrapper: wrapWithDataSourceContext({ id: 'id', options: { option: 'option' } })
})
expect(result.current.dataSourceRef?.setOptions).toHaveBeenLastCalledWith({ option: 'option' })
expect(result.current.dataSourceRef).toBeInstanceOf(atlas.source.DataSource)
expect((result.current.dataSourceRef as atlas.source.DataSource).setOptions).toHaveBeenLastCalledWith({ option: 'option' })
})
})
15 changes: 9 additions & 6 deletions src/contexts/AzureMapDataSourceContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ const AzureMapDataSourceStatefulProvider = ({
mref.events.add(eventType as any, dref, events[eventType])
}
mref.sources.add(dref)
if (dataFromUrl) {
dref.importDataFromUrl(dataFromUrl)
}
if (collection) {
dref.add(collection)
if (dref instanceof atlas.source.DataSource) {
if (dataFromUrl) {
dref.importDataFromUrl(dataFromUrl)
}
if (collection) {
dref.add(collection)
}
}
})

Expand Down Expand Up @@ -74,5 +76,6 @@ const AzureMapDataSourceStatefulProvider = ({
export {
AzureMapDataSourceContext,
AzureMapDataSourceConsumer,
AzureMapDataSourceStatefulProvider as AzureMapDataSourceProvider
AzureMapDataSourceStatefulProvider as AzureMapDataSourceProvider,
Provider as AzureMapDataSourceRawProvider
}
69 changes: 69 additions & 0 deletions src/contexts/AzureMapVectorTileSourceProvider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { renderHook } from '@testing-library/react-hooks'
import React, { useContext } from 'react'
import { Map } from 'azure-maps-control'
import { IAzureVectorTileSourceStatefulProviderProps } from "../types"
import { AzureMapsContext } from './AzureMapContext'
import { AzureMapVectorTileSourceProvider } from './AzureMapVectorTileSourceProvider'
import { AzureMapDataSourceContext } from '../contexts/AzureMapDataSourceContext'

const mapContextProps = {
mapRef: null,
isMapReady: false,
setMapReady: jest.fn(),
removeMapRef: jest.fn(),
setMapRef: jest.fn()
}
const mapRef = new Map('fake', {})

const useContextConsumer = () => {
const dataSourceContext = useContext(AzureMapDataSourceContext)
return dataSourceContext
}

const wrapWithVectorTileSourceContext = (props: IAzureVectorTileSourceStatefulProviderProps) => ({
children
}: {
children?: any
}) => {
return (
<AzureMapsContext.Provider
value={{
...mapContextProps,
mapRef
}}
>
<AzureMapVectorTileSourceProvider {...{ ...props }}>{children}</AzureMapVectorTileSourceProvider>
</AzureMapsContext.Provider>
)
}

describe('AzureMapVectorTileSourceProvider tests', () => {
it('should create data source with passed id and options', () => {
const { result } = renderHook(() => useContextConsumer(), {
wrapper: wrapWithVectorTileSourceContext({ id: 'id', options: { minZoom: 0, maxZoom: 12 } })
})

expect(result.current.dataSourceRef?.getId()).toEqual('id')
expect(result.current.dataSourceRef?.getOptions()).toEqual({ minZoom: 0, maxZoom: 12 })
})

it('should add data source to the map ref on mount', () => {
mapRef.sources.add = jest.fn()
const { result } = renderHook(() => useContextConsumer(), {
wrapper: wrapWithVectorTileSourceContext({ id: 'id' })
})
expect(mapRef.sources.add).toHaveBeenCalledWith(result.current.dataSourceRef)
})

it('should add event to data source', () => {
mapRef.events.add = jest.fn()
renderHook(() => useContextConsumer(), {
wrapper: wrapWithVectorTileSourceContext({ id: 'id', events: { sourceadded: (source) => {} } })
})
expect(mapRef.events.add).toHaveBeenCalledWith(
'sourceadded',
expect.any(Object),
expect.any(Function)
)
})
})
43 changes: 43 additions & 0 deletions src/contexts/AzureMapVectorTileSourceProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import atlas from 'azure-maps-control'
import React, { useContext, useState } from 'react'
import { useCheckRef } from '../hooks/useCheckRef'
import { DataSourceType, IAzureMapsContextProps, IAzureMapSourceEventType, IAzureVectorTileSourceStatefulProviderProps, MapType } from '../types'
import { AzureMapDataSourceRawProvider as Provider } from './AzureMapDataSourceContext'
import { AzureMapsContext } from './AzureMapContext'

/**
* @param id datasource identifier
* @param children layer providers representing datasource layers
* @param options vector tile datasource options: see atlas.VectorTileSourceOptions
* @param events a object containing sourceadded, sourceremoved event handlers
*/
const AzureMapVectorTileSourceStatefulProvider = ({
id,
children,
options,
events = {},
}: IAzureVectorTileSourceStatefulProviderProps) => {
const [dataSourceRef] = useState<atlas.source.VectorTileSource>(new atlas.source.VectorTileSource(id, options))
const { mapRef } = useContext<IAzureMapsContextProps>(AzureMapsContext)
useCheckRef<MapType, DataSourceType>(mapRef, dataSourceRef, (mref, dref) => {
for (const eventType in events) {
const handler = events[eventType as IAzureMapSourceEventType] as (e: atlas.source.Source) => void | undefined
if(handler) {
mref.events.add(eventType as IAzureMapSourceEventType, dref, handler)
}
}
mref.sources.add(dref)
})

return (
<Provider
value={{
dataSourceRef,
}}
>
{mapRef && children}
</Provider>
)
}

export { AzureMapVectorTileSourceStatefulProvider as AzureMapVectorTileSourceProvider }
2 changes: 1 addition & 1 deletion src/hooks/useAzureMapLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { MapType } from '../types'

export const constructLayer = (
{ id, options = {}, type }: Omit<IAzureLayerStatefulProviderProps, 'onCreateCustomLayer'>,
dataSourceRef: atlas.source.DataSource
dataSourceRef: DataSourceType
) => {
switch (type) {
case 'SymbolLayer':
Expand Down
3 changes: 2 additions & 1 deletion src/react-azure-maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ export {
export {
AzureMapDataSourceContext,
AzureMapDataSourceConsumer,
AzureMapDataSourceProvider
AzureMapDataSourceProvider,
} from './contexts/AzureMapDataSourceContext'
export { AzureMapVectorTileSourceProvider } from './contexts/AzureMapVectorTileSourceProvider'
export {
AzureMapLayerContext,
AzureMapLayerConsumer,
Expand Down
24 changes: 21 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import atlas, {
UserInteractionOptions,
Control,
BubbleLayerOptions,
LayerOptions
LayerOptions,
VectorTileSourceOptions
} from 'azure-maps-control'

export type IAzureMapOptions = ServiceOptions &
Expand Down Expand Up @@ -120,7 +121,7 @@ export type IAzureMapPopup = {
}

export type IAzureMapDataSourceContextState = {
dataSourceRef: atlas.source.DataSource | null
dataSourceRef: atlas.source.DataSource | atlas.source.VectorTileSource | null
}

export type IAzureMapLayerContextState = {
Expand All @@ -132,10 +133,16 @@ export type IAzureDataSourceChildren =
| ReactElement<IAzureMapFeature>
| ReactElement<IAzureLayerStatefulProviderProps>

export type IAzureVectorTileSourceChildren = ReactElement<IAzureLayerStatefulProviderProps>

export type IAzureMapDataSourceEvent = {
[property in IAzureMapDataSourceEventType]: (e: Shape[]) => void
}

export type IAzureMapVectorTileSourceEvent = {
[property in IAzureMapSourceEventType]?: (e: atlas.source.VectorTileSource) => void
}

export type IAzureMapEvent = {
[property in IAzureMapEventsType]: (
e:
Expand Down Expand Up @@ -169,6 +176,17 @@ export type IAzureDataSourceStatefulProviderProps = {
index?: number
}

export type IAzureVectorTileSourceStatefulProviderProps = {
id: string,
children?: | Array<IAzureVectorTileSourceChildren | IAzureVectorTileSourceChildren[] | null>
| IAzureVectorTileSourceChildren
| null
options?: VectorTileSourceOptions,
events?: IAzureMapVectorTileSourceEvent
// NOTE: not sure yet why this is needed, haven't seen this used in AzureMapsDataSource, though IAzureGeoJSONDataSourceStatefulProviderProps has it
index?: number
}

export type IAzureMapLayerEvent = {
[property in IAzureMapLayerEventType]: (
e: MapMouseEvent | MapTouchEvent | MapMouseWheelEvent
Expand Down Expand Up @@ -307,7 +325,7 @@ export type IAzureMapLayerProps = IAzureMapLayerContextState
export type IAzureMapMouseEventRef = HtmlMarker // && other possible iterfaces
export type IAzureMapsContextProps = IAzureMapContextState
export type IAzureMapDataSourceProps = IAzureMapDataSourceContextState
export type DataSourceType = atlas.source.DataSource
export type DataSourceType = atlas.source.DataSource | atlas.source.VectorTileSource
export type LayerType = atlas.layer.SymbolLayer | atlas.layer.ImageLayer | atlas.layer.TileLayer
export type MapType = atlas.Map
export type GeometryType = atlas.data.Geometry
Expand Down