Skip to content

Commit b57c6db

Browse files
committedJun 8, 2023
[Feature] Lazy load overlays: ViewControlsOverlay, MenuOverlay, ZoomOverlay, PageManipulationOverlay (#6922)
1 parent f19b2e2 commit b57c6db

File tree

24 files changed

+404
-192
lines changed

24 files changed

+404
-192
lines changed
 
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { loadViewerSample } from '../../playwright-utils';
2+
import { expect, test } from '@playwright/test';
3+
4+
test.describe('Flyout Menu', () => {
5+
test('should create flyout menus with the right size and position', async ({ page }) => {
6+
const { iframe, waitForInstance } = await loadViewerSample(page, 'viewing/blank');
7+
await waitForInstance();
8+
await page.waitForTimeout(5000);
9+
10+
const buttons = [
11+
'menuButton',
12+
'viewControlsButton',
13+
'zoomOverlayButton',
14+
'pageManipulationOverlayButton',
15+
];
16+
await iframe.click('[data-element=leftPanelButton]');
17+
await page.waitForTimeout(1000);
18+
for (let i = 0; i < buttons.length; i++) {
19+
const button = await iframe.$(`button[data-element=${buttons[i]}]`);
20+
await button.click();
21+
const currentFlyoutMenu = await iframe.$('.Overlay.FlyoutMenu');
22+
const body = await iframe.$('body');
23+
const flyoutMenuPos = await currentFlyoutMenu.boundingBox();
24+
const buttonPos = await button.boundingBox();
25+
const bodyPos = await body.boundingBox();
26+
if (buttons[i] === 'viewControlsOverlay') {
27+
expect(flyoutMenuPos.y > buttonPos.y).toBe(true);
28+
}
29+
30+
expect(flyoutMenuPos.x >= 0).toBe(true);
31+
expect(flyoutMenuPos.y >= 0).toBe(true);
32+
expect(flyoutMenuPos.x + flyoutMenuPos.width <= bodyPos.x + bodyPos.width).toBe(true);
33+
expect(flyoutMenuPos.y + flyoutMenuPos.height <= bodyPos.y + bodyPos.height).toBe(true);
34+
await body.click();
35+
}
36+
});
37+
});

‎src/components/App/App.js

+18-8
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ import selectors from 'selectors';
77
import core from 'core';
88
import actions from 'actions';
99

10+
import LogoBar from 'components/LogoBar';
1011
import Accessibility from 'components/Accessibility';
1112
import Header from 'components/Header';
12-
import ViewControlsOverlay from 'components/ViewControlsOverlay';
13-
import MenuOverlay from 'components/MenuOverlay';
1413
import AnnotationContentOverlay from 'components/AnnotationContentOverlay';
1514
import DocumentContainer from 'components/DocumentContainer';
1615
import LeftPanel from 'components/LeftPanel';
@@ -19,8 +18,6 @@ import FilePickerHandler from 'components/FilePickerHandler';
1918
import CopyTextHandler from 'components/CopyTextHandler';
2019
import PrintHandler from 'components/PrintHandler';
2120
import FontHandler from 'components/FontHandler';
22-
import ZoomOverlay from 'components/ZoomOverlay';
23-
import PageManipulationOverlay from 'components/PageManipulationOverlay';
2421
import RedactionPanel from 'components/RedactionPanel';
2522
import TextEditingPanel from 'components/TextEditingPanel';
2623
import Wv3dPropertiesPanel from 'components/Wv3dPropertiesPanel';
@@ -357,11 +354,23 @@ const App = ({ removeEventHandlers }) => {
357354
)}
358355
<BottomHeader />
359356
</div>
360-
<ViewControlsOverlay />
361-
<MenuOverlay />
362-
<ZoomOverlay />
357+
<LazyLoadWrapper
358+
Component={LazyLoadComponents.ViewControlsOverlay}
359+
dataElement={DataElements.VIEW_CONTROLS_OVERLAY}
360+
/>
361+
<LazyLoadWrapper
362+
Component={LazyLoadComponents.MenuOverlay}
363+
dataElement={DataElements.MENU_OVERLAY}
364+
/>
365+
<LazyLoadWrapper
366+
Component={LazyLoadComponents.ZoomOverlay}
367+
dataElement={DataElements.ZOOM_OVERLAY}
368+
/>
363369
<AnnotationContentOverlay />
364-
<PageManipulationOverlay />
370+
<LazyLoadWrapper
371+
Component={LazyLoadComponents.PageManipulationOverlay}
372+
dataElement={DataElements.PAGE_MANIPULATION_OVERLAY}
373+
/>
365374
<LeftPanelOverlayContainer />
366375
<FormFieldIndicatorContainer />
367376
{/* Popups */}
@@ -453,6 +462,7 @@ const App = ({ removeEventHandlers }) => {
453462
dataElement={DataElements.SIGNATURE_VALIDATION_MODAL}
454463
/>
455464
)}
465+
<LogoBar />
456466
</div>
457467

