From: Mark Otto Date: Sun, 28 Jun 2026 15:37:02 +0000 (-0700) Subject: Remove unused FocusTrap and ScrollBarHelper utilities (#42595) X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b06cb33fb51a08cb1028ea614c0f9e2b764f9750;p=thirdparty%2Fbootstrap.git Remove unused FocusTrap and ScrollBarHelper utilities (#42595) Both became dead code with the Modal->Dialog / Offcanvas->Drawer rewrite: the native element provides the focus trap and top-layer inerting, and the scroll lock is now :root.dialog-open in CSS. Nothing in js/src imports either utility and neither is part of the public bundle export — only their own unit specs referenced them. Removes the source modules, their committed dist builds, and their specs. --- diff --git a/js/dist/util/focustrap.js b/js/dist/util/focustrap.js deleted file mode 100644 index 59beaf8169..0000000000 --- a/js/dist/util/focustrap.js +++ /dev/null @@ -1,109 +0,0 @@ -/*! - * Bootstrap focustrap.js v6.0.0-alpha1 (https://getbootstrap.com/) - * Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -import EventHandler from '../dom/event-handler.js'; -import SelectorEngine from '../dom/selector-engine.js'; -import Config from './config.js'; - -/** - * -------------------------------------------------------------------------- - * Bootstrap util/focustrap.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - -/** - * Constants - */ - -const NAME = 'focustrap'; -const DATA_KEY = 'bs.focustrap'; -const EVENT_KEY = `.${DATA_KEY}`; -const EVENT_FOCUSIN = `focusin${EVENT_KEY}`; -const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`; -const TAB_KEY = 'Tab'; -const TAB_NAV_FORWARD = 'forward'; -const TAB_NAV_BACKWARD = 'backward'; -const Default = { - autofocus: true, - trapElement: null // The element to trap focus inside of -}; -const DefaultType = { - autofocus: 'boolean', - trapElement: 'element' -}; - -/** - * Class definition - */ - -class FocusTrap extends Config { - constructor(config) { - super(); - this._config = this._getConfig(config); - this._isActive = false; - this._lastTabNavDirection = null; - } - - // Getters - static get Default() { - return Default; - } - static get DefaultType() { - return DefaultType; - } - static get NAME() { - return NAME; - } - - // Public - activate() { - if (this._isActive) { - return; - } - if (this._config.autofocus) { - this._config.trapElement.focus(); - } - EventHandler.off(document, EVENT_KEY); // guard against infinite focus loop - EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event)); - EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event)); - this._isActive = true; - } - deactivate() { - if (!this._isActive) { - return; - } - this._isActive = false; - EventHandler.off(document, EVENT_KEY); - } - - // Private - _handleFocusin(event) { - const { - trapElement - } = this._config; - if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) { - return; - } - const elements = SelectorEngine.focusableChildren(trapElement); - if (elements.length === 0) { - trapElement.focus(); - } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) { - elements.at(-1).focus(); - } else { - elements[0].focus(); - } - } - _handleKeydown(event) { - if (event.key !== TAB_KEY) { - return; - } - this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD; - } -} - -export { FocusTrap as default }; -//# sourceMappingURL=focustrap.js.map diff --git a/js/dist/util/focustrap.js.map b/js/dist/util/focustrap.js.map deleted file mode 100644 index 0b4db47eb1..0000000000 --- a/js/dist/util/focustrap.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"focustrap.js","sources":["../../src/util/focustrap.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'focustrap'\nconst DATA_KEY = 'bs.focustrap'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`\n\nconst TAB_KEY = 'Tab'\nconst TAB_NAV_FORWARD = 'forward'\nconst TAB_NAV_BACKWARD = 'backward'\n\nconst Default = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n}\n\nconst DefaultType = {\n autofocus: 'boolean',\n trapElement: 'element'\n}\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return\n }\n\n if (this._config.autofocus) {\n this._config.trapElement.focus()\n }\n\n EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))\n\n this._isActive = true\n }\n\n deactivate() {\n if (!this._isActive) {\n return\n }\n\n this._isActive = false\n EventHandler.off(document, EVENT_KEY)\n }\n\n // Private\n _handleFocusin(event) {\n const { trapElement } = this._config\n\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return\n }\n\n const elements = SelectorEngine.focusableChildren(trapElement)\n\n if (elements.length === 0) {\n trapElement.focus()\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements.at(-1).focus()\n } else {\n elements[0].focus()\n }\n }\n\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return\n }\n\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD\n }\n}\n\nexport default FocusTrap\n"],"names":["NAME","DATA_KEY","EVENT_KEY","EVENT_FOCUSIN","EVENT_KEYDOWN_TAB","TAB_KEY","TAB_NAV_FORWARD","TAB_NAV_BACKWARD","Default","autofocus","trapElement","DefaultType","FocusTrap","Config","constructor","config","_config","_getConfig","_isActive","_lastTabNavDirection","activate","focus","EventHandler","off","document","on","event","_handleFocusin","_handleKeydown","deactivate","target","contains","elements","SelectorEngine","focusableChildren","length","at","key","shiftKey"],"mappings":"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"} \ No newline at end of file diff --git a/js/dist/util/scrollbar.js b/js/dist/util/scrollbar.js deleted file mode 100644 index d40ff86091..0000000000 --- a/js/dist/util/scrollbar.js +++ /dev/null @@ -1,109 +0,0 @@ -/*! - * Bootstrap scrollbar.js v6.0.0-alpha1 (https://getbootstrap.com/) - * Copyright 2011-2026 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -import Manipulator from '../dom/manipulator.js'; -import SelectorEngine from '../dom/selector-engine.js'; -import { isElement } from './index.js'; - -/** - * -------------------------------------------------------------------------- - * Bootstrap util/scrollBar.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - - -/** - * Constants - */ - -const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; -const SELECTOR_STICKY_CONTENT = '.sticky-top'; -const PROPERTY_PADDING = 'padding-right'; -const PROPERTY_MARGIN = 'margin-right'; - -/** - * Class definition - */ - -class ScrollBarHelper { - constructor() { - this._element = document.body; - } - - // Public - getWidth() { - // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes - const documentWidth = document.documentElement.clientWidth; - return Math.abs(window.innerWidth - documentWidth); - } - hide() { - const width = this.getWidth(); - this._disableOverFlow(); - // give padding to element to balance the hidden scrollbar width - this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); - // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth - this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width); - this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width); - } - reset() { - this._resetElementAttributes(this._element, 'overflow'); - this._resetElementAttributes(this._element, PROPERTY_PADDING); - this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING); - this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN); - } - isOverflowing() { - return this.getWidth() > 0; - } - - // Private - _disableOverFlow() { - this._saveInitialAttribute(this._element, 'overflow'); - this._element.style.overflow = 'hidden'; - } - _setElementAttributes(selector, styleProperty, callback) { - const scrollbarWidth = this.getWidth(); - const manipulationCallBack = element => { - if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { - return; - } - this._saveInitialAttribute(element, styleProperty); - const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty); - element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`); - }; - this._applyManipulationCallback(selector, manipulationCallBack); - } - _saveInitialAttribute(element, styleProperty) { - const actualValue = element.style.getPropertyValue(styleProperty); - if (actualValue) { - Manipulator.setDataAttribute(element, styleProperty, actualValue); - } - } - _resetElementAttributes(selector, styleProperty) { - const manipulationCallBack = element => { - const value = Manipulator.getDataAttribute(element, styleProperty); - // We only want to remove the property if the value is `null`; the value can also be zero - if (value === null) { - element.style.removeProperty(styleProperty); - return; - } - Manipulator.removeDataAttribute(element, styleProperty); - element.style.setProperty(styleProperty, value); - }; - this._applyManipulationCallback(selector, manipulationCallBack); - } - _applyManipulationCallback(selector, callBack) { - if (isElement(selector)) { - callBack(selector); - return; - } - for (const sel of SelectorEngine.find(selector, this._element)) { - callBack(sel); - } - } -} - -export { ScrollBarHelper as default }; -//# sourceMappingURL=scrollbar.js.map diff --git a/js/dist/util/scrollbar.js.map b/js/dist/util/scrollbar.js.map deleted file mode 100644 index dfee4dd9fc..0000000000 --- a/js/dist/util/scrollbar.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"scrollbar.js","sources":["../../src/util/scrollbar.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\nconst PROPERTY_PADDING = 'padding-right'\nconst PROPERTY_MARGIN = 'margin-right'\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth\n return Math.abs(window.innerWidth - documentWidth)\n }\n\n hide() {\n const width = this.getWidth()\n this._disableOverFlow()\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN)\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth()\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return\n }\n\n this._saveInitialAttribute(element, styleProperty)\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty)\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty)\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty)\n return\n }\n\n Manipulator.removeDataAttribute(element, styleProperty)\n element.style.setProperty(styleProperty, value)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n return\n }\n\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel)\n }\n }\n}\n\nexport default ScrollBarHelper\n"],"names":["SELECTOR_FIXED_CONTENT","SELECTOR_STICKY_CONTENT","PROPERTY_PADDING","PROPERTY_MARGIN","ScrollBarHelper","constructor","_element","document","body","getWidth","documentWidth","documentElement","clientWidth","Math","abs","window","innerWidth","hide","width","_disableOverFlow","_setElementAttributes","calculatedValue","reset","_resetElementAttributes","isOverflowing","_saveInitialAttribute","style","overflow","selector","styleProperty","callback","scrollbarWidth","manipulationCallBack","element","getComputedStyle","getPropertyValue","setProperty","Number","parseFloat","_applyManipulationCallback","actualValue","Manipulator","setDataAttribute","value","getDataAttribute","removeProperty","removeDataAttribute","callBack","isElement","sel","SelectorEngine","find"],"mappings":"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"} \ No newline at end of file diff --git a/js/src/util/focustrap.js b/js/src/util/focustrap.js deleted file mode 100644 index 7900ab875a..0000000000 --- a/js/src/util/focustrap.js +++ /dev/null @@ -1,115 +0,0 @@ -/** - * -------------------------------------------------------------------------- - * Bootstrap util/focustrap.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - -import EventHandler from '../dom/event-handler.js' -import SelectorEngine from '../dom/selector-engine.js' -import Config from './config.js' - -/** - * Constants - */ - -const NAME = 'focustrap' -const DATA_KEY = 'bs.focustrap' -const EVENT_KEY = `.${DATA_KEY}` -const EVENT_FOCUSIN = `focusin${EVENT_KEY}` -const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}` - -const TAB_KEY = 'Tab' -const TAB_NAV_FORWARD = 'forward' -const TAB_NAV_BACKWARD = 'backward' - -const Default = { - autofocus: true, - trapElement: null // The element to trap focus inside of -} - -const DefaultType = { - autofocus: 'boolean', - trapElement: 'element' -} - -/** - * Class definition - */ - -class FocusTrap extends Config { - constructor(config) { - super() - this._config = this._getConfig(config) - this._isActive = false - this._lastTabNavDirection = null - } - - // Getters - static get Default() { - return Default - } - - static get DefaultType() { - return DefaultType - } - - static get NAME() { - return NAME - } - - // Public - activate() { - if (this._isActive) { - return - } - - if (this._config.autofocus) { - this._config.trapElement.focus() - } - - EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop - EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event)) - EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event)) - - this._isActive = true - } - - deactivate() { - if (!this._isActive) { - return - } - - this._isActive = false - EventHandler.off(document, EVENT_KEY) - } - - // Private - _handleFocusin(event) { - const { trapElement } = this._config - - if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) { - return - } - - const elements = SelectorEngine.focusableChildren(trapElement) - - if (elements.length === 0) { - trapElement.focus() - } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) { - elements.at(-1).focus() - } else { - elements[0].focus() - } - } - - _handleKeydown(event) { - if (event.key !== TAB_KEY) { - return - } - - this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD - } -} - -export default FocusTrap diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js deleted file mode 100644 index 413f178dac..0000000000 --- a/js/src/util/scrollbar.js +++ /dev/null @@ -1,114 +0,0 @@ -/** - * -------------------------------------------------------------------------- - * Bootstrap util/scrollBar.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - -import Manipulator from '../dom/manipulator.js' -import SelectorEngine from '../dom/selector-engine.js' -import { isElement } from './index.js' - -/** - * Constants - */ - -const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' -const SELECTOR_STICKY_CONTENT = '.sticky-top' -const PROPERTY_PADDING = 'padding-right' -const PROPERTY_MARGIN = 'margin-right' - -/** - * Class definition - */ - -class ScrollBarHelper { - constructor() { - this._element = document.body - } - - // Public - getWidth() { - // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes - const documentWidth = document.documentElement.clientWidth - return Math.abs(window.innerWidth - documentWidth) - } - - hide() { - const width = this.getWidth() - this._disableOverFlow() - // give padding to element to balance the hidden scrollbar width - this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width) - // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth - this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width) - this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width) - } - - reset() { - this._resetElementAttributes(this._element, 'overflow') - this._resetElementAttributes(this._element, PROPERTY_PADDING) - this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING) - this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN) - } - - isOverflowing() { - return this.getWidth() > 0 - } - - // Private - _disableOverFlow() { - this._saveInitialAttribute(this._element, 'overflow') - this._element.style.overflow = 'hidden' - } - - _setElementAttributes(selector, styleProperty, callback) { - const scrollbarWidth = this.getWidth() - const manipulationCallBack = element => { - if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { - return - } - - this._saveInitialAttribute(element, styleProperty) - const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty) - element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`) - } - - this._applyManipulationCallback(selector, manipulationCallBack) - } - - _saveInitialAttribute(element, styleProperty) { - const actualValue = element.style.getPropertyValue(styleProperty) - if (actualValue) { - Manipulator.setDataAttribute(element, styleProperty, actualValue) - } - } - - _resetElementAttributes(selector, styleProperty) { - const manipulationCallBack = element => { - const value = Manipulator.getDataAttribute(element, styleProperty) - // We only want to remove the property if the value is `null`; the value can also be zero - if (value === null) { - element.style.removeProperty(styleProperty) - return - } - - Manipulator.removeDataAttribute(element, styleProperty) - element.style.setProperty(styleProperty, value) - } - - this._applyManipulationCallback(selector, manipulationCallBack) - } - - _applyManipulationCallback(selector, callBack) { - if (isElement(selector)) { - callBack(selector) - return - } - - for (const sel of SelectorEngine.find(selector, this._element)) { - callBack(sel) - } - } -} - -export default ScrollBarHelper diff --git a/js/tests/unit/util/focustrap.spec.js b/js/tests/unit/util/focustrap.spec.js deleted file mode 100644 index 0a20017d59..0000000000 --- a/js/tests/unit/util/focustrap.spec.js +++ /dev/null @@ -1,218 +0,0 @@ -import EventHandler from '../../../src/dom/event-handler.js' -import SelectorEngine from '../../../src/dom/selector-engine.js' -import FocusTrap from '../../../src/util/focustrap.js' -import { clearFixture, createEvent, getFixture } from '../../helpers/fixture.js' - -describe('FocusTrap', () => { - let fixtureEl - - beforeAll(() => { - fixtureEl = getFixture() - }) - - afterEach(() => { - clearFixture() - }) - - describe('activate', () => { - it('should autofocus itself by default', () => { - fixtureEl.innerHTML = '
' - - const trapElement = fixtureEl.querySelector('div') - - const spy = spyOn(trapElement, 'focus') - - const focustrap = new FocusTrap({ trapElement }) - focustrap.activate() - - expect(spy).toHaveBeenCalled() - }) - - it('if configured not to autofocus, should not autofocus itself', () => { - fixtureEl.innerHTML = '
' - - const trapElement = fixtureEl.querySelector('div') - - const spy = spyOn(trapElement, 'focus') - - const focustrap = new FocusTrap({ trapElement, autofocus: false }) - focustrap.activate() - - expect(spy).not.toHaveBeenCalled() - }) - - it('should force focus inside focus trap if it can', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - 'outside', - '
', - ' inside', - '
' - ].join('') - - const trapElement = fixtureEl.querySelector('div') - const focustrap = new FocusTrap({ trapElement }) - focustrap.activate() - - const inside = document.getElementById('inside') - - const focusInListener = () => { - expect(spy).toHaveBeenCalled() - document.removeEventListener('focusin', focusInListener) - resolve() - } - - const spy = spyOn(inside, 'focus') - spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [inside]) - - document.addEventListener('focusin', focusInListener) - - const focusInEvent = createEvent('focusin', { bubbles: true }) - Object.defineProperty(focusInEvent, 'target', { - value: document.getElementById('outside') - }) - - document.dispatchEvent(focusInEvent) - }) - }) - - it('should wrap focus around forward on tab', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - 'outside', - '
', - ' first', - ' inside', - ' last', - '
' - ].join('') - - const trapElement = fixtureEl.querySelector('div') - const focustrap = new FocusTrap({ trapElement }) - focustrap.activate() - - const first = document.getElementById('first') - const inside = document.getElementById('inside') - const last = document.getElementById('last') - const outside = document.getElementById('outside') - - spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [first, inside, last]) - const spy = spyOn(first, 'focus').and.callThrough() - - const focusInListener = () => { - expect(spy).toHaveBeenCalled() - first.removeEventListener('focusin', focusInListener) - resolve() - } - - first.addEventListener('focusin', focusInListener) - - const keydown = createEvent('keydown') - keydown.key = 'Tab' - - document.dispatchEvent(keydown) - outside.focus() - }) - }) - - it('should wrap focus around backwards on shift-tab', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - 'outside', - '
', - ' first', - ' inside', - ' last', - '
' - ].join('') - - const trapElement = fixtureEl.querySelector('div') - const focustrap = new FocusTrap({ trapElement }) - focustrap.activate() - - const first = document.getElementById('first') - const inside = document.getElementById('inside') - const last = document.getElementById('last') - const outside = document.getElementById('outside') - - spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [first, inside, last]) - const spy = spyOn(last, 'focus').and.callThrough() - - const focusInListener = () => { - expect(spy).toHaveBeenCalled() - last.removeEventListener('focusin', focusInListener) - resolve() - } - - last.addEventListener('focusin', focusInListener) - - const keydown = createEvent('keydown') - keydown.key = 'Tab' - keydown.shiftKey = true - - document.dispatchEvent(keydown) - outside.focus() - }) - }) - - it('should force focus on itself if there is no focusable content', () => { - return new Promise(resolve => { - fixtureEl.innerHTML = [ - 'outside', - '
' - ].join('') - - const trapElement = fixtureEl.querySelector('div') - const focustrap = new FocusTrap({ trapElement }) - focustrap.activate() - - const focusInListener = () => { - expect(spy).toHaveBeenCalled() - document.removeEventListener('focusin', focusInListener) - resolve() - } - - const spy = spyOn(focustrap._config.trapElement, 'focus') - - document.addEventListener('focusin', focusInListener) - - const focusInEvent = createEvent('focusin', { bubbles: true }) - Object.defineProperty(focusInEvent, 'target', { - value: document.getElementById('outside') - }) - - document.dispatchEvent(focusInEvent) - }) - }) - }) - - describe('deactivate', () => { - it('should flag itself as no longer active', () => { - const focustrap = new FocusTrap({ trapElement: fixtureEl }) - focustrap.activate() - expect(focustrap._isActive).toBeTrue() - - focustrap.deactivate() - expect(focustrap._isActive).toBeFalse() - }) - - it('should remove all event listeners', () => { - const focustrap = new FocusTrap({ trapElement: fixtureEl }) - focustrap.activate() - - const spy = spyOn(EventHandler, 'off') - focustrap.deactivate() - - expect(spy).toHaveBeenCalled() - }) - - it('doesn\'t try removing event listeners unless it needs to (in case it hasn\'t been activated)', () => { - const focustrap = new FocusTrap({ trapElement: fixtureEl }) - - const spy = spyOn(EventHandler, 'off') - focustrap.deactivate() - - expect(spy).not.toHaveBeenCalled() - }) - }) -}) diff --git a/js/tests/unit/util/scrollbar.spec.js b/js/tests/unit/util/scrollbar.spec.js deleted file mode 100644 index 6dadfcdd1c..0000000000 --- a/js/tests/unit/util/scrollbar.spec.js +++ /dev/null @@ -1,363 +0,0 @@ -import Manipulator from '../../../src/dom/manipulator.js' -import ScrollBarHelper from '../../../src/util/scrollbar.js' -import { clearBodyAndDocument, clearFixture, getFixture } from '../../helpers/fixture.js' - -describe('ScrollBar', () => { - let fixtureEl - const doc = document.documentElement - 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') - const getOverFlowAttr = el => Manipulator.getDataAttribute(el, 'overflow') - const windowCalculations = () => { - return { - htmlClient: document.documentElement.clientWidth, - htmlOffset: document.documentElement.offsetWidth, - docClient: document.body.clientWidth, - htmlBound: document.documentElement.getBoundingClientRect().width, - bodyBound: document.body.getBoundingClientRect().width, - window: window.innerWidth, - width: Math.abs(window.innerWidth - document.documentElement.clientWidth) - } - } - - // 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 - } - - beforeAll(() => { - fixtureEl = getFixture() - // custom fixture to avoid extreme style values - fixtureEl.removeAttribute('style') - }) - - afterAll(() => { - fixtureEl.remove() - }) - - afterEach(() => { - clearFixture() - clearBodyAndDocument() - }) - - beforeEach(() => { - clearBodyAndDocument() - }) - - describe('isBodyOverflowing', () => { - it('should return true if body is overflowing', () => { - document.documentElement.style.overflowY = 'scroll' - document.body.style.overflowY = 'scroll' - fixtureEl.innerHTML = '
' - const result = new ScrollBarHelper().isOverflowing() - - if (isScrollBarHidden()) { - expect(result).toBeFalse() - } else { - expect(result).toBeTrue() - } - }) - - it('should return false if body is not overflowing', () => { - doc.style.overflowY = 'hidden' - document.body.style.overflowY = 'hidden' - fixtureEl.innerHTML = '
' - const scrollBar = new ScrollBarHelper() - const result = scrollBar.isOverflowing() - - expect(result).toBeFalse() - }) - }) - - describe('getWidth', () => { - it('should return an integer greater than zero, if body is overflowing', () => { - doc.style.overflowY = 'scroll' - document.body.style.overflowY = 'scroll' - fixtureEl.innerHTML = '
' - const result = new ScrollBarHelper().getWidth() - - if (isScrollBarHidden()) { - expect(result).toEqual(0) - } else { - expect(result).toBeGreaterThan(1) - } - }) - - it('should return 0 if body is not overflowing', () => { - document.documentElement.style.overflowY = 'hidden' - document.body.style.overflowY = 'hidden' - fixtureEl.innerHTML = '
' - - const result = new ScrollBarHelper().getWidth() - - expect(result).toEqual(0) - }) - }) - - describe('hide - reset', () => { - it('should adjust the inline padding of fixed elements which are full-width', () => { - fixtureEl.innerHTML = [ - '
', - '
', - '
', - '
' - ].join('') - doc.style.overflowY = 'scroll' - - const fixedEl = fixtureEl.querySelector('#fixed1') - const fixedEl2 = fixtureEl.querySelector('#fixed2') - const originalPadding = getPaddingX(fixedEl) - const originalPadding2 = getPaddingX(fixedEl2) - const scrollBar = new ScrollBarHelper() - const expectedPadding = originalPadding + scrollBar.getWidth() - const expectedPadding2 = originalPadding2 + scrollBar.getWidth() - - scrollBar.hide() - - let currentPadding = getPaddingX(fixedEl) - let currentPadding2 = getPaddingX(fixedEl2) - 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)).toBeNull() - expect(getPaddingAttr(fixedEl2)).toBeNull() - expect(currentPadding).toEqual(originalPadding) - expect(currentPadding2).toEqual(originalPadding2) - }) - - it('should remove padding & margin if not existed before adjustment', () => { - fixtureEl.innerHTML = [ - '
', - '
', - '
', - '
' - ].join('') - doc.style.overflowY = 'scroll' - - const fixedEl = fixtureEl.querySelector('#fixed') - const stickyEl = fixtureEl.querySelector('#sticky') - const scrollBar = new ScrollBarHelper() - - scrollBar.hide() - scrollBar.reset() - - expect(fixedEl.getAttribute('style').includes('padding-right')).toBeFalse() - expect(stickyEl.getAttribute('style').includes('margin-right')).toBeFalse() - }) - - it('should adjust the inline margin and padding of sticky elements', () => { - fixtureEl.innerHTML = [ - '
', - '
', - '
' - ].join('') - doc.style.overflowY = 'scroll' - - const stickyTopEl = fixtureEl.querySelector('.sticky-top') - const originalMargin = getMarginX(stickyTopEl) - const originalPadding = getPaddingX(stickyTopEl) - const scrollBar = new ScrollBarHelper() - const expectedMargin = originalMargin - scrollBar.getWidth() - const expectedPadding = originalPadding + scrollBar.getWidth() - scrollBar.hide() - - 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)).toBeNull() - expect(getMarginX(stickyTopEl)).toEqual(originalMargin) - expect(getPaddingAttr(stickyTopEl)).toBeNull() - expect(getPaddingX(stickyTopEl)).toEqual(originalPadding) - }) - - it('should not adjust the inline margin and padding of sticky and fixed elements when element do not have full width', () => { - fixtureEl.innerHTML = '
' - - const stickyTopEl = fixtureEl.querySelector('.sticky-top') - const originalMargin = getMarginX(stickyTopEl) - const originalPadding = getPaddingX(stickyTopEl) - - const scrollBar = new ScrollBarHelper() - scrollBar.hide() - - const currentMargin = getMarginX(stickyTopEl) - const currentPadding = getPaddingX(stickyTopEl) - - 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 = [ - '
', - '
', - '
' - ].join('') - - document.body.style.overflowY = 'scroll' - const scrollBar = new ScrollBarHelper() - - const hasPaddingAttr = el => el.hasAttribute('data-bs-padding-right') - const hasMarginAttr = el => el.hasAttribute('data-bs-margin-right') - const stickyEl = fixtureEl.querySelector('#sticky') - const originalPadding = getPaddingX(stickyEl) - const originalMargin = getMarginX(stickyEl) - const scrollBarWidth = scrollBar.getWidth() - - scrollBar.hide() - - expect(getPaddingX(stickyEl)).toEqual(scrollBarWidth + originalPadding) - const expectedMargin = scrollBarWidth + originalMargin - expect(getMarginX(stickyEl)).toEqual(expectedMargin === 0 ? expectedMargin : -expectedMargin) - expect(hasMarginAttr(stickyEl)).toBeFalse() // We do not have to keep css margin - expect(hasPaddingAttr(stickyEl)).toBeFalse() // We do not have to keep css padding - - scrollBar.reset() - - expect(getPaddingX(stickyEl)).toEqual(originalPadding) - expect(getPaddingX(stickyEl)).toEqual(originalPadding) - }) - - describe('Body Handling', () => { - it('should ignore other inline styles when trying to restore body defaults ', () => { - document.body.style.color = 'red' - - const scrollBar = new ScrollBarHelper() - const scrollBarWidth = scrollBar.getWidth() - scrollBar.hide() - - expect(getPaddingX(document.body)).toEqual(scrollBarWidth) - expect(document.body.style.color).toEqual('red') - - scrollBar.reset() - }) - - it('should hide scrollbar and reset it to its initial value', () => { - const styleSheetPadding = '7px' - fixtureEl.innerHTML = [ - '' - ].join('') - - const el = document.body - const inlineStylePadding = '10px' - el.style.paddingRight = inlineStylePadding - - const originalPadding = getPaddingX(el) - 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() - const scrollBarWidth = scrollBar.getWidth() - - scrollBar.hide() - - const currentPadding = getPaddingX(el) - - expect(currentPadding).toEqual(scrollBarWidth + originalPadding) - expect(currentPadding).toEqual(scrollBarWidth + parseIntDecimal(inlineStylePadding)) - expect(getPaddingAttr(el)).toEqual(inlineStylePadding) - expect(getOverFlow(el)).toEqual('hidden') - expect(getOverFlowAttr(el)).toEqual(originalOverFlow) - - scrollBar.reset() - - const currentPadding1 = getPaddingX(el) - expect(currentPadding1).toEqual(originalPadding) - expect(getPaddingAttr(el)).toBeNull() - expect(getOverFlow(el)).toEqual(originalOverFlow) - expect(getOverFlowAttr(el)).toBeNull() - }) - - it('should hide scrollbar and reset it to its initial value - respecting css rules', () => { - const styleSheetPadding = '7px' - fixtureEl.innerHTML = [ - '' - ].join('') - const el = document.body - const originalPadding = getPaddingX(el) - const originalOverFlow = 'scroll' - el.style.overflow = originalOverFlow - const scrollBar = new ScrollBarHelper() - const scrollBarWidth = scrollBar.getWidth() - - scrollBar.hide() - - const currentPadding = getPaddingX(el) - - expect(currentPadding).toEqual(scrollBarWidth + originalPadding) - 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) - - scrollBar.reset() - - const currentPadding1 = getPaddingX(el) - expect(currentPadding1).toEqual(originalPadding) - expect(getPaddingAttr(el)).toBeNull() - expect(getOverFlow(el)).toEqual(originalOverFlow) - expect(getOverFlowAttr(el)).toBeNull() - }) - - it('should not adjust the inline body padding when it does not overflow', () => { - const originalPadding = getPaddingX(document.body) - const scrollBar = new ScrollBarHelper() - - // Hide scrollbars to prevent the body overflowing - doc.style.overflowY = 'hidden' - doc.style.paddingRight = '0px' - - scrollBar.hide() - const currentPadding = getPaddingX(document.body) - - expect(currentPadding).toEqual(originalPadding) - scrollBar.reset() - }) - - it('should not adjust the inline body padding when it does not overflow, even on a scaled display', () => { - const originalPadding = getPaddingX(document.body) - const scrollBar = new ScrollBarHelper() - // Remove body margins as would be done by Bootstrap css - document.body.style.margin = '0' - - // Hide scrollbars to prevent the body overflowing - doc.style.overflowY = 'hidden' - - // Simulate a discrepancy between exact, i.e. floating point body width, and rounded body width - // as it can occur when zooming or scaling the display to something else than 100% - doc.style.paddingRight = '.48px' - scrollBar.hide() - - const currentPadding = getPaddingX(document.body) - - expect(currentPadding).toEqual(originalPadding) - - scrollBar.reset() - }) - }) - }) -})