feat(mapbox): Add widget support to MapboxOverlay via IControl adapter#9963
feat(mapbox): Add widget support to MapboxOverlay via IControl adapter#9963chrisgervang wants to merge 30 commits intomasterfrom
Conversation
40d85b9 to
9426c8c
Compare
79355a6 to
287b467
Compare
Enable deck widgets to coexist with native Mapbox/MapLibre controls without DOM overlap by rendering widgets with viewId: 'mapbox' into the map's control container system. - Add DeckWidgetControl class that wraps deck widgets as IControls - Process widgets with viewId: 'mapbox' in MapboxOverlay - Set widget._container to Mapbox-positioned element via IControl - Widgets stack correctly with native controls in same container Closes #9962 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Add TestWidget class for testing widget integration - Test regular widgets render in deck container - Test widgets with viewId: 'mapbox' are wrapped as IControl - Test mixed widgets (regular + mapbox) - Test setProps updates widget controls - Test interleaved mode widget support - Update mock map to support control positions and hasControl Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Overlaid mode tests create their own Deck instances which can cause GL context corruption when sharing contexts across tests. Using an isolated WebGLDevice for these tests prevents hangs in the test suite. Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) <noreply@anthropic.com>
…d rootElement When setProps was called with widgets, _processWidgets would destroy and recreate all DeckWidgetControls. This removed the container from the DOM, orphaning the widget's rootElement since WidgetManager wouldn't re-attach it (same widget id means no change detected). Now matches widgets by id (like WidgetManager) and only recreates controls when the widget is new or placement changes. For existing widgets with same id and placement, the container is preserved and copied to the new widget instance to support React patterns where widgets are recreated on render. Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) <noreply@anthropic.com>
Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) <noreply@anthropic.com>
Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) <noreply@anthropic.com>
When _processWidgets reuses a DeckWidgetControl for a new widget instance with the same id and placement, the control's internal _widget reference must be updated. Otherwise the control references the old widget, causing incorrect behavior in onRemove() and potential memory leaks. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
287b467 to
e793c90
Compare
Resolve merge conflict in mapbox-overlay.spec.ts: - Keep isolated overlaidTestDevice for overlaid mode tests (our branch) - Add device import from @deck.gl/test-utils/vitest and webglTest helper (master) - Keep TestWidget class and widget tests (our branch) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The widget tests used tape-style assertions (t.ok, t.is, t.notOk, t.end) but the file uses vitest's test() which doesn't pass a t argument. Converted all widget tests to use vitest's expect() style. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Placement change recreates control - setWidget updates control widget reference - onRemove clears widget _container - getDefaultPosition maps placement correctly (incl. fill fallback) - Null widgets in array are filtered Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Demonstrates deck.gl widgets (viewId: 'mapbox') coexisting with native NavigationControl in the same map control container. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…upport Resolved conflict in test/modules/mapbox/mapbox-overlay.spec.ts: - Imports: combined Widget/WidgetPlacement imports (ours) with getDeckInstance/MapboxLayerGroup imports (master) - Tests: kept both our widget support tests and master's new MapboxLayerGroup tests (independent test suites appended together)
| const map = this._map; | ||
| if (!map) return; | ||
|
|
||
| const mapboxWidgets = widgets?.filter(w => w && w.viewId === 'mapbox') ?? []; |
There was a problem hiding this comment.
Hardcoded string instead of imported constant MAPBOX_VIEW_ID
Low Severity
_processWidgets uses the hardcoded string 'mapbox' to filter widgets, but the constant MAPBOX_VIEW_ID is already imported in this same file (line 13) and used for the same conceptual purpose at line 348 in _getViews. Using the literal instead of the constant creates an inconsistency — if MAPBOX_VIEW_ID were ever updated, this filter would silently stop matching.
Reviewed by Cursor Bugbot for commit b1babd7. Configure here.
| // Update the control's widget reference to the new instance | ||
| existingControl.setWidget(widget); | ||
| newControls.push(existingControl); | ||
| existingControlsById.delete(widget.id); |
There was a problem hiding this comment.
Widget control references diverge from WidgetManager's resolved widgets
Low Severity
When reusing a control for a new widget instance with the same id, setWidget(widget) updates the control to reference the new widget instance. However, WidgetManager._setWidgets independently reuses the old widget instance (calling oldWidget.setProps(widget.props) and keeping oldWidget). This means DeckWidgetControl._widget and the widget actually managed by WidgetManager are different objects. Consequently, DeckWidgetControl.onRemove clears _container on the wrong (unmanaged) widget instance, leaving the managed widget's _container stale and pointing to a removed DOM element.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit b1babd7. Configure here.
…ration - Add "Using Widgets" section to MapboxOverlay docs explaining viewId: 'mapbox' positioning, default deck overlay positioning, and known widget limitations (view controls, canvas capture) - Add "Basemap Integration" section to what's-new collecting 9.3 improvements: grouped rendering, auto-injected mapbox view, widget support, and bug fixes - Remove stale comment from test file Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit afec60a. Configure here.
When widgets are placed in a basemap control container (via viewId: 'mapbox'), the map already provides spacing between controls. Remove the widget's own margin to prevent double-spacing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…narios Combines coverage from widgets-9.2, widgets-infovis, widgets-multi-view-9.2, and controlled-widgets into a single React app with four tabs: - MapView: full widget showcase with controlled state/callbacks - InfoVis: OrbitView + OrthographicView with GimbalWidget, ScrollbarWidget - Multi-View: nested SplitterWidget with 3-way MapView layout - Basemap: MapLibre + MapboxOverlay with viewId: 'mapbox' widget positioning Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…upport Resolved conflict in docs/whats-new.md by keeping our Layers, Basemap Integration, and Views sections above the Widgets section from master. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removed duplicate Layers/Views sections that were introduced during merge conflict resolution. Merged basemap integration content (with PR links) into the existing @deck.gl/mapbox section, renamed to "Basemap Integration". Dropped standalone Views section (content covered by Improved 3D Support). Section order is now: Widgets > Layers > Basemap Integration > Improved 3D Support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…upport Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split "Basemap Integration" into @deck.gl/mapbox and @deck.gl/google-maps sections. Reword "ensuring consistent behavior" to sound less robotic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>


Closes #9962
Background
Enable deck widgets to coexist with native Mapbox/MapLibre controls without DOM overlap by rendering widgets with
viewId: 'mapbox'into the map's control container system via the new_containerprop.Change List
DeckWidgetControlclass that wraps deck widgets as Mapbox IControlsviewId: 'mapbox'in MapboxOverlay, automatically wrapping them as IControlswidget.props._containerto Mapbox-positioned element so WidgetManager appends thereNote
Medium Risk
Moderate risk because it changes
MapboxOverlaylifecycle/setPropsbehavior to dynamically add/remove map controls and mutate widget_containertargets, which could affect widget rendering or control cleanup across mapbox/maplibre versions.Overview
Adds support for rendering deck.gl widgets inside the Mapbox/MapLibre control container when a widget sets
viewId: 'mapbox', so widgets can be positioned alongside native controls without DOM overlap.MapboxOverlaynow detectsviewId: 'mapbox'widgets, wraps them in a newDeckWidgetControl(anIControladapter that setswidget.props._container), manages control reuse/removal acrosssetProps, and cleans everything up ononRemove(works in both overlaid and interleaved modes). Styling is adjusted to remove widget margins when embedded in map controls, docs/whats-new are updated, and examples/tests are expanded (including a new widget-browser test app and updated mapbox mocks).Reviewed by Cursor Bugbot for commit 3ef91cd. Bugbot is set up for automated code reviews on this repo. Configure here.