diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index a4ad6bd3473b..000000000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,254 +0,0 @@
-{
- "root": true,
- "extends": [
- "plugin:import/errors",
- "plugin:import/warnings",
- "plugin:unicorn/recommended",
- "xo",
- "xo/browser"
- ],
- "rules": {
- "arrow-body-style": "off",
- "capitalized-comments": "off",
- "comma-dangle": [
- "error",
- "never"
- ],
- "import/extensions": [
- "error",
- "ignorePackages",
- {
- "js": "always"
- }
- ],
- "import/first": "error",
- "import/newline-after-import": "error",
- "import/no-absolute-path": "error",
- "import/no-amd": "error",
- "import/no-cycle": [
- "error",
- {
- "ignoreExternal": true
- }
- ],
- "import/no-duplicates": "error",
- "import/no-extraneous-dependencies": "error",
- "import/no-mutable-exports": "error",
- "import/no-named-as-default": "error",
- "import/no-named-as-default-member": "error",
- "import/no-named-default": "error",
- "import/no-self-import": "error",
- "import/no-unassigned-import": [
- "error"
- ],
- "import/no-useless-path-segments": "error",
- "import/order": "error",
- "indent": [
- "error",
- 2,
- {
- "MemberExpression": "off",
- "SwitchCase": 1
- }
- ],
- "logical-assignment-operators": "off",
- "max-params": [
- "warn",
- 5
- ],
- "multiline-ternary": [
- "error",
- "always-multiline"
- ],
- "new-cap": [
- "error",
- {
- "properties": false
- }
- ],
- "no-console": "error",
- "no-negated-condition": "off",
- "object-curly-spacing": [
- "error",
- "always"
- ],
- "operator-linebreak": [
- "error",
- "after"
- ],
- "prefer-object-has-own": "off",
- "prefer-template": "error",
- "semi": [
- "error",
- "never"
- ],
- "strict": "error",
- "unicorn/explicit-length-check": "off",
- "unicorn/filename-case": "off",
- "unicorn/no-anonymous-default-export": "off",
- "unicorn/no-array-callback-reference": "off",
- "unicorn/no-array-method-this-argument": "off",
- "unicorn/no-null": "off",
- "unicorn/no-typeof-undefined": "off",
- "unicorn/no-unused-properties": "error",
- "unicorn/numeric-separators-style": "off",
- "unicorn/prefer-array-flat": "off",
- "unicorn/prefer-at": "off",
- "unicorn/prefer-dom-node-dataset": "off",
- "unicorn/prefer-global-this": "off",
- "unicorn/prefer-module": "off",
- "unicorn/prefer-query-selector": "off",
- "unicorn/prefer-spread": "off",
- "unicorn/prefer-string-raw": "off",
- "unicorn/prefer-string-replace-all": "off",
- "unicorn/prefer-structured-clone": "off",
- "unicorn/prevent-abbreviations": "off"
- },
- "overrides": [
- {
- "files": [
- "build/**"
- ],
- "env": {
- "browser": false,
- "node": true
- },
- "parserOptions": {
- "sourceType": "module"
- },
- "rules": {
- "no-console": "off",
- "unicorn/prefer-top-level-await": "off"
- }
- },
- {
- "files": [
- "js/**"
- ],
- "parserOptions": {
- "sourceType": "module"
- }
- },
- {
- "files": [
- "js/tests/*.js",
- "js/tests/integration/rollup*.js"
- ],
- "env": {
- "node": true
- },
- "parserOptions": {
- "sourceType": "script"
- }
- },
- {
- "files": [
- "js/tests/unit/**"
- ],
- "env": {
- "jasmine": true
- },
- "rules": {
- "no-console": "off",
- "unicorn/consistent-function-scoping": "off",
- "unicorn/no-useless-undefined": "off",
- "unicorn/prefer-add-event-listener": "off"
- }
- },
- {
- "files": [
- "js/tests/visual/**"
- ],
- "plugins": [
- "html"
- ],
- "settings": {
- "html/html-extensions": [
- ".html"
- ]
- },
- "rules": {
- "no-console": "off",
- "no-new": "off",
- "unicorn/no-array-for-each": "off"
- }
- },
- {
- "files": [
- "scss/tests/**"
- ],
- "env": {
- "node": true
- },
- "parserOptions": {
- "sourceType": "script"
- }
- },
- {
- "files": [
- "site/**"
- ],
- "env": {
- "browser": true,
- "node": false
- },
- "parserOptions": {
- "sourceType": "script",
- "ecmaVersion": 2019
- },
- "rules": {
- "no-new": "off",
- "unicorn/no-array-for-each": "off"
- }
- },
- {
- "files": [
- "site/src/assets/application.js",
- "site/src/assets/partials/*.js",
- "site/src/assets/search.js",
- "site/src/assets/snippets.js",
- "site/src/assets/stackblitz.js",
- "site/src/plugins/*.js"
- ],
- "parserOptions": {
- "sourceType": "module",
- "ecmaVersion": 2020
- }
- },
- {
- "files": [
- "site/src/assets/examples/cheatsheet/cheatsheet.js",
- "site/src/assets/examples/sidebars/sidebars.js"
- ],
- "parserOptions": {
- "sourceType": "module",
- "ecmaVersion": 2020
- },
- "rules": {
- "import/no-unresolved": "off"
- }
- },
- {
- "files": [
- "**/*.md"
- ],
- "plugins": [
- "markdown"
- ],
- "processor": "markdown/markdown"
- },
- {
- "files": [
- "**/*.md/*.js",
- "**/*.md/*.mjs"
- ],
- "extends": "plugin:markdown/recommended-legacy",
- "parserOptions": {
- "sourceType": "module"
- },
- "rules": {
- "unicorn/prefer-node-protocol": "off"
- }
- }
- ]
-}
diff --git a/eslint.config.js b/eslint.config.js
index 02cbed068584..d807078449a1 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -95,12 +95,12 @@ const localRules = {
'unicorn/no-unused-properties': 'error',
'unicorn/numeric-separators-style': 'off',
'unicorn/prefer-array-flat': 'off',
- 'unicorn/prefer-at': 'off',
+ 'unicorn/prefer-at': 'error',
'unicorn/prefer-dom-node-dataset': 'off',
'unicorn/prefer-global-this': 'off',
'unicorn/prefer-module': 'off',
'unicorn/prefer-query-selector': 'off',
- 'unicorn/prefer-spread': 'off',
+ 'unicorn/prefer-spread': 'error',
'unicorn/prefer-string-raw': 'off',
'unicorn/prefer-string-replace-all': 'off',
'unicorn/prefer-structured-clone': 'off',
@@ -266,17 +266,14 @@ const eslintConfig = [
}
},
- // site/** — browser, script mode, older ecmaVersion
+ // site/** — browser, script mode
{
files: ['site/**'],
languageOptions: {
globals: {
...globals.browser
},
- sourceType: 'script',
- parserOptions: {
- ecmaVersion: 2019
- }
+ sourceType: 'script'
},
rules: {
'no-new': 'off',
@@ -296,10 +293,7 @@ const eslintConfig = [
'site/src/plugins/*.js'
],
languageOptions: {
- sourceType: 'module',
- parserOptions: {
- ecmaVersion: 2020
- }
+ sourceType: 'module'
},
// These files may have eslint-disable directives for the old import plugin
linterOptions: {
@@ -322,10 +316,7 @@ const eslintConfig = [
'site/src/assets/examples/sidebars/sidebars.js'
],
languageOptions: {
- sourceType: 'module',
- parserOptions: {
- ecmaVersion: 2020
- }
+ sourceType: 'module'
},
rules: {
'import/no-unresolved': 'off'
diff --git a/js/src/combobox.js b/js/src/combobox.js
index fad08043b7ed..9076e13a2418 100644
--- a/js/src/combobox.js
+++ b/js/src/combobox.js
@@ -359,7 +359,7 @@ class Combobox extends BaseComponent {
const items = this._getVisibleItems()
if (items.length > 0) {
- const target = key === ARROW_DOWN_KEY ? items[0] : items[items.length - 1]
+ const target = key === ARROW_DOWN_KEY ? items[0] : items.at(-1)
target.focus()
}
@@ -404,7 +404,7 @@ class Combobox extends BaseComponent {
event.preventDefault()
const items = this._getVisibleItems()
if (items.length > 0) {
- const targetItem = key === HOME_KEY ? items[0] : items[items.length - 1]
+ const targetItem = key === HOME_KEY ? items[0] : items.at(-1)
targetItem.focus()
}
diff --git a/js/src/dom/data.js b/js/src/dom/data.js
index 10eee5e22469..6ab30c9bbe2d 100644
--- a/js/src/dom/data.js
+++ b/js/src/dom/data.js
@@ -23,7 +23,7 @@ export default {
// can be removed later when multiple key/instances are fine to be used
if (!instanceMap.has(key) && instanceMap.size !== 0) {
// eslint-disable-next-line no-console
- console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)
+ console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${[...instanceMap.keys()][0]}.`)
return
}
diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js
index d4cee4d811a1..9e83d2064ab2 100644
--- a/js/src/dom/selector-engine.js
+++ b/js/src/dom/selector-engine.js
@@ -34,7 +34,7 @@ const getSelector = element => {
const SelectorEngine = {
find(selector, element = document.documentElement) {
- return [].concat(...Element.prototype.querySelectorAll.call(element, selector))
+ return [...Element.prototype.querySelectorAll.call(element, selector)]
},
findOne(selector, element = document.documentElement) {
@@ -42,7 +42,7 @@ const SelectorEngine = {
},
children(element, selector) {
- return [].concat(...element.children).filter(child => child.matches(selector))
+ return [...element.children].filter(child => child.matches(selector))
},
parents(element, selector) {
diff --git a/js/src/menu.js b/js/src/menu.js
index 3689b3fdddf3..f70a13457606 100644
--- a/js/src/menu.js
+++ b/js/src/menu.js
@@ -186,7 +186,7 @@ class Menu extends BaseComponent {
this._createFloating()
if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {
- for (const element of [].concat(...document.body.children)) {
+ for (const element of document.body.children) {
EventHandler.on(element, 'mouseover', noop)
}
}
@@ -249,7 +249,7 @@ class Menu extends BaseComponent {
this._closeAllSubmenus()
if ('ontouchstart' in document.documentElement) {
- for (const element of [].concat(...document.body.children)) {
+ for (const element of document.body.children) {
EventHandler.off(element, 'mouseover', noop)
}
}
@@ -837,7 +837,7 @@ class Menu extends BaseComponent {
.filter(element => isVisible(element))
if (items.length) {
- const targetItem = key === HOME_KEY ? items[0] : items[items.length - 1]
+ const targetItem = key === HOME_KEY ? items[0] : items.at(-1)
targetItem.focus()
}
diff --git a/js/src/otp-input.js b/js/src/otp-input.js
index 6641eb39cd02..dffa1d97b31d 100644
--- a/js/src/otp-input.js
+++ b/js/src/otp-input.js
@@ -66,7 +66,7 @@ class OtpInput extends BaseComponent {
}
setValue(value) {
- const chars = String(value).split('')
+ const chars = [...String(value)]
for (const [index, input] of this._inputs.entries()) {
input.value = chars[index] || ''
}
@@ -136,7 +136,7 @@ class OtpInput extends BaseComponent {
// Handle multi-character input (some browsers/autofill)
if (value.length > 1) {
// Distribute characters across inputs
- const chars = value.split('')
+ const chars = [...value]
input.value = chars[0] || ''
for (let i = 1; i < chars.length && index + i < this._inputs.length; i++) {
diff --git a/js/src/tab.js b/js/src/tab.js
index f02519eb97b8..70375ba25d11 100644
--- a/js/src/tab.js
+++ b/js/src/tab.js
@@ -162,7 +162,7 @@ class Tab extends BaseComponent {
let nextActiveElement
if ([HOME_KEY, END_KEY].includes(event.key)) {
- nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]
+ nextActiveElement = event.key === HOME_KEY ? children[0] : children.at(-1)
} else {
const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)
nextActiveElement = getNextActiveElement(children, event.target, isNext, true)
diff --git a/js/src/tooltip.js b/js/src/tooltip.js
index bf5fadb97710..106085906dc7 100644
--- a/js/src/tooltip.js
+++ b/js/src/tooltip.js
@@ -242,7 +242,7 @@ class Tooltip extends BaseComponent {
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement) {
- for (const element of [].concat(...document.body.children)) {
+ for (const element of document.body.children) {
EventHandler.on(element, 'mouseover', noop)
}
}
@@ -276,7 +276,7 @@ class Tooltip extends BaseComponent {
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
- for (const element of [].concat(...document.body.children)) {
+ for (const element of document.body.children) {
EventHandler.off(element, 'mouseover', noop)
}
}
diff --git a/js/src/util/focustrap.js b/js/src/util/focustrap.js
index 158f3d1846d5..7900ab875a1d 100644
--- a/js/src/util/focustrap.js
+++ b/js/src/util/focustrap.js
@@ -97,7 +97,7 @@ class FocusTrap extends Config {
if (elements.length === 0) {
trapElement.focus()
} else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {
- elements[elements.length - 1].focus()
+ elements.at(-1).focus()
} else {
elements[0].focus()
}
diff --git a/js/src/util/sanitizer.js b/js/src/util/sanitizer.js
index bcd565a9cfef..cb33633ffd39 100644
--- a/js/src/util/sanitizer.js
+++ b/js/src/util/sanitizer.js
@@ -92,7 +92,7 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
const domParser = new window.DOMParser()
const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')
- const elements = [].concat(...createdDocument.body.querySelectorAll('*'))
+ const elements = [...createdDocument.body.querySelectorAll('*')]
for (const element of elements) {
const elementName = element.nodeName.toLowerCase()
@@ -102,8 +102,8 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
continue
}
- const attributeList = [].concat(...element.attributes)
- const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])
+ const attributeList = [...element.attributes]
+ const allowedAttributes = [...(allowList['*'] || []), ...(allowList[elementName] || [])]
for (const attribute of attributeList) {
if (!allowedAttribute(attribute, allowedAttributes)) {
diff --git a/js/tests/integration/bundle-modularity.js b/js/tests/integration/bundle-modularity.js
index d8f6fc0df844..574f54d219bd 100644
--- a/js/tests/integration/bundle-modularity.js
+++ b/js/tests/integration/bundle-modularity.js
@@ -2,6 +2,6 @@ import Tooltip from '../../dist/tooltip.js'
import '../../dist/carousel.js' // eslint-disable-line import/no-unassigned-import
window.addEventListener('load', () => {
- [].concat(...document.querySelectorAll('[data-bs-toggle="tooltip"]'))
+ [...document.querySelectorAll('[data-bs-toggle="tooltip"]')]
.map(tooltipNode => new Tooltip(tooltipNode))
})
diff --git a/js/tests/integration/bundle.js b/js/tests/integration/bundle.js
index 0603bfdfb2dc..9f7404142838 100644
--- a/js/tests/integration/bundle.js
+++ b/js/tests/integration/bundle.js
@@ -1,6 +1,6 @@
import { Tooltip } from '../../../dist/js/bootstrap.js'
window.addEventListener('load', () => {
- [].concat(...document.querySelectorAll('[data-bs-toggle="tooltip"]'))
+ [...document.querySelectorAll('[data-bs-toggle="tooltip"]')]
.map(tooltipNode => new Tooltip(tooltipNode))
})
diff --git a/js/tests/unit/collapse.spec.js b/js/tests/unit/collapse.spec.js
index f25d9fc3966b..fddea9c01fb1 100644
--- a/js/tests/unit/collapse.spec.js
+++ b/js/tests/unit/collapse.spec.js
@@ -130,7 +130,7 @@ describe('Collapse', () => {
const collapseEl1 = fixtureEl.querySelector('#collapse1')
const collapseEl2 = fixtureEl.querySelector('#collapse2')
- const collapseList = [].concat(...fixtureEl.querySelectorAll('.collapse'))
+ const collapseList = [...fixtureEl.querySelectorAll('.collapse')]
.map(el => new Collapse(el, {
parent,
toggle: false
diff --git a/js/tests/unit/dom/selector-engine.spec.js b/js/tests/unit/dom/selector-engine.spec.js
index 95d9bf8ec9d8..f0ec5faf9a0b 100644
--- a/js/tests/unit/dom/selector-engine.spec.js
+++ b/js/tests/unit/dom/selector-engine.spec.js
@@ -68,7 +68,7 @@ describe('SelectorEngine', () => {
].join('')
const list = fixtureEl.querySelector('ul')
- const liList = [].concat(...fixtureEl.querySelectorAll('li'))
+ const liList = [...fixtureEl.querySelectorAll('li')]
const result = SelectorEngine.children(list, 'li')
expect(result).toEqual(liList)
@@ -356,7 +356,7 @@ describe('SelectorEngine', () => {
const testEl = fixtureEl.querySelector('#test')
- expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual([...fixtureEl.querySelectorAll('.target')])
})
it('should get elements if several ids are given', () => {
@@ -368,7 +368,7 @@ describe('SelectorEngine', () => {
const testEl = fixtureEl.querySelector('#test')
- expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual([...fixtureEl.querySelectorAll('.target')])
})
it('should get elements if several ids with special chars are given', () => {
@@ -380,7 +380,7 @@ describe('SelectorEngine', () => {
const testEl = fixtureEl.querySelector('#test')
- expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual([...fixtureEl.querySelectorAll('.target')])
})
it('should get elements in array, from href if no data-bs-target set', () => {
@@ -392,7 +392,7 @@ describe('SelectorEngine', () => {
const testEl = fixtureEl.querySelector('#test')
- expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual([...fixtureEl.querySelectorAll('.target')])
})
it('should return empty array if elements not found', () => {
diff --git a/js/tests/visual/datepicker.html b/js/tests/visual/datepicker.html
index 0bf12ec7f072..e9e719edb81e 100644
--- a/js/tests/visual/datepicker.html
+++ b/js/tests/visual/datepicker.html
@@ -8,7 +8,7 @@
-
Datepicker Bootstrap Visual Test
+ Datepicker Bootstrap Visual Test
@@ -94,7 +94,7 @@ JavaScript Initialization
-
+
diff --git a/js/tests/visual/menu-submenu.html b/js/tests/visual/menu-submenu.html
index 2eb326cfc209..eb07538826ae 100644
--- a/js/tests/visual/menu-submenu.html
+++ b/js/tests/visual/menu-submenu.html
@@ -44,7 +44,7 @@
-
Menu Submenus Bootstrap Visual Test
+
Menu Submenus Bootstrap Visual Test
Keyboard Navigation:
@@ -59,7 +59,7 @@
Menu Submenus Bootstrap Visu
Basic Submenu
- Single level submenu with hover and click activation.
+ Single level submenu with hover and click activation.
@@ -89,7 +89,7 @@
Basic Submenu
Nested Submenus (Multi-level)
- Three levels of nested submenus.
+ Three levels of nested submenus.
@@ -126,7 +126,7 @@
Nested Submenus (Multi-level)
Multiple Submenus at Same Level
- Multiple submenu triggers in the same menu - opening one closes the other.
+ Multiple submenu triggers in the same menu - opening one closes the other.
@@ -175,7 +175,7 @@
Multiple Submenus at Same Level
Viewport Detection (Flipping)
- Submenus flip to the opposite side when there's not enough space. Try the one on the right.
+ Submenus flip to the opposite side when there's not enough space. Try the one on the right.
@@ -220,7 +220,7 @@
Viewport Detection (Flipping)
Navbar Integration
- Submenus work within navbar menus.
+ Submenus work within navbar menus.
@@ -127,7 +127,7 @@ import Placeholder from "@shortcodes/Placeholder.astro"
- 9 mins
+ 9 mins
@@ -142,7 +142,7 @@ import Placeholder from "@shortcodes/Placeholder.astro"
- 9 mins
+ 9 mins
@@ -158,7 +158,7 @@ import Placeholder from "@shortcodes/Placeholder.astro"
- 9 mins
+ 9 mins
@@ -173,7 +173,7 @@ import Placeholder from "@shortcodes/Placeholder.astro"
- 9 mins
+ 9 mins
@@ -188,7 +188,7 @@ import Placeholder from "@shortcodes/Placeholder.astro"
- 9 mins
+ 9 mins
@@ -199,7 +199,7 @@ import Placeholder from "@shortcodes/Placeholder.astro"
-