diff options
| author | Johann-S <[email protected]> | 2019-03-13 16:23:50 +0200 |
|---|---|---|
| committer | Johann-S <[email protected]> | 2019-07-23 14:23:50 +0200 |
| commit | c8c207465043d940aa031570f0bce5e8fff9ffcf (patch) | |
| tree | 482f430ee84aa4cae8f76cebdec75791a60a50d1 /js/src/alert | |
| parent | 08d81c843706c2b952fadb703f3f9be709a8ca6a (diff) | |
| download | bootstrap-c8c207465043d940aa031570f0bce5e8fff9ffcf.tar.xz bootstrap-c8c207465043d940aa031570f0bce5e8fff9ffcf.zip | |
Switch from QUnit to Jasmine.
Diffstat (limited to 'js/src/alert')
| -rw-r--r-- | js/src/alert/alert.js | 191 | ||||
| -rw-r--r-- | js/src/alert/alert.spec.js | 127 |
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() + }) + }) +}) |
