Skip to content

Commit

Permalink
Add getOrCreateInstance method in base-component (#33276)
Browse files Browse the repository at this point in the history
Co-authored-by: Rohit Sharma <rohit2sharma95@gmail.com>
Co-authored-by: XhmikosR <xhmikosr@gmail.com>
  • Loading branch information
3 people committed Jun 3, 2021
1 parent 4a5029e commit c98657b
Show file tree
Hide file tree
Showing 39 changed files with 744 additions and 124 deletions.
7 changes: 1 addition & 6 deletions js/src/alert.js
Expand Up @@ -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'

Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions js/src/base-component.js
Expand Up @@ -51,6 +51,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
}
Expand Down
13 changes: 2 additions & 11 deletions js/src/button.js
Expand Up @@ -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'

Expand Down Expand Up @@ -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]()
Expand All @@ -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()
})
Expand Down
26 changes: 6 additions & 20 deletions js/src/carousel.js
Expand Up @@ -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'
Expand Down Expand Up @@ -219,7 +218,8 @@ class Carousel extends BaseComponent {
_getConfig(config) {
config = {
...Default,
...config
...Manipulator.getDataAttributes(this._element),
...(typeof config === 'object' ? config : {})
}
typeCheckConfig(NAME, config, DefaultType)
return config
Expand Down Expand Up @@ -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') {
Expand Down Expand Up @@ -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()
Expand All @@ -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]))
}
})

Expand Down
6 changes: 3 additions & 3 deletions js/src/collapse.js
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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
Expand Down
10 changes: 2 additions & 8 deletions js/src/dropdown.js
Expand Up @@ -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'
Expand Down Expand Up @@ -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') {
Expand All @@ -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
}
Expand Down
6 changes: 3 additions & 3 deletions js/src/modal.js
Expand Up @@ -209,7 +209,7 @@ class Modal extends BaseComponent {
config = {
...Default,
...Manipulator.getDataAttributes(this._element),
...config
...(typeof config === 'object' ? config : {})
}
typeCheckConfig(NAME, config, DefaultType)
return config
Expand Down Expand Up @@ -389,7 +389,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
Expand Down Expand Up @@ -430,7 +430,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)
})
Expand Down
12 changes: 5 additions & 7 deletions js/src/offcanvas.js
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())
)

/**
* ------------------------------------------------------------------------
Expand Down
9 changes: 1 addition & 8 deletions js/src/popover.js
Expand Up @@ -6,7 +6,6 @@
*/

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

Expand Down Expand Up @@ -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)
}
const data = Popover.getOrCreateInstance(this, config)

if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
Expand Down
2 changes: 1 addition & 1 deletion js/src/scrollspy.js
Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions js/src/tab.js
Expand Up @@ -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'
Expand Down Expand Up @@ -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') {
Expand Down Expand Up @@ -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()
})

Expand Down
8 changes: 1 addition & 7 deletions js/src/toast.js
Expand Up @@ -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'
Expand Down Expand Up @@ -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') {
Expand Down
7 changes: 1 addition & 6 deletions js/src/tooltip.js
Expand Up @@ -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') {
Expand Down
22 changes: 22 additions & 0 deletions js/tests/unit/alert.spec.js
Expand Up @@ -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, {}))
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
Expand Up @@ -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', () => {
fixtureEl.innerHTML = '<div id="foo"></div>'
element = fixtureEl.querySelector('#foo')

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

0 comments on commit c98657b

Please sign in to comment.