aboutsummaryrefslogtreecommitdiff
path: root/js/src/alert
diff options
context:
space:
mode:
authorJohann-S <[email protected]>2019-03-13 16:23:50 +0200
committerJohann-S <[email protected]>2019-07-23 14:23:50 +0200
commitc8c207465043d940aa031570f0bce5e8fff9ffcf (patch)
tree482f430ee84aa4cae8f76cebdec75791a60a50d1 /js/src/alert
parent08d81c843706c2b952fadb703f3f9be709a8ca6a (diff)
downloadbootstrap-c8c207465043d940aa031570f0bce5e8fff9ffcf.tar.xz
bootstrap-c8c207465043d940aa031570f0bce5e8fff9ffcf.zip
Switch from QUnit to Jasmine.
Diffstat (limited to 'js/src/alert')
-rw-r--r--js/src/alert/alert.js191
-rw-r--r--js/src/alert/alert.spec.js127
2 files changed, 318 insertions, 0 deletions
diff --git a/js/src/alert/alert.js b/js/src/alert/alert.js
new file mode 100644
index 000000000..99164bd19
--- /dev/null
+++ b/js/src/alert/alert.js
@@ -0,0 +1,191 @@
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.3.1): alert.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+import {
+ jQuery as $,
+ TRANSITION_END,
+ emulateTransitionEnd,
+ getSelectorFromElement,
+ getTransitionDurationFromElement
+} from '../util/index'
+import Data from '../dom/data'
+import EventHandler from '../dom/event-handler'
+import SelectorEngine from '../dom/selector-engine'
+
+/**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+const NAME = 'alert'
+const VERSION = '4.3.1'
+const DATA_KEY = 'bs.alert'
+const EVENT_KEY = `.${DATA_KEY}`
+const DATA_API_KEY = '.data-api'
+
+const Selector = {
+ DISMISS: '[data-dismiss="alert"]'
+}
+
+const Event = {
+ CLOSE: `close${EVENT_KEY}`,
+ CLOSED: `closed${EVENT_KEY}`,
+ CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
+}
+
+const ClassName = {
+ ALERT: 'alert',
+ FADE: 'fade',
+ SHOW: 'show'
+}
+
+/**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+class Alert {
+ constructor(element) {
+ this._element = element
+
+ if (this._element) {
+ Data.setData(element, DATA_KEY, this)
+ }
+ }
+
+ // Getters
+
+ static get VERSION() {
+ return VERSION
+ }
+
+ // Public
+
+ close(element) {
+ let rootElement = this._element
+ if (element) {
+ rootElement = this._getRootElement(element)
+ }
+
+ const customEvent = this._triggerCloseEvent(rootElement)
+
+ if (customEvent === null || customEvent.defaultPrevented) {
+ return
+ }
+
+ this._removeElement(rootElement)
+ }
+
+ dispose() {
+ Data.removeData(this._element, DATA_KEY)
+ this._element = null
+ }
+
+ // Private
+
+ _getRootElement(element) {
+ const selector = getSelectorFromElement(element)
+ let parent = false
+
+ if (selector) {
+ parent = SelectorEngine.findOne(selector)
+ }
+
+ if (!parent) {
+ parent = SelectorEngine.closest(element, `.${ClassName.ALERT}`)
+ }
+
+ return parent
+ }
+
+ _triggerCloseEvent(element) {
+ return EventHandler.trigger(element, Event.CLOSE)
+ }
+
+ _removeElement(element) {
+ element.classList.remove(ClassName.SHOW)
+
+ if (!element.classList.contains(ClassName.FADE)) {
+ this._destroyElement(element)
+ return
+ }
+
+ const transitionDuration = getTransitionDurationFromElement(element)
+
+ EventHandler
+ .one(element, TRANSITION_END, () => this._destroyElement(element))
+ emulateTransitionEnd(element, transitionDuration)
+ }
+
+ _destroyElement(element) {
+ if (element.parentNode) {
+ element.parentNode.removeChild(element)
+ }
+
+ EventHandler.trigger(element, Event.CLOSED)
+ }
+
+ // Static
+
+ static _jQueryInterface(config) {
+ return this.each(function () {
+ let data = Data.getData(this, DATA_KEY)
+
+ if (!data) {
+ data = new Alert(this)
+ }
+
+ if (config === 'close') {
+ data[config](this)
+ }
+ })
+ }
+
+ static _handleDismiss(alertInstance) {
+ return function (event) {
+ if (event) {
+ event.preventDefault()
+ }
+
+ alertInstance.close(this)
+ }
+ }
+
+ static _getInstance(element) {
+ return Data.getData(element, DATA_KEY)
+ }
+}
+
+/**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+EventHandler
+ .on(document, Event.CLICK_DATA_API, Selector.DISMISS, Alert._handleDismiss(new Alert()))
+
+/**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ * add .alert to jQuery only if jQuery is present
+ */
+
+/* istanbul ignore if */
+if (typeof $ !== 'undefined') {
+ const JQUERY_NO_CONFLICT = $.fn[NAME]
+ $.fn[NAME] = Alert._jQueryInterface
+ $.fn[NAME].Constructor = Alert
+ $.fn[NAME].noConflict = () => {
+ $.fn[NAME] = JQUERY_NO_CONFLICT
+ return Alert._jQueryInterface
+ }
+}
+
+export default Alert
diff --git a/js/src/alert/alert.spec.js b/js/src/alert/alert.spec.js
new file mode 100644
index 000000000..cb7b57b7f
--- /dev/null
+++ b/js/src/alert/alert.spec.js
@@ -0,0 +1,127 @@
+import Alert from './alert'
+import { makeArray, getTransitionDurationFromElement } from '../util/index'
+
+/** Test helpers */
+import { getFixture, clearFixture } from '../../tests/helpers/fixture'
+
+describe('Alert', () => {
+ let fixtureEl
+
+ beforeAll(() => {
+ fixtureEl = getFixture()
+ })
+
+ afterEach(() => {
+ clearFixture()
+ })
+
+ it('should return version', () => {
+ expect(typeof Alert.VERSION).toEqual('string')
+ })
+
+ describe('data-api', () => {
+ it('should close an alert without instanciate it manually', () => {
+ fixtureEl.innerHTML = [
+ '<div class="alert">',
+ ' <button type="button" data-dismiss="alert">x</button>',
+ '</div>'
+ ].join('')
+
+ const button = document.querySelector('button')
+
+ button.click()
+ expect(makeArray(document.querySelectorAll('.alert')).length).toEqual(0)
+ })
+
+ it('should close an alert without instanciate it manually with the parent selector', () => {
+ fixtureEl.innerHTML = [
+ '<div class="alert">',
+ ' <button type="button" data-target=".alert" data-dismiss="alert">x</button>',
+ '</div>'
+ ].join('')
+
+ const button = document.querySelector('button')
+
+ button.click()
+ expect(makeArray(document.querySelectorAll('.alert')).length).toEqual(0)
+ })
+ })
+
+ describe('close', () => {
+ it('should close an alert', done => {
+ const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
+ fixtureEl.innerHTML = '<div class="alert"></div>'
+
+ const alertEl = document.querySelector('.alert')
+ const alert = new Alert(alertEl)
+
+ alertEl.addEventListener('closed.bs.alert', () => {
+ expect(makeArray(document.querySelectorAll('.alert')).length).toEqual(0)
+ expect(spy).not.toHaveBeenCalled()
+ done()
+ })
+
+ alert.close()
+ })
+
+ it('should close alert with fade class', done => {
+ fixtureEl.innerHTML = '<div class="alert fade"></div>'
+
+ const alertEl = document.querySelector('.alert')
+ const alert = new Alert(alertEl)
+
+ alertEl.addEventListener('transitionend', () => {
+ expect().nothing()
+ })
+
+ alertEl.addEventListener('closed.bs.alert', () => {
+ expect(makeArray(document.querySelectorAll('.alert')).length).toEqual(0)
+ done()
+ })
+
+ alert.close()
+ })
+
+ it('should not remove alert if close event is prevented', done => {
+ fixtureEl.innerHTML = '<div class="alert"></div>'
+
+ const alertEl = document.querySelector('.alert')
+ const alert = new Alert(alertEl)
+
+ const endTest = () => {
+ setTimeout(() => {
+ expect(alert._removeElement).not.toHaveBeenCalled()
+ done()
+ }, 10)
+ }
+
+ spyOn(alert, '_removeElement')
+
+ alertEl.addEventListener('close.bs.alert', event => {
+ event.preventDefault()
+ endTest()
+ })
+
+ alertEl.addEventListener('closed.bs.alert', () => {
+ endTest()
+ })
+
+ alert.close()
+ })
+ })
+
+ describe('dispose', () => {
+ it('should dispose an alert', () => {
+ fixtureEl.innerHTML = '<div class="alert"></div>'
+
+ const alertEl = document.querySelector('.alert')
+ const alert = new Alert(alertEl)
+
+ expect(Alert._getInstance(alertEl)).toBeDefined()
+
+ alert.dispose()
+
+ expect(Alert._getInstance(alertEl)).toBeNull()
+ })
+ })
+})