diff --git a/src/lib/viewers/controls/rotate/RotateControl.scss b/src/lib/viewers/controls/rotate/RotateControl.scss
new file mode 100644
index 000000000..55a6be2bd
--- /dev/null
+++ b/src/lib/viewers/controls/rotate/RotateControl.scss
@@ -0,0 +1,5 @@
+@import '../styles';
+
+.bp-RotateControl {
+ @include bp-ControlButton;
+}
diff --git a/src/lib/viewers/controls/rotate/RotateControl.tsx b/src/lib/viewers/controls/rotate/RotateControl.tsx
new file mode 100644
index 000000000..afaf633d3
--- /dev/null
+++ b/src/lib/viewers/controls/rotate/RotateControl.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+import IconRotate24 from '../icons/IconRotate24';
+import './RotateControl.scss';
+
+export type Props = {
+ onRotateLeft: () => void;
+};
+
+export default function RotateControl({ onRotateLeft }: Props): JSX.Element {
+ return (
+
+ );
+}
diff --git a/src/lib/viewers/controls/rotate/__tests__/RotateControl-test.tsx b/src/lib/viewers/controls/rotate/__tests__/RotateControl-test.tsx
new file mode 100644
index 000000000..a2b1d3bf4
--- /dev/null
+++ b/src/lib/viewers/controls/rotate/__tests__/RotateControl-test.tsx
@@ -0,0 +1,28 @@
+import React from 'react';
+import { shallow, ShallowWrapper } from 'enzyme';
+import IconRotate24 from '../../icons/IconRotate24';
+import RotateControl from '../RotateControl';
+
+describe('RotateControl', () => {
+ const getWrapper = (props = {}): ShallowWrapper => shallow();
+
+ describe('event handlers', () => {
+ test('should invoke onRotateLeft prop on click', () => {
+ const onRotateLeft = jest.fn();
+ const wrapper = getWrapper({ onRotateLeft });
+
+ wrapper.simulate('click');
+
+ expect(onRotateLeft).toHaveBeenCalled();
+ });
+ });
+
+ describe('render', () => {
+ test('should return a valid wrapper', () => {
+ const wrapper = getWrapper();
+
+ expect(wrapper.hasClass('bp-RotateControl')).toBe(true);
+ expect(wrapper.exists(IconRotate24)).toBe(true);
+ });
+ });
+});
diff --git a/src/lib/viewers/controls/rotate/index.ts b/src/lib/viewers/controls/rotate/index.ts
new file mode 100644
index 000000000..71bc7ef2a
--- /dev/null
+++ b/src/lib/viewers/controls/rotate/index.ts
@@ -0,0 +1,2 @@
+export * from './RotateControl';
+export { default } from './RotateControl';
diff --git a/src/lib/viewers/image/ImageControls.tsx b/src/lib/viewers/image/ImageControls.tsx
index b4bba1b9f..bcf3bce81 100644
--- a/src/lib/viewers/image/ImageControls.tsx
+++ b/src/lib/viewers/image/ImageControls.tsx
@@ -1,15 +1,22 @@
import React from 'react';
import ControlsBar from '../controls/controls-bar';
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 = FullscreenToggleProps & ZoomControlsProps;
+export type Props = FullscreenToggleProps & RotateControlProps & ZoomControlsProps;
-export default function ImageControls({ onFullscreenToggle, onZoomIn, onZoomOut, scale }: Props): JSX.Element {
+export default function ImageControls({
+ onFullscreenToggle,
+ onRotateLeft,
+ onZoomIn,
+ onZoomOut,
+ scale,
+}: Props): JSX.Element {
return (
- {/* TODO: RotateControl */}
+
{/* TODO: AnnotationControls (separate group) */}
diff --git a/src/lib/viewers/image/ImageViewer.js b/src/lib/viewers/image/ImageViewer.js
index 6344a2c5c..d8ec03793 100644
--- a/src/lib/viewers/image/ImageViewer.js
+++ b/src/lib/viewers/image/ImageViewer.js
@@ -393,6 +393,7 @@ class ImageViewer extends ImageBaseViewer {
this.controls.render(
{
expect(image.controls.render).toBeCalledWith(