diff --git a/js/src/modal.js b/js/src/modal.js index 5e99414449..779b9a402f 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -67,7 +67,8 @@ const Modal = (($) => { DIALOG : '.modal-dialog', DATA_TOGGLE : '[data-toggle="modal"]', DATA_DISMISS : '[data-dismiss="modal"]', - FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' + FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top', + NAVBAR_TOGGLER : '.navbar-toggler' } @@ -212,7 +213,6 @@ const Modal = (($) => { this._isShown = null this._isBodyOverflowing = null this._ignoreBackdropClick = null - this._originalBodyPadding = null this._scrollbarWidth = null } @@ -429,21 +429,53 @@ const Modal = (($) => { } _setScrollbar() { - const bodyPadding = parseInt( - $(Selector.FIXED_CONTENT).css('padding-right') || 0, - 10 - ) - - this._originalBodyPadding = document.body.style.paddingRight || '' - if (this._isBodyOverflowing) { - document.body.style.paddingRight = - `${bodyPadding + this._scrollbarWidth}px` + // Note: DOMNode.style.paddingRight returns the actual value or '' if not set + // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set + + // Adjust fixed content padding + $(Selector.FIXED_CONTENT).each((index, element) => { + const actualPadding = $(element)[0].style.paddingRight + const calculatedPadding = $(element).css('padding-right') + $(element).data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`) + }) + + // Adjust navbar-toggler margin + $(Selector.NAVBAR_TOGGLER).each((index, element) => { + const actualMargin = $(element)[0].style.marginRight + const calculatedMargin = $(element).css('margin-right') + $(element).data('margin-right', actualMargin).css('margin-right', `${parseFloat(calculatedMargin) + this._scrollbarWidth}px`) + }) + + // Adjust body padding + const actualPadding = document.body.style.paddingRight + const calculatedPadding = $('body').css('padding-right') + $('body').data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`) } } _resetScrollbar() { - document.body.style.paddingRight = this._originalBodyPadding + // Restore fixed content padding + $(Selector.FIXED_CONTENT).each((index, element) => { + const padding = $(element).data('padding-right') + if (typeof padding !== 'undefined') { + $(element).css('padding-right', padding).removeData('padding-right') + } + }) + + // Restore navbar-toggler margin + $(Selector.NAVBAR_TOGGLER).each((index, element) => { + const margin = $(element).data('margin-right') + if (typeof margin !== 'undefined') { + $(element).css('margin-right', margin).removeData('margin-right') + } + }) + + // Restore body padding + const padding = $('body').data('padding-right') + if (typeof padding !== 'undefined') { + $('body').css('padding-right', padding).removeData('padding-right') + } } _getScrollbarWidth() { // thx d.walsh diff --git a/js/tests/unit/modal.js b/js/tests/unit/modal.js index 84492cec2c..2c3e422306 100644 --- a/js/tests/unit/modal.js +++ b/js/tests/unit/modal.js @@ -9,6 +9,10 @@ $(function () { }) QUnit.module('modal', { + before: function () { + // Enable the scrollbar measurer + $('').appendTo('head') + }, beforeEach: function () { // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode $.fn.bootstrapModal = $.fn.modal.noConflict() @@ -336,23 +340,124 @@ $(function () { $toggleBtn.trigger('click') }) - QUnit.test('should restore inline body padding after closing', function (assert) { + QUnit.test('should adjust the inline body padding when opening and restore when closing', function (assert) { assert.expect(2) var done = assert.async() - var originalBodyPad = 0 var $body = $(document.body) - - $body.css('padding-right', originalBodyPad) + var originalPadding = $body.css('padding-right') $('
') .on('hidden.bs.modal', function () { - var currentBodyPad = parseInt($body.css('padding-right'), 10) - assert.notStrictEqual($body.attr('style'), '', 'body has non-empty style attribute') - assert.strictEqual(currentBodyPad, originalBodyPad, 'original body padding was not changed') + var currentPadding = $body.css('padding-right') + assert.strictEqual(currentPadding, originalPadding, 'body padding should be reset after closing') $body.removeAttr('style') done() }) .on('shown.bs.modal', function () { + var currentPadding = $body.css('padding-right') + assert.notStrictEqual(currentPadding, originalPadding, 'body padding should be adjusted while opening') + $(this).bootstrapModal('hide') + }) + .bootstrapModal('show') + }) + + QUnit.test('should store the original body padding in data-padding-right before showing', function (assert) { + assert.expect(2) + var done = assert.async() + var $body = $(document.body) + var originalPadding = '0px' + $body.css('padding-right', originalPadding) + + $('') + .on('hidden.bs.modal', function () { + assert.strictEqual($body.data('padding-right'), undefined, 'data-padding-right should be cleared after closing') + $body.removeAttr('style') + done() + }) + .on('shown.bs.modal', function () { + assert.strictEqual($body.data('padding-right'), originalPadding, 'original body padding should be stored in data-padding-right') + $(this).bootstrapModal('hide') + }) + .bootstrapModal('show') + }) + + QUnit.test('should adjust the inline padding of fixed elements when opening and restore when closing', function (assert) { + assert.expect(2) + var done = assert.async() + var $element = $('').appendTo('#qunit-fixture') + var originalPadding = $element.css('padding-right') + + $('') + .on('hidden.bs.modal', function () { + var currentPadding = $element.css('padding-right') + assert.strictEqual(currentPadding, originalPadding, 'fixed element padding should be reset after closing') + $element.remove() + done() + }) + .on('shown.bs.modal', function () { + var currentPadding = $element.css('padding-right') + assert.notStrictEqual(currentPadding, originalPadding, 'fixed element padding should be adjusted while opening') + $(this).bootstrapModal('hide') + }) + .bootstrapModal('show') + }) + + QUnit.test('should store the original padding of fixed elements in data-padding-right before showing', function (assert) { + assert.expect(2) + var done = assert.async() + var $element = $('').appendTo('#qunit-fixture') + var originalPadding = '0px' + $element.css('padding-right', originalPadding) + + $('') + .on('hidden.bs.modal', function () { + assert.strictEqual($element.data('padding-right'), undefined, 'data-padding-right should be cleared after closing') + $element.remove() + done() + }) + .on('shown.bs.modal', function () { + assert.strictEqual($element.data('padding-right'), originalPadding, 'original fixed element padding should be stored in data-padding-right') + $(this).bootstrapModal('hide') + }) + .bootstrapModal('show') + }) + + QUnit.test('should adjust the inline margin of the navbar-toggler when opening and restore when closing', function (assert) { + assert.expect(2) + var done = assert.async() + var $element = $('').appendTo('#qunit-fixture') + var originalMargin = $element.css('margin-right') + + $('') + .on('hidden.bs.modal', function () { + var currentMargin = $element.css('margin-right') + assert.strictEqual(currentMargin, originalMargin, 'navbar-toggler margin should be reset after closing') + $element.remove() + done() + }) + .on('shown.bs.modal', function () { + var currentMargin = $element.css('margin-right') + assert.notStrictEqual(currentMargin, originalMargin, 'navbar-toggler margin should be adjusted while opening') + $(this).bootstrapModal('hide') + }) + .bootstrapModal('show') + }) + + QUnit.test('should store the original margin of the navbar-toggler in data-margin-right before showing', function (assert) { + assert.expect(2) + var done = assert.async() + var $element = $('').appendTo('#qunit-fixture') + var originalMargin = '0px' + $element.css('margin-right', originalMargin) + + $('') + .on('hidden.bs.modal', function () { + assert.strictEqual($element.data('margin-right'), undefined, 'data-margin-right should be cleared after closing') + $element.remove() + done() + }) + .on('shown.bs.modal', function () { + assert.strictEqual($element.data('margin-right'), originalMargin, 'original navbar-toggler margin should be stored in data-margin-right') $(this).bootstrapModal('hide') }) .bootstrapModal('show') @@ -376,44 +481,6 @@ $(function () { .bootstrapModal('show') }) - QUnit.test('should have a paddingRight when the modal is taller than the viewport', function (assert) { - assert.expect(2) - var done = assert.async() - $('