Migrate to Bootstrap 5.3 and resolve modal/popover/tooltip lifecycle bug#74
Migrate to Bootstrap 5.3 and resolve modal/popover/tooltip lifecycle bug#74malberts wants to merge 9 commits into
Conversation
Port the parser-hook output of every component from Bootstrap 4 to
Bootstrap 5.3:
- All data-attribute names switched to the data-bs-* family
(data-toggle/target/dismiss/ride/slide/parent/content/placement/trigger).
- Badge: .badge-pill replaced with the .rounded-pill utility.
- Button: .btn-default is gone in BS5; color='default' now maps to
btn-secondary (Modal, Popover apply the same mapping for their
trigger buttons).
- Alert / Modal close buttons: .close + inner <span>×</span>
replaced with the BS5 .btn-close button.
- Jumbotron: the .jumbotron class was removed in BS5. The parser
function is retained for backward compatibility but now emits the
BS5 utility-class approximation per the official migration guide
('p-5 mb-4 bg-body-tertiary rounded-3').
- JavaScript modules for carousel/popover/tooltip rewritten from
jQuery to vanilla JS (BS5 dropped jQuery as a dependency).
- Test expectations updated to the new emitted markup.
Floor versions bumped to MediaWiki >= 1.43, PHP >= 8.1, and
mediawiki/bootstrap ^6.0 (the BS5.3-bearing major). Extension version
6.0.0-dev. README mention updated.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Update the CI matrix to cover the MediaWiki and PHP versions supported by the Bootstrap 5 release. Also disable Scrutinizer Ocular coverage upload (the upstream service is broken). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
With the floor at MediaWiki 1.43, the deprecated global-namespace fallbacks (e.g. \Html, \Title, \Parser) can be dropped in favour of the namespaced equivalents (MediaWiki\Html\Html, MediaWiki\Title\Title, MediaWiki\Parser\Parser). Replace deprecated aliases across the extension and tidy up unused / redundant use statements in passing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PHPUnit expectations across the Components, ImageModal and ModalBuilder tests updated to match the BS5 emission switched on by the migration commit (data-bs-* attributes, .btn-close close buttons, .rounded-pill badges, btn-secondary default-color, jumbotron utility classes). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The tooltip JS module added in the BS5 migration listens for 'DOMContentLoading' (which is not a real event) instead of 'DOMContentLoaded'. The bug guard branch is reached when the script loads before the DOM is parsed; with the typo the listener never fires and the tooltip initialiser is never called on first-render. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The old design path was: parser hook → ModalBuilder::parse() → ParserOutputHelper::injectLater() → $parserOutput->setExtensionData(DEFERRED_CONTENT_KEY, ...), and at render time → OutputPageParserOutput hook → $parserOutput->getExtensionData(DEFERRED_CONTENT_KEY) → $outputPage->addHTML($deferredText). Under MediaWiki 1.43+, the set-side and get-side operate on two different ParserOutput objects (an effect of ParserOutputAccess changes between 1.39 and 1.43), so the deferred-content array is stored on the parse-time object and never reaches the render-time one. Net effect: modal trigger HTML reaches the page but the modal container HTML does not, leaving the trigger with nothing to open. This is the symptom described in upstream issue #68 (open since 2025-08-08). The defensive design rationale for body-end injection (Tidy's hostility to <div> inside <p>; BS4 modal stacking concerns) no longer applies: MediaWiki 1.40+ uses RemexHtml which auto-closes the enclosing inline element rather than mangling block-in-inline, and Bootstrap 5 modals use position: fixed plus an ID-resolving data-bs-target attribute, so DOM placement is no longer significant. Refactor: - ModalBuilder::parse() returns the trigger HTML and modal HTML concatenated; modal markup emits as a sibling of its trigger at the wikitext tag's natural position. - Remove ParserOutputHelper::injectLater() and the unused EXTENSION_DATA_DEFERRED_CONTENT_KEY constant. - Trim OutputPageParserOutput::process() down to what survives the parse → render lifecycle: OutputPage::addModules() for the Bootstrap library JS module and BC's per-component JS init modules (OutputPage IS the persistent object — modules added there reach the page; modules added to the parse-time ParserOutput do not). - Add a new ext.bootstrapComponents.modal.js resource module that instantiates bootstrap.Modal on each .modal element (BS5 dropped jQuery-driven modal autoinit; without an explicit instantiation the data-bs-toggle='modal' triggers don't fire). Closes #68. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…family Bootstrap 5.3 removed the .badge-<color> class family. Replacement is .text-bg-<color>, which sets both background-color and a contrasting text color (so a primary-coloured badge gets readable text on it without manual intervention). Update Badge::calculateClassAttribute() to emit the new family, and update BadgeTest expectations to match. This was a gap in PR #69 — that PR updated almost all components for BS5 but missed Badge's color-class emission. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ModalTest, ImageModalTest, ModalBuilderTest, and OutputPageParserOutputTest all assumed the old deferred-content pattern — they mocked parserOutputHelper->injectLater() to capture the modal markup separately from the parser-hook return value. After the inline-emission refactor in 5f65471 the modal markup is concatenated into the parse output and injectLater() no longer exists. Updates: - ModalTest::testCanRender asserts the parse output equals the trigger HTML and the modal HTML concatenated. - ImageModalTest::testCanParseImage asserts the expected modal markup is a substring of the parse result. - ModalBuilderTest::testCanParse asserts ModalBuilder::parse() returns trigger HTML concatenated with the modal HTML. - OutputPageParserOutputTest rewritten: asserts that OutputPage::addModules is called for ext.bootstrap.scripts + the four BC component JS modules, plus vector-fix when the Vector skin is active. Drops the old expectation that the hook calls addHTML with deferred content (the deferred-content branch was removed). 96 of BC's PHPUnit tests now pass on bs5-followup (excluding the Lua test files, which require Scribunto in the test environment). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sweep through docs/components.md and the 12 per-component .md files under docs/components/ to bring them in line with what BS5 BC actually emits, plus a few related docs touch-ups: - All external 'see also' links pointing to https://getbootstrap.com/docs/4.1/ bumped to /docs/5.3/. Jumbotron's outdated direct link now points at the BS5 migration guide entry instead (BS5 doesn't have a jumbotron page). - jumbotron.md: prepended a deprecation banner noting the BS5 removal and the utility-class approximation BC now emits. Also clarified that the BS4-era 'enlarges fonts' behaviour does not survive the utility-class reimplementation (apply display-* utilities for that look). - components.md: inline note next to the Jumbotron list entry calling out the deprecation. - button.md / badge.md / modal.md / popover.md: clarified next to the color='default' list item that this maps to 'secondary' under Bootstrap 5 (matches the runtime behaviour Button.php, Modal.php, Popover.php already implemented). - known-issues.md: top-of-file note that the long-standing modal/popover/ tooltip 'stop working after a cache purge on MW 1.43.3' class of issues (upstream #68) was resolved in 6.0 by the inline-emission refactor. - release-notes.md: 6.0.0 entry expanded with the bug-fix section describing the deferred-content → inline-emission rewrite, the DOMContentLoading typo fix, the new modal JS module, and the OutputPageParserOutput addModules change. Also tweaked the badge bullet to mention the text-bg-<color> family and the default→secondary mapping. - migration-guide.md: bumped 'mediawiki/bootstrap 6.x-dev' reference to '^6.0' to match the released constraint. Code-level: Badge::calculateClassAttribute now maps color='default' to 'secondary' for backward-compat (matches Button/Modal/Popover behaviour). BadgeTest still 7/7. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Visual evidenceCaptured on the 4-stack visual-regression harness (MediaWiki 1.43.8). Wikitext source for both pages is in the PR's findings / available on request. 1. Modal click actually opens on the BS5 candidate (closes #68)
2. Comprehensive per-attribute matrix — Chameleon BS4 baseline vs BS5 candidate
Chameleon BS4 baseline (
Chameleon BS5 candidate (
Visible differences are exactly the accepted BS5 style-trend evolution (brighter primary blue / cyan, slightly larger border-radius, lighter 3. Cross-skin parity — Medik BS5 candidateSame
|




Closes #38.
Fixes #68.
Summary
Upgrades BootstrapComponents from Bootstrap 4 to Bootstrap 5.3, at functional parity with the last released BS4-era BC (5.2.2) and additionally resolves the upstream "Modal/Popover/Tooltip stop working under MediaWiki 1.43+" bug.
This branch was informed by the earlier WIP exploration in #69 (now closed as superseded); it is a clean, self-contained set of nine commits.
What changed
Nine self-contained commits:
data-toggle→data-bs-togglefamily,.btn-default→.btn-secondarymapping forcolor="default",.close→.btn-close,.badge-pill→.rounded-pill, jumbotron utility-class reimplementation per the BS5 migration guide, vanilla-JS rewrites of carousel/popover/tooltip). Floor versions bumped to MW ≥ 1.43, PHP ≥ 8.1,mediawiki/bootstrap: ^6.0.use \Htmletc. replaced withuse MediaWiki\…\Xacross the codebase (required-ish at the MW 1.43 floor).'DOMContentLoading'(which is not an event) is now'DOMContentLoaded'.text-bg-<color>for badges — BS5 removed the.badge-<color>family; replaced with.text-bg-<color>.color="default"maps totext-bg-secondaryfor backward-compat (matches Button/Modal/Popover behaviour).https://getbootstrap.com/docs/4.1/link bumped to/docs/5.3/; jumbotron deprecation banner;color="default"mapping noted in the relevant component docs;known-issues.mdupdated with the Tooltip, Popover and Modal stop working after a cache purge on MW 1.43.3 #68 resolution note; release-notes 6.0 entry expanded.Resolves upstream issue #68
PR #69 noted "Modal does not trigger" / "Popover does not trigger" as a TODO. Investigation showed it is the same bug as the open issue #68 ("Tooltip, Popover and Modal stop working after a cache purge on MW 1.43.3").
Root cause: BC's
ModalBuilder::parse()stashed the modal markup viaParserOutputHelper::injectLater()→$parserOutput->setExtensionData(DEFERRED_CONTENT_KEY, …), andOutputPageParserOutput::process()retrieved it via the same key at render time. Diagnostic instrumentation under MW 1.43.8 showed the parse-time and render-timeParserOutputinstances are different objects (spl_object_iddiffers); the deferred-content data set on the parse-time object never reaches the render-time hook. Effect: trigger renders, modal container does not, click does nothing.The defensive design rationale for the deferred-injection pattern no longer applies under modern MediaWiki + Bootstrap 5 — MW 1.40+ uses RemexHtml which auto-closes the enclosing inline element rather than mangling block-in-inline, and BS5 modals use
position: fixedplus an ID-resolvingdata-bs-targetattribute so DOM placement is no longer significant.Refactor: emit the modal markup inline as a sibling of its trigger button. Per-component JS init modules (the BS5 vanilla-JS replacements for the dropped jQuery plugins) are loaded via
OutputPage::addModules()from theOutputPageParserOutputhook (which works reliably;OutputPageis the persistent object). A newext.bootstrapComponents.modal.jsmodule explicitly instantiatesbootstrap.Modalon each.modalelement, since BS5 dropped the BS4 jQuery-driven autoinit.The same patch was applied workspace-only against BC 5.2.2 to confirm: with this commit, modal markup renders on MW 1.43+ even without the BS5 migration. So the fix would also help users still on BC 5.2.x if backported.
Verification
data-bs-toggleclick opens the modal (with backdrop,.modal.fade.show); popover and tooltip JS instances attach; dismissible alert.btn-closecloses the alert.LuaLibrary*Test.phpwhich require Scribunto in the test environment).Component deprecations (BS5)
bootstrap_jumbotron— BS5 removed the.jumbotronclass (migration guide entry). The parser function is retained and now emits the BS5 utility-class approximation per the migration guide (p-5 mb-4 bg-body-tertiary rounded-3). The deprecation is called out in docs/components/jumbotron.md and docs/components.md; the parser function may be removed in a future major release.Notes for users
mediawiki/bootstrapconstraint pins to^6.0(the released BS5.3-bearing major). Anyone on the previous^5.0is on a BS4 stack and should stay on BC 5.x until ready to upgrade their wiki to MW 1.43+ + the new Bootstrap extension major.color="default"automatically maps tobtn-secondary(button/modal/popover/badge).Out of scope
bootstrap_offcanvas,bootstrap_spinner,bootstrap_toast) — separate enhancement.*.vector-fix.cssmodules for continued necessity under BS5 — separate follow-up.MWExceptionto typed exceptions — latent debt, separate follow-up.🤖 Generated with Claude Code