diff --git a/.github/workflows/browserstack.yml b/.github/workflows/browserstack.yml index 917ccc188..ea0a25704 100644 --- a/.github/workflows/browserstack.yml +++ b/.github/workflows/browserstack.yml @@ -4,7 +4,6 @@ on: push: env: - CI: true FORCE_COLOR: 2 NODE: 14.x @@ -29,8 +28,8 @@ jobs: path: ~/.npm key: ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} restore-keys: | - ${{ runner.OS }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} - ${{ runner.OS }}-node-v${{ env.NODE }}- + ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} + ${{ runner.os }}-node-v${{ env.NODE }}- - name: Install npm dependencies run: npm ci diff --git a/.github/workflows/bundlewatch.yml b/.github/workflows/bundlewatch.yml index 03b3a2b52..989991f0d 100644 --- a/.github/workflows/bundlewatch.yml +++ b/.github/workflows/bundlewatch.yml @@ -7,7 +7,6 @@ on: pull_request: env: - CI: true FORCE_COLOR: 2 NODE: 14.x @@ -30,8 +29,8 @@ jobs: path: ~/.npm key: ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} restore-keys: | - ${{ runner.OS }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} - ${{ runner.OS }}-node-v${{ env.NODE }}- + ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} + ${{ runner.os }}-node-v${{ env.NODE }}- - name: Install npm dependencies run: npm ci diff --git a/.github/workflows/css.yml b/.github/workflows/css.yml index 7fb947005..4463d52f4 100644 --- a/.github/workflows/css.yml +++ b/.github/workflows/css.yml @@ -7,7 +7,6 @@ on: pull_request: env: - CI: true FORCE_COLOR: 2 NODE: 14.x @@ -30,8 +29,8 @@ jobs: path: ~/.npm key: ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} restore-keys: | - ${{ runner.OS }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} - ${{ runner.OS }}-node-v${{ env.NODE }}- + ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} + ${{ runner.os }}-node-v${{ env.NODE }}- - name: Install npm dependencies run: npm ci diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d2529cbfe..cda6f7748 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,7 +7,6 @@ on: pull_request: env: - CI: true FORCE_COLOR: 2 NODE: 14.x @@ -32,8 +31,8 @@ jobs: path: ~/.npm key: ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} restore-keys: | - ${{ runner.OS }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} - ${{ runner.OS }}-node-v${{ env.NODE }}- + ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} + ${{ runner.os }}-node-v${{ env.NODE }}- - name: Install npm dependencies run: npm ci diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 09c110fc8..c56a2dfdd 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -7,7 +7,6 @@ on: pull_request: env: - CI: true FORCE_COLOR: 2 jobs: @@ -35,8 +34,8 @@ jobs: path: ~/.npm key: ${{ runner.os }}-node-v${{ matrix.node }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }}} restore-keys: | - ${{ runner.OS }}-node-v${{ matrix.node }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} - ${{ runner.OS }}-node-v${{ matrix.node }}- + ${{ runner.os }}-node-v${{ matrix.node }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} + ${{ runner.os }}-node-v${{ matrix.node }}- - name: Install npm dependencies run: npm ci diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 30c434984..e7a55083d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,7 +7,6 @@ on: pull_request: env: - CI: true FORCE_COLOR: 2 NODE: 14.x @@ -30,8 +29,8 @@ jobs: path: ~/.npm key: ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} restore-keys: | - ${{ runner.OS }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} - ${{ runner.OS }}-node-v${{ env.NODE }}- + ${{ runner.os }}-node-v${{ env.NODE }}-${{ hashFiles('package.json') }}-${{ hashFiles('package-lock.json') }} + ${{ runner.os }}-node-v${{ env.NODE }}- - name: Install npm dependencies run: npm ci diff --git a/.github/workflows/node-sass.yml b/.github/workflows/node-sass.yml index 9e9cffa40..285d4bcc8 100644 --- a/.github/workflows/node-sass.yml +++ b/.github/workflows/node-sass.yml @@ -7,7 +7,6 @@ on: pull_request: env: - CI: true FORCE_COLOR: 2 NODE: 14.x diff --git a/.gitignore b/.gitignore index 6208c57d7..0a0f88d15 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,8 @@ # Ignore docs files /_gh_pages/ -# This is the old Jekyll docs dist folder; -# keeping it here so that when we switch branches it doesn't show up -/site/docs/**/dist/ -# Jekyll's cache folder; keeping it for the same reason as above -/site/.jekyll-cache/ # Hugo resources folder /resources/ -# Ignore ruby/bundler files; -# keeping them here so that when we switch branches they don't show up -/.bundle/ -/vendor/ -/.ruby-version - # Numerous always-ignore extensions *.diff *.err diff --git a/.stylelintrc b/.stylelintrc index 1c9ee1811..c068d30b5 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -3,6 +3,10 @@ "stylelint-config-twbs-bootstrap/scss" ], "rules": { + "declaration-property-value-disallowed-list": { + "border": "none", + "outline": "none" + }, "function-disallowed-list": [ "calc", "lighten", diff --git a/build/zip-examples.js b/build/zip-examples.js index c98ed9763..e5eb56f22 100644 --- a/build/zip-examples.js +++ b/build/zip-examples.js @@ -12,36 +12,67 @@ const path = require('path') const sh = require('shelljs') -const { version, version_short: versionShort } = require('../package.json') +const pkg = require('../package.json') -const folderName = `bootstrap-${version}-examples` +const versionShort = pkg.config.version_short +const distFolder = `bootstrap-${pkg.version}-examples` +const rootDocsDir = '_gh_pages' +const docsDir = `${rootDocsDir}/docs/${versionShort}/` + +// these are the files we need in the examples +const cssFiles = [ + 'bootstrap.min.css', + 'bootstrap.min.css.map', + 'bootstrap.rtl.min.css', + 'bootstrap.rtl.min.css.map' +] +const jsFiles = [ + 'bootstrap.bundle.min.js', + 'bootstrap.bundle.min.js.map' +] +const imgFiles = [ + 'bootstrap-logo.svg', + 'bootstrap-logo-white.svg' +] sh.config.fatal = true -if (!sh.test('-d', '_gh_pages')) { - throw new Error('The "_gh_pages" folder does not exist, did you forget building the docs?') +if (!sh.test('-d', rootDocsDir)) { + throw new Error(`The "${rootDocsDir}" folder does not exist, did you forget building the docs?`) } // switch to the root dir sh.cd(path.join(__dirname, '..')) // remove any previously created folder with the same name -sh.rm('-rf', folderName) -// create any folders so that `cp` works -sh.mkdir('-p', folderName) -sh.mkdir('-p', `${folderName}/assets/brand/`) +sh.rm('-rf', distFolder) -sh.cp('-Rf', `_gh_pages/docs/${versionShort}/examples/*`, folderName) -sh.cp('-Rf', `_gh_pages/docs/${versionShort}/dist/`, `${folderName}/assets/`) -// also copy the two brand images we use in the examples -sh.cp('-f', [ - `_gh_pages/docs/${versionShort}/assets/brand/bootstrap-logo.svg`, - `_gh_pages/docs/${versionShort}/assets/brand/bootstrap-logo-white.svg` -], `${folderName}/assets/brand/`) -sh.rm(`${folderName}/index.html`) +// create any folders so that `cp` works +sh.mkdir('-p', [ + distFolder, + `${distFolder}/assets/brand/`, + `${distFolder}/assets/dist/css/`, + `${distFolder}/assets/dist/js/` +]) + +sh.cp('-Rf', `${docsDir}/examples/*`, distFolder) + +cssFiles.forEach(file => { + sh.cp('-f', `${docsDir}/dist/css/${file}`, `${distFolder}/assets/dist/css/`) +}) + +jsFiles.forEach(file => { + sh.cp('-f', `${docsDir}/dist/js/${file}`, `${distFolder}/assets/dist/js/`) +}) + +imgFiles.forEach(file => { + sh.cp('-f', `${docsDir}/assets/brand/${file}`, `${distFolder}/assets/brand/`) +}) + +sh.rm(`${distFolder}/index.html`) // get all examples' HTML files -sh.find(`${folderName}/**/*.html`).forEach(file => { +sh.find(`${distFolder}/**/*.html`).forEach(file => { const fileContents = sh.cat(file) .toString() .replace(new RegExp(`"/docs/${versionShort}/`, 'g'), '"../') @@ -53,7 +84,7 @@ sh.find(`${folderName}/**/*.html`).forEach(file => { }) // create the zip file -sh.exec(`zip -r9 "${folderName}.zip" "${folderName}"`, { fatal: true }) +sh.exec(`zip -r9 "${distFolder}.zip" "${distFolder}"`) // remove the folder we created -sh.rm('-rf', folderName) +sh.rm('-rf', distFolder) diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 96024f7c4..73c09c2f4 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -263,7 +263,7 @@ class Dropdown extends BaseComponent { typeof config.reference.getBoundingClientRect !== 'function' ) { // Popper virtual elements require a getBoundingClientRect method - throw new Error(`${NAME}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`) + throw new TypeError(`${NAME.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`) } return config diff --git a/js/src/modal.js b/js/src/modal.js index b1656803d..25013ca3b 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -589,7 +589,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( data = new Modal(target, config) } - data.show(this) + data.toggle(this) }) /** diff --git a/js/src/util/index.js b/js/src/util/index.js index dce4f893f..2f0398003 100644 --- a/js/src/util/index.js +++ b/js/src/util/index.js @@ -111,15 +111,14 @@ const typeCheckConfig = (componentName, config, configTypes) => { Object.keys(configTypes).forEach(property => { const expectedTypes = configTypes[property] const value = config[property] - const valueType = value && isElement(value) ? - 'element' : - toType(value) + const valueType = value && isElement(value) ? 'element' : toType(value) if (!new RegExp(expectedTypes).test(valueType)) { - throw new Error( + throw new TypeError( `${componentName.toUpperCase()}: ` + `Option "${property}" provided type "${valueType}" ` + - `but expected type "${expectedTypes}".`) + `but expected type "${expectedTypes}".` + ) } }) } diff --git a/js/tests/karma.conf.js b/js/tests/karma.conf.js index 38c7003bb..cd4946525 100644 --- a/js/tests/karma.conf.js +++ b/js/tests/karma.conf.js @@ -1,5 +1,7 @@ /* eslint-env node */ +'use strict' + const path = require('path') const ip = require('ip') const { babel } = require('@rollup/plugin-babel') @@ -12,10 +14,11 @@ const { browsersKeys } = require('./browsers') -const { env } = process -const browserStack = env.BROWSER === 'true' -const debug = env.DEBUG === 'true' -const jQueryTest = env.JQUERY === 'true' +const ENV = process.env +const BROWSERSTACK = Boolean(ENV.BROWSERSTACK) +const DEBUG = Boolean(ENV.DEBUG) +const JQUERY_TEST = Boolean(ENV.JQUERY) + const frameworks = [ 'jasmine' ] @@ -30,16 +33,16 @@ const reporters = ['dots'] const detectBrowsers = { usePhantomJS: false, postDetection(availableBrowser) { - if (env.CI === true || availableBrowser.includes('Chrome')) { - return debug ? ['Chrome'] : ['ChromeHeadless'] + if (ENV.CI === true || availableBrowser.includes('Chrome')) { + return DEBUG ? ['Chrome'] : ['ChromeHeadless'] } if (availableBrowser.includes('Chromium')) { - return debug ? ['Chromium'] : ['ChromiumHeadless'] + return DEBUG ? ['Chromium'] : ['ChromiumHeadless'] } if (availableBrowser.includes('Firefox')) { - return debug ? ['Firefox'] : ['FirefoxHeadless'] + return DEBUG ? ['Firefox'] : ['FirefoxHeadless'] } throw new Error('Please install Chrome, Chromium or Firefox') @@ -67,7 +70,7 @@ const conf = { 'node_modules/hammer-simulator/index.js', { pattern: 'js/tests/unit/**/!(jquery).spec.js', - watched: !browserStack + watched: !BROWSERSTACK } ], preprocessors: { @@ -101,11 +104,11 @@ const conf = { } } -if (browserStack) { +if (BROWSERSTACK) { conf.hostname = ip.address() conf.browserStack = { - username: env.BROWSER_STACK_USERNAME, - accessKey: env.BROWSER_STACK_ACCESS_KEY, + username: ENV.BROWSER_STACK_USERNAME, + accessKey: ENV.BROWSER_STACK_ACCESS_KEY, build: `bootstrap-${new Date().toISOString()}`, project: 'Bootstrap', retryLimit: 2 @@ -114,7 +117,7 @@ if (browserStack) { conf.customLaunchers = browsers conf.browsers = browsersKeys reporters.push('BrowserStack', 'kjhtml') -} else if (jQueryTest) { +} else if (JQUERY_TEST) { frameworks.push('detectBrowsers') plugins.push( 'karma-chrome-launcher', @@ -155,7 +158,7 @@ if (browserStack) { } } - if (debug) { + if (DEBUG) { conf.hostname = ip.address() plugins.push('karma-jasmine-html-reporter') reporters.push('kjhtml') @@ -169,7 +172,6 @@ conf.plugins = plugins conf.reporters = reporters module.exports = karmaConfig => { - // possible values: karmaConfig.LOG_DISABLE || karmaConfig.LOG_ERROR || karmaConfig.LOG_WARN || karmaConfig.LOG_INFO || karmaConfig.LOG_DEBUG - conf.logLevel = karmaConfig.LOG_ERROR || karmaConfig.LOG_WARN + conf.logLevel = karmaConfig.LOG_ERROR karmaConfig.set(conf) } diff --git a/js/tests/unit/carousel.spec.js b/js/tests/unit/carousel.spec.js index 0286762fa..787a276de 100644 --- a/js/tests/unit/carousel.spec.js +++ b/js/tests/unit/carousel.spec.js @@ -1136,11 +1136,9 @@ describe('Carousel', () => { jQueryMock.fn.carousel = Carousel.jQueryInterface jQueryMock.elements = [div] - try { + expect(() => { jQueryMock.fn.carousel.call(jQueryMock, action) - } catch (error) { - expect(error.message).toEqual(`No method named "${action}"`) - } + }).toThrowError(TypeError, `No method named "${action}"`) }) }) diff --git a/js/tests/unit/collapse.spec.js b/js/tests/unit/collapse.spec.js index 4af21e13f..ff493cf51 100644 --- a/js/tests/unit/collapse.spec.js +++ b/js/tests/unit/collapse.spec.js @@ -819,11 +819,9 @@ describe('Collapse', () => { jQueryMock.fn.collapse = Collapse.jQueryInterface jQueryMock.elements = [div] - try { + expect(() => { jQueryMock.fn.collapse.call(jQueryMock, action) - } catch (error) { - expect(error.message).toEqual(`No method named "${action}"`) - } + }).toThrowError(TypeError, `No method named "${action}"`) }) }) diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js index ba1d0f443..cc4139603 100644 --- a/js/tests/unit/dropdown.spec.js +++ b/js/tests/unit/dropdown.spec.js @@ -393,13 +393,13 @@ describe('Dropdown', () => { expect(() => new Dropdown(btnDropdown, { reference: {} - })).toThrow() + })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.') expect(() => new Dropdown(btnDropdown, { reference: { getBoundingClientRect: 'not-a-function' } - })).toThrow() + })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.') // use onFirstUpdate as Poppers internal update is executed async const dropdown = new Dropdown(btnDropdown, { @@ -1557,11 +1557,9 @@ describe('Dropdown', () => { jQueryMock.fn.dropdown = Dropdown.jQueryInterface jQueryMock.elements = [div] - try { + expect(() => { jQueryMock.fn.dropdown.call(jQueryMock, action) - } catch (error) { - expect(error.message).toEqual(`No method named "${action}"`) - } + }).toThrowError(TypeError, `No method named "${action}"`) }) }) diff --git a/js/tests/unit/modal.spec.js b/js/tests/unit/modal.spec.js index f645e9892..29c90bbf1 100644 --- a/js/tests/unit/modal.spec.js +++ b/js/tests/unit/modal.spec.js @@ -870,7 +870,7 @@ describe('Modal', () => { }) describe('data-api', () => { - it('should open modal', done => { + it('should toggle modal', done => { fixtureEl.innerHTML = [ '', '' @@ -885,6 +885,15 @@ describe('Modal', () => { expect(modalEl.getAttribute('aria-hidden')).toEqual(null) expect(modalEl.style.display).toEqual('block') expect(document.querySelector('.modal-backdrop')).toBeDefined() + setTimeout(() => trigger.click(), 10) + }) + + modalEl.addEventListener('hidden.bs.modal', () => { + expect(modalEl.getAttribute('aria-modal')).toEqual(null) + expect(modalEl.getAttribute('role')).toEqual(null) + expect(modalEl.getAttribute('aria-hidden')).toEqual('true') + expect(modalEl.style.display).toEqual('none') + expect(document.querySelector('.modal-backdrop')).toEqual(null) done() }) @@ -1061,11 +1070,9 @@ describe('Modal', () => { jQueryMock.fn.modal = Modal.jQueryInterface jQueryMock.elements = [div] - try { + expect(() => { jQueryMock.fn.modal.call(jQueryMock, action) - } catch (error) { - expect(error.message).toEqual(`No method named "${action}"`) - } + }).toThrowError(TypeError, `No method named "${action}"`) }) it('should call show method', () => { diff --git a/js/tests/unit/popover.spec.js b/js/tests/unit/popover.spec.js index 3c04e7ac1..e5c235e2a 100644 --- a/js/tests/unit/popover.spec.js +++ b/js/tests/unit/popover.spec.js @@ -206,11 +206,9 @@ describe('Popover', () => { jQueryMock.fn.popover = Popover.jQueryInterface jQueryMock.elements = [popoverEl] - try { + expect(() => { jQueryMock.fn.popover.call(jQueryMock, action) - } catch (error) { - expect(error.message).toEqual(`No method named "${action}"`) - } + }).toThrowError(TypeError, `No method named "${action}"`) }) it('should should call show method', () => { diff --git a/js/tests/unit/scrollspy.spec.js b/js/tests/unit/scrollspy.spec.js index eab9183aa..0d175aafa 100644 --- a/js/tests/unit/scrollspy.spec.js +++ b/js/tests/unit/scrollspy.spec.js @@ -625,11 +625,9 @@ describe('ScrollSpy', () => { jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface jQueryMock.elements = [div] - try { + expect(() => { jQueryMock.fn.scrollspy.call(jQueryMock, action) - } catch (error) { - expect(error.message).toEqual(`No method named "${action}"`) - } + }).toThrowError(TypeError, `No method named "${action}"`) }) }) diff --git a/js/tests/unit/tab.spec.js b/js/tests/unit/tab.spec.js index 67a85b2e4..d098ab027 100644 --- a/js/tests/unit/tab.spec.js +++ b/js/tests/unit/tab.spec.js @@ -397,11 +397,9 @@ describe('Tab', () => { jQueryMock.fn.tab = Tab.jQueryInterface jQueryMock.elements = [div] - try { + expect(() => { jQueryMock.fn.tab.call(jQueryMock, action) - } catch (error) { - expect(error.message).toEqual(`No method named "${action}"`) - } + }).toThrowError(TypeError, `No method named "${action}"`) }) }) diff --git a/js/tests/unit/toast.spec.js b/js/tests/unit/toast.spec.js index cc873d1fe..2920eb2ef 100644 --- a/js/tests/unit/toast.spec.js +++ b/js/tests/unit/toast.spec.js @@ -368,11 +368,9 @@ describe('Toast', () => { jQueryMock.fn.toast = Toast.jQueryInterface jQueryMock.elements = [div] - try { + expect(() => { jQueryMock.fn.toast.call(jQueryMock, action) - } catch (error) { - expect(error.message).toEqual(`No method named "${action}"`) - } + }).toThrowError(TypeError, `No method named "${action}"`) }) }) diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js index 8224c3437..213dcc91f 100644 --- a/js/tests/unit/tooltip.spec.js +++ b/js/tests/unit/tooltip.spec.js @@ -1207,11 +1207,9 @@ describe('Tooltip', () => { jQueryMock.fn.tooltip = Tooltip.jQueryInterface jQueryMock.elements = [div] - try { + expect(() => { jQueryMock.fn.tooltip.call(jQueryMock, action) - } catch (error) { - expect(error.message).toEqual(`No method named "${action}"`) - } + }).toThrowError(TypeError, `No method named "${action}"`) }) }) }) diff --git a/js/tests/unit/util/index.spec.js b/js/tests/unit/util/index.spec.js index d4b09bf77..00ab44f1e 100644 --- a/js/tests/unit/util/index.spec.js +++ b/js/tests/unit/util/index.spec.js @@ -212,7 +212,7 @@ describe('Util', () => { expect(() => { Util.typeCheckConfig(namePlugin, config, defaultType) - }).toThrow(new Error('COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".')) + }).toThrowError(TypeError, 'COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".') }) it('should return null stringified when null is passed', () => { diff --git a/package-lock.json b/package-lock.json index e3dd0cee0..a8d8dd4a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1612,9 +1612,9 @@ "optional": true }, "autoprefixer": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.0.tgz", - "integrity": "sha512-5SdPoK2Q3rxVh4xC8GKdy772KbEESdl2SyJADCw5FMUrd5eNtpOTLo7oCFMEKvK86zQdvsIVTYk93E8dAfRy6g==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.1.tgz", + "integrity": "sha512-dwP0UjyYvROUvtU+boBx8ff5pPWami1NGTrJs9YUsS/oZVbRAcdNHOOuXSA1fc46tgKqe072cVaKD69rvCc3QQ==", "dev": true, "requires": { "browserslist": "^4.16.1", @@ -1623,21 +1623,6 @@ "fraction.js": "^4.0.13", "normalize-range": "^0.1.2", "postcss-value-parser": "^4.1.0" - }, - "dependencies": { - "browserslist": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.1.tgz", - "integrity": "sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001173", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.634", - "escalade": "^3.1.1", - "node-releases": "^1.1.69" - } - } } }, "axios": { @@ -2068,16 +2053,16 @@ } }, "browserslist": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.0.tgz", - "integrity": "sha512-/j6k8R0p3nxOC6kx5JGAxsnhc9ixaWJfYc+TNTzxg6+ARaESAvQGV7h0uNOB4t+pLQJZWzcrMxXOxjgsCj3dqQ==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.1.tgz", + "integrity": "sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001165", + "caniuse-lite": "^1.0.30001173", "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.621", + "electron-to-chromium": "^1.3.634", "escalade": "^3.1.1", - "node-releases": "^1.1.67" + "node-releases": "^1.1.69" } }, "browserstack": { @@ -2284,13 +2269,13 @@ } }, "call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.1.tgz", + "integrity": "sha512-tvAvUwNcRikl3RVF20X9lsYmmepsovzTWeJiXjO0PkJp15uy/6xKFZOQtuiSULwYW+6ToZBprphCgWXC2dSgcQ==", "dev": true, "requires": { "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.0" + "get-intrinsic": "^1.0.2" } }, "callsites": { @@ -2401,14 +2386,14 @@ } }, "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.0.tgz", + "integrity": "sha512-JgQM9JS92ZbFR4P90EvmzNpSGhpPBGBSj10PILeDyYFwp4h2/D9OM03wsJ4zW1fEp4ka2DGrnUeD7FuvQ2aZ2Q==", "dev": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", + "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -3336,9 +3321,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.634", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.634.tgz", - "integrity": "sha512-QPrWNYeE/A0xRvl/QP3E0nkaEvYUvH3gM04ZWYtIa6QlSpEetRlRI1xvQ7hiMIySHHEV+mwDSX8Kj4YZY6ZQAw==", + "version": "1.3.635", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.635.tgz", + "integrity": "sha512-RRriZOLs9CpW6KTLmgBqyUdnY0QNqqWs0HOtuQGGEMizOTNNn1P7sGRBxARnUeLejOsgwjDyRqT3E/CSst02ZQ==", "dev": true }, "emoji-regex": { @@ -4640,9 +4625,9 @@ "dev": true }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", + "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", "dev": true, "optional": true }, @@ -4879,9 +4864,9 @@ "dev": true }, "globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", + "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -6594,9 +6579,9 @@ "dev": true }, "meow": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.0.tgz", - "integrity": "sha512-fNWkgM1UVMey2kf24yLiccxLihc5W+6zVus3/N0b+VfnJgxV99E9u04X6NAiKdg6ED7DAQBX5sy36NM0QJZkWA==", + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, "requires": { "@types/minimist": "^1.2.0", @@ -7849,9 +7834,9 @@ "optional": true }, "postcss": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.2.tgz", - "integrity": "sha512-HM1NDNWLgglJPQQMNwvLxgH2KcrKZklKLi/xXYIOaqQB57p/pDWEJNS83PVICYsn1Dg/9C26TiejNr422/ePaQ==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.4.tgz", + "integrity": "sha512-kRFftRoExRVXZlwUuay9iC824qmXPcQQVzAjbCCgjpXnkdMCJYBu2gTwAaFBzv8ewND6O8xFb3aELmEkh9zTzg==", "dev": true, "requires": { "colorette": "^1.2.1", @@ -8854,6 +8839,15 @@ "dev": true, "requires": { "fsevents": "~2.1.2" + }, + "dependencies": { + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + } } }, "rollup-plugin-istanbul": { @@ -9020,9 +9014,9 @@ "dev": true }, "sass": { - "version": "1.32.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.2.tgz", - "integrity": "sha512-u1pUuzqwz3SAgvHSWp1k0mRhX82b2DdlVnP6UIetQPZtYbuJUDaPQhZE12jyjB7vYeOScfz9WPsZJB6Rpk7heA==", + "version": "1.32.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.4.tgz", + "integrity": "sha512-N0BT0PI/t3+gD8jKa83zJJUb7ssfQnRRfqN+GIErokW6U4guBpfYl8qYB+OFLEho+QvnV5ZH1R9qhUC/Z2Ch9w==", "dev": true, "requires": { "chokidar": ">=2.0.0 <4.0.0" @@ -9478,9 +9472,9 @@ } }, "socket.io": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.0.tgz", - "integrity": "sha512-9UPJ1UTvKayuQfVv2IQ3k7tCQC/fboDyIK62i99dAQIyHKaBsNdTpwHLgKJ6guRWxRtC9H+138UwpaGuQO9uWQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz", + "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==", "dev": true, "requires": { "debug": "~4.1.0", diff --git a/package.json b/package.json index 6a6fc40a0..f70adc436 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,9 @@ "name": "bootstrap", "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", "version": "5.0.0-beta1", - "version_short": "5.0", + "config": { + "version_short": "5.0" + }, "keywords": [ "css", "sass", @@ -32,7 +34,7 @@ "css-prefix": "npm-run-all --parallel css-prefix-*", "css-prefix-main": "postcss --config build/postcss.config.js --replace \"dist/css/*.css\" \"!dist/css/*.rtl*.css\" \"!dist/css/*.min.css\"", "css-prefix-examples": "postcss --config build/postcss.config.js --replace \"site/content/**/*.css\"", - "css-prefix-examples-rtl": "cross-env-shell NODE_ENV=RTL postcss --config build/postcss.config.js --dir \"site/content/docs/$npm_package_version_short/examples/\" --ext \".rtl.css\" --base \"site/content/docs/$npm_package_version_short/examples/\" \"site/content/docs/$npm_package_version_short/examples/{blog,carousel,dashboard,cheatsheet}/*.css\" \"!site/content/docs/$npm_package_version_short/examples/{blog,carousel,dashboard,cheatsheet}/*.rtl.css\"", + "css-prefix-examples-rtl": "cross-env-shell NODE_ENV=RTL postcss --config build/postcss.config.js --dir \"site/content/docs/$npm_package_config_version_short/examples/\" --ext \".rtl.css\" --base \"site/content/docs/$npm_package_config_version_short/examples/\" \"site/content/docs/$npm_package_config_version_short/examples/{blog,carousel,dashboard,cheatsheet}/*.css\" \"!site/content/docs/$npm_package_config_version_short/examples/{blog,carousel,dashboard,cheatsheet}/*.rtl.css\"", "js": "npm-run-all js-compile js-minify", "js-compile": "npm-run-all --parallel js-compile-*", "js-compile-standalone": "rollup --environment BUNDLE:false --config build/rollup.config.js --sourcemap", @@ -49,7 +51,7 @@ "js-test-karma": "karma start js/tests/karma.conf.js", "js-test-integration-bundle": "rollup --config js/tests/integration/rollup.bundle.js", "js-test-integration-modularity": "rollup --config js/tests/integration/rollup.bundle-modularity.js", - "js-test-cloud": "cross-env BROWSER=true npm run js-test-karma", + "js-test-cloud": "cross-env BROWSERSTACK=true npm run js-test-karma", "js-test-jquery": "cross-env JQUERY=true npm run js-test-karma", "lint": "npm-run-all --parallel js-lint css-lint lockfile-lint", "docs": "npm-run-all docs-build docs-lint", @@ -106,7 +108,7 @@ "@rollup/plugin-commonjs": "^17.0.0", "@rollup/plugin-node-resolve": "^11.0.1", "@rollup/plugin-replace": "^2.3.4", - "autoprefixer": "^10.2.0", + "autoprefixer": "^10.2.1", "bundlewatch": "^0.3.1", "clean-css-cli": "^4.3.0", "cross-env": "^7.0.3", @@ -133,12 +135,12 @@ "lockfile-lint": "^4.3.7", "nodemon": "^2.0.7", "npm-run-all": "^4.1.5", - "postcss": "^8.2.2", + "postcss": "^8.2.4", "postcss-cli": "^8.3.1", "rollup": "^2.36.1", "rollup-plugin-istanbul": "^3.0.0", "rtlcss": "^3.0.0", - "sass": "^1.32.2", + "sass": "^1.32.4", "shelljs": "^0.8.4", "stylelint": "^13.8.0", "stylelint-config-twbs-bootstrap": "^2.1.0", diff --git a/scss/_close.scss b/scss/_close.scss index db5d02787..32a0f68cc 100644 --- a/scss/_close.scss +++ b/scss/_close.scss @@ -22,7 +22,7 @@ } &:focus { - outline: none; + outline: 0; box-shadow: $btn-close-focus-shadow; opacity: $btn-close-focus-opacity; } diff --git a/scss/_reboot.scss b/scss/_reboot.scss index 58e1a34d0..973b5d16c 100644 --- a/scss/_reboot.scss +++ b/scss/_reboot.scss @@ -411,6 +411,15 @@ button { border-radius: 0; } +// Explicitly remove focus outline in Chromium when it shouldn't be +// visible (e.g. as result of mouse click or touch tap). It already +// should be doing this automatically, but seems to currently be +// confused and applies its very visible two-tone outline anyway. + +button:focus:not(:focus-visible) { + outline: 0; +} + // 1. Remove the margin in Firefox and Safari input, diff --git a/scss/_toasts.scss b/scss/_toasts.scss index 7a1cf7354..5c533d7f5 100644 --- a/scss/_toasts.scss +++ b/scss/_toasts.scss @@ -47,4 +47,5 @@ .toast-body { padding: $toast-padding-x; // apply to both vertical and horizontal + word-wrap: break-word; } diff --git a/scss/_variables.scss b/scss/_variables.scss index 3fc03a08e..3d677728a 100644 --- a/scss/_variables.scss +++ b/scss/_variables.scss @@ -938,7 +938,7 @@ $dropdown-box-shadow: $box-shadow !default; $dropdown-link-color: $gray-900 !default; $dropdown-link-hover-color: shade-color($gray-900, 10%) !default; -$dropdown-link-hover-bg: $gray-100 !default; +$dropdown-link-hover-bg: $gray-200 !default; $dropdown-link-active-color: $component-active-color !default; $dropdown-link-active-bg: $component-active-bg !default; diff --git a/scss/forms/_form-range.scss b/scss/forms/_form-range.scss index b603a7bc3..ae1d841d5 100644 --- a/scss/forms/_form-range.scss +++ b/scss/forms/_form-range.scss @@ -12,7 +12,7 @@ appearance: none; &:focus { - outline: none; + outline: 0; // Pseudo-elements must be split across multiple rulesets to have an effect. // No box-shadow() mixin for focus accessibility. diff --git a/site/content/docs/5.0/components/toasts.md b/site/content/docs/5.0/components/toasts.md index b6f7e3893..bd7bcae9c 100644 --- a/site/content/docs/5.0/components/toasts.md +++ b/site/content/docs/5.0/components/toasts.md @@ -43,7 +43,7 @@ Toasts are as flexible as you need and have very little required markup. At a mi ### Translucent -Toasts are slightly translucent, too, so they blend over whatever they might appear over. For browsers that support the `backdrop-filter` CSS property, we'll also attempt to blur the elements under a toast. +Toasts are slightly translucent, too, so they blend over whatever they might appear over. {{< example class="bg-dark" >}}