mirror of
				https://github.com/gristlabs/grist-core.git
				synced 2025-06-13 20:53:59 +00:00 
			
		
		
		
	(core) Enabling height adjustment on tutorial popup
Summary: Support height adjustment for tutorial popup, also change the way we calculate delta for the movement to make it follow the cursor more smoothly. Test Plan: Added Reviewers: georgegevoian Reviewed By: georgegevoian Subscribers: JakubSerafin Differential Revision: https://phab.getgrist.com/D3858
This commit is contained in:
		
							parent
							
								
									c077f3c304
								
							
						
					
					
						commit
						35f7072bea
					
				@ -1,9 +1,12 @@
 | 
			
		||||
import {hoverTooltip} from 'app/client/ui/tooltips';
 | 
			
		||||
import {isNarrowScreen, isNarrowScreenObs, theme} from 'app/client/ui2018/cssVars';
 | 
			
		||||
import {icon} from 'app/client/ui2018/icons';
 | 
			
		||||
import {Disposable, dom, DomArg, DomContents, makeTestId, Observable, styled} from 'grainjs';
 | 
			
		||||
import {Disposable, dom, DomArg, DomContents, IDisposable, makeTestId, Observable, styled} from 'grainjs';
 | 
			
		||||
 | 
			
		||||
const POPUP_PADDING_PX = 16;
 | 
			
		||||
const POPUP_INITIAL_PADDING_PX = 16;
 | 
			
		||||
const POPUP_MIN_HEIGHT = 300;
 | 
			
		||||
const POPUP_MAX_HEIGHT = 711;
 | 
			
		||||
const POPUP_HEADER_HEIGHT = 30;
 | 
			
		||||
 | 
			
		||||
const testId = makeTestId('test-floating-popup-');
 | 
			
		||||
 | 
			
		||||