458468
<PrintHandler />

‎src/components/FlyoutMenu/FlyoutMenu.js

+33-23
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,44 @@
11
import actions from 'actions';
22
import classNames from 'classnames';
33
import getOverlayPositionBasedOn from 'helpers/getOverlayPositionBasedOn';
4-
import useMedia from 'hooks/useMedia';
4+
import { isMobileSize, isTabletAndMobileSize } from 'helpers/getDeviceSize';
55
import useOnClickOutside from 'hooks/useOnClickOutside';
66
import PropTypes from 'prop-types';
77
import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
88
import { useDispatch, useSelector } from 'react-redux';
99
import { Swipeable } from 'react-swipeable';
1010
import selectors from 'selectors';
1111
import useArrowFocus from '../../hooks/useArrowFocus';
12+
import getRootNode from 'helpers/getRootNode';
13+
import DataElements from 'constants/dataElement';
1214
import './FlyoutMenu.scss';
1315

1416
const MENUS = [
15-
'menuOverlay',
17+
DataElements.MENU_OVERLAY,
1618
'groupOverlay',
17-
'viewControlsOverlay',
19+
DataElements.VIEW_CONTROLS_OVERLAY,
1820
'searchOverlay',
1921
'signatureOverlay',
20-
'zoomOverlay',
22+
DataElements.ZOOM_OVERLAY,
2123
'zoomOverlay1',
2224
'zoomOverlay2',
2325
'redactionOverlay',
2426
'toolStylePopup',
25-
'pageManipulationOverlay',
27+
DataElements.PAGE_MANIPULATION_OVERLAY,
2628
'thumbnailsControlRotatePopup',
27-
'thumbnailsControlInsertPopup',
2829
'thumbnailsControlManipulatePopup',
2930
'thumbnailsControlManipulatePopupSmall',
3031
'tabMenu',
3132
];
3233

