Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(controls): Add react versions of findbar and sidebar controls #1288

Merged
merged 2 commits into from
Nov 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/lib/viewers/controls/findbar/FindBarToggle.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import '../styles';

.bp-FindBarToggle {
@include bp-ControlButton;
}
15 changes: 15 additions & 0 deletions src/lib/viewers/controls/findbar/FindBarToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import IconSearch18 from '../icons/IconSearch18';
import './FindBarToggle.scss';

export type Props = {
onFindBarToggle: () => void;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the onFindBarToggle too repetitive? How about onToggle?

};

export default function FindBarToggle({ onFindBarToggle }: Props): JSX.Element {
return (
<button className="bp-FindBarToggle" onClick={onFindBarToggle} title={__('toggle_findbar')} type="button">
<IconSearch18 />
</button>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import FindBarToggle from '../FindBarToggle';
import IconSearch18 from '../../icons/IconSearch18';

describe('FindBarToggle', () => {
const getWrapper = (props = {}): ShallowWrapper =>
shallow(<FindBarToggle onFindBarToggle={jest.fn()} {...props} />);

describe('event handlers', () => {
test('should forward the click from the button', () => {
const onToggle = jest.fn();
const wrapper = getWrapper({ onFindBarToggle: onToggle });

wrapper.simulate('click');

expect(onToggle).toBeCalled();
});
});

describe('render', () => {
test('should return a valid wrapper', () => {
const wrapper = getWrapper();

expect(wrapper.hasClass('bp-FindBarToggle')).toBe(true);
expect(wrapper.exists(IconSearch18)).toBe(true);
});
});
});
2 changes: 2 additions & 0 deletions src/lib/viewers/controls/findbar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './FindBarToggle';
export { default } from './FindBarToggle';
4 changes: 2 additions & 2 deletions src/lib/viewers/controls/icons/IconSearch18.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';

function IconSearch(props: React.SVGProps<SVGSVGElement>): JSX.Element {
function IconSearch18(props: React.SVGProps<SVGSVGElement>): JSX.Element {
return (
<svg focusable="false" height={18} viewBox="0 0 15 14" width={18} {...props}>
<g fill="none" fillRule="evenodd">
Expand All @@ -15,4 +15,4 @@ function IconSearch(props: React.SVGProps<SVGSVGElement>): JSX.Element {
);
}

export default IconSearch;
export default IconSearch18;
4 changes: 2 additions & 2 deletions src/lib/viewers/controls/icons/IconThumbnailsToggle18.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';

function IconThumbnailsToggle(props: React.SVGProps<SVGSVGElement>): JSX.Element {
function IconThumbnailsToggle18(props: React.SVGProps<SVGSVGElement>): JSX.Element {
return (
<svg focusable="false" height={18} viewBox="0 0 18 18" width={18} {...props}>
<path
Expand All @@ -12,4 +12,4 @@ function IconThumbnailsToggle(props: React.SVGProps<SVGSVGElement>): JSX.Element
);
}

export default IconThumbnailsToggle;
export default IconThumbnailsToggle18;
5 changes: 5 additions & 0 deletions src/lib/viewers/controls/sidebar/ThumbnailsToggle.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import '../styles';

.bp-ThumbnailsToggle {
@include bp-ControlButton;
}
20 changes: 20 additions & 0 deletions src/lib/viewers/controls/sidebar/ThumbnailsToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import IconThumbnailsToggle18 from '../icons/IconThumbnailsToggle18';
import './ThumbnailsToggle.scss';

export type Props = {
onThumbnailsToggle: () => void;
};

export default function ThumbnailsToggle({ onThumbnailsToggle }: Props): JSX.Element {
return (
<button
className="bp-ThumbnailsToggle"
onClick={onThumbnailsToggle}
title={__('toggle_thumbnails')}
type="button"
>
<IconThumbnailsToggle18 />
</button>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import IconThumbnailsToggle18 from '../../icons/IconThumbnailsToggle18';
import ThumbnailsToggle from '../ThumbnailsToggle';

describe('ThumbnailsToggle', () => {
const getWrapper = (props = {}): ShallowWrapper =>
shallow(<ThumbnailsToggle onThumbnailsToggle={jest.fn()} {...props} />);

describe('event handlers', () => {
test('should forward the click from the button', () => {
const onToggle = jest.fn();
const wrapper = getWrapper({ onThumbnailsToggle: onToggle });

wrapper.simulate('click');

expect(onToggle).toBeCalled();
});
});

describe('render', () => {
test('should return a valid wrapper', () => {
const wrapper = getWrapper();

expect(wrapper.hasClass('bp-ThumbnailsToggle')).toBe(true);
expect(wrapper.exists(IconThumbnailsToggle18)).toBe(true);
});
});
});
2 changes: 2 additions & 0 deletions src/lib/viewers/controls/sidebar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './ThumbnailsToggle';
export { default } from './ThumbnailsToggle';
50 changes: 44 additions & 6 deletions src/lib/viewers/doc/DocBaseViewer.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import React from 'react';
import throttle from 'lodash/throttle';
import AnnotationControls, { AnnotationMode } from '../../AnnotationControls';
import BaseViewer from '../BaseViewer';
import Browser from '../../Browser';
import Controls from '../../Controls';
import PageControls from '../../PageControls';
import ZoomControls from '../../ZoomControls';
import ControlsRoot from '../controls/controls-root';
import DocControls from './DocControls';
import DocFindBar from './DocFindBar';
import PageControls from '../../PageControls';
import Popup from '../../Popup';
import RepStatus from '../../RepStatus';
import PreviewError from '../../PreviewError';
import RepStatus from '../../RepStatus';
import ThumbnailsSidebar from '../../ThumbnailsSidebar';
import ZoomControls from '../../ZoomControls';
import { AnnotationInput, AnnotationState } from '../../AnnotationControlsFSM';
import {
ANNOTATOR_EVENT,
Expand Down Expand Up @@ -119,6 +122,7 @@ class DocBaseViewer extends BaseViewer {
this.print = this.print.bind(this);
this.setPage = this.setPage.bind(this);
this.throttledScrollHandler = this.getScrollHandler().bind(this);
this.toggleFindBar = this.toggleFindBar.bind(this);
this.toggleThumbnails = this.toggleThumbnails.bind(this);
this.updateDiscoverabilityResinTag = this.updateDiscoverabilityResinTag.bind(this);
this.zoomIn = this.zoomIn.bind(this);
Expand Down Expand Up @@ -1045,6 +1049,32 @@ class DocBaseViewer extends BaseViewer {
this.bindControlListeners();
}

loadUIReact() {
this.controls = new ControlsRoot({ containerEl: this.containerEl });
this.renderUI();
}

renderUI() {
if (this.zoomControls) {
this.zoomControls.setCurrentScale(this.pdfViewer.currentScale);
}

if (this.controls && this.options.useReactControls) {
this.controls.render(
<DocControls
maxScale={MAX_SCALE}
minScale={MIN_SCALE}
onFindBarToggle={this.toggleFindBar}
onFullscreenToggle={this.toggleFullscreen}
onThumbnailsToggle={this.toggleThumbnails}
onZoomIn={this.zoomIn}
onZoomOut={this.zoomOut}
scale={this.pdfViewer.currentScale}
/>,
);
}
}

//--------------------------------------------------------------------------
// Event Listeners
//--------------------------------------------------------------------------
Expand Down Expand Up @@ -1101,7 +1131,7 @@ class DocBaseViewer extends BaseViewer {
}

if (!this.isFindDisabled()) {
this.controls.add(__('toggle_findbar'), () => this.findBar.toggle(), 'bp-toggle-findbar-icon', ICON_SEARCH);
this.controls.add(__('toggle_findbar'), this.toggleFindBar, 'bp-toggle-findbar-icon', ICON_SEARCH);
}

this.zoomControls.init(this.pdfViewer.currentScale, {
Expand Down Expand Up @@ -1144,7 +1174,11 @@ class DocBaseViewer extends BaseViewer {
pagesinitHandler() {
this.pdfViewer.currentScaleValue = 'auto';

this.loadUI();
if (this.options.useReactControls) {
this.loadUIReact();
} else {
this.loadUI();
}

const { pagesCount, currentScale } = this.pdfViewer;

Expand Down Expand Up @@ -1207,7 +1241,7 @@ class DocBaseViewer extends BaseViewer {
return;
}

this.zoomControls.setCurrentScale(this.pdfViewer.currentScale);
this.renderUI();

// Page rendered event
this.emit('pagerender', pageNumber);
Expand Down Expand Up @@ -1452,6 +1486,10 @@ class DocBaseViewer extends BaseViewer {
this.pinchPage = null;
}

toggleFindBar() {
this.findBar.toggle();
}

/**
* Callback when the toggle thumbnail sidebar button is clicked.
*
Expand Down
34 changes: 34 additions & 0 deletions src/lib/viewers/doc/DocControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import ControlsBar from '../controls/controls-bar';
import FindBarToggle, { Props as FindBarToggleProps } from '../controls/findbar';
import FullscreenToggle, { Props as FullscreenToggleProps } from '../controls/fullscreen';
import ThumbnailsToggle, { Props as ThumbnailsToggleProps } from '../controls/sidebar';
import ZoomControls, { Props as ZoomControlsProps } from '../controls/zoom';

export type Props = FindBarToggleProps & FullscreenToggleProps & ThumbnailsToggleProps & ZoomControlsProps;

export default function DocControls({
maxScale,
minScale,
onFindBarToggle,
onFullscreenToggle,
onThumbnailsToggle,
onZoomIn,
onZoomOut,
scale,
}: Props): JSX.Element {
return (
<ControlsBar>
<ThumbnailsToggle onThumbnailsToggle={onThumbnailsToggle} />
<FindBarToggle onFindBarToggle={onFindBarToggle} />
<ZoomControls
maxScale={maxScale}
minScale={minScale}
onZoomIn={onZoomIn}
onZoomOut={onZoomOut}
scale={scale}
/>
<FullscreenToggle onFullscreenToggle={onFullscreenToggle} />
</ControlsBar>
);
}
44 changes: 43 additions & 1 deletion src/lib/viewers/doc/__tests__/DocBaseViewer-test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/* eslint-disable no-unused-expressions */
import React from 'react';
import Api from '../../../api';
import AnnotationControls, { AnnotationMode } from '../../../AnnotationControls';
import AnnotationControlsFSM, { AnnotationInput, AnnotationState } from '../../../AnnotationControlsFSM';
import ControlsRoot from '../../controls/controls-root';
import DocBaseViewer, { DISCOVERABILITY_STATES } from '../DocBaseViewer';
import DocControls from '../DocControls';
import DocFindBar from '../DocFindBar';
import Browser from '../../../Browser';
import BaseViewer from '../../BaseViewer';
Expand Down Expand Up @@ -38,6 +41,8 @@ import {
import { LOAD_METRIC, RENDER_EVENT, USER_DOCUMENT_THUMBNAIL_EVENTS, VIEWER_EVENT } from '../../../events';
import Timer from '../../../Timer';

jest.mock('../../controls/controls-root');

const LOAD_TIMEOUT_MS = 180000; // 3 min timeout
const PRINT_TIMEOUT_MS = 1000; // Wait 1s before trying to print
const PRINT_DIALOG_TIMEOUT_MS = 500;
Expand Down Expand Up @@ -1683,6 +1688,30 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => {
});
});

describe('loadUIReact()', () => {
test('should create controls root and render the react controls', () => {
docBase.pdfViewer = {
currentScale: 1,
};
docBase.options.useReactControls = true;
docBase.loadUIReact();

expect(docBase.controls).toBeInstanceOf(ControlsRoot);
expect(docBase.controls.render).toBeCalledWith(
<DocControls
maxScale={10}
minScale={0.1}
onFindBarToggle={docBase.toggleFindBar}
onFullscreenToggle={docBase.toggleFullscreen}
onThumbnailsToggle={docBase.toggleThumbnails}
onZoomIn={docBase.zoomIn}
onZoomOut={docBase.zoomOut}
scale={1}
/>,
);
});
});

describe('bindDOMListeners()', () => {
beforeEach(() => {
stubs.addEventListener = jest.spyOn(docBase.docEl, 'addEventListener').mockImplementation();
Expand Down Expand Up @@ -1768,6 +1797,7 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => {
describe('pagesinitHandler()', () => {
beforeEach(() => {
stubs.loadUI = jest.spyOn(docBase, 'loadUI').mockImplementation();
stubs.loadUIReact = jest.spyOn(docBase, 'loadUIReact').mockImplementation();
stubs.setPage = jest.spyOn(docBase, 'setPage').mockImplementation();
stubs.getCachedPage = jest.spyOn(docBase, 'getCachedPage').mockImplementation();
stubs.emit = jest.spyOn(docBase, 'emit').mockImplementation();
Expand All @@ -1780,12 +1810,24 @@ describe('src/lib/viewers/doc/DocBaseViewer', () => {
};

docBase.pagesinitHandler();
expect(docBase.docEl).toHaveClass('bp-is-scrollable');
expect(stubs.loadUI).toBeCalled();
expect(stubs.loadUIReact).not.toBeCalled();
expect(stubs.setPage).toBeCalled();
expect(docBase.docEl).toHaveClass('bp-is-scrollable');
expect(stubs.setupPages).toBeCalled();
});

test('should load the React UI if the option is enabled', () => {
docBase.pdfViewer = {
currentScale: 'unknown',
};
docBase.options.useReactControls = true;
docBase.pagesinitHandler();

expect(stubs.loadUI).not.toBeCalled();
expect(stubs.loadUIReact).toBeCalled();
});

test("should broadcast that the preview is loaded if it hasn't already", () => {
docBase.pdfViewer = {
currentScale: 'unknown',
Expand Down