Skip to content

fix(mapbox): autoload map children declared with the hidden attribute - #655

Open
titouanmathis wants to merge 1 commit into
2.xfrom
fix/v2-mapbox-hidden-autoload
Open

titouanmathis wants to merge 1 commit into
2.xfrom
fix/v2-mapbox-hidden-autoload

Conversation

@titouanmathis

Copy link
Copy Markdown
Contributor

Map children declared with the hidden attribute never autoloaded.

packages/ui-mapbox/src/catalog.ts declared one strategy: 'visible' for the whole package, and the generator stamped it onto all 14 manifest entries. The visible strategy installs an IntersectionObserver on the element. A hidden element is never rendered, so it never intersects, the dynamic import() never runs, and nothing is logged. hidden is the pattern packages/ui-mapbox/README.md recommends for the declarative-only children, so the two recommendations contradicted each other.

What changed

  • scripts/manifest-types.tsCuratedComponentMetadata takes an optional strategy.
  • scripts/generate-manifests.tsserializeComponent serializes component.strategy ?? catalog.strategy.
  • packages/ui-mapbox/src/catalog.ts — the package default stays visible; the twelve map children declare eager. MapboxMap and StoreLocator are the rendered roots and keep visible.
  • packages/ui-mapbox/src/manifest.ts — regenerated.

Why eager is right here

In js-toolkit v4, eager does not mean "load at page load".

The registry never walks the manifest. reconcileElement reads the data-component tokens declared on an element and calls schedule(el, name) per token, so a manifest entry is consulted only for a name present in the DOM. Nothing loads for a token absent from the page. applyMountStrategy handles eager with defaultScheduler.background(mount), which posts through scheduler.postTask at background priority: deferred and off-frame. For a manifest source the mount hook is importPair, which calls entry.load(). Imports are deduplicated per name and the entry is deleted once loaded, so many markers on a page cost one import.

So eager means "no condition left to wait for beyond the element existing". That is what these declarative-only children need. idle would also fix the bug but can delay controls and markers noticeably after the map appears. The heavy mapbox-gl import stays gated behind MapboxMap, which keeps visible.

Tests

  • packages/tests/autoload/manifest.spec.ts asserted one uniform strategy per package. It now takes a per-token expectation, and a dedicated case lists both halves of the ui-mapbox split, so a new component fails the suite until it is classified.
  • packages/tests/autoload/hidden-elements.spec.ts is new. It mounts a MapboxMap holding hidden children declared through the real package manifest and asserts each one loads and mounts, and it shows the contrast: a visible entry on a hidden element is never imported. Both positive tests fail against the old uniform visible manifest.

Full suite: 87 files, 973 tests, all passing. npm run manifest:check and npm run lint pass.

Docs

packages/docs/guide/autoloading/index.md gains a "What eager means" section and a "Package defaults" section describing the ui-mapbox split, and the Mapbox integration note is corrected. packages/ui-mapbox/README.md explains why the hidden children are eager (and its Usage section no longer shows the importWhen* helpers, which js-toolkit v4 removed). The hand-written manifest examples in the MapboxMap reference and the vue-mapbox-gl migration guide gave visible to MapboxMarker and MapboxPopup, which reproduces this bug; they now give eager.

Closes #625

🤖 Generated with Claude Code

https://claude.ai/code/session_01LMSCm41fm3g7chxD728vAu

Map children configure the map from markup that renders nothing, and the
package documents `hidden` on those elements. The whole `@studiometa/ui-mapbox`
catalog declared `visible`, which installs an `IntersectionObserver` on the
element: a `hidden` element is never rendered, never intersects, and its
dynamic `import()` never ran.

A catalog component can now declare its own strategy, and the twelve map
children declare `eager`. The registry only schedules a token an element
declares, so the import happens because the element exists and runs on a
background scheduler task. `MapboxMap` and `StoreLocator` render and keep
`visible`, so the heavy `mapbox-gl` import stays gated behind the map.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LMSCm41fm3g7chxD728vAu
@titouanmathis
titouanmathis force-pushed the fix/v2-mapbox-hidden-autoload branch from b30e482 to 93c5185 Compare September 16, 2026 16:30
@codecov

codecov Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.32%. Comparing base (2d5253b) to head (93c5185).
⚠️ Report is 8 commits behind head on 2.x.

Additional details and impacted files
@@            Coverage Diff            @@
##                2.x     #655   +/-   ##
=========================================
  Coverage     86.32%   86.32%           
  Complexity      145      145           
=========================================
  Files            20       20           
  Lines           746      746           
  Branches         88       88           
=========================================
  Hits            644      644           
  Misses           95       95           
  Partials          7        7           
Flag Coverage Δ
unittests 86.32% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

1 participant