3334
const TRIGGERS = [
34-
'menuButton',
35-
'viewControlsButton',
36-
'zoomOverlayButton',
35+
DataElements.MENU_OVERLAY_BUTTON,
36+
DataElements.VIEW_CONTROLS_OVERLAY_BUTTON,
37+
DataElements.ZOOM_OVERLAY_BUTTON,
3738
'zoomOverlayButton1',
3839
'zoomOverlayButton2',
39-
'pageManipulationOverlayButton',
40+
DataElements.PAGE_MANIPULATION_OVERLAY_BUTTON,
4041
'thumbnailsControlRotatePopupTrigger',
41-
'thumbnailsControlInsertPopupTrigger',
4242
'thumbnailsControlManipulatePopupTrigger',
4343
'thumbnailsControlManipulatePopupSmallTrigger',
4444
'tabTrigger',
@@ -57,6 +57,8 @@ const propTypes = {
5757
ariaLabel: PropTypes.string,
5858
};
5959

60+
const viewerHeight = window.isApryseWebViewerWebComponent ? getRootNode()?.host.clientHeight : window.innerHeight;
61+
6062
function FlyoutMenu({ menu, trigger, onClose, children, ariaLabel }) {
6163
const dispatch = useDispatch();
6264

@@ -66,7 +68,8 @@ function FlyoutMenu({ menu, trigger, onClose, children, ariaLabel }) {
6668
const isOpen = useSelector((state) => selectors.isElementOpen(state, menu));
6769

6870
const isInDesktopOnlyMode = useSelector((state) => selectors.isInDesktopOnlyMode(state));
69-
const pageManipulationOverlayAlternativePosition = useSelector((state) => selectors.getPageManipulationOverlayAlternativePosition(state));
71+
const pageManipulationOverlayAlternativePosition = useSelector((state) => selectors.getPageManipulationOverlayAlternativePosition(state),
72+
);
7073

7174
const closeMenu = useCallback(() => {
7275
dispatch(actions.closeElements([menu]));
@@ -78,7 +81,7 @@ function FlyoutMenu({ menu, trigger, onClose, children, ariaLabel }) {
7881

7982
const onClickOutside = useCallback(
8083
(e) => {
81-
const menuButton = document.querySelector(`[data-element="${trigger}"]`);
84+
const menuButton = getRootNode().querySelector(`[data-element="${trigger}"]`);
8285
const clickedMenuButton = menuButton?.contains(e.target);
8386
if (!clickedMenuButton) {
8487
closeMenu();
@@ -89,12 +92,12 @@ function FlyoutMenu({ menu, trigger, onClose, children, ariaLabel }) {
8992
useOnClickOutside(overlayRef, onClickOutside);
9093

9194
const [position, setPosition] = useState(() => ({ left: 0, right: 'auto', top: 'auto' }));
92-
const isMobile = useMedia(['(max-width: 640px)'], [true], false);
93-
const isTabletOrMobile = useMedia(['(max-width: 900px)'], [true], false);
95+
const isMobile = isMobileSize();
96+
const isTabletAndMobile = isTabletAndMobileSize();
9497

9598
const getAlternativePosition = () => {
9699
const alternativePosition = pageManipulationOverlayAlternativePosition;
97-
const verticalGap = isMobile && isTabletOrMobile ? 14 : 6;
100+
const verticalGap = isMobile && isTabletAndMobile ? 14 : 6;
98101
const clickTop = alternativePosition.top;
99102
let top = clickTop + verticalGap;
100103
if (clickTop > 100) {
@@ -103,8 +106,8 @@ function FlyoutMenu({ menu, trigger, onClose, children, ariaLabel }) {
103106
* if the right-click click was not on the top of the page, we should check if the popup will fit on the
104107
* the viewport and, if not, can adjust its position to "pass" the click position, otherwise the popup should always be below them
105108
*/
106-
if (clickTop + flyoutMenuHeight > window.innerHeight) {
107-
const calculatedTop = window.innerHeight - flyoutMenuHeight - verticalGap;
109+
if (clickTop + flyoutMenuHeight > viewerHeight) {
110+
const calculatedTop = viewerHeight - flyoutMenuHeight - verticalGap;
108111
top = calculatedTop > 0 ? calculatedTop : 0;
109112
alternativePosition.top = top;
110113
}
@@ -122,11 +125,11 @@ function FlyoutMenu({ menu, trigger, onClose, children, ariaLabel }) {
122125
const onResize = () => {
123126
let overlayAlternativePosition;
124127

125-
if (trigger === 'pageManipulationOverlayButton' && pageManipulationOverlayAlternativePosition) {
128+
if (trigger === DataElements.PAGE_MANIPULATION_OVERLAY_BUTTON && pageManipulationOverlayAlternativePosition) {
126129
overlayAlternativePosition = getAlternativePosition();
127130
} else {
128-
overlayAlternativePosition = getOverlayPositionBasedOn(trigger, overlayRef, isMobile && isTabletOrMobile);
129-
overlayAlternativePosition.maxHeight = window.innerHeight - overlayAlternativePosition.top;
131+
overlayAlternativePosition = getOverlayPositionBasedOn(trigger, overlayRef, isMobile && isTabletAndMobile);
132+
overlayAlternativePosition.maxHeight = viewerHeight - overlayAlternativePosition.top;
130133
}
131134
setPosition(overlayAlternativePosition);
132135
};
@@ -135,7 +138,7 @@ function FlyoutMenu({ menu, trigger, onClose, children, ariaLabel }) {
135138
window.addEventListener('resize', onResize);
136139
return () => window.removeEventListener('resize', onResize);
137140
}
138-
}, [allOtherMenus, dispatch, isOpen, isTabletOrMobile, trigger, isMobile]);
141+
}, [allOtherMenus, dispatch, isOpen, isTabletAndMobile, trigger, isMobile]);
139142

140143
if (isDisabled) {
141144
return null;
@@ -148,7 +151,14 @@ function FlyoutMenu({ menu, trigger, onClose, children, ariaLabel }) {
148151

149152
return (
150153
<Swipeable onSwipedUp={closeMenu} onSwipedDown={closeMenu} preventDefaultTouchmoveEvent>
151-
<div className={overlayClass} data-element={menu} style={(!isMobile || isInDesktopOnlyMode) ? position : undefined} ref={overlayRef} role="listbox" aria-label={ariaLabel}>
154+
<div
155+
className={overlayClass}
156+
data-element={menu}
157+
style={!isMobile || isInDesktopOnlyMode ? position : undefined}
158+
ref={overlayRef}
159+
role="listbox"
160+
aria-label={ariaLabel}
161+
>
152162
{isMobile && !isInDesktopOnlyMode && <div className="swipe-indicator" />}
153163
{children}
154164
</div>

‎src/components/LazyLoadWrapper/LazyLoadComponents.js

+19-2
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@ import { lazy } from 'react';
22

33
// Overlays
44
const ScaleOverlayContainer = lazy(() => import('components/ScaleOverlay/ScaleOverlayContainer'));
5+
const MeasurementOverlay = lazy(() => import('components/MeasurementOverlay'));
6+
const ViewControlsOverlay = lazy(() => import('components/ViewControlsOverlay'));
7+
const MenuOverlay = lazy(() => import('components/MenuOverlay'));
8+
const ZoomOverlay = lazy(() => import('components/ZoomOverlay'));
9+
const PageManipulationOverlay = lazy(() => import('components/PageManipulationOverlay'));
510

611
// Popups
712
const AnnotationPopup = lazy(() => import('components/AnnotationPopup'));
813
const FormFieldEditPopup = lazy(() => import('components/FormFieldEditPopup'));
914
const TextPopup = lazy(() => import('components/TextPopup'));
1015
const ContextMenuPopup = lazy(() => import('components/ContextMenuPopup'));
11-
const InlineCommentingPopup = lazy(() => import('components/InlineCommentingPopup'));
1216
const RichTextPopup = lazy(() => import('components/RichTextPopup'));
1317
const AudioPlaybackPopup = lazy(() => import('components/AudioPlaybackPopup'));
1418
const DocumentCropPopup = lazy(() => import('components/DocumentCropPopup'));
15-
const MeasurementOverlay = lazy(() => import('components/MeasurementOverlay'));
19+
const InlineCommentingPopup = lazy(() => import('components/InlineCommentingPopup'));
1620

1721
// Modals
1822
const ContentEditModal = lazy(() => import('components/ContentEditModal'));
@@ -40,6 +44,12 @@ const OpenFileModal = lazy(() => import('components/OpenFileModal'));
4044
const SignatureValidationModal = lazy(() => import('components/SignatureValidationModal'));
4145
const CustomModal = lazy(() => import('components/CustomModal'));
4246

47+
// Panels
48+
const NotesPanel = lazy(() => import('components/NotesPanel'));
49+
const SearchPanel = lazy(() => import('components/SearchPanel'));
50+
51+
const OfficeEditorToolsHeader = lazy(() => import('components/Header/OfficeEditorToolsHeader'));
52+
4353
const LazyLoadComponents = {
4454
AnnotationPopup,
4555
FormFieldEditPopup,
@@ -51,6 +61,10 @@ const LazyLoadComponents = {
5161
DocumentCropPopup,
5262
MeasurementOverlay,
5363
ScaleOverlayContainer,
64+
ViewControlsOverlay,
65+
MenuOverlay,
66+
ZoomOverlay,
67+
PageManipulationOverlay,
5468
ContentEditModal,
5569
ContentEditLinkModal,
5670
SignatureModal,
@@ -75,6 +89,9 @@ const LazyLoadComponents = {
7589
OpenFileModal,
7690
SignatureValidationModal,
7791
CustomModal,
92+
NotesPanel,
93+
SearchPanel,
94+
OfficeEditorToolsHeader,
7895
};
7996

8097
export default LazyLoadComponents;

‎src/components/LeftPanelPageTabs/LeftPanelPageTabsContainer.js

+21-20
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,49 @@
11
import React from 'react';
2-
import { useDispatch, useSelector } from "react-redux";
2+
import { useDispatch, useSelector } from 'react-redux';
33
import selectors from 'selectors';
4+
import actions from 'actions';
45
import {
56
deletePages,
67
extractPages,
7-
insertAbove,
8-
insertBelow,
98
noPagesSelectedWarning,
109
replace,
1110
rotateClockwise,
1211
rotateCounterClockwise,
1312
movePagesToBottom,
1413
movePagesToTop
15-
} from "helpers/pageManipulationFunctions";
16-
import LeftPanelPageTabsSmall from "src/components/LeftPanelPageTabs/LeftPanelPageTabsSmall/LeftPanelPageTabsSmall";
17-
import LeftPanelPageTabs from "components/LeftPanelPageTabs/LeftPanelPageTabs/LeftPanelPageTabs";
18-
import { workerTypes } from "constants/types";
19-
import core from "src/core";
20-
import LeftPanelPageTabsRotate from "components/LeftPanelPageTabs/LeftPanelPageTabsRotate/LeftPanelPageTabsRotate";
14+
} from 'helpers/pageManipulationFunctions';
15+
import LeftPanelPageTabsSmall from 'src/components/LeftPanelPageTabs/LeftPanelPageTabsSmall/LeftPanelPageTabsSmall';
16+
import LeftPanelPageTabs from 'components/LeftPanelPageTabs/LeftPanelPageTabs/LeftPanelPageTabs';
17+
import { workerTypes } from 'constants/types';
18+
import core from 'src/core';
19+
import LeftPanelPageTabsRotate from 'components/LeftPanelPageTabs/LeftPanelPageTabsRotate/LeftPanelPageTabsRotate';
2120
import LeftPanelPageTabsLarge from './LeftPanelPageTabsLarge/LeftPanelPageTabsLarge';
21+
import DataElements from 'constants/dataElement';
2222

2323
function LeftPanelPageTabsContainer() {
2424
const dispatch = useDispatch();
25-
const [selectedPageIndexes, leftPanelWidth, deleteModalEnabled, multiPageManipulationControlsItems, multiPageManipulationControlsSmall, multiPageManipulationControlsLarge] = useSelector(state => [
25+
const [selectedPageIndexes, leftPanelWidth, deleteModalEnabled, multiPageManipulationControlsItems, multiPageManipulationControlsSmall, multiPageManipulationControlsLarge] = useSelector((state) => [
2626
selectors.getSelectedThumbnailPageIndexes(state),
2727
selectors.getLeftPanelWidth(state),
2828
selectors.pageDeletionConfirmationModalEnabled(state),
2929
selectors.getMultiPageManipulationControlsItems(state),
3030
selectors.getMultiPageManipulationControlsItemsSmall(state),
31-
selectors.getMultiPageManipulationControlsItemsLarge(state)
31+
selectors.getMultiPageManipulationControlsItemsLarge(state),
3232
]);
3333

34-
const pageNumbers = selectedPageIndexes.map(index => index + 1);
34+
const pageNumbers = selectedPageIndexes.map((index) => index + 1);
35+
36+
const openInsertPageModal = () => {
37+
dispatch(actions.closeElement(DataElements.PAGE_MANIPULATION_OVERLAY));
38+
dispatch(actions.openElement('insertPageModal'));
39+
};
3540

3641
const onReplace = () => !noPagesSelectedWarning(pageNumbers, dispatch) && replace(dispatch);
3742
const onExtractPages = () => !noPagesSelectedWarning(pageNumbers, dispatch) && extractPages(pageNumbers, dispatch);
3843
const onDeletePages = () => !noPagesSelectedWarning(pageNumbers, dispatch) && deletePages(pageNumbers, dispatch, deleteModalEnabled);
3944
const onRotateClockwise = () => !noPagesSelectedWarning(pageNumbers, dispatch) && rotateClockwise(pageNumbers);
4045
const onRotateCounterClockwise = () => !noPagesSelectedWarning(pageNumbers, dispatch) && rotateCounterClockwise(pageNumbers);
41-
const onInsertAbove = () => !noPagesSelectedWarning(pageNumbers, dispatch) && insertAbove(pageNumbers);
42-
const onInsertBelow = () => !noPagesSelectedWarning(pageNumbers, dispatch) && insertBelow(pageNumbers);
46+
const onInsert = () => !noPagesSelectedWarning(pageNumbers, dispatch) && openInsertPageModal();
4347
const moveToTop = () => !noPagesSelectedWarning(pageNumbers, dispatch) && movePagesToTop(pageNumbers);
4448
const moveToBottom = () => !noPagesSelectedWarning(pageNumbers, dispatch) && movePagesToBottom(pageNumbers);
4549

@@ -65,8 +69,7 @@ function LeftPanelPageTabsContainer() {
6569
onDeletePages={onDeletePages}
6670
onRotateCounterClockwise={onRotateCounterClockwise}
6771
onRotateClockwise={onRotateClockwise}
68-
onInsertAbove={onInsertAbove}
69-
onInsertBelow={onInsertBelow}
72+
onInsert={onInsert}
7073
pageNumbers={pageNumbers}
7174
multiPageManipulationControlsItemsSmall={multiPageManipulationControlsSmall}
7275
/>;
@@ -79,8 +82,7 @@ function LeftPanelPageTabsContainer() {
7982
onDeletePages={onDeletePages}
8083
onRotateCounterClockwise={onRotateCounterClockwise}
8184
onRotateClockwise={onRotateClockwise}
82-
onInsertAbove={onInsertAbove}
83-
onInsertBelow={onInsertBelow}
85+
onInsert={onInsert}
8486
moveToTop={moveToTop}
8587
moveToBottom={moveToBottom}
8688
pageNumbers={pageNumbers}
@@ -96,8 +98,7 @@ function LeftPanelPageTabsContainer() {
9698
onDeletePages={onDeletePages}
9799
onRotateCounterClockwise={onRotateCounterClockwise}
98100
onRotateClockwise={onRotateClockwise}
99-
onInsertAbove={onInsertAbove}
100-
onInsertBelow={onInsertBelow}
101+
onInsert={onInsert}
101102
pageNumbers={pageNumbers}
102103
multiPageManipulationControlsItems={multiPageManipulationControlsItems}
103104
/>

0 commit comments

Comments
 (0)