| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- /**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
- /* global document, window */
- import { getOptimalPosition } from 'ckeditor5/utils/dom/position.js';
- import Rect from 'ckeditor5/utils/dom/rect.js';
- import testUtils from 'tests/core/_utils/utils.js';
- testUtils.createSinonSandbox();
- let element, target, limiter, revertWindowScroll;
- describe( 'getOptimalPosition', () => {
- beforeEach( () => {
- // Give us a lot of space.
- testUtils.sinon.stub( Rect, 'getViewportRect' ).returns( new Rect( {
- top: 0,
- right: 10000,
- bottom: 10000,
- left: 0,
- width: 10000,
- height: 10000
- } ) );
- } );
- afterEach( () => {
- if ( revertWindowScroll ) {
- revertWindowScroll();
- }
- } );
- describe( 'for single position', () => {
- beforeEach( setElementTargetPlayground );
- it( 'should return coordinates', () => {
- assertPosition( { element, target, positions: [ attachLeft ] }, {
- top: 100,
- left: 80,
- name: 'left'
- } );
- } );
- it( 'should return coordinates (window scroll)', () => {
- stubWindowScroll( 100, 100 );
- assertPosition( { element, target, positions: [ attachLeft ] }, {
- top: 200,
- left: 180,
- name: 'left'
- } );
- } );
- it( 'should return coordinates (positioned element parent)', () => {
- const positionedParent = document.createElement( 'div' );
- stubWindowScroll( 1000, 1000 );
- Object.assign( positionedParent.style, {
- position: 'absolute',
- top: '1000px',
- left: '1000px'
- } );
- document.body.appendChild( positionedParent );
- positionedParent.appendChild( element );
- stubElementRect( positionedParent, {
- top: 1000,
- right: 1010,
- bottom: 1010,
- left: 1000,
- width: 10,
- height: 10
- } );
- assertPosition( { element, target, positions: [ attachLeft ] }, {
- top: -900,
- left: -920,
- name: 'left'
- } );
- } );
- } );
- describe( 'for multiple positions', () => {
- beforeEach( setElementTargetPlayground );
- it( 'should return coordinates', () => {
- assertPosition( {
- element, target,
- positions: [ attachLeft, attachRight ]
- }, {
- top: 100,
- left: 80,
- name: 'left'
- } );
- } );
- it( 'should return coordinates (position preference order)', () => {
- assertPosition( {
- element, target,
- positions: [ attachRight, attachLeft ]
- }, {
- top: 100,
- left: 110,
- name: 'right'
- } );
- } );
- } );
- describe( 'with a limiter', () => {
- beforeEach( setElementTargetLimiterPlayground );
- it( 'should return coordinates (#1)', () => {
- assertPosition( {
- element, target, limiter,
- positions: [ attachLeft, attachRight ]
- }, {
- top: 100,
- left: -20,
- name: 'left'
- } );
- } );
- it( 'should return coordinates (#2)', () => {
- assertPosition( {
- element, target, limiter,
- positions: [ attachRight, attachLeft ]
- }, {
- top: 100,
- left: -20,
- name: 'left'
- } );
- } );
- } );
- describe( 'with fitInViewport on', () => {
- beforeEach( setElementTargetLimiterPlayground );
- it( 'should return coordinates (#1)', () => {
- assertPosition( {
- element, target,
- positions: [ attachLeft, attachRight ],
- fitInViewport: true
- }, {
- top: 100,
- left: 10,
- name: 'right'
- } );
- } );
- it( 'should return coordinates (#2)', () => {
- assertPosition( {
- element, target,
- positions: [ attachRight, attachLeft ],
- fitInViewport: true
- }, {
- top: 100,
- left: 10,
- name: 'right'
- } );
- } );
- it( 'should return coordinates (#3)', () => {
- assertPosition( {
- element, target,
- positions: [ attachLeft, attachBottom, attachRight ],
- fitInViewport: true
- }, {
- top: 110,
- left: 0,
- name: 'bottom'
- } );
- } );
- } );
- describe( 'with limiter and fitInViewport on', () => {
- beforeEach( setElementTargetLimiterPlayground );
- it( 'should return coordinates (#1)', () => {
- assertPosition( {
- element, target, limiter,
- positions: [ attachLeft, attachRight ],
- fitInViewport: true
- }, {
- top: 100,
- left: 10,
- name: 'right'
- } );
- } );
- it( 'should return coordinates (#2)', () => {
- assertPosition( {
- element, target, limiter,
- positions: [ attachRight, attachLeft ],
- fitInViewport: true
- }, {
- top: 100,
- left: 10,
- name: 'right'
- } );
- } );
- it( 'should return coordinates (#3)', () => {
- assertPosition( {
- element, target, limiter,
- positions: [ attachRight, attachLeft, attachBottom ],
- fitInViewport: true
- }, {
- top: 110,
- left: 0,
- name: 'bottom'
- } );
- } );
- it( 'should return coordinates (#4)', () => {
- assertPosition( {
- element, target, limiter,
- positions: [ attachTop, attachRight ],
- fitInViewport: true
- }, {
- top: 100,
- left: 10,
- name: 'right'
- } );
- } );
- it( 'should return the very first coordinates if no fitting position with a positive intersection has been found', () => {
- assertPosition( {
- element, target, limiter,
- positions: [
- () => ( {
- left: -10000,
- top: -10000,
- name: 'no-intersect-position'
- } )
- ],
- fitInViewport: true
- }, {
- left: -10000,
- top: -10000,
- name: 'no-intersect-position'
- } );
- } );
- it( 'should return the very first coordinates if limiter does not fit into the viewport', () => {
- stubElementRect( limiter, {
- top: -100,
- right: -80,
- bottom: -80,
- left: -100,
- width: 20,
- height: 20
- } );
- assertPosition( {
- element, target, limiter,
- positions: [ attachRight, attachTop ],
- fitInViewport: true
- }, {
- top: 100,
- left: 10,
- name: 'right'
- } );
- } );
- } );
- } );
- function assertPosition( options, expected ) {
- const position = getOptimalPosition( options );
- expect( position ).to.deep.equal( expected );
- }
- // +--------+-----+
- // | E | T |
- // +--------+-----+
- const attachLeft = ( targetRect, elementRect ) => ( {
- top: targetRect.top,
- left: targetRect.left - elementRect.width,
- name: 'left'
- } );
- // +-----+--------+
- // | T | E |
- // +-----+--------+
- const attachRight = ( targetRect ) => ( {
- top: targetRect.top,
- left: targetRect.left + targetRect.width,
- name: 'right'
- } );
- // +-----+
- // | T |
- // +-----+--+
- // | E |
- // +--------+
- const attachBottom = ( targetRect ) => ( {
- top: targetRect.bottom,
- left: targetRect.left,
- name: 'bottom'
- } );
- // +--------+
- // | E |
- // +--+-----+
- // | T |
- // +-----+
- const attachTop = ( targetRect, elementRect ) => ( {
- top: targetRect.top - elementRect.height,
- left: targetRect.left - ( elementRect.width - targetRect.width ),
- name: 'bottom'
- } );
- function stubWindowScroll( x, y ) {
- const { scrollX: savedX, scrollY: savedY } = window;
- window.scrollX = x;
- window.scrollY = y;
- revertWindowScroll = () => {
- window.scrollX = savedX;
- window.scrollY = savedY;
- revertWindowScroll = null;
- };
- }
- function stubElementRect( element, rect ) {
- if ( element.getBoundingClientRect.restore ) {
- element.getBoundingClientRect.restore();
- }
- testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( rect );
- }
- // <-- 100px ->
- //
- // ^ +--------------[ Viewport ]----------
- // | |
- // 100px |
- // | | <-- 10px -->
- // V |
- // | ^ +---------+
- // | | | |
- // | 10px | T |
- // | | | |
- // | V +---------+
- // |
- //
- function setElementTargetPlayground() {
- element = document.createElement( 'div' );
- target = document.createElement( 'div' );
- stubElementRect( element, {
- top: 0,
- right: 20,
- bottom: 20,
- left: 0,
- width: 20,
- height: 20
- } );
- stubElementRect( target, {
- top: 100,
- right: 110,
- bottom: 110,
- left: 100,
- width: 10,
- height: 10
- } );
- }
- //
- //
- // ^ +-----------[ Viewport ]----------------------
- // | |
- // 100px |
- // | <--------- 20px ------->
- // | <-- 10px -->
- // V |
- // +------------+---------+ ^ ^
- // | | | | |
- // | | T | 10px |
- // | | | | |
- // | +---------+ V 20px
- // | | | |
- // | | | |
- // | | | |
- // +------[ Limiter ]-----+ V
- // |
- // |
- //
- //
- function setElementTargetLimiterPlayground() {
- element = document.createElement( 'div' );
- target = document.createElement( 'div' );
- limiter = document.createElement( 'div' );
- stubElementRect( element, {
- top: 0,
- right: 20,
- bottom: 20,
- left: 0,
- width: 20,
- height: 20
- } );
- stubElementRect( limiter, {
- top: 100,
- right: 10,
- bottom: 120,
- left: -10,
- width: 20,
- height: 20
- } );
- stubElementRect( target, {
- top: 100,
- right: 10,
- bottom: 110,
- left: 0,
- width: 10,
- height: 10
- } );
- }
|