diff --git a/src/components/AMapRangingTool/AMapRangingTool.tsx b/src/components/AMapRangingTool/AMapRangingTool.tsx new file mode 100644 index 0000000..55211f1 --- /dev/null +++ b/src/components/AMapRangingTool/AMapRangingTool.tsx @@ -0,0 +1,43 @@ +import type { FC } from 'react'; +import { useEffect } from 'react'; + +import useAMapPluginInstance from '../../hooks/useAMapPluginInstance'; +import useAMapEventBinder from '../../hooks/useAMapEventBinder'; + +import type { AMapRangingToolProps } from './interface'; + +const initInstance = ( + AMap: typeof globalThis.AMap, + map: AMap.Map, // +) => new AMap!.RangingTool(map, {} as any); + +const AMapRangingTool: FC = ({ + disabled, + onNodeAdded, + onNodeRemoved, + onEnd, +}) => { + const curInstance = useAMapPluginInstance('RangingTool', initInstance); + + useEffect(() => { + if (!curInstance) return; + if (disabled) { + curInstance.turnOff(); + } else { + curInstance.turnOn(); + } + }, [disabled, curInstance]); + + useAMapEventBinder(curInstance, 'addnode', onNodeAdded); + useAMapEventBinder(curInstance, 'removenode', onNodeRemoved); + useAMapEventBinder(curInstance, 'end', onEnd); + + return null; +}; + +AMapRangingTool.defaultProps = { + // eslint-disable-next-line react/default-props-match-prop-types + disabled: false, +}; + +export default AMapRangingTool; diff --git a/src/components/AMapRangingTool/__tests__/AMapRangingTool.test.tsx b/src/components/AMapRangingTool/__tests__/AMapRangingTool.test.tsx new file mode 100644 index 0000000..97bc3cd --- /dev/null +++ b/src/components/AMapRangingTool/__tests__/AMapRangingTool.test.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import { render, cleanup } from '@testing-library/react'; + +import useAMapPluginInstance from '../../../hooks/useAMapPluginInstance'; + +import AMapRangingTool from '../AMapRangingTool'; + +const mockInstance = { + turnOn: jest.fn(), + turnOff: jest.fn(), + on: jest.fn(), + off: jest.fn(), +}; + +jest.mock('../../../hooks/useAMapPluginInstance', () => ({ + esModule: true, + default: jest.fn((__, cb) => { + cb( + { + RangingTool: jest.fn(), + }, + {}, + ); + + return mockInstance; + }), +})); + +describe('AMapRangingTool', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + afterEach(cleanup); + + test('renders without crashing', () => { + expect(() => { + render(); + }).not.toThrowError(); + expect(useAMapPluginInstance).toHaveBeenCalledWith('RangingTool', expect.any(Function)); + }); + + test('renders without crashing when instance is null', () => { + (useAMapPluginInstance as jest.Mock).mockReturnValueOnce(null); + expect(() => { + render(); + }).not.toThrowError(); + }); + + test('bind event correctly', () => { + const onNodeAdded = jest.fn(); + const onNodeRemoved = jest.fn(); + const onEnd = jest.fn(); + + const { unmount } = render( + , + ); + + expect(mockInstance.on).toBeCalledTimes(3); + expect(mockInstance.on).toBeCalledWith('addnode', onNodeAdded); + expect(mockInstance.on).toBeCalledWith('removenode', onNodeRemoved); + expect(mockInstance.on).toBeCalledWith('end', onEnd); + + unmount(); + + expect(mockInstance.on).toBeCalledTimes(3); + expect(mockInstance.off).toBeCalledWith('addnode', onNodeAdded); + expect(mockInstance.off).toBeCalledWith('removenode', onNodeRemoved); + expect(mockInstance.off).toBeCalledWith('end', onEnd); + }); +}); diff --git a/src/components/AMapRangingTool/index.ts b/src/components/AMapRangingTool/index.ts new file mode 100644 index 0000000..bea0791 --- /dev/null +++ b/src/components/AMapRangingTool/index.ts @@ -0,0 +1,2 @@ +export * from './interface'; +export { default } from './AMapRangingTool'; diff --git a/src/components/AMapRangingTool/interface.ts b/src/components/AMapRangingTool/interface.ts new file mode 100644 index 0000000..d3cd813 --- /dev/null +++ b/src/components/AMapRangingTool/interface.ts @@ -0,0 +1,6 @@ +export type AMapRangingToolProps = { + disabled?: boolean; + onNodeAdded?: (event?: any) => void; + onNodeRemoved?: (event?: any) => void; + onEnd?: (event?: any) => void; +}; diff --git a/src/components/AMapRangingTool/stories/AMapRangingTool.stories.tsx b/src/components/AMapRangingTool/stories/AMapRangingTool.stories.tsx new file mode 100644 index 0000000..8a50540 --- /dev/null +++ b/src/components/AMapRangingTool/stories/AMapRangingTool.stories.tsx @@ -0,0 +1,79 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { actions } from '@storybook/addon-actions'; + +import { AMapRangingTool } from '../../../index'; + +import withAMap from '../../../storybook-decorators/withAMap'; +import withAMapContainer from '../../../storybook-decorators/withAMapContainer'; +import withAPIContainer from '../../../storybook-decorators/withAPIContainer'; + +const eventHandler = actions('onNodeAdded', 'onNodeRemoved', 'onEnd'); + +const meta: Meta = { + title: '组件(Components)/工具(Tools)/AMapRangingTool', + component: AMapRangingTool, + decorators: [withAMap(), withAMapContainer, withAPIContainer], + args: {}, + argTypes: { + disabled: { + description: '禁用 RangingTool', + type: { required: false, name: 'boolean' }, + table: { + type: { summary: 'boolean' }, + defaultValue: { summary: false }, + }, + control: 'boolean', + }, + onNodeAdded: { + description: '每添加一个量测点时触发此事件', + type: { required: false, name: 'function' }, + table: { + type: { + summary: '(event: AMap.Event<"addnode">) => void', + }, + }, + }, + onNodeRemoved: { + description: '每删除一个量测点时触发此事件', + type: { required: false, name: 'function' }, + table: { + type: { + summary: '(event: AMap.Event<"removenode">) => void', + }, + }, + }, + onEnd: { + description: '距离量测结束后触发此事件', + type: { required: false, name: 'function' }, + table: { + type: { + summary: '(event: AMap.Event<"end">) => void', + }, + }, + }, + }, +}; + +export default meta; + +type AMapRangingToolStory = StoryObj; + +export const CommonUse: AMapRangingToolStory = { + name: '基本使用', +}; + +export const Disabled: AMapRangingToolStory = { + name: '禁用', + args: { + disabled: true, + }, +}; + +export const BindEvent: AMapRangingToolStory = { + name: '监听事件', + args: { + onNodeAdded: eventHandler.onNodeAdded, + onNodeRemoved: eventHandler.onNodeRemoved, + onEnd: eventHandler.onEnd, + }, +}; diff --git a/src/index.ts b/src/index.ts index 0c25f0f..f0f1ef5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,7 +41,8 @@ export { default as AMapPolylineEditor } from './components/AMapPolylineEditor'; export type { AMapMouseToolProps } from './components/AMapMouseTool'; export { default as AMapMouseTool } from './components/AMapMouseTool'; - +export type { AMapRangingToolProps } from './components/AMapRangingTool'; +export { default as AMapRangingTool } from './components/AMapRangingTool'; // helpers export { default as coordsOfGeoJSON2AMapPolygonPath } from './helpers/coordsOfGeoJSON2AMapPolygonPath'; export { default as amapPolygonPath2GeoJSONCoords } from './helpers/amapPolygonPath2GeoJSONCoords';