diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js index acc0971fa2..db65340391 100644 --- a/js/src/offcanvas.js +++ b/js/src/offcanvas.js @@ -31,6 +31,8 @@ const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}` const ESCAPE_KEY = 'Escape' const CLASS_NAME_SHOW = 'show' +const CLASS_NAME_SHOWING = 'showing' +const CLASS_NAME_HIDING = 'hiding' const CLASS_NAME_BACKDROP = 'offcanvas-backdrop' const OPEN_SELECTOR = '.offcanvas.show' @@ -99,24 +101,23 @@ class Offcanvas extends BaseComponent { } this._isShown = true - this._element.style.visibility = 'visible' - this._backdrop.show() if (!this._config.scroll) { new ScrollBarHelper().hide() } - this._element.removeAttribute('aria-hidden') this._element.setAttribute('aria-modal', true) this._element.setAttribute('role', 'dialog') - this._element.classList.add(CLASS_NAME_SHOW) + this._element.classList.add(CLASS_NAME_SHOWING) const completeCallBack = () => { if (!this._config.scroll) { this._focustrap.activate() } + this._element.classList.add(CLASS_NAME_SHOW) + this._element.classList.remove(CLASS_NAME_SHOWING) EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget }) } @@ -137,14 +138,13 @@ class Offcanvas extends BaseComponent { this._focustrap.deactivate() this._element.blur() this._isShown = false - this._element.classList.remove(CLASS_NAME_SHOW) + this._element.classList.add(CLASS_NAME_HIDING) this._backdrop.hide() const completeCallback = () => { - this._element.setAttribute('aria-hidden', true) + this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING) this._element.removeAttribute('aria-modal') this._element.removeAttribute('role') - this._element.style.visibility = 'hidden' if (!this._config.scroll) { new ScrollBarHelper().reset() diff --git a/js/tests/unit/offcanvas.spec.js b/js/tests/unit/offcanvas.spec.js index f4b0b64828..f87527fb2f 100644 --- a/js/tests/unit/offcanvas.spec.js +++ b/js/tests/unit/offcanvas.spec.js @@ -242,23 +242,46 @@ describe('Offcanvas', () => { expect(offCanvas.show).toHaveBeenCalled() }) - it('should call hide method if show class is present', () => { + it('should call hide method if show class is present', done => { fixtureEl.innerHTML = '
' const offCanvasEl = fixtureEl.querySelector('.offcanvas') const offCanvas = new Offcanvas(offCanvasEl) + + offCanvasEl.addEventListener('shown.bs.offcanvas', () => { + expect(offCanvasEl).toHaveClass('show') + spyOn(offCanvas, 'hide') + + offCanvas.toggle() + + expect(offCanvas.hide).toHaveBeenCalled() + done() + }) + offCanvas.show() - expect(offCanvasEl).toHaveClass('show') - - spyOn(offCanvas, 'hide') - - offCanvas.toggle() - - expect(offCanvas.hide).toHaveBeenCalled() }) }) describe('show', () => { + it('should add `showing` class during opening and `show` class on end', done => { + fixtureEl.innerHTML = '
' + const offCanvasEl = fixtureEl.querySelector('.offcanvas') + const offCanvas = new Offcanvas(offCanvasEl) + + offCanvasEl.addEventListener('show.bs.offcanvas', () => { + expect(offCanvasEl).not.toHaveClass('show') + }) + + offCanvasEl.addEventListener('shown.bs.offcanvas', () => { + expect(offCanvasEl).not.toHaveClass('showing') + expect(offCanvasEl).toHaveClass('show') + done() + }) + + offCanvas.show() + expect(offCanvasEl).toHaveClass('showing') + }) + it('should do nothing if already shown', () => { fixtureEl.innerHTML = '
' @@ -353,6 +376,30 @@ describe('Offcanvas', () => { }) describe('hide', () => { + it('should add `hiding` class during closing and remover `show` & `hiding` classes on end', done => { + fixtureEl.innerHTML = '
' + const offCanvasEl = fixtureEl.querySelector('.offcanvas') + const offCanvas = new Offcanvas(offCanvasEl) + + offCanvasEl.addEventListener('hide.bs.offcanvas', () => { + expect(offCanvasEl).not.toHaveClass('showing') + expect(offCanvasEl).toHaveClass('show') + }) + + offCanvasEl.addEventListener('hidden.bs.offcanvas', () => { + expect(offCanvasEl).not.toHaveClass('hiding') + expect(offCanvasEl).not.toHaveClass('show') + done() + }) + + offCanvas.show() + offCanvasEl.addEventListener('shown.bs.offcanvas', () => { + offCanvas.hide() + expect(offCanvasEl).not.toHaveClass('showing') + expect(offCanvasEl).toHaveClass('hiding') + }) + }) + it('should do nothing if already shown', () => { fixtureEl.innerHTML = '
' diff --git a/scss/_offcanvas.scss b/scss/_offcanvas.scss index a089c2a081..5288fa9ce6 100644 --- a/scss/_offcanvas.scss +++ b/scss/_offcanvas.scss @@ -12,6 +12,17 @@ outline: 0; @include box-shadow($offcanvas-box-shadow); @include transition(transform $offcanvas-transition-duration ease-in-out); + + &.showing, + &.show:not(.hiding) { + transform: none; + } + + &.showing, + &.hiding, + &.show { + visibility: visible; + } } .offcanvas-backdrop { @@ -77,7 +88,3 @@ border-top: $offcanvas-border-width solid $offcanvas-border-color; transform: translateY(100%); } - -.offcanvas.show { - transform: none; -}