diff options
| author | Rohit Sharma <[email protected]> | 2021-02-22 12:31:04 +0530 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-02-22 09:01:04 +0200 |
| commit | dc5e3328c12058de7fb6404edbe5dcee61f3400f (patch) | |
| tree | ef141013ee4df32fbd3e581d9647e71f713bb043 | |
| parent | d983744d12be0937bb1aaef06057385d9b553e0c (diff) | |
| download | bootstrap-dc5e3328c12058de7fb6404edbe5dcee61f3400f.tar.xz bootstrap-dc5e3328c12058de7fb6404edbe5dcee61f3400f.zip | |
Allow constructors to accept a CSS selector (#32245)
Co-authored-by: XhmikosR <[email protected]>
Co-authored-by: Mark Otto <[email protected]>
| -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 9de274bd0..989a64156 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 0a1b47547..f86166765 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 79a2f143a..4f42e733e 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 43a91e5e9..0c51eab0f 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 916c7fd07..194da420e 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 e442fd90d..e7d92cb6d 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 533e1ba7e..c475489c0 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 cd30ed8da..bc7c15771 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 658cb65b0..b7e771c95 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 8a159eef6..7f16bfc1d 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 a00da485f..f7258ba35 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 c52812f52..35d17e16b 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 f8ef6e54b..d298dc993 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 84f5abcda..7bf6aa3ab 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 53845fdfc..f57a3aedf 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 94f2214d7..903b282cf 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. |
