mirror of
https://github.com/tenrok/bootstrap.git
synced 2026-05-15 11:59:39 +03:00
Merge branch 'main' into main-fod-table-separator
This commit is contained in:
@@ -46,7 +46,7 @@
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.esm.min.js",
|
||||
"maxSize": "18.25 kB"
|
||||
"maxSize": "18.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/bootstrap.js",
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@
|
||||
"Woohoo",
|
||||
"zindex",
|
||||
"بالعالم",
|
||||
"مرحبا"
|
||||
"مرحبًا"
|
||||
],
|
||||
"language": "en,en-US",
|
||||
"ignorePaths": [
|
||||
|
||||
@@ -2,6 +2,7 @@ name: BrowserStack
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
|
||||
@@ -14,6 +14,7 @@ on:
|
||||
- "!dependabot/**"
|
||||
schedule:
|
||||
- cron: "0 2 * * 5"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
|
||||
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update_release_draft:
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
# Ignore docs files
|
||||
/_site/
|
||||
# Hugo resources folder
|
||||
# Hugo files
|
||||
/resources/
|
||||
/.hugo_build.lock
|
||||
|
||||
# Numerous always-ignore extensions
|
||||
*.diff
|
||||
|
||||
@@ -65,8 +65,6 @@ Read the [Getting started page](https://getbootstrap.com/docs/5.1/getting-starte
|
||||
[](https://atmospherejs.com/twbs/bootstrap)
|
||||
[](https://packagist.org/packages/twbs/bootstrap)
|
||||
[](https://www.nuget.org/packages/bootstrap/absoluteLatest)
|
||||
[](https://david-dm.org/twbs/bootstrap?type=peer)
|
||||
[](https://david-dm.org/twbs/bootstrap?type=dev)
|
||||
[](https://coveralls.io/github/twbs/bootstrap?branch=main)
|
||||
[](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css)
|
||||
[](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css)
|
||||
@@ -79,7 +77,9 @@ Read the [Getting started page](https://getbootstrap.com/docs/5.1/getting-starte
|
||||
|
||||
## What's included
|
||||
|
||||
Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this:
|
||||
Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations.
|
||||
|
||||
<details><summary>Download contents</summary>
|
||||
|
||||
```text
|
||||
bootstrap/
|
||||
@@ -130,6 +130,7 @@ bootstrap/
|
||||
├── bootstrap.min.js
|
||||
└── bootstrap.min.js.map
|
||||
```
|
||||
</details>
|
||||
|
||||
We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). [Source maps](https://developers.google.com/web/tools/chrome-devtools/javascript/source-maps) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Bundled JS files (`bootstrap.bundle.js` and minified `bootstrap.bundle.min.js`) include [Popper](https://popper.js.org/).
|
||||
|
||||
|
||||
+66
-145
@@ -11,173 +11,94 @@
|
||||
|
||||
const path = require('path')
|
||||
const rollup = require('rollup')
|
||||
const glob = require('glob')
|
||||
const { babel } = require('@rollup/plugin-babel')
|
||||
const banner = require('./banner.js')
|
||||
|
||||
const rootPath = path.resolve(__dirname, '../js/dist/')
|
||||
const plugins = [
|
||||
babel({
|
||||
// Only transpile our source code
|
||||
exclude: 'node_modules/**',
|
||||
// Include the helpers in each file, at most one copy of each
|
||||
babelHelpers: 'bundled'
|
||||
const srcPath = path.resolve(__dirname, '../js/src/')
|
||||
const jsFiles = glob.sync(srcPath + '/**/*.js')
|
||||
|
||||
// Array which holds the resolved plugins
|
||||
const resolvedPlugins = []
|
||||
|
||||
// Trims the "js" extension and uppercases => first letter, hyphens, backslashes & slashes
|
||||
const filenameToEntity = filename => filename.replace('.js', '')
|
||||
.replace(/(?:^|-|\/|\\)[a-z]/g, str => str.slice(-1).toUpperCase())
|
||||
|
||||
for (const file of jsFiles) {
|
||||
resolvedPlugins.push({
|
||||
src: file.replace('.js', ''),
|
||||
dist: file.replace('src', 'dist'),
|
||||
fileName: path.basename(file),
|
||||
className: filenameToEntity(path.basename(file))
|
||||
// safeClassName: filenameToEntity(path.relative(srcPath, file))
|
||||
})
|
||||
]
|
||||
const bsPlugins = {
|
||||
Data: path.resolve(__dirname, '../js/src/dom/data.js'),
|
||||
EventHandler: path.resolve(__dirname, '../js/src/dom/event-handler.js'),
|
||||
Manipulator: path.resolve(__dirname, '../js/src/dom/manipulator.js'),
|
||||
SelectorEngine: path.resolve(__dirname, '../js/src/dom/selector-engine.js'),
|
||||
Alert: path.resolve(__dirname, '../js/src/alert.js'),
|
||||
Base: path.resolve(__dirname, '../js/src/base-component.js'),
|
||||
Button: path.resolve(__dirname, '../js/src/button.js'),
|
||||
Carousel: path.resolve(__dirname, '../js/src/carousel.js'),
|
||||
Collapse: path.resolve(__dirname, '../js/src/collapse.js'),
|
||||
Dropdown: path.resolve(__dirname, '../js/src/dropdown.js'),
|
||||
Modal: path.resolve(__dirname, '../js/src/modal.js'),
|
||||
Offcanvas: path.resolve(__dirname, '../js/src/offcanvas.js'),
|
||||
Popover: path.resolve(__dirname, '../js/src/popover.js'),
|
||||
ScrollSpy: path.resolve(__dirname, '../js/src/scrollspy.js'),
|
||||
Tab: path.resolve(__dirname, '../js/src/tab.js'),
|
||||
Toast: path.resolve(__dirname, '../js/src/toast.js'),
|
||||
Tooltip: path.resolve(__dirname, '../js/src/tooltip.js')
|
||||
}
|
||||
|
||||
const defaultPluginConfig = {
|
||||
external: [
|
||||
bsPlugins.Data,
|
||||
bsPlugins.Base,
|
||||
bsPlugins.EventHandler,
|
||||
bsPlugins.SelectorEngine
|
||||
],
|
||||
globals: {
|
||||
[bsPlugins.Data]: 'Data',
|
||||
[bsPlugins.Base]: 'Base',
|
||||
[bsPlugins.EventHandler]: 'EventHandler',
|
||||
[bsPlugins.SelectorEngine]: 'SelectorEngine'
|
||||
}
|
||||
}
|
||||
|
||||
const getConfigByPluginKey = pluginKey => {
|
||||
switch (pluginKey) {
|
||||
case 'Alert':
|
||||
case 'Offcanvas':
|
||||
case 'Tab':
|
||||
return defaultPluginConfig
|
||||
|
||||
case 'Base':
|
||||
case 'Button':
|
||||
case 'Carousel':
|
||||
case 'Collapse':
|
||||
case 'Modal':
|
||||
case 'ScrollSpy': {
|
||||
const config = Object.assign(defaultPluginConfig)
|
||||
config.external.push(bsPlugins.Manipulator)
|
||||
config.globals[bsPlugins.Manipulator] = 'Manipulator'
|
||||
return config
|
||||
}
|
||||
|
||||
case 'Dropdown':
|
||||
case 'Tooltip': {
|
||||
const config = Object.assign(defaultPluginConfig)
|
||||
config.external.push(bsPlugins.Manipulator, '@popperjs/core')
|
||||
config.globals[bsPlugins.Manipulator] = 'Manipulator'
|
||||
config.globals['@popperjs/core'] = 'Popper'
|
||||
return config
|
||||
}
|
||||
|
||||
case 'Popover':
|
||||
return {
|
||||
external: [
|
||||
bsPlugins.Data,
|
||||
bsPlugins.SelectorEngine,
|
||||
bsPlugins.Tooltip
|
||||
],
|
||||
globals: {
|
||||
[bsPlugins.Data]: 'Data',
|
||||
[bsPlugins.SelectorEngine]: 'SelectorEngine',
|
||||
[bsPlugins.Tooltip]: 'Tooltip'
|
||||
}
|
||||
}
|
||||
|
||||
case 'Toast':
|
||||
return {
|
||||
external: [
|
||||
bsPlugins.Data,
|
||||
bsPlugins.Base,
|
||||
bsPlugins.EventHandler,
|
||||
bsPlugins.Manipulator
|
||||
],
|
||||
globals: {
|
||||
[bsPlugins.Data]: 'Data',
|
||||
[bsPlugins.Base]: 'Base',
|
||||
[bsPlugins.EventHandler]: 'EventHandler',
|
||||
[bsPlugins.Manipulator]: 'Manipulator'
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return {
|
||||
external: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const utilObjects = new Set([
|
||||
'Util',
|
||||
'Sanitizer',
|
||||
'Backdrop'
|
||||
])
|
||||
|
||||
const domObjects = new Set([
|
||||
'Data',
|
||||
'EventHandler',
|
||||
'Manipulator',
|
||||
'SelectorEngine'
|
||||
])
|
||||
|
||||
const build = async plugin => {
|
||||
console.log(`Building ${plugin} plugin...`)
|
||||
|
||||
const { external, globals } = getConfigByPluginKey(plugin)
|
||||
const pluginFilename = path.basename(bsPlugins[plugin])
|
||||
let pluginPath = rootPath
|
||||
|
||||
if (utilObjects.has(plugin)) {
|
||||
pluginPath = `${rootPath}/util/`
|
||||
}
|
||||
|
||||
if (domObjects.has(plugin)) {
|
||||
pluginPath = `${rootPath}/dom/`
|
||||
}
|
||||
const globals = {}
|
||||
|
||||
const bundle = await rollup.rollup({
|
||||
input: bsPlugins[plugin],
|
||||
plugins,
|
||||
external
|
||||
input: plugin.src,
|
||||
plugins: [
|
||||
babel({
|
||||
// Only transpile our source code
|
||||
exclude: 'node_modules/**',
|
||||
// Include the helpers in each file, at most one copy of each
|
||||
babelHelpers: 'bundled'
|
||||
})
|
||||
],
|
||||
external: source => {
|
||||
// Pattern to identify local files
|
||||
const pattern = /^(\.{1,2})\//
|
||||
|
||||
// It's not a local file, e.g a Node.js package
|
||||
if (!pattern.test(source)) {
|
||||
globals[source] = source
|
||||
return true
|
||||
}
|
||||
|
||||
const usedPlugin = resolvedPlugins.find(plugin => {
|
||||
return plugin.src.includes(source.replace(pattern, ''))
|
||||
})
|
||||
|
||||
if (!usedPlugin) {
|
||||
throw new Error(`Source ${source} is not mapped!`)
|
||||
}
|
||||
|
||||
// We can change `Index` with `UtilIndex` etc if we use
|
||||
// `safeClassName` instead of `className` everywhere
|
||||
globals[path.normalize(usedPlugin.src)] = usedPlugin.className
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
await bundle.write({
|
||||
banner: banner(pluginFilename),
|
||||
banner: banner(plugin.fileName),
|
||||
format: 'umd',
|
||||
name: plugin,
|
||||
name: plugin.className,
|
||||
sourcemap: true,
|
||||
globals,
|
||||
generatedCode: 'es2015',
|
||||
file: path.resolve(__dirname, `${pluginPath}/${pluginFilename}`)
|
||||
file: plugin.dist
|
||||
})
|
||||
|
||||
console.log(`Building ${plugin} plugin... Done!`)
|
||||
console.log(`Built ${plugin.className}`)
|
||||
}
|
||||
|
||||
const main = async () => {
|
||||
(async () => {
|
||||
try {
|
||||
await Promise.all(Object.keys(bsPlugins).map(plugin => build(plugin)))
|
||||
const basename = path.basename(__filename)
|
||||
const timeLabel = `[${basename}] finished`
|
||||
|
||||
console.log('Building individual plugins...')
|
||||
console.time(timeLabel)
|
||||
|
||||
await Promise.all(Object.values(resolvedPlugins).map(plugin => build(plugin)))
|
||||
|
||||
console.timeEnd(timeLabel)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
})()
|
||||
|
||||
@@ -57,7 +57,7 @@ async function replaceRecursively(file, oldVersion, newVersion) {
|
||||
}
|
||||
|
||||
async function main(args) {
|
||||
const [oldVersion, newVersion] = args
|
||||
let [oldVersion, newVersion] = args
|
||||
|
||||
if (!oldVersion || !newVersion) {
|
||||
console.error('USAGE: change-version old_version new_version [--verbose] [--dry[-run]]')
|
||||
@@ -66,7 +66,7 @@ async function main(args) {
|
||||
}
|
||||
|
||||
// Strip any leading `v` from arguments because otherwise we will end up with duplicate `v`s
|
||||
[oldVersion, newVersion].map(arg => arg.startsWith('v') ? arg.slice(1) : arg)
|
||||
[oldVersion, newVersion] = [oldVersion, newVersion].map(arg => arg.startsWith('v') ? arg.slice(1) : arg)
|
||||
|
||||
try {
|
||||
const files = await globby(GLOB, GLOBBY_OPTIONS)
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
# Usage:
|
||||
# install svgo globally: `npm i -g svgo`
|
||||
# svgo --config=build/svgo.yml --input=foo.svg
|
||||
|
||||
# https://github.com/svg/svgo/blob/master/docs/how-it-works/en.md
|
||||
# replace default config
|
||||
|
||||
multipass: true
|
||||
#full: true
|
||||
|
||||
# https://github.com/svg/svgo/blob/master/lib/svgo/js2svg.js#L6 for more config options
|
||||
|
||||
js2svg:
|
||||
pretty: true
|
||||
indent: 2
|
||||
|
||||
plugins:
|
||||
# - addAttributesToSVGElement:
|
||||
# attributes:
|
||||
# - focusable: false
|
||||
- cleanupAttrs: true
|
||||
- cleanupEnableBackground: true
|
||||
- cleanupIDs: true
|
||||
- cleanupListOfValues: true
|
||||
- cleanupNumericValues: true
|
||||
- collapseGroups: true
|
||||
- convertColors: true
|
||||
- convertPathData: true
|
||||
- convertShapeToPath: true
|
||||
- convertStyleToAttrs: true
|
||||
- convertTransform: true
|
||||
- inlineStyles: true
|
||||
- mergePaths: true
|
||||
- minifyStyles: true
|
||||
- moveElemsAttrsToGroup: true
|
||||
- moveGroupAttrsToElems: true
|
||||
- removeAttrs:
|
||||
attrs:
|
||||
- "data-name"
|
||||
- removeComments: true
|
||||
- removeDesc: true
|
||||
- removeDoctype: true
|
||||
- removeEditorsNSData: true
|
||||
- removeEmptyAttrs: true
|
||||
- removeEmptyContainers: true
|
||||
- removeEmptyText: true
|
||||
- removeHiddenElems: true
|
||||
- removeMetadata: true
|
||||
- removeNonInheritableGroupAttrs: true
|
||||
- removeTitle: false
|
||||
- removeUnknownsAndDefaults:
|
||||
keepRoleAttr: true
|
||||
- removeUnusedNS: true
|
||||
- removeUselessDefs: true
|
||||
- removeUselessStrokeAndFill: true
|
||||
- removeViewBox: false
|
||||
- removeXMLNS: false
|
||||
- removeXMLProcInst: true
|
||||
- sortAttrs: true
|
||||
+2
-2
@@ -75,5 +75,5 @@ params:
|
||||
js_hash: "sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13"
|
||||
js_bundle: "https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
|
||||
js_bundle_hash: "sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
|
||||
popper: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js"
|
||||
popper_hash: "sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB"
|
||||
popper: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.0/dist/umd/popper.min.js"
|
||||
popper_hash: "sha384-ThCKQ0fDhFQh8PSFLvjhmjy7oRKp5gRbY+bcEnQWtRhuvli/qxsn0xMcMmzXkuIa"
|
||||
|
||||
+12
-27
@@ -5,30 +5,15 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import Alert from './src/alert'
|
||||
import Button from './src/button'
|
||||
import Carousel from './src/carousel'
|
||||
import Collapse from './src/collapse'
|
||||
import Dropdown from './src/dropdown'
|
||||
import Modal from './src/modal'
|
||||
import Offcanvas from './src/offcanvas'
|
||||
import Popover from './src/popover'
|
||||
import ScrollSpy from './src/scrollspy'
|
||||
import Tab from './src/tab'
|
||||
import Toast from './src/toast'
|
||||
import Tooltip from './src/tooltip'
|
||||
|
||||
export {
|
||||
Alert,
|
||||
Button,
|
||||
Carousel,
|
||||
Collapse,
|
||||
Dropdown,
|
||||
Modal,
|
||||
Offcanvas,
|
||||
Popover,
|
||||
ScrollSpy,
|
||||
Tab,
|
||||
Toast,
|
||||
Tooltip
|
||||
}
|
||||
export { default as Alert } from './src/alert'
|
||||
export { default as Button } from './src/button'
|
||||
export { default as Carousel } from './src/carousel'
|
||||
export { default as Collapse } from './src/collapse'
|
||||
export { default as Dropdown } from './src/dropdown'
|
||||
export { default as Modal } from './src/modal'
|
||||
export { default as Offcanvas } from './src/offcanvas'
|
||||
export { default as Popover } from './src/popover'
|
||||
export { default as ScrollSpy } from './src/scrollspy'
|
||||
export { default as Tab } from './src/tab'
|
||||
export { default as Toast } from './src/toast'
|
||||
export { default as Tooltip } from './src/tooltip'
|
||||
|
||||
+44
-64
@@ -33,14 +33,11 @@ const EVENT_KEY = `.${DATA_KEY}`
|
||||
const DATA_API_KEY = '.data-api'
|
||||
|
||||
const ESCAPE_KEY = 'Escape'
|
||||
const SPACE_KEY = 'Space'
|
||||
const TAB_KEY = 'Tab'
|
||||
const ARROW_UP_KEY = 'ArrowUp'
|
||||
const ARROW_DOWN_KEY = 'ArrowDown'
|
||||
const RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button
|
||||
|
||||
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`)
|
||||
|
||||
const EVENT_HIDE = `hide${EVENT_KEY}`
|
||||
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
|
||||
const EVENT_SHOW = `show${EVENT_KEY}`
|
||||
@@ -53,10 +50,10 @@ const CLASS_NAME_SHOW = 'show'
|
||||
const CLASS_NAME_DROPUP = 'dropup'
|
||||
const CLASS_NAME_DROPEND = 'dropend'
|
||||
const CLASS_NAME_DROPSTART = 'dropstart'
|
||||
const CLASS_NAME_NAVBAR = 'navbar'
|
||||
|
||||
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]'
|
||||
const SELECTOR_MENU = '.dropdown-menu'
|
||||
const SELECTOR_NAVBAR = '.navbar'
|
||||
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
|
||||
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
|
||||
|
||||
@@ -132,13 +129,9 @@ class Dropdown extends BaseComponent {
|
||||
return
|
||||
}
|
||||
|
||||
const parent = Dropdown.getParentFromElement(this._element)
|
||||
// Totally disable Popper for Dropdowns in Navbar
|
||||
if (this._inNavbar) {
|
||||
Manipulator.setDataAttribute(this._menu, 'popper', 'none')
|
||||
} else {
|
||||
this._createPopper(parent)
|
||||
}
|
||||
const parent = getElementFromSelector(this._element) || this._element.parentNode
|
||||
|
||||
this._createPopper(parent)
|
||||
|
||||
// If this is a touch-enabled device we add extra
|
||||
// empty mouseover listeners to the body's immediate children;
|
||||
@@ -246,13 +239,7 @@ class Dropdown extends BaseComponent {
|
||||
}
|
||||
|
||||
const popperConfig = this._getPopperConfig()
|
||||
const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false)
|
||||
|
||||
this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)
|
||||
|
||||
if (isDisplayStatic) {
|
||||
Manipulator.setDataAttribute(this._menu, 'popper', 'static')
|
||||
}
|
||||
}
|
||||
|
||||
_isShown(element = this._element) {
|
||||
@@ -285,7 +272,7 @@ class Dropdown extends BaseComponent {
|
||||
}
|
||||
|
||||
_detectNavbar() {
|
||||
return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null
|
||||
return this._element.closest(SELECTOR_NAVBAR) !== null
|
||||
}
|
||||
|
||||
_getOffset() {
|
||||
@@ -319,8 +306,9 @@ class Dropdown extends BaseComponent {
|
||||
}]
|
||||
}
|
||||
|
||||
// Disable Popper if we have a static display
|
||||
if (this._config.display === 'static') {
|
||||
// Disable Popper if we have a static display or Dropdown is in Navbar
|
||||
if (this._inNavbar || this._config.display === 'static') {
|
||||
Manipulator.setDataAttribute(this._menu, 'popper', 'static') // todo:v6 remove
|
||||
defaultBsPopperConfig.modifiers = [{
|
||||
name: 'applyStyles',
|
||||
enabled: false
|
||||
@@ -363,7 +351,7 @@ class Dropdown extends BaseComponent {
|
||||
}
|
||||
|
||||
static clearMenus(event) {
|
||||
if (event && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) {
|
||||
if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -383,54 +371,54 @@ class Dropdown extends BaseComponent {
|
||||
relatedTarget: context._element
|
||||
}
|
||||
|
||||
if (event) {
|
||||
const composedPath = event.composedPath()
|
||||
const isMenuTarget = composedPath.includes(context._menu)
|
||||
if (
|
||||
composedPath.includes(context._element) ||
|
||||
(context._config.autoClose === 'inside' && !isMenuTarget) ||
|
||||
(context._config.autoClose === 'outside' && isMenuTarget)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
const composedPath = event.composedPath()
|
||||
const isMenuTarget = composedPath.includes(context._menu)
|
||||
if (
|
||||
composedPath.includes(context._element) ||
|
||||
(context._config.autoClose === 'inside' && !isMenuTarget) ||
|
||||
(context._config.autoClose === 'outside' && isMenuTarget)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
|
||||
if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {
|
||||
continue
|
||||
}
|
||||
// Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
|
||||
if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (event.type === 'click') {
|
||||
relatedTarget.clickEvent = event
|
||||
}
|
||||
if (event.type === 'click') {
|
||||
relatedTarget.clickEvent = event
|
||||
}
|
||||
|
||||
context._completeHide(relatedTarget)
|
||||
}
|
||||
}
|
||||
|
||||
static getParentFromElement(element) {
|
||||
return getElementFromSelector(element) || element.parentNode
|
||||
}
|
||||
|
||||
static dataApiKeydownHandler(event) {
|
||||
// If not input/textarea:
|
||||
// - And not a key in REGEXP_KEYDOWN => not a dropdown command
|
||||
// If input/textarea:
|
||||
// - If space key => not a dropdown command
|
||||
// - If key is other than escape
|
||||
// - If key is not up or down => not a dropdown command
|
||||
// - And not a key in UP | DOWN | ESCAPE => not a dropdown command
|
||||
// If input/textarea && If key is other than ESCAPE
|
||||
// - If key is not UP or DOWN => not a dropdown command
|
||||
// - If trigger inside the menu => not a dropdown command
|
||||
if (/input|textarea/i.test(event.target.tagName) ?
|
||||
event.key === SPACE_KEY || (event.key !== ESCAPE_KEY &&
|
||||
((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) ||
|
||||
event.target.closest(SELECTOR_MENU))) :
|
||||
!REGEXP_KEYDOWN.test(event.key)) {
|
||||
|
||||
const isInput = /input|textarea/i.test(event.target.tagName)
|
||||
const isEscapeEvent = event.key === ESCAPE_KEY
|
||||
const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)
|
||||
|
||||
if (!isInput && !(isUpOrDownEvent || isEscapeEvent)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isInput && !isEscapeEvent) {
|
||||
// eslint-disable-next-line unicorn/no-lonely-if
|
||||
if (!isUpOrDownEvent || event.target.closest(SELECTOR_MENU)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const isActive = this.classList.contains(CLASS_NAME_SHOW)
|
||||
|
||||
if (!isActive && event.key === ESCAPE_KEY) {
|
||||
if (!isActive && isEscapeEvent) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -444,22 +432,14 @@ class Dropdown extends BaseComponent {
|
||||
const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
|
||||
const instance = Dropdown.getOrCreateInstance(getToggleButton)
|
||||
|
||||
if (event.key === ESCAPE_KEY) {
|
||||
if (isEscapeEvent) {
|
||||
instance.hide()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {
|
||||
if (!isActive) {
|
||||
instance.show()
|
||||
}
|
||||
|
||||
if (isUpOrDownEvent) {
|
||||
instance.show()
|
||||
instance._selectMenuItem(event)
|
||||
return
|
||||
}
|
||||
|
||||
if (!isActive || event.key === SPACE_KEY) {
|
||||
Dropdown.clearMenus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+50
-69
@@ -40,8 +40,6 @@ const EVENT_SHOWN = `shown${EVENT_KEY}`
|
||||
const EVENT_RESIZE = `resize${EVENT_KEY}`
|
||||
const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`
|
||||
const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`
|
||||
const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`
|
||||
const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`
|
||||
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
|
||||
|
||||
const CLASS_NAME_OPEN = 'modal-open'
|
||||
@@ -79,7 +77,6 @@ class Modal extends BaseComponent {
|
||||
this._backdrop = this._initializeBackDrop()
|
||||
this._focustrap = this._initializeFocusTrap()
|
||||
this._isShown = false
|
||||
this._ignoreBackdropClick = false
|
||||
this._isTransitioning = false
|
||||
this._scrollBar = new ScrollBarHelper()
|
||||
}
|
||||
@@ -112,10 +109,7 @@ class Modal extends BaseComponent {
|
||||
}
|
||||
|
||||
this._isShown = true
|
||||
|
||||
if (this._isAnimated()) {
|
||||
this._isTransitioning = true
|
||||
}
|
||||
this._isTransitioning = true
|
||||
|
||||
this._scrollBar.hide()
|
||||
|
||||
@@ -123,16 +117,8 @@ class Modal extends BaseComponent {
|
||||
|
||||
this._adjustDialog()
|
||||
|
||||
this._setEscapeEvent()
|
||||
this._setResizeEvent()
|
||||
|
||||
EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {
|
||||
EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => {
|
||||
if (event.target === this._element) {
|
||||
this._ignoreBackdropClick = true
|
||||
}
|
||||
})
|
||||
})
|
||||
this._toggleEscapeEventListener(true)
|
||||
this._toggleResizeEventListener(true)
|
||||
|
||||
this._showBackdrop(() => this._showElement(relatedTarget))
|
||||
}
|
||||
@@ -149,23 +135,16 @@ class Modal extends BaseComponent {
|
||||
}
|
||||
|
||||
this._isShown = false
|
||||
const isAnimated = this._isAnimated()
|
||||
this._isTransitioning = true
|
||||
|
||||
if (isAnimated) {
|
||||
this._isTransitioning = true
|
||||
}
|
||||
|
||||
this._setEscapeEvent()
|
||||
this._setResizeEvent()
|
||||
this._toggleEscapeEventListener(false)
|
||||
this._toggleResizeEventListener(false)
|
||||
|
||||
this._focustrap.deactivate()
|
||||
|
||||
this._element.classList.remove(CLASS_NAME_SHOW)
|
||||
|
||||
EventHandler.off(this._element, EVENT_CLICK_DISMISS)
|
||||
EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS)
|
||||
|
||||
this._queueCallback(() => this._hideModal(), this._element, isAnimated)
|
||||
this._queueCallback(() => this._hideModal(), this._element, this._isAnimated())
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -207,11 +186,8 @@ class Modal extends BaseComponent {
|
||||
}
|
||||
|
||||
_showElement(relatedTarget) {
|
||||
const isAnimated = this._isAnimated()
|
||||
const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)
|
||||
|
||||
if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
|
||||
// Don't move modal's DOM position
|
||||
// try to append dynamic modal
|
||||
if (!document.body.contains(this._element)) {
|
||||
document.body.append(this._element)
|
||||
}
|
||||
|
||||
@@ -221,13 +197,12 @@ class Modal extends BaseComponent {
|
||||
this._element.setAttribute('role', 'dialog')
|
||||
this._element.scrollTop = 0
|
||||
|
||||
const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)
|
||||
if (modalBody) {
|
||||
modalBody.scrollTop = 0
|
||||
}
|
||||
|
||||
if (isAnimated) {
|
||||
reflow(this._element)
|
||||
}
|
||||
reflow(this._element)
|
||||
|
||||
this._element.classList.add(CLASS_NAME_SHOW)
|
||||
|
||||
@@ -242,30 +217,37 @@ class Modal extends BaseComponent {
|
||||
})
|
||||
}
|
||||
|
||||
this._queueCallback(transitionComplete, this._dialog, isAnimated)
|
||||
this._queueCallback(transitionComplete, this._dialog, this._isAnimated())
|
||||
}
|
||||
|
||||
_setEscapeEvent() {
|
||||
if (this._isShown) {
|
||||
EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
|
||||
if (this._config.keyboard && event.key === ESCAPE_KEY) {
|
||||
event.preventDefault()
|
||||
this.hide()
|
||||
} else if (!this._config.keyboard && event.key === ESCAPE_KEY) {
|
||||
this._triggerBackdropTransition()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
_toggleEscapeEventListener(enable) {
|
||||
if (!enable) {
|
||||
EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS)
|
||||
return
|
||||
}
|
||||
|
||||
EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
|
||||
if (event.key !== ESCAPE_KEY) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._config.keyboard) {
|
||||
event.preventDefault()
|
||||
this.hide()
|
||||
return
|
||||
}
|
||||
|
||||
this._triggerBackdropTransition()
|
||||
})
|
||||
}
|
||||
|
||||
_setResizeEvent() {
|
||||
if (this._isShown) {
|
||||
_toggleResizeEventListener(enable) {
|
||||
if (enable) {
|
||||
EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog())
|
||||
} else {
|
||||
EventHandler.off(window, EVENT_RESIZE)
|
||||
return
|
||||
}
|
||||
|
||||
EventHandler.off(window, EVENT_RESIZE)
|
||||
}
|
||||
|
||||
_hideModal() {
|
||||
@@ -274,6 +256,7 @@ class Modal extends BaseComponent {
|
||||
this._element.removeAttribute('aria-modal')
|
||||
this._element.removeAttribute('role')
|
||||
this._isTransitioning = false
|
||||
|
||||
this._backdrop.hide(() => {
|
||||
document.body.classList.remove(CLASS_NAME_OPEN)
|
||||
this._resetAdjustments()
|
||||
@@ -284,18 +267,16 @@ class Modal extends BaseComponent {
|
||||
|
||||
_showBackdrop(callback) {
|
||||
EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {
|
||||
if (this._ignoreBackdropClick) {
|
||||
this._ignoreBackdropClick = false
|
||||
return
|
||||
}
|
||||
|
||||
if (event.target !== event.currentTarget) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._config.backdrop === true) {
|
||||
this.hide()
|
||||
} else if (this._config.backdrop === 'static') {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._config.backdrop === 'static') {
|
||||
this._triggerBackdropTransition()
|
||||
}
|
||||
})
|
||||
@@ -315,9 +296,9 @@ class Modal extends BaseComponent {
|
||||
|
||||
const { classList, scrollHeight, style } = this._element
|
||||
const isModalOverflowing = scrollHeight > document.documentElement.clientHeight
|
||||
|
||||
const initialOverflowY = style.overflowY
|
||||
// return if the following background transition hasn't yet completed
|
||||
if ((!isModalOverflowing && style.overflowY === 'hidden') || classList.contains(CLASS_NAME_STATIC)) {
|
||||
if (initialOverflowY === 'hidden' || classList.contains(CLASS_NAME_STATIC)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -328,11 +309,9 @@ class Modal extends BaseComponent {
|
||||
classList.add(CLASS_NAME_STATIC)
|
||||
this._queueCallback(() => {
|
||||
classList.remove(CLASS_NAME_STATIC)
|
||||
if (!isModalOverflowing) {
|
||||
this._queueCallback(() => {
|
||||
style.overflowY = ''
|
||||
}, this._dialog)
|
||||
}
|
||||
this._queueCallback(() => {
|
||||
style.overflowY = initialOverflowY
|
||||
}, this._dialog)
|
||||
}, this._dialog)
|
||||
|
||||
this._element.focus()
|
||||
@@ -347,12 +326,14 @@ class Modal extends BaseComponent {
|
||||
const scrollbarWidth = this._scrollBar.getWidth()
|
||||
const isBodyOverflowing = scrollbarWidth > 0
|
||||
|
||||
if ((!isBodyOverflowing && isModalOverflowing && !isRTL()) || (isBodyOverflowing && !isModalOverflowing && isRTL())) {
|
||||
this._element.style.paddingLeft = `${scrollbarWidth}px`
|
||||
if (isBodyOverflowing && !isModalOverflowing) {
|
||||
const property = isRTL() ? 'paddingLeft' : 'paddingRight'
|
||||
this._element.style[property] = `${scrollbarWidth}px`
|
||||
}
|
||||
|
||||
if ((isBodyOverflowing && !isModalOverflowing && !isRTL()) || (!isBodyOverflowing && isModalOverflowing && isRTL())) {
|
||||
this._element.style.paddingRight = `${scrollbarWidth}px`
|
||||
if (!isBodyOverflowing && isModalOverflowing) {
|
||||
const property = isRTL() ? 'paddingRight' : 'paddingLeft'
|
||||
this._element.style[property] = `${scrollbarWidth}px`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-18
@@ -15,7 +15,6 @@ import Tooltip from './tooltip'
|
||||
const NAME = 'popover'
|
||||
const DATA_KEY = 'bs.popover'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const CLASS_PREFIX = 'bs-popover'
|
||||
|
||||
const SELECTOR_TITLE = '.popover-header'
|
||||
const SELECTOR_CONTENT = '.popover-body'
|
||||
@@ -74,22 +73,20 @@ class Popover extends Tooltip {
|
||||
}
|
||||
|
||||
// Overrides
|
||||
isWithContent() {
|
||||
return this.getTitle() || this._getContent()
|
||||
}
|
||||
|
||||
setContent(tip) {
|
||||
this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TITLE)
|
||||
this._sanitizeAndSetContent(tip, this._getContent(), SELECTOR_CONTENT)
|
||||
_isWithContent() {
|
||||
return this._getTitle() || this._getContent()
|
||||
}
|
||||
|
||||
// Private
|
||||
_getContent() {
|
||||
return this._resolvePossibleFunction(this._config.content)
|
||||
_getContentForTemplate() {
|
||||
return {
|
||||
[SELECTOR_TITLE]: this._getTitle(),
|
||||
[SELECTOR_CONTENT]: this._getContent()
|
||||
}
|
||||
}
|
||||
|
||||
_getBasicClassPrefix() {
|
||||
return CLASS_PREFIX
|
||||
_getContent() {
|
||||
return this._resolvePossibleFunction(this._config.content)
|
||||
}
|
||||
|
||||
// Static
|
||||
@@ -97,13 +94,15 @@ class Popover extends Tooltip {
|
||||
return this.each(function () {
|
||||
const data = Popover.getOrCreateInstance(this, config)
|
||||
|
||||
if (typeof config === 'string') {
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
|
||||
data[config]()
|
||||
if (typeof config !== 'string') {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
|
||||
data[config]()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+159
-263
@@ -11,17 +11,15 @@ import {
|
||||
findShadowRoot,
|
||||
getElement,
|
||||
getUID,
|
||||
isElement,
|
||||
isRTL,
|
||||
noop,
|
||||
typeCheckConfig
|
||||
} from './util/index'
|
||||
import { DefaultAllowlist, sanitizeHtml } from './util/sanitizer'
|
||||
import Data from './dom/data'
|
||||
import { DefaultAllowlist } from './util/sanitizer'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Manipulator from './dom/manipulator'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import BaseComponent from './base-component'
|
||||
import TemplateFactory from './util/template-factory'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -30,16 +28,12 @@ import BaseComponent from './base-component'
|
||||
const NAME = 'tooltip'
|
||||
const DATA_KEY = 'bs.tooltip'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const CLASS_PREFIX = 'bs-tooltip'
|
||||
const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])
|
||||
|
||||
const CLASS_NAME_FADE = 'fade'
|
||||
const CLASS_NAME_MODAL = 'modal'
|
||||
const CLASS_NAME_SHOW = 'show'
|
||||
|
||||
const HOVER_STATE_SHOW = 'show'
|
||||
const HOVER_STATE_OUT = 'out'
|
||||
|
||||
const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
|
||||
const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`
|
||||
|
||||
@@ -129,9 +123,10 @@ class Tooltip extends BaseComponent {
|
||||
// Private
|
||||
this._isEnabled = true
|
||||
this._timeout = 0
|
||||
this._hoverState = ''
|
||||
this._isHovered = false
|
||||
this._activeTrigger = {}
|
||||
this._popper = null
|
||||
this._templateFactory = null
|
||||
|
||||
// Protected
|
||||
this._config = this._getConfig(config)
|
||||
@@ -181,17 +176,17 @@ class Tooltip extends BaseComponent {
|
||||
context._activeTrigger.click = !context._activeTrigger.click
|
||||
|
||||
if (context._isWithActiveTrigger()) {
|
||||
context._enter(null, context)
|
||||
context._enter()
|
||||
} else {
|
||||
context._leave(null, context)
|
||||
context._leave()
|
||||
}
|
||||
} else {
|
||||
if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {
|
||||
this._leave(null, this)
|
||||
if (this._getTipElement().classList.contains(CLASS_NAME_SHOW)) {
|
||||
this._leave()
|
||||
return
|
||||
}
|
||||
|
||||
this._enter(null, this)
|
||||
this._enter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +208,7 @@ class Tooltip extends BaseComponent {
|
||||
throw new Error('Please use show on visible elements')
|
||||
}
|
||||
|
||||
if (!(this.isWithContent() && this._isEnabled)) {
|
||||
if (!(this._isWithContent() && this._isEnabled)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -227,33 +222,11 @@ class Tooltip extends BaseComponent {
|
||||
return
|
||||
}
|
||||
|
||||
// A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-bs-original-title`
|
||||
// This will be removed later in favor of a `setContent` method
|
||||
if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) {
|
||||
this._disposePopper()
|
||||
this.tip.remove()
|
||||
this.tip = null
|
||||
}
|
||||
const tip = this._getTipElement()
|
||||
|
||||
const tip = this.getTipElement()
|
||||
const tipId = getUID(this.constructor.NAME)
|
||||
|
||||
tip.setAttribute('id', tipId)
|
||||
this._element.setAttribute('aria-describedby', tipId)
|
||||
|
||||
if (this._config.animation) {
|
||||
tip.classList.add(CLASS_NAME_FADE)
|
||||
}
|
||||
|
||||
const placement = typeof this._config.placement === 'function' ?
|
||||
this._config.placement.call(this, tip, this._element) :
|
||||
this._config.placement
|
||||
|
||||
const attachment = this._getAttachment(placement)
|
||||
this._addAttachmentClass(attachment)
|
||||
this._element.setAttribute('aria-describedby', tip.getAttribute('id'))
|
||||
|
||||
const { container } = this._config
|
||||
Data.set(tip, this.constructor.DATA_KEY, this)
|
||||
|
||||
if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
|
||||
container.append(tip)
|
||||
@@ -263,16 +236,15 @@ class Tooltip extends BaseComponent {
|
||||
if (this._popper) {
|
||||
this._popper.update()
|
||||
} else {
|
||||
const placement = typeof this._config.placement === 'function' ?
|
||||
this._config.placement.call(this, tip, this._element) :
|
||||
this._config.placement
|
||||
const attachment = AttachmentMap[placement.toUpperCase()]
|
||||
this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
|
||||
}
|
||||
|
||||
tip.classList.add(CLASS_NAME_SHOW)
|
||||
|
||||
const customClass = this._resolvePossibleFunction(this._config.customClass)
|
||||
if (customClass) {
|
||||
tip.classList.add(...customClass.split(' '))
|
||||
}
|
||||
|
||||
// If this is a touch-enabled device we add extra
|
||||
// empty mouseover listeners to the body's immediate children;
|
||||
// only needed because of broken event delegation on iOS
|
||||
@@ -284,18 +256,17 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
|
||||
const complete = () => {
|
||||
const prevHoverState = this._hoverState
|
||||
const prevHoverState = this._isHovered
|
||||
|
||||
this._hoverState = null
|
||||
this._isHovered = false
|
||||
EventHandler.trigger(this._element, this.constructor.Event.SHOWN)
|
||||
|
||||
if (prevHoverState === HOVER_STATE_OUT) {
|
||||
this._leave(null, this)
|
||||
if (prevHoverState) {
|
||||
this._leave()
|
||||
}
|
||||
}
|
||||
|
||||
const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)
|
||||
this._queueCallback(complete, this.tip, isAnimated)
|
||||
this._queueCallback(complete, this.tip, this._isAnimated())
|
||||
}
|
||||
|
||||
hide() {
|
||||
@@ -303,28 +274,12 @@ class Tooltip extends BaseComponent {
|
||||
return
|
||||
}
|
||||
|
||||
const tip = this.getTipElement()
|
||||
const complete = () => {
|
||||
if (this._isWithActiveTrigger()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._hoverState !== HOVER_STATE_SHOW) {
|
||||
tip.remove()
|
||||
}
|
||||
|
||||
this._cleanTipClass()
|
||||
this._element.removeAttribute('aria-describedby')
|
||||
EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)
|
||||
|
||||
this._disposePopper()
|
||||
}
|
||||
|
||||
const hideEvent = EventHandler.trigger(this._element, this.constructor.Event.HIDE)
|
||||
if (hideEvent.defaultPrevented) {
|
||||
return
|
||||
}
|
||||
|
||||
const tip = this._getTipElement()
|
||||
tip.classList.remove(CLASS_NAME_SHOW)
|
||||
|
||||
// If this is a touch-enabled device we remove the extra
|
||||
@@ -339,107 +294,116 @@ class Tooltip extends BaseComponent {
|
||||
this._activeTrigger[TRIGGER_FOCUS] = false
|
||||
this._activeTrigger[TRIGGER_HOVER] = false
|
||||
|
||||
const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)
|
||||
this._queueCallback(complete, this.tip, isAnimated)
|
||||
this._hoverState = ''
|
||||
const complete = () => {
|
||||
if (this._isWithActiveTrigger()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!this._isHovered) {
|
||||
tip.remove()
|
||||
}
|
||||
|
||||
this._element.removeAttribute('aria-describedby')
|
||||
EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)
|
||||
|
||||
this._disposePopper()
|
||||
}
|
||||
|
||||
this._queueCallback(complete, this.tip, this._isAnimated())
|
||||
this._isHovered = false
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this._popper !== null) {
|
||||
if (this._popper) {
|
||||
this._popper.update()
|
||||
}
|
||||
}
|
||||
|
||||
// Protected
|
||||
isWithContent() {
|
||||
return Boolean(this.getTitle())
|
||||
_isWithContent() {
|
||||
return Boolean(this._getTitle())
|
||||
}
|
||||
|
||||
getTipElement() {
|
||||
if (this.tip) {
|
||||
return this.tip
|
||||
_getTipElement() {
|
||||
if (!this.tip) {
|
||||
this.tip = this._createTipElement(this._getContentForTemplate())
|
||||
}
|
||||
|
||||
const element = document.createElement('div')
|
||||
element.innerHTML = this._config.template
|
||||
|
||||
const tip = element.children[0]
|
||||
this.setContent(tip)
|
||||
tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
|
||||
|
||||
this.tip = tip
|
||||
return this.tip
|
||||
}
|
||||
|
||||
setContent(tip) {
|
||||
this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER)
|
||||
_createTipElement(content) {
|
||||
const tip = this._getTemplateFactory(content).toHtml()
|
||||
|
||||
// todo: remove this check on v6
|
||||
if (!tip) {
|
||||
return null
|
||||
}
|
||||
|
||||
tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
|
||||
// todo: on v6 the following can be achieved with CSS only
|
||||
tip.classList.add(`bs-${this.constructor.NAME}-auto`)
|
||||
|
||||
const tipId = getUID(this.constructor.NAME).toString()
|
||||
|
||||
tip.setAttribute('id', tipId)
|
||||
|
||||
if (this._isAnimated()) {
|
||||
tip.classList.add(CLASS_NAME_FADE)
|
||||
}
|
||||
|
||||
return tip
|
||||
}
|
||||
|
||||
_sanitizeAndSetContent(template, content, selector) {
|
||||
const templateElement = SelectorEngine.findOne(selector, template)
|
||||
|
||||
if (!content && templateElement) {
|
||||
templateElement.remove()
|
||||
return
|
||||
setContent(content) {
|
||||
let isShown = false
|
||||
if (this.tip) {
|
||||
isShown = this.tip.classList.contains(CLASS_NAME_SHOW)
|
||||
this.tip.remove()
|
||||
this.tip = null
|
||||
}
|
||||
|
||||
// we use append for html objects to maintain js events
|
||||
this.setElementContent(templateElement, content)
|
||||
this._disposePopper()
|
||||
this.tip = this._createTipElement(content)
|
||||
|
||||
if (isShown) {
|
||||
this.show()
|
||||
}
|
||||
}
|
||||
|
||||
setElementContent(element, content) {
|
||||
if (element === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isElement(content)) {
|
||||
content = getElement(content)
|
||||
|
||||
// content is a DOM node or a jQuery
|
||||
if (this._config.html) {
|
||||
if (content.parentNode !== element) {
|
||||
element.innerHTML = ''
|
||||
element.append(content)
|
||||
}
|
||||
} else {
|
||||
element.textContent = content.textContent
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (this._config.html) {
|
||||
if (this._config.sanitize) {
|
||||
content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn)
|
||||
}
|
||||
|
||||
element.innerHTML = content // lgtm [js/xss-through-dom]
|
||||
_getTemplateFactory(content) {
|
||||
if (this._templateFactory) {
|
||||
this._templateFactory.changeContent(content)
|
||||
} else {
|
||||
element.textContent = content
|
||||
this._templateFactory = new TemplateFactory({
|
||||
...this._config,
|
||||
// the `content` var has to be after `this._config`
|
||||
// to override config.content in case of popover
|
||||
content,
|
||||
extraClass: this._resolvePossibleFunction(this._config.customClass)
|
||||
})
|
||||
}
|
||||
|
||||
return this._templateFactory
|
||||
}
|
||||
|
||||
_getContentForTemplate() {
|
||||
return {
|
||||
[SELECTOR_TOOLTIP_INNER]: this._getTitle()
|
||||
}
|
||||
}
|
||||
|
||||
getTitle() {
|
||||
const title = this._element.getAttribute('data-bs-original-title') || this._config.title
|
||||
|
||||
return this._resolvePossibleFunction(title)
|
||||
}
|
||||
|
||||
updateAttachment(attachment) {
|
||||
if (attachment === 'right') {
|
||||
return 'end'
|
||||
}
|
||||
|
||||
if (attachment === 'left') {
|
||||
return 'start'
|
||||
}
|
||||
|
||||
return attachment
|
||||
_getTitle() {
|
||||
return this._config.title
|
||||
}
|
||||
|
||||
// Private
|
||||
_initializeOnDelegatedTarget(event, context) {
|
||||
return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())
|
||||
_initializeOnDelegatedTarget(event) {
|
||||
return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())
|
||||
}
|
||||
|
||||
_isAnimated() {
|
||||
return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))
|
||||
}
|
||||
|
||||
_getOffset() {
|
||||
@@ -456,8 +420,8 @@ class Tooltip extends BaseComponent {
|
||||
return offset
|
||||
}
|
||||
|
||||
_resolvePossibleFunction(content) {
|
||||
return typeof content === 'function' ? content.call(this._element) : content
|
||||
_resolvePossibleFunction(arg) {
|
||||
return typeof arg === 'function' ? arg.call(this._element) : arg
|
||||
}
|
||||
|
||||
_getPopperConfig(attachment) {
|
||||
@@ -487,19 +451,8 @@ class Tooltip extends BaseComponent {
|
||||
options: {
|
||||
element: `.${this.constructor.NAME}-arrow`
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'onChange',
|
||||
enabled: true,
|
||||
phase: 'afterWrite',
|
||||
fn: data => this._handlePopperPlacementChange(data)
|
||||
}
|
||||
],
|
||||
onFirstUpdate: data => {
|
||||
if (data.options.placement !== data.placement) {
|
||||
this._handlePopperPlacementChange(data)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -508,14 +461,6 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
_addAttachmentClass(attachment) {
|
||||
this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`)
|
||||
}
|
||||
|
||||
_getAttachment(placement) {
|
||||
return AttachmentMap[placement.toUpperCase()]
|
||||
}
|
||||
|
||||
_setListeners() {
|
||||
const triggers = this._config.trigger.split(' ')
|
||||
|
||||
@@ -530,8 +475,18 @@ class Tooltip extends BaseComponent {
|
||||
this.constructor.Event.MOUSELEAVE :
|
||||
this.constructor.Event.FOCUSOUT
|
||||
|
||||
EventHandler.on(this._element, eventIn, this._config.selector, event => this._enter(event))
|
||||
EventHandler.on(this._element, eventOut, this._config.selector, event => this._leave(event))
|
||||
EventHandler.on(this._element, eventIn, this._config.selector, event => {
|
||||
const context = this._initializeOnDelegatedTarget(event)
|
||||
context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true
|
||||
context._enter()
|
||||
})
|
||||
EventHandler.on(this._element, eventOut, this._config.selector, event => {
|
||||
const context = this._initializeOnDelegatedTarget(event)
|
||||
context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =
|
||||
context._element.contains(event.relatedTarget)
|
||||
|
||||
context._leave()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,86 +510,55 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
|
||||
_fixTitle() {
|
||||
const title = this._element.getAttribute('title')
|
||||
const originalTitleType = typeof this._element.getAttribute('data-bs-original-title')
|
||||
const title = this._config.originalTitle
|
||||
|
||||
if (title || originalTitleType !== 'string') {
|
||||
this._element.setAttribute('data-bs-original-title', title || '')
|
||||
if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {
|
||||
this._element.setAttribute('aria-label', title)
|
||||
}
|
||||
|
||||
this._element.setAttribute('title', '')
|
||||
if (!title) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!this._element.getAttribute('aria-label') && !this._element.textContent) {
|
||||
this._element.setAttribute('aria-label', title)
|
||||
}
|
||||
|
||||
this._element.removeAttribute('title')
|
||||
}
|
||||
|
||||
_enter(event, context) {
|
||||
context = this._initializeOnDelegatedTarget(event, context)
|
||||
|
||||
if (event) {
|
||||
context._activeTrigger[
|
||||
event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER
|
||||
] = true
|
||||
}
|
||||
|
||||
if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
|
||||
context._hoverState = HOVER_STATE_SHOW
|
||||
_enter() {
|
||||
if (this._getTipElement().classList.contains(CLASS_NAME_SHOW) || this._isHovered) {
|
||||
this._isHovered = true
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(context._timeout)
|
||||
this._isHovered = true
|
||||
|
||||
context._hoverState = HOVER_STATE_SHOW
|
||||
|
||||
if (!context._config.delay || !context._config.delay.show) {
|
||||
context.show()
|
||||
return
|
||||
}
|
||||
|
||||
context._timeout = setTimeout(() => {
|
||||
if (context._hoverState === HOVER_STATE_SHOW) {
|
||||
context.show()
|
||||
this._setTimeout(() => {
|
||||
if (this._isHovered) {
|
||||
this.show()
|
||||
}
|
||||
}, context._config.delay.show)
|
||||
}, this._config.delay.show)
|
||||
}
|
||||
|
||||
_leave(event, context) {
|
||||
context = this._initializeOnDelegatedTarget(event, context)
|
||||
|
||||
if (event) {
|
||||
context._activeTrigger[
|
||||
event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER
|
||||
] = context._element.contains(event.relatedTarget)
|
||||
}
|
||||
|
||||
if (context._isWithActiveTrigger()) {
|
||||
_leave() {
|
||||
if (this._isWithActiveTrigger()) {
|
||||
return
|
||||
}
|
||||
|
||||
clearTimeout(context._timeout)
|
||||
this._isHovered = false
|
||||
|
||||
context._hoverState = HOVER_STATE_OUT
|
||||
|
||||
if (!context._config.delay || !context._config.delay.hide) {
|
||||
context.hide()
|
||||
return
|
||||
}
|
||||
|
||||
context._timeout = setTimeout(() => {
|
||||
if (context._hoverState === HOVER_STATE_OUT) {
|
||||
context.hide()
|
||||
this._setTimeout(() => {
|
||||
if (!this._isHovered) {
|
||||
this.hide()
|
||||
}
|
||||
}, context._config.delay.hide)
|
||||
}, this._config.delay.hide)
|
||||
}
|
||||
|
||||
_setTimeout(handler, timeout) {
|
||||
clearTimeout(this._timeout)
|
||||
this._timeout = setTimeout(handler, timeout)
|
||||
}
|
||||
|
||||
_isWithActiveTrigger() {
|
||||
for (const trigger in this._activeTrigger) {
|
||||
if (this._activeTrigger[trigger]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return Object.values(this._activeTrigger).includes(true)
|
||||
}
|
||||
|
||||
_getConfig(config) {
|
||||
@@ -661,6 +585,8 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
config.originalTitle = this._element.getAttribute('title') || ''
|
||||
config.title = this._resolvePossibleFunction(config.title) || config.originalTitle
|
||||
if (typeof config.title === 'number') {
|
||||
config.title = config.title.toString()
|
||||
}
|
||||
@@ -670,11 +596,6 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
|
||||
typeCheckConfig(NAME, config, this.constructor.DefaultType)
|
||||
|
||||
if (config.sanitize) {
|
||||
config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -693,33 +614,6 @@ class Tooltip extends BaseComponent {
|
||||
return config
|
||||
}
|
||||
|
||||
_cleanTipClass() {
|
||||
const tip = this.getTipElement()
|
||||
const basicClassPrefixRegex = new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`, 'g')
|
||||
const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex)
|
||||
if (tabClass !== null && tabClass.length > 0) {
|
||||
for (const tClass of tabClass.map(token => token.trim())) {
|
||||
tip.classList.remove(tClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_getBasicClassPrefix() {
|
||||
return CLASS_PREFIX
|
||||
}
|
||||
|
||||
_handlePopperPlacementChange(popperData) {
|
||||
const { state } = popperData
|
||||
|
||||
if (!state) {
|
||||
return
|
||||
}
|
||||
|
||||
this.tip = state.elements.popper
|
||||
this._cleanTipClass()
|
||||
this._addAttachmentClass(this._getAttachment(state.placement))
|
||||
}
|
||||
|
||||
_disposePopper() {
|
||||
if (this._popper) {
|
||||
this._popper.destroy()
|
||||
@@ -733,13 +627,15 @@ class Tooltip extends BaseComponent {
|
||||
return this.each(function () {
|
||||
const data = Tooltip.getOrCreateInstance(this, config)
|
||||
|
||||
if (typeof config === 'string') {
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
|
||||
data[config]()
|
||||
if (typeof config !== 'string') {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof data[config] === 'undefined') {
|
||||
throw new TypeError(`No method named "${config}"`)
|
||||
}
|
||||
|
||||
data[config]()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v5.1.3): util/template-factory.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { DefaultAllowlist, sanitizeHtml } from './sanitizer'
|
||||
import { getElement, isElement, typeCheckConfig } from '../util/index'
|
||||
import SelectorEngine from '../dom/selector-engine'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
const NAME = 'TemplateFactory'
|
||||
|
||||
const Default = {
|
||||
extraClass: '',
|
||||
template: '<div></div>',
|
||||
content: {}, // { selector : text , selector2 : text2 , }
|
||||
html: false,
|
||||
sanitize: true,
|
||||
sanitizeFn: null,
|
||||
allowList: DefaultAllowlist
|
||||
}
|
||||
|
||||
const DefaultType = {
|
||||
extraClass: '(string|function)',
|
||||
template: 'string',
|
||||
content: 'object',
|
||||
html: 'boolean',
|
||||
sanitize: 'boolean',
|
||||
sanitizeFn: '(null|function)',
|
||||
allowList: 'object'
|
||||
}
|
||||
|
||||
const DefaultContentType = {
|
||||
selector: '(string|element)',
|
||||
entry: '(string|element|function|null)'
|
||||
}
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
|
||||
class TemplateFactory {
|
||||
constructor(config) {
|
||||
this._config = this._getConfig(config)
|
||||
}
|
||||
|
||||
// Getters
|
||||
static get NAME() {
|
||||
return NAME
|
||||
}
|
||||
|
||||
static get Default() {
|
||||
return Default
|
||||
}
|
||||
|
||||
// Public
|
||||
getContent() {
|
||||
return Object.values(this._config.content)
|
||||
.map(config => this._resolvePossibleFunction(config))
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
hasContent() {
|
||||
return this.getContent().length > 0
|
||||
}
|
||||
|
||||
changeContent(content) {
|
||||
this._checkContent(content)
|
||||
this._config.content = { ...this._config.content, ...content }
|
||||
return this
|
||||
}
|
||||
|
||||
toHtml() {
|
||||
const templateWrapper = document.createElement('div')
|
||||
templateWrapper.innerHTML = this._maybeSanitize(this._config.template)
|
||||
|
||||
for (const [selector, text] of Object.entries(this._config.content)) {
|
||||
this._setContent(templateWrapper, text, selector)
|
||||
}
|
||||
|
||||
const template = templateWrapper.children[0]
|
||||
const extraClass = this._resolvePossibleFunction(this._config.extraClass)
|
||||
|
||||
if (extraClass) {
|
||||
template.classList.add(...extraClass.split(' '))
|
||||
}
|
||||
|
||||
return template
|
||||
}
|
||||
|
||||
// Private
|
||||
_getConfig(config) {
|
||||
config = {
|
||||
...Default,
|
||||
...(typeof config === 'object' ? config : {})
|
||||
}
|
||||
|
||||
typeCheckConfig(NAME, config, DefaultType)
|
||||
this._checkContent(config.content)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
_checkContent(arg) {
|
||||
for (const [selector, content] of Object.entries(arg)) {
|
||||
typeCheckConfig(NAME, { selector, entry: content }, DefaultContentType)
|
||||
}
|
||||
}
|
||||
|
||||
_setContent(template, content, selector) {
|
||||
const templateElement = SelectorEngine.findOne(selector, template)
|
||||
|
||||
if (!templateElement) {
|
||||
return
|
||||
}
|
||||
|
||||
content = this._resolvePossibleFunction(content)
|
||||
|
||||
if (!content) {
|
||||
templateElement.remove()
|
||||
return
|
||||
}
|
||||
|
||||
if (isElement(content)) {
|
||||
this._putElementInTemplate(getElement(content), templateElement)
|
||||
return
|
||||
}
|
||||
|
||||
if (this._config.html) {
|
||||
templateElement.innerHTML = this._maybeSanitize(content)
|
||||
return
|
||||
}
|
||||
|
||||
templateElement.textContent = content
|
||||
}
|
||||
|
||||
_maybeSanitize(arg) {
|
||||
return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg
|
||||
}
|
||||
|
||||
_resolvePossibleFunction(arg) {
|
||||
return typeof arg === 'function' ? arg(this) : arg
|
||||
}
|
||||
|
||||
_putElementInTemplate(element, templateElement) {
|
||||
if (this._config.html) {
|
||||
templateElement.innerHTML = ''
|
||||
templateElement.append(element)
|
||||
return
|
||||
}
|
||||
|
||||
templateElement.textContent = element.textContent
|
||||
}
|
||||
}
|
||||
|
||||
export default TemplateFactory
|
||||
+1
-1
@@ -35,7 +35,7 @@ Currently we're aiming for at least 90% test coverage for our code. To ensure yo
|
||||
describe('getInstance', () => {
|
||||
it('should return null if there is no instance', () => {
|
||||
// Make assertion
|
||||
expect(Tab.getInstance(fixtureEl)).toEqual(null)
|
||||
expect(Tab.getInstance(fixtureEl)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return this instance', () => {
|
||||
|
||||
@@ -74,9 +74,6 @@ const browsers = {
|
||||
}
|
||||
}
|
||||
|
||||
const browsersKeys = Object.keys(browsers)
|
||||
|
||||
module.exports = {
|
||||
browsers,
|
||||
browsersKeys
|
||||
browsers
|
||||
}
|
||||
|
||||
@@ -8,11 +8,7 @@ const { babel } = require('@rollup/plugin-babel')
|
||||
const istanbul = require('rollup-plugin-istanbul')
|
||||
const { nodeResolve } = require('@rollup/plugin-node-resolve')
|
||||
const replace = require('@rollup/plugin-replace')
|
||||
|
||||
const {
|
||||
browsers,
|
||||
browsersKeys
|
||||
} = require('./browsers')
|
||||
const { browsers } = require('./browsers')
|
||||
|
||||
const ENV = process.env
|
||||
const BROWSERSTACK = Boolean(ENV.BROWSERSTACK)
|
||||
@@ -115,7 +111,7 @@ if (BROWSERSTACK) {
|
||||
}
|
||||
plugins.push('karma-browserstack-launcher', 'karma-jasmine-html-reporter')
|
||||
conf.customLaunchers = browsers
|
||||
conf.browsers = browsersKeys
|
||||
conf.browsers = Object.keys(browsers)
|
||||
reporters.push('BrowserStack', 'kjhtml')
|
||||
} else if (JQUERY_TEST) {
|
||||
frameworks.push('detectBrowsers')
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('Alert', () => {
|
||||
})
|
||||
|
||||
it('should return version', () => {
|
||||
expect(typeof Alert.VERSION).toEqual('string')
|
||||
expect(Alert.VERSION).toEqual(jasmine.any(String))
|
||||
})
|
||||
|
||||
describe('DATA_KEY', () => {
|
||||
@@ -45,7 +45,7 @@ describe('Alert', () => {
|
||||
const button = document.querySelector('button')
|
||||
|
||||
button.click()
|
||||
expect(document.querySelectorAll('.alert').length).toEqual(0)
|
||||
expect(document.querySelectorAll('.alert')).toHaveSize(0)
|
||||
})
|
||||
|
||||
it('should close an alert without instantiating it manually with the parent selector', () => {
|
||||
@@ -58,7 +58,7 @@ describe('Alert', () => {
|
||||
const button = document.querySelector('button')
|
||||
|
||||
button.click()
|
||||
expect(document.querySelectorAll('.alert').length).toEqual(0)
|
||||
expect(document.querySelectorAll('.alert')).toHaveSize(0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -71,7 +71,7 @@ describe('Alert', () => {
|
||||
const alert = new Alert(alertEl)
|
||||
|
||||
alertEl.addEventListener('closed.bs.alert', () => {
|
||||
expect(document.querySelectorAll('.alert').length).toEqual(0)
|
||||
expect(document.querySelectorAll('.alert')).toHaveSize(0)
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
done()
|
||||
})
|
||||
@@ -90,7 +90,7 @@ describe('Alert', () => {
|
||||
})
|
||||
|
||||
alertEl.addEventListener('closed.bs.alert', () => {
|
||||
expect(document.querySelectorAll('.alert').length).toEqual(0)
|
||||
expect(document.querySelectorAll('.alert')).toHaveSize(0)
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -179,6 +179,34 @@ describe('Alert', () => {
|
||||
expect(Alert.getInstance(alertEl)).not.toBeNull()
|
||||
expect(fixtureEl.querySelector('.alert')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should throw an error on undefined method', () => {
|
||||
fixtureEl.innerHTML = '<div></div>'
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
const action = 'undefinedMethod'
|
||||
|
||||
jQueryMock.fn.alert = Alert.jQueryInterface
|
||||
jQueryMock.elements = [div]
|
||||
|
||||
expect(() => {
|
||||
jQueryMock.fn.alert.call(jQueryMock, action)
|
||||
}).toThrowError(TypeError, `No method named "${action}"`)
|
||||
})
|
||||
|
||||
it('should throw an error on protected method', () => {
|
||||
fixtureEl.innerHTML = '<div></div>'
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
const action = '_getConfig'
|
||||
|
||||
jQueryMock.fn.alert = Alert.jQueryInterface
|
||||
jQueryMock.elements = [div]
|
||||
|
||||
expect(() => {
|
||||
jQueryMock.fn.alert.call(jQueryMock, action)
|
||||
}).toThrowError(TypeError, `No method named "${action}"`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getInstance', () => {
|
||||
@@ -197,7 +225,7 @@ describe('Alert', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Alert.getInstance(div)).toEqual(null)
|
||||
expect(Alert.getInstance(div)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -218,7 +246,7 @@ describe('Alert', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Alert.getInstance(div)).toEqual(null)
|
||||
expect(Alert.getInstance(div)).toBeNull()
|
||||
expect(Alert.getOrCreateInstance(div)).toBeInstanceOf(Alert)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('Base Component', () => {
|
||||
describe('Static Methods', () => {
|
||||
describe('VERSION', () => {
|
||||
it('should return version', () => {
|
||||
expect(typeof DummyClass.VERSION).toEqual('string')
|
||||
expect(DummyClass.VERSION).toEqual(jasmine.any(String))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -48,6 +48,13 @@ describe('Base Component', () => {
|
||||
})
|
||||
|
||||
describe('NAME', () => {
|
||||
it('should throw an Error if it is not initialized', () => {
|
||||
expect(() => {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
BaseComponent.NAME
|
||||
}).toThrowError(Error)
|
||||
})
|
||||
|
||||
it('should return plugin NAME', () => {
|
||||
expect(DummyClass.NAME).toEqual(name)
|
||||
})
|
||||
@@ -59,6 +66,7 @@ describe('Base Component', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Public Methods', () => {
|
||||
describe('constructor', () => {
|
||||
it('should accept element, either passed as a CSS selector or DOM element', () => {
|
||||
@@ -74,7 +82,19 @@ describe('Base Component', () => {
|
||||
expect(elInstance._element).toEqual(el)
|
||||
expect(selectorInstance._element).toEqual(fixtureEl.querySelector('#bar'))
|
||||
})
|
||||
|
||||
it('should not initialize and add element record to Data (caching), if argument `element` is not an HTML element', () => {
|
||||
fixtureEl.innerHTML = ''
|
||||
|
||||
const el = fixtureEl.querySelector('#foo')
|
||||
const elInstance = new DummyClass(el)
|
||||
const selectorInstance = new DummyClass('#bar')
|
||||
|
||||
expect(elInstance._element).not.toBeDefined()
|
||||
expect(selectorInstance._element).not.toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('dispose', () => {
|
||||
it('should dispose an component', () => {
|
||||
createInstance()
|
||||
@@ -123,9 +143,10 @@ describe('Base Component', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(DummyClass.getInstance(div)).toEqual(null)
|
||||
expect(DummyClass.getInstance(div)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getOrCreateInstance', () => {
|
||||
it('should return an instance', () => {
|
||||
createInstance()
|
||||
@@ -139,7 +160,7 @@ describe('Base Component', () => {
|
||||
fixtureEl.innerHTML = '<div id="foo"></div>'
|
||||
element = fixtureEl.querySelector('#foo')
|
||||
|
||||
expect(DummyClass.getInstance(element)).toEqual(null)
|
||||
expect(DummyClass.getInstance(element)).toBeNull()
|
||||
expect(DummyClass.getOrCreateInstance(element)).toBeInstanceOf(DummyClass)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -45,19 +45,19 @@ describe('Button', () => {
|
||||
const divTest = fixtureEl.querySelector('.test')
|
||||
const btnTestParent = fixtureEl.querySelector('.testParent')
|
||||
|
||||
expect(btn.classList.contains('active')).toEqual(false)
|
||||
expect(btn).not.toHaveClass('active')
|
||||
|
||||
btn.click()
|
||||
|
||||
expect(btn.classList.contains('active')).toEqual(true)
|
||||
expect(btn).toHaveClass('active')
|
||||
|
||||
btn.click()
|
||||
|
||||
expect(btn.classList.contains('active')).toEqual(false)
|
||||
expect(btn).not.toHaveClass('active')
|
||||
|
||||
divTest.click()
|
||||
|
||||
expect(btnTestParent.classList.contains('active')).toEqual(true)
|
||||
expect(btnTestParent).toHaveClass('active')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -69,12 +69,12 @@ describe('Button', () => {
|
||||
const button = new Button(btnEl)
|
||||
|
||||
expect(btnEl.getAttribute('aria-pressed')).toEqual('false')
|
||||
expect(btnEl.classList.contains('active')).toEqual(false)
|
||||
expect(btnEl).not.toHaveClass('active')
|
||||
|
||||
button.toggle()
|
||||
|
||||
expect(btnEl.getAttribute('aria-pressed')).toEqual('true')
|
||||
expect(btnEl.classList.contains('active')).toEqual(true)
|
||||
expect(btnEl).toHaveClass('active')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -121,7 +121,7 @@ describe('Button', () => {
|
||||
jQueryMock.fn.button.call(jQueryMock, 'toggle')
|
||||
|
||||
expect(Button.getInstance(btnEl)).not.toBeNull()
|
||||
expect(btnEl.classList.contains('active')).toEqual(true)
|
||||
expect(btnEl).toHaveClass('active')
|
||||
})
|
||||
|
||||
it('should just create a button instance without calling toggle', () => {
|
||||
@@ -135,7 +135,7 @@ describe('Button', () => {
|
||||
jQueryMock.fn.button.call(jQueryMock)
|
||||
|
||||
expect(Button.getInstance(btnEl)).not.toBeNull()
|
||||
expect(btnEl.classList.contains('active')).toEqual(false)
|
||||
expect(btnEl).not.toHaveClass('active')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -155,7 +155,7 @@ describe('Button', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Button.getInstance(div)).toEqual(null)
|
||||
expect(Button.getInstance(div)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -176,7 +176,7 @@ describe('Button', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Button.getInstance(div)).toEqual(null)
|
||||
expect(Button.getInstance(div)).toBeNull()
|
||||
expect(Button.getOrCreateInstance(div)).toBeInstanceOf(Button)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -143,7 +143,7 @@ describe('Carousel', () => {
|
||||
|
||||
carouselEl.addEventListener('keydown', event => {
|
||||
expect(carousel._keydown).toHaveBeenCalled()
|
||||
expect(event.defaultPrevented).toEqual(false)
|
||||
expect(event.defaultPrevented).toBeFalse()
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -235,9 +235,7 @@ describe('Carousel', () => {
|
||||
|
||||
const carouselEl = fixtureEl.querySelector('#myCarousel')
|
||||
const carousel = new Carousel(carouselEl, { wrap: true })
|
||||
const getActiveId = () => {
|
||||
return carouselEl.querySelector('.carousel-item.active').getAttribute('id')
|
||||
}
|
||||
const getActiveId = () => carouselEl.querySelector('.carousel-item.active').getAttribute('id')
|
||||
|
||||
carouselEl.addEventListener('slid.bs.carousel', event => {
|
||||
const activeId = getActiveId()
|
||||
@@ -285,7 +283,7 @@ describe('Carousel', () => {
|
||||
carousel.prev()
|
||||
|
||||
setTimeout(() => {
|
||||
expect(firstElement.classList.contains('active')).toEqual(true)
|
||||
expect(firstElement).toHaveClass('active')
|
||||
done()
|
||||
}, 10)
|
||||
})
|
||||
@@ -368,7 +366,7 @@ describe('Carousel', () => {
|
||||
spyOn(carousel, '_slide').and.callThrough()
|
||||
|
||||
carouselEl.addEventListener('slid.bs.carousel', event => {
|
||||
expect(item.classList.contains('active')).toEqual(true)
|
||||
expect(item).toHaveClass('active')
|
||||
expect(carousel._slide).toHaveBeenCalledWith('right')
|
||||
expect(event.direction).toEqual('right')
|
||||
stylesCarousel.remove()
|
||||
@@ -413,7 +411,7 @@ describe('Carousel', () => {
|
||||
spyOn(carousel, '_slide').and.callThrough()
|
||||
|
||||
carouselEl.addEventListener('slid.bs.carousel', event => {
|
||||
expect(item.classList.contains('active')).toEqual(false)
|
||||
expect(item).not.toHaveClass('active')
|
||||
expect(carousel._slide).toHaveBeenCalledWith('left')
|
||||
expect(event.direction).toEqual('left')
|
||||
stylesCarousel.remove()
|
||||
@@ -453,7 +451,7 @@ describe('Carousel', () => {
|
||||
spyOn(carousel, '_slide').and.callThrough()
|
||||
|
||||
carouselEl.addEventListener('slid.bs.carousel', event => {
|
||||
expect(item.classList.contains('active')).toEqual(true)
|
||||
expect(item).toHaveClass('active')
|
||||
expect(carousel._slide).toHaveBeenCalledWith('right')
|
||||
expect(event.direction).toEqual('right')
|
||||
delete document.documentElement.ontouchstart
|
||||
@@ -492,7 +490,7 @@ describe('Carousel', () => {
|
||||
spyOn(carousel, '_slide').and.callThrough()
|
||||
|
||||
carouselEl.addEventListener('slid.bs.carousel', event => {
|
||||
expect(item.classList.contains('active')).toEqual(false)
|
||||
expect(item).not.toHaveClass('active')
|
||||
expect(carousel._slide).toHaveBeenCalledWith('left')
|
||||
expect(event.direction).toEqual('left')
|
||||
delete document.documentElement.ontouchstart
|
||||
@@ -632,7 +630,7 @@ describe('Carousel', () => {
|
||||
|
||||
const doneTest = () => {
|
||||
setTimeout(() => {
|
||||
expect(slidEvent).toEqual(false)
|
||||
expect(slidEvent).toBeFalse()
|
||||
done()
|
||||
}, 20)
|
||||
}
|
||||
@@ -665,7 +663,7 @@ describe('Carousel', () => {
|
||||
|
||||
const onSlide = event => {
|
||||
expect(event.direction).toEqual('left')
|
||||
expect(event.relatedTarget.classList.contains('carousel-item')).toEqual(true)
|
||||
expect(event.relatedTarget).toHaveClass('carousel-item')
|
||||
expect(event.from).toEqual(0)
|
||||
expect(event.to).toEqual(1)
|
||||
|
||||
@@ -700,7 +698,7 @@ describe('Carousel', () => {
|
||||
|
||||
const onSlid = event => {
|
||||
expect(event.direction).toEqual('left')
|
||||
expect(event.relatedTarget.classList.contains('carousel-item')).toEqual(true)
|
||||
expect(event.relatedTarget).toHaveClass('carousel-item')
|
||||
expect(event.from).toEqual(0)
|
||||
expect(event.to).toEqual(1)
|
||||
|
||||
@@ -761,9 +759,9 @@ describe('Carousel', () => {
|
||||
const carousel = new Carousel(carouselEl)
|
||||
|
||||
carouselEl.addEventListener('slid.bs.carousel', () => {
|
||||
expect(firstIndicator.classList.contains('active')).toEqual(false)
|
||||
expect(firstIndicator.hasAttribute('aria-current')).toEqual(false)
|
||||
expect(secondIndicator.classList.contains('active')).toEqual(true)
|
||||
expect(firstIndicator).not.toHaveClass('active')
|
||||
expect(firstIndicator.hasAttribute('aria-current')).toBeFalse()
|
||||
expect(secondIndicator).toHaveClass('active')
|
||||
expect(secondIndicator.getAttribute('aria-current')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -859,7 +857,7 @@ describe('Carousel', () => {
|
||||
|
||||
expect(carousel.cycle).toHaveBeenCalledWith(true)
|
||||
expect(window.clearInterval).toHaveBeenCalled()
|
||||
expect(carousel._isPaused).toEqual(true)
|
||||
expect(carousel._isPaused).toBeTrue()
|
||||
})
|
||||
|
||||
it('should not call cycle if nothing is in transition', () => {
|
||||
@@ -885,7 +883,7 @@ describe('Carousel', () => {
|
||||
|
||||
expect(carousel.cycle).not.toHaveBeenCalled()
|
||||
expect(window.clearInterval).toHaveBeenCalled()
|
||||
expect(carousel._isPaused).toEqual(true)
|
||||
expect(carousel._isPaused).toBeTrue()
|
||||
})
|
||||
|
||||
it('should not set is paused at true if an event is passed', () => {
|
||||
@@ -910,7 +908,7 @@ describe('Carousel', () => {
|
||||
carousel.pause(event)
|
||||
|
||||
expect(window.clearInterval).toHaveBeenCalled()
|
||||
expect(carousel._isPaused).toEqual(false)
|
||||
expect(carousel._isPaused).toBeFalse()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1153,6 +1151,7 @@ describe('Carousel', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('rtl function', () => {
|
||||
it('"_directionToOrder" and "_orderToDirection" must return the right results', () => {
|
||||
fixtureEl.innerHTML = '<div></div>'
|
||||
@@ -1175,7 +1174,7 @@ describe('Carousel', () => {
|
||||
|
||||
const carouselEl = fixtureEl.querySelector('div')
|
||||
const carousel = new Carousel(carouselEl, {})
|
||||
expect(isRTL()).toEqual(true, 'rtl has to be true')
|
||||
expect(isRTL()).toBeTrue()
|
||||
|
||||
expect(carousel._directionToOrder('left')).toEqual('prev')
|
||||
expect(carousel._directionToOrder('prev')).toEqual('prev')
|
||||
@@ -1292,7 +1291,7 @@ describe('Carousel', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Carousel.getInstance(div)).toEqual(null)
|
||||
expect(Carousel.getInstance(div)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1313,7 +1312,7 @@ describe('Carousel', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Carousel.getInstance(div)).toEqual(null)
|
||||
expect(Carousel.getInstance(div)).toBeNull()
|
||||
expect(Carousel.getOrCreateInstance(div)).toBeInstanceOf(Carousel)
|
||||
})
|
||||
|
||||
@@ -1322,7 +1321,7 @@ describe('Carousel', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Carousel.getInstance(div)).toEqual(null)
|
||||
expect(Carousel.getInstance(div)).toBeNull()
|
||||
const carousel = Carousel.getOrCreateInstance(div, {
|
||||
interval: 1
|
||||
})
|
||||
@@ -1431,7 +1430,7 @@ describe('Carousel', () => {
|
||||
' <div class="carousel-item">item 3</div>',
|
||||
' </div>',
|
||||
' <button class="carousel-control-prev" data-bs-target="#myCarousel" type="button" data-bs-slide="prev"></button>',
|
||||
' <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></div>',
|
||||
' <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></button>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -1441,7 +1440,7 @@ describe('Carousel', () => {
|
||||
next.click()
|
||||
|
||||
setTimeout(() => {
|
||||
expect(item2.classList.contains('active')).toEqual(true)
|
||||
expect(item2).toHaveClass('active')
|
||||
done()
|
||||
}, 10)
|
||||
})
|
||||
@@ -1454,8 +1453,8 @@ describe('Carousel', () => {
|
||||
' <div id="item2" class="carousel-item">item 2</div>',
|
||||
' <div class="carousel-item">item 3</div>',
|
||||
' </div>',
|
||||
' <a class="carousel-control-prev" href="#myCarousel" role="button" data-bs-slide="prev"></button>',
|
||||
' <a id="next" class="carousel-control-next" href="#myCarousel" role="button" data-bs-slide="next"></div>',
|
||||
' <a class="carousel-control-prev" href="#myCarousel" role="button" data-bs-slide="prev"></a>',
|
||||
' <a id="next" class="carousel-control-next" href="#myCarousel" role="button" data-bs-slide="next"></a>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -1465,7 +1464,7 @@ describe('Carousel', () => {
|
||||
next.click()
|
||||
|
||||
setTimeout(() => {
|
||||
expect(item2.classList.contains('active')).toEqual(true)
|
||||
expect(item2).toHaveClass('active')
|
||||
done()
|
||||
}, 10)
|
||||
})
|
||||
@@ -1488,7 +1487,7 @@ describe('Carousel', () => {
|
||||
next.click()
|
||||
|
||||
setTimeout(() => {
|
||||
expect(item2.classList.contains('active')).toEqual(true)
|
||||
expect(item2).toHaveClass('active')
|
||||
done()
|
||||
}, 10)
|
||||
})
|
||||
@@ -1521,8 +1520,8 @@ describe('Carousel', () => {
|
||||
' <div id="item2" class="carousel-item">item 2</div>',
|
||||
' <div class="carousel-item">item 3</div>',
|
||||
' </div>',
|
||||
' <button class="carousel-control-prev" data-bs-target="#myCarousel" type="button" data-bs-slide="prev"></div>',
|
||||
' <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></div>',
|
||||
' <button class="carousel-control-prev" data-bs-target="#myCarousel" type="button" data-bs-slide="prev"></button>',
|
||||
' <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></button>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
|
||||
+114
-113
@@ -159,8 +159,8 @@ describe('Collapse', () => {
|
||||
}))
|
||||
|
||||
collapseEl2.addEventListener('shown.bs.collapse', () => {
|
||||
expect(collapseEl2.classList.contains('show')).toEqual(true)
|
||||
expect(collapseEl1.classList.contains('show')).toEqual(false)
|
||||
expect(collapseEl2).toHaveClass('show')
|
||||
expect(collapseEl1).not.toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -212,7 +212,7 @@ describe('Collapse', () => {
|
||||
expect(collapseEl.style.height).toEqual('0px')
|
||||
})
|
||||
collapseEl.addEventListener('shown.bs.collapse', () => {
|
||||
expect(collapseEl.classList.contains('show')).toEqual(true)
|
||||
expect(collapseEl).toHaveClass('show')
|
||||
expect(collapseEl.style.height).toEqual('')
|
||||
done()
|
||||
})
|
||||
@@ -232,7 +232,7 @@ describe('Collapse', () => {
|
||||
expect(collapseEl.style.width).toEqual('0px')
|
||||
})
|
||||
collapseEl.addEventListener('shown.bs.collapse', () => {
|
||||
expect(collapseEl.classList.contains('show')).toEqual(true)
|
||||
expect(collapseEl).toHaveClass('show')
|
||||
expect(collapseEl.style.width).toEqual('')
|
||||
done()
|
||||
})
|
||||
@@ -257,8 +257,8 @@ describe('Collapse', () => {
|
||||
})
|
||||
|
||||
el1.addEventListener('shown.bs.collapse', () => {
|
||||
expect(el1.classList.contains('show')).toEqual(true)
|
||||
expect(el2.classList.contains('show')).toEqual(true)
|
||||
expect(el1).toHaveClass('show')
|
||||
expect(el2).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -268,31 +268,31 @@ describe('Collapse', () => {
|
||||
it('should be able to handle toggling of other children siblings', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="parentGroup" class="accordion">',
|
||||
' <div id="parentHeader" class="accordion-header">',
|
||||
' <button data-bs-target="#parentContent" data-bs-toggle="collapse" role="button" class="accordion-toggle">Parent</button>',
|
||||
' </div>',
|
||||
' <div id="parentContent" class="accordion-collapse collapse" aria-labelledby="parentHeader" data-bs-parent="#parentGroup">',
|
||||
' <div class="accordion-body">',
|
||||
' <div id="childGroup" class="accordion">',
|
||||
' <div class="accordion-item">',
|
||||
' <div id="childHeader1" class="accordion-header">',
|
||||
' <button data-bs-target="#childContent1" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 1</button>',
|
||||
' </div>',
|
||||
' <div id="childContent1" class="accordion-collapse collapse" aria-labelledby="childHeader1" data-bs-parent="#childGroup">',
|
||||
' <div>content</div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="accordion-item">',
|
||||
' <div id="childHeader2" class="accordion-header">',
|
||||
' <button data-bs-target="#childContent2" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 2</button>',
|
||||
' </div>',
|
||||
' <div id="childContent2" class="accordion-collapse collapse" aria-labelledby="childHeader2" data-bs-parent="#childGroup">',
|
||||
' <div>content</div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div id="parentHeader" class="accordion-header">',
|
||||
' <button data-bs-target="#parentContent" data-bs-toggle="collapse" role="button" class="accordion-toggle">Parent</button>',
|
||||
' </div>',
|
||||
' <div id="parentContent" class="accordion-collapse collapse" aria-labelledby="parentHeader" data-bs-parent="#parentGroup">',
|
||||
' <div class="accordion-body">',
|
||||
' <div id="childGroup" class="accordion">',
|
||||
' <div class="accordion-item">',
|
||||
' <div id="childHeader1" class="accordion-header">',
|
||||
' <button data-bs-target="#childContent1" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 1</button>',
|
||||
' </div>',
|
||||
' <div id="childContent1" class="accordion-collapse collapse" aria-labelledby="childHeader1" data-bs-parent="#childGroup">',
|
||||
' <div>content</div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="accordion-item">',
|
||||
' <div id="childHeader2" class="accordion-header">',
|
||||
' <button data-bs-target="#childContent2" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 2</button>',
|
||||
' </div>',
|
||||
' <div id="childContent2" class="accordion-collapse collapse" aria-labelledby="childHeader2" data-bs-parent="#childGroup">',
|
||||
' <div>content</div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -307,46 +307,47 @@ describe('Collapse', () => {
|
||||
const childCollapseEl2 = el('#childContent2')
|
||||
|
||||
parentCollapseEl.addEventListener('shown.bs.collapse', () => {
|
||||
expect(parentCollapseEl.classList.contains('show')).toEqual(true)
|
||||
expect(parentCollapseEl).toHaveClass('show')
|
||||
childBtn1.click()
|
||||
})
|
||||
childCollapseEl1.addEventListener('shown.bs.collapse', () => {
|
||||
expect(childCollapseEl1.classList.contains('show')).toEqual(true)
|
||||
expect(childCollapseEl1).toHaveClass('show')
|
||||
childBtn2.click()
|
||||
})
|
||||
childCollapseEl2.addEventListener('shown.bs.collapse', () => {
|
||||
expect(childCollapseEl2.classList.contains('show')).toEqual(true)
|
||||
expect(childCollapseEl1.classList.contains('show')).toEqual(false)
|
||||
expect(childCollapseEl2).toHaveClass('show')
|
||||
expect(childCollapseEl1).not.toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
parentBtn.click()
|
||||
})
|
||||
|
||||
it('should not change tab tabpanels descendants on accordion', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="accordion" id="accordionExample">',
|
||||
' <div class="accordion-item">',
|
||||
' <h2 class="accordion-header" id="headingOne">',
|
||||
' <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">',
|
||||
' Accordion Item #1',
|
||||
' </button>',
|
||||
' </h2>',
|
||||
' <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">',
|
||||
' <div class="accordion-body">',
|
||||
' <nav>',
|
||||
' <div class="nav nav-tabs" id="nav-tab" role="tablist">',
|
||||
' <button class="nav-link active" id="nav-home-tab" data-bs-toggle="tab" data-bs-target="#nav-home" type="button" role="tab" aria-controls="nav-home" aria-selected="true">Home</button>',
|
||||
' <button class="nav-link" id="nav-profile-tab" data-bs-toggle="tab" data-bs-target="#nav-profile" type="button" role="tab" aria-controls="nav-profile" aria-selected="false">Profile</button>',
|
||||
' </div>',
|
||||
' </nav>',
|
||||
' <div class="tab-content" id="nav-tabContent">',
|
||||
' <div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">Home</div>',
|
||||
' <div class="tab-pane fade" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">Profile</div>',
|
||||
' </div>',
|
||||
' <div class="accordion-item">',
|
||||
' <h2 class="accordion-header" id="headingOne">',
|
||||
' <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">',
|
||||
' Accordion Item #1',
|
||||
' </button>',
|
||||
' </h2>',
|
||||
' <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">',
|
||||
' <div class="accordion-body">',
|
||||
' <nav>',
|
||||
' <div class="nav nav-tabs" id="nav-tab" role="tablist">',
|
||||
' <button class="nav-link active" id="nav-home-tab" data-bs-toggle="tab" data-bs-target="#nav-home" type="button" role="tab" aria-controls="nav-home" aria-selected="true">Home</button>',
|
||||
' <button class="nav-link" id="nav-profile-tab" data-bs-toggle="tab" data-bs-target="#nav-profile" type="button" role="tab" aria-controls="nav-profile" aria-selected="false">Profile</button>',
|
||||
' </div>',
|
||||
' </nav>',
|
||||
' <div class="tab-content" id="nav-tabContent">',
|
||||
' <div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">Home</div>',
|
||||
' <div class="tab-pane fade" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">Profile</div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' </div>'
|
||||
' </div>',
|
||||
' </div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const el = fixtureEl.querySelector('#collapseOne')
|
||||
@@ -359,7 +360,7 @@ describe('Collapse', () => {
|
||||
})
|
||||
|
||||
el.addEventListener('shown.bs.collapse', () => {
|
||||
expect(activeTabPane.classList.contains('show')).toEqual(true)
|
||||
expect(activeTabPane).toHaveClass('show')
|
||||
times++
|
||||
if (times === 2) {
|
||||
done()
|
||||
@@ -440,7 +441,7 @@ describe('Collapse', () => {
|
||||
})
|
||||
|
||||
collapseEl.addEventListener('hidden.bs.collapse', () => {
|
||||
expect(collapseEl.classList.contains('show')).toEqual(false)
|
||||
expect(collapseEl).not.toHaveClass('show')
|
||||
expect(collapseEl.style.height).toEqual('')
|
||||
done()
|
||||
})
|
||||
@@ -489,7 +490,7 @@ describe('Collapse', () => {
|
||||
|
||||
collapse.dispose()
|
||||
|
||||
expect(Collapse.getInstance(collapseEl)).toEqual(null)
|
||||
expect(Collapse.getInstance(collapseEl)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -508,8 +509,8 @@ describe('Collapse', () => {
|
||||
spyOn(Event.prototype, 'preventDefault').and.callThrough()
|
||||
|
||||
triggerEl.addEventListener('click', event => {
|
||||
expect(event.target.isEqualNode(nestedTriggerEl)).toEqual(true)
|
||||
expect(event.delegateTarget.isEqualNode(triggerEl)).toEqual(true)
|
||||
expect(event.target.isEqualNode(nestedTriggerEl)).toBeTrue()
|
||||
expect(event.delegateTarget.isEqualNode(triggerEl)).toBeTrue()
|
||||
expect(Event.prototype.preventDefault).toHaveBeenCalled()
|
||||
done()
|
||||
})
|
||||
@@ -530,9 +531,9 @@ describe('Collapse', () => {
|
||||
|
||||
collapse2.addEventListener('shown.bs.collapse', () => {
|
||||
expect(trigger.getAttribute('aria-expanded')).toEqual('true')
|
||||
expect(trigger.classList.contains('collapsed')).toEqual(false)
|
||||
expect(collapse1.classList.contains('show')).toEqual(true)
|
||||
expect(collapse1.classList.contains('show')).toEqual(true)
|
||||
expect(trigger).not.toHaveClass('collapsed')
|
||||
expect(collapse1).toHaveClass('show')
|
||||
expect(collapse1).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -552,9 +553,9 @@ describe('Collapse', () => {
|
||||
|
||||
collapse2.addEventListener('hidden.bs.collapse', () => {
|
||||
expect(trigger.getAttribute('aria-expanded')).toEqual('false')
|
||||
expect(trigger.classList.contains('collapsed')).toEqual(true)
|
||||
expect(collapse1.classList.contains('show')).toEqual(false)
|
||||
expect(collapse1.classList.contains('show')).toEqual(false)
|
||||
expect(trigger).toHaveClass('collapsed')
|
||||
expect(collapse1).not.toHaveClass('show')
|
||||
expect(collapse1).not.toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -575,8 +576,8 @@ describe('Collapse', () => {
|
||||
collapseTest1.addEventListener('shown.bs.collapse', () => {
|
||||
expect(link1.getAttribute('aria-expanded')).toEqual('true')
|
||||
expect(link2.getAttribute('aria-expanded')).toEqual('true')
|
||||
expect(link1.classList.contains('collapsed')).toEqual(false)
|
||||
expect(link2.classList.contains('collapsed')).toEqual(false)
|
||||
expect(link1).not.toHaveClass('collapsed')
|
||||
expect(link2).not.toHaveClass('collapsed')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -597,8 +598,8 @@ describe('Collapse', () => {
|
||||
collapseTest1.addEventListener('hidden.bs.collapse', () => {
|
||||
expect(link1.getAttribute('aria-expanded')).toEqual('false')
|
||||
expect(link2.getAttribute('aria-expanded')).toEqual('false')
|
||||
expect(link1.classList.contains('collapsed')).toEqual(true)
|
||||
expect(link2.classList.contains('collapsed')).toEqual(true)
|
||||
expect(link1).toHaveClass('collapsed')
|
||||
expect(link2).toHaveClass('collapsed')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -625,12 +626,12 @@ describe('Collapse', () => {
|
||||
const collapseTwo = fixtureEl.querySelector('#collapseTwo')
|
||||
|
||||
collapseOne.addEventListener('shown.bs.collapse', () => {
|
||||
expect(collapseOne.classList.contains('show')).toEqual(true)
|
||||
expect(collapseTwo.classList.contains('show')).toEqual(false)
|
||||
expect(collapseOne).toHaveClass('show')
|
||||
expect(collapseTwo).not.toHaveClass('show')
|
||||
|
||||
collapseTwo.addEventListener('shown.bs.collapse', () => {
|
||||
expect(collapseOne.classList.contains('show')).toEqual(false)
|
||||
expect(collapseTwo.classList.contains('show')).toEqual(true)
|
||||
expect(collapseOne).not.toHaveClass('show')
|
||||
expect(collapseTwo).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -650,8 +651,8 @@ describe('Collapse', () => {
|
||||
const collapseEl = fixtureEl.querySelector('#collapsediv1')
|
||||
|
||||
collapseEl.addEventListener('shown.bs.collapse', () => {
|
||||
expect(collapseEl.classList.contains('show')).toEqual(true)
|
||||
expect(target.checked).toEqual(true)
|
||||
expect(collapseEl).toHaveClass('show')
|
||||
expect(target.checked).toBeTrue()
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -684,21 +685,21 @@ describe('Collapse', () => {
|
||||
const collapseTwoEl = fixtureEl.querySelector('#collapseTwo')
|
||||
|
||||
collapseOneEl.addEventListener('shown.bs.collapse', () => {
|
||||
expect(collapseOneEl.classList.contains('show')).toEqual(true)
|
||||
expect(triggerEl.classList.contains('collapsed')).toEqual(false)
|
||||
expect(collapseOneEl).toHaveClass('show')
|
||||
expect(triggerEl).not.toHaveClass('collapsed')
|
||||
expect(triggerEl.getAttribute('aria-expanded')).toEqual('true')
|
||||
|
||||
expect(collapseTwoEl.classList.contains('show')).toEqual(false)
|
||||
expect(triggerTwoEl.classList.contains('collapsed')).toEqual(true)
|
||||
expect(collapseTwoEl).not.toHaveClass('show')
|
||||
expect(triggerTwoEl).toHaveClass('collapsed')
|
||||
expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('false')
|
||||
|
||||
collapseTwoEl.addEventListener('shown.bs.collapse', () => {
|
||||
expect(collapseOneEl.classList.contains('show')).toEqual(false)
|
||||
expect(triggerEl.classList.contains('collapsed')).toEqual(true)
|
||||
expect(collapseOneEl).not.toHaveClass('show')
|
||||
expect(triggerEl).toHaveClass('collapsed')
|
||||
expect(triggerEl.getAttribute('aria-expanded')).toEqual('false')
|
||||
|
||||
expect(collapseTwoEl.classList.contains('show')).toEqual(true)
|
||||
expect(triggerTwoEl.classList.contains('collapsed')).toEqual(false)
|
||||
expect(collapseTwoEl).toHaveClass('show')
|
||||
expect(triggerTwoEl).not.toHaveClass('collapsed')
|
||||
expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -733,21 +734,21 @@ describe('Collapse', () => {
|
||||
}
|
||||
|
||||
function firstTest() {
|
||||
expect(collapseOneOne.classList.contains('show')).toEqual(true)
|
||||
expect(collapseOneTwo.classList.contains('show')).toEqual(true)
|
||||
expect(collapseOneOne).toHaveClass('show')
|
||||
expect(collapseOneTwo).toHaveClass('show')
|
||||
|
||||
expect(collapseTwoOne.classList.contains('show')).toEqual(false)
|
||||
expect(collapseTwoTwo.classList.contains('show')).toEqual(false)
|
||||
expect(collapseTwoOne).not.toHaveClass('show')
|
||||
expect(collapseTwoTwo).not.toHaveClass('show')
|
||||
|
||||
triggerTwo.click()
|
||||
}
|
||||
|
||||
function secondTest() {
|
||||
expect(collapseOneOne.classList.contains('show')).toEqual(false)
|
||||
expect(collapseOneTwo.classList.contains('show')).toEqual(false)
|
||||
expect(collapseOneOne).not.toHaveClass('show')
|
||||
expect(collapseOneTwo).not.toHaveClass('show')
|
||||
|
||||
expect(collapseTwoOne.classList.contains('show')).toEqual(true)
|
||||
expect(collapseTwoTwo.classList.contains('show')).toEqual(true)
|
||||
expect(collapseTwoOne).toHaveClass('show')
|
||||
expect(collapseTwoTwo).toHaveClass('show')
|
||||
done()
|
||||
}
|
||||
|
||||
@@ -815,9 +816,9 @@ describe('Collapse', () => {
|
||||
const nestedCollapseOne = fixtureEl.querySelector('#nestedCollapseOne')
|
||||
|
||||
function handlerCollapseOne() {
|
||||
expect(collapseOne.classList.contains('show')).toEqual(true)
|
||||
expect(collapseTwo.classList.contains('show')).toEqual(false)
|
||||
expect(nestedCollapseOne.classList.contains('show')).toEqual(false)
|
||||
expect(collapseOne).toHaveClass('show')
|
||||
expect(collapseTwo).not.toHaveClass('show')
|
||||
expect(nestedCollapseOne).not.toHaveClass('show')
|
||||
|
||||
nestedCollapseOne.addEventListener('shown.bs.collapse', handlerNestedCollapseOne)
|
||||
nestedTrigger.click()
|
||||
@@ -825,14 +826,14 @@ describe('Collapse', () => {
|
||||
}
|
||||
|
||||
function handlerNestedCollapseOne() {
|
||||
expect(collapseOne.classList.contains('show')).toEqual(true)
|
||||
expect(collapseTwo.classList.contains('show')).toEqual(false)
|
||||
expect(nestedCollapseOne.classList.contains('show')).toEqual(true)
|
||||
expect(collapseOne).toHaveClass('show')
|
||||
expect(collapseTwo).not.toHaveClass('show')
|
||||
expect(nestedCollapseOne).toHaveClass('show')
|
||||
|
||||
collapseTwo.addEventListener('shown.bs.collapse', () => {
|
||||
expect(collapseOne.classList.contains('show')).toEqual(false)
|
||||
expect(collapseTwo.classList.contains('show')).toEqual(true)
|
||||
expect(nestedCollapseOne.classList.contains('show')).toEqual(true)
|
||||
expect(collapseOne).not.toHaveClass('show')
|
||||
expect(collapseTwo).toHaveClass('show')
|
||||
expect(nestedCollapseOne).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -860,33 +861,33 @@ describe('Collapse', () => {
|
||||
const target2 = fixtureEl.querySelector('#test2')
|
||||
|
||||
const target2Shown = () => {
|
||||
expect(trigger1.classList.contains('collapsed')).toEqual(false)
|
||||
expect(trigger1).not.toHaveClass('collapsed')
|
||||
expect(trigger1.getAttribute('aria-expanded')).toEqual('true')
|
||||
|
||||
expect(trigger2.classList.contains('collapsed')).toEqual(false)
|
||||
expect(trigger2).not.toHaveClass('collapsed')
|
||||
expect(trigger2.getAttribute('aria-expanded')).toEqual('true')
|
||||
|
||||
expect(trigger3.classList.contains('collapsed')).toEqual(false)
|
||||
expect(trigger3).not.toHaveClass('collapsed')
|
||||
expect(trigger3.getAttribute('aria-expanded')).toEqual('true')
|
||||
|
||||
target2.addEventListener('hidden.bs.collapse', () => {
|
||||
expect(trigger1.classList.contains('collapsed')).toEqual(false)
|
||||
expect(trigger1).not.toHaveClass('collapsed')
|
||||
expect(trigger1.getAttribute('aria-expanded')).toEqual('true')
|
||||
|
||||
expect(trigger2.classList.contains('collapsed')).toEqual(true)
|
||||
expect(trigger2).toHaveClass('collapsed')
|
||||
expect(trigger2.getAttribute('aria-expanded')).toEqual('false')
|
||||
|
||||
expect(trigger3.classList.contains('collapsed')).toEqual(false)
|
||||
expect(trigger3).not.toHaveClass('collapsed')
|
||||
expect(trigger3.getAttribute('aria-expanded')).toEqual('true')
|
||||
|
||||
target1.addEventListener('hidden.bs.collapse', () => {
|
||||
expect(trigger1.classList.contains('collapsed')).toEqual(true)
|
||||
expect(trigger1).toHaveClass('collapsed')
|
||||
expect(trigger1.getAttribute('aria-expanded')).toEqual('false')
|
||||
|
||||
expect(trigger2.classList.contains('collapsed')).toEqual(true)
|
||||
expect(trigger2).toHaveClass('collapsed')
|
||||
expect(trigger2.getAttribute('aria-expanded')).toEqual('false')
|
||||
|
||||
expect(trigger3.classList.contains('collapsed')).toEqual(true)
|
||||
expect(trigger3).toHaveClass('collapsed')
|
||||
expect(trigger3.getAttribute('aria-expanded')).toEqual('false')
|
||||
done()
|
||||
})
|
||||
@@ -961,7 +962,7 @@ describe('Collapse', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Collapse.getInstance(div)).toEqual(null)
|
||||
expect(Collapse.getInstance(div)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -982,7 +983,7 @@ describe('Collapse', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Collapse.getInstance(div)).toEqual(null)
|
||||
expect(Collapse.getInstance(div)).toBeNull()
|
||||
expect(Collapse.getOrCreateInstance(div)).toBeInstanceOf(Collapse)
|
||||
})
|
||||
|
||||
@@ -991,13 +992,13 @@ describe('Collapse', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Collapse.getInstance(div)).toEqual(null)
|
||||
expect(Collapse.getInstance(div)).toBeNull()
|
||||
const collapse = Collapse.getOrCreateInstance(div, {
|
||||
toggle: false
|
||||
})
|
||||
expect(collapse).toBeInstanceOf(Collapse)
|
||||
|
||||
expect(collapse._config.toggle).toEqual(false)
|
||||
expect(collapse._config.toggle).toBeFalse()
|
||||
})
|
||||
|
||||
it('should return the instance when exists without given configuration', () => {
|
||||
@@ -1015,7 +1016,7 @@ describe('Collapse', () => {
|
||||
expect(collapse).toBeInstanceOf(Collapse)
|
||||
expect(collapse2).toEqual(collapse)
|
||||
|
||||
expect(collapse2._config.toggle).toEqual(false)
|
||||
expect(collapse2._config.toggle).toBeFalse()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('Data', () => {
|
||||
|
||||
Data.set(div, TEST_KEY, data)
|
||||
|
||||
expect(Data.get(div, TEST_KEY)).toBe(data)
|
||||
expect(Data.get(div, TEST_KEY)).toEqual(data)
|
||||
})
|
||||
|
||||
it('should overwrite data if something is already stored', () => {
|
||||
@@ -60,11 +60,12 @@ describe('Data', () => {
|
||||
Data.set(div, TEST_KEY, data)
|
||||
Data.set(div, TEST_KEY, copy)
|
||||
|
||||
// Using `toBe` since spread creates a shallow copy
|
||||
expect(Data.get(div, TEST_KEY)).not.toBe(data)
|
||||
expect(Data.get(div, TEST_KEY)).toBe(copy)
|
||||
})
|
||||
|
||||
it('should do nothing when an element have nothing stored', () => {
|
||||
it('should do nothing when an element has nothing stored', () => {
|
||||
Data.remove(div, TEST_KEY)
|
||||
|
||||
expect().nothing()
|
||||
@@ -76,7 +77,7 @@ describe('Data', () => {
|
||||
Data.set(div, TEST_KEY, data)
|
||||
Data.remove(div, UNKNOWN_KEY)
|
||||
|
||||
expect(Data.get(div, TEST_KEY)).toBe(data)
|
||||
expect(Data.get(div, TEST_KEY)).toEqual(data)
|
||||
})
|
||||
|
||||
it('should remove data for a given key', () => {
|
||||
@@ -99,7 +100,7 @@ describe('Data', () => {
|
||||
Data.set(div, UNKNOWN_KEY, copy)
|
||||
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
expect(Data.get(div, UNKNOWN_KEY)).toBe(null)
|
||||
expect(Data.get(div, UNKNOWN_KEY)).toBeNull()
|
||||
})
|
||||
/* eslint-enable no-console */
|
||||
})
|
||||
|
||||
@@ -105,10 +105,10 @@ describe('EventHandler', () => {
|
||||
EventHandler.on(outer, 'mouseleave', '.inner', delegateLeaveSpy)
|
||||
|
||||
EventHandler.on(sibling, 'mouseenter', () => {
|
||||
expect(enterSpy.calls.count()).toBe(2)
|
||||
expect(leaveSpy.calls.count()).toBe(2)
|
||||
expect(delegateEnterSpy.calls.count()).toBe(2)
|
||||
expect(delegateLeaveSpy.calls.count()).toBe(2)
|
||||
expect(enterSpy.calls.count()).toEqual(2)
|
||||
expect(leaveSpy.calls.count()).toEqual(2)
|
||||
expect(delegateEnterSpy.calls.count()).toEqual(2)
|
||||
expect(delegateLeaveSpy.calls.count()).toEqual(2)
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -133,10 +133,10 @@ describe('EventHandler', () => {
|
||||
moveMouse(inner, outer)
|
||||
|
||||
setTimeout(() => {
|
||||
expect(enterSpy.calls.count()).toBe(1)
|
||||
expect(leaveSpy.calls.count()).toBe(1)
|
||||
expect(delegateEnterSpy.calls.count()).toBe(1)
|
||||
expect(delegateLeaveSpy.calls.count()).toBe(1)
|
||||
expect(enterSpy.calls.count()).toEqual(1)
|
||||
expect(leaveSpy.calls.count()).toEqual(1)
|
||||
expect(delegateEnterSpy.calls.count()).toEqual(1)
|
||||
expect(delegateLeaveSpy.calls.count()).toEqual(1)
|
||||
|
||||
// from outer to inner to sibling (adjacent)
|
||||
moveMouse(outer, inner)
|
||||
|
||||
@@ -96,10 +96,10 @@ describe('Manipulator', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Manipulator.getDataAttribute(div, 'test')).toEqual(false)
|
||||
expect(Manipulator.getDataAttribute(div, 'test')).toBeFalse()
|
||||
|
||||
div.setAttribute('data-bs-test', 'true')
|
||||
expect(Manipulator.getDataAttribute(div, 'test')).toEqual(true)
|
||||
expect(Manipulator.getDataAttribute(div, 'test')).toBeTrue()
|
||||
|
||||
div.setAttribute('data-bs-test', '1')
|
||||
expect(Manipulator.getDataAttribute(div, 'test')).toEqual(1)
|
||||
@@ -152,8 +152,8 @@ describe('Manipulator', () => {
|
||||
body.append(forceScrollBars)
|
||||
|
||||
const scrollHandler = () => {
|
||||
expect(window.pageYOffset).toBe(scrollY)
|
||||
expect(window.pageXOffset).toBe(scrollX)
|
||||
expect(window.pageYOffset).toEqual(scrollY)
|
||||
expect(window.pageXOffset).toEqual(scrollX)
|
||||
|
||||
const newOffset = Manipulator.offset(div)
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('SelectorEngine', () => {
|
||||
|
||||
describe('parents', () => {
|
||||
it('should return parents', () => {
|
||||
expect(SelectorEngine.parents(fixtureEl, 'body').length).toEqual(1)
|
||||
expect(SelectorEngine.parents(fixtureEl, 'body')).toHaveSize(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -197,9 +197,7 @@ describe('SelectorEngine', () => {
|
||||
})
|
||||
|
||||
it('should return not return elements with negative tab index', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<button tabindex="-1">lorem</button>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<button tabindex="-1">lorem</button>'
|
||||
|
||||
const expectedElements = []
|
||||
|
||||
@@ -207,9 +205,7 @@ describe('SelectorEngine', () => {
|
||||
})
|
||||
|
||||
it('should return contenteditable elements', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div contenteditable="true">lorem</div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div contenteditable="true">lorem</div>'
|
||||
|
||||
const expectedElements = [fixtureEl.querySelector('[contenteditable="true"]')]
|
||||
|
||||
@@ -217,9 +213,7 @@ describe('SelectorEngine', () => {
|
||||
})
|
||||
|
||||
it('should not return disabled elements', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<button disabled="true">lorem</button>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<button disabled="true">lorem</button>'
|
||||
|
||||
const expectedElements = []
|
||||
|
||||
@@ -227,9 +221,7 @@ describe('SelectorEngine', () => {
|
||||
})
|
||||
|
||||
it('should not return invisible elements', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<button style="display:none;">lorem</button>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<button style="display:none;">lorem</button>'
|
||||
|
||||
const expectedElements = []
|
||||
|
||||
|
||||
+84
-103
@@ -130,7 +130,7 @@ describe('Dropdown', () => {
|
||||
it('should allow to pass config to Popper with `popperConfig` as a function', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="dropdown">',
|
||||
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-placement="right" >Dropdown</button>',
|
||||
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-placement="right">Dropdown</button>',
|
||||
' <div class="dropdown-menu">',
|
||||
' <a class="dropdown-item" href="#">Secondary link</a>',
|
||||
' </div>',
|
||||
@@ -165,7 +165,7 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -196,7 +196,7 @@ describe('Dropdown', () => {
|
||||
const dropdown1 = new Dropdown(btnDropdown1)
|
||||
|
||||
firstDropdownEl.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown1.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown1).toHaveClass('show')
|
||||
spyOn(dropdown1._popper, 'destroy')
|
||||
btnDropdown2.click()
|
||||
})
|
||||
@@ -228,7 +228,7 @@ describe('Dropdown', () => {
|
||||
spyOn(EventHandler, 'off')
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
|
||||
|
||||
@@ -236,7 +236,7 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(false)
|
||||
expect(btnDropdown).not.toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
|
||||
expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
|
||||
|
||||
@@ -261,7 +261,7 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -284,7 +284,7 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
dropupEl.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -307,7 +307,7 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
dropupEl.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -330,7 +330,7 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
dropendEl.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -353,7 +353,7 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
dropstartEl.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -377,7 +377,7 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -401,7 +401,7 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -425,7 +425,7 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -474,7 +474,7 @@ describe('Dropdown', () => {
|
||||
popperConfig: {
|
||||
onFirstUpdate() {
|
||||
expect(virtualElement.getBoundingClientRect).toHaveBeenCalled()
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
}
|
||||
@@ -606,7 +606,7 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -734,7 +734,7 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
|
||||
expect(dropdownMenu.classList.contains('show')).toEqual(false)
|
||||
expect(dropdownMenu).not.toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
|
||||
done()
|
||||
})
|
||||
@@ -789,7 +789,7 @@ describe('Dropdown', () => {
|
||||
dropdown.hide()
|
||||
|
||||
setTimeout(() => {
|
||||
expect(dropdownMenu.classList.contains('show')).toEqual(true)
|
||||
expect(dropdownMenu).toHaveClass('show')
|
||||
done()
|
||||
}, 10)
|
||||
})
|
||||
@@ -815,7 +815,7 @@ describe('Dropdown', () => {
|
||||
dropdown.hide()
|
||||
|
||||
setTimeout(() => {
|
||||
expect(dropdownMenu.classList.contains('show')).toEqual(true)
|
||||
expect(dropdownMenu).toHaveClass('show')
|
||||
done()
|
||||
}, 10)
|
||||
})
|
||||
@@ -870,7 +870,7 @@ describe('Dropdown', () => {
|
||||
dropdown.hide()
|
||||
|
||||
setTimeout(() => {
|
||||
expect(dropdownMenu.classList.contains('show')).toEqual(true)
|
||||
expect(dropdownMenu).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
})
|
||||
@@ -897,7 +897,7 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(false)
|
||||
expect(btnDropdown).not.toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
|
||||
expect(EventHandler.off).toHaveBeenCalled()
|
||||
|
||||
@@ -1032,9 +1032,9 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', event => setTimeout(() => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
expect(showEventTriggered).toEqual(true)
|
||||
expect(showEventTriggered).toBeTrue()
|
||||
expect(event.relatedTarget).toEqual(btnDropdown)
|
||||
document.body.click()
|
||||
}))
|
||||
@@ -1044,9 +1044,9 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
btnDropdown.addEventListener('hidden.bs.dropdown', event => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(false)
|
||||
expect(btnDropdown).not.toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
|
||||
expect(hideEventTriggered).toEqual(true)
|
||||
expect(hideEventTriggered).toBeTrue()
|
||||
expect(event.relatedTarget).toEqual(btnDropdown)
|
||||
done()
|
||||
})
|
||||
@@ -1054,7 +1054,7 @@ describe('Dropdown', () => {
|
||||
btnDropdown.click()
|
||||
})
|
||||
|
||||
it('should not use Popper in navbar', done => {
|
||||
it('should not use "static" Popper in navbar', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<nav class="navbar navbar-expand-md navbar-light bg-light">',
|
||||
' <div class="dropdown">',
|
||||
@@ -1071,8 +1071,8 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(dropdown._popper).toBeNull()
|
||||
expect(dropdownMenu.getAttribute('style')).toEqual(null, 'no inline style applied by Popper')
|
||||
expect(dropdown._popper).not.toBeNull()
|
||||
expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -1120,7 +1120,7 @@ describe('Dropdown', () => {
|
||||
dropdown.show()
|
||||
})
|
||||
|
||||
it('should manage bs attribute `data-bs-popper`="none" when dropdown is in navbar', done => {
|
||||
it('should manage bs attribute `data-bs-popper`="static" when dropdown is in navbar', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<nav class="navbar navbar-expand-md navbar-light bg-light">',
|
||||
' <div class="dropdown">',
|
||||
@@ -1137,7 +1137,7 @@ describe('Dropdown', () => {
|
||||
const dropdown = new Dropdown(btnDropdown)
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('none')
|
||||
expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
|
||||
dropdown.hide()
|
||||
})
|
||||
|
||||
@@ -1164,7 +1164,7 @@ describe('Dropdown', () => {
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
// Popper adds this attribute when we use it
|
||||
expect(dropdownMenu.getAttribute('data-popper-placement')).toEqual(null)
|
||||
expect(dropdownMenu.getAttribute('data-popper-placement')).toBeNull()
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -1211,7 +1211,7 @@ describe('Dropdown', () => {
|
||||
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
|
||||
const keyup = createEvent('keyup')
|
||||
|
||||
@@ -1220,7 +1220,7 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
btnDropdown.addEventListener('hidden.bs.dropdown', () => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(false)
|
||||
expect(btnDropdown).not.toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -1248,29 +1248,29 @@ describe('Dropdown', () => {
|
||||
|
||||
const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
|
||||
|
||||
expect(triggerDropdownList.length).toEqual(2)
|
||||
expect(triggerDropdownList).toHaveSize(2)
|
||||
|
||||
const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
|
||||
|
||||
triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(triggerDropdownFirst.classList.contains('show')).toEqual(true)
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1)
|
||||
expect(triggerDropdownFirst).toHaveClass('show')
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
|
||||
document.body.click()
|
||||
})
|
||||
|
||||
triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0)
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
|
||||
triggerDropdownLast.click()
|
||||
})
|
||||
|
||||
triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(triggerDropdownLast.classList.contains('show')).toEqual(true)
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1)
|
||||
expect(triggerDropdownLast).toHaveClass('show')
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
|
||||
document.body.click()
|
||||
})
|
||||
|
||||
triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0)
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -1296,13 +1296,13 @@ describe('Dropdown', () => {
|
||||
|
||||
const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
|
||||
|
||||
expect(triggerDropdownList.length).toEqual(2)
|
||||
expect(triggerDropdownList).toHaveSize(2)
|
||||
|
||||
const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
|
||||
|
||||
triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(triggerDropdownFirst.classList.contains('show')).toEqual(true, '"show" class added on click')
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown')
|
||||
expect(triggerDropdownFirst).toHaveClass('show')
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
|
||||
|
||||
const keyup = createEvent('keyup')
|
||||
keyup.key = 'Tab'
|
||||
@@ -1311,13 +1311,13 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0, '"show" class removed')
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
|
||||
triggerDropdownLast.click()
|
||||
})
|
||||
|
||||
triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(triggerDropdownLast.classList.contains('show')).toEqual(true, '"show" class added on click')
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown')
|
||||
expect(triggerDropdownLast).toHaveClass('show')
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
|
||||
|
||||
const keyup = createEvent('keyup')
|
||||
keyup.key = 'Tab'
|
||||
@@ -1326,7 +1326,7 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0, '"show" class removed')
|
||||
expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -1456,8 +1456,8 @@ describe('Dropdown', () => {
|
||||
triggerDropdown.dispatchEvent(keydown)
|
||||
triggerDropdown.dispatchEvent(keydown)
|
||||
|
||||
expect(document.activeElement.classList.contains('disabled')).toEqual(false, '.disabled not focused')
|
||||
expect(document.activeElement.hasAttribute('disabled')).toEqual(false, ':disabled not focused')
|
||||
expect(document.activeElement).not.toHaveClass('disabled')
|
||||
expect(document.activeElement.hasAttribute('disabled')).toBeFalse()
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -1490,9 +1490,9 @@ describe('Dropdown', () => {
|
||||
|
||||
triggerDropdown.dispatchEvent(keydown)
|
||||
|
||||
expect(document.activeElement.classList.contains('d-none')).toEqual(false, '.d-none not focused')
|
||||
expect(document.activeElement.style.display).not.toBe('none', '"display: none" not focused')
|
||||
expect(document.activeElement.style.visibility).not.toBe('hidden', '"visibility: hidden" not focused')
|
||||
expect(document.activeElement).not.toHaveClass('d-none')
|
||||
expect(document.activeElement.style.display).not.toEqual('none')
|
||||
expect(document.activeElement.style.visibility).not.toEqual('hidden')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -1603,12 +1603,12 @@ describe('Dropdown', () => {
|
||||
const input = fixtureEl.querySelector('input')
|
||||
|
||||
input.addEventListener('click', () => {
|
||||
expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
|
||||
expect(triggerDropdown).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
|
||||
expect(triggerDropdown).toHaveClass('show')
|
||||
input.dispatchEvent(createEvent('click'))
|
||||
})
|
||||
|
||||
@@ -1629,12 +1629,12 @@ describe('Dropdown', () => {
|
||||
const textarea = fixtureEl.querySelector('textarea')
|
||||
|
||||
textarea.addEventListener('click', () => {
|
||||
expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
|
||||
expect(triggerDropdown).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
|
||||
expect(triggerDropdown).toHaveClass('show')
|
||||
textarea.dispatchEvent(createEvent('click'))
|
||||
})
|
||||
|
||||
@@ -1684,57 +1684,38 @@ describe('Dropdown', () => {
|
||||
const input = fixtureEl.querySelector('input')
|
||||
const textarea = fixtureEl.querySelector('textarea')
|
||||
|
||||
const keydownSpace = createEvent('keydown')
|
||||
keydownSpace.key = 'Space'
|
||||
|
||||
const keydownArrowUp = createEvent('keydown')
|
||||
keydownArrowUp.key = 'ArrowUp'
|
||||
|
||||
const keydownArrowDown = createEvent('keydown')
|
||||
keydownArrowDown.key = 'ArrowDown'
|
||||
const test = (eventKey, elementToDispatch) => {
|
||||
const event = createEvent('keydown')
|
||||
event.key = eventKey
|
||||
elementToDispatch.focus()
|
||||
elementToDispatch.dispatchEvent(event)
|
||||
expect(document.activeElement).toEqual(elementToDispatch, `${elementToDispatch.tagName} still focused`)
|
||||
}
|
||||
|
||||
const keydownEscape = createEvent('keydown')
|
||||
keydownEscape.key = 'Escape'
|
||||
|
||||
triggerDropdown.addEventListener('shown.bs.dropdown', () => {
|
||||
// Key Space
|
||||
input.focus()
|
||||
input.dispatchEvent(keydownSpace)
|
||||
test('Space', input)
|
||||
|
||||
expect(document.activeElement).toEqual(input, 'input still focused')
|
||||
|
||||
textarea.focus()
|
||||
textarea.dispatchEvent(keydownSpace)
|
||||
|
||||
expect(document.activeElement).toEqual(textarea, 'textarea still focused')
|
||||
test('Space', textarea)
|
||||
|
||||
// Key ArrowUp
|
||||
input.focus()
|
||||
input.dispatchEvent(keydownArrowUp)
|
||||
test('ArrowUp', input)
|
||||
|
||||
expect(document.activeElement).toEqual(input, 'input still focused')
|
||||
|
||||
textarea.focus()
|
||||
textarea.dispatchEvent(keydownArrowUp)
|
||||
|
||||
expect(document.activeElement).toEqual(textarea, 'textarea still focused')
|
||||
test('ArrowUp', textarea)
|
||||
|
||||
// Key ArrowDown
|
||||
input.focus()
|
||||
input.dispatchEvent(keydownArrowDown)
|
||||
test('ArrowDown', input)
|
||||
|
||||
expect(document.activeElement).toEqual(input, 'input still focused')
|
||||
|
||||
textarea.focus()
|
||||
textarea.dispatchEvent(keydownArrowDown)
|
||||
|
||||
expect(document.activeElement).toEqual(textarea, 'textarea still focused')
|
||||
test('ArrowDown', textarea)
|
||||
|
||||
// Key Escape
|
||||
input.focus()
|
||||
input.dispatchEvent(keydownEscape)
|
||||
|
||||
expect(triggerDropdown.classList.contains('show')).toEqual(false, 'dropdown menu is not shown')
|
||||
expect(triggerDropdown).not.toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -1750,8 +1731,8 @@ describe('Dropdown', () => {
|
||||
' <a class="dropdown-item" href="#">Secondary link</a>',
|
||||
' <a class="dropdown-item" href="#">Something else here</a>',
|
||||
' <div class="divider"></div>',
|
||||
' <a class="dropdown-item" href="#">Another link</a>',
|
||||
' </div>',
|
||||
' <a class="dropdown-item" href="#">Another link</a>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
'</div>'
|
||||
]
|
||||
@@ -1771,7 +1752,7 @@ describe('Dropdown', () => {
|
||||
|
||||
setTimeout(() => {
|
||||
expect(dropdown.toggle).not.toHaveBeenCalled()
|
||||
expect(triggerDropdown.classList.contains('show')).toEqual(false)
|
||||
expect(triggerDropdown).not.toHaveClass('show')
|
||||
done()
|
||||
}, 20)
|
||||
})
|
||||
@@ -1783,7 +1764,7 @@ describe('Dropdown', () => {
|
||||
' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
|
||||
' <div class="dropdown-menu">',
|
||||
' <a class="dropdown-item" href="#">Some Item</a>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
'</div>'
|
||||
]
|
||||
@@ -1823,7 +1804,7 @@ describe('Dropdown', () => {
|
||||
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
|
||||
|
||||
const expectDropdownToBeOpened = () => setTimeout(() => {
|
||||
expect(dropdownToggle.classList.contains('show')).toEqual(true)
|
||||
expect(dropdownToggle).toHaveClass('show')
|
||||
dropdownMenu.click()
|
||||
}, 150)
|
||||
|
||||
@@ -1833,7 +1814,7 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
dropdownToggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => {
|
||||
expect(dropdownToggle.classList.contains('show')).toEqual(false)
|
||||
expect(dropdownToggle).not.toHaveClass('show')
|
||||
done()
|
||||
}))
|
||||
|
||||
@@ -1854,7 +1835,7 @@ describe('Dropdown', () => {
|
||||
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
|
||||
|
||||
const expectDropdownToBeOpened = () => setTimeout(() => {
|
||||
expect(dropdownToggle.classList.contains('show')).toEqual(true)
|
||||
expect(dropdownToggle).toHaveClass('show')
|
||||
document.documentElement.click()
|
||||
}, 150)
|
||||
|
||||
@@ -1864,7 +1845,7 @@ describe('Dropdown', () => {
|
||||
})
|
||||
|
||||
dropdownToggle.addEventListener('hidden.bs.dropdown', () => {
|
||||
expect(dropdownToggle.classList.contains('show')).toEqual(false)
|
||||
expect(dropdownToggle).not.toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -1885,7 +1866,7 @@ describe('Dropdown', () => {
|
||||
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
|
||||
|
||||
const expectDropdownToBeOpened = (shouldTriggerClick = true) => setTimeout(() => {
|
||||
expect(dropdownToggle.classList.contains('show')).toEqual(true)
|
||||
expect(dropdownToggle).toHaveClass('show')
|
||||
if (shouldTriggerClick) {
|
||||
document.documentElement.click()
|
||||
} else {
|
||||
@@ -1963,7 +1944,7 @@ describe('Dropdown', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Dropdown.getInstance(div)).toEqual(null)
|
||||
expect(Dropdown.getInstance(div)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1984,7 +1965,7 @@ describe('Dropdown', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Dropdown.getInstance(div)).toEqual(null)
|
||||
expect(Dropdown.getInstance(div)).toBeNull()
|
||||
expect(Dropdown.getOrCreateInstance(div)).toBeInstanceOf(Dropdown)
|
||||
})
|
||||
|
||||
@@ -1993,7 +1974,7 @@ describe('Dropdown', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Dropdown.getInstance(div)).toEqual(null)
|
||||
expect(Dropdown.getInstance(div)).toBeNull()
|
||||
const dropdown = Dropdown.getOrCreateInstance(div, {
|
||||
display: 'dynamic'
|
||||
})
|
||||
@@ -2043,7 +2024,7 @@ describe('Dropdown', () => {
|
||||
keyup.key = 'ArrowUp'
|
||||
|
||||
const handleArrowDown = () => {
|
||||
expect(triggerDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(triggerDropdown).toHaveClass('show')
|
||||
expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
setTimeout(() => {
|
||||
dropdown.hide()
|
||||
@@ -2053,7 +2034,7 @@ describe('Dropdown', () => {
|
||||
}
|
||||
|
||||
const handleArrowUp = () => {
|
||||
expect(triggerDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(triggerDropdown).toHaveClass('show')
|
||||
expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
}
|
||||
@@ -2108,7 +2089,7 @@ describe('Dropdown', () => {
|
||||
const childElement = fixtureEl.querySelector('#childElement')
|
||||
|
||||
btnDropdown.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
|
||||
expect(btnDropdown.classList.contains('show')).toEqual(true)
|
||||
expect(btnDropdown).toHaveClass('show')
|
||||
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
|
||||
done()
|
||||
}))
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('jQuery', () => {
|
||||
|
||||
$(fixtureEl).find('.alert')
|
||||
.one('closed.bs.alert', () => {
|
||||
expect($(fixtureEl).find('.alert').length).toEqual(0)
|
||||
expect($(fixtureEl).find('.alert')).toHaveSize(0)
|
||||
done()
|
||||
})
|
||||
|
||||
|
||||
+48
-20
@@ -57,9 +57,7 @@ describe('Modal', () => {
|
||||
|
||||
describe('toggle', () => {
|
||||
it('should call ScrollBarHelper to handle scrollBar on body', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
|
||||
spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
|
||||
@@ -234,12 +232,12 @@ describe('Modal', () => {
|
||||
|
||||
modalEl.addEventListener('show.bs.modal', () => {
|
||||
setTimeout(() => {
|
||||
expect(modal._isTransitioning).toEqual(true)
|
||||
expect(modal._isTransitioning).toBeTrue()
|
||||
})
|
||||
})
|
||||
|
||||
modalEl.addEventListener('shown.bs.modal', () => {
|
||||
expect(modal._isTransitioning).toEqual(false)
|
||||
expect(modal._isTransitioning).toBeFalse()
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -279,8 +277,7 @@ describe('Modal', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<button type="button" data-bs-dismiss="modal" data-bs-target="#modal1"></button>',
|
||||
'<div id="modal1" class="modal fade">',
|
||||
' <div class="modal-dialog">',
|
||||
' </div>',
|
||||
' <div class="modal-dialog"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -305,8 +302,7 @@ describe('Modal', () => {
|
||||
it('should set .modal\'s scroll top to 0', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="modal fade">',
|
||||
' <div class="modal-dialog">',
|
||||
' </div>',
|
||||
' <div class="modal-dialog"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -432,6 +428,38 @@ describe('Modal', () => {
|
||||
modal.show()
|
||||
})
|
||||
|
||||
it('should not close modal when clicking on modal-content', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="modal">',
|
||||
' <div class="modal-dialog">',
|
||||
' <div class="modal-content"></div>',
|
||||
' </div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const modalEl = fixtureEl.querySelector('.modal')
|
||||
const modal = new Modal(modalEl)
|
||||
|
||||
const shownCallback = () => {
|
||||
setTimeout(() => {
|
||||
expect(modal._isShown).toEqual(true)
|
||||
done()
|
||||
}, 10)
|
||||
}
|
||||
|
||||
modalEl.addEventListener('shown.bs.modal', () => {
|
||||
fixtureEl.querySelector('.modal-dialog').click()
|
||||
fixtureEl.querySelector('.modal-content').click()
|
||||
shownCallback()
|
||||
})
|
||||
|
||||
modalEl.addEventListener('hidden.bs.modal', () => {
|
||||
throw new Error('Should not hide a modal')
|
||||
})
|
||||
|
||||
modal.show()
|
||||
})
|
||||
|
||||
it('should not close modal when clicking outside of modal-content if backdrop = false', done => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
@@ -442,7 +470,7 @@ describe('Modal', () => {
|
||||
|
||||
const shownCallback = () => {
|
||||
setTimeout(() => {
|
||||
expect(modal._isShown).toEqual(true)
|
||||
expect(modal._isShown).toBeTrue()
|
||||
done()
|
||||
}, 10)
|
||||
}
|
||||
@@ -469,7 +497,7 @@ describe('Modal', () => {
|
||||
|
||||
const shownCallback = () => {
|
||||
setTimeout(() => {
|
||||
expect(modal._isShown).toEqual(true)
|
||||
expect(modal._isShown).toBeTrue()
|
||||
done()
|
||||
}, 10)
|
||||
}
|
||||
@@ -497,7 +525,7 @@ describe('Modal', () => {
|
||||
|
||||
const shownCallback = () => {
|
||||
setTimeout(() => {
|
||||
expect(modal._isShown).toEqual(false)
|
||||
expect(modal._isShown).toBeFalse()
|
||||
done()
|
||||
}, 10)
|
||||
}
|
||||
@@ -523,7 +551,7 @@ describe('Modal', () => {
|
||||
|
||||
const shownCallback = () => {
|
||||
setTimeout(() => {
|
||||
expect(modal._isShown).toEqual(true)
|
||||
expect(modal._isShown).toBeTrue()
|
||||
done()
|
||||
}, 10)
|
||||
}
|
||||
@@ -687,7 +715,7 @@ describe('Modal', () => {
|
||||
|
||||
const hideCallback = () => {
|
||||
setTimeout(() => {
|
||||
expect(modal._isShown).toEqual(true)
|
||||
expect(modal._isShown).toBeTrue()
|
||||
done()
|
||||
}, 10)
|
||||
}
|
||||
@@ -994,7 +1022,7 @@ describe('Modal', () => {
|
||||
})
|
||||
modalEl1.addEventListener('hidden.bs.modal', () => {
|
||||
expect(Modal.getInstance(modalEl2)).not.toBeNull()
|
||||
expect(modalEl2.classList.contains('show')).toBeTrue()
|
||||
expect(modalEl2).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
modal1.show()
|
||||
@@ -1029,7 +1057,7 @@ describe('Modal', () => {
|
||||
|
||||
const modal = Modal.getInstance(div)
|
||||
expect(modal).not.toBeNull()
|
||||
expect(modal._config.keyboard).toBe(false)
|
||||
expect(modal._config.keyboard).toBeFalse()
|
||||
})
|
||||
|
||||
it('should not re create a modal', () => {
|
||||
@@ -1129,7 +1157,7 @@ describe('Modal', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Modal.getInstance(div)).toEqual(null)
|
||||
expect(Modal.getInstance(div)).toBeNull()
|
||||
expect(Modal.getOrCreateInstance(div)).toBeInstanceOf(Modal)
|
||||
})
|
||||
|
||||
@@ -1138,13 +1166,13 @@ describe('Modal', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Modal.getInstance(div)).toEqual(null)
|
||||
expect(Modal.getInstance(div)).toBeNull()
|
||||
const modal = Modal.getOrCreateInstance(div, {
|
||||
backdrop: true
|
||||
})
|
||||
expect(modal).toBeInstanceOf(Modal)
|
||||
|
||||
expect(modal._config.backdrop).toEqual(true)
|
||||
expect(modal._config.backdrop).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return the instance when exists without given configuration', () => {
|
||||
@@ -1162,7 +1190,7 @@ describe('Modal', () => {
|
||||
expect(modal).toBeInstanceOf(Modal)
|
||||
expect(modal2).toEqual(modal)
|
||||
|
||||
expect(modal2._config.backdrop).toEqual(true)
|
||||
expect(modal2._config.backdrop).toBeTrue()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('Offcanvas', () => {
|
||||
|
||||
closeEl.click()
|
||||
|
||||
expect(offCanvas._config.keyboard).toBe(true)
|
||||
expect(offCanvas._config.keyboard).toBeTrue()
|
||||
expect(offCanvas.hide).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -101,47 +101,38 @@ describe('Offcanvas', () => {
|
||||
|
||||
document.dispatchEvent(keyDownEsc)
|
||||
|
||||
expect(offCanvas._config.keyboard).toBe(false)
|
||||
expect(offCanvas._config.keyboard).toBeFalse()
|
||||
expect(offCanvas.hide).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('config', () => {
|
||||
it('should have default values', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="offcanvas">',
|
||||
'</div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div class="offcanvas"></div>'
|
||||
|
||||
const offCanvasEl = fixtureEl.querySelector('.offcanvas')
|
||||
const offCanvas = new Offcanvas(offCanvasEl)
|
||||
|
||||
expect(offCanvas._config.backdrop).toEqual(true)
|
||||
expect(offCanvas._backdrop._config.isVisible).toEqual(true)
|
||||
expect(offCanvas._config.keyboard).toEqual(true)
|
||||
expect(offCanvas._config.scroll).toEqual(false)
|
||||
expect(offCanvas._config.backdrop).toBeTrue()
|
||||
expect(offCanvas._backdrop._config.isVisible).toBeTrue()
|
||||
expect(offCanvas._config.keyboard).toBeTrue()
|
||||
expect(offCanvas._config.scroll).toBeFalse()
|
||||
})
|
||||
|
||||
it('should read data attributes and override default config', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="offcanvas" data-bs-scroll="true" data-bs-backdrop="false" data-bs-keyboard="false">',
|
||||
'</div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div class="offcanvas" data-bs-scroll="true" data-bs-backdrop="false" data-bs-keyboard="false"></div>'
|
||||
|
||||
const offCanvasEl = fixtureEl.querySelector('.offcanvas')
|
||||
const offCanvas = new Offcanvas(offCanvasEl)
|
||||
|
||||
expect(offCanvas._config.backdrop).toEqual(false)
|
||||
expect(offCanvas._backdrop._config.isVisible).toEqual(false)
|
||||
expect(offCanvas._config.keyboard).toEqual(false)
|
||||
expect(offCanvas._config.scroll).toEqual(true)
|
||||
expect(offCanvas._config.backdrop).toBeFalse()
|
||||
expect(offCanvas._backdrop._config.isVisible).toBeFalse()
|
||||
expect(offCanvas._config.keyboard).toBeFalse()
|
||||
expect(offCanvas._config.scroll).toBeTrue()
|
||||
})
|
||||
|
||||
it('given a config object must override data attributes', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="offcanvas" data-bs-scroll="true" data-bs-backdrop="false" data-bs-keyboard="false">',
|
||||
'</div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div class="offcanvas" data-bs-scroll="true" data-bs-backdrop="false" data-bs-keyboard="false"></div>'
|
||||
|
||||
const offCanvasEl = fixtureEl.querySelector('.offcanvas')
|
||||
const offCanvas = new Offcanvas(offCanvasEl, {
|
||||
@@ -149,11 +140,12 @@ describe('Offcanvas', () => {
|
||||
keyboard: true,
|
||||
scroll: false
|
||||
})
|
||||
expect(offCanvas._config.backdrop).toEqual(true)
|
||||
expect(offCanvas._config.keyboard).toEqual(true)
|
||||
expect(offCanvas._config.scroll).toEqual(false)
|
||||
expect(offCanvas._config.backdrop).toBeTrue()
|
||||
expect(offCanvas._config.keyboard).toBeTrue()
|
||||
expect(offCanvas._config.scroll).toBeFalse()
|
||||
})
|
||||
})
|
||||
|
||||
describe('options', () => {
|
||||
it('if scroll is enabled, should allow body to scroll while offcanvas is open', done => {
|
||||
fixtureEl.innerHTML = '<div class="offcanvas"></div>'
|
||||
@@ -204,7 +196,7 @@ describe('Offcanvas', () => {
|
||||
spyOn(offCanvas._backdrop._config, 'clickCallback').and.callThrough()
|
||||
|
||||
offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
|
||||
expect(typeof offCanvas._backdrop._config.clickCallback).toBe('function')
|
||||
expect(offCanvas._backdrop._config.clickCallback).toEqual(jasmine.any(Function))
|
||||
|
||||
offCanvas._backdrop._getElement().dispatchEvent(clickEvent)
|
||||
})
|
||||
@@ -256,7 +248,7 @@ describe('Offcanvas', () => {
|
||||
const offCanvasEl = fixtureEl.querySelector('.offcanvas')
|
||||
const offCanvas = new Offcanvas(offCanvasEl)
|
||||
offCanvas.show()
|
||||
expect(offCanvasEl.classList.contains('show')).toBe(true)
|
||||
expect(offCanvasEl).toHaveClass('show')
|
||||
|
||||
spyOn(offCanvas, 'hide')
|
||||
|
||||
@@ -274,7 +266,7 @@ describe('Offcanvas', () => {
|
||||
const offCanvas = new Offcanvas(offCanvasEl)
|
||||
offCanvas.show()
|
||||
|
||||
expect(offCanvasEl.classList.contains('show')).toBe(true)
|
||||
expect(offCanvasEl).toHaveClass('show')
|
||||
|
||||
spyOn(offCanvas._backdrop, 'show').and.callThrough()
|
||||
spyOn(EventHandler, 'trigger').and.callThrough()
|
||||
@@ -292,7 +284,7 @@ describe('Offcanvas', () => {
|
||||
spyOn(offCanvas._backdrop, 'show').and.callThrough()
|
||||
|
||||
offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
|
||||
expect(offCanvasEl.classList.contains('show')).toEqual(true)
|
||||
expect(offCanvasEl).toHaveClass('show')
|
||||
expect(offCanvas._backdrop.show).toHaveBeenCalled()
|
||||
done()
|
||||
})
|
||||
@@ -384,7 +376,7 @@ describe('Offcanvas', () => {
|
||||
offCanvas.show()
|
||||
|
||||
offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
|
||||
expect(offCanvasEl.classList.contains('show')).toEqual(false)
|
||||
expect(offCanvasEl).not.toHaveClass('show')
|
||||
expect(offCanvas._backdrop.hide).toHaveBeenCalled()
|
||||
done()
|
||||
})
|
||||
@@ -458,7 +450,7 @@ describe('Offcanvas', () => {
|
||||
expect(offCanvas._backdrop).toBeNull()
|
||||
expect(focustrap.deactivate).toHaveBeenCalled()
|
||||
expect(offCanvas._focustrap).toBeNull()
|
||||
expect(Offcanvas.getInstance(offCanvasEl)).toEqual(null)
|
||||
expect(Offcanvas.getInstance(offCanvasEl)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -473,8 +465,8 @@ describe('Offcanvas', () => {
|
||||
const offCanvasEl = fixtureEl.querySelector('#offcanvasdiv1')
|
||||
|
||||
offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
|
||||
expect(offCanvasEl.classList.contains('show')).toEqual(true)
|
||||
expect(target.checked).toEqual(true)
|
||||
expect(offCanvasEl).toHaveClass('show')
|
||||
expect(target.checked).toBeTrue()
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -498,7 +490,7 @@ describe('Offcanvas', () => {
|
||||
|
||||
it('should call hide first, if another offcanvas is open', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<button id="btn2" data-bs-toggle="offcanvas" data-bs-target="#offcanvas2" ></button>',
|
||||
'<button id="btn2" data-bs-toggle="offcanvas" data-bs-target="#offcanvas2"></button>',
|
||||
'<div id="offcanvas1" class="offcanvas"></div>',
|
||||
'<div id="offcanvas2" class="offcanvas"></div>'
|
||||
].join('')
|
||||
@@ -520,7 +512,7 @@ describe('Offcanvas', () => {
|
||||
|
||||
it('should focus on trigger element after closing offcanvas', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas" ></button>',
|
||||
'<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas"></button>',
|
||||
'<div id="offcanvas" class="offcanvas"></div>'
|
||||
].join('')
|
||||
|
||||
@@ -544,7 +536,7 @@ describe('Offcanvas', () => {
|
||||
|
||||
it('should not focus on trigger element after closing offcanvas, if it is not visible', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas" ></button>',
|
||||
'<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas"></button>',
|
||||
'<div id="offcanvas" class="offcanvas"></div>'
|
||||
].join('')
|
||||
|
||||
@@ -559,7 +551,7 @@ describe('Offcanvas', () => {
|
||||
})
|
||||
offcanvasEl.addEventListener('hidden.bs.offcanvas', () => {
|
||||
setTimeout(() => {
|
||||
expect(isVisible(trigger)).toBe(false)
|
||||
expect(isVisible(trigger)).toBeFalse()
|
||||
expect(trigger.focus).not.toHaveBeenCalled()
|
||||
done()
|
||||
}, 5)
|
||||
@@ -665,7 +657,7 @@ describe('Offcanvas', () => {
|
||||
|
||||
const offcanvas = Offcanvas.getInstance(div)
|
||||
expect(offcanvas).not.toBeNull()
|
||||
expect(offcanvas._config.scroll).toBe(true)
|
||||
expect(offcanvas._config.scroll).toBeTrue()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -706,7 +698,7 @@ describe('Offcanvas', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Offcanvas.getInstance(div)).toEqual(null)
|
||||
expect(Offcanvas.getInstance(div)).toBeNull()
|
||||
expect(Offcanvas.getOrCreateInstance(div)).toBeInstanceOf(Offcanvas)
|
||||
})
|
||||
|
||||
@@ -715,13 +707,13 @@ describe('Offcanvas', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Offcanvas.getInstance(div)).toEqual(null)
|
||||
expect(Offcanvas.getInstance(div)).toBeNull()
|
||||
const offcanvas = Offcanvas.getOrCreateInstance(div, {
|
||||
scroll: true
|
||||
})
|
||||
expect(offcanvas).toBeInstanceOf(Offcanvas)
|
||||
|
||||
expect(offcanvas._config.scroll).toEqual(true)
|
||||
expect(offcanvas._config.scroll).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return the instance when exists without given configuration', () => {
|
||||
@@ -739,7 +731,7 @@ describe('Offcanvas', () => {
|
||||
expect(offcanvas).toBeInstanceOf(Offcanvas)
|
||||
expect(offcanvas2).toEqual(offcanvas)
|
||||
|
||||
expect(offcanvas2._config.scroll).toEqual(true)
|
||||
expect(offcanvas2._config.scroll).toBeTrue()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -155,6 +155,22 @@ describe('Popover', () => {
|
||||
popover.show()
|
||||
})
|
||||
|
||||
it('"setContent" should keep the initial template', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap" data-bs-custom-class="custom-class">BS twitter</a>'
|
||||
|
||||
const popoverEl = fixtureEl.querySelector('a')
|
||||
const popover = new Popover(popoverEl)
|
||||
|
||||
popover.setContent({ '.tooltip-inner': 'foo' })
|
||||
const tip = popover._getTipElement()
|
||||
|
||||
expect(tip).toHaveClass('popover')
|
||||
expect(tip).toHaveClass('bs-popover-auto')
|
||||
expect(tip.querySelector('.popover-arrow')).not.toBeNull()
|
||||
expect(tip.querySelector('.popover-header')).not.toBeNull()
|
||||
expect(tip.querySelector('.popover-body')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should call setContent once', done => {
|
||||
fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
|
||||
|
||||
@@ -162,8 +178,8 @@ describe('Popover', () => {
|
||||
const popover = new Popover(popoverEl, {
|
||||
content: 'Popover content'
|
||||
})
|
||||
|
||||
const spy = spyOn(popover, 'setContent').and.callThrough()
|
||||
expect(popover._templateFactory).toBeNull()
|
||||
let spy = null
|
||||
let times = 1
|
||||
|
||||
popoverEl.addEventListener('hidden.bs.popover', () => {
|
||||
@@ -171,11 +187,12 @@ describe('Popover', () => {
|
||||
})
|
||||
|
||||
popoverEl.addEventListener('shown.bs.popover', () => {
|
||||
spy = spy || spyOn(popover._templateFactory, 'constructor').and.callThrough()
|
||||
const popoverDisplayed = document.querySelector('.popover')
|
||||
|
||||
expect(popoverDisplayed).not.toBeNull()
|
||||
expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Popover content')
|
||||
expect(spy).toHaveBeenCalledTimes(1)
|
||||
expect(spy).toHaveBeenCalledTimes(0)
|
||||
if (times > 1) {
|
||||
done()
|
||||
}
|
||||
@@ -195,7 +212,7 @@ describe('Popover', () => {
|
||||
popoverEl.addEventListener('shown.bs.popover', () => {
|
||||
const tip = document.querySelector('.popover')
|
||||
expect(tip).not.toBeNull()
|
||||
expect(tip.classList.contains('custom-class')).toBeTrue()
|
||||
expect(tip).toHaveClass('custom-class')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -313,7 +330,7 @@ describe('Popover', () => {
|
||||
|
||||
const popoverEl = fixtureEl.querySelector('a')
|
||||
|
||||
expect(Popover.getInstance(popoverEl)).toEqual(null)
|
||||
expect(Popover.getInstance(popoverEl)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -334,7 +351,7 @@ describe('Popover', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Popover.getInstance(div)).toEqual(null)
|
||||
expect(Popover.getInstance(div)).toBeNull()
|
||||
expect(Popover.getOrCreateInstance(div)).toBeInstanceOf(Popover)
|
||||
})
|
||||
|
||||
@@ -343,7 +360,7 @@ describe('Popover', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Popover.getInstance(div)).toEqual(null)
|
||||
expect(Popover.getInstance(div)).toBeNull()
|
||||
const popover = Popover.getOrCreateInstance(div, {
|
||||
placement: 'top'
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ describe('ScrollSpy', () => {
|
||||
const scrollHeight = Math.ceil(contentEl.scrollTop + Manipulator.position(target).top) + paddingTop
|
||||
|
||||
function listener() {
|
||||
expect(element.classList.contains('active')).toEqual(true)
|
||||
expect(element).toHaveClass('active')
|
||||
contentEl.removeEventListener('scroll', listener)
|
||||
expect(scrollSpy._process).toHaveBeenCalled()
|
||||
spy.calls.reset()
|
||||
@@ -73,8 +73,8 @@ describe('ScrollSpy', () => {
|
||||
' </ul>',
|
||||
'</nav>',
|
||||
'<div id="content" style="height: 200px; overflow-y: auto;">',
|
||||
' <div id="two" style="height: 300px;"></div>',
|
||||
' <div id="three" style="height: 10px;"></div>',
|
||||
' <div id="two" style="height: 300px;"></div>',
|
||||
' <div id="three" style="height: 10px;"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -82,7 +82,7 @@ describe('ScrollSpy', () => {
|
||||
target: '#navigation'
|
||||
})
|
||||
|
||||
expect(scrollSpy._targets.length).toEqual(2)
|
||||
expect(scrollSpy._targets).toHaveSize(2)
|
||||
})
|
||||
|
||||
it('should only switch "active" class on current target', done => {
|
||||
@@ -120,7 +120,7 @@ describe('ScrollSpy', () => {
|
||||
spyOn(scrollSpy, '_process').and.callThrough()
|
||||
|
||||
scrollSpyEl.addEventListener('scroll', () => {
|
||||
expect(rootEl.classList.contains('active')).toEqual(true)
|
||||
expect(rootEl).toHaveClass('active')
|
||||
expect(scrollSpy._process).toHaveBeenCalled()
|
||||
done()
|
||||
})
|
||||
@@ -163,7 +163,7 @@ describe('ScrollSpy', () => {
|
||||
spyOn(scrollSpy, '_process').and.callThrough()
|
||||
|
||||
scrollSpyEl.addEventListener('scroll', () => {
|
||||
expect(rootEl.classList.contains('active')).toEqual(true)
|
||||
expect(rootEl).toHaveClass('active')
|
||||
expect(scrollSpy._process).toHaveBeenCalled()
|
||||
done()
|
||||
})
|
||||
@@ -175,16 +175,16 @@ describe('ScrollSpy', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="header" style="height: 500px;"></div>',
|
||||
'<nav id="navigation" class="navbar">',
|
||||
' <ul class="navbar-nav">',
|
||||
' <li class="nav-item"><a class="nav-link active" id="one-link" href="#one">One</a></li>',
|
||||
' <li class="nav-item"><a class="nav-link" id="two-link" href="#two">Two</a></li>',
|
||||
' <li class="nav-item"><a class="nav-link" id="three-link" href="#three">Three</a></li>',
|
||||
' </ul>',
|
||||
' <ul class="navbar-nav">',
|
||||
' <li class="nav-item"><a class="nav-link active" id="one-link" href="#one">One</a></li>',
|
||||
' <li class="nav-item"><a class="nav-link" id="two-link" href="#two">Two</a></li>',
|
||||
' <li class="nav-item"><a class="nav-link" id="three-link" href="#three">Three</a></li>',
|
||||
' </ul>',
|
||||
'</nav>',
|
||||
'<div id="content" style="height: 200px; overflow-y: auto;">',
|
||||
' <div id="one" style="height: 500px;"></div>',
|
||||
' <div id="two" style="height: 300px;"></div>',
|
||||
' <div id="three" style="height: 10px;"></div>',
|
||||
' <div id="one" style="height: 500px;"></div>',
|
||||
' <div id="two" style="height: 300px;"></div>',
|
||||
' <div id="three" style="height: 10px;"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -197,9 +197,9 @@ describe('ScrollSpy', () => {
|
||||
spyOn(scrollSpy, '_process').and.callThrough()
|
||||
|
||||
contentEl.addEventListener('scroll', () => {
|
||||
expect(fixtureEl.querySelector('#one-link').classList.contains('active')).toEqual(false)
|
||||
expect(fixtureEl.querySelector('#two-link').classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('#three-link').classList.contains('active')).toEqual(false)
|
||||
expect(fixtureEl.querySelector('#one-link')).not.toHaveClass('active')
|
||||
expect(fixtureEl.querySelector('#two-link')).toHaveClass('active')
|
||||
expect(fixtureEl.querySelector('#three-link')).not.toHaveClass('active')
|
||||
expect(scrollSpy._process).toHaveBeenCalled()
|
||||
done()
|
||||
})
|
||||
@@ -361,7 +361,7 @@ describe('ScrollSpy', () => {
|
||||
expect(spy).toHaveBeenCalled()
|
||||
spy.calls.reset()
|
||||
if (firstTime) {
|
||||
expect(fixtureEl.querySelectorAll('.active').length).toEqual(1)
|
||||
expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
|
||||
expect(active.getAttribute('id')).toEqual('two-link')
|
||||
firstTime = false
|
||||
contentEl.scrollTop = 0
|
||||
@@ -409,12 +409,12 @@ describe('ScrollSpy', () => {
|
||||
expect(spy).toHaveBeenCalled()
|
||||
spy.calls.reset()
|
||||
if (firstTime) {
|
||||
expect(fixtureEl.querySelectorAll('.active').length).toEqual(1)
|
||||
expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
|
||||
expect(active.getAttribute('id')).toEqual('two-link')
|
||||
firstTime = false
|
||||
contentEl.scrollTop = negativeHeight
|
||||
} else {
|
||||
expect(fixtureEl.querySelectorAll('.active').length).toEqual(1)
|
||||
expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
|
||||
expect(active.getAttribute('id')).toEqual('one-link')
|
||||
done()
|
||||
}
|
||||
@@ -602,7 +602,7 @@ describe('ScrollSpy', () => {
|
||||
|
||||
const scrollspy = ScrollSpy.getInstance(div)
|
||||
expect(scrollspy).not.toBeNull()
|
||||
expect(scrollspy._config.offset).toBe(15)
|
||||
expect(scrollspy._config.offset).toEqual(15)
|
||||
})
|
||||
|
||||
it('should not re create a scrollspy', () => {
|
||||
@@ -663,7 +663,7 @@ describe('ScrollSpy', () => {
|
||||
})
|
||||
|
||||
it('should return null if there is no instance', () => {
|
||||
expect(ScrollSpy.getInstance(fixtureEl)).toEqual(null)
|
||||
expect(ScrollSpy.getInstance(fixtureEl)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -684,7 +684,7 @@ describe('ScrollSpy', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(ScrollSpy.getInstance(div)).toEqual(null)
|
||||
expect(ScrollSpy.getInstance(div)).toBeNull()
|
||||
expect(ScrollSpy.getOrCreateInstance(div)).toBeInstanceOf(ScrollSpy)
|
||||
})
|
||||
|
||||
@@ -693,7 +693,7 @@ describe('ScrollSpy', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(ScrollSpy.getInstance(div)).toEqual(null)
|
||||
expect(ScrollSpy.getInstance(div)).toBeNull()
|
||||
const scrollspy = ScrollSpy.getOrCreateInstance(div, {
|
||||
offset: 1
|
||||
})
|
||||
|
||||
+46
-36
@@ -21,8 +21,12 @@ describe('Tab', () => {
|
||||
describe('constructor', () => {
|
||||
it('should take care of element either passed as a CSS selector or DOM element', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<ul class="nav"><li><a href="#home" role="tab">Home</a></li></ul>',
|
||||
'<ul><li id="home"></li></ul>'
|
||||
'<ul class="nav">',
|
||||
' <li><a href="#home" role="tab">Home</a></li>',
|
||||
'</ul>',
|
||||
'<ul>',
|
||||
' <li id="home"></li>',
|
||||
'</ul>'
|
||||
].join('')
|
||||
|
||||
const tabEl = fixtureEl.querySelector('[href="#home"]')
|
||||
@@ -51,7 +55,7 @@ describe('Tab', () => {
|
||||
const tab = new Tab(profileTriggerEl)
|
||||
|
||||
profileTriggerEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
|
||||
expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -75,7 +79,7 @@ describe('Tab', () => {
|
||||
const tab = new Tab(profileTriggerEl)
|
||||
|
||||
profileTriggerEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
|
||||
expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
|
||||
done()
|
||||
})
|
||||
@@ -99,7 +103,7 @@ describe('Tab', () => {
|
||||
const tab = new Tab(profileTriggerEl)
|
||||
|
||||
profileTriggerEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -110,16 +114,19 @@ describe('Tab', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<nav class="nav">',
|
||||
' <button type="button" data-bs-target="#home" role="tab">Home</button>',
|
||||
' <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</a>',
|
||||
' <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button>',
|
||||
'</nav>',
|
||||
'<div><div id="home" role="tabpanel"></div><div id="profile" role="tabpanel"></div></div>'
|
||||
'<div>',
|
||||
' <div id="home" role="tabpanel"></div>',
|
||||
' <div id="profile" role="tabpanel"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
|
||||
const tab = new Tab(profileTriggerEl)
|
||||
|
||||
profileTriggerEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -132,14 +139,17 @@ describe('Tab', () => {
|
||||
' <button type="button" data-bs-target="#home" role="tab">Home</button>',
|
||||
' <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button>',
|
||||
'</div>',
|
||||
'<div><div id="home" role="tabpanel"></div><div id="profile" role="tabpanel"></div></div>'
|
||||
'<div>',
|
||||
' <div id="home" role="tabpanel"></div>',
|
||||
' <div id="profile" role="tabpanel"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
|
||||
const tab = new Tab(profileTriggerEl)
|
||||
|
||||
profileTriggerEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -248,7 +258,7 @@ describe('Tab', () => {
|
||||
})
|
||||
|
||||
triggerList[0].addEventListener('hidden.bs.tab', ev => {
|
||||
expect(hideCalled).toEqual(true)
|
||||
expect(hideCalled).toBeTrue()
|
||||
expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#profile')
|
||||
done()
|
||||
})
|
||||
@@ -321,7 +331,7 @@ describe('Tab', () => {
|
||||
const secondNavTab = new Tab(secondNavEl)
|
||||
|
||||
secondNavEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(fixtureEl.querySelectorAll('.nav-tab').length).toEqual(2)
|
||||
expect(fixtureEl.querySelectorAll('.nav-tab')).toHaveSize(2)
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -417,7 +427,7 @@ describe('Tab', () => {
|
||||
|
||||
describe('getInstance', () => {
|
||||
it('should return null if there is no instance', () => {
|
||||
expect(Tab.getInstance(fixtureEl)).toEqual(null)
|
||||
expect(Tab.getInstance(fixtureEl)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return this instance', () => {
|
||||
@@ -448,7 +458,7 @@ describe('Tab', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Tab.getInstance(div)).toEqual(null)
|
||||
expect(Tab.getInstance(div)).toBeNull()
|
||||
expect(Tab.getOrCreateInstance(div)).toBeInstanceOf(Tab)
|
||||
})
|
||||
})
|
||||
@@ -469,8 +479,8 @@ describe('Tab', () => {
|
||||
const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
|
||||
|
||||
secondTabTrigger.addEventListener('shown.bs.tab', () => {
|
||||
expect(secondTabTrigger.classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
|
||||
expect(secondTabTrigger).toHaveClass('active')
|
||||
expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -495,9 +505,9 @@ describe('Tab', () => {
|
||||
const firstLiLinkEl = fixtureEl.querySelector('li:first-child a')
|
||||
|
||||
firstLiLinkEl.click()
|
||||
expect(firstLiLinkEl.classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('li:last-child a').classList.contains('active')).toEqual(false)
|
||||
expect(fixtureEl.querySelector('li:last-child .dropdown-menu a:first-child').classList.contains('active')).toEqual(false)
|
||||
expect(firstLiLinkEl).toHaveClass('active')
|
||||
expect(fixtureEl.querySelector('li:last-child a')).not.toHaveClass('active')
|
||||
expect(fixtureEl.querySelector('li:last-child .dropdown-menu a:first-child')).not.toHaveClass('active')
|
||||
})
|
||||
|
||||
it('selecting a dropdown tab does not activate another', () => {
|
||||
@@ -529,10 +539,10 @@ describe('Tab', () => {
|
||||
const firstDropItem = fixtureEl.querySelector('#nav1 .dropdown-item')
|
||||
|
||||
firstDropItem.click()
|
||||
expect(firstDropItem.classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('#nav1 .dropdown-toggle').classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('#nav2 .dropdown-toggle').classList.contains('active')).toEqual(false)
|
||||
expect(fixtureEl.querySelector('#nav2 .dropdown-item').classList.contains('active')).toEqual(false)
|
||||
expect(firstDropItem).toHaveClass('active')
|
||||
expect(fixtureEl.querySelector('#nav1 .dropdown-toggle')).toHaveClass('active')
|
||||
expect(fixtureEl.querySelector('#nav2 .dropdown-toggle')).not.toHaveClass('active')
|
||||
expect(fixtureEl.querySelector('#nav2 .dropdown-item')).not.toHaveClass('active')
|
||||
})
|
||||
|
||||
it('should support li > .dropdown-item', () => {
|
||||
@@ -553,8 +563,8 @@ describe('Tab', () => {
|
||||
const firstDropItem = fixtureEl.querySelector('.dropdown-item')
|
||||
|
||||
firstDropItem.click()
|
||||
expect(firstDropItem.classList.contains('active')).toEqual(true)
|
||||
expect(fixtureEl.querySelector('.nav-link').classList.contains('active')).toEqual(false)
|
||||
expect(firstDropItem).toHaveClass('active')
|
||||
expect(fixtureEl.querySelector('.nav-link')).not.toHaveClass('active')
|
||||
})
|
||||
|
||||
it('should handle nested tabs', done => {
|
||||
@@ -585,12 +595,12 @@ describe('Tab', () => {
|
||||
const xTab1El = fixtureEl.querySelector('#x-tab1')
|
||||
|
||||
tabNested2El.addEventListener('shown.bs.tab', () => {
|
||||
expect(xTab1El.classList.contains('active')).toEqual(true)
|
||||
expect(xTab1El).toHaveClass('active')
|
||||
done()
|
||||
})
|
||||
|
||||
tab1El.addEventListener('shown.bs.tab', () => {
|
||||
expect(xTab1El.classList.contains('active')).toEqual(true)
|
||||
expect(xTab1El).toHaveClass('active')
|
||||
tabNested2El.click()
|
||||
})
|
||||
|
||||
@@ -615,15 +625,15 @@ describe('Tab', () => {
|
||||
const tabHomeEl = fixtureEl.querySelector('#home')
|
||||
|
||||
triggerTabProfileEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(tabProfileEl.classList.contains('fade')).toEqual(true)
|
||||
expect(tabProfileEl.classList.contains('show')).toEqual(true)
|
||||
expect(tabProfileEl).toHaveClass('fade')
|
||||
expect(tabProfileEl).toHaveClass('show')
|
||||
|
||||
triggerTabHomeEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(tabProfileEl.classList.contains('fade')).toEqual(true)
|
||||
expect(tabProfileEl.classList.contains('show')).toEqual(false)
|
||||
expect(tabProfileEl).toHaveClass('fade')
|
||||
expect(tabProfileEl).not.toHaveClass('show')
|
||||
|
||||
expect(tabHomeEl.classList.contains('fade')).toEqual(true)
|
||||
expect(tabHomeEl.classList.contains('show')).toEqual(true)
|
||||
expect(tabHomeEl).toHaveClass('fade')
|
||||
expect(tabHomeEl).toHaveClass('show')
|
||||
|
||||
done()
|
||||
})
|
||||
@@ -653,7 +663,7 @@ describe('Tab', () => {
|
||||
const secondNavEl = fixtureEl.querySelector('#secondNav')
|
||||
|
||||
secondNavEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(fixtureEl.querySelectorAll('.show').length).toEqual(0)
|
||||
expect(fixtureEl.querySelectorAll('.show')).toHaveSize(0)
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -679,7 +689,7 @@ describe('Tab', () => {
|
||||
const secondNavEl = fixtureEl.querySelector('#secondNav')
|
||||
|
||||
secondNavEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(fixtureEl.querySelectorAll('.show').length).toEqual(1)
|
||||
expect(fixtureEl.querySelectorAll('.show')).toHaveSize(1)
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -698,7 +708,7 @@ describe('Tab', () => {
|
||||
spyOn(Event.prototype, 'preventDefault').and.callThrough()
|
||||
|
||||
tabEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(tabEl.classList.contains('active')).toEqual(true)
|
||||
expect(tabEl).toHaveClass('active')
|
||||
expect(Event.prototype.preventDefault).toHaveBeenCalled()
|
||||
done()
|
||||
})
|
||||
|
||||
+14
-14
@@ -51,7 +51,7 @@ describe('Toast', () => {
|
||||
})
|
||||
|
||||
toastEl.addEventListener('shown.bs.toast', () => {
|
||||
expect(toastEl.classList.contains('show')).toEqual(true)
|
||||
expect(toastEl).toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -69,7 +69,7 @@ describe('Toast', () => {
|
||||
const toast = new Toast(toastEl)
|
||||
|
||||
toastEl.addEventListener('shown.bs.toast', () => {
|
||||
expect(toastEl.classList.contains('show')).toEqual(true)
|
||||
expect(toastEl).toHaveClass('show')
|
||||
|
||||
const button = toastEl.querySelector('.btn-close')
|
||||
|
||||
@@ -77,7 +77,7 @@ describe('Toast', () => {
|
||||
})
|
||||
|
||||
toastEl.addEventListener('hidden.bs.toast', () => {
|
||||
expect(toastEl.classList.contains('show')).toEqual(false)
|
||||
expect(toastEl).not.toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -124,7 +124,7 @@ describe('Toast', () => {
|
||||
const toast = new Toast(toastEl)
|
||||
|
||||
toastEl.addEventListener('hidden.bs.toast', () => {
|
||||
expect(toastEl.classList.contains('show')).toEqual(false)
|
||||
expect(toastEl).not.toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -144,7 +144,7 @@ describe('Toast', () => {
|
||||
const toast = new Toast(toastEl)
|
||||
|
||||
toastEl.addEventListener('shown.bs.toast', () => {
|
||||
expect(toastEl.classList.contains('fade')).toEqual(false)
|
||||
expect(toastEl).not.toHaveClass('fade')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -165,7 +165,7 @@ describe('Toast', () => {
|
||||
|
||||
const assertDone = () => {
|
||||
setTimeout(() => {
|
||||
expect(toastEl.classList.contains('show')).toEqual(false)
|
||||
expect(toastEl).not.toHaveClass('show')
|
||||
done()
|
||||
}, 20)
|
||||
}
|
||||
@@ -393,7 +393,7 @@ describe('Toast', () => {
|
||||
' <div class="toast-body">',
|
||||
' a simple toast',
|
||||
' </div>',
|
||||
' </div>'
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const toastEl = fixtureEl.querySelector('.toast')
|
||||
@@ -404,7 +404,7 @@ describe('Toast', () => {
|
||||
})
|
||||
|
||||
toastEl.addEventListener('hidden.bs.toast', () => {
|
||||
expect(toastEl.classList.contains('show')).toEqual(false)
|
||||
expect(toastEl).not.toHaveClass('show')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -438,7 +438,7 @@ describe('Toast', () => {
|
||||
|
||||
const assertDone = () => {
|
||||
setTimeout(() => {
|
||||
expect(toastEl.classList.contains('show')).toEqual(true)
|
||||
expect(toastEl).toHaveClass('show')
|
||||
done()
|
||||
}, 20)
|
||||
}
|
||||
@@ -487,13 +487,13 @@ describe('Toast', () => {
|
||||
const toastEl = fixtureEl.querySelector('div')
|
||||
const toast = new Toast(toastEl)
|
||||
const expected = () => {
|
||||
expect(toastEl.classList.contains('show')).toEqual(true)
|
||||
expect(toastEl).toHaveClass('show')
|
||||
expect(Toast.getInstance(toastEl)).not.toBeNull()
|
||||
|
||||
toast.dispose()
|
||||
|
||||
expect(Toast.getInstance(toastEl)).toBeNull()
|
||||
expect(toastEl.classList.contains('show')).toEqual(false)
|
||||
expect(toastEl).not.toHaveClass('show')
|
||||
|
||||
done()
|
||||
}
|
||||
@@ -582,7 +582,7 @@ describe('Toast', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Toast.getInstance(div)).toEqual(null)
|
||||
expect(Toast.getInstance(div)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -603,7 +603,7 @@ describe('Toast', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Toast.getInstance(div)).toEqual(null)
|
||||
expect(Toast.getInstance(div)).toBeNull()
|
||||
expect(Toast.getOrCreateInstance(div)).toBeInstanceOf(Toast)
|
||||
})
|
||||
|
||||
@@ -612,7 +612,7 @@ describe('Toast', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Toast.getInstance(div)).toEqual(null)
|
||||
expect(Toast.getInstance(div)).toBeNull()
|
||||
const toast = Toast.getOrCreateInstance(div, {
|
||||
delay: 1
|
||||
})
|
||||
|
||||
+139
-119
@@ -78,7 +78,7 @@ describe('Tooltip', () => {
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
expect(tooltip._config.sanitize).toEqual(true)
|
||||
expect(tooltip._config.sanitize).toBeTrue()
|
||||
})
|
||||
|
||||
it('should convert title and content to string if numbers', () => {
|
||||
@@ -137,7 +137,7 @@ describe('Tooltip', () => {
|
||||
|
||||
const offset = tooltip._getOffset()
|
||||
|
||||
expect(typeof offset).toEqual('function')
|
||||
expect(offset).toEqual(jasmine.any(Function))
|
||||
|
||||
tooltip.show()
|
||||
})
|
||||
@@ -180,6 +180,15 @@ describe('Tooltip', () => {
|
||||
expect(getPopperConfig).toHaveBeenCalled()
|
||||
expect(popperConfig.placement).toEqual('left')
|
||||
})
|
||||
|
||||
it('should use original title, if not "data-bs-title" is given', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
expect(tooltip._config.title).toEqual('Another tooltip')
|
||||
})
|
||||
})
|
||||
|
||||
describe('enable', () => {
|
||||
@@ -229,11 +238,11 @@ describe('Tooltip', () => {
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
expect(tooltip._isEnabled).toEqual(true)
|
||||
expect(tooltip._isEnabled).toBeTrue()
|
||||
|
||||
tooltip.toggleEnabled()
|
||||
|
||||
expect(tooltip._isEnabled).toEqual(false)
|
||||
expect(tooltip._isEnabled).toBeFalse()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -354,7 +363,7 @@ describe('Tooltip', () => {
|
||||
|
||||
tooltip.dispose()
|
||||
|
||||
expect(Tooltip.getInstance(tooltipEl)).toEqual(null)
|
||||
expect(Tooltip.getInstance(tooltipEl)).toBeNull()
|
||||
expect(removeEventSpy.calls.allArgs()).toEqual(expectedArgs)
|
||||
})
|
||||
|
||||
@@ -369,8 +378,8 @@ describe('Tooltip', () => {
|
||||
})
|
||||
tooltipEl.addEventListener('hidden.bs.tooltip', () => {
|
||||
tooltip.dispose()
|
||||
expect(tooltip.tip).toEqual(null)
|
||||
expect(Tooltip.getInstance(tooltipEl)).toEqual(null)
|
||||
expect(tooltip.tip).toBeNull()
|
||||
expect(Tooltip.getInstance(tooltipEl)).toBeNull()
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -415,14 +424,15 @@ describe('Tooltip', () => {
|
||||
tooltip.show()
|
||||
})
|
||||
|
||||
it('should show a tooltip when hovering a children element', done => {
|
||||
fixtureEl.innerHTML =
|
||||
'<a href="#" rel="tooltip" title="Another tooltip">' +
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 100 100">' +
|
||||
'<rect width="100%" fill="#563d7c"/>' +
|
||||
'<circle cx="50" cy="50" r="30" fill="#fff"/>' +
|
||||
'</svg>' +
|
||||
it('should show a tooltip when hovering a child element', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" rel="tooltip" title="Another tooltip">',
|
||||
' <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 100 100">',
|
||||
' <rect width="100%" fill="#563d7c"/>',
|
||||
' <circle cx="50" cy="50" r="30" fill="#fff"/>',
|
||||
' </svg>',
|
||||
'</a>'
|
||||
].join('')
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
@@ -465,13 +475,12 @@ describe('Tooltip', () => {
|
||||
})
|
||||
|
||||
tooltipEl.addEventListener('inserted.bs.tooltip', () => {
|
||||
expect(tooltip.getTipElement().classList.contains('bs-tooltip-bottom')).toEqual(true)
|
||||
expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
|
||||
})
|
||||
|
||||
tooltipEl.addEventListener('shown.bs.tooltip', () => {
|
||||
const tooltipShown = document.querySelector('.tooltip')
|
||||
|
||||
expect(tooltipShown.classList.contains('bs-tooltip-bottom')).toEqual(true)
|
||||
expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
|
||||
expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('bottom')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -586,7 +595,7 @@ describe('Tooltip', () => {
|
||||
const tip = document.querySelector('.tooltip')
|
||||
|
||||
expect(tip).not.toBeNull()
|
||||
expect(tip.classList.contains('fade')).toEqual(false)
|
||||
expect(tip).not.toHaveClass('fade')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -670,7 +679,7 @@ describe('Tooltip', () => {
|
||||
|
||||
setTimeout(() => {
|
||||
expect(tooltip.show).toHaveBeenCalled()
|
||||
expect(document.querySelectorAll('.tooltip').length).toEqual(0)
|
||||
expect(document.querySelectorAll('.tooltip')).toHaveSize(0)
|
||||
done()
|
||||
}, 200)
|
||||
|
||||
@@ -689,19 +698,20 @@ describe('Tooltip', () => {
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
expect(tooltip.getTipElement().classList.contains('show')).toEqual(true)
|
||||
expect(tooltip._getTipElement()).toHaveClass('show')
|
||||
tooltipEl.dispatchEvent(createEvent('mouseout'))
|
||||
|
||||
setTimeout(() => {
|
||||
expect(tooltip.getTipElement().classList.contains('show')).toEqual(true)
|
||||
expect(tooltip._getTipElement()).toHaveClass('show')
|
||||
tooltipEl.dispatchEvent(createEvent('mouseover'))
|
||||
}, 100)
|
||||
|
||||
setTimeout(() => {
|
||||
expect(tooltip.getTipElement().classList.contains('show')).toEqual(true)
|
||||
expect(tooltip._getTipElement()).toHaveClass('show')
|
||||
expect(document.querySelectorAll('.tooltip')).toHaveSize(1)
|
||||
done()
|
||||
}, 200)
|
||||
}, 0)
|
||||
}, 10)
|
||||
|
||||
tooltipEl.dispatchEvent(createEvent('mouseover'))
|
||||
})
|
||||
@@ -751,20 +761,20 @@ describe('Tooltip', () => {
|
||||
|
||||
setTimeout(() => {
|
||||
expect(tooltip._popper).not.toBeNull()
|
||||
expect(tooltip.getTipElement().getAttribute('data-popper-placement')).toBe('top')
|
||||
expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('top')
|
||||
tooltipEl.dispatchEvent(createEvent('mouseout'))
|
||||
|
||||
setTimeout(() => {
|
||||
expect(tooltip.getTipElement().classList.contains('show')).toEqual(false)
|
||||
expect(tooltip._getTipElement()).not.toHaveClass('show')
|
||||
tooltipEl.dispatchEvent(createEvent('mouseover'))
|
||||
}, 100)
|
||||
|
||||
setTimeout(() => {
|
||||
expect(tooltip._popper).not.toBeNull()
|
||||
expect(tooltip.getTipElement().getAttribute('data-popper-placement')).toBe('top')
|
||||
expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('top')
|
||||
done()
|
||||
}, 200)
|
||||
}, 0)
|
||||
}, 10)
|
||||
|
||||
tooltipEl.dispatchEvent(createEvent('mouseover'))
|
||||
})
|
||||
@@ -809,7 +819,7 @@ describe('Tooltip', () => {
|
||||
tooltipEl.addEventListener('shown.bs.tooltip', () => {
|
||||
const tip = document.querySelector('.tooltip')
|
||||
expect(tip).not.toBeNull()
|
||||
expect(tip.classList.contains('custom-class')).toBeTrue()
|
||||
expect(tip).toHaveClass('custom-class')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -827,8 +837,8 @@ describe('Tooltip', () => {
|
||||
tooltipEl.addEventListener('shown.bs.tooltip', () => {
|
||||
const tip = document.querySelector('.tooltip')
|
||||
expect(tip).not.toBeNull()
|
||||
expect(tip.classList.contains('custom-class')).toBeTrue()
|
||||
expect(tip.classList.contains('custom-class-2')).toBeTrue()
|
||||
expect(tip).toHaveClass('custom-class')
|
||||
expect(tip).toHaveClass('custom-class-2')
|
||||
done()
|
||||
})
|
||||
|
||||
@@ -848,12 +858,25 @@ describe('Tooltip', () => {
|
||||
const tip = document.querySelector('.tooltip')
|
||||
expect(tip).not.toBeNull()
|
||||
expect(spy).toHaveBeenCalled()
|
||||
expect(tip.classList.contains('custom-class')).toBeTrue()
|
||||
expect(tip).toHaveClass('custom-class')
|
||||
done()
|
||||
})
|
||||
|
||||
tooltip.show()
|
||||
})
|
||||
|
||||
it('should remove `title` attribute if exists', done => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
tooltipEl.addEventListener('shown.bs.tooltip', () => {
|
||||
expect(tooltipEl.getAttribute('title')).toBeNull()
|
||||
done()
|
||||
})
|
||||
tooltip.show()
|
||||
})
|
||||
})
|
||||
|
||||
describe('hide', () => {
|
||||
@@ -985,14 +1008,14 @@ describe('Tooltip', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('isWithContent', () => {
|
||||
describe('_isWithContent', () => {
|
||||
it('should return true if there is content', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
expect(tooltip.isWithContent()).toEqual(true)
|
||||
expect(tooltip._isWithContent()).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return false if there is no content', () => {
|
||||
@@ -1001,11 +1024,11 @@ describe('Tooltip', () => {
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
expect(tooltip.isWithContent()).toEqual(false)
|
||||
expect(tooltip._isWithContent()).toBeFalse()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getTipElement', () => {
|
||||
describe('_getTipElement', () => {
|
||||
it('should create the tip element and return it', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
|
||||
@@ -1014,7 +1037,7 @@ describe('Tooltip', () => {
|
||||
|
||||
spyOn(document, 'createElement').and.callThrough()
|
||||
|
||||
expect(tooltip.getTipElement()).toBeDefined()
|
||||
expect(tooltip._getTipElement()).toBeDefined()
|
||||
expect(document.createElement).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -1026,12 +1049,12 @@ describe('Tooltip', () => {
|
||||
|
||||
const spy = spyOn(document, 'createElement').and.callThrough()
|
||||
|
||||
expect(tooltip.getTipElement()).toBeDefined()
|
||||
expect(tooltip._getTipElement()).toBeDefined()
|
||||
expect(spy).toHaveBeenCalled()
|
||||
|
||||
spy.calls.reset()
|
||||
|
||||
expect(tooltip.getTipElement()).toBeDefined()
|
||||
expect(tooltip._getTipElement()).toBeDefined()
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1041,84 +1064,78 @@ describe('Tooltip', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
const tooltip = new Tooltip(tooltipEl, { animation: false })
|
||||
|
||||
const tip = tooltip.getTipElement()
|
||||
const tip = tooltip._getTipElement()
|
||||
|
||||
tooltip.setContent(tip)
|
||||
|
||||
expect(tip.classList.contains('show')).toEqual(false)
|
||||
expect(tip.classList.contains('fade')).toEqual(false)
|
||||
expect(tip).not.toHaveClass('show')
|
||||
expect(tip).not.toHaveClass('fade')
|
||||
expect(tip.querySelector('.tooltip-inner').textContent).toEqual('Another tooltip')
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateAttachment', () => {
|
||||
it('should use end class name when right placement specified', done => {
|
||||
it('should re-show tip if it was already shown', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
tooltip.show()
|
||||
const tip = () => tooltip._getTipElement()
|
||||
|
||||
expect(tip()).toHaveClass('show')
|
||||
tooltip.setContent({ '.tooltip-inner': 'foo' })
|
||||
|
||||
expect(tip()).toHaveClass('show')
|
||||
expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
|
||||
})
|
||||
|
||||
it('should keep tip hidden, if it was already hidden before', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
const tip = () => tooltip._getTipElement()
|
||||
|
||||
expect(tip()).not.toHaveClass('show')
|
||||
tooltip.setContent({ '.tooltip-inner': 'foo' })
|
||||
|
||||
expect(tip()).not.toHaveClass('show')
|
||||
expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
|
||||
})
|
||||
|
||||
it('"setContent" should keep the initial template', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl, {
|
||||
placement: 'right'
|
||||
})
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
tooltipEl.addEventListener('inserted.bs.tooltip', () => {
|
||||
expect(tooltip.getTipElement().classList.contains('bs-tooltip-end')).toEqual(true)
|
||||
done()
|
||||
})
|
||||
tooltip.setContent({ '.tooltip-inner': 'foo' })
|
||||
const tip = tooltip._getTipElement()
|
||||
|
||||
tooltip.show()
|
||||
})
|
||||
|
||||
it('should use start class name when left placement specified', done => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl, {
|
||||
placement: 'left'
|
||||
})
|
||||
|
||||
tooltipEl.addEventListener('inserted.bs.tooltip', () => {
|
||||
expect(tooltip.getTipElement().classList.contains('bs-tooltip-start')).toEqual(true)
|
||||
done()
|
||||
})
|
||||
|
||||
tooltip.show()
|
||||
expect(tip).toHaveClass('tooltip')
|
||||
expect(tip).toHaveClass('bs-tooltip-auto')
|
||||
expect(tip.querySelector('.tooltip-arrow')).not.toBeNull()
|
||||
expect(tip.querySelector('.tooltip-inner')).not.toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('setElementContent', () => {
|
||||
describe('setContent', () => {
|
||||
it('should do nothing if the element is null', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
tooltip.setElementContent(null, null)
|
||||
tooltip.setContent({ '.tooltip': null })
|
||||
expect().nothing()
|
||||
})
|
||||
|
||||
it('should add the content as a child of the element', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" rel="tooltip" title="Another tooltip">',
|
||||
'<div id="childContent"></div>'
|
||||
].join('')
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const childContent = fixtureEl.querySelector('div')
|
||||
const tooltip = new Tooltip(tooltipEl, {
|
||||
html: true
|
||||
})
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), childContent)
|
||||
|
||||
expect(childContent.parentNode).toEqual(tooltip.getTipElement())
|
||||
})
|
||||
|
||||
it('should do nothing if the content is a child of the element', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" rel="tooltip" title="Another tooltip">',
|
||||
'<div id="childContent"></div>'
|
||||
' <div id="childContent"></div>',
|
||||
'</a>'
|
||||
].join('')
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
@@ -1127,8 +1144,8 @@ describe('Tooltip', () => {
|
||||
html: true
|
||||
})
|
||||
|
||||
tooltip.getTipElement().append(childContent)
|
||||
tooltip.setElementContent(tooltip.getTipElement(), childContent)
|
||||
tooltip._getTipElement().append(childContent)
|
||||
tooltip.setContent({ '.tooltip': childContent })
|
||||
|
||||
expect().nothing()
|
||||
})
|
||||
@@ -1136,7 +1153,8 @@ describe('Tooltip', () => {
|
||||
it('should add the content as a child of the element for jQuery elements', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" rel="tooltip" title="Another tooltip">',
|
||||
'<div id="childContent"></div>'
|
||||
' <div id="childContent"></div>',
|
||||
'</a>'
|
||||
].join('')
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
@@ -1145,28 +1163,29 @@ describe('Tooltip', () => {
|
||||
html: true
|
||||
})
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), { 0: childContent, jquery: 'jQuery' })
|
||||
tooltip.setContent({ '.tooltip': { 0: childContent, jquery: 'jQuery' } })
|
||||
|
||||
expect(childContent.parentNode).toEqual(tooltip.getTipElement())
|
||||
expect(childContent.parentNode).toEqual(tooltip._getTipElement())
|
||||
})
|
||||
|
||||
it('should add the child text content in the element', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" rel="tooltip" title="Another tooltip">',
|
||||
'<div id="childContent">Tooltip</div>'
|
||||
' <div id="childContent">Tooltip</div>',
|
||||
'</a>'
|
||||
].join('')
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const childContent = fixtureEl.querySelector('div')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), childContent)
|
||||
tooltip.setContent({ '.tooltip': childContent })
|
||||
|
||||
expect(childContent.textContent).toEqual(tooltip.getTipElement().textContent)
|
||||
expect(childContent.textContent).toEqual(tooltip._getTipElement().textContent)
|
||||
})
|
||||
|
||||
it('should add html without sanitize it', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl, {
|
||||
@@ -1174,49 +1193,50 @@ describe('Tooltip', () => {
|
||||
html: true
|
||||
})
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), '<div id="childContent">Tooltip</div>')
|
||||
tooltip.setContent({ '.tooltip': '<div id="childContent">Tooltip</div>' })
|
||||
|
||||
expect(tooltip.getTipElement().querySelector('div').id).toEqual('childContent')
|
||||
expect(tooltip._getTipElement().querySelector('div').id).toEqual('childContent')
|
||||
})
|
||||
|
||||
it('should add html sanitized', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl, {
|
||||
html: true
|
||||
})
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), [
|
||||
const content = [
|
||||
'<div id="childContent">',
|
||||
' <button type="button">test btn</button>',
|
||||
' <button type="button">test btn</button>',
|
||||
'</div>'
|
||||
].join(''))
|
||||
].join('')
|
||||
|
||||
expect(tooltip.getTipElement().querySelector('div').id).toEqual('childContent')
|
||||
expect(tooltip.getTipElement().querySelector('button')).toEqual(null)
|
||||
tooltip.setContent({ '.tooltip': content })
|
||||
expect(tooltip._getTipElement().querySelector('div').id).toEqual('childContent')
|
||||
expect(tooltip._getTipElement().querySelector('button')).toBeNull()
|
||||
})
|
||||
|
||||
it('should add text content', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
tooltip.setElementContent(tooltip.getTipElement(), 'test')
|
||||
tooltip.setContent({ '.tooltip': 'test' })
|
||||
|
||||
expect(tooltip.getTipElement().textContent).toEqual('test')
|
||||
expect(tooltip._getTipElement().textContent).toEqual('test')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getTitle', () => {
|
||||
describe('_getTitle', () => {
|
||||
it('should return the title', () => {
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
|
||||
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
|
||||
|
||||
const tooltipEl = fixtureEl.querySelector('a')
|
||||
const tooltip = new Tooltip(tooltipEl)
|
||||
|
||||
expect(tooltip.getTitle()).toEqual('Another tooltip')
|
||||
expect(tooltip._getTitle()).toEqual('Another tooltip')
|
||||
})
|
||||
|
||||
it('should call title function', () => {
|
||||
@@ -1227,7 +1247,7 @@ describe('Tooltip', () => {
|
||||
title: () => 'test'
|
||||
})
|
||||
|
||||
expect(tooltip.getTitle()).toEqual('test')
|
||||
expect(tooltip._getTitle()).toEqual('test')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1247,7 +1267,7 @@ describe('Tooltip', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Tooltip.getInstance(div)).toEqual(null)
|
||||
expect(Tooltip.getInstance(div)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1321,7 +1341,7 @@ describe('Tooltip', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Tooltip.getInstance(div)).toEqual(null)
|
||||
expect(Tooltip.getInstance(div)).toBeNull()
|
||||
expect(Tooltip.getOrCreateInstance(div)).toBeInstanceOf(Tooltip)
|
||||
})
|
||||
|
||||
@@ -1330,13 +1350,13 @@ describe('Tooltip', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Tooltip.getInstance(div)).toEqual(null)
|
||||
expect(Tooltip.getInstance(div)).toBeNull()
|
||||
const tooltip = Tooltip.getOrCreateInstance(div, {
|
||||
title: () => 'test'
|
||||
})
|
||||
expect(tooltip).toBeInstanceOf(Tooltip)
|
||||
|
||||
expect(tooltip.getTitle()).toEqual('test')
|
||||
expect(tooltip._getTitle()).toEqual('test')
|
||||
})
|
||||
|
||||
it('should return the instance when exists without given configuration', () => {
|
||||
@@ -1354,7 +1374,7 @@ describe('Tooltip', () => {
|
||||
expect(tooltip).toBeInstanceOf(Tooltip)
|
||||
expect(tooltip2).toEqual(tooltip)
|
||||
|
||||
expect(tooltip2.getTitle()).toEqual('nothing')
|
||||
expect(tooltip2._getTitle()).toEqual('nothing')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -23,53 +23,53 @@ describe('Backdrop', () => {
|
||||
})
|
||||
|
||||
describe('show', () => {
|
||||
it('if it is "shown", should append the backdrop html once, on show, and contain "show" class', done => {
|
||||
it('should append the backdrop html once on show and include the "show" class if it is "shown"', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: true,
|
||||
isAnimated: false
|
||||
})
|
||||
const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
|
||||
|
||||
expect(getElements().length).toEqual(0)
|
||||
expect(getElements()).toHaveSize(0)
|
||||
|
||||
instance.show()
|
||||
instance.show(() => {
|
||||
expect(getElements().length).toEqual(1)
|
||||
expect(getElements()).toHaveSize(1)
|
||||
for (const el of getElements()) {
|
||||
expect(el.classList.contains(CLASS_NAME_SHOW)).toEqual(true)
|
||||
expect(el).toHaveClass(CLASS_NAME_SHOW)
|
||||
}
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('if it is not "shown", should not append the backdrop html', done => {
|
||||
it('should not append the backdrop html if it is not "shown"', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: false,
|
||||
isAnimated: true
|
||||
})
|
||||
const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
|
||||
|
||||
expect(getElements().length).toEqual(0)
|
||||
expect(getElements()).toHaveSize(0)
|
||||
instance.show(() => {
|
||||
expect(getElements().length).toEqual(0)
|
||||
expect(getElements()).toHaveSize(0)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('if it is "shown" and "animated", should append the backdrop html once, and contain "fade" class', done => {
|
||||
it('should append the backdrop html once and include the "fade" class if it is "shown" and "animated"', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: true,
|
||||
isAnimated: true
|
||||
})
|
||||
const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
|
||||
|
||||
expect(getElements().length).toEqual(0)
|
||||
expect(getElements()).toHaveSize(0)
|
||||
|
||||
instance.show(() => {
|
||||
expect(getElements().length).toEqual(1)
|
||||
expect(getElements()).toHaveSize(1)
|
||||
for (const el of getElements()) {
|
||||
expect(el.classList.contains(CLASS_NAME_FADE)).toEqual(true)
|
||||
expect(el).toHaveClass(CLASS_NAME_FADE)
|
||||
}
|
||||
|
||||
done()
|
||||
@@ -86,17 +86,17 @@ describe('Backdrop', () => {
|
||||
|
||||
const getElements = () => document.body.querySelectorAll(CLASS_BACKDROP)
|
||||
|
||||
expect(getElements().length).toEqual(0)
|
||||
expect(getElements()).toHaveSize(0)
|
||||
instance.show(() => {
|
||||
expect(getElements().length).toEqual(1)
|
||||
expect(getElements()).toHaveSize(1)
|
||||
instance.hide(() => {
|
||||
expect(getElements().length).toEqual(0)
|
||||
expect(getElements()).toHaveSize(0)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should remove "show" class', done => {
|
||||
it('should remove the "show" class', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: true,
|
||||
isAnimated: true
|
||||
@@ -105,12 +105,12 @@ describe('Backdrop', () => {
|
||||
|
||||
instance.show()
|
||||
instance.hide(() => {
|
||||
expect(elem.classList.contains(CLASS_NAME_SHOW)).toEqual(false)
|
||||
expect(elem).not.toHaveClass(CLASS_NAME_SHOW)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('if it is not "shown", should not try to remove Node on remove method', done => {
|
||||
it('should not try to remove Node on remove method if it is not "shown"', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: false,
|
||||
isAnimated: true
|
||||
@@ -118,13 +118,13 @@ describe('Backdrop', () => {
|
||||
const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
|
||||
const spy = spyOn(instance, 'dispose').and.callThrough()
|
||||
|
||||
expect(getElements().length).toEqual(0)
|
||||
expect(instance._isAppended).toEqual(false)
|
||||
expect(getElements()).toHaveSize(0)
|
||||
expect(instance._isAppended).toBeFalse()
|
||||
instance.show(() => {
|
||||
instance.hide(() => {
|
||||
expect(getElements().length).toEqual(0)
|
||||
expect(getElements()).toHaveSize(0)
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
expect(instance._isAppended).toEqual(false)
|
||||
expect(instance._isAppended).toBeFalse()
|
||||
done()
|
||||
})
|
||||
})
|
||||
@@ -145,7 +145,7 @@ describe('Backdrop', () => {
|
||||
instance.show(() => {
|
||||
wrapper.remove()
|
||||
instance.hide(() => {
|
||||
expect(getElements().length).toEqual(0)
|
||||
expect(getElements()).toHaveSize(0)
|
||||
done()
|
||||
})
|
||||
})
|
||||
@@ -153,7 +153,7 @@ describe('Backdrop', () => {
|
||||
})
|
||||
|
||||
describe('click callback', () => {
|
||||
it('it should execute callback on click', done => {
|
||||
it('should execute callback on click', done => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
|
||||
const instance = new Backdrop({
|
||||
@@ -178,7 +178,7 @@ describe('Backdrop', () => {
|
||||
})
|
||||
|
||||
describe('animation callbacks', () => {
|
||||
it('if it is animated, should show and hide backdrop after counting transition duration', done => {
|
||||
it('should show and hide backdrop after counting transition duration if it is animated', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: true,
|
||||
isAnimated: true
|
||||
@@ -200,7 +200,7 @@ describe('Backdrop', () => {
|
||||
expect(spy2).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('if it is not animated, should show and hide backdrop without delay', done => {
|
||||
it('should show and hide backdrop without a delay if it is not animated', done => {
|
||||
const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
|
||||
const instance = new Backdrop({
|
||||
isVisible: true,
|
||||
@@ -218,7 +218,7 @@ describe('Backdrop', () => {
|
||||
}, 10)
|
||||
})
|
||||
|
||||
it('if it is not "shown", should not call delay callbacks', done => {
|
||||
it('should not call delay callbacks if it is not "shown"', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: false,
|
||||
isAnimated: true
|
||||
@@ -232,9 +232,10 @@ describe('Backdrop', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Config', () => {
|
||||
describe('rootElement initialization', () => {
|
||||
it('Should be appended on "document.body" by default', done => {
|
||||
it('should be appended on "document.body" by default', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: true
|
||||
})
|
||||
@@ -245,7 +246,7 @@ describe('Backdrop', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('Should find the rootElement if passed as a string', done => {
|
||||
it('should find the rootElement if passed as a string', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: true,
|
||||
rootElement: 'body'
|
||||
@@ -257,11 +258,8 @@ describe('Backdrop', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('Should appended on any element given by the proper config', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="wrapper">',
|
||||
'</div>'
|
||||
].join('')
|
||||
it('should be appended on any element given by the proper config', done => {
|
||||
fixtureEl.innerHTML = '<div id="wrapper"></div>'
|
||||
|
||||
const wrapper = fixtureEl.querySelector('#wrapper')
|
||||
const instance = new Backdrop({
|
||||
@@ -277,7 +275,7 @@ describe('Backdrop', () => {
|
||||
})
|
||||
|
||||
describe('ClassName', () => {
|
||||
it('Should be able to have different classNames than default', done => {
|
||||
it('should allow configuring className', done => {
|
||||
const instance = new Backdrop({
|
||||
isVisible: true,
|
||||
className: 'foo'
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('Plugin functions', () => {
|
||||
it('should get Plugin and execute the given method, when a click occurred on data-bs-dismiss="PluginName"', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="foo" class="test">',
|
||||
' <button type="button" data-bs-dismiss="test" data-bs-target="#foo"></button>',
|
||||
' <button type="button" data-bs-dismiss="test" data-bs-target="#foo"></button>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('Plugin functions', () => {
|
||||
it('if data-bs-dismiss="PluginName" hasn\'t got "data-bs-target", "getOrCreateInstance" has to be initialized by closest "plugin.Name" class', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="foo" class="test">',
|
||||
' <button type="button" data-bs-dismiss="test"></button>',
|
||||
' <button type="button" data-bs-dismiss="test"></button>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('Plugin functions', () => {
|
||||
it('if data-bs-dismiss="PluginName" is disabled, must not trigger function', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="foo" class="test">',
|
||||
' <button type="button" disabled data-bs-dismiss="test"></button>',
|
||||
' <button type="button" disabled data-bs-dismiss="test"></button>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -90,7 +90,7 @@ describe('Plugin functions', () => {
|
||||
it('should prevent default when the trigger is <a> or <area>', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="foo" class="test">',
|
||||
' <a type="button" data-bs-dismiss="test"></a>',
|
||||
' <a type="button" data-bs-dismiss="test"></a>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ describe('FocusTrap', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" id="outside">outside</a>',
|
||||
'<div id="focustrap" tabindex="-1">',
|
||||
' <a href="#" id="inside">inside</a>',
|
||||
' <a href="#" id="inside">inside</a>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -78,9 +78,9 @@ describe('FocusTrap', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" id="outside">outside</a>',
|
||||
'<div id="focustrap" tabindex="-1">',
|
||||
' <a href="#" id="first">first</a>',
|
||||
' <a href="#" id="inside">inside</a>',
|
||||
' <a href="#" id="last">last</a>',
|
||||
' <a href="#" id="first">first</a>',
|
||||
' <a href="#" id="inside">inside</a>',
|
||||
' <a href="#" id="last">last</a>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -115,9 +115,9 @@ describe('FocusTrap', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a href="#" id="outside">outside</a>',
|
||||
'<div id="focustrap" tabindex="-1">',
|
||||
' <a href="#" id="first">first</a>',
|
||||
' <a href="#" id="inside">inside</a>',
|
||||
' <a href="#" id="last">last</a>',
|
||||
' <a href="#" id="first">first</a>',
|
||||
' <a href="#" id="inside">inside</a>',
|
||||
' <a href="#" id="last">last</a>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -182,10 +182,10 @@ describe('FocusTrap', () => {
|
||||
it('should flag itself as no longer active', () => {
|
||||
const focustrap = new FocusTrap({ trapElement: fixtureEl })
|
||||
focustrap.activate()
|
||||
expect(focustrap._isActive).toBe(true)
|
||||
expect(focustrap._isActive).toBeTrue()
|
||||
|
||||
focustrap.deactivate()
|
||||
expect(focustrap._isActive).toBe(false)
|
||||
expect(focustrap._isActive).toBeFalse()
|
||||
})
|
||||
|
||||
it('should remove all event listeners', () => {
|
||||
|
||||
@@ -179,9 +179,9 @@ describe('Util', () => {
|
||||
|
||||
const el = fixtureEl.querySelector('#foo')
|
||||
|
||||
expect(Util.isElement(el)).toEqual(true)
|
||||
expect(Util.isElement({})).toEqual(false)
|
||||
expect(Util.isElement(fixtureEl.querySelectorAll('.test'))).toEqual(false)
|
||||
expect(Util.isElement(el)).toBeTrue()
|
||||
expect(Util.isElement({})).toBeFalse()
|
||||
expect(Util.isElement(fixtureEl.querySelectorAll('.test'))).toBeFalse()
|
||||
})
|
||||
|
||||
it('should detect jQuery element', () => {
|
||||
@@ -193,7 +193,7 @@ describe('Util', () => {
|
||||
jquery: 'foo'
|
||||
}
|
||||
|
||||
expect(Util.isElement(fakejQuery)).toEqual(true)
|
||||
expect(Util.isElement(fakejQuery)).toBeTrue()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -274,12 +274,12 @@ describe('Util', () => {
|
||||
|
||||
describe('isVisible', () => {
|
||||
it('should return false if the element is not defined', () => {
|
||||
expect(Util.isVisible(null)).toEqual(false)
|
||||
expect(Util.isVisible(undefined)).toEqual(false)
|
||||
expect(Util.isVisible(null)).toBeFalse()
|
||||
expect(Util.isVisible(undefined)).toBeFalse()
|
||||
})
|
||||
|
||||
it('should return false if the element provided is not a dom element', () => {
|
||||
expect(Util.isVisible({})).toEqual(false)
|
||||
expect(Util.isVisible({})).toBeFalse()
|
||||
})
|
||||
|
||||
it('should return false if the element is not visible with display none', () => {
|
||||
@@ -287,7 +287,7 @@ describe('Util', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Util.isVisible(div)).toEqual(false)
|
||||
expect(Util.isVisible(div)).toBeFalse()
|
||||
})
|
||||
|
||||
it('should return false if the element is not visible with visibility hidden', () => {
|
||||
@@ -295,7 +295,7 @@ describe('Util', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Util.isVisible(div)).toEqual(false)
|
||||
expect(Util.isVisible(div)).toBeFalse()
|
||||
})
|
||||
|
||||
it('should return false if an ancestor element is display none', () => {
|
||||
@@ -311,7 +311,7 @@ describe('Util', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('.content')
|
||||
|
||||
expect(Util.isVisible(div)).toEqual(false)
|
||||
expect(Util.isVisible(div)).toBeFalse()
|
||||
})
|
||||
|
||||
it('should return false if an ancestor element is visibility hidden', () => {
|
||||
@@ -327,7 +327,7 @@ describe('Util', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('.content')
|
||||
|
||||
expect(Util.isVisible(div)).toEqual(false)
|
||||
expect(Util.isVisible(div)).toBeFalse()
|
||||
})
|
||||
|
||||
it('should return true if an ancestor element is visibility hidden, but reverted', () => {
|
||||
@@ -343,7 +343,7 @@ describe('Util', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('.content')
|
||||
|
||||
expect(Util.isVisible(div)).toEqual(true)
|
||||
expect(Util.isVisible(div)).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return true if the element is visible', () => {
|
||||
@@ -355,7 +355,7 @@ describe('Util', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('#element')
|
||||
|
||||
expect(Util.isVisible(div)).toEqual(true)
|
||||
expect(Util.isVisible(div)).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return false if the element is hidden, but not via display or visibility', () => {
|
||||
@@ -367,20 +367,20 @@ describe('Util', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('#element')
|
||||
|
||||
expect(Util.isVisible(div)).toEqual(false)
|
||||
expect(Util.isVisible(div)).toBeFalse()
|
||||
})
|
||||
})
|
||||
|
||||
describe('isDisabled', () => {
|
||||
it('should return true if the element is not defined', () => {
|
||||
expect(Util.isDisabled(null)).toEqual(true)
|
||||
expect(Util.isDisabled(undefined)).toEqual(true)
|
||||
expect(Util.isDisabled()).toEqual(true)
|
||||
expect(Util.isDisabled(null)).toBeTrue()
|
||||
expect(Util.isDisabled(undefined)).toBeTrue()
|
||||
expect(Util.isDisabled()).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return true if the element provided is not a dom element', () => {
|
||||
expect(Util.isDisabled({})).toEqual(true)
|
||||
expect(Util.isDisabled('test')).toEqual(true)
|
||||
expect(Util.isDisabled({})).toBeTrue()
|
||||
expect(Util.isDisabled('test')).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return true if the element has disabled attribute', () => {
|
||||
@@ -396,9 +396,9 @@ describe('Util', () => {
|
||||
const div1 = fixtureEl.querySelector('#element1')
|
||||
const div2 = fixtureEl.querySelector('#element2')
|
||||
|
||||
expect(Util.isDisabled(div)).toEqual(true)
|
||||
expect(Util.isDisabled(div1)).toEqual(true)
|
||||
expect(Util.isDisabled(div2)).toEqual(true)
|
||||
expect(Util.isDisabled(div)).toBeTrue()
|
||||
expect(Util.isDisabled(div1)).toBeTrue()
|
||||
expect(Util.isDisabled(div2)).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return false if the element has disabled attribute with "false" value, or doesn\'t have attribute', () => {
|
||||
@@ -412,8 +412,8 @@ describe('Util', () => {
|
||||
const div = fixtureEl.querySelector('#element')
|
||||
const div1 = fixtureEl.querySelector('#element1')
|
||||
|
||||
expect(Util.isDisabled(div)).toEqual(false)
|
||||
expect(Util.isDisabled(div1)).toEqual(false)
|
||||
expect(Util.isDisabled(div)).toBeFalse()
|
||||
expect(Util.isDisabled(div1)).toBeFalse()
|
||||
})
|
||||
|
||||
it('should return false if the element is not disabled ', () => {
|
||||
@@ -427,10 +427,11 @@ describe('Util', () => {
|
||||
|
||||
const el = selector => fixtureEl.querySelector(selector)
|
||||
|
||||
expect(Util.isDisabled(el('#button'))).toEqual(false)
|
||||
expect(Util.isDisabled(el('#select'))).toEqual(false)
|
||||
expect(Util.isDisabled(el('#input'))).toEqual(false)
|
||||
expect(Util.isDisabled(el('#button'))).toBeFalse()
|
||||
expect(Util.isDisabled(el('#select'))).toBeFalse()
|
||||
expect(Util.isDisabled(el('#input'))).toBeFalse()
|
||||
})
|
||||
|
||||
it('should return true if the element has disabled attribute', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div>',
|
||||
@@ -446,12 +447,12 @@ describe('Util', () => {
|
||||
|
||||
const el = selector => fixtureEl.querySelector(selector)
|
||||
|
||||
expect(Util.isDisabled(el('#input'))).toEqual(true)
|
||||
expect(Util.isDisabled(el('#input1'))).toEqual(true)
|
||||
expect(Util.isDisabled(el('#button'))).toEqual(true)
|
||||
expect(Util.isDisabled(el('#button1'))).toEqual(true)
|
||||
expect(Util.isDisabled(el('#button2'))).toEqual(true)
|
||||
expect(Util.isDisabled(el('#input'))).toEqual(true)
|
||||
expect(Util.isDisabled(el('#input'))).toBeTrue()
|
||||
expect(Util.isDisabled(el('#input1'))).toBeTrue()
|
||||
expect(Util.isDisabled(el('#button'))).toBeTrue()
|
||||
expect(Util.isDisabled(el('#button1'))).toBeTrue()
|
||||
expect(Util.isDisabled(el('#button2'))).toBeTrue()
|
||||
expect(Util.isDisabled(el('#input'))).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return true if the element has class "disabled"', () => {
|
||||
@@ -463,7 +464,7 @@ describe('Util', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('#element')
|
||||
|
||||
expect(Util.isDisabled(div)).toEqual(true)
|
||||
expect(Util.isDisabled(div)).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return true if the element has class "disabled" but disabled attribute is false', () => {
|
||||
@@ -475,7 +476,7 @@ describe('Util', () => {
|
||||
|
||||
const div = fixtureEl.querySelector('#input')
|
||||
|
||||
expect(Util.isDisabled(div)).toEqual(true)
|
||||
expect(Util.isDisabled(div)).toBeTrue()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -493,7 +494,7 @@ describe('Util', () => {
|
||||
|
||||
spyOn(document.documentElement, 'attachShadow').and.returnValue(null)
|
||||
|
||||
expect(Util.findShadowRoot(div)).toEqual(null)
|
||||
expect(Util.findShadowRoot(div)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return null when we do not find a shadow root', () => {
|
||||
@@ -505,7 +506,7 @@ describe('Util', () => {
|
||||
|
||||
spyOn(document, 'getRootNode').and.returnValue(undefined)
|
||||
|
||||
expect(Util.findShadowRoot(document)).toEqual(null)
|
||||
expect(Util.findShadowRoot(document)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return the shadow root when found', () => {
|
||||
@@ -532,7 +533,7 @@ describe('Util', () => {
|
||||
|
||||
describe('noop', () => {
|
||||
it('should be a function', () => {
|
||||
expect(typeof Util.noop).toEqual('function')
|
||||
expect(Util.noop).toEqual(jasmine.any(Function))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -569,14 +570,14 @@ describe('Util', () => {
|
||||
document.body.setAttribute('data-bs-no-jquery', '')
|
||||
|
||||
expect(window.jQuery).toEqual(fakejQuery)
|
||||
expect(Util.getjQuery()).toEqual(null)
|
||||
expect(Util.getjQuery()).toBeNull()
|
||||
|
||||
document.body.removeAttribute('data-bs-no-jquery')
|
||||
})
|
||||
|
||||
it('should not return jQuery if not present', () => {
|
||||
window.jQuery = undefined
|
||||
expect(Util.getjQuery()).toEqual(null)
|
||||
expect(Util.getjQuery()).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -628,9 +629,9 @@ describe('Util', () => {
|
||||
pluginMock.jQueryInterface = function () {}
|
||||
|
||||
Util.defineJQueryPlugin(pluginMock)
|
||||
expect(fakejQuery.fn.test).toBe(pluginMock.jQueryInterface)
|
||||
expect(fakejQuery.fn.test.Constructor).toBe(pluginMock)
|
||||
expect(typeof fakejQuery.fn.test.noConflict).toEqual('function')
|
||||
expect(fakejQuery.fn.test).toEqual(pluginMock.jQueryInterface)
|
||||
expect(fakejQuery.fn.test.Constructor).toEqual(pluginMock)
|
||||
expect(fakejQuery.fn.test.noConflict).toEqual(jasmine.any(Function))
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import ScrollBarHelper from '../../../src/util/scrollbar'
|
||||
describe('ScrollBar', () => {
|
||||
let fixtureEl
|
||||
const doc = document.documentElement
|
||||
const parseInt = arg => Number.parseInt(arg, 10)
|
||||
const getPaddingX = el => parseInt(window.getComputedStyle(el).paddingRight)
|
||||
const getMarginX = el => parseInt(window.getComputedStyle(el).marginRight)
|
||||
const parseIntDecimal = arg => Number.parseInt(arg, 10)
|
||||
const getPaddingX = el => parseIntDecimal(window.getComputedStyle(el).paddingRight)
|
||||
const getMarginX = el => parseIntDecimal(window.getComputedStyle(el).marginRight)
|
||||
const getOverFlow = el => el.style.overflow
|
||||
const getPaddingAttr = el => Manipulator.getDataAttribute(el, 'padding-right')
|
||||
const getMarginAttr = el => Manipulator.getDataAttribute(el, 'margin-right')
|
||||
@@ -24,7 +24,9 @@ describe('ScrollBar', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const isScrollBarHidden = () => { // IOS devices, Android devices and Browsers on Mac, hide scrollbar by default and appear it, only while scrolling. So the tests for scrollbar would fail
|
||||
// iOS, Android devices and macOS browsers hide scrollbar by default and show it only while scrolling.
|
||||
// So the tests for scrollbar would fail
|
||||
const isScrollBarHidden = () => {
|
||||
const calc = windowCalculations()
|
||||
return calc.htmlClient === calc.htmlOffset && calc.htmlClient === calc.window
|
||||
}
|
||||
@@ -52,28 +54,24 @@ describe('ScrollBar', () => {
|
||||
it('should return true if body is overflowing', () => {
|
||||
document.documentElement.style.overflowY = 'scroll'
|
||||
document.body.style.overflowY = 'scroll'
|
||||
fixtureEl.innerHTML = [
|
||||
'<div style="height: 110vh; width: 100%"></div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div style="height: 110vh; width: 100%"></div>'
|
||||
const result = new ScrollBarHelper().isOverflowing()
|
||||
|
||||
if (isScrollBarHidden()) {
|
||||
expect(result).toEqual(false)
|
||||
expect(result).toBeFalse()
|
||||
} else {
|
||||
expect(result).toEqual(true)
|
||||
expect(result).toBeTrue()
|
||||
}
|
||||
})
|
||||
|
||||
it('should return false if body is not overflowing', () => {
|
||||
doc.style.overflowY = 'hidden'
|
||||
document.body.style.overflowY = 'hidden'
|
||||
fixtureEl.innerHTML = [
|
||||
'<div style="height: 110vh; width: 100%"></div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div style="height: 110vh; width: 100%"></div>'
|
||||
const scrollBar = new ScrollBarHelper()
|
||||
const result = scrollBar.isOverflowing()
|
||||
|
||||
expect(result).toEqual(false)
|
||||
expect(result).toBeFalse()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -81,13 +79,11 @@ describe('ScrollBar', () => {
|
||||
it('should return an integer greater than zero, if body is overflowing', () => {
|
||||
doc.style.overflowY = 'scroll'
|
||||
document.body.style.overflowY = 'scroll'
|
||||
fixtureEl.innerHTML = [
|
||||
'<div style="height: 110vh; width: 100%"></div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div style="height: 110vh; width: 100%"></div>'
|
||||
const result = new ScrollBarHelper().getWidth()
|
||||
|
||||
if (isScrollBarHidden()) {
|
||||
expect(result).toBe(0)
|
||||
expect(result).toEqual(0)
|
||||
} else {
|
||||
expect(result).toBeGreaterThan(1)
|
||||
}
|
||||
@@ -96,9 +92,7 @@ describe('ScrollBar', () => {
|
||||
it('should return 0 if body is not overflowing', () => {
|
||||
document.documentElement.style.overflowY = 'hidden'
|
||||
document.body.style.overflowY = 'hidden'
|
||||
fixtureEl.innerHTML = [
|
||||
'<div style="height: 110vh; width: 100%"></div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div style="height: 110vh; width: 100%"></div>'
|
||||
|
||||
const result = new ScrollBarHelper().getWidth()
|
||||
|
||||
@@ -109,9 +103,9 @@ describe('ScrollBar', () => {
|
||||
describe('hide - reset', () => {
|
||||
it('should adjust the inline padding of fixed elements which are full-width', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div style="height: 110vh; width: 100%">' +
|
||||
'<div class="fixed-top" id="fixed1" style="padding-right: 0px; width: 100vw"></div>',
|
||||
'<div class="fixed-top" id="fixed2" style="padding-right: 5px; width: 100vw"></div>',
|
||||
'<div style="height: 110vh; width: 100%">',
|
||||
' <div class="fixed-top" id="fixed1" style="padding-right: 0px; width: 100vw"></div>',
|
||||
' <div class="fixed-top" id="fixed2" style="padding-right: 5px; width: 100vw"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
doc.style.overflowY = 'scroll'
|
||||
@@ -128,25 +122,25 @@ describe('ScrollBar', () => {
|
||||
|
||||
let currentPadding = getPaddingX(fixedEl)
|
||||
let currentPadding2 = getPaddingX(fixedEl2)
|
||||
expect(getPaddingAttr(fixedEl)).toEqual(`${originalPadding}px`, 'original fixed element padding should be stored in data-bs-padding-right')
|
||||
expect(getPaddingAttr(fixedEl2)).toEqual(`${originalPadding2}px`, 'original fixed element padding should be stored in data-bs-padding-right')
|
||||
expect(currentPadding).toEqual(expectedPadding, 'fixed element padding should be adjusted while opening')
|
||||
expect(currentPadding2).toEqual(expectedPadding2, 'fixed element padding should be adjusted while opening')
|
||||
expect(getPaddingAttr(fixedEl)).toEqual(`${originalPadding}px`)
|
||||
expect(getPaddingAttr(fixedEl2)).toEqual(`${originalPadding2}px`)
|
||||
expect(currentPadding).toEqual(expectedPadding)
|
||||
expect(currentPadding2).toEqual(expectedPadding2)
|
||||
|
||||
scrollBar.reset()
|
||||
currentPadding = getPaddingX(fixedEl)
|
||||
currentPadding2 = getPaddingX(fixedEl2)
|
||||
expect(getPaddingAttr(fixedEl)).toEqual(null, 'data-bs-padding-right should be cleared after closing')
|
||||
expect(getPaddingAttr(fixedEl2)).toEqual(null, 'data-bs-padding-right should be cleared after closing')
|
||||
expect(currentPadding).toEqual(originalPadding, 'fixed element padding should be reset after closing')
|
||||
expect(currentPadding2).toEqual(originalPadding2, 'fixed element padding should be reset after closing')
|
||||
expect(getPaddingAttr(fixedEl)).toBeNull()
|
||||
expect(getPaddingAttr(fixedEl2)).toBeNull()
|
||||
expect(currentPadding).toEqual(originalPadding)
|
||||
expect(currentPadding2).toEqual(originalPadding2)
|
||||
done()
|
||||
})
|
||||
|
||||
it('should adjust the inline margin and padding of sticky elements', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div style="height: 110vh">' +
|
||||
'<div class="sticky-top" style="margin-right: 10px; padding-right: 20px; width: 100vw; height: 10px"></div>',
|
||||
'<div style="height: 110vh">',
|
||||
' <div class="sticky-top" style="margin-right: 10px; padding-right: 20px; width: 100vw; height: 10px"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
doc.style.overflowY = 'scroll'
|
||||
@@ -159,23 +153,21 @@ describe('ScrollBar', () => {
|
||||
const expectedPadding = originalPadding + scrollBar.getWidth()
|
||||
scrollBar.hide()
|
||||
|
||||
expect(getMarginAttr(stickyTopEl)).toEqual(`${originalMargin}px`, 'original sticky element margin should be stored in data-bs-margin-right')
|
||||
expect(getMarginX(stickyTopEl)).toEqual(expectedMargin, 'sticky element margin should be adjusted while opening')
|
||||
expect(getPaddingAttr(stickyTopEl)).toEqual(`${originalPadding}px`, 'original sticky element margin should be stored in data-bs-margin-right')
|
||||
expect(getPaddingX(stickyTopEl)).toEqual(expectedPadding, 'sticky element margin should be adjusted while opening')
|
||||
expect(getMarginAttr(stickyTopEl)).toEqual(`${originalMargin}px`)
|
||||
expect(getMarginX(stickyTopEl)).toEqual(expectedMargin)
|
||||
expect(getPaddingAttr(stickyTopEl)).toEqual(`${originalPadding}px`)
|
||||
expect(getPaddingX(stickyTopEl)).toEqual(expectedPadding)
|
||||
|
||||
scrollBar.reset()
|
||||
expect(getMarginAttr(stickyTopEl)).toEqual(null, 'data-bs-margin-right should be cleared after closing')
|
||||
expect(getMarginX(stickyTopEl)).toEqual(originalMargin, 'sticky element margin should be reset after closing')
|
||||
expect(getPaddingAttr(stickyTopEl)).toEqual(null, 'data-bs-margin-right should be cleared after closing')
|
||||
expect(getPaddingX(stickyTopEl)).toEqual(originalPadding, 'sticky element margin should be reset after closing')
|
||||
expect(getMarginAttr(stickyTopEl)).toBeNull()
|
||||
expect(getMarginX(stickyTopEl)).toEqual(originalMargin)
|
||||
expect(getPaddingAttr(stickyTopEl)).toBeNull()
|
||||
expect(getPaddingX(stickyTopEl)).toEqual(originalPadding)
|
||||
done()
|
||||
})
|
||||
|
||||
it('should not adjust the inline margin and padding of sticky and fixed elements when element do not have full width', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="sticky-top" style="margin-right: 0px; padding-right: 0px; width: 50vw"></div>'
|
||||
].join('')
|
||||
fixtureEl.innerHTML = '<div class="sticky-top" style="margin-right: 0px; padding-right: 0px; width: 50vw"></div>'
|
||||
|
||||
const stickyTopEl = fixtureEl.querySelector('.sticky-top')
|
||||
const originalMargin = getMarginX(stickyTopEl)
|
||||
@@ -187,16 +179,16 @@ describe('ScrollBar', () => {
|
||||
const currentMargin = getMarginX(stickyTopEl)
|
||||
const currentPadding = getPaddingX(stickyTopEl)
|
||||
|
||||
expect(currentMargin).toEqual(originalMargin, 'sticky element\'s margin should not be adjusted while opening')
|
||||
expect(currentPadding).toEqual(originalPadding, 'sticky element\'s padding should not be adjusted while opening')
|
||||
expect(currentMargin).toEqual(originalMargin)
|
||||
expect(currentPadding).toEqual(originalPadding)
|
||||
|
||||
scrollBar.reset()
|
||||
})
|
||||
|
||||
it('should not put data-attribute if element doesn\'t have the proper style property, should just remove style property if element didn\'t had one', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div style="height: 110vh; width: 100%">' +
|
||||
'<div class="sticky-top" id="sticky" style="width: 100vw"></div>',
|
||||
'<div style="height: 110vh; width: 100%">',
|
||||
' <div class="sticky-top" id="sticky" style="width: 100vw"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
@@ -232,8 +224,8 @@ describe('ScrollBar', () => {
|
||||
const scrollBarWidth = scrollBar.getWidth()
|
||||
scrollBar.hide()
|
||||
|
||||
expect(getPaddingX(document.body)).toEqual(scrollBarWidth, 'body does not have inline padding set')
|
||||
expect(document.body.style.color).toEqual('red', 'body still has other inline styles set')
|
||||
expect(getPaddingX(document.body)).toEqual(scrollBarWidth)
|
||||
expect(document.body.style.color).toEqual('red')
|
||||
|
||||
scrollBar.reset()
|
||||
})
|
||||
@@ -243,7 +235,7 @@ describe('ScrollBar', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<style>',
|
||||
' body {',
|
||||
` padding-right: ${styleSheetPadding} }`,
|
||||
` padding-right: ${styleSheetPadding}`,
|
||||
' }',
|
||||
'</style>'
|
||||
].join('')
|
||||
@@ -253,7 +245,7 @@ describe('ScrollBar', () => {
|
||||
el.style.paddingRight = inlineStylePadding
|
||||
|
||||
const originalPadding = getPaddingX(el)
|
||||
expect(originalPadding).toEqual(parseInt(inlineStylePadding)) // Respect only the inline style as it has prevails this of css
|
||||
expect(originalPadding).toEqual(parseIntDecimal(inlineStylePadding)) // Respect only the inline style as it has prevails this of css
|
||||
const originalOverFlow = 'auto'
|
||||
el.style.overflow = originalOverFlow
|
||||
const scrollBar = new ScrollBarHelper()
|
||||
@@ -264,7 +256,7 @@ describe('ScrollBar', () => {
|
||||
const currentPadding = getPaddingX(el)
|
||||
|
||||
expect(currentPadding).toEqual(scrollBarWidth + originalPadding)
|
||||
expect(currentPadding).toEqual(scrollBarWidth + parseInt(inlineStylePadding))
|
||||
expect(currentPadding).toEqual(scrollBarWidth + parseIntDecimal(inlineStylePadding))
|
||||
expect(getPaddingAttr(el)).toEqual(inlineStylePadding)
|
||||
expect(getOverFlow(el)).toEqual('hidden')
|
||||
expect(getOverFlowAttr(el)).toEqual(originalOverFlow)
|
||||
@@ -273,9 +265,9 @@ describe('ScrollBar', () => {
|
||||
|
||||
const currentPadding1 = getPaddingX(el)
|
||||
expect(currentPadding1).toEqual(originalPadding)
|
||||
expect(getPaddingAttr(el)).toEqual(null)
|
||||
expect(getPaddingAttr(el)).toBeNull()
|
||||
expect(getOverFlow(el)).toEqual(originalOverFlow)
|
||||
expect(getOverFlowAttr(el)).toEqual(null)
|
||||
expect(getOverFlowAttr(el)).toBeNull()
|
||||
})
|
||||
|
||||
it('should hide scrollbar and reset it to its initial value - respecting css rules', () => {
|
||||
@@ -283,7 +275,7 @@ describe('ScrollBar', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<style>',
|
||||
' body {',
|
||||
` padding-right: ${styleSheetPadding} }`,
|
||||
` padding-right: ${styleSheetPadding}`,
|
||||
' }',
|
||||
'</style>'
|
||||
].join('')
|
||||
@@ -299,7 +291,7 @@ describe('ScrollBar', () => {
|
||||
const currentPadding = getPaddingX(el)
|
||||
|
||||
expect(currentPadding).toEqual(scrollBarWidth + originalPadding)
|
||||
expect(currentPadding).toEqual(scrollBarWidth + parseInt(styleSheetPadding))
|
||||
expect(currentPadding).toEqual(scrollBarWidth + parseIntDecimal(styleSheetPadding))
|
||||
expect(getPaddingAttr(el)).toBeNull() // We do not have to keep css padding
|
||||
expect(getOverFlow(el)).toEqual('hidden')
|
||||
expect(getOverFlowAttr(el)).toEqual(originalOverFlow)
|
||||
@@ -308,9 +300,9 @@ describe('ScrollBar', () => {
|
||||
|
||||
const currentPadding1 = getPaddingX(el)
|
||||
expect(currentPadding1).toEqual(originalPadding)
|
||||
expect(getPaddingAttr(el)).toEqual(null)
|
||||
expect(getPaddingAttr(el)).toBeNull()
|
||||
expect(getOverFlow(el)).toEqual(originalOverFlow)
|
||||
expect(getOverFlowAttr(el)).toEqual(null)
|
||||
expect(getOverFlowAttr(el)).toBeNull()
|
||||
})
|
||||
|
||||
it('should not adjust the inline body padding when it does not overflow', () => {
|
||||
@@ -324,7 +316,7 @@ describe('ScrollBar', () => {
|
||||
scrollBar.hide()
|
||||
const currentPadding = getPaddingX(document.body)
|
||||
|
||||
expect(currentPadding).toEqual(originalPadding, 'body padding should not be adjusted')
|
||||
expect(currentPadding).toEqual(originalPadding)
|
||||
scrollBar.reset()
|
||||
})
|
||||
|
||||
@@ -344,7 +336,7 @@ describe('ScrollBar', () => {
|
||||
|
||||
const currentPadding = getPaddingX(document.body)
|
||||
|
||||
expect(currentPadding).toEqual(originalPadding, 'body padding should not be adjusted')
|
||||
expect(currentPadding).toEqual(originalPadding)
|
||||
|
||||
scrollBar.reset()
|
||||
})
|
||||
|
||||
@@ -39,17 +39,17 @@ describe('Swipe', () => {
|
||||
fixtureEl = getFixture()
|
||||
const cssStyle = [
|
||||
'<style>',
|
||||
' #fixture .pointer-event {',
|
||||
' touch-action: pan-y;',
|
||||
' #fixture .pointer-event {',
|
||||
' touch-action: pan-y;',
|
||||
' }',
|
||||
' #fixture div {',
|
||||
' width: 300px;',
|
||||
' height: 300px;',
|
||||
' #fixture div {',
|
||||
' width: 300px;',
|
||||
' height: 300px;',
|
||||
' }',
|
||||
'</style>'
|
||||
].join('')
|
||||
|
||||
fixtureEl.innerHTML = '<div id="swipeEl"></div>' + cssStyle
|
||||
fixtureEl.innerHTML = `<div id="swipeEl"></div>${cssStyle}`
|
||||
swipeEl = fixtureEl.querySelector('div')
|
||||
})
|
||||
|
||||
@@ -266,7 +266,7 @@ describe('Swipe', () => {
|
||||
expect(Swipe.isSupported()).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return "false" if "touchstart" not exists in document element and "navigator.maxTouchPoints" are zero (0)', () => {
|
||||
it('should return "false" if "touchstart" not exists in document element and "navigator.maxTouchPoints" are zero (0)', () => {
|
||||
Object.defineProperty(window.navigator, 'maxTouchPoints', () => 0)
|
||||
deleteDocumentElementOntouchstart()
|
||||
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
import { clearFixture, getFixture } from '../../helpers/fixture'
|
||||
import TemplateFactory from '../../../src/util/template-factory'
|
||||
|
||||
describe('TemplateFactory', () => {
|
||||
let fixtureEl
|
||||
|
||||
beforeAll(() => {
|
||||
fixtureEl = getFixture()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
clearFixture()
|
||||
})
|
||||
|
||||
describe('NAME', () => {
|
||||
it('should return plugin NAME', () => {
|
||||
expect(TemplateFactory.NAME).toEqual('TemplateFactory')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Default', () => {
|
||||
it('should return plugin default config', () => {
|
||||
expect(TemplateFactory.Default).toEqual(jasmine.any(Object))
|
||||
})
|
||||
})
|
||||
|
||||
describe('toHtml', () => {
|
||||
describe('Sanitization', () => {
|
||||
it('should use "sanitizeHtml" to sanitize template', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
template: '<div><a href="javascript:alert(7)">Click me</a></div>'
|
||||
})
|
||||
const spy = spyOn(factory, '_maybeSanitize').and.callThrough()
|
||||
|
||||
expect(factory.toHtml().innerHTML).not.toContain('href="javascript:alert(7)')
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not sanitize template', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: false,
|
||||
template: '<div><a href="javascript:alert(7)">Click me</a></div>'
|
||||
})
|
||||
const spy = spyOn(factory, '_maybeSanitize').and.callThrough()
|
||||
|
||||
expect(factory.toHtml().innerHTML).toContain('href="javascript:alert(7)')
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should use "sanitizeHtml" to sanitize content', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: true,
|
||||
template: '<div id="foo"></div>',
|
||||
content: { '#foo': '<a href="javascript:alert(7)">Click me</a>' }
|
||||
})
|
||||
expect(factory.toHtml().innerHTML).not.toContain('href="javascript:alert(7)')
|
||||
})
|
||||
|
||||
it('should not sanitize content', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: false,
|
||||
html: true,
|
||||
template: '<div id="foo"></div>',
|
||||
content: { '#foo': '<a href="javascript:alert(7)">Click me</a>' }
|
||||
})
|
||||
expect(factory.toHtml().innerHTML).toContain('href="javascript:alert(7)')
|
||||
})
|
||||
|
||||
it('should sanitize content only if "config.html" is enabled', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: false,
|
||||
template: '<div id="foo"></div>',
|
||||
content: { '#foo': '<a href="javascript:alert(7)">Click me</a>' }
|
||||
})
|
||||
const spy = spyOn(factory, '_maybeSanitize').and.callThrough()
|
||||
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Extra Class', () => {
|
||||
it('should add extra class', () => {
|
||||
const factory = new TemplateFactory({
|
||||
extraClass: 'testClass'
|
||||
})
|
||||
expect(factory.toHtml()).toHaveClass('testClass')
|
||||
})
|
||||
|
||||
it('should add extra classes', () => {
|
||||
const factory = new TemplateFactory({
|
||||
extraClass: 'testClass testClass2'
|
||||
})
|
||||
expect(factory.toHtml()).toHaveClass('testClass')
|
||||
expect(factory.toHtml()).toHaveClass('testClass2')
|
||||
})
|
||||
|
||||
it('should resolve class if function is given', () => {
|
||||
const factory = new TemplateFactory({
|
||||
extraClass: arg => {
|
||||
expect(arg).toEqual(factory)
|
||||
return 'testClass'
|
||||
}
|
||||
})
|
||||
|
||||
expect(factory.toHtml()).toHaveClass('testClass')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Content', () => {
|
||||
it('add simple text content', () => {
|
||||
const template = [
|
||||
'<div>',
|
||||
' <div class="foo"></div>',
|
||||
' <div class="foo2"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
template,
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2'
|
||||
}
|
||||
})
|
||||
|
||||
const html = factory.toHtml()
|
||||
expect(html.querySelector('.foo').textContent).toEqual('bar')
|
||||
expect(html.querySelector('.foo2').textContent).toEqual('bar2')
|
||||
})
|
||||
|
||||
it('should not fill template if selector not exists', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: true,
|
||||
template: '<div id="foo"></div>',
|
||||
content: { '#bar': 'test' }
|
||||
})
|
||||
|
||||
expect(factory.toHtml().outerHTML).toEqual('<div id="foo"></div>')
|
||||
})
|
||||
|
||||
it('should remove template selector, if content is null', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: true,
|
||||
template: '<div><div id="foo"></div></div>',
|
||||
content: { '#foo': null }
|
||||
})
|
||||
|
||||
expect(factory.toHtml().outerHTML).toEqual('<div></div>')
|
||||
})
|
||||
|
||||
it('should resolve content if is function', () => {
|
||||
const factory = new TemplateFactory({
|
||||
sanitize: true,
|
||||
html: true,
|
||||
template: '<div><div id="foo"></div></div>',
|
||||
content: { '#foo': () => null }
|
||||
})
|
||||
|
||||
expect(factory.toHtml().outerHTML).toEqual('<div></div>')
|
||||
})
|
||||
|
||||
it('if content is element and "config.html=false", should put content\'s textContent', () => {
|
||||
fixtureEl.innerHTML = '<div>foo<span>bar</span></div>'
|
||||
const contentElement = fixtureEl.querySelector('div')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
html: false,
|
||||
template: '<div><div id="foo"></div></div>',
|
||||
content: { '#foo': contentElement }
|
||||
})
|
||||
|
||||
const fooEl = factory.toHtml().querySelector('#foo')
|
||||
expect(fooEl.innerHTML).not.toEqual(contentElement.innerHTML)
|
||||
expect(fooEl.textContent).toEqual(contentElement.textContent)
|
||||
expect(fooEl.textContent).toEqual('foobar')
|
||||
})
|
||||
|
||||
it('if content is element and "config.html=true", should put content\'s outerHtml as child', () => {
|
||||
fixtureEl.innerHTML = '<div>foo<span>bar</span></div>'
|
||||
const contentElement = fixtureEl.querySelector('div')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
html: true,
|
||||
template: '<div><div id="foo"></div></div>',
|
||||
content: { '#foo': contentElement }
|
||||
})
|
||||
|
||||
const fooEl = factory.toHtml().querySelector('#foo')
|
||||
expect(fooEl.innerHTML).toEqual(contentElement.outerHTML)
|
||||
expect(fooEl.textContent).toEqual(contentElement.textContent)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getContent', () => {
|
||||
it('should get content as array', () => {
|
||||
const factory = new TemplateFactory({
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2'
|
||||
}
|
||||
})
|
||||
expect(factory.getContent()).toEqual(['bar', 'bar2'])
|
||||
})
|
||||
|
||||
it('should filter empties', () => {
|
||||
const factory = new TemplateFactory({
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': '',
|
||||
'.foo3': null,
|
||||
'.foo4': () => 2,
|
||||
'.foo5': () => null
|
||||
}
|
||||
})
|
||||
expect(factory.getContent()).toEqual(['bar', 2])
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasContent', () => {
|
||||
it('should return true, if it has', () => {
|
||||
const factory = new TemplateFactory({
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2',
|
||||
'.foo3': ''
|
||||
}
|
||||
})
|
||||
expect(factory.hasContent()).toBeTrue()
|
||||
})
|
||||
|
||||
it('should return false, if filtered content is empty', () => {
|
||||
const factory = new TemplateFactory({
|
||||
content: {
|
||||
'.foo2': '',
|
||||
'.foo3': null,
|
||||
'.foo4': () => null
|
||||
}
|
||||
})
|
||||
expect(factory.hasContent()).toBeFalse()
|
||||
})
|
||||
})
|
||||
|
||||
describe('changeContent', () => {
|
||||
it('should change Content', () => {
|
||||
const template = [
|
||||
'<div>',
|
||||
' <div class="foo"></div>',
|
||||
' <div class="foo2"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
template,
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2'
|
||||
}
|
||||
})
|
||||
|
||||
const html = selector => factory.toHtml().querySelector(selector).textContent
|
||||
expect(html('.foo')).toEqual('bar')
|
||||
expect(html('.foo2')).toEqual('bar2')
|
||||
factory.changeContent({
|
||||
'.foo': 'test',
|
||||
'.foo2': 'test2'
|
||||
})
|
||||
|
||||
expect(html('.foo')).toEqual('test')
|
||||
expect(html('.foo2')).toEqual('test2')
|
||||
})
|
||||
|
||||
it('should change only the given, content', () => {
|
||||
const template = [
|
||||
'<div>',
|
||||
' <div class="foo"></div>',
|
||||
' <div class="foo2"></div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const factory = new TemplateFactory({
|
||||
template,
|
||||
content: {
|
||||
'.foo': 'bar',
|
||||
'.foo2': 'bar2'
|
||||
}
|
||||
})
|
||||
|
||||
const html = selector => factory.toHtml().querySelector(selector).textContent
|
||||
expect(html('.foo')).toEqual('bar')
|
||||
expect(html('.foo2')).toEqual('bar2')
|
||||
factory.changeContent({
|
||||
'.foo': 'test',
|
||||
'.wrong': 'wrong'
|
||||
})
|
||||
|
||||
expect(html('.foo')).toEqual('test')
|
||||
expect(html('.foo2')).toEqual('bar2')
|
||||
})
|
||||
})
|
||||
})
|
||||
Generated
+2144
-2191
File diff suppressed because it is too large
Load Diff
+26
-26
@@ -64,7 +64,7 @@
|
||||
"docs-serve": "hugo server --port 9001 --disableFastRender",
|
||||
"docs-serve-only": "npx sirv-cli _site --port 9001",
|
||||
"lockfile-lint": "lockfile-lint --allowed-hosts npm --allowed-schemes https: --empty-hostname false --type npm --path package-lock.json",
|
||||
"update-deps": "ncu -u -x globby,karma-browserstack-launcher && echo Manually update site/assets/js/vendor",
|
||||
"update-deps": "ncu -u -x globby,karma-browserstack-launcher,stylelint && echo Manually update site/assets/js/vendor",
|
||||
"release": "npm-run-all dist release-sri docs-build release-zip*",
|
||||
"release-sri": "node build/generate-sri.js",
|
||||
"release-version": "node build/change-version.js",
|
||||
@@ -97,56 +97,56 @@
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@popperjs/core": "^2.10.2"
|
||||
"@popperjs/core": "^2.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.15.7",
|
||||
"@babel/core": "^7.15.8",
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"@popperjs/core": "^2.10.2",
|
||||
"@babel/cli": "^7.16.0",
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/preset-env": "^7.16.4",
|
||||
"@popperjs/core": "^2.11.0",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^21.0.0",
|
||||
"@rollup/plugin-node-resolve": "^13.0.5",
|
||||
"@rollup/plugin-commonjs": "^21.0.1",
|
||||
"@rollup/plugin-node-resolve": "^13.0.6",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"autoprefixer": "^10.3.7",
|
||||
"autoprefixer": "^10.4.0",
|
||||
"bundlewatch": "^0.3.2",
|
||||
"clean-css-cli": "^5.4.1",
|
||||
"clean-css-cli": "^5.4.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"cspell": "^5.12.3",
|
||||
"eslint": "^8.0.0",
|
||||
"cspell": "^5.13.2",
|
||||
"eslint": "^8.4.1",
|
||||
"eslint-config-xo": "^0.39.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-unicorn": "^37.0.1",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-unicorn": "^39.0.0",
|
||||
"find-unused-sass-variables": "^3.1.0",
|
||||
"glob": "^7.2.0",
|
||||
"globby": "^11.0.4",
|
||||
"hammer-simulator": "0.0.1",
|
||||
"hugo-bin": "^0.76.1",
|
||||
"hugo-bin": "^0.77.4",
|
||||
"ip": "^1.1.5",
|
||||
"jquery": "^3.6.0",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.3.9",
|
||||
"karma-browserstack-launcher": "1.4.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "^3.0.3",
|
||||
"karma-detect-browsers": "^2.3.3",
|
||||
"karma-firefox-launcher": "^2.1.1",
|
||||
"karma-firefox-launcher": "^2.1.2",
|
||||
"karma-jasmine": "^4.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.7.0",
|
||||
"karma-rollup-preprocessor": "^7.0.7",
|
||||
"linkinator": "^2.14.4",
|
||||
"linkinator": "^2.16.2",
|
||||
"lockfile-lint": "^4.6.2",
|
||||
"nodemon": "^2.0.13",
|
||||
"nodemon": "^2.0.15",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.3.9",
|
||||
"postcss-cli": "^9.0.1",
|
||||
"rollup": "^2.58.0",
|
||||
"postcss": "^8.4.4",
|
||||
"postcss-cli": "^9.0.2",
|
||||
"rollup": "^2.60.2",
|
||||
"rollup-plugin-istanbul": "^3.0.0",
|
||||
"rtlcss": "^3.3.0",
|
||||
"sass": "^1.42.1",
|
||||
"rtlcss": "^3.5.0",
|
||||
"sass": "^1.44.0",
|
||||
"shelljs": "^0.8.4",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-twbs-bootstrap": "^2.2.4",
|
||||
"terser": "^5.9.0",
|
||||
"terser": "^5.10.0",
|
||||
"vnu-jar": "21.10.12"
|
||||
},
|
||||
"files": [
|
||||
@@ -172,7 +172,7 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"@popperjs/core": "^2.10.2"
|
||||
"@popperjs/core": "^2.11.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
}
|
||||
|
||||
.accordion-item {
|
||||
color: color-contrast($accordion-bg);
|
||||
background-color: $accordion-bg;
|
||||
border: $accordion-border-width solid $accordion-border-color;
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// transparent background and border properties included for button version.
|
||||
// Transparent background and border properties included for button version.
|
||||
// iOS requires the button element instead of an anchor tag.
|
||||
// If you want the anchor version, it requires `href="#"`.
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Re-assigned maps
|
||||
//
|
||||
// Placed here so that others can override the default Sass maps and see automatic updates to utilities and more.
|
||||
|
||||
// scss-docs-start theme-colors-rgb
|
||||
$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value") !default;
|
||||
// scss-docs-end theme-colors-rgb
|
||||
|
||||
// Utilities maps
|
||||
//
|
||||
// Extends the default `$theme-colors` maps to help create our utilities.
|
||||
|
||||
// Come v6, we'll de-dupe these variables. Until then, for backward compatibility, we keep them to reassign.
|
||||
// scss-docs-start utilities-colors
|
||||
$utilities-colors: $theme-colors-rgb !default;
|
||||
// scss-docs-end utilities-colors
|
||||
|
||||
// scss-docs-start utilities-text-colors
|
||||
$utilities-text: map-merge(
|
||||
$utilities-colors,
|
||||
(
|
||||
"black": to-rgb($black),
|
||||
"white": to-rgb($white),
|
||||
"body": to-rgb($body-color)
|
||||
)
|
||||
) !default;
|
||||
$utilities-text-colors: map-loop($utilities-text, rgba-css-var, "$key", "text") !default;
|
||||
// scss-docs-end utilities-text-colors
|
||||
|
||||
// scss-docs-start utilities-bg-colors
|
||||
$utilities-bg: map-merge(
|
||||
$utilities-colors,
|
||||
(
|
||||
"black": to-rgb($black),
|
||||
"white": to-rgb($white),
|
||||
"body": to-rgb($body-bg)
|
||||
)
|
||||
) !default;
|
||||
$utilities-bg-colors: map-loop($utilities-bg, rgba-css-var, "$key", "bg") !default;
|
||||
// scss-docs-end utilities-bg-colors
|
||||
|
||||
$negative-spacers: if($enable-negative-margins, negativify-map($spacers), null) !default;
|
||||
|
||||
$gutters: $spacers !default;
|
||||
+3
-11
@@ -49,7 +49,7 @@
|
||||
body {
|
||||
margin: 0; // 1
|
||||
font-family: var(--#{$variable-prefix}body-font-family);
|
||||
@include font-size(var(--#{$variable-prefix}body-font-size));
|
||||
font-size: var(--#{$variable-prefix}body-font-size);
|
||||
font-weight: var(--#{$variable-prefix}body-font-weight);
|
||||
line-height: var(--#{$variable-prefix}body-line-height);
|
||||
color: var(--#{$variable-prefix}body-color);
|
||||
@@ -279,8 +279,6 @@ kbd,
|
||||
samp {
|
||||
font-family: $font-family-code;
|
||||
@include font-size(1em); // Correct the odd `em` font sizing in all browsers.
|
||||
direction: ltr #{"/* rtl:ignore */"};
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
|
||||
// 1. Remove browser default top margin
|
||||
@@ -571,16 +569,10 @@ legend {
|
||||
}
|
||||
|
||||
|
||||
// Inherit font family and line height for file input buttons
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
// 1. Change font properties to `inherit`
|
||||
// 1. Inherit font family and line height for file input buttons
|
||||
// 2. Correct the inability to style clickable types in iOS and Safari.
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
::file-selector-button {
|
||||
font: inherit; // 1
|
||||
-webkit-appearance: button; // 2
|
||||
}
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@
|
||||
--#{$variable-prefix}root-font-size: #{$font-size-root};
|
||||
}
|
||||
--#{$variable-prefix}body-font-family: #{$font-family-base};
|
||||
--#{$variable-prefix}body-font-size: #{$font-size-base};
|
||||
@include rfs($font-size-base, --#{$variable-prefix}body-font-size);
|
||||
--#{$variable-prefix}body-font-weight: #{$font-weight-base};
|
||||
--#{$variable-prefix}body-line-height: #{$line-height-base};
|
||||
--#{$variable-prefix}body-color: #{$body-color};
|
||||
|
||||
+2
-43
@@ -90,10 +90,6 @@ $theme-colors: (
|
||||
) !default;
|
||||
// scss-docs-end theme-colors-map
|
||||
|
||||
// scss-docs-start theme-colors-rgb
|
||||
$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value") !default;
|
||||
// scss-docs-end theme-colors-rgb
|
||||
|
||||
// The contrast ratio to reach against white, to determine if color changes from "light" to "dark". Acceptable values for WCAG 2.0 are 3, 4.5 and 7.
|
||||
// See https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast
|
||||
$min-contrast-ratio: 4.5 !default;
|
||||
@@ -228,8 +224,8 @@ $indigos: (
|
||||
) !default;
|
||||
|
||||
$purples: (
|
||||
"purple-100": $purple-200,
|
||||
"purple-200": $purple-100,
|
||||
"purple-100": $purple-100,
|
||||
"purple-200": $purple-200,
|
||||
"purple-300": $purple-300,
|
||||
"purple-400": $purple-400,
|
||||
"purple-500": $purple-500,
|
||||
@@ -382,8 +378,6 @@ $spacers: (
|
||||
4: $spacer * 1.5,
|
||||
5: $spacer * 3,
|
||||
) !default;
|
||||
|
||||
$negative-spacers: if($enable-negative-margins, negativify-map($spacers), null) !default;
|
||||
// scss-docs-end spacer-variables-maps
|
||||
|
||||
// Position
|
||||
@@ -406,39 +400,6 @@ $body-bg: $white !default;
|
||||
$body-color: $gray-900 !default;
|
||||
$body-text-align: null !default;
|
||||
|
||||
// Utilities maps
|
||||
//
|
||||
// Extends the default `$theme-colors` maps to help create our utilities.
|
||||
|
||||
// Come v6, we'll de-dupe these variables. Until then, for backward compatibility, we keep them to reassign.
|
||||
// scss-docs-start utilities-colors
|
||||
$utilities-colors: $theme-colors-rgb !default;
|
||||
// scss-docs-end utilities-colors
|
||||
|
||||
// scss-docs-start utilities-text-colors
|
||||
$utilities-text: map-merge(
|
||||
$utilities-colors,
|
||||
(
|
||||
"black": to-rgb($black),
|
||||
"white": to-rgb($white),
|
||||
"body": to-rgb($body-color)
|
||||
)
|
||||
) !default;
|
||||
$utilities-text-colors: map-loop($utilities-text, rgba-css-var, "$key", "text") !default;
|
||||
// scss-docs-end utilities-text-colors
|
||||
|
||||
// scss-docs-start utilities-bg-colors
|
||||
$utilities-bg: map-merge(
|
||||
$utilities-colors,
|
||||
(
|
||||
"black": to-rgb($black),
|
||||
"white": to-rgb($white),
|
||||
"body": to-rgb($body-bg)
|
||||
)
|
||||
) !default;
|
||||
$utilities-bg-colors: map-loop($utilities-bg, rgba-css-var, "$key", "bg") !default;
|
||||
// scss-docs-end utilities-bg-colors
|
||||
|
||||
// Links
|
||||
//
|
||||
// Style anchor elements.
|
||||
@@ -504,8 +465,6 @@ $grid-columns: 12 !default;
|
||||
$grid-gutter-width: 1.5rem !default;
|
||||
$grid-row-columns: 6 !default;
|
||||
|
||||
$gutters: $spacers !default;
|
||||
|
||||
// Container padding
|
||||
|
||||
$container-padding-x: $grid-gutter-width * .5 !default;
|
||||
|
||||
Vendored
+1
@@ -9,6 +9,7 @@ $include-column-box-sizing: true !default;
|
||||
|
||||
@import "functions";
|
||||
@import "variables";
|
||||
@import "maps";
|
||||
|
||||
@import "mixins/lists";
|
||||
@import "mixins/breakpoints";
|
||||
|
||||
Vendored
+1
@@ -8,6 +8,7 @@
|
||||
|
||||
@import "functions";
|
||||
@import "variables";
|
||||
@import "maps";
|
||||
@import "mixins";
|
||||
@import "root";
|
||||
@import "reboot";
|
||||
|
||||
Vendored
+1
@@ -8,6 +8,7 @@
|
||||
// Configuration
|
||||
@import "functions";
|
||||
@import "variables";
|
||||
@import "maps";
|
||||
@import "mixins";
|
||||
@import "utilities";
|
||||
|
||||
|
||||
Vendored
+1
@@ -9,6 +9,7 @@
|
||||
// Configuration
|
||||
@import "functions";
|
||||
@import "variables";
|
||||
@import "maps";
|
||||
@import "mixins";
|
||||
@import "utilities";
|
||||
|
||||
|
||||
@@ -91,25 +91,6 @@
|
||||
&:hover:not(:disabled):not([readonly])::file-selector-button {
|
||||
background-color: $form-file-button-hover-bg;
|
||||
}
|
||||
|
||||
&::-webkit-file-upload-button {
|
||||
padding: $input-padding-y $input-padding-x;
|
||||
margin: (-$input-padding-y) (-$input-padding-x);
|
||||
margin-inline-end: $input-padding-x;
|
||||
color: $form-file-button-color;
|
||||
@include gradient-bg($form-file-button-bg);
|
||||
pointer-events: none;
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
border-inline-end-width: $input-border-width;
|
||||
border-radius: 0; // stylelint-disable-line property-disallowed-list
|
||||
@include transition($btn-transition);
|
||||
}
|
||||
|
||||
&:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {
|
||||
background-color: $form-file-button-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// Readonly controls as plain text
|
||||
@@ -153,12 +134,6 @@
|
||||
margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
|
||||
margin-inline-end: $input-padding-x-sm;
|
||||
}
|
||||
|
||||
&::-webkit-file-upload-button {
|
||||
padding: $input-padding-y-sm $input-padding-x-sm;
|
||||
margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
|
||||
margin-inline-end: $input-padding-x-sm;
|
||||
}
|
||||
}
|
||||
|
||||
.form-control-lg {
|
||||
@@ -172,12 +147,6 @@
|
||||
margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
|
||||
margin-inline-end: $input-padding-x-lg;
|
||||
}
|
||||
|
||||
&::-webkit-file-upload-button {
|
||||
padding: $input-padding-y-lg $input-padding-x-lg;
|
||||
margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
|
||||
margin-inline-end: $input-padding-x-lg;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure textareas don't shrink too much when resized
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Container mixins
|
||||
|
||||
@mixin make-container($gutter: $container-padding-x) {
|
||||
--#{$variable-prefix}gutter-x: #{$gutter};
|
||||
--#{$variable-prefix}gutter-y: 0;
|
||||
width: 100%;
|
||||
padding-right: var(--#{$variable-prefix}gutter-x, #{$gutter});
|
||||
padding-left: var(--#{$variable-prefix}gutter-x, #{$gutter});
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
margin-left: calc(-.5 * var(--#{$variable-prefix}gutter-x)); // stylelint-disable-line function-disallowed-list
|
||||
}
|
||||
|
||||
@mixin make-col-ready($gutter: $grid-gutter-width) {
|
||||
@mixin make-col-ready() {
|
||||
// Add box sizing if only the grid is loaded
|
||||
box-sizing: if(variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null);
|
||||
// Prevent columns from becoming too narrow when at smaller grid tiers by
|
||||
|
||||
@@ -144,11 +144,12 @@
|
||||
|
||||
clipboard.on('success', function (event) {
|
||||
var tooltipBtn = bootstrap.Tooltip.getInstance(event.trigger)
|
||||
var originalTitle = event.trigger.getAttribute('title')
|
||||
|
||||
event.trigger.setAttribute('data-bs-original-title', 'Copied!')
|
||||
tooltipBtn.show()
|
||||
|
||||
event.trigger.setAttribute('data-bs-original-title', 'Copy to clipboard')
|
||||
tooltipBtn.setContent({ '.tooltip-inner': 'Copied!' })
|
||||
event.trigger.addEventListener('hidden.bs.tooltip', function () {
|
||||
tooltipBtn.setContent({ '.tooltip-inner': originalTitle })
|
||||
}, { once: true })
|
||||
event.clearSelection()
|
||||
})
|
||||
|
||||
@@ -156,11 +157,12 @@
|
||||
var modifierKey = /mac/i.test(navigator.userAgent) ? '\u2318' : 'Ctrl-'
|
||||
var fallbackMsg = 'Press ' + modifierKey + 'C to copy'
|
||||
var tooltipBtn = bootstrap.Tooltip.getInstance(event.trigger)
|
||||
var originalTitle = event.trigger.getAttribute('title')
|
||||
|
||||
event.trigger.setAttribute('data-bs-original-title', fallbackMsg)
|
||||
tooltipBtn.show()
|
||||
|
||||
event.trigger.setAttribute('data-bs-original-title', 'Copy to clipboard')
|
||||
tooltipBtn.setContent({ '.tooltip-inner': fallbackMsg })
|
||||
event.trigger.addEventListener('hidden.bs.tooltip', function () {
|
||||
tooltipBtn.setContent({ '.tooltip-inner': originalTitle })
|
||||
}, { once: true })
|
||||
})
|
||||
|
||||
anchors.options = {
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
.bd-example {
|
||||
position: relative;
|
||||
padding: 1rem;
|
||||
margin: 1rem (-$grid-gutter-width * .5) 0;
|
||||
margin: 1rem ($bd-gutter-x * -1) 0;
|
||||
border: solid $gray-300;
|
||||
border-width: 1px 0 0;
|
||||
@include clearfix();
|
||||
@@ -313,7 +313,7 @@
|
||||
//
|
||||
|
||||
.highlight {
|
||||
padding: 1rem;
|
||||
padding: var(--bs-gutter-x) $bd-gutter-x;
|
||||
margin-bottom: 1rem;
|
||||
background-color: $gray-100;
|
||||
|
||||
@@ -338,8 +338,8 @@
|
||||
}
|
||||
|
||||
.bd-content .highlight {
|
||||
margin-right: (-$grid-gutter-width * .5);
|
||||
margin-left: (-$grid-gutter-width * .5);
|
||||
margin-right: $bd-gutter-x * -1;
|
||||
margin-left: $bd-gutter-x * -1;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin-right: 0;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
.bd-layout {
|
||||
padding-right: $bd-gutter-x;
|
||||
padding-left: $bd-gutter-x;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
display: grid;
|
||||
gap: $grid-gutter-width;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
.bd-navbar {
|
||||
--bs-gutter-x: $bd-gutter-x;
|
||||
--bs-gutter-y: $bd-gutter-x;
|
||||
|
||||
padding: .75rem 0;
|
||||
background-color: $bd-purple-bright;
|
||||
|
||||
@@ -29,4 +32,13 @@
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.offcanvas {
|
||||
background-color: $bd-purple-bright;
|
||||
border-left: 0;
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
box-shadow: $box-shadow-lg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.bd-sidebar {
|
||||
@include media-breakpoint-down(md) {
|
||||
margin: 0 -.75rem 1rem;
|
||||
margin: 0 ($bd-gutter-x * -1) 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
.bd-subnavbar {
|
||||
--bs-gutter-x: $bd-gutter-x;
|
||||
--bs-gutter-y: $bd-gutter-x;
|
||||
|
||||
// The position and z-index are needed for the dropdown to stay on top of the content
|
||||
position: relative;
|
||||
z-index: $zindex-sticky;
|
||||
@@ -31,16 +34,16 @@
|
||||
position: absolute;
|
||||
top: .4rem;
|
||||
right: .4rem;
|
||||
bottom: .4rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 1.5rem;
|
||||
padding-right: .25rem;
|
||||
padding-left: .25rem;
|
||||
padding-right: .3125rem;
|
||||
padding-left: .3125rem;
|
||||
@include font-size(.75rem);
|
||||
color: $gray-600;
|
||||
content: "Ctrl + /";
|
||||
border: $border-width solid $border-color;
|
||||
background-color: $gray-100;
|
||||
@include border-radius(.125rem);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,3 +11,5 @@ $bd-warning: #f0ad4e;
|
||||
$bd-danger: #d9534f;
|
||||
$dropdown-active-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'><path fill='#292b2c' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/></svg>");
|
||||
$sidebar-collapse-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path fill='none' stroke='rgba(0,0,0,.5)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/></svg>");
|
||||
|
||||
$bd-gutter-x: 1.25rem;
|
||||
|
||||
@@ -65,7 +65,7 @@ Here's an example of all the sub-components included in a responsive light-theme
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -74,7 +74,7 @@ Here's an example of all the sub-components included in a responsive light-theme
|
||||
</nav>
|
||||
{{< /example >}}
|
||||
|
||||
This example uses [background]({{< docsref "/utilities/background" >}}) (`bg-light`) and [spacing]({{< docsref "/utilities/spacing" >}}) (`my-2`, `my-lg-0`, `me-sm-0`, `my-sm-0`) utility classes.
|
||||
This example uses [background]({{< docsref "/utilities/background" >}}) (`bg-light`) and [spacing]({{< docsref "/utilities/spacing" >}}) (`me-auto`, `mb-2`, `mb-lg-0`, `me-2`) utility classes.
|
||||
|
||||
### Brand
|
||||
|
||||
@@ -228,7 +228,7 @@ Place various form controls and components within a navbar:
|
||||
{{< example >}}
|
||||
<nav class="navbar navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -242,7 +242,7 @@ Immediate child elements of `.navbar` use flex layout and will default to `justi
|
||||
<nav class="navbar navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand">Navbar</a>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -343,7 +343,7 @@ Theming the navbar has never been easier thanks to the combination of theming cl
|
||||
<a class="nav-link" href="#">About</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-light" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -372,7 +372,7 @@ Theming the navbar has never been easier thanks to the combination of theming cl
|
||||
<a class="nav-link" href="#">About</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-light" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -401,7 +401,7 @@ Theming the navbar has never been easier thanks to the combination of theming cl
|
||||
<a class="nav-link" href="#">About</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-primary" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -524,7 +524,7 @@ Here's an example navbar using `.navbar-nav-scroll` with `style="--bs-scroll-hei
|
||||
<a class="nav-link disabled">Link</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -564,7 +564,7 @@ With no `.navbar-brand` shown at the smallest breakpoint:
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -594,7 +594,7 @@ With a brand name shown on the left and toggler on the right:
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -624,7 +624,7 @@ With a toggler on the left and brand name on the right:
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -695,7 +695,7 @@ In the example below, to create an offcanvas navbar that is always collapsed acr
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
@@ -8,7 +8,7 @@ toc: true
|
||||
|
||||
## How it works
|
||||
|
||||
Offcanvas is a sidebar component that can be toggled via JavaScript to appear from the left, right, or bottom edge of the viewport. Buttons or anchors are used as triggers that are attached to specific elements you toggle, and `data` attributes are used to invoke our JavaScript.
|
||||
Offcanvas is a sidebar component that can be toggled via JavaScript to appear from the left, right, top, or bottom edge of the viewport. Buttons or anchors are used as triggers that are attached to specific elements you toggle, and `data` attributes are used to invoke our JavaScript.
|
||||
|
||||
- Offcanvas shares some of the same JavaScript code as modals. Conceptually, they are quite similar, but they are separate plugins.
|
||||
- Similarly, some [source Sass](#sass) variables for offcanvas's styles and dimensions are inherited from the modal's variables.
|
||||
@@ -95,7 +95,7 @@ Try the top, right, and bottom examples out below.
|
||||
|
||||
<div class="offcanvas offcanvas-top" tabindex="-1" id="offcanvasTop" aria-labelledby="offcanvasTopLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 id="offcanvasTopLabel">Offcanvas top</h5>
|
||||
<h5 class="offcanvas-title" id="offcanvasTopLabel">Offcanvas top</h5>
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
@@ -109,7 +109,7 @@ Try the top, right, and bottom examples out below.
|
||||
|
||||
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasRight" aria-labelledby="offcanvasRightLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 id="offcanvasRightLabel">Offcanvas right</h5>
|
||||
<h5 class="offcanvas-title" id="offcanvasRightLabel">Offcanvas right</h5>
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
@@ -188,6 +188,7 @@ The offcanvas plugin utilizes a few classes and attributes to handle the heavy l
|
||||
- `.offcanvas.show` shows the content
|
||||
- `.offcanvas-start` hides the offcanvas on the left
|
||||
- `.offcanvas-end` hides the offcanvas on the right
|
||||
- `.offcanvas-top` hides the offcanvas on the top
|
||||
- `.offcanvas-bottom` hides the offcanvas on the bottom
|
||||
|
||||
Add a dismiss button with the `data-bs-dismiss="offcanvas"` attribute, which triggers the JavaScript functionality. Be sure to use the `<button>` element with it for proper behavior across all devices.
|
||||
|
||||
@@ -368,6 +368,21 @@ Removes the ability for an element's popover to be shown. The popover will only
|
||||
myPopover.disable()
|
||||
```
|
||||
|
||||
#### setContent
|
||||
|
||||
Gives a way to change the popover's content after its initialization.
|
||||
|
||||
```js
|
||||
myPopover.setContent({
|
||||
'.popover-header': 'another title',
|
||||
'.popover-body': 'another content'
|
||||
})
|
||||
```
|
||||
|
||||
{{< callout info >}}
|
||||
The `setContent` method accepts an `object` argument, where each property-key is a valid `string` selector within the popover template, and each related property-value can be `string` | `element` | `function` | `null`
|
||||
{{< /callout >}}
|
||||
|
||||
#### toggleEnabled
|
||||
|
||||
Toggles the ability for an element's popover to be shown or hidden.
|
||||
|
||||
@@ -392,6 +392,17 @@ Removes the ability for an element's tooltip to be shown. The tooltip will only
|
||||
tooltip.disable()
|
||||
```
|
||||
|
||||
#### setContent
|
||||
|
||||
Gives a way to change the tooltip's content after its initialization.
|
||||
|
||||
```js
|
||||
tooltip.setContent({ '.tooltip-inner': 'another title' })
|
||||
```
|
||||
{{< callout info >}}
|
||||
The `setContent` method accepts an `object` argument, where each property-key is a valid `string` selector within the popover template, and each related property-value can be `string` | `element` | `function` | `null`
|
||||
{{< /callout >}}
|
||||
|
||||
#### toggleEnabled
|
||||
|
||||
Toggles the ability for an element's tooltip to be shown or hidden.
|
||||
|
||||
@@ -48,7 +48,7 @@ Align images with the [helper float classes]({{< docsref "/utilities/float" >}})
|
||||
If you are using the `<picture>` element to specify multiple `<source>` elements for a specific `<img>`, make sure to add the `.img-*` classes to the `<img>` and not to the `<picture>` tag.
|
||||
|
||||
```html
|
||||
<picture>
|
||||
<picture>
|
||||
<source srcset="..." type="image/svg+xml">
|
||||
<img src="..." class="img-fluid img-thumbnail" alt="...">
|
||||
</picture>
|
||||
|
||||
@@ -252,7 +252,7 @@ Add `.table-sm` to make any `.table` more compact by cutting all cell `padding`
|
||||
|
||||
## Vertical alignment
|
||||
|
||||
Table cells of `<thead>` are always vertical aligned to the bottom. Table cells in `<tbody>` inherit their alignment from `<table>` and are aligned to the the top by default. Use the [vertical align]({{< docsref "/utilities/vertical-align" >}}) classes to re-align where needed.
|
||||
Table cells of `<thead>` are always vertical aligned to the bottom. Table cells in `<tbody>` inherit their alignment from `<table>` and are aligned to the top by default. Use the [vertical align]({{< docsref "/utilities/vertical-align" >}}) classes to re-align where needed.
|
||||
|
||||
<div class="bd-example">
|
||||
<div class="table-responsive">
|
||||
@@ -786,4 +786,4 @@ Use `.table-responsive{-sm|-md|-lg|-xl|-xxl}` as needed to create responsive tab
|
||||
### Customizing
|
||||
|
||||
- The factor variables (`$table-striped-bg-factor`, `$table-active-bg-factor` & `$table-hover-bg-factor`) are used to determine the contrast in table variants.
|
||||
- Apart from the light & dark table variants, theme colors are lightened by the `$table-bg-level` variable.
|
||||
- Apart from the light & dark table variants, theme colors are lightened by the `$table-bg-scale` variable.
|
||||
|
||||
@@ -184,7 +184,7 @@ Add `.initialism` to an abbreviation for a slightly smaller font-size.
|
||||
|
||||
## Blockquotes
|
||||
|
||||
For quoting blocks of content from another source within your document. Wrap `<blockquote class="blockquote">` around any <abbr title="HyperText Markup Language">HTML</abbr> as the quote.
|
||||
For quoting blocks of content from another source within your document. Wrap `<blockquote class="blockquote">` around any HTML as the quote.
|
||||
|
||||
{{< example >}}
|
||||
<blockquote class="blockquote">
|
||||
|
||||
@@ -122,6 +122,7 @@ Here's an example that generates text color utilities (e.g., `.text-purple-500`)
|
||||
```scss
|
||||
@import "bootstrap/scss/functions";
|
||||
@import "bootstrap/scss/variables";
|
||||
@import "bootstrap/scss/maps";
|
||||
@import "bootstrap/scss/mixins";
|
||||
@import "bootstrap/scss/utilities";
|
||||
|
||||
|
||||
@@ -59,10 +59,15 @@ In your `custom.scss`, you'll import Bootstrap's source Sass files. You have two
|
||||
|
||||
// 3. Include remainder of required Bootstrap stylesheets
|
||||
@import "../node_modules/bootstrap/scss/variables";
|
||||
|
||||
// 4. Include any default map overrides here
|
||||
|
||||
// 5. Include remainder of required parts
|
||||
@import "../node_modules/bootstrap/scss/maps";
|
||||
@import "../node_modules/bootstrap/scss/mixins";
|
||||
@import "../node_modules/bootstrap/scss/root";
|
||||
|
||||
// 4. Include any optional Bootstrap CSS as needed
|
||||
// 6. Optionally include any other parts as needed
|
||||
@import "../node_modules/bootstrap/scss/utilities";
|
||||
@import "../node_modules/bootstrap/scss/reboot";
|
||||
@import "../node_modules/bootstrap/scss/type";
|
||||
@@ -71,10 +76,10 @@ In your `custom.scss`, you'll import Bootstrap's source Sass files. You have two
|
||||
@import "../node_modules/bootstrap/scss/grid";
|
||||
@import "../node_modules/bootstrap/scss/helpers";
|
||||
|
||||
// 5. Optionally include utilities API last to generate classes based on the Sass map in `_utilities.scss`
|
||||
// 7. Optionally include utilities API last to generate classes based on the Sass map in `_utilities.scss`
|
||||
@import "../node_modules/bootstrap/scss/utilities/api";
|
||||
|
||||
// 6. Add additional custom code here
|
||||
// 8. Add additional custom code here
|
||||
```
|
||||
|
||||
With that setup in place, you can begin to modify any of the Sass variables and maps in your `custom.scss`. You can also start to add parts of Bootstrap under the `// Optional` section as needed. We suggest using the full import stack from our `bootstrap.scss` file as your starting point.
|
||||
@@ -99,6 +104,7 @@ $body-color: #111;
|
||||
|
||||
// Required
|
||||
@import "../node_modules/bootstrap/scss/variables";
|
||||
@import "../node_modules/bootstrap/scss/maps";
|
||||
@import "../node_modules/bootstrap/scss/mixins";
|
||||
@import "../node_modules/bootstrap/scss/root";
|
||||
|
||||
@@ -160,6 +166,7 @@ To remove colors from `$theme-colors`, or any other map, use `map-remove`. Be aw
|
||||
// Required
|
||||
@import "../node_modules/bootstrap/scss/functions";
|
||||
@import "../node_modules/bootstrap/scss/variables";
|
||||
@import "../node_modules/bootstrap/scss/maps";
|
||||
@import "../node_modules/bootstrap/scss/mixins";
|
||||
@import "../node_modules/bootstrap/scss/root";
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ extra_css:
|
||||
<a class="nav-link disabled">رابط غير مفعل</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="بحث" aria-label="بحث">
|
||||
<button class="btn btn-outline-success" type="submit">بحث</button>
|
||||
</form>
|
||||
|
||||
@@ -24,7 +24,7 @@ extra_css:
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
@@ -1240,7 +1240,7 @@ direction: rtl
|
||||
<a class="nav-link disabled">معطل</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="بحث" aria-label="بحث">
|
||||
<button class="btn btn-outline-dark" type="submit">بحث</button>
|
||||
</form>
|
||||
@@ -1279,7 +1279,7 @@ direction: rtl
|
||||
<a class="nav-link disabled">معطل</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="بحث" aria-label="بحث">
|
||||
<button class="btn btn-outline-light" type="submit">بحث</button>
|
||||
</form>
|
||||
@@ -1497,7 +1497,7 @@ direction: rtl
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="قريب"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
مرحبا بالعالم! هذه رسالة إشعار.
|
||||
مرحبًا بالعالم! هذه رسالة إشعار.
|
||||
</div>
|
||||
</div>
|
||||
{{< /example >}}
|
||||
|
||||
@@ -1239,7 +1239,7 @@ body_class: "bg-light"
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-dark" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -1278,7 +1278,7 @@ body_class: "bg-light"
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-light" type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
@@ -107,19 +107,19 @@ body_class: ""
|
||||
</form>
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li><a class="dropdown-item d-flex align-items-center gap-2 py-2" href="#">
|
||||
<span class="d-inline-block bg-success rounded-circle" style="width: .5em; height: .5em;"></span>
|
||||
<span class="d-inline-block bg-success rounded-circle p-1"></span>
|
||||
Action
|
||||
</a></li>
|
||||
<li><a class="dropdown-item d-flex align-items-center gap-2 py-2" href="#">
|
||||
<span class="d-inline-block bg-primary rounded-circle" style="width: .5em; height: .5em;"></span>
|
||||
<span class="d-inline-block bg-primary rounded-circle p-1"></span>
|
||||
Another action
|
||||
</a></li>
|
||||
<li><a class="dropdown-item d-flex align-items-center gap-2 py-2" href="#">
|
||||
<span class="d-inline-block bg-danger rounded-circle" style="width: .5em; height: .5em;"></span>
|
||||
<span class="d-inline-block bg-danger rounded-circle p-1"></span>
|
||||
Something else here
|
||||
</a></li>
|
||||
<li><a class="dropdown-item d-flex align-items-center gap-2 py-2" href="#">
|
||||
<span class="d-inline-block bg-info rounded-circle" style="width: .5em; height: .5em;"></span>
|
||||
<span class="d-inline-block bg-info rounded-circle p-1"></span>
|
||||
Separated link
|
||||
</a></li>
|
||||
</ul>
|
||||
@@ -131,19 +131,19 @@ body_class: ""
|
||||
</form>
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li><a class="dropdown-item d-flex align-items-center gap-2 py-2" href="#">
|
||||
<span class="d-inline-block bg-success rounded-circle" style="width: .5em; height: .5em;"></span>
|
||||
<span class="d-inline-block bg-success rounded-circle p-1"></span>
|
||||
Action
|
||||
</a></li>
|
||||
<li><a class="dropdown-item d-flex align-items-center gap-2 py-2" href="#">
|
||||
<span class="d-inline-block bg-primary rounded-circle" style="width: .5em; height: .5em;"></span>
|
||||
<span class="d-inline-block bg-primary rounded-circle p-1"></span>
|
||||
Another action
|
||||
</a></li>
|
||||
<li><a class="dropdown-item d-flex align-items-center gap-2 py-2" href="#">
|
||||
<span class="d-inline-block bg-danger rounded-circle" style="width: .5em; height: .5em;"></span>
|
||||
<span class="d-inline-block bg-danger rounded-circle p-1"></span>
|
||||
Something else here
|
||||
</a></li>
|
||||
<li><a class="dropdown-item d-flex align-items-center gap-2 py-2" href="#">
|
||||
<span class="d-inline-block bg-info rounded-circle" style="width: .5em; height: .5em;"></span>
|
||||
<span class="d-inline-block bg-info rounded-circle p-1"></span>
|
||||
Separated link
|
||||
</a></li>
|
||||
</ul>
|
||||
@@ -336,4 +336,4 @@ body_class: ""
|
||||
<div class="col-4">
|
||||
...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@ body_class: ""
|
||||
<a href="/" class="mb-3 me-2 mb-md-0 text-muted text-decoration-none lh-1">
|
||||
<svg class="bi" width="30" height="24"><use xlink:href="#bootstrap"/></svg>
|
||||
</a>
|
||||
<span class="text-muted">© {{< year >}} Company, Inc</span>
|
||||
<span class="mb-3 mb-md-0 text-muted">© {{< year >}} Company, Inc</span>
|
||||
</div>
|
||||
|
||||
<ul class="nav col-md-4 justify-content-end list-unstyled d-flex">
|
||||
@@ -77,19 +77,19 @@ body_class: ""
|
||||
<div class="b-example-divider"></div>
|
||||
|
||||
<div class="container">
|
||||
<footer class="row row-cols-5 py-5 my-5 border-top">
|
||||
<div class="col">
|
||||
<footer class="row row-cols-1 row-cols-sm-2 row-cols-md-5 py-5 my-5 border-top">
|
||||
<div class="col mb-3">
|
||||
<a href="/" class="d-flex align-items-center mb-3 link-dark text-decoration-none">
|
||||
<svg class="bi me-2" width="40" height="32"><use xlink:href="#bootstrap"/></svg>
|
||||
</a>
|
||||
<p class="text-muted">© {{< year >}}</p>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="col mb-3">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="col mb-3">
|
||||
<h5>Section</h5>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Home</a></li>
|
||||
@@ -100,7 +100,7 @@ body_class: ""
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="col mb-3">
|
||||
<h5>Section</h5>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Home</a></li>
|
||||
@@ -111,7 +111,7 @@ body_class: ""
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="col mb-3">
|
||||
<h5>Section</h5>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Home</a></li>
|
||||
@@ -130,7 +130,7 @@ body_class: ""
|
||||
<div class="container">
|
||||
<footer class="py-5">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<div class="col-6 col-md-2 mb-3">
|
||||
<h5>Section</h5>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Home</a></li>
|
||||
@@ -141,7 +141,7 @@ body_class: ""
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-2">
|
||||
<div class="col-6 col-md-2 mb-3">
|
||||
<h5>Section</h5>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Home</a></li>
|
||||
@@ -152,7 +152,7 @@ body_class: ""
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-2">
|
||||
<div class="col-6 col-md-2 mb-3">
|
||||
<h5>Section</h5>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item mb-2"><a href="#" class="nav-link p-0 text-muted">Home</a></li>
|
||||
@@ -163,11 +163,11 @@ body_class: ""
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-4 offset-1">
|
||||
<div class="col-md-5 offset-md-1 mb-3">
|
||||
<form>
|
||||
<h5>Subscribe to our newsletter</h5>
|
||||
<p>Monthly digest of whats new and exciting from us.</p>
|
||||
<div class="d-flex w-100 gap-2">
|
||||
<div class="d-flex flex-column flex-sm-row w-100 gap-2">
|
||||
<label for="newsletter1" class="visually-hidden">Email address</label>
|
||||
<input id="newsletter1" type="text" class="form-control" placeholder="Email address">
|
||||
<button class="btn btn-primary" type="button">Subscribe</button>
|
||||
@@ -176,7 +176,7 @@ body_class: ""
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between py-4 my-4 border-top">
|
||||
<div class="d-flex flex-column flex-sm-row justify-content-between py-4 my-4 border-top">
|
||||
<p>© {{< year >}} Company, Inc. All rights reserved.</p>
|
||||
<ul class="list-unstyled d-flex">
|
||||
<li class="ms-3"><a class="link-dark" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#twitter"/></svg></a></li>
|
||||
|
||||
@@ -110,8 +110,8 @@ include_js: false
|
||||
<hr class="my-4">
|
||||
|
||||
<h2 class="mt-4">Mixed: mobile and desktop</h2>
|
||||
<p>The Bootstrap v4 grid system has five tiers of classes: xs (extra small, this class infix is not used), sm (small), md (medium), lg (large), and xl (extra large). You can use nearly any combination of these classes to create more dynamic and flexible layouts.</p>
|
||||
<p>Each tier of classes scales up, meaning if you plan on setting the same widths for md, lg and xl, you only need to specify md.</p>
|
||||
<p>The Bootstrap v5 grid system has six tiers of classes: xs (extra small, this class infix is not used), sm (small), md (medium), lg (large), xl (x-large), and xxl (xx-large). You can use nearly any combination of these classes to create more dynamic and flexible layouts.</p>
|
||||
<p>Each tier of classes scales up, meaning if you plan on setting the same widths for md, lg, xl and xxl, you only need to specify md.</p>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8 themed-grid-col">.col-md-8</div>
|
||||
<div class="col-6 col-md-4 themed-grid-col">.col-6 .col-md-4</div>
|
||||
|
||||
@@ -104,7 +104,7 @@ body_class: ""
|
||||
<li><a href="#" class="nav-link px-2 text-white">About</a></li>
|
||||
</ul>
|
||||
|
||||
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3">
|
||||
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search">
|
||||
<input type="search" class="form-control form-control-dark" placeholder="Search..." aria-label="Search">
|
||||
</form>
|
||||
|
||||
@@ -132,7 +132,7 @@ body_class: ""
|
||||
<li><a href="#" class="nav-link px-2 link-dark">Products</a></li>
|
||||
</ul>
|
||||
|
||||
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3">
|
||||
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search">
|
||||
<input type="search" class="form-control" placeholder="Search..." aria-label="Search">
|
||||
</form>
|
||||
|
||||
@@ -172,7 +172,7 @@ body_class: ""
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<form class="w-100 me-3">
|
||||
<form class="w-100 me-3" role="search">
|
||||
<input type="search" class="form-control" placeholder="Search..." aria-label="Search">
|
||||
</form>
|
||||
|
||||
@@ -226,7 +226,7 @@ body_class: ""
|
||||
<svg class="bi me-2" width="40" height="32"><use xlink:href="#bootstrap"/></svg>
|
||||
<span class="fs-4">Double header</span>
|
||||
</a>
|
||||
<form class="col-12 col-lg-auto mb-3 mb-lg-0">
|
||||
<form class="col-12 col-lg-auto mb-3 mb-lg-0" role="search">
|
||||
<input type="search" class="form-control" placeholder="Search..." aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
@@ -279,7 +279,7 @@ body_class: ""
|
||||
</div>
|
||||
<div class="px-3 py-2 border-bottom mb-3">
|
||||
<div class="container d-flex flex-wrap justify-content-center">
|
||||
<form class="col-12 col-lg-auto mb-2 mb-lg-0 me-lg-auto">
|
||||
<form class="col-12 col-lg-auto mb-2 mb-lg-0 me-lg-auto" role="search">
|
||||
<input type="search" class="form-control" placeholder="Search..." aria-label="Search">
|
||||
</form>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ extra_css:
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
@@ -23,7 +23,7 @@ extra_css:
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
@@ -33,8 +33,8 @@ extra_css:
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,8 +56,8 @@ extra_css:
|
||||
<a class="nav-link" href="#">Link</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,8 +90,8 @@ extra_css:
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,8 +124,8 @@ extra_css:
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -158,8 +158,8 @@ extra_css:
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -192,8 +192,8 @@ extra_css:
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -226,8 +226,8 @@ extra_css:
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -260,8 +260,8 @@ extra_css:
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -294,8 +294,8 @@ extra_css:
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -363,8 +363,8 @@ extra_css:
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form>
|
||||
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
|
||||
<form role="search">
|
||||
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ aliases: "/docs/5.1/examples/offcanvas/"
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
@@ -27,7 +27,7 @@ body_class: "d-flex flex-column h-100"
|
||||
<a class="nav-link disabled">Disabled</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<form class="d-flex" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
@@ -89,7 +89,7 @@ When working with the Bootstrap grid system, be sure to place form elements with
|
||||
</div>
|
||||
<div class="col-md">
|
||||
<div class="form-floating">
|
||||
<select class="form-select" id="floatingSelectGrid" aria-label="Floating label select example">
|
||||
<select class="form-select" id="floatingSelectGrid">
|
||||
<option selected>Open this select menu</option>
|
||||
<option value="1">One</option>
|
||||
<option value="2">Two</option>
|
||||
|
||||
@@ -145,10 +145,10 @@ While using visually hidden content (`.visually-hidden`, `aria-label`, and even
|
||||
|
||||
## Sass
|
||||
|
||||
Many form variables are set at a general level to be re-used and extended by individual form components. You'll see these most often as `$btn-input-*` and `$input-*` variables.
|
||||
Many form variables are set at a general level to be re-used and extended by individual form components. You'll see these most often as `$input-btn-*` and `$input-*` variables.
|
||||
|
||||
### Variables
|
||||
|
||||
`$btn-input-*` variables are shared global variables between our [buttons]({{< docsref "/components/buttons" >}}) and our form components. You'll find these frequently reassigned as values to other component-specific variables.
|
||||
`$input-btn-*` variables are shared global variables between our [buttons]({{< docsref "/components/buttons" >}}) and our form components. You'll find these frequently reassigned as values to other component-specific variables.
|
||||
|
||||
{{< scss-docs name="input-btn-variables" file="scss/_variables.scss" >}}
|
||||
|
||||
@@ -46,10 +46,10 @@ You can see the above requirements reflected in this modified RTL starter templa
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="{{< param "cdn.css_rtl" >}}" integrity="{{< param "cdn.css_rtl_hash" >}}" crossorigin="anonymous">
|
||||
|
||||
<title>مرحبا بالعالم!</title>
|
||||
<title>مرحبًا بالعالم!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>مرحبا بالعالم!</h1>
|
||||
<h1>مرحبًا بالعالم!</h1>
|
||||
|
||||
<!-- Optional JavaScript; choose one of the two! -->
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ Gutter classes can also be added to [row columns]({{< docsref "/layout/grid#row-
|
||||
|
||||
The gutters between columns in our predefined grid classes can be removed with `.g-0`. This removes the negative `margin`s from `.row` and the horizontal `padding` from all immediate children columns.
|
||||
|
||||
**Need an edge-to-edge design?** Drop the parent `.container` or `.container-fluid`.
|
||||
**Need an edge-to-edge design?** Drop the parent `.container` or `.container-fluid` and add `.mx-0` to the `.row` to prevent overflow.
|
||||
|
||||
In practice, here's how it looks. Note you can continue to use this with all other predefined grid classes (including column widths, responsive tiers, reorders, and more).
|
||||
|
||||
|
||||
@@ -9,6 +9,52 @@ toc: true
|
||||
|
||||
## v5.2.0
|
||||
|
||||
### New `_maps.scss`
|
||||
|
||||
Bootstrap v5.2.0 introduced a new Sass file, `_maps.scss`, that pulled out several Sass maps from `_variables.scss` to fix an issue where updates to an original map were not applied to secondary maps that extend them. For example, updates to `$theme-colors` were not being applied to other theme maps that relied on `$theme-colors`, breaking key customization workflows. In short, Sass has a limitation where once a default variable or map has been _used_, it cannot be updated.
|
||||
|
||||
This is why variable customizations in Bootstrap have to come after `@import "functions"`, but before `@import "variables"` and the rest of our import stack. The same applies to Sass maps—you must override the defaults before the defaults get used. The following maps have been moved to the new `_maps.scss`:
|
||||
|
||||
- `$theme-colors-rgb`
|
||||
- `$utilities-colors`
|
||||
- `$utilities-text`
|
||||
- `$utilities-text-colors`
|
||||
- `$utilities-bg`
|
||||
- `$utilities-bg-colors`
|
||||
- `$negative-spacers`
|
||||
- `$gutters`
|
||||
|
||||
Your custom Bootstrap CSS builds should now look something like this with a separate maps import.
|
||||
|
||||
```diff
|
||||
// Functions come first
|
||||
@import "functions";
|
||||
|
||||
// Optional variable overrides here
|
||||
+ $custom-color: #df711b;
|
||||
+ $custom-theme-colors: (
|
||||
+ "custom": $custom-color
|
||||
+ );
|
||||
|
||||
// Variables come next
|
||||
@import "variables";
|
||||
|
||||
+ // Optional Sass map overrides here
|
||||
+ $theme-colors: map-merge($theme-colors, $custom-theme-colors);
|
||||
+
|
||||
+ // Followed by our default maps
|
||||
+ @import "maps";
|
||||
+
|
||||
// Rest of our imports
|
||||
@import "mixins";
|
||||
@import "utilities";
|
||||
@import "root";
|
||||
@import "reboot";
|
||||
// etc
|
||||
```
|
||||
|
||||
### Key changes
|
||||
|
||||
- **Introduced new `$enable-container-classes` option.** Now when opting into the experimental CSS Grid layout, `.container-*` classes will still be compiled, unless this option is set to `false`.
|
||||
|
||||
## Dependencies
|
||||
@@ -251,7 +297,7 @@ toc: true
|
||||
|
||||
- <span class="badge bg-danger">Breaking</span> All the events for the dropdown are now triggered on the dropdown toggle button and then bubbled up to the parent element.
|
||||
|
||||
- Dropdown menus now have a `data-bs-popper="static"` attribute set when the positioning of the dropdown is static and `data-bs-popper="none"` when dropdown is in the navbar. This is added by our JavaScript and helps us use custom position styles without interfering with Popper's positioning.
|
||||
- Dropdown menus now have a `data-bs-popper="static"` attribute set when the positioning of the dropdown is static, or dropdown is in the navbar. This is added by our JavaScript and helps us use custom position styles without interfering with Popper's positioning.
|
||||
|
||||
- <span class="badge bg-danger">Breaking</span> Dropped `flip` option for dropdown plugin in favor of native Popper configuration. You can now disable the flipping behavior by passing an empty array for [`fallbackPlacements`](https://popper.js.org/docs/v2/modifiers/flip/#fallbackplacements) option in [flip](https://popper.js.org/docs/v2/modifiers/flip/) modifier.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user