Skip to content

feat(mapbox): Add widget support to MapboxOverlay via IControl adapter#9963

Open
chrisgervang wants to merge 7 commits intomasterfrom
chr/mapbox-widget-support
Open

feat(mapbox): Add widget support to MapboxOverlay via IControl adapter#9963
chrisgervang wants to merge 7 commits intomasterfrom
chr/mapbox-widget-support

Conversation

@chrisgervang
Copy link
Collaborator

@chrisgervang chrisgervang commented Jan 25, 2026

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 _container prop.

Change List

  • Add DeckWidgetControl class that wraps deck widgets as Mapbox IControls
  • Process widgets with viewId: 'mapbox' in MapboxOverlay, automatically wrapping them as IControls
  • Set widget.props._container to Mapbox-positioned element so WidgetManager appends there
  • Widgets stack correctly with native controls in the same DOM container, preventing overlap

Note

Medium Risk
Introduces new widget lifecycle/control-management inside MapboxOverlay, including DOM container reassignment via the internal widget.props._container, which could affect widget rendering/cleanup if edge cases aren’t covered. Scope is limited to widgets with viewId: 'mapbox' and is backed by new unit tests.

Overview
Adds Mapbox/MapLibre control-container support for deck.gl widgets. Widgets with viewId: 'mapbox' are now wrapped in a new DeckWidgetControl IControl adapter so they render in the map’s native control positions instead of overlapping the overlay.

MapboxOverlay now processes widgets on onAdd (before Deck creation) and on setProps, creating/reusing/removing widget controls by widget id and placement and cleaning them up on onRemove.

Tests are expanded with a mock addControl(control, position)/hasControl implementation and new widget-focused specs (including container reuse and interleaved mode); test GL setup is also tweaked to use an isolated device and remove unused imports.

Written by Cursor Bugbot for commit e793c90. This will update automatically on new commits. Configure here.

@coveralls
Copy link

coveralls commented Jan 25, 2026

Coverage Status

coverage: 91.028% (+0.02%) from 91.011%
when pulling e793c90 on chr/mapbox-widget-support
into aee311f on master.

@chrisgervang chrisgervang mentioned this pull request Jan 25, 2026
62 tasks
@chrisgervang chrisgervang added this to the v9.3 milestone Feb 2, 2026
@chrisgervang chrisgervang force-pushed the chr/mapbox-widget-support branch from 40d85b9 to 9426c8c Compare February 4, 2026 23:24
for (const widget of mapboxWidgets) {
const existingControl = existingControlsById.get(widget.id);

if (existingControl && existingControl.widget.placement === widget.placement) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Placement change undetected for mutated widget objects

Low Severity

The placement comparison existingControl.widget.placement === widget.placement fails to detect placement changes when the same widget object is passed with a mutated placement property. After setWidget(widget) is called in a previous iteration, existingControl.widget becomes the same reference as the current widget. When both sides of the comparison reference the same object, the comparison is trivially true regardless of whether placement was actually changed. The control remains in its original map position even though the widget's placement changed.

Fix in Cursor Fix in Web

@chrisgervang chrisgervang force-pushed the chr/mapbox-widget-support branch from 79355a6 to 287b467 Compare February 27, 2026 19:08
chrisgervang and others added 7 commits March 9, 2026 11:05
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>
@chrisgervang chrisgervang force-pushed the chr/mapbox-widget-support branch from 287b467 to e793c90 Compare March 9, 2026 18:06
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

this._widget.props._container = null;
}
this._container?.remove();
this._container = null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onRemove clears _container on wrong widget instance

Low Severity

After successive setProps calls with new widget instances of the same id, DeckWidgetControl._widget references the latest new instance (via setWidget), while WidgetManager tracks the original old instance (via oldWidget.setProps(widget.props)). When DeckWidgetControl.onRemove runs, it clears _container on the control's widget (the wrong instance), leaving a stale _container reference pointing to a detached DOM element on the widget that WidgetManager actually manages.

Additional Locations (1)

Fix in Cursor Fix in Web

@ibgreen ibgreen mentioned this pull request Mar 22, 2026
34 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(mapbox): Add widget support to MapboxOverlay via IControl adapter

3 participants