Skip to content

Commit 0f695ad

Browse files
Stefano CasasolaMatteo Vivona
Stefano Casasola
authored and
Matteo Vivona
committed
Adds Spinner, Confirm and Toaster components.
1 parent 67e3b4b commit 0f695ad

18 files changed

+378
-1
lines changed

.editorconfig

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
charset = utf-8
7+
trim_trailing_whitespace = true
8+
insert_final_newline = true
9+
end_of_line = lf
10+
# editorconfig-tools is unable to ignore longs strings or urls
11+
max_line_length = 100

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ jspm_packages
4141
.env.*
4242
\.vscode/
4343
.rts2*
44-
.package-lock.json
44+
package-lock.json

package.json

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"name": "@hospitalrun-org/components",
3+
"version": "0.1.0",
4+
"main": "dist/index.js",
5+
"module": "dist/components.esm.js",
6+
"typings": "dist/index.d.ts",
7+
"files": [
8+
"dist",
9+
"scss"
10+
],
11+
"repository": {
12+
"type": "git",
13+
"url": "git+https://github.com/HospitalRun/components.git"
14+
},
15+
"scripts": {
16+
"dev": "tsdx watch",
17+
"build": "tsdx build",
18+
"test": "tsdx test --env=jsdom",
19+
"lint": "tsdx lint"
20+
},
21+
"peerDependencies": {
22+
"react": ">=16"
23+
},
24+
"husky": {
25+
"hooks": {
26+
"pre-commit": "pretty-quick --staged"
27+
}
28+
},
29+
"prettier": {
30+
"printWidth": 100,
31+
"tabWidth": 2,
32+
"semi": false,
33+
"singleQuote": true,
34+
"trailingComma": "all",
35+
"bracketSpacing": true,
36+
"jsxBracketSameLine": false,
37+
"arrowParens": "avoid"
38+
},
39+
"devDependencies": {
40+
"@types/jest": "^24.0.17",
41+
"@types/react": "^16.9.1",
42+
"@types/react-dom": "^16.8.5",
43+
"husky": "^3.0.3",
44+
"prettier": "^1.18.2",
45+
"pretty-quick": "^1.11.1",
46+
"react": "^16.9.0",
47+
"react-bootstrap": "^1.0.0-beta.11",
48+
"react-dom": "^16.9.0",
49+
"tsdx": "^0.7.2",
50+
"tslib": "^1.10.0",
51+
"typescript": "^3.5.3"
52+
},
53+
"dependencies": {
54+
"formik": "^1.5.8",
55+
"react-confirm-alert": "^2.4.1",
56+
"react-spinners": "^0.5.12",
57+
"react-toastify": "^5.3.2"
58+
}
59+
}

