2021-04-25 05:50:16 +02:00
import { clearBodyAndDocument , clearFixture , getFixture } from '../../helpers/fixture'
import Manipulator from '../../../src/dom/manipulator'
2021-06-06 08:26:36 +02:00
import ScrollBarHelper from '../../../src/util/scrollbar'
2021-03-02 18:10:10 +01:00
describe ( 'ScrollBar' , ( ) => {
let fixtureEl
2021-05-20 15:29:04 +02:00
const doc = document . documentElement
2021-11-26 09:15:58 +01:00
const parseIntDecimal = arg => Number . parseInt ( arg , 10 )
const getPaddingX = el => parseIntDecimal ( window . getComputedStyle ( el ) . paddingRight )
const getMarginX = el => parseIntDecimal ( window . getComputedStyle ( el ) . marginRight )
2021-04-25 05:50:16 +02:00
const getOverFlow = el => el . style . overflow
const getPaddingAttr = el => Manipulator . getDataAttribute ( el , 'padding-right' )
2021-05-20 15:29:04 +02:00
const getMarginAttr = el => Manipulator . getDataAttribute ( el , 'margin-right' )
2021-04-25 05:50:16 +02:00
const getOverFlowAttr = el => Manipulator . getDataAttribute ( el , 'overflow' )
2021-03-02 18:10:10 +01:00
const windowCalculations = ( ) => {
return {
htmlClient : document . documentElement . clientWidth ,
htmlOffset : document . documentElement . offsetWidth ,
docClient : document . body . clientWidth ,
htmlBound : document . documentElement . getBoundingClientRect ( ) . width ,
bodyBound : document . body . getBoundingClientRect ( ) . width ,
window : window . innerWidth ,
width : Math . abs ( window . innerWidth - document . documentElement . clientWidth )
}
}
2021-11-26 08:16:59 +01:00
// iOS, Android devices and macOS browsers hide scrollbar by default and show it only while scrolling.
// So the tests for scrollbar would fail
const isScrollBarHidden = ( ) => {
2021-03-02 18:10:10 +01:00
const calc = windowCalculations ( )
return calc . htmlClient === calc . htmlOffset && calc . htmlClient === calc . window
}
beforeAll ( ( ) => {
fixtureEl = getFixture ( )
// custom fixture to avoid extreme style values
fixtureEl . removeAttribute ( 'style' )
} )
afterAll ( ( ) => {
fixtureEl . remove ( )
} )
afterEach ( ( ) => {
clearFixture ( )
2021-04-25 05:50:16 +02:00
clearBodyAndDocument ( )
2021-03-02 18:10:10 +01:00
} )
beforeEach ( ( ) => {
2021-04-25 05:50:16 +02:00
clearBodyAndDocument ( )
2021-03-02 18:10:10 +01:00
} )
describe ( 'isBodyOverflowing' , ( ) => {
it ( 'should return true if body is overflowing' , ( ) => {
document . documentElement . style . overflowY = 'scroll'
document . body . style . overflowY = 'scroll'
2021-11-26 08:16:59 +01:00
fixtureEl . innerHTML = '<div style="height: 110vh; width: 100%"></div>'
2021-06-06 08:26:36 +02:00
const result = new ScrollBarHelper ( ) . isOverflowing ( )
2021-03-02 18:10:10 +01:00
if ( isScrollBarHidden ( ) ) {
2021-10-14 17:16:54 +02:00
expect ( result ) . toBeFalse ( )
2021-03-02 18:10:10 +01:00
} else {
2021-10-14 17:16:54 +02:00
expect ( result ) . toBeTrue ( )
2021-03-02 18:10:10 +01:00
}
} )
2021-05-20 15:29:04 +02:00
it ( 'should return false if body is not overflowing' , ( ) => {
doc . style . overflowY = 'hidden'
2021-03-02 18:10:10 +01:00
document . body . style . overflowY = 'hidden'
2021-11-26 08:16:59 +01:00
fixtureEl . innerHTML = '<div style="height: 110vh; width: 100%"></div>'
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
const result = scrollBar . isOverflowing ( )
2021-03-02 18:10:10 +01:00
2021-10-14 17:16:54 +02:00
expect ( result ) . toBeFalse ( )
2021-03-02 18:10:10 +01:00
} )
} )
describe ( 'getWidth' , ( ) => {
it ( 'should return an integer greater than zero, if body is overflowing' , ( ) => {
2021-05-20 15:29:04 +02:00
doc . style . overflowY = 'scroll'
2021-03-02 18:10:10 +01:00
document . body . style . overflowY = 'scroll'
2021-11-26 08:16:59 +01:00
fixtureEl . innerHTML = '<div style="height: 110vh; width: 100%"></div>'
2021-06-06 08:26:36 +02:00
const result = new ScrollBarHelper ( ) . getWidth ( )
2021-03-02 18:10:10 +01:00
if ( isScrollBarHidden ( ) ) {
2021-10-14 17:16:54 +02:00
expect ( result ) . toEqual ( 0 )
2021-03-02 18:10:10 +01:00
} else {
expect ( result ) . toBeGreaterThan ( 1 )
}
} )
it ( 'should return 0 if body is not overflowing' , ( ) => {
document . documentElement . style . overflowY = 'hidden'
document . body . style . overflowY = 'hidden'
2021-11-26 08:16:59 +01:00
fixtureEl . innerHTML = '<div style="height: 110vh; width: 100%"></div>'
2021-03-02 18:10:10 +01:00
2021-06-06 08:26:36 +02:00
const result = new ScrollBarHelper ( ) . getWidth ( )
2021-03-02 18:10:10 +01:00
expect ( result ) . toEqual ( 0 )
} )
} )
describe ( 'hide - reset' , ( ) => {
it ( 'should adjust the inline padding of fixed elements which are full-width' , done => {
fixtureEl . innerHTML = [
2021-11-26 08:16:59 +01:00
'<div style="height: 110vh; width: 100%">' ,
' <div class="fixed-top" id="fixed1" style="padding-right: 0px; width: 100vw"></div>' ,
' <div class="fixed-top" id="fixed2" style="padding-right: 5px; width: 100vw"></div>' ,
2021-03-02 18:10:10 +01:00
'</div>'
] . join ( '' )
2021-05-20 15:29:04 +02:00
doc . style . overflowY = 'scroll'
2021-03-02 18:10:10 +01:00
const fixedEl = fixtureEl . querySelector ( '#fixed1' )
const fixedEl2 = fixtureEl . querySelector ( '#fixed2' )
2021-05-20 15:29:04 +02:00
const originalPadding = getPaddingX ( fixedEl )
const originalPadding2 = getPaddingX ( fixedEl2 )
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
const expectedPadding = originalPadding + scrollBar . getWidth ( )
const expectedPadding2 = originalPadding2 + scrollBar . getWidth ( )
2021-03-02 18:10:10 +01:00
2021-06-06 08:26:36 +02:00
scrollBar . hide ( )
2021-03-02 18:10:10 +01:00
2021-05-20 15:29:04 +02:00
let currentPadding = getPaddingX ( fixedEl )
let currentPadding2 = getPaddingX ( fixedEl2 )
2021-10-14 17:16:54 +02:00
expect ( getPaddingAttr ( fixedEl ) ) . toEqual ( ` ${ originalPadding } px ` )
expect ( getPaddingAttr ( fixedEl2 ) ) . toEqual ( ` ${ originalPadding2 } px ` )
expect ( currentPadding ) . toEqual ( expectedPadding )
expect ( currentPadding2 ) . toEqual ( expectedPadding2 )
2021-03-02 18:10:10 +01:00
2021-06-06 08:26:36 +02:00
scrollBar . reset ( )
2021-05-20 15:29:04 +02:00
currentPadding = getPaddingX ( fixedEl )
currentPadding2 = getPaddingX ( fixedEl2 )
2021-10-14 17:16:54 +02:00
expect ( getPaddingAttr ( fixedEl ) ) . toBeNull ( )
expect ( getPaddingAttr ( fixedEl2 ) ) . toBeNull ( )
expect ( currentPadding ) . toEqual ( originalPadding )
expect ( currentPadding2 ) . toEqual ( originalPadding2 )
2021-03-02 18:10:10 +01:00
done ( )
} )
2021-12-09 14:05:50 +01:00
it ( 'should remove padding & margin if not existed before adjustment' , done => {
fixtureEl . innerHTML = [
'<div style="height: 110vh; width: 100%">' ,
' <div class="fixed" id="fixed" style="width: 100vw;"></div>' ,
' <div class="sticky-top" id="sticky" style=" width: 100vw;"></div>' ,
'</div>'
] . join ( '' )
doc . style . overflowY = 'scroll'
const fixedEl = fixtureEl . querySelector ( '#fixed' )
const stickyEl = fixtureEl . querySelector ( '#sticky' )
const scrollBar = new ScrollBarHelper ( )
scrollBar . hide ( )
scrollBar . reset ( )
expect ( fixedEl . getAttribute ( 'style' ) . includes ( 'padding-right' ) ) . toBeFalse ( )
expect ( stickyEl . getAttribute ( 'style' ) . includes ( 'margin-right' ) ) . toBeFalse ( )
done ( )
} )
2021-05-20 15:29:04 +02:00
it ( 'should adjust the inline margin and padding of sticky elements' , done => {
2021-03-02 18:10:10 +01:00
fixtureEl . innerHTML = [
2021-11-26 08:16:59 +01:00
'<div style="height: 110vh">' ,
' <div class="sticky-top" style="margin-right: 10px; padding-right: 20px; width: 100vw; height: 10px"></div>' ,
2021-03-02 18:10:10 +01:00
'</div>'
] . join ( '' )
2021-05-20 15:29:04 +02:00
doc . style . overflowY = 'scroll'
2021-03-02 18:10:10 +01:00
const stickyTopEl = fixtureEl . querySelector ( '.sticky-top' )
2021-05-20 15:29:04 +02:00
const originalMargin = getMarginX ( stickyTopEl )
const originalPadding = getPaddingX ( stickyTopEl )
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
const expectedMargin = originalMargin - scrollBar . getWidth ( )
const expectedPadding = originalPadding + scrollBar . getWidth ( )
scrollBar . hide ( )
2021-03-02 18:10:10 +01:00
2021-10-14 17:16:54 +02:00
expect ( getMarginAttr ( stickyTopEl ) ) . toEqual ( ` ${ originalMargin } px ` )
expect ( getMarginX ( stickyTopEl ) ) . toEqual ( expectedMargin )
expect ( getPaddingAttr ( stickyTopEl ) ) . toEqual ( ` ${ originalPadding } px ` )
expect ( getPaddingX ( stickyTopEl ) ) . toEqual ( expectedPadding )
2021-03-02 18:10:10 +01:00
2021-06-06 08:26:36 +02:00
scrollBar . reset ( )
2021-10-14 17:16:54 +02:00
expect ( getMarginAttr ( stickyTopEl ) ) . toBeNull ( )
expect ( getMarginX ( stickyTopEl ) ) . toEqual ( originalMargin )
expect ( getPaddingAttr ( stickyTopEl ) ) . toBeNull ( )
expect ( getPaddingX ( stickyTopEl ) ) . toEqual ( originalPadding )
2021-03-02 18:10:10 +01:00
done ( )
} )
it ( 'should not adjust the inline margin and padding of sticky and fixed elements when element do not have full width' , ( ) => {
2021-11-26 08:16:59 +01:00
fixtureEl . innerHTML = '<div class="sticky-top" style="margin-right: 0px; padding-right: 0px; width: 50vw"></div>'
2021-03-02 18:10:10 +01:00
const stickyTopEl = fixtureEl . querySelector ( '.sticky-top' )
2021-05-20 15:29:04 +02:00
const originalMargin = getMarginX ( stickyTopEl )
const originalPadding = getPaddingX ( stickyTopEl )
2021-03-02 18:10:10 +01:00
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
scrollBar . hide ( )
2021-03-02 18:10:10 +01:00
2021-05-20 15:29:04 +02:00
const currentMargin = getMarginX ( stickyTopEl )
const currentPadding = getPaddingX ( stickyTopEl )
2021-03-02 18:10:10 +01:00
2021-10-14 17:16:54 +02:00
expect ( currentMargin ) . toEqual ( originalMargin )
expect ( currentPadding ) . toEqual ( originalPadding )
2021-03-02 18:10:10 +01:00
2021-06-06 08:26:36 +02:00
scrollBar . reset ( )
2021-03-02 18:10:10 +01:00
} )
2021-04-25 05:50:16 +02:00
2021-05-20 15:29:04 +02:00
it ( 'should not put data-attribute if element doesn\'t have the proper style property, should just remove style property if element didn\'t had one' , ( ) => {
fixtureEl . innerHTML = [
2021-11-26 08:16:59 +01:00
'<div style="height: 110vh; width: 100%">' ,
' <div class="sticky-top" id="sticky" style="width: 100vw"></div>' ,
2021-05-20 15:29:04 +02:00
'</div>'
] . join ( '' )
document . body . style . overflowY = 'scroll'
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
2021-05-20 15:29:04 +02:00
const hasPaddingAttr = el => el . hasAttribute ( 'data-bs-padding-right' )
const hasMarginAttr = el => el . hasAttribute ( 'data-bs-margin-right' )
const stickyEl = fixtureEl . querySelector ( '#sticky' )
const originalPadding = getPaddingX ( stickyEl )
const originalMargin = getMarginX ( stickyEl )
2021-06-06 08:26:36 +02:00
const scrollBarWidth = scrollBar . getWidth ( )
2021-05-20 15:29:04 +02:00
2021-06-06 08:26:36 +02:00
scrollBar . hide ( )
2021-05-20 15:29:04 +02:00
expect ( getPaddingX ( stickyEl ) ) . toEqual ( scrollBarWidth + originalPadding )
const expectedMargin = scrollBarWidth + originalMargin
expect ( getMarginX ( stickyEl ) ) . toEqual ( expectedMargin === 0 ? expectedMargin : - expectedMargin )
expect ( hasMarginAttr ( stickyEl ) ) . toBeFalse ( ) // We do not have to keep css margin
expect ( hasPaddingAttr ( stickyEl ) ) . toBeFalse ( ) // We do not have to keep css padding
2021-06-06 08:26:36 +02:00
scrollBar . reset ( )
2021-05-20 15:29:04 +02:00
expect ( getPaddingX ( stickyEl ) ) . toEqual ( originalPadding )
expect ( getPaddingX ( stickyEl ) ) . toEqual ( originalPadding )
} )
2021-04-25 05:50:16 +02:00
describe ( 'Body Handling' , ( ) => {
2021-05-20 15:29:04 +02:00
it ( 'should ignore other inline styles when trying to restore body defaults ' , ( ) => {
document . body . style . color = 'red'
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
const scrollBarWidth = scrollBar . getWidth ( )
scrollBar . hide ( )
2021-05-20 15:29:04 +02:00
2021-10-14 17:16:54 +02:00
expect ( getPaddingX ( document . body ) ) . toEqual ( scrollBarWidth )
expect ( document . body . style . color ) . toEqual ( 'red' )
2021-05-20 15:29:04 +02:00
2021-06-06 08:26:36 +02:00
scrollBar . reset ( )
2021-05-20 15:29:04 +02:00
} )
2021-04-25 05:50:16 +02:00
it ( 'should hide scrollbar and reset it to its initial value' , ( ) => {
const styleSheetPadding = '7px'
fixtureEl . innerHTML = [
'<style>' ,
' body {' ,
2021-11-26 08:16:59 +01:00
` padding-right: ${ styleSheetPadding } ` ,
2021-04-25 05:50:16 +02:00
' }' ,
'</style>'
] . join ( '' )
const el = document . body
const inlineStylePadding = '10px'
el . style . paddingRight = inlineStylePadding
2021-05-20 15:29:04 +02:00
const originalPadding = getPaddingX ( el )
2021-11-26 09:15:58 +01:00
expect ( originalPadding ) . toEqual ( parseIntDecimal ( inlineStylePadding ) ) // Respect only the inline style as it has prevails this of css
2021-04-25 05:50:16 +02:00
const originalOverFlow = 'auto'
el . style . overflow = originalOverFlow
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
const scrollBarWidth = scrollBar . getWidth ( )
2021-04-25 05:50:16 +02:00
2021-06-06 08:26:36 +02:00
scrollBar . hide ( )
2021-04-25 05:50:16 +02:00
2021-05-20 15:29:04 +02:00
const currentPadding = getPaddingX ( el )
2021-04-25 05:50:16 +02:00
expect ( currentPadding ) . toEqual ( scrollBarWidth + originalPadding )
2021-11-26 09:15:58 +01:00
expect ( currentPadding ) . toEqual ( scrollBarWidth + parseIntDecimal ( inlineStylePadding ) )
2021-04-25 05:50:16 +02:00
expect ( getPaddingAttr ( el ) ) . toEqual ( inlineStylePadding )
expect ( getOverFlow ( el ) ) . toEqual ( 'hidden' )
expect ( getOverFlowAttr ( el ) ) . toEqual ( originalOverFlow )
2021-06-06 08:26:36 +02:00
scrollBar . reset ( )
2021-04-25 05:50:16 +02:00
2021-05-20 15:29:04 +02:00
const currentPadding1 = getPaddingX ( el )
2021-04-25 05:50:16 +02:00
expect ( currentPadding1 ) . toEqual ( originalPadding )
2021-10-14 17:16:54 +02:00
expect ( getPaddingAttr ( el ) ) . toBeNull ( )
2021-04-25 05:50:16 +02:00
expect ( getOverFlow ( el ) ) . toEqual ( originalOverFlow )
2021-10-14 17:16:54 +02:00
expect ( getOverFlowAttr ( el ) ) . toBeNull ( )
2021-04-25 05:50:16 +02:00
} )
it ( 'should hide scrollbar and reset it to its initial value - respecting css rules' , ( ) => {
const styleSheetPadding = '7px'
fixtureEl . innerHTML = [
'<style>' ,
' body {' ,
2021-11-26 08:16:59 +01:00
` padding-right: ${ styleSheetPadding } ` ,
2021-04-25 05:50:16 +02:00
' }' ,
'</style>'
] . join ( '' )
const el = document . body
2021-05-20 15:29:04 +02:00
const originalPadding = getPaddingX ( el )
2021-04-25 05:50:16 +02:00
const originalOverFlow = 'scroll'
el . style . overflow = originalOverFlow
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
const scrollBarWidth = scrollBar . getWidth ( )
2021-04-25 05:50:16 +02:00
2021-06-06 08:26:36 +02:00
scrollBar . hide ( )
2021-04-25 05:50:16 +02:00
2021-05-20 15:29:04 +02:00
const currentPadding = getPaddingX ( el )
2021-04-25 05:50:16 +02:00
expect ( currentPadding ) . toEqual ( scrollBarWidth + originalPadding )
2021-11-26 09:15:58 +01:00
expect ( currentPadding ) . toEqual ( scrollBarWidth + parseIntDecimal ( styleSheetPadding ) )
2021-04-25 05:50:16 +02:00
expect ( getPaddingAttr ( el ) ) . toBeNull ( ) // We do not have to keep css padding
expect ( getOverFlow ( el ) ) . toEqual ( 'hidden' )
expect ( getOverFlowAttr ( el ) ) . toEqual ( originalOverFlow )
2021-06-06 08:26:36 +02:00
scrollBar . reset ( )
2021-04-25 05:50:16 +02:00
2021-05-20 15:29:04 +02:00
const currentPadding1 = getPaddingX ( el )
2021-04-25 05:50:16 +02:00
expect ( currentPadding1 ) . toEqual ( originalPadding )
2021-10-14 17:16:54 +02:00
expect ( getPaddingAttr ( el ) ) . toBeNull ( )
2021-04-25 05:50:16 +02:00
expect ( getOverFlow ( el ) ) . toEqual ( originalOverFlow )
2021-10-14 17:16:54 +02:00
expect ( getOverFlowAttr ( el ) ) . toBeNull ( )
2021-04-25 05:50:16 +02:00
} )
2021-05-20 15:29:04 +02:00
it ( 'should not adjust the inline body padding when it does not overflow' , ( ) => {
const originalPadding = getPaddingX ( document . body )
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
2021-05-20 15:29:04 +02:00
// Hide scrollbars to prevent the body overflowing
doc . style . overflowY = 'hidden'
doc . style . paddingRight = '0px'
2021-06-06 08:26:36 +02:00
scrollBar . hide ( )
2021-05-20 15:29:04 +02:00
const currentPadding = getPaddingX ( document . body )
2021-10-14 17:16:54 +02:00
expect ( currentPadding ) . toEqual ( originalPadding )
2021-06-06 08:26:36 +02:00
scrollBar . reset ( )
2021-05-20 15:29:04 +02:00
} )
it ( 'should not adjust the inline body padding when it does not overflow, even on a scaled display' , ( ) => {
const originalPadding = getPaddingX ( document . body )
2021-06-06 08:26:36 +02:00
const scrollBar = new ScrollBarHelper ( )
2021-05-20 15:29:04 +02:00
// Remove body margins as would be done by Bootstrap css
document . body . style . margin = '0'
// Hide scrollbars to prevent the body overflowing
doc . style . overflowY = 'hidden'
// Simulate a discrepancy between exact, i.e. floating point body width, and rounded body width
// as it can occur when zooming or scaling the display to something else than 100%
doc . style . paddingRight = '.48px'
2021-06-06 08:26:36 +02:00
scrollBar . hide ( )
2021-05-20 15:29:04 +02:00
const currentPadding = getPaddingX ( document . body )
2021-10-14 17:16:54 +02:00
expect ( currentPadding ) . toEqual ( originalPadding )
2021-05-20 15:29:04 +02:00
2021-06-06 08:26:36 +02:00
scrollBar . reset ( )
2021-05-20 15:29:04 +02:00
} )
2021-04-25 05:50:16 +02:00
} )
2021-03-02 18:10:10 +01:00
} )
} )