diff --git a/example/src/App.js b/example/src/App.js index d22c75d7..f5d08254 100644 --- a/example/src/App.js +++ b/example/src/App.js @@ -28,6 +28,7 @@ import { Typography, Modal, Kite, + Loader, // IMPORT_INJECTOR } from '@cision/rover-ui'; @@ -474,6 +475,14 @@ const App = () => { Success Kite! +
+
+ +
+
+ +
+
{/** USAGE_INJECTOR */} diff --git a/src/components/Loader/Loader.module.css b/src/components/Loader/Loader.module.css new file mode 100644 index 00000000..a423a407 --- /dev/null +++ b/src/components/Loader/Loader.module.css @@ -0,0 +1,80 @@ +@keyframes LOADERRIPPLE { + 0% { + transform: scale(0, 0); + opacity: 1; + } + + 100% { + transform: scale(0.9, 0.9); + opacity: 0; + } +} + +.Loader { + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: center; +} + +.Loader::before { + content: ''; + background-color: transparent; + position: absolute; + opacity: 1; + width: 50px; + height: 50px; + border-radius: 50%; + border: 5px solid #0092c2; + animation: LOADERRIPPLE 1.8s infinite; + transform: scale(0, 0); + animation-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1); + animation-delay: 0s; +} + +.Loader::after { + content: ''; + background-color: transparent; + position: absolute; + opacity: 1; + width: 50px; + height: 50px; + border-radius: 50%; + border: 5px solid #0092c2; + animation: LOADERRIPPLE 1.8s infinite; + transform: scale(0, 0); + animation-timing-function: cubic-bezier(0.3, 0.61, 0.335, 1); + animation-delay: -0.9s; +} + +.sm, +.sm::before, +.sm::after { + width: 10px; + height: 10px; + margin: auto; +} + +.md, +.md::before, +.md::after { + width: 20px; + height: 20px; + margin: auto; +} + +.lg, +.lg::before, +.lg::after { + width: 50px; + height: 50px; + margin: auto; +} + +.xl, +.xl::before, +.xl::after { + width: 100px; + height: 100px; + margin: auto; +} diff --git a/src/components/Loader/Loader.test.tsx b/src/components/Loader/Loader.test.tsx new file mode 100644 index 00000000..26d90087 --- /dev/null +++ b/src/components/Loader/Loader.test.tsx @@ -0,0 +1,40 @@ +import React from 'react'; + +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom'; + +import Loader from '.'; + +describe('Loader', () => { + it('renders', () => { + render(); + }); + + describe('when rendered with custom style with width and height', () => { + it('should override the default loader sizes', () => { + const { getByTestId } = render( + + ); + + const loaderElem = getByTestId('loaderElem') as HTMLDivElement; + expect(loaderElem.style.width).toBe('123px'); + }); + }); + + describe('when rendered with additional class names', () => { + it('should not replace the default classes', () => { + const { getByTestId } = render( + + ); + + const loaderElem = getByTestId('loaderElem') as HTMLDivElement; + const loaderClasses = loaderElem.className + .split(' ') + .filter((classes) => classes.match('\\w*(Loader)\\w*')); + expect(loaderClasses.length).toBe(2); + }); + }); +}); diff --git a/src/components/Loader/Loader.tsx b/src/components/Loader/Loader.tsx new file mode 100644 index 00000000..3831f50a --- /dev/null +++ b/src/components/Loader/Loader.tsx @@ -0,0 +1,28 @@ +import React from 'react'; + +import classNames from 'classnames'; + +import styles from './Loader.module.css'; + +export type Size = 'sm' | 'md' | 'lg' | 'xl'; + +interface LoaderProps extends React.HTMLAttributes { + size?: Size; +} + +const Loader: React.FC = ({ size = 'lg', ...rest }) => { + const mainClass = classNames( + styles.Loader, + rest.className, + `${styles[size]}` + ); + + const prop = { + ...rest, + className: mainClass, + }; + + return
; +}; + +export default Loader; diff --git a/src/components/Loader/README.md b/src/components/Loader/README.md new file mode 100644 index 00000000..79da27ec --- /dev/null +++ b/src/components/Loader/README.md @@ -0,0 +1,16 @@ +# \ + +render a loading icon + +### Valid Sizes + +Size is optional, defaults to large when omitted + +- sm +- md +- lg +- xl + +```jsx + +``` diff --git a/src/components/Loader/index.ts b/src/components/Loader/index.ts new file mode 100644 index 00000000..348c02a9 --- /dev/null +++ b/src/components/Loader/index.ts @@ -0,0 +1 @@ +export { default } from './Loader'; diff --git a/src/components/Loader/story.tsx b/src/components/Loader/story.tsx new file mode 100644 index 00000000..4742eeab --- /dev/null +++ b/src/components/Loader/story.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { select } from '@storybook/addon-knobs'; + +import Loader from './Loader'; +import Readme from './README.md'; + +import { Wrap } from '../../stories/storybook-helpers'; + +const FlexWrapper = (props) => ( +
+); + +storiesOf('Planets/Loader', module) + .addParameters({ + readme: { + sidebar: Readme, + }, + }) + .add( + 'Overview', + () => ( + + + + ), + { + info: { + inline: false, + source: true, + }, + } + ) + .add( + 'Examples', + () => ( + <> + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + + ), + { + info: { + inline: false, + source: true, + }, + } + ); diff --git a/src/index.ts b/src/index.ts index 1dfeb6ea..4d71b292 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,3 +36,4 @@ export { default as InputTime } from './components/InputTime'; export { default as Typography } from './components/Typography'; export { default as Modal } from './components/Modal'; export { default as Kite } from './components/Kite'; +export { default as Loader } from './components/Loader'; diff --git a/src/stories/index.js b/src/stories/index.js index 6124ceb7..21259e0d 100644 --- a/src/stories/index.js +++ b/src/stories/index.js @@ -24,6 +24,7 @@ import '../components/Input/story'; import '../components/Input/Checkbox/story'; import '../components/InputTime/story'; import '../components/Typography/story'; +import '../components/Loader/story'; /* * STAR SYSTEMS