bootstrap: swap BS4 (alexwine) CSS for bootstrap@5 npm

Completes the Bootstrap 4->5 migration now that every jQuery/BS4 widget is React
(navbar, carousel, dropdowns) — plus the feedback toggle here (Feedback.js:
global `$('#feedback-form').toggle()` -> React state).

- Load Bootstrap 5 CSS from the `bootstrap` npm package in client/index.js
  (imported first so app + component styles and react-bootstrap override it).
- Remove the `alexwine:bootstrap-4` meteor package (BS4 CSS + jQuery + BS4 JS).
  jQuery for jquery-validation still comes from the npm `jquery` dep.
- Utility renames to BS5: ml-auto->ms-auto, float-right->float-end,
  btn-block->w-100, data-toggle->data-bs-toggle (FromNow tooltip).
- forms.scss `.form-label` is no longer a shim (BS5 ships it); comment updated.

Full-app build boots clean; server suite 36 passing. Needs a visual staging pass
across all pages (BS4->5 shifts grid gutters/typography); forms should improve
since react-bootstrap v2 already emitted BS5 markup.
This commit is contained in:
vjrj 2026-07-22 05:55:34 +02:00
parent 9ff9abacc3
commit bddfb392c1
12 changed files with 63 additions and 38 deletions

View file

@ -30,7 +30,6 @@ audit-argument-checks@1.0.8
ddp-rate-limiter@1.2.2 ddp-rate-limiter@1.2.2
dynamic-import@0.7.4 dynamic-import@0.7.4
static-html@1.4.0 static-html@1.4.0
alexwine:bootstrap-4
gadicc:blaze-react-component # Add braze to react gadicc:blaze-react-component # Add braze to react
255kb:meteor-status # Connect status 255kb:meteor-status # Connect status
@ -45,6 +44,10 @@ ostrio:meteor-root
aldeed:schema-deny aldeed:schema-deny
dburles:collection-helpers dburles:collection-helpers
reywood:publish-composite reywood:publish-composite
# Bootstrap CSS/JS now comes from the `bootstrap` npm package (BS5), imported in
# imports/startup/client/index.js. The old alexwine:bootstrap-4 package (BS4 CSS +
# jQuery + BS4 JS) was removed after the navbar/carousel/dropdown/feedback widgets
# were migrated off jQuery.
facts-base@1.0.2 facts-base@1.0.2
nspangler:autoreconnect nspangler:autoreconnect
quave:synced-cron quave:synced-cron

View file

@ -8,7 +8,6 @@ accounts-password@3.0.3
aldeed:collection2@4.2.0 aldeed:collection2@4.2.0
aldeed:schema-deny@4.0.2 aldeed:schema-deny@4.0.2
aldeed:simple-schema@1.13.1 aldeed:simple-schema@1.13.1
alexwine:bootstrap-4@4.1.0
allow-deny@2.0.0 allow-deny@2.0.0
audit-argument-checks@1.0.8 audit-argument-checks@1.0.8
autoupdate@2.0.0 autoupdate@2.0.0

View file

