diff --git a/javascripts/src/components/common/Spinner.spec.tsx b/javascripts/src/components/common/Spinner.spec.tsx
new file mode 100644
index 00000000..202e4a36
--- /dev/null
+++ b/javascripts/src/components/common/Spinner.spec.tsx
@@ -0,0 +1,24 @@
+// License: LGPL-3.0-or-later
+import * as React from 'react';
+import 'jest';
+import Spinner from './Spinner'
+import { mount } from 'enzyme';
+import toJson from 'enzyme-to-json';
+
+describe('Spinner', () => {
+ test('is small', () => {
+ expect(toJson(mount())).toMatchSnapshot()
+ })
+
+ test('is normal', () => {
+ expect(toJson(mount())).toMatchSnapshot()
+ })
+
+ test('is large', () => {
+ expect(toJson(mount())).toMatchSnapshot()
+ })
+
+ test('has custom color ', () => {
+ expect(toJson(mount())).toMatchSnapshot()
+ })
+})
\ No newline at end of file
diff --git a/javascripts/src/components/common/Spinner.tsx b/javascripts/src/components/common/Spinner.tsx
new file mode 100644
index 00000000..dc530466
--- /dev/null
+++ b/javascripts/src/components/common/Spinner.tsx
@@ -0,0 +1,55 @@
+// License: LGPL-3.0-or-later
+import { Color } from 'csstype';
+import * as React from 'react';
+import { VelocityComponent } from 'velocity-react';
+import ScreenReaderOnlyText from './ScreenReaderOnlyText';
+
+export interface SpinnerProps {
+ size: 'small' | 'normal' | 'large'
+ color?: Color
+}
+
+class Spinner extends React.Component {
+ static defaultProps = {
+ color: 'currentcolor'
+ }
+
+ generateStyle(): React.CSSProperties {
+ let spinnerDimension: number
+ let spinnerBorderWidth: number = 3
+ switch (this.props.size) {
+ case 'small':
+ spinnerDimension = 25
+ break;
+ case 'normal':
+ spinnerDimension = 50
+ break;
+ case 'large':
+ spinnerDimension = 100
+ break;
+ }
+
+ return {
+ display: 'inline-block',
+ width: `${spinnerDimension}px`,
+ height: `${spinnerDimension}px`,
+ verticalAlign: 'text-bottom',
+ border: `${spinnerBorderWidth}px solid ${this.props.color}`,
+ borderRightColor: 'transparent',
+ borderRadius: '50%',
+ }
+
+ }
+ render() {
+ return
+
+ Loading...
+
+ ;
+ }
+}
+
+export default Spinner;
+
+
+
diff --git a/javascripts/src/components/common/__snapshots__/Spinner.spec.tsx.snap b/javascripts/src/components/common/__snapshots__/Spinner.spec.tsx.snap
new file mode 100644
index 00000000..4cb66665
--- /dev/null
+++ b/javascripts/src/components/common/__snapshots__/Spinner.spec.tsx.snap
@@ -0,0 +1,221 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Spinner has custom color 1`] = `
+
+
+
+
+
+ Loading...
+
+
+
+
+
+`;
+
+exports[`Spinner is large 1`] = `
+
+
+
+
+
+ Loading...
+
+
+
+
+
+`;
+
+exports[`Spinner is normal 1`] = `
+
+
+
+
+
+ Loading...
+
+
+
+
+
+`;
+
+exports[`Spinner is small 1`] = `
+
+
+
+
+
+ Loading...
+
+
+
+
+
+`;
diff --git a/types/velocity-react/index.d.ts b/types/velocity-react/index.d.ts
index dd9872e8..5c5d6cc3 100644
--- a/types/velocity-react/index.d.ts
+++ b/types/velocity-react/index.d.ts
@@ -11,11 +11,10 @@ interface VelocityComponentProps
animation: Animation
runOnMount?: boolean
targetQuerySelector?: TargetQuerySelector
-
}
-export declare class VelocityComponent extends React.Component
+export declare class VelocityComponent extends React.Component
{
runAnimation():void
}