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

add ripple loader component from trendkite-ui #329

Merged
merged 4 commits into from
Aug 9, 2021
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
9 changes: 9 additions & 0 deletions example/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
Typography,
Modal,
Kite,
Loader,
// IMPORT_INJECTOR
} from '@cision/rover-ui';

Expand Down Expand Up @@ -474,6 +475,14 @@ const App = () => {
<Kite.Header>Success Kite!</Kite.Header>
</Kite>
</Section>
<Section title="Loader">
<div>
<Loader size="sm" />
</div>
<div>
<Loader />
</div>
</Section>

{/** USAGE_INJECTOR */}
</div>
Expand Down
80 changes: 80 additions & 0 deletions src/components/Loader/Loader.module.css
Original file line number Diff line number Diff line change
@@ -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;
}
40 changes: 40 additions & 0 deletions src/components/Loader/Loader.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<Loader />);
});

describe('when rendered with custom style with width and height', () => {
it('should override the default loader sizes', () => {
const { getByTestId } = render(
<Loader
data-testid="loaderElem"
style={{ width: '123px', height: '123px' }}
/>
);

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(
<Loader data-testid="loaderElem" className="Loader" />
);

const loaderElem = getByTestId('loaderElem') as HTMLDivElement;
const loaderClasses = loaderElem.className
.split(' ')
.filter((classes) => classes.match('\\w*(Loader)\\w*'));
expect(loaderClasses.length).toBe(2);
});
});
});
28 changes: 28 additions & 0 deletions src/components/Loader/Loader.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement> {
size?: Size;
}

const Loader: React.FC<LoaderProps> = ({ size = 'lg', ...rest }) => {
const mainClass = classNames(
styles.Loader,
rest.className,
`${styles[size]}`
);

const prop = {
...rest,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Need a second opinion on spreading custom props in the nested div rather than the wrapper div.
Attaching it to the nested div allows consumer to pass in custom sizes and have it override the default sm, md, lg, xl options.
An alternative option would be to pass a numeric to the size prop rather than enum strings. Then the custom prop can be attached to the wrapper div. This will break away from the enum string pattern used in trendkite-ui

Copy link
Contributor

@pixelbandito pixelbandito Jul 30, 2021

Choose a reason for hiding this comment

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

I don't think I'd call this an "icon", I might just say "Loader" or something, since our icons are usually svgs that you can change colors on.

If the circular image ends up being its own component, it would probably be called Loader, but if it's nested inside a loader component and it would need a distinct name, maybe image, animation, ripple, or something.

Copy link
Collaborator Author

@lihnick lihnick Jul 30, 2021

Choose a reason for hiding this comment

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

Updated the loader to render with one div and custom props would get spread on that div.
I kept the css that renders the loader's location in the center of its parent html element.
That way consumers can adjust the loader's horizontal positioning with a parent html element that has a defined width and margin.
They could also override the default horizontally centered loader position via style prop if consumers do not have control over the parent html element.

className: mainClass,
};

return <div {...prop} />;
};

export default Loader;
16 changes: 16 additions & 0 deletions src/components/Loader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# \<Loader\>

render a loading icon

### Valid Sizes

Size is optional, defaults to large when omitted

- sm
- md
- lg
- xl

```jsx
<Loader size="md" />
```
1 change: 1 addition & 0 deletions src/components/Loader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Loader';
83 changes: 83 additions & 0 deletions src/components/Loader/story.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignContent: 'center',
alignItems: 'center',
marginTop: '100px',
marginBottom: '100px',
}}
{...props}
/>
);

storiesOf('Planets/Loader', module)
.addParameters({
readme: {
sidebar: Readme,
},
})
.add(
'Overview',
() => (
<FlexWrapper>
<Loader size={select('size', ['sm', 'md', 'lg', 'xl'], 'lg')} />
</FlexWrapper>
),
{
info: {
inline: false,
source: true,
},
}
)
.add(
'Examples',
() => (
<>
<Wrap>
<Loader size="sm" />
</Wrap>
<Wrap>
<Loader size="md" />
</Wrap>
<Wrap>
<Loader size="lg" />
</Wrap>
<Wrap>
<Loader size="xl" />
</Wrap>
<Wrap>
<Loader style={{ marginLeft: '0' }} />
</Wrap>
<Wrap>
<Loader style={{ marginRight: '0' }} />
</Wrap>
<Wrap>
<div style={{ width: '50%' }}>
<Loader size="lg" />
</div>
</Wrap>
<Wrap>
<Loader style={{ marginTop: '75px', marginBottom: '75px' }} />
</Wrap>
</>
),
{
info: {
inline: false,
source: true,
},
}
);
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
1 change: 1 addition & 0 deletions src/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down