Skip to content

Commit

Permalink
feat(drawing): add ColorPickerControl for images (#1307)
Browse files Browse the repository at this point in the history
* feat(drawing): add ColorPickerControl for images
  • Loading branch information
ChenCodes authored Dec 9, 2020
1 parent 8303edf commit 0f8d7d9
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 35 deletions.
2 changes: 2 additions & 0 deletions src/lib/AnnotationModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const AnnotationColor = {
GRIMACE: bdlGrimace,
};

export const ANNOTATION_COLORS = Object.values(AnnotationColor);

export default class AnnotationModule {
private cache: Cache;

Expand Down
28 changes: 28 additions & 0 deletions src/lib/viewers/controls/annotations/DrawingControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import ColorPickerControl from '../color-picker';
import { ANNOTATION_COLORS } from '../../../AnnotationModule';
import { AnnotationMode } from './types';

export type Props = {
annotationColor?: string;
annotationMode?: AnnotationMode;
onAnnotationColorChange: (color: string) => void;
};

export default function DrawingControls({
annotationColor,
annotationMode,
onAnnotationColorChange,
}: Props): JSX.Element | null {
if (annotationMode !== AnnotationMode.DRAWING) {
return null;
}

return (
<ColorPickerControl
activeColor={annotationColor}
colors={ANNOTATION_COLORS}
onColorSelect={onAnnotationColorChange}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import DrawingControls from '../DrawingControls';
import { AnnotationMode } from '../types';

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

describe('render', () => {
test('should return nothing if annotationMode is not DRAWING', () => {
const wrapper = getWrapper();
expect(wrapper.isEmptyRender()).toBe(true);
});

test('should return ColorPickerControl if annotationMode is DRAWING', () => {
const wrapper = getWrapper({ annotationMode: AnnotationMode.DRAWING });

expect(wrapper.exists('ColorPickerControl')).toBe(true);
});
});
});
8 changes: 6 additions & 2 deletions src/lib/viewers/controls/controls-bar/ControlsBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import React from 'react';
import './ControlsBar.scss';

export type Props = {
children: React.ReactNode;
children?: React.ReactNode;
};

export default function ControlsBar({ children, ...rest }: Props): JSX.Element {
export default function ControlsBar({ children, ...rest }: Props): JSX.Element | null {
if (!children) {
return null;
}

return (
<div className="bp-ControlsBar" {...rest}>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,12 @@ describe('ControlsBar', () => {
expect(wrapper.contains(children)).toBe(true);
expect(wrapper.hasClass('bp-ControlsBar')).toBe(true);
});

test('should return null if the children property is undefined', () => {
const children = undefined;
const wrapper = shallow(<ControlsBar>{children}</ControlsBar>);

expect(wrapper.isEmptyRender()).toBe(true);
});
});
});
28 changes: 10 additions & 18 deletions src/lib/viewers/doc/DocControls.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import React from 'react';
import AnnotationsControls, { Props as AnnotationsControlsProps } from '../controls/annotations';
import ColorPickerControl, { Props as ColorPickerControlProps } from '../controls/color-picker';
import ControlsBar from '../controls/controls-bar';
import DrawingControls, { Props as DrawingControlsProps } from '../controls/annotations/DrawingControls';
import FindBarToggle, { Props as FindBarToggleProps } from '../controls/findbar';
import FullscreenToggle, { Props as FullscreenToggleProps } from '../controls/fullscreen';
import PageControls, { Props as PageControlsProps } from '../controls/page';
import ThumbnailsToggle, { Props as ThumbnailsToggleProps } from '../controls/sidebar';
import ZoomControls, { Props as ZoomControlsProps } from '../controls/zoom';
import { AnnotationColor } from '../../AnnotationModule';
import { AnnotationMode } from '../controls/annotations/types';

const colors = Object.values(AnnotationColor);

export type Props = AnnotationsControlsProps &
ColorPickerControlProps &
DrawingControlsProps &
FindBarToggleProps &
FullscreenToggleProps &
PageControlsProps &
ThumbnailsToggleProps &
ZoomControlsProps & {
onAnnotationColorChange: (color: string) => void;
};
ZoomControlsProps;

export default function DocControls({
annotationColor,
Expand Down Expand Up @@ -73,15 +67,13 @@ export default function DocControls({
onAnnotationModeEscape={onAnnotationModeEscape}
/>
</ControlsBar>
{hasDrawing && annotationMode === AnnotationMode.DRAWING && (
<ControlsBar>
<ColorPickerControl
activeColor={annotationColor}
colors={colors}
onColorSelect={onAnnotationColorChange}
/>
</ControlsBar>
)}
<ControlsBar>
<DrawingControls
annotationColor={annotationColor}
annotationMode={annotationMode}
onAnnotationColorChange={onAnnotationColorChange}
/>
</ControlsBar>
</>
);
}
45 changes: 30 additions & 15 deletions src/lib/viewers/image/ImageControls.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import React from 'react';
import AnnotationsControls, { Props as AnnotationsControlsProps } from '../controls/annotations';
import ControlsBar from '../controls/controls-bar';
import DrawingControls, { Props as DrawingControlsProps } from '../controls/annotations/DrawingControls';
import FullscreenToggle, { Props as FullscreenToggleProps } from '../controls/fullscreen';
import RotateControl, { Props as RotateControlProps } from '../controls/rotate';
import ZoomControls, { Props as ZoomControlsProps } from '../controls/zoom';

export type Props = AnnotationsControlsProps & FullscreenToggleProps & RotateControlProps & ZoomControlsProps;
export type Props = AnnotationsControlsProps &
DrawingControlsProps &
FullscreenToggleProps &
RotateControlProps &
ZoomControlsProps;

export default function ImageControls({
annotationColor,
annotationMode,
hasDrawing,
hasHighlight,
hasRegion,
onAnnotationColorChange,
onAnnotationModeClick,
onAnnotationModeEscape,
onFullscreenToggle,
Expand All @@ -22,19 +28,28 @@ export default function ImageControls({
scale,
}: Props): JSX.Element {
return (
<ControlsBar>
<ZoomControls onZoomIn={onZoomIn} onZoomOut={onZoomOut} scale={scale} />
<RotateControl onRotateLeft={onRotateLeft} />
<FullscreenToggle onFullscreenToggle={onFullscreenToggle} />
<AnnotationsControls
annotationColor={annotationColor}
annotationMode={annotationMode}
hasDrawing={hasDrawing}
hasHighlight={hasHighlight}
hasRegion={hasRegion}
onAnnotationModeClick={onAnnotationModeClick}
onAnnotationModeEscape={onAnnotationModeEscape}
/>
</ControlsBar>
<>
<ControlsBar>
<ZoomControls onZoomIn={onZoomIn} onZoomOut={onZoomOut} scale={scale} />
<RotateControl onRotateLeft={onRotateLeft} />
<FullscreenToggle onFullscreenToggle={onFullscreenToggle} />
<AnnotationsControls
annotationColor={annotationColor}
annotationMode={annotationMode}
hasDrawing={hasDrawing}
hasHighlight={hasHighlight}
hasRegion={hasRegion}
onAnnotationModeClick={onAnnotationModeClick}
onAnnotationModeEscape={onAnnotationModeEscape}
/>
</ControlsBar>
<ControlsBar>
<DrawingControls
annotationColor={annotationColor}
annotationMode={annotationMode}
onAnnotationColorChange={onAnnotationColorChange}
/>
</ControlsBar>
</>
);
}
8 changes: 8 additions & 0 deletions src/lib/viewers/image/ImageViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ImageViewer extends ImageBaseViewer {
// Bind context for callbacks
this.applyCursorFtux = this.applyCursorFtux.bind(this);
this.getViewportDimensions = this.getViewportDimensions.bind(this);
this.handleAnnotationColorChange = this.handleAnnotationColorChange.bind(this);
this.handleAnnotationControlsClick = this.handleAnnotationControlsClick.bind(this);
this.handleAnnotationCreateEvent = this.handleAnnotationCreateEvent.bind(this);
this.handleAssetAndRepLoad = this.handleAssetAndRepLoad.bind(this);
Expand Down Expand Up @@ -407,10 +408,12 @@ class ImageViewer extends ImageBaseViewer {

this.controls.render(
<ImageControls
annotationColor={this.annotationModule.getColor()}
annotationMode={this.annotationControlsFSM.getMode()}
hasDrawing={canDraw}
hasHighlight={false}
hasRegion={canAnnotate}
onAnnotationColorChange={this.handleAnnotationColorChange}
onAnnotationModeClick={this.handleAnnotationControlsClick}
onAnnotationModeEscape={this.handleAnnotationControlsEscape}
onFullscreenToggle={this.toggleFullscreen}
Expand Down Expand Up @@ -549,6 +552,11 @@ class ImageViewer extends ImageBaseViewer {
});
}

handleAnnotationColorChange(color) {
this.annotationModule.setColor(color);
this.renderUI();
}

/**
* Handler for annotation controls button click event.
*
Expand Down
21 changes: 21 additions & 0 deletions src/lib/viewers/image/__tests__/ImageViewer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,9 @@ describe('lib/viewers/image/ImageViewer', () => {

describe('loadUIReact()', () => {
beforeEach(() => {
image.annotationModule = {
getColor: jest.fn(),
};
image.options.useReactControls = true;
});

Expand All @@ -435,6 +438,7 @@ describe('lib/viewers/image/ImageViewer', () => {
hasDrawing={false}
hasHighlight={false}
hasRegion={false}
onAnnotationColorChange={image.handleAnnotationColorChange}
onAnnotationModeClick={image.handleAnnotationControlsClick}
onAnnotationModeEscape={image.handleAnnotationControlsEscape}
onFullscreenToggle={image.toggleFullscreen}
Expand Down Expand Up @@ -703,6 +707,23 @@ describe('lib/viewers/image/ImageViewer', () => {
});
});

describe('handleAnnotationColorChange', () => {
beforeEach(() => {
image.annotationModule = {
setColor: jest.fn(),
};
image.renderUI = jest.fn();
});

test('should call setColor and renderUI', () => {
const color = '#fff';
image.handleAnnotationColorChange(color);

expect(image.annotationModule.setColor).toBeCalledWith(color);
expect(image.renderUI).toHaveBeenCalled();
});
});

describe('handleAnnotationControlsClick', () => {
beforeEach(() => {
image.annotator = {
Expand Down

0 comments on commit 0f8d7d9

Please sign in to comment.