5.5 KiB
5.5 KiB
Tasks: enterprise-react-map
Change ID: enterprise-react-map
Package: @suaveplan/react-map
Target path: react/platform/react-map
Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/events, @suaveplan/react-translate
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold
- Create directory tree. Write
package.jsonat0.1.0,tsconfig.json,vitest.config.ts, scripts. - Dependencies:
@suaveplan/error workspace:^,@suaveplan/events workspace:^,@suaveplan/react-translate workspace:^,@opentelemetry/api catalog:observability,zod catalog:forms,tslib catalog:build. Peer/optional:maplibre-gl catalog:maps,leaflet catalog:maps.
2. Error codes and types
- Define
MapError; codes:MAP_ADAPTER_INIT_FAILED,MAP_RENDER_FAILED. - Define all types:
LatLng,MapBounds,MapConfig,MapProps,MapContainerProps,MarkerData,MarkerProps,MarkerClusterData,MarkerClusterProps,PolygonData,PolygonProps,ClusterConfig,MapEventMap,UseMapReturn,MapAdapter,MapAdapterOptions.
3. Utils
geo.ts
haversine(a: LatLng, b: LatLng): number— distance in metres.boundsUnion(a: MapBounds, b: MapBounds): MapBounds— smallest bounding box covering both.pointInPolygon(point: LatLng, polygon: LatLng[]): boolean— ray-cast algorithm.- Write
geo.test.ts(≤500 lines): known-distance pairs, bounds union, polygon in/out.{ timeout: 5000 }. - Write
geo.md.
cluster.ts
clusterMarkers(markers: MarkerData[], config: ClusterConfig): MarkerClusterData[]— grid-based clustering with configurable cell size.- Write
cluster.test.ts(≤500 lines): single cluster, no overlap, empty input, zoom-level variation.{ timeout: 5000 }. - Write
cluster.md.
4. Adapters
MapAdapter interface
adapter.types.ts—MapAdapter<TMap>interface:init(container, config): TMap,setCenter(map, center),setZoom(map, zoom),getBounds(map): MapBounds,on(map, event, handler),off(map, event, handler),destroy(map).
MapLibreAdapter
createMapLibreAdapter(): MapAdapter— wrapsmaplibre-gl; gate withif (maplibregl === undefined) throw MapError(MAP_ADAPTER_INIT_FAILED).- Write
maplibre.test.ts(≤500 lines): init, center/zoom set, bounds read, destroy, missing-lib error. DI-injected maplibre mock.{ timeout: 10000 }. - Write
maplibre.md.
LeafletAdapter
createLeafletAdapter(): MapAdapter— wrapsleaflet; gate similarly.- Write
leaflet.test.ts(≤500 lines).{ timeout: 10000 }. - Write
leaflet.md.
5. Hooks
useMap
useMap(config)— manages center, zoom, bounds, markers, polygons, clusters. Acceptsadapterprop. ReturnsUseMapReturn. OTelmap.initspan.- Write
use-map.test.ts(≤500 lines): state init, center update, marker add/remove, bounds change.{ timeout: 10000 }. - Write
use-map.md.
useMapContext
- React context +
useMapContext()hook. ThrowsMapErrorif used outsideMapContainer. - Write test + md.
6. Components
MapContainer
- Provides
MapContext; renders the vendor-specific map instance. Acceptsadapter,config,className. - Write test, stories (maplibre, leaflet, playground), md.
MapView
- Top-level composite:
MapContainer+ children overlay slot. Keyboard pan (arrow keys), zoom (+/-),role="application",aria-labelviauseTranslation. - Write test, stories (default, markers, polygons, clusters, playground), md.
Marker
- Rendered via adapter; accepts
position,onClick,label,icon. Screen-reader description generated from label. - Write test, stories (default, custom-icon, with-popup, playground), md.
MarkerCluster
- Renders cluster circle with count using
clusterMarkers. Animated expand on click. - Write test, stories (small-cluster, large-cluster, expanding, playground), md.
Polygon
- Renders polygon overlay via adapter. Accepts
positions[],fillColor,strokeColor,onClick. - Write test, stories (default, filled, multi-polygon, playground), md.
7. E2E
e2e/map.spec.ts— Playwright:- Render
MapViewwith MapLibre adapter; verify map canvas present. - Click a
Marker; verify popup/callback fires. - Render
MarkerCluster; click cluster; verify it expands to individual markers. - Draw a
Polygon; verifyonClickfires with correct coordinates. - Use keyboard arrow keys to pan; verify center coordinates change.
- Render
8. Verify and publish
bunx turbo lint typecheck test --filter=@suaveplan/react-map— green.bunx turbo test:coverage --filter=@suaveplan/react-map— 100%.bun scripts/publish.ts --package @suaveplan/react-map.- Archive change folder.