@ -19,8 +22,13 @@ export class FloatingPopup extends Disposable {
 | 
			
		||||
  protected _isMinimized = Observable.create(this, false);
 | 
			
		||||
  private _popupElement: HTMLElement | null = null;
 | 
			
		||||
 | 
			
		||||
  private _clientX: number;
 | 
			
		||||
  private _clientY: number;
 | 
			
		||||
  private _startX: number;
 | 
			
		||||
  private _startY: number;
 | 
			
		||||
  private _initialTop: number;
 | 
			
		||||
  private _initialBottom: number;
 | 
			
		||||
  private _initialLeft: number;
 | 
			
		||||
  private _resize = false;
 | 
			
		||||
  private _cursorGrab: IDisposable|null = null;
 | 
			
		||||
 | 
			
		||||
  constructor(protected _options: PopupOptions = {}, private _args: DomArg[] = []) {
 | 
			
		||||
    super();
 | 
			
		||||
@ -37,6 +45,7 @@ export class FloatingPopup extends Disposable {
 | 
			
		||||
 | 
			
		||||
    this.onDispose(() => {
 | 
			
		||||
      this._closePopup();
 | 
			
		||||
      this._cursorGrab?.dispose();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -44,7 +53,7 @@ export class FloatingPopup extends Disposable {
 | 
			
		||||
    this._popupElement = this._buildPopup();
 | 
			
		||||
    document.body.appendChild(this._popupElement);
 | 
			
		||||
    const topPaddingPx = getTopPopupPaddingPx();
 | 
			
		||||
    const initialLeft = document.body.offsetWidth - this._popupElement.offsetWidth - POPUP_PADDING_PX;
 | 
			
		||||
    const initialLeft = document.body.offsetWidth - this._popupElement.offsetWidth - POPUP_INITIAL_PADDING_PX;
 | 
			
		||||
    const initialTop = document.body.offsetHeight - this._popupElement.offsetHeight - topPaddingPx;
 | 
			
		||||
    this._popupElement.style.left = `${initialLeft}px`;
 | 
			
		||||
    this._popupElement.style.top = `${initialTop}px`;
 | 
			
		||||
@ -69,61 +78,125 @@ export class FloatingPopup extends Disposable {
 | 
			
		||||
    return this._args;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _rememberPosition() {
 | 
			
		||||
    this._initialLeft = this._popupElement!.offsetLeft;
 | 
			
		||||
    this._initialTop = this._popupElement!.offsetTop;
 | 
			
		||||
    this._initialBottom = this._popupElement!.offsetTop + this._popupElement!.offsetHeight;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleMouseDown(ev: MouseEvent) {
 | 
			
		||||
    if (ev.button !== 0) { return; } // Only handle left-click.
 | 
			
		||||
    this._clientX = ev.clientX;
 | 
			
		||||
    this._clientY = ev.clientY;
 | 
			
		||||
    this._startX = ev.clientX;
 | 
			
		||||
    this._startY = ev.clientY;
 | 
			
		||||
    this._rememberPosition();
 | 
			
		||||
    document.addEventListener('mousemove', this._handleMouseMove);
 | 
			
		||||
    document.addEventListener('mouseup', this._handleMouseUp);
 | 
			
		||||
    this._forceCursor();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleTouchStart(ev: TouchEvent) {
 | 
			
		||||
    this._clientX = ev.touches[0].clientX;
 | 
			
		||||
    this._clientY = ev.touches[0].clientY;
 | 
			
		||||
    this._startX = ev.touches[0].clientX;
 | 
			
		||||
    this._startY = ev.touches[0].clientY;
 | 
			
		||||
    this._rememberPosition();
 | 
			
		||||
    document.addEventListener('touchmove', this._handleTouchMove);
 | 
			
		||||
    document.addEventListener('touchend', this._handleTouchEnd);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleMouseMove({clientX, clientY}: MouseEvent) {
 | 
			
		||||
    this._handleMove(clientX, clientY);
 | 
			
		||||
    this._resize = false;
 | 
			
		||||
    this._forceCursor();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleTouchMove({touches}: TouchEvent) {
 | 
			
		||||
    this._handleMove(touches[0].clientX, touches[0].clientY);
 | 
			
		||||
    this._handleMouseMove(touches[0]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleMouseMove({clientX, clientY}: MouseEvent | Touch) {
 | 
			
		||||
    if (this._resize) {
 | 
			
		||||
      this._handleResize(clientY);
 | 
			
		||||
    } else {
 | 
			
		||||
      this._handleMove(clientX, clientY);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleResize(clientY: number) {
 | 
			
		||||
    const deltaY = clientY - this._startY;
 | 
			
		||||
    if (this._resize && !isNarrowScreen()) {
 | 
			
		||||
      // First calculate the boundaries for the new top.
 | 
			
		||||
 | 
			
		||||
      // First just how much we can resize the popup.
 | 
			
		||||
      let minTop = this._initialBottom - POPUP_MAX_HEIGHT;
 | 
			
		||||
      let maxTop = this._initialBottom - POPUP_MIN_HEIGHT;
 | 
			
		||||
 | 
			
		||||
      // Now how far we can move top (leave at least some padding for mobile).
 | 
			
		||||
      minTop = Math.max(minTop, getTopPopupPaddingPx());
 | 
			
		||||
      // And bottom (we want the header to be visible)
 | 
			
		||||
      maxTop = Math.min(document.body.offsetHeight - POPUP_HEADER_HEIGHT - 2, maxTop);
 | 
			
		||||
 | 
			
		||||
      // Now get new top from those boundaries.
 | 
			
		||||
      const newTop = Math.max(minTop, Math.min(maxTop, this._initialTop + deltaY));
 | 
			
		||||
      // And calculate the new height.
 | 
			
		||||
      const newHeight = this._initialBottom - newTop;
 | 
			
		||||
      this._popupElement!.style.top = `${newTop}px`;
 | 
			
		||||
      this._popupElement!.style.setProperty('--height', `${newHeight}px`);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleMove(clientX: number, clientY: number) {
 | 
			
		||||
    const deltaX = clientX - this._clientX;
 | 
			
		||||
    const deltaY = clientY - this._clientY;
 | 
			
		||||
    let newLeft = this._popupElement!.offsetLeft + deltaX;
 | 
			
		||||
    let newTop = this._popupElement!.offsetTop + deltaY;
 | 
			
		||||
    // Last change in position (from last move).
 | 
			
		||||
    const deltaX = clientX - this._startX;
 | 
			
		||||
    const deltaY = clientY - this._startY;
 | 
			
		||||
 | 
			
		||||
    const topPaddingPx = getTopPopupPaddingPx();
 | 
			
		||||
    if (newLeft - POPUP_PADDING_PX < 0) { newLeft = POPUP_PADDING_PX; }
 | 
			
		||||
    if (newTop - topPaddingPx < 0) { newTop = topPaddingPx; }
 | 
			
		||||
    if (newLeft + POPUP_PADDING_PX > document.body.offsetWidth - this._popupElement!.offsetWidth) {
 | 
			
		||||
      newLeft = document.body.offsetWidth - this._popupElement!.offsetWidth - POPUP_PADDING_PX;
 | 
			
		||||
    }
 | 
			
		||||
    if (newTop + topPaddingPx > document.body.offsetHeight - this._popupElement!.offsetHeight) {
 | 
			
		||||
      newTop = document.body.offsetHeight - this._popupElement!.offsetHeight - topPaddingPx;
 | 
			
		||||
    }
 | 
			
		||||
    // Available space where we can put the popup (anchored at top left corner).
 | 
			
		||||
    const viewPort = {
 | 
			
		||||
      right: document.body.offsetWidth,
 | 
			
		||||
      bottom: document.body.offsetHeight,
 | 
			
		||||
      top: getTopPopupPaddingPx(),
 | 
			
		||||
      left: 0,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Allow some extra space, where we can still move the popup outside the viewport.
 | 
			
		||||
    viewPort.right += this._popupElement!.offsetWidth - (POPUP_HEADER_HEIGHT + 2) * 4;
 | 
			
		||||
    viewPort.left -= this._popupElement!.offsetWidth - (POPUP_HEADER_HEIGHT + 2) * 4;
 | 
			
		||||
    viewPort.bottom += this._popupElement!.offsetHeight - POPUP_HEADER_HEIGHT - 2; // 2px border top
 | 
			
		||||
 | 
			
		||||
    let newLeft = this._initialLeft + deltaX;
 | 
			
		||||
    let newTop = this._initialTop + deltaY;
 | 
			
		||||
    const newRight = (val?: number) => {
 | 
			
		||||
      if (val !== undefined) { newLeft = val - this._popupElement!.offsetWidth; }
 | 
			
		||||
      return newLeft + this._popupElement!.offsetWidth;
 | 
			
		||||
    };
 | 
			
		||||
    const newBottom = (val?: number) => {
 | 
			
		||||
      if (val !== undefined) { newTop = val - this._popupElement!.offsetHeight; }
 | 
			
		||||
      return newTop + this._popupElement!.offsetHeight;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Calculate new position in the padding area.
 | 
			
		||||
    if (newLeft < viewPort.left) { newLeft = viewPort.left; }
 | 
			
		||||
    if (newRight() > viewPort.right) { newRight(viewPort.right); }
 | 
			
		||||
    if (newTop  < viewPort.top) { newTop = viewPort.top; }
 | 
			
		||||
    if (newBottom() > viewPort.bottom) { newBottom(viewPort.bottom); }
 | 
			
		||||
 | 
			
		||||
    this._popupElement!.style.left = `${newLeft}px`;
 | 
			
		||||
    this._popupElement!.style.top = `${newTop}px`;
 | 
			
		||||
    this._clientX = clientX;
 | 
			
		||||
    this._clientY = clientY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleMouseUp() {
 | 
			
		||||
    document.removeEventListener('mousemove', this._handleMouseMove);
 | 
			
		||||
    document.removeEventListener('mouseup', this._handleMouseUp);
 | 
			
		||||
    document.body.removeEventListener('mouseleave', this._handleMouseUp);
 | 
			
		||||
    this._handleMouseEnd();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleTouchEnd() {
 | 
			
		||||
    document.removeEventListener('touchmove', this._handleTouchMove);
 | 
			
		||||
    document.removeEventListener('touchend', this._handleTouchEnd);
 | 
			
		||||
    document.body.removeEventListener('touchcancel', this._handleTouchEnd);
 | 
			
		||||
    this._handleMouseEnd();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleMouseEnd() {
 | 
			
		||||
    this._resize = false;
 | 
			
		||||
    this._cursorGrab?.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleWindowResize() {
 | 
			
		||||
@ -135,10 +208,10 @@ export class FloatingPopup extends Disposable {
 | 
			
		||||
    let newTop = this._popupElement!.offsetTop;
 | 
			
		||||
 | 
			
		||||
    const topPaddingPx = getTopPopupPaddingPx();
 | 
			
		||||
    if (newLeft - POPUP_PADDING_PX < 0) { newLeft = POPUP_PADDING_PX; }
 | 
			
		||||
    if (newLeft - POPUP_INITIAL_PADDING_PX < 0) { newLeft = POPUP_INITIAL_PADDING_PX; }
 | 
			
		||||
    if (newTop - topPaddingPx < 0) { newTop = topPaddingPx; }
 | 
			
		||||
    if (newLeft + POPUP_PADDING_PX > document.body.offsetWidth - this._popupElement!.offsetWidth) {
 | 
			
		||||
      newLeft = document.body.offsetWidth - this._popupElement!.offsetWidth - POPUP_PADDING_PX;
 | 
			
		||||
    if (newLeft + POPUP_INITIAL_PADDING_PX > document.body.offsetWidth - this._popupElement!.offsetWidth) {
 | 
			
		||||
      newLeft = document.body.offsetWidth - this._popupElement!.offsetWidth - POPUP_INITIAL_PADDING_PX;
 | 
			
		||||
    }
 | 
			
		||||
    if (newTop + topPaddingPx > document.body.offsetHeight - this._popupElement!.offsetHeight) {
 | 
			
		||||
      newTop = document.body.offsetHeight - this._popupElement!.offsetHeight - topPaddingPx;
 | 
			
		||||
@ -153,6 +226,17 @@ export class FloatingPopup extends Disposable {
 | 
			
		||||
      {tabIndex: '-1'},
 | 
			
		||||
      cssPopup.cls('-auto', this._options.autoHeight ?? false),
 | 
			
		||||
      cssPopupHeader(
 | 
			
		||||
        cssBottomHandle(testId('move-handle')),
 | 
			
		||||
        dom.maybe(use => !use(this._isMinimized), () => {
 | 
			
		||||
          return cssResizeTopLayer(
 | 
			
		||||
            cssTopHandle(testId('resize-handle')),
 | 
			
		||||
            dom.on('mousedown', () => this._resize = true),
 | 
			
		||||
            dom.on('dblclick', () => {
 | 
			
		||||
              this._popupElement?.style.setProperty('--height', `${POPUP_MAX_HEIGHT}px`);
 | 
			
		||||
              this._repositionPopup();
 | 
			
		||||
            })
 | 
			
		||||
          );
 | 
			
		||||
        }),
 | 
			
		||||
        dom.domComputed(this._isMinimized, isMinimized => {
 | 
			
		||||
          return [
 | 
			
		||||
            // Copy buttons on the left side of the header, to automatically
 | 
			
		||||
@ -223,26 +307,48 @@ export class FloatingPopup extends Disposable {
 | 
			
		||||
 | 
			
		||||
    return body;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _forceCursor() {
 | 
			
		||||
    this._cursorGrab?.dispose();
 | 
			
		||||
    const type = this._resize ? 'ns-resize' : 'grabbing';
 | 
			
		||||
    const cursorStyle: HTMLStyleElement = document.createElement('style');
 | 
			
		||||
    cursorStyle.innerHTML = `*{cursor: ${type}!important;}`;
 | 
			
		||||
    cursorStyle.id = 'cursor-style';
 | 
			
		||||
    document.head.appendChild(cursorStyle);
 | 
			
		||||
    const cursorOwner = {
 | 
			
		||||
      dispose() {
 | 
			
		||||
        if (this.isDisposed()) { return; }
 | 
			
		||||
        document.head.removeChild(cursorStyle);
 | 
			
		||||
      },
 | 
			
		||||
      isDisposed() {
 | 
			
		||||
        return !cursorStyle.isConnected;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    this._cursorGrab = cursorOwner;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function getTopPopupPaddingPx(): number {
 | 
			
		||||
  // On mobile, we need additional padding to avoid blocking the top and bottom bars.
 | 
			
		||||
  return POPUP_PADDING_PX + (isNarrowScreen() ? 50 : 0);
 | 
			
		||||
  return POPUP_INITIAL_PADDING_PX + (isNarrowScreen() ? 50 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const POPUP_HEIGHT = `min(711px, calc(100% - (2 * ${POPUP_PADDING_PX}px)))`;
 | 
			
		||||
const POPUP_HEIGHT_MOBILE = `min(711px, calc(100% - (2 * ${POPUP_PADDING_PX}px) - (2 * 50px)))`;
 | 
			
		||||
const POPUP_WIDTH = `min(436px, calc(100% - (2 * ${POPUP_PADDING_PX}px)))`;
 | 
			
		||||
const POPUP_HEIGHT = `min(var(--height), calc(100% - (2 * ${POPUP_INITIAL_PADDING_PX}px)))`;
 | 
			
		||||
const POPUP_HEIGHT_MOBILE = `min(var(--height), calc(100% - (2 * ${POPUP_INITIAL_PADDING_PX}px) - (2 * 50px)))`;
 | 
			
		||||
const POPUP_WIDTH = `min(436px, calc(100% - (2 * ${POPUP_INITIAL_PADDING_PX}px)))`;
 | 
			
		||||
 | 
			
		||||
const cssPopup = styled('div', `
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
  border: 2px solid ${theme.accentBorder};
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  z-index: 999;
 | 
			
		||||
  --height: ${POPUP_MAX_HEIGHT}px;
 | 
			
		||||
  height: ${POPUP_HEIGHT};
 | 
			
		||||
  width: ${POPUP_WIDTH};
 | 
			
		||||
  min-height: ${POPUP_MIN_HEIGHT}px;
 | 
			
		||||
  background-color: ${theme.popupBg};
 | 
			
		||||
  box-shadow: 0 2px 18px 0 ${theme.popupInnerShadow}, 0 0 1px 0 ${theme.popupOuterShadow};
 | 
			
		||||
  outline: unset;
 | 
			
		||||
@ -254,6 +360,7 @@ const cssPopup = styled('div', `
 | 
			
		||||
  &-minimized {
 | 
			
		||||
    max-width: 225px;
 | 
			
		||||
    height: unset;
 | 
			
		||||
    min-height: unset;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &-minimized:not(&-mobile) {
 | 
			
		||||
@ -267,6 +374,7 @@ const cssPopup = styled('div', `
 | 
			
		||||
  &-auto {
 | 
			
		||||
    height: auto;
 | 
			
		||||
    max-height: ${POPUP_HEIGHT};
 | 
			
		||||
    min-height: unset;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &-auto&-mobile {
 | 
			
		||||
@ -283,11 +391,12 @@ const cssPopupHeader = styled('div', `
 | 
			
		||||
  cursor: grab;
 | 
			
		||||
  padding-left: 4px;
 | 
			
		||||
  padding-right: 4px;
 | 
			
		||||
  height: 30px;
 | 
			
		||||
  height: ${POPUP_HEADER_HEIGHT}px;
 | 
			
		||||
  user-select: none;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  isolation: isolate;
 | 
			
		||||
  &:active {
 | 
			
		||||
    cursor: grabbing;
 | 
			
		||||
  }
 | 
			
		||||
@ -323,8 +432,36 @@ const cssPopupHeaderButton = styled('div', `
 | 
			
		||||
  padding: 4px;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  z-index: 1000;
 | 
			
		||||
 | 
			
		||||
  &:hover {
 | 
			
		||||
    background-color: ${theme.hover};
 | 
			
		||||
  }
 | 
			
		||||
`);
 | 
			
		||||
 | 
			
		||||
const cssResizeTopLayer = styled('div', `
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  bottom: 70%;
 | 
			
		||||
  z-index: 500;
 | 
			
		||||
  cursor: ns-resize;
 | 
			
		||||
`);
 | 
			
		||||
 | 
			
		||||
const cssTopHandle = styled('div', `
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  width: 1px;
 | 
			
		||||
  height: 1px;
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
  user-select: none;
 | 
			
		||||
  visibility: hidden;
 | 
			
		||||
`);
 | 
			
		||||
 | 
			
		||||
const cssBottomHandle = styled(cssTopHandle, `
 | 
			
		||||
  top: unset;
 | 
			
		||||
  bottom: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
`);
 | 
			
		||||
 | 
			
		||||
@ -108,6 +108,160 @@ describe('DocTutorial', function () {
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('can be moved around and minimized', async function() {
 | 
			
		||||
      // Get the initial position of the popup.
 | 
			
		||||
      const initialDims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      // Get the move handle.
 | 
			
		||||
      const mover = await driver.find('.test-floating-popup-move-handle');
 | 
			
		||||
      const moverInitial = await mover.getRect();
 | 
			
		||||
 | 
			
		||||
      const move = async (pos: {x?: number, y?: number}) => driver.withActions((actions) => actions
 | 
			
		||||
        .move({origin: driver.find('.test-floating-popup-move-handle')})
 | 
			
		||||
        .press()
 | 
			
		||||
        .move({origin: driver.find('.test-floating-popup-move-handle'), ...pos})
 | 
			
		||||
        .release()
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const resize = async (pos: {x?: number, y?: number}) => driver.withActions((actions) => actions
 | 
			
		||||
        .move({origin: driver.find('.test-floating-popup-resize-handle')})
 | 
			
		||||
        .press()
 | 
			
		||||
        .move({origin: driver.find('.test-floating-popup-resize-handle'), ...pos})
 | 
			
		||||
        .release()
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // Move it a little bit down.
 | 
			
		||||
      await move({y: 100, x: -10});
 | 
			
		||||
 | 
			
		||||
      // Check it is moved but not shrinked.
 | 
			
		||||
      let dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.height, initialDims.height);
 | 
			
		||||
      // And moving down.
 | 
			
		||||
      assert.equal(dims.y, initialDims.y + 100);
 | 
			
		||||
      assert.equal(dims.x, initialDims.x - 10);
 | 
			
		||||
 | 
			
		||||
      // Now move it a little up and test it doesn't grow.
 | 
			
		||||
      await move({y: -100});
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.height, initialDims.height);
 | 
			
		||||
      assert.equal(dims.y, initialDims.y);
 | 
			
		||||
 | 
			
		||||
      // Resize it in steps.
 | 
			
		||||
      await resize({y: 10});
 | 
			
		||||
      await resize({y: 10});
 | 
			
		||||
      await resize({y: 10});
 | 
			
		||||
      await resize({y: 10});
 | 
			
		||||
      await resize({y: 10});
 | 
			
		||||
      await resize({y: 50});
 | 
			
		||||
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.height, initialDims.height - 100);
 | 
			
		||||
      assert.equal(dims.y, initialDims.y + 100);
 | 
			
		||||
 | 
			
		||||
      // Resize back (in steps, to simulate user actions)
 | 
			
		||||
      await resize({y: -20});
 | 
			
		||||
      await resize({y: -20});
 | 
			
		||||
      await resize({y: -20});
 | 
			
		||||
      await resize({y: -40});
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.height, initialDims.height);
 | 
			
		||||
      assert.equal(dims.y, initialDims.y);
 | 
			
		||||
 | 
			
		||||
      // Now resize it beyond the maximum size and check it doesn't move.
 | 
			
		||||
      await resize({y: -10});
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.height, initialDims.height);
 | 
			
		||||
      assert.equal(dims.y, initialDims.y);
 | 
			
		||||
 | 
			
		||||
      // Now resize it to the minimum size.
 | 
			
		||||
      await resize({y: 700});
 | 
			
		||||
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.height, 300);
 | 
			
		||||
 | 
			
		||||
      // Get window inner size.
 | 
			
		||||
      const windowHeight: any = await driver.executeScript('return window.innerHeight');
 | 
			
		||||
      const windowWidth: any = await driver.executeScript('return window.innerWidth');
 | 
			
		||||
      assert.equal(dims.y + dims.height, windowHeight - 16);
 | 
			
		||||
 | 
			
		||||
      // Now move it offscreen as low as possible.
 | 
			
		||||
      await move({y: windowHeight - dims.y - 10});
 | 
			
		||||
 | 
			
		||||
      // Make sure it is still visible.
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.y, windowHeight - 32); // 32px is a header size
 | 
			
		||||
      assert.isBelow(dims.x, windowWidth - (32 * 4) + 1); // 120px is the right overflow value.
 | 
			
		||||
 | 
			
		||||
      // Now move it to the right as far as possible.
 | 
			
		||||
      await move({x: windowWidth - dims.x - 10});
 | 
			
		||||
 | 
			
		||||
      // Make sure it is still visible.
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.y, windowHeight - 32);
 | 
			
		||||
      assert.equal(dims.x, windowWidth - 32 * 4);
 | 
			
		||||
 | 
			
		||||
      // Now move it to the left as far as possible.
 | 
			
		||||
      await move({x: -3000});
 | 
			
		||||
 | 
			
		||||
      // Make sure it is still visible.
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.isBelow(dims.x, 0);
 | 
			
		||||
      assert.isAbove(dims.x + dims.width, 30);
 | 
			
		||||
 | 
			
		||||
      const miniButton = driver.find(".test-floating-popup-minimize-maximize");
 | 
			
		||||
      // Now move it back, but this time manually as the move handle is off screen.
 | 
			
		||||
      await driver.withActions((a) => a
 | 
			
		||||
        .move({origin: miniButton })
 | 
			
		||||
        .press()
 | 
			
		||||
        .move({origin: miniButton, x: Math.abs(dims.x) + 20})
 | 
			
		||||
        .release()
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // Maximize it (it was minimized as we used the button to move it).
 | 
			
		||||
      await driver.find(".test-floating-popup-minimize-maximize").click();
 | 
			
		||||
 | 
			
		||||
      // Now move it to the top as far as possible.
 | 
			
		||||
      // Move it a little right, so that we don't end up on the logo. Driver is clicking logo sometimes.
 | 
			
		||||
      await move({y: -windowHeight, x: 100});
 | 
			
		||||
 | 
			
		||||
      // Make sure it is still visible.
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.y, 16);
 | 
			
		||||
      assert.isAbove(dims.x, 100);
 | 
			
		||||
      assert.isBelow(dims.x, windowWidth);
 | 
			
		||||
 | 
			
		||||
      // Move back where it was.
 | 
			
		||||
      let moverNow = await driver.find('.test-floating-popup-move-handle').getRect();
 | 
			
		||||
      await move({x: moverInitial.x - moverNow.x});
 | 
			
		||||
      // And restore the size by double clicking the resizer.
 | 
			
		||||
      await driver.withActions((a) => a.doubleClick(driver.find('.test-floating-popup-resize-handle')));
 | 
			
		||||
 | 
			
		||||
      // Now test if we can't resize it offscreen.
 | 
			
		||||
      await move({y: 10000});
 | 
			
		||||
      await move({y: -100});
 | 
			
		||||
      // Header is about 100px above the viewport.
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.isBelow(dims.y, windowHeight);
 | 
			
		||||
      assert.isAbove(dims.x, windowHeight - 140);
 | 
			
		||||
 | 
			
		||||
      // Now resize as far as possible.
 | 
			
		||||
      await resize({y: 10});
 | 
			
		||||
      await resize({y: 300});
 | 
			
		||||
 | 
			
		||||
      // Make sure it is still visible.
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.isBelow(dims.y, windowHeight - 16);
 | 
			
		||||
 | 
			
		||||
      // Now move back and resize.
 | 
			
		||||
      moverNow = await driver.find('.test-floating-popup-move-handle').getRect();
 | 
			
		||||
      await move({x: moverInitial.x - moverNow.x, y: moverInitial.y - moverNow.y});
 | 
			
		||||
      await driver.withActions((a) => a.doubleClick(driver.find('.test-floating-popup-resize-handle')));
 | 
			
		||||
 | 
			
		||||
      dims = await driver.find('.test-floating-popup-window').getRect();
 | 
			
		||||
      assert.equal(dims.height, initialDims.height);
 | 
			
		||||
      assert.equal(dims.y, initialDims.y);
 | 
			
		||||
      assert.equal(dims.x, initialDims.x);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('is visible on all pages', async function() {
 | 
			
		||||
      for (const page of ['access-rules', 'raw', 'code', 'settings']) {
 | 
			
		||||
        await driver.find(`.test-tools-${page}`).click();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user