-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
758 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/* eslint-disable react/default-props-match-prop-types */ | ||
import { | ||
forwardRef, | ||
useImperativeHandle, | ||
useCallback, | ||
useMemo, | ||
} from 'react'; | ||
|
||
import useAMapPluginInstance from '../../hooks/useAMapPluginInstance'; | ||
import useSetter from '../../hooks/useSetter'; | ||
import useAMapOverlayBinder from '../../hooks/useAMapOverlayBinder'; | ||
import useAMapEventBinder from '../../hooks/useAMapEventBinder'; | ||
import useVisible from '../../hooks/useVisible'; | ||
|
||
import type { AMapRectangleProps } from './interface'; | ||
|
||
/** | ||
* Origin API see: | ||
* https://lbs.amap.com/api/javascript-api-v2/documentation#rectangle | ||
*/ | ||
|
||
const defaultProps = { | ||
visible: true, | ||
}; | ||
|
||
const AMapRectangle = forwardRef<AMap.Rectangle, AMapRectangleProps>(({ | ||
bounds, | ||
zIndex, | ||
bubble, | ||
cursor, | ||
draggable, | ||
visible, | ||
extData, | ||
height, | ||
|
||
// styles | ||
strokeColor, | ||
strokeOpacity, | ||
strokeWeight, | ||
strokeStyle, | ||
strokeDasharray, | ||
fillColor, | ||
fillOpacity, | ||
// event properties | ||
onShow, | ||
onHide, | ||
onClick, | ||
onDBLClick, | ||
onRightClick, | ||
onMousedown, | ||
onMouseup, | ||
onMouseover, | ||
onMouseout, | ||
onTouchstart, | ||
onTouchmove, | ||
onTouchend, | ||
}, ref) => { | ||
const initInstance = useCallback((AMap) => new AMap!.Rectangle(), []); | ||
const curInstance = useAMapPluginInstance<AMap.Rectangle>( | ||
'Rectangle', | ||
initInstance, | ||
); | ||
|
||
useImperativeHandle(ref, () => curInstance!, [curInstance]); | ||
|
||
useSetter<Parameters<AMap.Rectangle['setExtData']>>( | ||
curInstance, | ||
'setExtData', | ||
extData!, | ||
); | ||
|
||
const options: Parameters<AMap.Rectangle['setOptions']>[0] = useMemo(() => { | ||
const opts = Object.entries({ | ||
// style options | ||
zIndex, | ||
cursor, | ||
strokeColor, | ||
strokeOpacity, | ||
strokeWeight, | ||
strokeStyle, | ||
strokeDasharray, | ||
fillColor, | ||
fillOpacity, | ||
// other options | ||
draggable, | ||
bubble, | ||
}) | ||
.filter(([, val]) => val !== undefined && val !== null) | ||
.reduce((finallyObj, [key, val]) => { | ||
// eslint-disable-next-line no-param-reassign | ||
finallyObj[key] = val; | ||
return finallyObj; | ||
}, {}); | ||
return opts; | ||
}, [ | ||
bubble, | ||
cursor, | ||
draggable, | ||
fillColor, | ||
fillOpacity, | ||
strokeColor, | ||
strokeDasharray, | ||
strokeOpacity, | ||
strokeStyle, | ||
strokeWeight, | ||
zIndex, | ||
]); | ||
useSetter<Parameters<AMap.Rectangle['setOptions']>>( | ||
curInstance, | ||
'setOptions', | ||
options!, | ||
); | ||
|
||
useSetter<Parameters<AMap.Rectangle['setBounds']>>( | ||
curInstance, | ||
'setBounds', | ||
bounds!, | ||
); | ||
useSetter<Parameters<AMap.Rectangle['setHeight']>>(curInstance, 'setHeight', height || 0); | ||
|
||
useVisible(curInstance, !!visible); | ||
|
||
useAMapEventBinder(curInstance, 'show', onShow); | ||
useAMapEventBinder(curInstance, 'hide', onHide); | ||
useAMapEventBinder(curInstance, 'click', onClick); | ||
useAMapEventBinder(curInstance, 'dblclick', onDBLClick); | ||
useAMapEventBinder(curInstance, 'rightclick', onRightClick); | ||
useAMapEventBinder(curInstance, 'mousedown', onMousedown); | ||
useAMapEventBinder(curInstance, 'mouseup', onMouseup); | ||
useAMapEventBinder(curInstance, 'mouseover', onMouseover); | ||
useAMapEventBinder(curInstance, 'mouseout', onMouseout); | ||
useAMapEventBinder(curInstance, 'touchstart', onTouchstart); | ||
useAMapEventBinder(curInstance, 'touchmove', onTouchmove); | ||
useAMapEventBinder(curInstance, 'touchend', onTouchend); | ||
|
||
useAMapOverlayBinder(curInstance); | ||
|
||
return null; | ||
}); | ||
|
||
AMapRectangle.defaultProps = defaultProps; | ||
|
||
export default AMapRectangle; |
234 changes: 234 additions & 0 deletions
234
src/components/AMapRectangle/__tests__/AMapRectangle.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
import * as React from 'react'; | ||
import { createRef } from 'react'; | ||
import { render, cleanup } from '@testing-library/react'; | ||
|
||
import useAMapPluginInstance from '../../../hooks/useAMapPluginInstance'; | ||
|
||
import AMapRectangle from '../index'; | ||
|
||
const mockInstance = { | ||
setBounds: jest.fn(), | ||
setOptions: jest.fn(), | ||
setExtData: jest.fn(), | ||
setHeight: jest.fn(), | ||
show: jest.fn(), | ||
hide: jest.fn(), | ||
on: jest.fn(), | ||
off: jest.fn(), | ||
}; | ||
|
||
jest.mock('../../../hooks/useAMapPluginInstance', () => ({ | ||
esModule: true, | ||
default: jest.fn((__, cb) => { | ||
cb({ | ||
Rectangle: jest.fn(), | ||
}, {}); | ||
return mockInstance; | ||
}), | ||
})); | ||
|
||
describe('AMapRectangle Component', () => { | ||
const bounds: AMap.BoundsLike = [ | ||
116.39, 39.9, | ||
117.39, 40.9, | ||
]; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
afterEach(cleanup); | ||
|
||
test('renders without crashing', () => { | ||
expect(() => { | ||
render(<AMapRectangle bounds={bounds} />); | ||
}).not.toThrowError(); | ||
expect(useAMapPluginInstance).toHaveBeenCalledWith('Rectangle', expect.any(Function)); | ||
}); | ||
|
||
test('renders without crashing when instance is null', () => { | ||
(useAMapPluginInstance as jest.Mock).mockReturnValueOnce(null); | ||
expect(() => { | ||
render(<AMapRectangle bounds={bounds} />); | ||
}).not.toThrowError(); | ||
}); | ||
|
||
test('support ref to instance', () => { | ||
(useAMapPluginInstance as jest.Mock) | ||
.mockReturnValueOnce(null); | ||
const $ref = createRef<any>(); | ||
const { rerender } = render(<AMapRectangle bounds={bounds} ref={$ref} />); | ||
expect($ref.current).toBe(null); | ||
rerender(<AMapRectangle bounds={bounds} ref={$ref} />); | ||
expect($ref.current).toBe(mockInstance); | ||
}); | ||
|
||
test('sets the circle center and radius', () => { | ||
render(<AMapRectangle bounds={bounds} />); | ||
|
||
expect(mockInstance.setBounds).toHaveBeenCalledWith(bounds); | ||
}); | ||
|
||
test('sets the extra data', () => { | ||
const extData = { id: 1 }; | ||
render(<AMapRectangle bounds={bounds} extData={extData} />); | ||
|
||
expect(mockInstance.setExtData).toHaveBeenCalledWith(extData); | ||
}); | ||
|
||
test('set to invisible', () => { | ||
const { rerender } = render(<AMapRectangle bounds={bounds} />); | ||
|
||
expect(mockInstance.show).toBeCalled(); | ||
|
||
rerender(<AMapRectangle bounds={bounds} visible={false} />); | ||
|
||
expect(mockInstance.hide).toBeCalled(); | ||
}); | ||
|
||
test('set height', () => { | ||
render(<AMapRectangle bounds={bounds} height={10} />); | ||
expect(mockInstance.setHeight).toHaveBeenCalledWith(10); | ||
}); | ||
|
||
test('updates options when props change', () => { | ||
const zIndex = 1; | ||
const bubble = false; | ||
const cursor = '1'; | ||
const fillColor = '#ffffff'; | ||
const fillOpacity = 1; | ||
const strokeColor = '#000000'; | ||
const strokeStyle = 'solid'; | ||
const strokeOpacity = 1; | ||
const strokeWeight = 1; | ||
const strokeDasharray: [number, number] = [10, 40]; | ||
const draggable = false; | ||
|
||
const { rerender } = render( | ||
<AMapRectangle | ||
bounds={bounds} | ||
zIndex={zIndex} | ||
bubble={bubble} | ||
cursor={cursor} | ||
fillColor={fillColor} | ||
fillOpacity={fillOpacity} | ||
strokeColor={strokeColor} | ||
strokeStyle={strokeStyle} | ||
strokeOpacity={strokeOpacity} | ||
strokeWeight={strokeWeight} | ||
strokeDasharray={strokeDasharray} | ||
draggable={draggable} | ||
/>, | ||
); | ||
|
||
expect(mockInstance.setOptions).toHaveBeenCalledWith({ | ||
zIndex, | ||
bubble, | ||
cursor, | ||
fillColor, | ||
fillOpacity, | ||
strokeColor, | ||
strokeStyle, | ||
strokeOpacity, | ||
strokeWeight, | ||
strokeDasharray, | ||
draggable, | ||
}); | ||
|
||
const newFillColor = '#00ff00'; | ||
const newStrokeColor = '#ffff00'; | ||
|
||
rerender( | ||
<AMapRectangle | ||
bounds={bounds} | ||
zIndex={zIndex} | ||
bubble={undefined} | ||
cursor={undefined} | ||
fillColor={newFillColor} | ||
fillOpacity={fillOpacity} | ||
strokeColor={newStrokeColor} | ||
strokeStyle={strokeStyle} | ||
strokeOpacity={strokeOpacity} | ||
strokeWeight={strokeWeight} | ||
strokeDasharray={strokeDasharray} | ||
draggable={draggable} | ||
/>, | ||
); | ||
|
||
expect(mockInstance.setOptions).toHaveBeenCalledWith({ | ||
zIndex, | ||
// bubble, | ||
// cursor, | ||
fillColor: newFillColor, | ||
fillOpacity, | ||
strokeColor: newStrokeColor, | ||
strokeStyle, | ||
strokeOpacity, | ||
strokeWeight, | ||
strokeDasharray, | ||
draggable, | ||
}); | ||
}); | ||
|
||
test('bind event correctly', () => { | ||
const onShow = jest.fn(); | ||
const onHide = jest.fn(); | ||
const onClick = jest.fn(); | ||
const onDBLClick = jest.fn(); | ||
const onRightClick = jest.fn(); | ||
const onMousedown = jest.fn(); | ||
const onMouseup = jest.fn(); | ||
const onMouseover = jest.fn(); | ||
const onMouseout = jest.fn(); | ||
const onTouchstart = jest.fn(); | ||
const onTouchmove = jest.fn(); | ||
const onTouchend = jest.fn(); | ||
|
||
const { unmount } = render( | ||
<AMapRectangle | ||
bounds={bounds} | ||
onShow={onShow} | ||
onHide={onHide} | ||
onClick={onClick} | ||
onDBLClick={onDBLClick} | ||
onRightClick={onRightClick} | ||
onMousedown={onMousedown} | ||
onMouseup={onMouseup} | ||
onMouseover={onMouseover} | ||
onMouseout={onMouseout} | ||
onTouchstart={onTouchstart} | ||
onTouchmove={onTouchmove} | ||
onTouchend={onTouchend} | ||
/>, | ||
); | ||
|
||
expect(mockInstance.on).toBeCalledTimes(12); | ||
expect(mockInstance.on).toHaveBeenCalledWith('show', onShow); | ||
expect(mockInstance.on).toHaveBeenCalledWith('hide', onHide); | ||
expect(mockInstance.on).toHaveBeenCalledWith('click', onClick); | ||
expect(mockInstance.on).toHaveBeenCalledWith('dblclick', onDBLClick); | ||
expect(mockInstance.on).toHaveBeenCalledWith('rightclick', onRightClick); | ||
expect(mockInstance.on).toHaveBeenCalledWith('mousedown', onMousedown); | ||
expect(mockInstance.on).toHaveBeenCalledWith('mouseup', onMouseup); | ||
expect(mockInstance.on).toHaveBeenCalledWith('mouseover', onMouseover); | ||
expect(mockInstance.on).toHaveBeenCalledWith('mouseout', onMouseout); | ||
expect(mockInstance.on).toHaveBeenCalledWith('touchstart', onTouchstart); | ||
expect(mockInstance.on).toHaveBeenCalledWith('touchmove', onTouchmove); | ||
expect(mockInstance.on).toHaveBeenCalledWith('touchend', onTouchend); | ||
|
||
unmount(); | ||
|
||
expect(mockInstance.off).toBeCalledTimes(12); | ||
expect(mockInstance.off).toHaveBeenCalledWith('show', onShow); | ||
expect(mockInstance.off).toHaveBeenCalledWith('hide', onHide); | ||
expect(mockInstance.off).toHaveBeenCalledWith('click', onClick); | ||
expect(mockInstance.off).toHaveBeenCalledWith('dblclick', onDBLClick); | ||
expect(mockInstance.off).toHaveBeenCalledWith('rightclick', onRightClick); | ||
expect(mockInstance.off).toHaveBeenCalledWith('mousedown', onMousedown); | ||
expect(mockInstance.off).toHaveBeenCalledWith('mouseup', onMouseup); | ||
expect(mockInstance.off).toHaveBeenCalledWith('mouseover', onMouseover); | ||
expect(mockInstance.off).toHaveBeenCalledWith('mouseout', onMouseout); | ||
expect(mockInstance.off).toHaveBeenCalledWith('touchstart', onTouchstart); | ||
expect(mockInstance.off).toHaveBeenCalledWith('touchmove', onTouchmove); | ||
expect(mockInstance.off).toHaveBeenCalledWith('touchend', onTouchend); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './interface'; | ||
export { default } from './AMapRectangle'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
export type AMapRectangleProps = AMap.RectangleOptions & { | ||
bounds: AMap.BoundsLike; | ||
visible?: boolean; | ||
|
||
onShow?: (event?: any) => void; | ||
onHide?: (event?: any) => void; | ||
onClick?: (event?: any) => void; | ||
onDBLClick?: (event?: any) => void; | ||
onRightClick?: (event?: any) => void; | ||
onMousedown?: (event?: any) => void; | ||
onMouseup?: (event?: any) => void; | ||
onMouseover?: (event?: any) => void; | ||
onMouseout?: (event?: any) => void; | ||
onTouchstart?: (event?: any) => void; | ||
onTouchmove?: (event?: any) => void; | ||
onTouchend?: (event?: any) => void; | ||
}; |
Oops, something went wrong.