scss/main.scss

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
.react-confirm-alert-blur {
2+
filter: url(#gaussian-blur);
3+
filter: blur(1px);
4+
-webkit-filter: blur(1px);
5+
@media (max-width: 575px) {
6+
filter: blur(8px);
7+
-webkit-filter: blur(8px);
8+
}
9+
}
10+
.react-confirm-alert-overlay {
11+
background: rgba(255, 255, 255, 0.3);
12+
@media (max-width: 575px) {
13+
background: rgba(255, 255, 255, 0.5);
14+
}
15+
z-index: 1500;
16+
}
17+
.confirm-btn {
18+
border-radius: 20px !important;
19+
width: 46%;
20+
}
21+
.confirm-container {
22+
width: 425px;
23+
text-align: center;
24+
border-radius: 10px;
25+
padding: 2rem;
26+
background: white;
27+
-webkit-box-shadow: 0 12px 30px -8px rgba(0, 0, 0, 0.2);
28+
box-shadow: 0 12px 30px -8px rgba(0, 0, 0, 0.2);
29+
@media (max-width: 575px) {
30+
width: 330px;
31+
padding: 1rem;
32+
}
33+
h3 {
34+
font-size: 2.6rem;
35+
line-height: 1;
36+
font-weight: 700;
37+
padding-bottom: 1rem;
38+
}
39+
p {
40+
font-size: 1.2rem;
41+
padding-bottom: 2rem;
42+
}
43+
}
44+
.confirm-buttons {
45+
width: 90%;
46+
height: 3rem;
47+
margin-top: 1.5em;
48+
margin-right: auto;
49+
margin-left: auto;
50+
}

src/components/Confirm/index.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { confirmAlert } from 'react-confirm-alert'
2+
import 'react-confirm-alert/src/react-confirm-alert.css'
3+
import Modal from './modal'
4+
import { ConfirmProps } from './interfaces'
5+
6+
export const Confirm = (props: ConfirmProps) =>
7+
confirmAlert({
8+
customUI: Modal(props),
9+
})

src/components/Confirm/interfaces.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export interface ConfirmProps {
2+
title: string
3+
message: string
4+
ok: {
5+
callback(): void
6+
label?: string
7+
}
8+
cancel: {
9+
callback(): void
10+
label?: string
11+
}
12+
}

src/components/Confirm/modal.tsx

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as React from 'react'
2+
import { ConfirmProps } from './interfaces'
3+
import * as strings from './strings'
4+
import Button from 'react-bootstrap/Button'
5+
6+
const Modal = (props: ConfirmProps) => ({ onClose }: any) => (
7+
<>
8+
<div className="confirm-container">
9+
<h3>{props.title}</h3>
10+
<p>{props.message}</p>
11+
<div className="confirm-buttons">
12+
<Button
13+
onClick={() => {
14+
props.cancel.callback()
15+
onClose()
16+
}}
17+
style={{ float: 'left' }}
18+
className="confirm-btn"
19+
variant="danger"
20+
>
21+
{props.cancel.label || strings.defaultCancelLabel}
22+
</Button>
23+
<Button
24+
onClick={() => {
25+
props.ok.callback()
26+
onClose()
27+
}}
28+
style={{ float: 'right' }}
29+
className="confirm-btn"
30+
variant="success"
31+
>
32+
{props.ok.label || strings.defaultOkLabel}
33+
</Button>
34+
</div>
35+
</div>
36+
</>
37+
)
38+
39+
export default Modal

src/components/Confirm/strings.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const defaultOkLabel = 'Confirm'
2+
export const defaultCancelLabel = 'Cancel'

src/components/Spinner/index.tsx

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React, { Component } from 'react'
2+
import * as Spinners from 'react-spinners'
3+
import { SpinnerType, SpinnerSizeUnit } from './interfaces'
4+
import * as strings from './strings'
5+
6+
interface Props {
7+
type: SpinnerType
8+
loading: boolean
9+
color?: string
10+
margin?: string
11+
size?: number | [number, number]
12+
sizeUnit?: SpinnerSizeUnit | [SpinnerSizeUnit, SpinnerSizeUnit]
13+
}
14+
15+
class Spinner extends Component<Props, {}> {
16+
commonStyles = {
17+
loading: this.props.loading,
18+
color: this.props.color ? this.props.color : 'grey',
19+
margin: this.props.margin ? this.props.margin : '2px',
20+
}
21+
22+
loaderStyles1 = {
23+
...this.commonStyles,
24+
size: this.props.size ? (this.props.size as number) : 15,
25+
sizeUnit: this.props.sizeUnit ? (this.props.sizeUnit as SpinnerSizeUnit) : 'px',
26+
}
27+
28+
loaderStyles2 = {
29+
...this.commonStyles,
30+
width: this.props.size ? (this.props.size as [number, number])[0] : 5,
31+
height: this.props.size ? (this.props.size as [number, number])[1] : 15,
32+
widthUnit: this.props.sizeUnit
33+
? (this.props.sizeUnit as [SpinnerSizeUnit, SpinnerSizeUnit])[0]
34+
: 'px',
35+
heightUnit: this.props.sizeUnit
36+
? (this.props.sizeUnit as [SpinnerSizeUnit, SpinnerSizeUnit])[1]
37+
: 'px',
38+
}
39+
40+
render() {
41+
switch (this.props.type) {
42+
case 'BarLoader':
43+
return <Spinners.BarLoader {...this.loaderStyles2} />
44+
case 'BeatLoader':
45+
return <Spinners.BeatLoader {...this.loaderStyles1} />
46+
case 'BounceLoader':
47+
return <Spinners.BounceLoader {...this.loaderStyles1} />
48+
case 'ClimbingBoxLoader':
49+
return <Spinners.ClimbingBoxLoader {...this.loaderStyles1} />
50+
case 'ClipLoader':
51+
return <Spinners.ClipLoader {...this.loaderStyles1} />
52+
case 'DotLoader':
53+
return <Spinners.DotLoader {...this.loaderStyles1} />
54+
case 'FadeLoader':
55+
return <Spinners.FadeLoader {...this.loaderStyles2} />
56+
case 'PulseLoader':
57+
return <Spinners.PulseLoader {...this.loaderStyles1} />
58+
case 'RotateLoader':
59+
return <Spinners.RotateLoader {...this.loaderStyles1} />
60+
case 'ScaleLoader':
61+
return <Spinners.ScaleLoader {...this.loaderStyles2} />
62+
case 'SyncLoader':
63+
return <Spinners.SyncLoader {...this.loaderStyles1} />
64+
default:
65+
return <div>{strings.invalidSpinner}</div>
66+
}
67+
}
68+
}
69+
70+
export { Spinner }

src/components/Spinner/interfaces.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export type SpinnerType =
2+
| 'BarLoader'
3+
| 'BeatLoader'
4+
| 'BounceLoader'
5+
| 'ClimbingBoxLoader'
6+
| 'ClipLoader'
7+
| 'DotLoader'
8+
| 'FadeLoader'
9+
| 'PulseLoader'
10+
| 'RotateLoader'
11+
| 'ScaleLoader'
12+
| 'SyncLoader'
13+
14+
export type SpinnerSizeUnit = 'px' | '%' | 'em'

src/components/Spinner/strings.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const invalidSpinner = 'Invalid spinner'

src/components/Toaster/components.tsx

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as React from 'react'
2+
import { ToastProps } from './interfaces'
3+
4+
export const titleWithMessage = (title: ToastProps['title'], message: ToastProps['message']) => (
5+
<>
6+
<div className="titles" style={{ fontSize: '1.1em', fontWeight: 600 }}>
7+
{title}
8+
</div>
9+
<div>{message}</div>
10+
</>
11+
)
12+
13+
export const titleWithoutMessage = (title: ToastProps['title']) => <div>{title}</div>

src/components/Toaster/index.tsx

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as React from 'react'
2+
import { ToastContainer, toast, Slide } from 'react-toastify'
3+
import { ToastProps, ToasterProps } from './interfaces'
4+
import { titleWithMessage, titleWithoutMessage } from './components'
5+
import 'react-toastify/dist/ReactToastify.min.css'
6+
7+
export const Toast: any = (
8+
type: ToastProps['type'],
9+
title: ToastProps['title'],
10+
message?: ToastProps['message'],
11+
) =>
12+
type === 'error'
13+
? message
14+
? toast.error(titleWithMessage(title, message))
15+
: toast.error(titleWithoutMessage(title))
16+
: type === 'info'
17+
? message
18+
? toast.info(titleWithMessage(title, message))
19+
: toast.info(titleWithoutMessage(title))
20+
: type === 'success'
21+
? message
22+
? toast.success(titleWithMessage(title, message))
23+
: toast.success(titleWithoutMessage(title))
24+
: type === 'warning'
25+
? message
26+
? toast.warn(titleWithMessage(title, message))
27+
: toast.warn(titleWithoutMessage(title))
28+
: message
29+
? toast(titleWithMessage(title, message))
30+
: toast(titleWithoutMessage(title))
31+
32+
export const Toaster = (props: ToasterProps) => (
33+
<ToastContainer
34+
autoClose={props.autoClose || 5000}
35+
hideProgressBar={props.hideProgressBar === false ? false : true}
36+
draggable={props.draggable === false ? false : true}
37+
transition={Slide}
38+
draggablePercent={35}
39+
/>
40+
)

src/components/Toaster/interfaces.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export interface ToasterProps {
2+
autoClose?: number
3+
hideProgressBar?: boolean
4+
draggable?: boolean
5+
}
6+
7+
export interface ToastProps {
8+
type: 'success' | 'info' | 'warning' | 'error'
9+
title: string
10+
message?: string
11+
}

src/components/Toaster/strings.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const okText = 'Confirm'
2+
export const cancelText = 'Cancel'

src/index.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './components/Toaster'
2+
export * from './components/Spinner'
3+
export * from './components/Confirm'

test/toaster.test.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as React from 'react'
2+
import * as ReactDOM from 'react-dom'
3+
// import { Toaster } from '../src/components/Toaster';
4+
5+
describe('it', () => {
6+
it('renders without crashing', () => {
7+
const div = document.createElement('div')
8+
ReactDOM.render(<span></span>, div)
9+
ReactDOM.unmountComponentAtNode(div)
10+
})
11+
})

tsconfig.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"include": ["src", "types"],
3+
"compilerOptions": {
4+
"target": "es5",
5+
"module": "esnext",
6+
"lib": ["dom", "esnext"],
7+
"importHelpers": true,
8+
"declaration": true,
9+
"sourceMap": true,
10+
"rootDir": "./",
11+
"strict": true,
12+
"noImplicitAny": true,
13+
"strictNullChecks": true,
14+
"strictFunctionTypes": true,
15+
"strictPropertyInitialization": true,
16+
"noImplicitThis": true,
17+
"alwaysStrict": true,
18+
"noUnusedLocals": true,
19+
"noUnusedParameters": true,
20+
"noImplicitReturns": true,
21+
"noFallthroughCasesInSwitch": true,
22+
"moduleResolution": "node",
23+
"baseUrl": "./",
24+
"paths": {
25+
"*": ["src/*", "node_modules/*"]
26+
},
27+
"jsx": "react",
28+
"esModuleInterop": true
29+
}
30+
}

0 commit comments

Comments
 (0)