diff options
| -rw-r--r-- | .eslintrc.json | 8 | ||||
| -rw-r--r-- | js/src/base-component.js | 6 | ||||
| -rw-r--r-- | js/src/button.js | 4 | ||||
| -rw-r--r-- | js/src/carousel.js | 16 | ||||
| -rw-r--r-- | js/src/collapse.js | 5 | ||||
| -rw-r--r-- | js/src/dom/event-handler.js | 4 | ||||
| -rw-r--r-- | js/src/dom/manipulator.js | 7 | ||||
| -rw-r--r-- | js/src/dom/selector-engine.js | 6 | ||||
| -rw-r--r-- | js/src/dropdown.js | 19 | ||||
| -rw-r--r-- | js/src/modal.js | 15 | ||||
| -rw-r--r-- | js/src/offcanvas.js | 8 | ||||
| -rw-r--r-- | js/src/scrollspy.js | 13 | ||||
| -rw-r--r-- | js/src/tab.js | 5 | ||||
| -rw-r--r-- | js/src/tooltip.js | 10 | ||||
| -rw-r--r-- | js/src/util/component-functions.js | 4 | ||||
| -rw-r--r-- | js/src/util/index.js | 38 | ||||
| -rw-r--r-- | js/src/util/sanitizer.js | 5 | ||||
| -rw-r--r-- | js/src/util/scrollbar.js | 4 | ||||
| -rw-r--r-- | js/tests/integration/bundle-modularity.js | 1 | ||||
| -rw-r--r-- | js/tests/integration/bundle.js | 1 | ||||
| -rw-r--r-- | package-lock.json | 26 | ||||
| -rw-r--r-- | package.json | 1 |
22 files changed, 139 insertions, 67 deletions
diff --git a/.eslintrc.json b/.eslintrc.json index 9bd404dfa..8a433a16f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,11 +1,13 @@ { "root": true, + "plugins": ["ssr-friendly"], "extends": [ "plugin:import/errors", "plugin:import/warnings", "plugin:unicorn/recommended", "xo", - "xo/browser" + "xo/browser", + "plugin:ssr-friendly/recommended" ], "rules": { "arrow-body-style": "off", @@ -63,6 +65,8 @@ "unicorn/prefer-prototype-methods": "off", "unicorn/prefer-query-selector": "off", "unicorn/prefer-spread": "off", - "unicorn/prevent-abbreviations": "off" + "unicorn/prevent-abbreviations": "off", + "ssr-friendly/no-dom-globals-in-react-cc-render": "off", + "ssr-friendly/no-dom-globals-in-react-fc": "off" } } diff --git a/js/src/base-component.js b/js/src/base-component.js index cb65bed8e..2f4d9f38b 100644 --- a/js/src/base-component.js +++ b/js/src/base-component.js @@ -8,7 +8,9 @@ import Data from './dom/data' import { executeAfterTransition, - getElement + getElement, + getWindow, + getDocument } from './util/index' import EventHandler from './dom/event-handler' @@ -29,6 +31,8 @@ class BaseComponent { } this._element = element + this._window = getWindow() + this._document = getDocument() Data.set(this._element, this.constructor.DATA_KEY, this) } diff --git a/js/src/button.js b/js/src/button.js index 0578ed6b9..432fe4e0d 100644 --- a/js/src/button.js +++ b/js/src/button.js @@ -5,7 +5,7 @@ * -------------------------------------------------------------------------- */ -import { defineJQueryPlugin } from './util/index' +import { defineJQueryPlugin, getDocument } from './util/index' import EventHandler from './dom/event-handler' import BaseComponent from './base-component' @@ -65,7 +65,7 @@ class Button extends BaseComponent { * ------------------------------------------------------------------------ */ -EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => { +EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => { event.preventDefault() const button = event.target.closest(SELECTOR_DATA_TOGGLE) diff --git a/js/src/carousel.js b/js/src/carousel.js index 86daa0795..5a92f630d 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -13,7 +13,9 @@ import { getNextActiveElement, reflow, triggerTransitionEnd, - typeCheckConfig + typeCheckConfig, + getDocument, + getWindow } from './util/index' import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' @@ -120,8 +122,8 @@ class Carousel extends BaseComponent { this._config = this._getConfig(config) this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element) - this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 - this._pointerEvent = Boolean(window.PointerEvent) + this._touchSupported = 'ontouchstart' in this._document.documentElement || this._window.navigator.maxTouchPoints > 0 + this._pointerEvent = Boolean(this._window.PointerEvent) this._addEventListeners() } @@ -145,7 +147,7 @@ class Carousel extends BaseComponent { nextWhenVisible() { // Don't call next when the page isn't visible // or the carousel or its parent isn't visible - if (!document.hidden && isVisible(this._element)) { + if (!this._document.hidden && isVisible(this._element)) { this.next() } } @@ -182,7 +184,7 @@ class Carousel extends BaseComponent { this._updateInterval() this._interval = setInterval( - (document.visibilityState ? this.nextWhenVisible : this.next).bind(this), + (this._document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval ) } @@ -569,9 +571,9 @@ class Carousel extends BaseComponent { * ------------------------------------------------------------------------ */ -EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler) +EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler) -EventHandler.on(window, EVENT_LOAD_DATA_API, () => { +EventHandler.on(getWindow(), EVENT_LOAD_DATA_API, () => { const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE) for (let i = 0, len = carousels.length; i < len; i++) { diff --git a/js/src/collapse.js b/js/src/collapse.js index edfc7ea85..a438c6c3d 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -11,7 +11,8 @@ import { getSelectorFromElement, getElementFromSelector, reflow, - typeCheckConfig + typeCheckConfig, + getDocument } from './util/index' import Data from './dom/data' import EventHandler from './dom/event-handler' @@ -309,7 +310,7 @@ class Collapse extends BaseComponent { * ------------------------------------------------------------------------ */ -EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { +EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { // preventDefault only for <a> elements (which change the URL) not inside the collapsible element if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) { event.preventDefault() diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js index bf895dc6e..95e9bf454 100644 --- a/js/src/dom/event-handler.js +++ b/js/src/dom/event-handler.js @@ -5,7 +5,7 @@ * -------------------------------------------------------------------------- */ -import { getjQuery } from '../util/index' +import { getjQuery, getDocument } from '../util/index' /** * ------------------------------------------------------------------------ @@ -309,7 +309,7 @@ const EventHandler = { } if (isNative) { - evt = document.createEvent('HTMLEvents') + evt = getDocument().createEvent('HTMLEvents') evt.initEvent(typeEvent, bubbles, true) } else { evt = new CustomEvent(event, { diff --git a/js/src/dom/manipulator.js b/js/src/dom/manipulator.js index 1be3a793f..a8ceb5b0e 100644 --- a/js/src/dom/manipulator.js +++ b/js/src/dom/manipulator.js @@ -5,6 +5,8 @@ * -------------------------------------------------------------------------- */ +import { getWindow } from '../util/index' + function normalizeData(val) { if (val === 'true') { return true @@ -62,10 +64,11 @@ const Manipulator = { offset(element) { const rect = element.getBoundingClientRect() + const windowRef = getWindow() return { - top: rect.top + window.pageYOffset, - left: rect.left + window.pageXOffset + top: rect.top + windowRef.pageYOffset, + left: rect.left + windowRef.pageXOffset } }, diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js index 19e45c205..0f8e62349 100644 --- a/js/src/dom/selector-engine.js +++ b/js/src/dom/selector-engine.js @@ -11,16 +11,16 @@ * ------------------------------------------------------------------------ */ -import { isDisabled, isVisible } from '../util/index' +import { isDisabled, isVisible, getDocument } from '../util/index' const NODE_TEXT = 3 const SelectorEngine = { - find(selector, element = document.documentElement) { + find(selector, element = getDocument().documentElement) { return [].concat(...Element.prototype.querySelectorAll.call(element, selector)) }, - findOne(selector, element = document.documentElement) { + findOne(selector, element = getDocument().documentElement) { return Element.prototype.querySelector.call(element, selector) }, diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 874cf907b..32d65f335 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -17,7 +17,8 @@ import { isRTL, isVisible, noop, - typeCheckConfig + typeCheckConfig, + getDocument } from './util/index' import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' @@ -151,9 +152,9 @@ class Dropdown extends BaseComponent { // empty mouseover listeners to the body's immediate children; // 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 && + if ('ontouchstart' in this._document.documentElement && !parent.closest(SELECTOR_NAVBAR_NAV)) { - [].concat(...document.body.children) + [].concat(...this._document.body.children) .forEach(elem => EventHandler.on(elem, 'mouseover', noop)) } @@ -478,11 +479,13 @@ class Dropdown extends BaseComponent { * ------------------------------------------------------------------------ */ -EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler) -EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler) -EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus) -EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus) -EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { +const documentRef = getDocument() + +EventHandler.on(documentRef, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler) +EventHandler.on(documentRef, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler) +EventHandler.on(documentRef, EVENT_CLICK_DATA_API, Dropdown.clearMenus) +EventHandler.on(documentRef, EVENT_KEYUP_DATA_API, Dropdown.clearMenus) +EventHandler.on(documentRef, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { event.preventDefault() Dropdown.getOrCreateInstance(this).toggle() }) diff --git a/js/src/modal.js b/js/src/modal.js index b4700f02a..3cd78cc98 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -11,7 +11,8 @@ import { isRTL, isVisible, reflow, - typeCheckConfig + typeCheckConfig, + getDocument } from './util/index' import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' @@ -218,7 +219,7 @@ class Modal extends BaseComponent { if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { // Don't move modal's DOM position - document.body.append(this._element) + this._document.body.append(this._element) } this._element.style.display = 'block' @@ -281,7 +282,7 @@ class Modal extends BaseComponent { this._element.removeAttribute('role') this._isTransitioning = false this._backdrop.hide(() => { - document.body.classList.remove(CLASS_NAME_OPEN) + this._document.body.classList.remove(CLASS_NAME_OPEN) this._resetAdjustments() this._scrollBar.reset() EventHandler.trigger(this._element, EVENT_HIDDEN) @@ -320,7 +321,7 @@ class Modal extends BaseComponent { } const { classList, scrollHeight, style } = this._element - const isModalOverflowing = scrollHeight > document.documentElement.clientHeight + const isModalOverflowing = scrollHeight > this._document.documentElement.clientHeight // return if the following background transition hasn't yet completed if ((!isModalOverflowing && style.overflowY === 'hidden') || classList.contains(CLASS_NAME_STATIC)) { @@ -349,7 +350,7 @@ class Modal extends BaseComponent { // ---------------------------------------------------------------------- _adjustDialog() { - const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight + const isModalOverflowing = this._element.scrollHeight > this._document.documentElement.clientHeight const scrollbarWidth = this._scrollBar.getWidth() const isBodyOverflowing = scrollbarWidth > 0 @@ -392,7 +393,7 @@ class Modal extends BaseComponent { * ------------------------------------------------------------------------ */ -EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { +EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { const target = getElementFromSelector(this) if (['A', 'AREA'].includes(this.tagName)) { @@ -412,7 +413,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( }) }) - // avoid conflict when clicking moddal toggler while another one is open + // avoid conflict when clicking modal toggler while another one is open const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR) if (allReadyOpen) { Modal.getInstance(allReadyOpen).hide() diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js index ba809cdf2..dcb1b8bfc 100644 --- a/js/src/offcanvas.js +++ b/js/src/offcanvas.js @@ -10,7 +10,9 @@ import { getElementFromSelector, isDisabled, isVisible, - typeCheckConfig + typeCheckConfig, + getDocument, + getWindow } from './util/index' import ScrollBarHelper from './util/scrollbar' import EventHandler from './dom/event-handler' @@ -228,7 +230,7 @@ class Offcanvas extends BaseComponent { * ------------------------------------------------------------------------ */ -EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { +EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { const target = getElementFromSelector(this) if (['A', 'AREA'].includes(this.tagName)) { @@ -256,7 +258,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( data.toggle(this) }) -EventHandler.on(window, EVENT_LOAD_DATA_API, () => +EventHandler.on(getWindow(), EVENT_LOAD_DATA_API, () => SelectorEngine.find(OPEN_SELECTOR).forEach(el => Offcanvas.getOrCreateInstance(el).show()) ) diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js index 6ac00fedd..0bcbaa88e 100644 --- a/js/src/scrollspy.js +++ b/js/src/scrollspy.js @@ -9,7 +9,8 @@ import { defineJQueryPlugin, getElement, getSelectorFromElement, - typeCheckConfig + typeCheckConfig, + getWindow } from './util/index' import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' @@ -67,7 +68,7 @@ const METHOD_POSITION = 'position' class ScrollSpy extends BaseComponent { constructor(element, config) { super(element) - this._scrollElement = this._element.tagName === 'BODY' ? window : this._element + this._scrollElement = this._element.tagName === 'BODY' ? this._window : this._element this._config = this._getConfig(config) this._offsets = [] this._targets = [] @@ -164,14 +165,14 @@ class ScrollSpy extends BaseComponent { _getScrollHeight() { return this._scrollElement.scrollHeight || Math.max( - document.body.scrollHeight, - document.documentElement.scrollHeight + this._document.body.scrollHeight, + this._document.documentElement.scrollHeight ) } _getOffsetHeight() { return this._scrollElement === window ? - window.innerHeight : + this._window.innerHeight : this._scrollElement.getBoundingClientRect().height } @@ -278,7 +279,7 @@ class ScrollSpy extends BaseComponent { * ------------------------------------------------------------------------ */ -EventHandler.on(window, EVENT_LOAD_DATA_API, () => { +EventHandler.on(getWindow(), EVENT_LOAD_DATA_API, () => { SelectorEngine.find(SELECTOR_DATA_SPY) .forEach(spy => new ScrollSpy(spy)) }) diff --git a/js/src/tab.js b/js/src/tab.js index 161bccbca..c047dbe7d 100644 --- a/js/src/tab.js +++ b/js/src/tab.js @@ -9,7 +9,8 @@ import { defineJQueryPlugin, getElementFromSelector, isDisabled, - reflow + reflow, + getDocument } from './util/index' import EventHandler from './dom/event-handler' import SelectorEngine from './dom/selector-engine' @@ -199,7 +200,7 @@ class Tab extends BaseComponent { * ------------------------------------------------------------------------ */ -EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { +EventHandler.on(getDocument(), EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault() } diff --git a/js/src/tooltip.js b/js/src/tooltip.js index 747555411..f04031a5c 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -284,8 +284,8 @@ class Tooltip extends BaseComponent { // empty mouseover listeners to the body's immediate children; // 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) { - [].concat(...document.body.children).forEach(element => { + if ('ontouchstart' in this._document.documentElement) { + [].concat(...this._document.body.children).forEach(element => { EventHandler.on(element, 'mouseover', noop) }) } @@ -336,8 +336,8 @@ 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) { - [].concat(...document.body.children) + if ('ontouchstart' in this._document.documentElement) { + [].concat(...this._document.body.children) .forEach(element => EventHandler.off(element, 'mouseover', noop)) } @@ -367,7 +367,7 @@ class Tooltip extends BaseComponent { return this.tip } - const element = document.createElement('div') + const element = this._document.createElement('div') element.innerHTML = this._config.template const tip = element.children[0] diff --git a/js/src/util/component-functions.js b/js/src/util/component-functions.js index ff9d87ee6..6c31fad5c 100644 --- a/js/src/util/component-functions.js +++ b/js/src/util/component-functions.js @@ -6,13 +6,13 @@ */ import EventHandler from '../dom/event-handler' -import { getElementFromSelector, isDisabled } from './index' +import { getElementFromSelector, isDisabled, getDocument } from './index' const enableDismissTrigger = (component, method = 'hide') => { const clickEvent = `click.dismiss${component.EVENT_KEY}` const name = component.NAME - EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) { + EventHandler.on(getDocument(), clickEvent, `[data-bs-dismiss="${name}"]`, function (event) { if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault() } diff --git a/js/src/util/index.js b/js/src/util/index.js index a4ad9c941..6f5838992 100644 --- a/js/src/util/index.js +++ b/js/src/util/index.js @@ -27,7 +27,7 @@ const toType = obj => { const getUID = prefix => { do { prefix += Math.floor(Math.random() * MAX_UID) - } while (document.getElementById(prefix)) + } while (getDocument().getElementById(prefix)) return prefix } @@ -61,7 +61,7 @@ const getSelectorFromElement = element => { const selector = getSelector(element) if (selector) { - return document.querySelector(selector) ? selector : null + return getDocument().querySelector(selector) ? selector : null } return null @@ -70,7 +70,7 @@ const getSelectorFromElement = element => { const getElementFromSelector = element => { const selector = getSelector(element) - return selector ? document.querySelector(selector) : null + return selector ? getDocument().querySelector(selector) : null } const getTransitionDurationFromElement = element => { @@ -79,7 +79,7 @@ const getTransitionDurationFromElement = element => { } // Get transition-duration of the element - let { transitionDuration, transitionDelay } = window.getComputedStyle(element) + let { transitionDuration, transitionDelay } = getWindow().getComputedStyle(element) const floatTransitionDuration = Number.parseFloat(transitionDuration) const floatTransitionDelay = Number.parseFloat(transitionDelay) @@ -163,7 +163,7 @@ const isDisabled = element => { } const findShadowRoot = element => { - if (!document.documentElement.attachShadow) { + if (!getDocument().documentElement.attachShadow) { return null } @@ -203,7 +203,7 @@ const reflow = element => { const getjQuery = () => { const { jQuery } = window - if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { + if (jQuery && !getDocument().body.hasAttribute('data-bs-no-jquery')) { return jQuery } @@ -213,10 +213,10 @@ const getjQuery = () => { const DOMContentLoadedCallbacks = [] const onDOMContentLoaded = callback => { - if (document.readyState === 'loading') { + if (getDocument().readyState === 'loading') { // add listener on the first call when the document is in loading state if (!DOMContentLoadedCallbacks.length) { - document.addEventListener('DOMContentLoaded', () => { + getDocument().addEventListener('DOMContentLoaded', () => { DOMContentLoadedCallbacks.forEach(callback => callback()) }) } @@ -227,7 +227,7 @@ const onDOMContentLoaded = callback => { } } -const isRTL = () => document.documentElement.dir === 'rtl' +const isRTL = () => getDocument().documentElement.dir === 'rtl' const defineJQueryPlugin = plugin => { onDOMContentLoaded(() => { @@ -309,6 +309,22 @@ const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed return list[Math.max(0, Math.min(index, listLength - 1))] } +const getWindow = () => { + if (typeof window !== 'undefined') { + return window + } + + return {} +} + +const getDocument = () => { + if (typeof document !== 'undefined') { + return document + } + + return {} +} + export { getElement, getUID, @@ -329,5 +345,7 @@ export { isRTL, defineJQueryPlugin, execute, - executeAfterTransition + executeAfterTransition, + getWindow, + getDocument } diff --git a/js/src/util/sanitizer.js b/js/src/util/sanitizer.js index 11b28a9d9..9df43142a 100644 --- a/js/src/util/sanitizer.js +++ b/js/src/util/sanitizer.js @@ -5,6 +5,8 @@ * -------------------------------------------------------------------------- */ +import { getWindow } from './index' + const uriAttributes = new Set([ 'background', 'cite', @@ -98,7 +100,8 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) { return sanitizeFn(unsafeHtml) } - const domParser = new window.DOMParser() + const windowRef = getWindow() + const domParser = new windowRef.DOMParser() const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html') const elements = [].concat(...createdDocument.body.querySelectorAll('*')) diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js index 73c28254e..7a44c463e 100644 --- a/js/src/util/scrollbar.js +++ b/js/src/util/scrollbar.js @@ -7,14 +7,14 @@ import SelectorEngine from '../dom/selector-engine' import Manipulator from '../dom/manipulator' -import { isElement } from './index' +import { isElement, getDocument } from './index' const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' const SELECTOR_STICKY_CONTENT = '.sticky-top' class ScrollBarHelper { constructor() { - this._element = document.body + this._element = getDocument().body } getWidth() { diff --git a/js/tests/integration/bundle-modularity.js b/js/tests/integration/bundle-modularity.js index 8546141b1..ecfd2335f 100644 --- a/js/tests/integration/bundle-modularity.js +++ b/js/tests/integration/bundle-modularity.js @@ -1,6 +1,7 @@ import Tooltip from '../../dist/tooltip' import '../../dist/carousel' +// eslint-disable-next-line ssr-friendly/no-dom-globals-in-module-scope window.addEventListener('load', () => { [].concat(...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 452088a7d..8c5442626 100644 --- a/js/tests/integration/bundle.js +++ b/js/tests/integration/bundle.js @@ -1,5 +1,6 @@ import { Tooltip } from '../../../dist/js/bootstrap.esm.js' +// eslint-disable-next-line ssr-friendly/no-dom-globals-in-module-scope window.addEventListener('load', () => { [].concat(...document.querySelectorAll('[data-bs-toggle="tooltip"]')) .map(tooltipNode => new Tooltip(tooltipNode)) diff --git a/package-lock.json b/package-lock.json index a4a7bf37e..6696c4bde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3988,6 +3988,32 @@ } } }, + "eslint-plugin-ssr-friendly": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-ssr-friendly/-/eslint-plugin-ssr-friendly-1.0.5.tgz", + "integrity": "sha512-F1vKfzhOnrIXhcx91Y3r1x8vjJAoCex25PUgYErOe6q95T4KuCTz6+LgGQ4TTvhBdCfNqu1U0krAHe3UNuEOqg==", + "dev": true, + "requires": { + "globals": "^13.2.0" + }, + "dependencies": { + "globals": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, "eslint-plugin-unicorn": { "version": "36.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-36.0.0.tgz", diff --git a/package.json b/package.json index b412f65c2..296931849 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "eslint": "^7.32.0", "eslint-config-xo": "^0.38.0", "eslint-plugin-import": "^2.24.2", + "eslint-plugin-ssr-friendly": "^1.0.5", "eslint-plugin-unicorn": "^36.0.0", "find-unused-sass-variables": "^3.1.0", "glob": "^7.1.7", |
