Remove Bootstrap version 3 (#27420)

* Remove bootstrap-3 entrypoint

* Rename bootstrap-5 entrypoint to bootstrap

* Restore entrypoints

* Remove `bootstrap-5` and `bootstrap-3` entrypoints and a single `bootstrap.ts` file is now the default entrypoint

* Update `bootstrap-5` component imports to `bootstrap`

* Update `bootstrap-5` CSS imports to `bootstrap`

* Remove the `isBootstrap5` utility

* Remove `bootstrapVersion`

* Remove `ol-bootstrapVersion`

* Remove getCssThemeModifier

* Update path and rename

* Source format

* Remove Bootstrap v3 and Bootstrap v5 alias npm packages

* Remove bootstrap argument

* Remove unused files

* Update with the latest split tests

* Remove remaining bootstrap5PageStatus

* Update path

GitOrigin-RevId: 7acda2f80114d2de8699e1f06729a2a29218e284
This commit is contained in:
Rebeka Dekany
2025-08-08 08:06:00 +00:00
committed by Copybot
parent 70e0ca3eb5
commit c9a1ecd060
26 changed files with 68 additions and 216 deletions
+5 -27
View File
@@ -12026,16 +12026,6 @@
"@popperjs/core": "^2.9.2"
}
},
"node_modules/@types/bootstrap-5": {
"name": "@types/bootstrap",
"version": "5.2.10",
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.10.tgz",
"integrity": "sha512-F2X+cd6551tep0MvVZ6nM8v7XgGN/twpdNDjqS1TUM7YFNEtQYWk+dKAnH+T1gr6QgCoGMPl487xw/9hXooa2g==",
"dev": true,
"dependencies": {
"@popperjs/core": "^2.9.2"
}
},
"node_modules/@types/bunyan": {
"version": "1.8.8",
"resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.8.tgz",
@@ -16591,19 +16581,9 @@
"dev": true
},
"node_modules/bootstrap": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz",
"integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/bootstrap-5": {
"name": "bootstrap",
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
"version": "5.3.7",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz",
"integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==",
"dev": true,
"funding": [
{
@@ -43614,7 +43594,7 @@
"@overleaf/o-error": "*",
"aws-sdk": "^2.1174.0",
"body-parser": "^1.20.3",
"bootstrap": "^5.3.3",
"bootstrap": "5.3.3",
"compression": "^1.7.1",
"cookie-parser": "^1.4.6",
"cross-env": "^7.0.3",
@@ -45171,7 +45151,6 @@
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.4.3",
"@types/bootstrap": "^5.2.10",
"@types/bootstrap-5": "npm:@types/bootstrap@^5.2.10",
"@types/chai": "^4.3.0",
"@types/dateformat": "^5.0.2",
"@types/diff": "^5.0.9",
@@ -45206,8 +45185,7 @@
"babel-plugin-macros": "^3.1.0",
"babel-plugin-module-resolver": "^5.0.2",
"backbone": "^1.6.0",
"bootstrap": "^3.4.1",
"bootstrap-5": "npm:bootstrap@^5.3.6",
"bootstrap": "^5.3.7",
"c8": "^7.2.0",
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
@@ -186,32 +186,6 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) {
res.locals.moment = moment
res.locals.isIEEE = brandId => brandId === IEEE_BRAND_ID
res.locals.getCssThemeModifier = function (
userSettings,
brandVariation,
enableIeeeBranding
) {
// Themes only exist in OL v2
if (Settings.overleaf != null) {
// The IEEE theme is no longer applied in the editor, which sets
// enableIeeeBranding to false, but is used in the IEEE portal. If
// this is an IEEE-branded page and IEEE branding is disabled in this
// page, always use the default theme (i.e. no light theme in the
// IEEE-branded editor)
if (res.locals.isIEEE(brandVariation?.brand_id)) {
return enableIeeeBranding ? 'ieee-' : ''
} else if (userSettings && userSettings.overallTheme != null) {
if (!['', 'light-'].includes(userSettings.overallTheme)) {
return ''
}
return userSettings.overallTheme
}
}
return ''
}
res.locals.buildStylesheetPath = function (cssFileName) {
return staticFilesBase + webpackManifest[cssFileName]
}
@@ -1,3 +1,3 @@
mixin bootstrap-js(bootstrapVersion)
each file in entrypointScripts(bootstrapVersion === 5 ? 'bootstrap-5' : 'bootstrap-3')
mixin bootstrap-js
each file in entrypointScripts('bootstrap')
script(type='text/javascript' nonce=scriptNonce src=file)
+1 -10
View File
@@ -6,8 +6,6 @@ html(
class=fixedSizeDocument ? 'fixed-size-document' : undefined
)
- metadata = metadata || {}
- let bootstrap5PageStatus = 'enabled' // One of 'disabled' and 'enabled'
- let bootstrap5PageSplitTest = '' // Limits Bootstrap 5 usage on this page to users with an assignment of "enabled" for the specified split test. If left empty and bootstrap5PageStatus is "enabled", the page always uses Bootstrap 5.
- let isWebsiteRedesign = false
- let isApplicationPage = false
- let enableIeeeBranding = true
@@ -21,14 +19,8 @@ html(
head
include ./_metadata.pug
- const bootstrapVersion = bootstrap5PageStatus !== 'disabled' && (bootstrap5Override || bootstrap5PageSplitTest === '' || splitTestVariants[bootstrap5PageSplitTest] === 'enabled') ? 5 : 3
//- Stylesheet
link(
rel='stylesheet'
href=buildCssPath(getCssThemeModifier(userSettings, brandVariation, enableIeeeBranding), bootstrapVersion)
id='main-stylesheet'
)
link(rel='stylesheet' href=buildCssPath() id='main-stylesheet')
block css
each file in entrypointStyles(entrypoint)
link(rel='stylesheet' href=file)
@@ -103,7 +95,6 @@ html(
)
each restriction in userRestrictions || []
meta(name='ol-cannot-' + restriction data-type='boolean' content)
meta(name='ol-bootstrapVersion' data-type='json' content=bootstrapVersion)
block head-scripts
+4 -13
View File
@@ -8,29 +8,20 @@ block entrypointVar
block body
if typeof suppressNavbar === 'undefined'
if bootstrapVersion === 5
include layout/navbar-marketing-bootstrap-5
else
include layout/navbar-marketing
include layout/navbar-marketing-bootstrap-5
block content
if typeof suppressFooter === 'undefined'
if showThinFooter
if bootstrapVersion === 5
include layout/thin-footer-bootstrap-5
else
include layout/thin-footer
include layout/thin-footer-bootstrap-5
else
include layout/fat-footer
if typeof suppressPugCookieBanner == 'undefined'
include _cookie_banner
if bootstrapVersion === 5
!= moduleIncludes('contactModal-marketing-bootstrap-5', locals)
else
!= moduleIncludes('contactModal-marketing', locals)
!= moduleIncludes('contactModal-marketing-bootstrap-5', locals)
block prepend foot-scripts
+bootstrap-js(bootstrapVersion)
+bootstrap-js
@@ -11,19 +11,13 @@ block append meta
block body
if typeof suppressNavbar == 'undefined'
if bootstrapVersion === 5
include layout/navbar-marketing-bootstrap-5
else
include layout/navbar-website-redesign
include layout/navbar-marketing-bootstrap-5
block content
if typeof suppressFooter == 'undefined'
if showThinFooter
if bootstrapVersion === 5
include layout/thin-footer-bootstrap-5
else
include layout/thin-footer
include layout/thin-footer-bootstrap-5
else
include layout/fat-footer-website-redesign
@@ -31,10 +25,7 @@ block body
include _cookie_banner
block contactModal
if bootstrapVersion === 5
!= moduleIncludes('contactModal-marketing-bootstrap-5', locals)
else
!= moduleIncludes('contactModal-marketing', locals)
!= moduleIncludes('contactModal-marketing-bootstrap-5', locals)
block prepend foot-scripts
+bootstrap-js(bootstrapVersion)
+bootstrap-js
@@ -5,10 +5,7 @@
a(href='/legal') #{translate('privacy_and_terms')}
a(href='https://www.digital-science.com/security-certifications/') #{translate('compliance')}
ul.fat-footer-base-item.list-unstyled.fat-footer-base-language
if bootstrapVersion === 5
include language-picker-bootstrap-5
else
include language-picker
include language-picker-bootstrap-5
.fat-footer-base-section.fat-footer-base-social
.fat-footer-base-item
a.fat-footer-social.x-logo(href='https://x.com/overleaf')
@@ -15,6 +15,6 @@ html(lang='en')
link(rel='icon' href='/favicon.ico')
if buildCssPath
link(rel='stylesheet' href=buildCssPath('', 5))
link(rel='stylesheet' href=buildCssPath())
block body
@@ -4,12 +4,10 @@ block append meta
meta(name='ol-user' data-type='json' content=user)
block content
- var colClass = bootstrapVersion === 5 ? 'col-lg-8 m-auto' : 'col-md-8 col-md-offset-2'
main#main-content.content.content-alt.team-invite
.container
.row
div(class=colClass)
.col-lg-8.m-auto
.card.text-center
.card-body
.page-header
+1 -1
View File
@@ -5,7 +5,7 @@ import './shared/commands'
import './shared/exceptions'
import './ct/commands'
import './ct/codemirror'
import '../../test/frontend/helpers/bootstrap-5'
import '../../test/frontend/helpers/bootstrap'
beforeEach(function () {
cy.wrap(localesPromise).then(resetMeta)
-4
View File
@@ -1,4 +0,0 @@
import './features/bookmarkable-tab/index-bs5'
import './features/tooltip/index-bs5'
import './features/navbar/index'
import 'bootstrap-5'
@@ -1,5 +1,4 @@
import 'bootstrap'
import './features/contact-form'
import './features/bookmarkable-tab/index'
import './features/tooltip/index-bs3'
import './features/tooltip/index'
import './features/navbar/index'
import 'bootstrap'
@@ -1,21 +0,0 @@
function bookmarkableTab(tabEl) {
tabEl.addEventListener('click', () => {
window.location.hash = tabEl.getAttribute('href')
})
}
function handleHashChange() {
const hash = window.location.hash
if (!hash) return
// Find the bookmarkable tab that links to the hash
const $tabEl = $(`[data-ol-bookmarkable-tab][href="${hash}"]`)
if (!$tabEl) return
// Select the tab via Bootstrap
$tabEl.tab('show')
}
document.querySelectorAll('[data-ol-bookmarkable-tab]').forEach(bookmarkableTab)
window.addEventListener('hashchange', handleHashChange)
handleHashChange()
@@ -1,4 +1,4 @@
import { Tab } from 'bootstrap-5'
import { Tab } from 'bootstrap'
function bookmarkableTab(tabEl: HTMLElement) {
tabEl.addEventListener('click', () => {
@@ -17,7 +17,6 @@ function handleHashChange() {
if (!tabEl) return
// Select the tab via Bootstrap 5
const tab = new Tab(tabEl)
tab.show()
}
@@ -3,7 +3,6 @@ import { FetchError, postJSON } from '../../infrastructure/fetch-json'
import { canSkipCaptcha, validateCaptchaV2 } from './captcha'
import inputValidator from './input-validator'
import { disableElement, enableElement } from '../utils/disableElement'
import { isBootstrap5 } from '@/features/utils/bootstrap-5'
import { materialIcon as createMaterialIcon } from '@/features/utils/material-icon'
// Form helper(s) to handle:
@@ -136,7 +135,7 @@ function hideFormElements(formEl) {
}
/**
* Creates a notification element from a message object, with BS5 classes.
* Creates a notification element from a message object.
*
* @param {Object} message
* @param {'error' | 'success' | 'warning' | 'info'} message.type
@@ -145,7 +144,7 @@ function hideFormElements(formEl) {
* @param {string[]} message.hints
* @returns {HTMLDivElement}
*/
function createNotificationFromMessageBS5(message) {
function createNotificationFromMessage(message) {
const messageEl = document.createElement('div')
messageEl.className = classNames('mb-3 notification', {
'notification-type-error': message.type === 'error',
@@ -216,32 +215,9 @@ function showMessages(formEl, messageBag) {
customErrorElements.forEach(el => {
el.hidden = false
})
} else if (isBootstrap5()) {
const notification = createNotificationFromMessageBS5(message)
messagesEl.append(notification)
} else {
// No custom error element for key on page, append a new error message
const messageEl = document.createElement('div')
messageEl.className = classNames('alert mb-2', {
'alert-danger': message.type === 'error',
'alert-success': message.type !== 'error',
})
messageEl.textContent = message.text || `Error: ${message.key}`
messageEl.setAttribute('aria-live', 'assertive')
messageEl.setAttribute(
'role',
message.type === 'error' ? 'alert' : 'status'
)
if (message.hints && message.hints.length) {
const listEl = document.createElement('ul')
message.hints.forEach(hint => {
const listItemEl = document.createElement('li')
listItemEl.textContent = hint
listEl.append(listItemEl)
})
messageEl.append(listEl)
}
messagesEl.append(messageEl)
const notification = createNotificationFromMessage(message)
messagesEl.append(notification)
}
if (message.key) {
// Hide the form elements on specific message types
@@ -1,4 +1,3 @@
import { isBootstrap5 } from '@/features/utils/bootstrap-5'
import { materialIcon } from '@/features/utils/material-icon'
export default function inputValidator(inputEl) {
@@ -13,11 +12,8 @@ export default function inputValidator(inputEl) {
const messageTextNode = document.createTextNode('')
// In Bootstrap 5, add an icon
if (isBootstrap5()) {
const iconEl = materialIcon('error')
messageInnerEl.append(iconEl)
}
const iconEl = materialIcon('error')
messageInnerEl.append(iconEl)
messageInnerEl.append(messageTextNode)
inputEl.insertAdjacentElement('afterend', messageEl)
@@ -1,2 +0,0 @@
$('[data-ol-lang-selector-tooltip]').tooltip({ trigger: 'hover' })
$('[data-toggle="tooltip"]').tooltip()
@@ -1,4 +1,4 @@
import { Tooltip } from 'bootstrap-5'
import { Tooltip } from 'bootstrap'
function getElementWidth(el: Element) {
const elComputedStyle = window.getComputedStyle(el)
@@ -1,5 +0,0 @@
import getMeta from '@/utils/meta'
// The reason this is a function is to ensure that the meta tag is read before
// any isBootstrap5 check is performed
export const isBootstrap5 = () => getMeta('ol-bootstrapVersion') === 5
@@ -1,7 +1,5 @@
import { isBootstrap5 } from './bootstrap-5'
export function disableElement(el) {
if (isBootstrap5() && el.tagName.toLowerCase() === 'a') {
if (el.tagName.toLowerCase() === 'a') {
el.classList.add('disabled')
} else {
el.disabled = true
@@ -10,7 +8,7 @@ export function disableElement(el) {
}
export function enableElement(el) {
if (isBootstrap5() && el.tagName.toLowerCase() === 'a') {
if (el.tagName.toLowerCase() === 'a') {
el.classList.remove('disabled')
} else {
el.disabled = false
-1
View File
@@ -75,7 +75,6 @@ export interface Meta {
'ol-allowedImageNames': AllowedImageName[]
'ol-anonymous': boolean
'ol-baseAssetPath': string
'ol-bootstrapVersion': 3 | 5
'ol-brandVariation': Record<string, any>
// dynamic keys based on permissions
+33 -33
View File
@@ -1,9 +1,9 @@
// Only include the parts of Bootstrap we need to minimize bundle size
// Include functions first (enabling manipulation of colors, SVGs, calc, etc. in the variable overrides)
@import 'bootstrap-5/scss/functions';
@import 'bootstrap/scss/functions';
// Overrides for Bootstrap 5 Sass variables. These have to be included before
// Overrides for Bootstrap Sass variables. These have to be included before
// Bootstrap itself because Bootstrap uses them to create the CSS variables it
// uses, and in calculations to determine, for example, what color text to use
// on a button based on contrast.
@@ -11,44 +11,44 @@
@import '../abstracts/all';
// Include remainder of required Bootstrap stylesheets (including any separate color mode stylesheets)
@import 'bootstrap-5/scss/variables';
@import 'bootstrap-5/scss/variables-dark';
@import 'bootstrap/scss/variables';
@import 'bootstrap/scss/variables-dark';
// Include remainder of required parts
@import 'bootstrap-5/scss/maps';
@import 'bootstrap-5/scss/mixins';
@import 'bootstrap-5/scss/utilities';
@import 'bootstrap/scss/maps';
@import 'bootstrap/scss/mixins';
@import 'bootstrap/scss/utilities';
// Layout & components
@import 'bootstrap-5/scss/root';
@import 'bootstrap-5/scss/reboot';
@import 'bootstrap-5/scss/transitions';
@import 'bootstrap-5/scss/type';
@import 'bootstrap-5/scss/images';
@import 'bootstrap-5/scss/containers';
@import 'bootstrap-5/scss/grid';
@import 'bootstrap-5/scss/tables';
@import 'bootstrap-5/scss/forms';
@import 'bootstrap-5/scss/buttons';
@import 'bootstrap-5/scss/dropdown';
@import 'bootstrap-5/scss/button-group';
@import 'bootstrap-5/scss/badge';
@import 'bootstrap-5/scss/list-group';
@import 'bootstrap-5/scss/modal';
@import 'bootstrap-5/scss/tooltip';
@import 'bootstrap-5/scss/popover';
@import 'bootstrap-5/scss/spinners';
@import 'bootstrap-5/scss/card';
@import 'bootstrap-5/scss/close';
@import 'bootstrap-5/scss/nav';
@import 'bootstrap-5/scss/navbar';
@import 'bootstrap-5/scss/offcanvas';
@import 'bootstrap/scss/root';
@import 'bootstrap/scss/reboot';
@import 'bootstrap/scss/transitions';
@import 'bootstrap/scss/type';
@import 'bootstrap/scss/images';
@import 'bootstrap/scss/containers';
@import 'bootstrap/scss/grid';
@import 'bootstrap/scss/tables';
@import 'bootstrap/scss/forms';
@import 'bootstrap/scss/buttons';
@import 'bootstrap/scss/dropdown';
@import 'bootstrap/scss/button-group';
@import 'bootstrap/scss/badge';
@import 'bootstrap/scss/list-group';
@import 'bootstrap/scss/modal';
@import 'bootstrap/scss/tooltip';
@import 'bootstrap/scss/popover';
@import 'bootstrap/scss/spinners';
@import 'bootstrap/scss/card';
@import 'bootstrap/scss/close';
@import 'bootstrap/scss/nav';
@import 'bootstrap/scss/navbar';
@import 'bootstrap/scss/offcanvas';
// Helpers
@import 'bootstrap-5/scss/helpers';
@import 'bootstrap/scss/helpers';
// Utilities
@import 'bootstrap-5/scss/utilities/api';
@import 'bootstrap/scss/utilities/api';
// Mixins
@import 'bootstrap-5/scss/mixins/breakpoints';
@import 'bootstrap/scss/mixins/breakpoints';
@@ -1,4 +1,4 @@
@import 'bootstrap-5/scss/functions';
@import 'bootstrap/scss/functions';
:root {
// Basics
+1 -3
View File
@@ -232,7 +232,6 @@
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.4.3",
"@types/bootstrap": "^5.2.10",
"@types/bootstrap-5": "npm:@types/bootstrap@^5.2.10",
"@types/chai": "^4.3.0",
"@types/dateformat": "^5.0.2",
"@types/diff": "^5.0.9",
@@ -267,8 +266,7 @@
"babel-plugin-macros": "^3.1.0",
"babel-plugin-module-resolver": "^5.0.2",
"backbone": "^1.6.0",
"bootstrap": "^3.4.1",
"bootstrap-5": "npm:bootstrap@^5.3.6",
"bootstrap": "^5.3.7",
"c8": "^7.2.0",
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
+1 -2
View File
@@ -16,8 +16,7 @@ invalidateBabelCacheIfNeeded()
// Generate a hash of entry points, including modules
const entryPoints = {
'bootstrap-3': './frontend/js/bootstrap-3.ts',
'bootstrap-5': './frontend/js/bootstrap-5.ts',
bootstrap: './frontend/js/bootstrap.ts',
devToolbar: './frontend/js/dev-toolbar.ts',
'ide-detached': './frontend/js/ide-detached.ts',
marketing: './frontend/js/marketing.ts',