Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getOrCreateInstance method in base-component #33276

Merged
merged 13 commits into from
Jun 3, 2021
7 changes: 1 addition & 6 deletions js/src/alert.js
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@ import {
defineJQueryPlugin,
getElementFromSelector
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import BaseComponent from './base-component'

@@ -87,11 +86,7 @@ class Alert extends BaseComponent {

static jQueryInterface(config) {
return this.each(function () {
let data = Data.get(this, DATA_KEY)

if (!data) {
data = new Alert(this)
}
const data = Alert.getOrCreateInstance(this)

if (config === 'close') {
data[config](this)
4 changes: 4 additions & 0 deletions js/src/base-component.js
Original file line number Diff line number Diff line change
@@ -61,6 +61,10 @@ class BaseComponent {
return Data.get(element, this.DATA_KEY)
}

static getOrCreateInstance(element, config = {}) {
return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null)
}

static get VERSION() {
return VERSION
}
13 changes: 2 additions & 11 deletions js/src/button.js
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@
*/

import { defineJQueryPlugin } from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import BaseComponent from './base-component'

@@ -51,11 +50,7 @@ class Button extends BaseComponent {

static jQueryInterface(config) {
return this.each(function () {
let data = Data.get(this, DATA_KEY)

if (!data) {
data = new Button(this)
}
const data = Button.getOrCreateInstance(this)

if (config === 'toggle') {
data[config]()
@@ -74,11 +69,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
event.preventDefault()

const button = event.target.closest(SELECTOR_DATA_TOGGLE)

let data = Data.get(button, DATA_KEY)
if (!data) {
data = new Button(button)
}
const data = Button.getOrCreateInstance(button)

data.toggle()
})
26 changes: 6 additions & 20 deletions js/src/carousel.js
Original file line number Diff line number Diff line change
@@ -15,7 +15,6 @@ import {
triggerTransitionEnd,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selector-engine'
@@ -219,7 +218,8 @@ class Carousel extends BaseComponent {
_getConfig(config) {
config = {
...Default,
...config
...Manipulator.getDataAttributes(this._element),
...(typeof config === 'object' ? config : {})
rohit2sharma95 marked this conversation as resolved.
Show resolved Hide resolved
}
typeCheckConfig(NAME, config, DefaultType)
return config
@@ -496,25 +496,11 @@ class Carousel extends BaseComponent {
// Static

static carouselInterface(element, config) {
let data = Data.get(element, DATA_KEY)
let _config = {
...Default,
...Manipulator.getDataAttributes(element)
}

if (typeof config === 'object') {
_config = {
..._config,
...config
}
}
const data = Carousel.getOrCreateInstance(element, config)

const { _config } = data
const action = typeof config === 'string' ? config : _config.slide

if (!data) {
data = new Carousel(element, _config)
}

if (typeof config === 'number') {
data.to(config)
} else if (typeof action === 'string') {
@@ -555,7 +541,7 @@ class Carousel extends BaseComponent {
Carousel.carouselInterface(target, config)

if (slideIndex) {
Data.get(target, DATA_KEY).to(slideIndex)
Carousel.getInstance(target).to(slideIndex)
}

event.preventDefault()
@@ -574,7 +560,7 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)

for (let i = 0, len = carousels.length; i < len; i++) {
Carousel.carouselInterface(carousels[i], Data.get(carousels[i], DATA_KEY))
Carousel.carouselInterface(carousels[i], Carousel.getInstance(carousels[i]))
}
})

6 changes: 3 additions & 3 deletions js/src/collapse.js
Original file line number Diff line number Diff line change
@@ -145,7 +145,7 @@ class Collapse extends BaseComponent {
const container = SelectorEngine.findOne(this._selector)
if (actives) {
const tempActiveData = actives.find(elem => container !== elem)
activesData = tempActiveData ? Data.get(tempActiveData, DATA_KEY) : null
activesData = tempActiveData ? Collapse.getInstance(tempActiveData) : null

if (activesData && activesData._isTransitioning) {
return
@@ -310,7 +310,7 @@ class Collapse extends BaseComponent {
// Static

static collapseInterface(element, config) {
let data = Data.get(element, DATA_KEY)
let data = Collapse.getInstance(element)
const _config = {
...Default,
...Manipulator.getDataAttributes(element),
@@ -358,7 +358,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
const selectorElements = SelectorEngine.find(selector)

selectorElements.forEach(element => {
const data = Data.get(element, DATA_KEY)
const data = Collapse.getInstance(element)
let config
if (data) {
// update parent attribute
10 changes: 2 additions & 8 deletions js/src/dropdown.js
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@ import {
getNextActiveElement,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selector-engine'
@@ -369,12 +368,7 @@ class Dropdown extends BaseComponent {
// Static

static dropdownInterface(element, config) {
let data = Data.get(element, DATA_KEY)
const _config = typeof config === 'object' ? config : null

if (!data) {
data = new Dropdown(element, _config)
}
const data = Dropdown.getOrCreateInstance(element, config)

if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
@@ -399,7 +393,7 @@ class Dropdown extends BaseComponent {
const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)

for (let i = 0, len = toggles.length; i < len; i++) {
const context = Data.get(toggles[i], DATA_KEY)
const context = Dropdown.getInstance(toggles[i])
if (!context || context._config.autoClose === false) {
continue
}
6 changes: 3 additions & 3 deletions js/src/modal.js
Original file line number Diff line number Diff line change
@@ -211,7 +211,7 @@ class Modal extends BaseComponent {
config = {
...Default,
...Manipulator.getDataAttributes(this._element),
...config
...(typeof config === 'object' ? config : {})
}
typeCheckConfig(NAME, config, DefaultType)
return config
@@ -388,7 +388,7 @@ class Modal extends BaseComponent {

static jQueryInterface(config, relatedTarget) {
return this.each(function () {
const data = Modal.getInstance(this) || new Modal(this, typeof config === 'object' ? config : {})
const data = Modal.getOrCreateInstance(this, config)

if (typeof config !== 'string') {
return
@@ -429,7 +429,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
})
})

const data = Modal.getInstance(target) || new Modal(target)
const data = Modal.getOrCreateInstance(target)

data.toggle(this)
})
12 changes: 5 additions & 7 deletions js/src/offcanvas.js
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@ import {
typeCheckConfig
} from './util/index'
import { hide as scrollBarHide, reset as scrollBarReset } from './util/scrollbar'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import BaseComponent from './base-component'
import SelectorEngine from './dom/selector-engine'
@@ -211,7 +210,7 @@ class Offcanvas extends BaseComponent {

static jQueryInterface(config) {
return this.each(function () {
const data = Data.get(this, DATA_KEY) || new Offcanvas(this, typeof config === 'object' ? config : {})
const data = Offcanvas.getOrCreateInstance(this, config)

if (typeof config !== 'string') {
return
@@ -256,14 +255,13 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
Offcanvas.getInstance(allReadyOpen).hide()
}

const data = Data.get(target, DATA_KEY) || new Offcanvas(target)

const data = Offcanvas.getOrCreateInstance(target)
data.toggle(this)
})

EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
SelectorEngine.find(OPEN_SELECTOR).forEach(el => (Data.get(el, DATA_KEY) || new Offcanvas(el)).show())
})
EventHandler.on(window, EVENT_LOAD_DATA_API, () =>
SelectorEngine.find(OPEN_SELECTOR).forEach(el => (Offcanvas.getOrCreateInstance(el)).show())
GeoSot marked this conversation as resolved.
Show resolved Hide resolved
)
GeoSot marked this conversation as resolved.
Show resolved Hide resolved

/**
* ------------------------------------------------------------------------
9 changes: 1 addition & 8 deletions js/src/popover.js
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@
*/

import { defineJQueryPlugin } from './util/index'
import Data from './dom/data'
import SelectorEngine from './dom/selector-engine'
import Tooltip from './tooltip'

@@ -146,13 +145,7 @@ class Popover extends Tooltip {

static jQueryInterface(config) {
return this.each(function () {
let data = Data.get(this, DATA_KEY)
const _config = typeof config === 'object' ? config : null

if (!data) {
data = new Popover(this, _config)
Data.set(this, DATA_KEY, data)
GeoSot marked this conversation as resolved.
Show resolved Hide resolved
}
const data = Popover.getOrCreateInstance(this, config)

if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
2 changes: 1 addition & 1 deletion js/src/scrollspy.js
Original file line number Diff line number Diff line change
@@ -270,7 +270,7 @@ class ScrollSpy extends BaseComponent {

static jQueryInterface(config) {
return this.each(function () {
const data = ScrollSpy.getInstance(this) || new ScrollSpy(this, typeof config === 'object' ? config : {})
const data = ScrollSpy.getOrCreateInstance(this, config)

if (typeof config !== 'string') {
return
5 changes: 2 additions & 3 deletions js/src/tab.js
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@ import {
isDisabled,
reflow
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import SelectorEngine from './dom/selector-engine'
import BaseComponent from './base-component'
@@ -181,7 +180,7 @@ class Tab extends BaseComponent {

static jQueryInterface(config) {
return this.each(function () {
const data = Data.get(this, DATA_KEY) || new Tab(this)
const data = Tab.getOrCreateInstance(this)

if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
@@ -209,7 +208,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
return
}

const data = Data.get(this, DATA_KEY) || new Tab(this)
const data = Tab.getOrCreateInstance(this)
data.show()
})

8 changes: 1 addition & 7 deletions js/src/toast.js
Original file line number Diff line number Diff line change
@@ -10,7 +10,6 @@ import {
reflow,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import BaseComponent from './base-component'
@@ -218,12 +217,7 @@ class Toast extends BaseComponent {

static jQueryInterface(config) {
return this.each(function () {
let data = Data.get(this, DATA_KEY)
const _config = typeof config === 'object' && config

if (!data) {
data = new Toast(this, _config)
}
const data = Toast.getOrCreateInstance(this, config)

if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
7 changes: 1 addition & 6 deletions js/src/tooltip.js
Original file line number Diff line number Diff line change
@@ -722,12 +722,7 @@ class Tooltip extends BaseComponent {

static jQueryInterface(config) {
return this.each(function () {
let data = Data.get(this, DATA_KEY)
const _config = typeof config === 'object' && config

if (!data) {
data = new Tooltip(this, _config)
}
const data = Tooltip.getOrCreateInstance(this, config)

if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
22 changes: 22 additions & 0 deletions js/tests/unit/alert.spec.js
Original file line number Diff line number Diff line change
@@ -207,4 +207,26 @@ describe('Alert', () => {
expect(Alert.getInstance(div)).toEqual(null)
})
})

describe('getOrCreateInstance', () => {
it('should return alert instance', () => {
fixtureEl.innerHTML = '<div></div>'

const div = fixtureEl.querySelector('div')
const alert = new Alert(div)

expect(Alert.getOrCreateInstance(div)).toEqual(alert)
expect(Alert.getInstance(div)).toEqual(Alert.getOrCreateInstance(div, {}))
alpadev marked this conversation as resolved.
Show resolved Hide resolved
expect(Alert.getOrCreateInstance(div)).toBeInstanceOf(Alert)
})

it('should return new instance when there is no alert instance', () => {
fixtureEl.innerHTML = '<div></div>'

const div = fixtureEl.querySelector('div')

expect(Alert.getInstance(div)).toEqual(null)
expect(Alert.getOrCreateInstance(div)).toBeInstanceOf(Alert)
})
})
})
17 changes: 17 additions & 0 deletions js/tests/unit/base-component.spec.js
Original file line number Diff line number Diff line change
@@ -112,5 +112,22 @@ describe('Base Component', () => {
expect(DummyClass.getInstance(div)).toEqual(null)
})
})
describe('getOrCreateInstance', () => {
it('should return an instance', () => {
createInstance()

expect(DummyClass.getOrCreateInstance(element)).toEqual(instance)
expect(DummyClass.getInstance(element)).toEqual(DummyClass.getOrCreateInstance(element, {}))
expect(DummyClass.getOrCreateInstance(element)).toBeInstanceOf(DummyClass)
})

it('should return new instance when there is no alert instance', () => {
GeoSot marked this conversation as resolved.
Show resolved Hide resolved
fixtureEl.innerHTML = '<div id="foo"></div>'
element = fixtureEl.querySelector('#foo')

expect(DummyClass.getInstance(element)).toEqual(null)
expect(DummyClass.getOrCreateInstance(element)).toBeInstanceOf(DummyClass)
})
})
})
})
Loading