Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor into getRoundedOffsets.js module, add tests
- Loading branch information
Showing
3 changed files
with
175 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/** | ||
* @function | ||
* @memberof Popper.Utils | ||
* @argument {Object} data - The data object generated by `update` method | ||
* @argument {Boolean} shouldRound - If the offsets should be rounded at all | ||
* @returns {Object} The popper's position offsets rounded | ||
* | ||
* The tale of pixel-perfect positioning. It's still not 100% perfect, but as | ||
* good as it can be within reason. | ||
* Discussion here: https://github.com/FezVrasta/popper.js/pull/715 | ||
* | ||
* Low DPI screens cause a popper to be blurry if not using full pixels (Safari | ||
* as well on High DPI screens). | ||
* | ||
* Firefox prefers no rounding for positioning and does not have blurriness on | ||
* high DPI screens. | ||
* | ||
* Only horizontal placement and left/right values need to be considered. | ||
*/ | ||
export default function getRoundedOffsets(data, shouldRound) { | ||
const { popper, reference } = data.offsets; | ||
|
||
const isVertical = ['left', 'right'].indexOf(data.placement) !== -1; | ||
const isVariation = data.placement.indexOf('-') !== -1; | ||
const sameWidthOddness = reference.width % 2 === popper.width % 2; | ||
const bothOddWidth = reference.width % 2 === 1 && popper.width % 2 === 1; | ||
const noRound = v => v; | ||
|
||
const horizontalToInteger = !shouldRound | ||
? noRound | ||
: isVertical || isVariation || sameWidthOddness | ||
? Math.round | ||
: Math.floor; | ||
const verticalToInteger = !shouldRound ? noRound : Math.round; | ||
|
||
return { | ||
left: horizontalToInteger( | ||
bothOddWidth && !isVariation && shouldRound | ||
? popper.left - 1 | ||
: popper.left | ||
), | ||
top: verticalToInteger(popper.top), | ||
bottom: verticalToInteger(popper.bottom), | ||
right: horizontalToInteger(popper.right), | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import chai from 'chai'; | ||
const { expect } = chai; | ||
import getRoundedOffsets from '../../src/utils/getRoundedOffsets'; | ||
import placements from '../../src/methods/placements'; | ||
|
||
const TOP = 200; | ||
const BOTTOM = 300; | ||
const EVEN_SIZE = { width: 20, height: 20, top: TOP, bottom: BOTTOM, }; | ||
const ODD_SIZE = { width: 21, height: 21, top: TOP, bottom: BOTTOM, }; | ||
const ROUNDS_UP = { left: 18.57127, right: 38.57127, }; | ||
const ROUNDS_DOWN = { left: 18.47127, right: 38.47127, }; | ||
const ROUNDED_UP = { left: 19, right: 39, }; | ||
const ROUNDED_DOWN = { left: 18, right: 38, }; | ||
const ALL_SIZE_COMBINATIONS = [ | ||
[EVEN_SIZE, EVEN_SIZE], | ||
[EVEN_SIZE, ODD_SIZE], | ||
[ODD_SIZE, EVEN_SIZE], | ||
[ODD_SIZE, ODD_SIZE], | ||
]; | ||
const variationPlacements = placements.filter( | ||
placement => placement.includes('-') | ||
); | ||
|
||
describe('utils/getRoundedOffsets', () => { | ||
it('Math.round()s when both popper and reference have even width', () => { | ||
const offsets = getRoundedOffsets({ | ||
placement: 'bottom', | ||
offsets: { | ||
popper: { ...EVEN_SIZE, ...ROUNDS_UP, }, | ||
reference: EVEN_SIZE, | ||
}, | ||
}, true); | ||
expect(offsets.left).toBe(ROUNDED_UP.left); | ||
expect(offsets.right).toBe(ROUNDED_UP.right); | ||
expect(offsets.top).toBe(TOP); | ||
expect(offset.bottom).toBe(BOTTOM); | ||
}); | ||
|
||
it('Math.floor()s when popper and reference have a difference in width oddness', () => { | ||
const offsets1 = getRoundedOffsets({ | ||
placement: 'bottom', | ||
offsets: { | ||
popper: { ...EVEN_SIZE, ...ROUNDS_UP, }, | ||
reference: ODD_SIZE, | ||
}, | ||
}, true); | ||
const offsets2 = getRoundedOffsets({ | ||
placement: 'bottom', | ||
offsets: { | ||
popper: { ...ODD_SIZE, ...ROUNDS_UP, }, | ||
reference: EVEN_SIZE, | ||
}, | ||
}, true); | ||
[offsets1, offsets2].forEach(offsets => { | ||
expect(offsets.left).toBe(ROUNDED_DOWN.left); | ||
expect(offsets.right).toBe(ROUNDED_DOWN.right); | ||
expect(offsets.top).toBe(TOP); | ||
expect(offset.bottom).toBe(BOTTOM); | ||
}) | ||
}); | ||
|
||
it('Math.floor()s and subtracts 1 from left offset if both are odd in width', () => { | ||
const offsets = getRoundedOffsets({ | ||
placement: 'bottom', | ||
offsets: { | ||
popper: { ...ODD_SIZE, ...ROUNDS_UP, }, | ||
reference: ODD_SIZE, | ||
}, | ||
}, true); | ||
expect(offsets.left).toBe(ROUNDED_DOWN.left - 1); | ||
expect(offsets.right).toBe(ROUNDED_DOWN.right); | ||
expect(offsets.top).toBe(TOP); | ||
expect(offset.bottom).toBe(BOTTOM); | ||
}); | ||
|
||
it('always Math.round()s variation placements', () => { | ||
variationPlacements.forEach(placement => { | ||
ALL_SIZE_COMBINATIONS.forEach(([popperSize, referenceSize]) => { | ||
const offsets = getRoundedOffsets({ | ||
placement, | ||
offsets: { | ||
popper: { ...popperSize, ...ROUNDS_UP, }, | ||
reference: referenceSize, | ||
}, | ||
}, true); | ||
expect(offsets.left).toBe(ROUNDED_UP.left); | ||
expect(offsets.right).toBe(ROUNDED_UP.right); | ||
expect(offsets.top).toBe(TOP); | ||
expect(offset.bottom).toBe(BOTTOM); | ||
}); | ||
}); | ||
}); | ||
|
||
it('always Math.round()s vertical offsets', () => { | ||
ALL_SIZE_COMBINATIONS.forEach(([popperSize, referenceSize]) => { | ||
const offsets = getRoundedOffsets({ | ||
placement, | ||
offsets: { | ||
popper: { ...popperSize, ...ROUNDS_UP, top: 218.6, bottom: 318.6, }, | ||
reference: referenceSize, | ||
}, | ||
}, true); | ||
expect(offsets.top).toBe(219); | ||
expect(offset.bottom).toBe(319); | ||
}); | ||
}); | ||
|
||
it('does not integerize the offsets if second argument is `false`', () => { | ||
const offsets = getRoundedOffsets({ | ||
placement: 'bottom', | ||
offsets: { | ||
popper: { ...EVEN_SIZE, ...ROUNDS_UP, }, | ||
reference: EVEN_SIZE, | ||
}, | ||
}, false); | ||
expect(offsets.left).toBe(ROUNDS_UP.left); | ||
expect(offsets.right).toBe(ROUNDS_UP.right); | ||
expect(offsets.top).toBe(TOP); | ||
expect(offset.bottom).toBe(BOTTOM); | ||
}); | ||
}); |