diff options
-rw-r--r-- | js/src/base-component.js | 4 | ||||
-rw-r--r-- | js/src/collapse.js | 6 | ||||
-rw-r--r-- | js/src/modal.js | 2 | ||||
-rw-r--r-- | js/src/scrollspy.js | 2 | ||||
-rw-r--r-- | js/tests/unit/alert.spec.js | 11 | ||||
-rw-r--r-- | js/tests/unit/button.spec.js | 10 | ||||
-rw-r--r-- | js/tests/unit/carousel.spec.js | 11 | ||||
-rw-r--r-- | js/tests/unit/collapse.spec.js | 11 | ||||
-rw-r--r-- | js/tests/unit/dropdown.spec.js | 18 | ||||
-rw-r--r-- | js/tests/unit/modal.spec.js | 13 | ||||
-rw-r--r-- | js/tests/unit/scrollspy.spec.js | 11 | ||||
-rw-r--r-- | js/tests/unit/tab.spec.js | 16 | ||||
-rw-r--r-- | js/tests/unit/toast.spec.js | 11 | ||||
-rw-r--r-- | js/tests/unit/tooltip.spec.js | 11 | ||||
-rw-r--r-- | site/content/docs/5.0/getting-started/javascript.md | 9 | ||||
-rw-r--r-- | site/content/docs/5.0/migration.md | 11 |
16 files changed, 151 insertions, 6 deletions
diff --git a/js/src/base-component.js b/js/src/base-component.js index 9de274bd09..989a641561 100644 --- a/js/src/base-component.js +++ b/js/src/base-component.js @@ -17,12 +17,14 @@ const VERSION = '5.0.0-beta2' class BaseComponent { constructor(element) { + element = typeof element === 'string' ? document.querySelector(element) : element + if (!element) { return } this._element = element - Data.setData(element, this.constructor.DATA_KEY, this) + Data.setData(this._element, this.constructor.DATA_KEY, this) } dispose() { diff --git a/js/src/collapse.js b/js/src/collapse.js index 0a1b475470..f861667659 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -72,8 +72,8 @@ class Collapse extends BaseComponent { this._isTransitioning = false this._config = this._getConfig(config) this._triggerArray = SelectorEngine.find( - `${SELECTOR_DATA_TOGGLE}[href="#${element.id}"],` + - `${SELECTOR_DATA_TOGGLE}[data-bs-target="#${element.id}"]` + `${SELECTOR_DATA_TOGGLE}[href="#${this._element.id}"],` + + `${SELECTOR_DATA_TOGGLE}[data-bs-target="#${this._element.id}"]` ) const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE) @@ -82,7 +82,7 @@ class Collapse extends BaseComponent { const elem = toggleList[i] const selector = getSelectorFromElement(elem) const filterElement = SelectorEngine.find(selector) - .filter(foundElem => foundElem === element) + .filter(foundElem => foundElem === this._element) if (selector !== null && filterElement.length) { this._selector = selector diff --git a/js/src/modal.js b/js/src/modal.js index 79a2f143a3..4f42e733eb 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -83,7 +83,7 @@ class Modal extends BaseComponent { super(element) this._config = this._getConfig(config) - this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, element) + this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element) this._backdrop = null this._isShown = false this._isBodyOverflowing = false diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js index 43a91e5e93..0c51eab0fe 100644 --- a/js/src/scrollspy.js +++ b/js/src/scrollspy.js @@ -68,7 +68,7 @@ const METHOD_POSITION = 'position' class ScrollSpy extends BaseComponent { constructor(element, config) { super(element) - this._scrollElement = element.tagName === 'BODY' ? window : element + this._scrollElement = this._element.tagName === 'BODY' ? window : this._element this._config = this._getConfig(config) this._selector = `${this._config.target} ${SELECTOR_NAV_LINKS}, ${this._config.target} ${SELECTOR_LIST_ITEMS}, ${this._config.target} .${CLASS_NAME_DROPDOWN_ITEM}` this._offsets = [] diff --git a/js/tests/unit/alert.spec.js b/js/tests/unit/alert.spec.js index 916c7fd07c..194da420e9 100644 --- a/js/tests/unit/alert.spec.js +++ b/js/tests/unit/alert.spec.js @@ -15,6 +15,17 @@ describe('Alert', () => { clearFixture() }) + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = '<div class="alert"></div>' + + const alertEl = fixtureEl.querySelector('.alert') + const alertBySelector = new Alert('.alert') + const alertByElement = new Alert(alertEl) + + expect(alertBySelector._element).toEqual(alertEl) + expect(alertByElement._element).toEqual(alertEl) + }) + it('should return version', () => { expect(typeof Alert.VERSION).toEqual('string') }) diff --git a/js/tests/unit/button.spec.js b/js/tests/unit/button.spec.js index e442fd90d0..e7d92cb6d2 100644 --- a/js/tests/unit/button.spec.js +++ b/js/tests/unit/button.spec.js @@ -18,6 +18,16 @@ describe('Button', () => { clearFixture() }) + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = '<button data-bs-toggle="button">Placeholder</button>' + const buttonEl = fixtureEl.querySelector('[data-bs-toggle="button"]') + const buttonBySelector = new Button('[data-bs-toggle="button"]') + const buttonByElement = new Button(buttonEl) + + expect(buttonBySelector._element).toEqual(buttonEl) + expect(buttonByElement._element).toEqual(buttonEl) + }) + describe('VERSION', () => { it('should return plugin version', () => { expect(Button.VERSION).toEqual(jasmine.any(String)) diff --git a/js/tests/unit/carousel.spec.js b/js/tests/unit/carousel.spec.js index 533e1ba7e2..c475489c06 100644 --- a/js/tests/unit/carousel.spec.js +++ b/js/tests/unit/carousel.spec.js @@ -52,6 +52,17 @@ describe('Carousel', () => { }) describe('constructor', () => { + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = '<div id="myCarousel" class="carousel slide"></div>' + + const carouselEl = fixtureEl.querySelector('#myCarousel') + const carouselBySelector = new Carousel('#myCarousel') + const carouselByElement = new Carousel(carouselEl) + + expect(carouselBySelector._element).toEqual(carouselEl) + expect(carouselByElement._element).toEqual(carouselEl) + }) + it('should go to next item if right arrow key is pressed', done => { fixtureEl.innerHTML = [ '<div id="myCarousel" class="carousel slide">', diff --git a/js/tests/unit/collapse.spec.js b/js/tests/unit/collapse.spec.js index cd30ed8daa..bc7c157714 100644 --- a/js/tests/unit/collapse.spec.js +++ b/js/tests/unit/collapse.spec.js @@ -34,6 +34,17 @@ describe('Collapse', () => { }) describe('constructor', () => { + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = '<div class="my-collapse"></div>' + + const collapseEl = fixtureEl.querySelector('div.my-collapse') + const collapseBySelector = new Collapse('div.my-collapse') + const collapseByElement = new Collapse(collapseEl) + + expect(collapseBySelector._element).toEqual(collapseEl) + expect(collapseByElement._element).toEqual(collapseEl) + }) + it('should allow jquery object in parent config', () => { fixtureEl.innerHTML = [ '<div class="my-collapse">', diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js index 658cb65b04..b7e771c950 100644 --- a/js/tests/unit/dropdown.spec.js +++ b/js/tests/unit/dropdown.spec.js @@ -40,6 +40,24 @@ describe('Dropdown', () => { }) describe('constructor', () => { + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = [ + '<div class="dropdown">', + ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>', + ' <div class="dropdown-menu">', + ' <a class="dropdown-item" href="#">Link</a>', + ' </div>', + '</div>' + ].join('') + + const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') + const dropdownBySelector = new Dropdown('[data-bs-toggle="dropdown"]') + const dropdownByElement = new Dropdown(btnDropdown) + + expect(dropdownBySelector._element).toEqual(btnDropdown) + expect(dropdownByElement._element).toEqual(btnDropdown) + }) + it('should add a listener on trigger which do not have data-bs-toggle="dropdown"', () => { fixtureEl.innerHTML = [ '<div class="dropdown">', diff --git a/js/tests/unit/modal.spec.js b/js/tests/unit/modal.spec.js index 8a159eef6c..7f16bfc1d0 100644 --- a/js/tests/unit/modal.spec.js +++ b/js/tests/unit/modal.spec.js @@ -62,6 +62,19 @@ describe('Modal', () => { }) }) + describe('constructor', () => { + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>' + + const modalEl = fixtureEl.querySelector('.modal') + const modalBySelector = new Modal('.modal') + const modalByElement = new Modal(modalEl) + + expect(modalBySelector._element).toEqual(modalEl) + expect(modalByElement._element).toEqual(modalEl) + }) + }) + describe('toggle', () => { it('should toggle a modal', done => { fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>' diff --git a/js/tests/unit/scrollspy.spec.js b/js/tests/unit/scrollspy.spec.js index a00da485f8..f7258ba355 100644 --- a/js/tests/unit/scrollspy.spec.js +++ b/js/tests/unit/scrollspy.spec.js @@ -54,6 +54,17 @@ describe('ScrollSpy', () => { }) describe('constructor', () => { + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = '<nav id="navigation"></nav><div class="content"></div>' + + const sSpyEl = fixtureEl.querySelector('#navigation') + const sSpyBySelector = new ScrollSpy('#navigation') + const sSpyByElement = new ScrollSpy(sSpyEl) + + expect(sSpyBySelector._element).toEqual(sSpyEl) + expect(sSpyByElement._element).toEqual(sSpyEl) + }) + it('should generate an id when there is not one', () => { fixtureEl.innerHTML = [ '<nav></nav>', diff --git a/js/tests/unit/tab.spec.js b/js/tests/unit/tab.spec.js index c52812f52e..35d17e16b9 100644 --- a/js/tests/unit/tab.spec.js +++ b/js/tests/unit/tab.spec.js @@ -20,6 +20,22 @@ 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>' + ].join('') + + const tabEl = fixtureEl.querySelector('[href="#home"]') + const tabBySelector = new Tab('[href="#home"]') + const tabByElement = new Tab(tabEl) + + expect(tabBySelector._element).toEqual(tabEl) + expect(tabByElement._element).toEqual(tabEl) + }) + }) + describe('show', () => { it('should activate element by tab id (using buttons, the preferred semantic way)', done => { fixtureEl.innerHTML = [ diff --git a/js/tests/unit/toast.spec.js b/js/tests/unit/toast.spec.js index f8ef6e54b4..d298dc9931 100644 --- a/js/tests/unit/toast.spec.js +++ b/js/tests/unit/toast.spec.js @@ -27,6 +27,17 @@ describe('Toast', () => { }) describe('constructor', () => { + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = '<div class="toast"></div>' + + const toastEl = fixtureEl.querySelector('.toast') + const toastBySelector = new Toast('.toast') + const toastByElement = new Toast(toastEl) + + expect(toastBySelector._element).toEqual(toastEl) + expect(toastByElement._element).toEqual(toastEl) + }) + it('should allow to config in js', done => { fixtureEl.innerHTML = [ '<div class="toast">', diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js index 84f5abcdad..7bf6aa3ab8 100644 --- a/js/tests/unit/tooltip.spec.js +++ b/js/tests/unit/tooltip.spec.js @@ -63,6 +63,17 @@ describe('Tooltip', () => { }) describe('constructor', () => { + it('should take care of element either passed as a CSS selector or DOM element', () => { + fixtureEl.innerHTML = '<a href="#" id="tooltipEl" rel="tooltip" title="Nice and short title">' + + const tooltipEl = fixtureEl.querySelector('#tooltipEl') + const tooltipBySelector = new Tooltip('#tooltipEl') + const tooltipByElement = new Tooltip(tooltipEl) + + expect(tooltipBySelector._element).toEqual(tooltipEl) + expect(tooltipByElement._element).toEqual(tooltipEl) + }) + it('should not take care of disallowed data attributes', () => { fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-sanitize="false" title="Another tooltip">' diff --git a/site/content/docs/5.0/getting-started/javascript.md b/site/content/docs/5.0/getting-started/javascript.md index 53845fdfc2..f57a3aedf6 100644 --- a/site/content/docs/5.0/getting-started/javascript.md +++ b/site/content/docs/5.0/getting-started/javascript.md @@ -93,6 +93,15 @@ var modal = new bootstrap.Modal(myModalEl, { keyboard: false }) // initialized w If you'd like to get a particular plugin instance, each plugin exposes a `getInstance` method. In order to retrieve it directly from an element, do this: `bootstrap.Popover.getInstance(myPopoverEl)`. +### CSS selectors in constructors + +You can also use a CSS selector as the first argument instead of a DOM element to initialize the plugin. Currently the element for the plugin is found by the `querySelector` method since our plugins support a single element only. + +```js +var modal = new bootstrap.Modal('#myModal') +var dropdown = new bootstrap.Dropdown('[data-bs-toggle="dropdown"]') +``` + ### Asynchronous functions and transitions All programmatic API methods are **asynchronous** and return to the caller once the transition is started but **before it ends**. diff --git a/site/content/docs/5.0/migration.md b/site/content/docs/5.0/migration.md index 94f2214d70..903b282cf6 100644 --- a/site/content/docs/5.0/migration.md +++ b/site/content/docs/5.0/migration.md @@ -9,6 +9,17 @@ toc: true ## v5.0.0-beta3 +### JavaScript + +- All plugins can now accept a CSS selector as the first argument. You can either pass a DOM element or any valid CSS selector to create a new instance of the plugin: + + ```js + var modal = new bootstrap.Modal('#myModal') + var dropdown = new bootstrap.Dropdown('[data-bs-toggle="dropdown"]') + ``` + +## v5.0.0-beta2 + ### Utilities - Dropped the `0` entry in `$border-widths` map to remove the duplicated `.border-0` class. |