@ -65,25 +65,27 @@ React 19: el `<Blaze serverFacts>` de Status.js (`findDOMNode`), solo en `/statu
- ✅ **`meteor-accounts-t9n` actualizado** a `^2.6.0` (es/en OK). El paquete sigue - ✅ **`meteor-accounts-t9n` actualizado** a `^2.6.0` (es/en OK). El paquete sigue
sin build gallego (`gl`), así que se mantiene el fallback gl→es documentado en sin build gallego (`gl`), así que se mantiene el fallback gl→es documentado en
`i18n.js`. Traducción gallega de la app (`gl/common.json`): 0 claves faltantes. `i18n.js`. Traducción gallega de la app (`gl/common.json`): 0 claves faltantes.
- **Salto Bootstrap 4→5 (CSS/JS) pendiente:** react-bootstrap ya está en v2 y - ✅ **Salto Bootstrap 4→5 (CSS/JS) hecho** — react-bootstrap v2 ahora corre sobre
corre sobre el CSS Bootstrap 4 actual (`alexwine:bootstrap-4`) con un único su CSS nativo (BS5). **Compila y arranca; falta SOLO verificación visual en
shim `.form-label` en `forms.scss`. **Ya no quedan dependencias del JS jQuery de staging** (BS4→5 cambia sutilezas de grid/gutters/tipografía en todas las páginas).
BS4** — los dos blockers se migraron a React: - Widgets jQuery/BS4 migrados a React (los blockers): navbar collapse
- ✅ **Collapse del navbar** (`Navigation.js` + `NavItem.js`): `data-toggle= (`Navigation.js`/`NavItem.js`), carrusel del home (`Index.js``<Carousel>`,
"collapse"` jQuery → estado React (`useState` que alterna `.show`). conserva `.lazy` vía `onSlide`), dropdowns de idioma/tipo (`Profile.js`/
*Verificar en staging el menú móvil (<lg): abrir/cerrar y cierre al pulsar un `Fires.js``<Dropdown>`), y toggle del feedback (`Feedback.js` → estado React).
enlace.* Deps `bootstrap-carousel-swipe` y `alexwine:bootstrap-4` eliminadas; jQuery
- ✅ **Carrusel del home** (`Index.js`): `bootstrap-carousel-swipe` (jQuery) + global ya no se usa (solo `jquery`+`jquery-validation` vía npm en `validate.js`).
`$(...).carousel()``<Carousel>` de react-bootstrap con swipe nativo; se - CSS: `bootstrap@5` npm importado en `imports/startup/client/index.js` (antes
conserva el mecanismo `.lazy` de fondos (blur→full) vía `onSlide`. Dep que `app.scss`, para que los overrides ganen). Renombres de utilidades:
`bootstrap-carousel-swipe` eliminada. Ports de CSS en `Index-custom.scss`: `ml-auto``ms-auto`, `float-right``float-end`, `btn-block``w-100`,
indicadores `li``button` (markup BS5) y shim `.visually-hidden`. `data-toggle``data-bs-toggle`, `sr-only``.visually-hidden` (shim).
*Verificar en staging el home: transiciones, swipe, indicadores, prev/next y - **QA visual pendiente en staging (todas las páginas):** formularios (react-
carga progresiva de las imágenes de fondo.* bootstrap ya emite markup BS5 → deberían mejorar), navbar, botones, modales,
- Falta (el salto de CSS en sí, ya sin bloqueos de JS): importar `bootstrap@5` cards, grid/gutters, y los widgets migrados (menú móvil, carrusel, dropdowns,
SCSS+JS, `ml-auto``ms-auto` (y demás utilidades direccionales), revisar feedback). Alertas de `themeteorchef:bert` (comprobar que siguen bien sin el
`new-age.scss`/`custom.scss`/`bootstrap-overrides.scss` y retirar jQuery de alexwine).
`alexwine:bootstrap-4`. - Menor: `popper.js@1` en `package.json` es legacy sin uso (react-bootstrap trae
`@popperjs/core@2`) → se puede quitar. El bloque `.form-label` de `forms.scss`
ya es redundante (BS5 lo trae) salvo por el `display:block` explícito.
- **Limpieza menor:** migración 217 `// TODO remove falsepositives lowercase - **Limpieza menor:** migración 217 `// TODO remove falsepositives lowercase
collection`; `prerender.js` gating comentado; sección per-fire del sitemap collection`; `prerender.js` gating comentado; sección per-fire del sitemap
eliminada (era código muerto tras `firesMapEnabled=false`). eliminada (era código muerto tras `firesMapEnabled=false`).

View file

@ -1,4 +1,8 @@
/* global */ /* global */
// Bootstrap 5 CSS (npm) replaces the old `alexwine:bootstrap-4` meteor package.
// Import it first so the app + component stylesheets below (and react-bootstrap,
// which targets BS5) override it.
import 'bootstrap/dist/css/bootstrap.css';
import React from 'react'; import React from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { HelmetProvider } from 'react-helmet-async'; import { HelmetProvider } from 'react-helmet-async';

View file

@ -18,6 +18,7 @@ import './Feedback.scss';
class Feedback extends Component { class Feedback extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { open: false };
this.handleSubmit = this.handleSubmit.bind(this); this.handleSubmit = this.handleSubmit.bind(this);
this.onTabClick = this.onTabClick.bind(this); this.onTabClick = this.onTabClick.bind(this);
} }
@ -50,7 +51,9 @@ class Feedback extends Component {
} }
onTabClick() { onTabClick() {
$('#feedback-form').toggle('slide'); // Was `$('#feedback-form').toggle('slide')` — global jQuery from the BS4
// meteor package. React state instead, so nothing depends on that global.
this.setState(s => ({ open: !s.open }));
} }
handleSubmit() { handleSubmit() {
@ -76,7 +79,7 @@ class Feedback extends Component {
<div> <div>
{ !this.props.isHome && { !this.props.isHome &&
<div id="feedback"> <div id="feedback">
<div id="feedback-form" ref={formdiv => (this.formdiv = formdiv)} style={{ display: 'none' }} className="card"> <div id="feedback-form" ref={formdiv => (this.formdiv = formdiv)} style={{ display: this.state.open ? 'block' : 'none' }} className="card">
<form <form
ref={form => (this.form = form)} ref={form => (this.form = form)}
className="form card-body" className="form card-body"
@ -109,7 +112,7 @@ class Feedback extends Component {
/> />
<Form.Control.Feedback /> <Form.Control.Feedback />
</Form.Group> </Form.Group>
<Button type="submit" variant="success" className="float-right" id={testId('sendFeedbackBtn')} > <Button type="submit" variant="success" className="float-end" id={testId('sendFeedbackBtn')} >
{t('Enviar')} {t('Enviar')}
</Button> </Button>
</form> </form>

View file

@ -33,7 +33,7 @@ class FromNow extends Component {
render() { render() {
return ( return (
<span data-toggle="tooltip" className="from-now" title={this.thatIs()}>{this.state.when}</span> <span data-bs-toggle="tooltip" className="from-now" title={this.thatIs()}>{this.state.when}</span>
); );
} }
} }

View file

@ -37,7 +37,7 @@ const Navigation = ({ name = '', ...props }) => {
</button> </button>
<div className={`collapse navbar-collapse${open ? ' show' : ''}`} id="navbarNavDropdown"> <div className={`collapse navbar-collapse${open ? ' show' : ''}`} id="navbarNavDropdown">
{/* onClick closes the mobile menu when a main nav link is tapped */} {/* onClick closes the mobile menu when a main nav link is tapped */}
<ul className="navbar-nav ml-auto " onClick={close}> <ul className="navbar-nav ms-auto " onClick={close}>
{/* <LinkContainer className="nav-item" anchorClassName="nav-link" to="/sandbox"> {/* <LinkContainer className="nav-item" anchorClassName="nav-link" to="/sandbox">
<NavItem eventKey={1.1} href="/sandbox">Sandbox</NavItem> <NavItem eventKey={1.1} href="/sandbox">Sandbox</NavItem>
</LinkContainer> */} </LinkContainer> */}

View file

@ -45,7 +45,7 @@ const defaultCallback = (error) => {
const OAuthLoginButton = ({ service, callback = defaultCallback }) => ( const OAuthLoginButton = ({ service, callback = defaultCallback }) => (
<button <button
className={`btn btn-block btn-raised btn-danger OAuthLoginButton OAuthLoginButton-${service}`} className={`btn w-100 btn-raised btn-danger OAuthLoginButton OAuthLoginButton-${service}`}
type="button" type="button"
onClick={() => handleLogin(service, callback)} onClick={() => handleLogin(service, callback)}
> >

View file

@ -132,7 +132,7 @@ class Signup extends React.Component {
{ Meteor.settings.public.telegramAuth && { Meteor.settings.public.telegramAuth &&
<Col xs={12}> <Col xs={12}>
<button <button
className="btn btn-block btn-raised btn-primary OAuthLoginButtonDis OAuthLoginButton-telegram" className="btn w-100 btn-raised btn-primary OAuthLoginButtonDis OAuthLoginButton-telegram"
type="button" type="button"
onClick={this.onTelegramAuth} onClick={this.onTelegramAuth}
> >

View file

@ -1,11 +1,10 @@
@use './mixins' as *; @use './mixins' as *;
@use './colors' as *; @use './colors' as *;
// react-bootstrap v2 emits Bootstrap 5 form markup (`.form-label`) while we // Bootstrap 5 now provides `.form-label` natively, so this is no longer a shim
// still ship the Bootstrap 4 stylesheet (alexwine:bootstrap-4), which has no // we keep it only for the explicit `display: block` (v2's Form.Group is a plain
// `.form-label`. Shim it until the Bootstrap 4 -> 5 CSS jump lands (tracked as // div and some fields render the label inline otherwise). The bottom margin
// debt in UPGRADE.md). v2's Form.Group is a plain div, so restore the old // matches BS5's own `.form-label`.
// per-field bottom margin the removed `.form-group` used to give.
.form-label { .form-label {
display: block; display: block;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;

24
package-lock.json generated
View file

@ -13,7 +13,7 @@
"@turf/buffer": "^5.1.5", "@turf/buffer": "^5.1.5",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"bootstrap-carousel-swipe": "0.0.6", "bootstrap": "^5.3.8",
"commonmark": "^0.28.1", "commonmark": "^0.28.1",
"core-js": "^2.5.1", "core-js": "^2.5.1",
"crypto-random-hex": "^1.0.0", "crypto-random-hex": "^1.0.0",
@ -2699,10 +2699,24 @@
"node": ">=4.0.0" "node": ">=4.0.0"
} }
}, },
"node_modules/bootstrap-carousel-swipe": { "node_modules/bootstrap": {
"version": "0.0.6", "version": "5.3.8",
"resolved": "https://registry.npmjs.org/bootstrap-carousel-swipe/-/bootstrap-carousel-swipe-0.0.6.tgz", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
"integrity": "sha1-FTn+656kduHRDRwwnWWb0WJZ/wo=" "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/twbs"
},
{
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
}
],
"license": "MIT",
"peerDependencies": {
"@popperjs/core": "^2.11.8"
}
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.7", "version": "1.1.7",

View file

@ -14,6 +14,7 @@
"@turf/buffer": "^5.1.5", "@turf/buffer": "^5.1.5",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"bootstrap": "^5.3.8",
"commonmark": "^0.28.1", "commonmark": "^0.28.1",
"core-js": "^2.5.1", "core-js": "^2.5.1",
"crypto-random-hex": "^1.0.0", "crypto-random-hex": "^1.0.0",