Skip to content

Commit 9fbc7b2

Browse files
committed
Merge branch 'main' into footer
2 parents f6b6d60 + 1a47e12 commit 9fbc7b2

File tree

15 files changed

+611
-21
lines changed

15 files changed

+611
-21
lines changed

.storybook/main.js

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,41 @@
11
const path = require('path');
2-
const custom = require('./webpack.config.js')
2+
const custom = require('./webpack.config.js');
33

44
module.exports = {
5-
"stories": [
6-
"../src/**/*.stories.mdx",
7-
"../src/**/*.stories.@(js|jsx|ts|tsx)"
8-
],
9-
"addons": [
10-
"@storybook/addon-links",
11-
"@storybook/addon-essentials"
12-
],
5+
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
6+
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
137
webpackFinal: async (config, { configType }) => {
148
// `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
159
// You can change the configuration based on that.
1610
// 'PRODUCTION' is used when building the static version of storybook.
1711

1812
// allow for css modules -- overriding storybook default
1913
config.module.rules = config.module.rules.filter(
20-
rule => rule.test.toString() !== '/\\.css$/'
14+
(rule) => rule.test.toString() !== '/\\.css$/'
2115
);
2216
config.module.rules.push({
2317
test: /\.css$/,
24-
use: ['style-loader', 'css-loader'],
18+
use: ['style-loader', 'css-loader']
2519
});
2620

2721
//allow for sass support
2822
config.module.rules.push({
2923
test: /\.scss$/,
30-
use: ['style-loader', 'css-loader', 'sass-loader'],
31-
include: path.resolve(__dirname, '../'),
24+
use: [
25+
'style-loader',
26+
'css-loader',
27+
'sass-loader',
28+
{
29+
loader: require.resolve('sass-resources-loader'),
30+
options: {
31+
resources: path.resolve(__dirname, '../src/global/_global.scss')
32+
}
33+
}
34+
],
35+
include: path.resolve(__dirname, '../')
3236
});
3337

34-
3538
// Return the altered config
3639
return config;
37-
},
38-
}
40+
}
41+
};

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"dependencies": {
7676
"@fortawesome/fontawesome-free": "^5.15.1",
7777
"@popperjs/core": "^2.5.4",
78-
"@types/jest": "^26.0.18"
78+
"@types/jest": "^26.0.18",
79+
"sass-resources-loader": "^2.1.1"
7980
}
8081
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { Modal, ModalHeader, ModalBody, ModalFooter } from './Modal';
2+
import { Button } from '../Button/Button';
3+
import React, { useState, useEffect } from 'react';
4+
5+
export default {
6+
component: Modal,
7+
title: 'Modal',
8+
argTypes: {
9+
align: {
10+
control: {
11+
type: 'select',
12+
options: ['left', 'center', 'right']
13+
}
14+
},
15+
staticBackground: {
16+
control: {
17+
type: 'select',
18+
options: [true, false]
19+
}
20+
}
21+
}
22+
};
23+
24+
export const Basic = (args) => {
25+
const [showModal, setShowModal] = useState(true);
26+
useEffect(() => {}, [showModal]);
27+
28+
function processOnClick() {
29+
setShowModal(false);
30+
alert('Accept button pressed');
31+
}
32+
33+
function cancel() {
34+
setShowModal(false);
35+
alert('Cancel button pressed');
36+
}
37+
38+
function reopenModal() {
39+
setShowModal(true);
40+
}
41+
return (
42+
<div>
43+
<p>
44+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Perferendis ut
45+
est repellendus ab. Eaque vero ipsa asperiores provident, sed dolore
46+
iste error nemo voluptatum, deserunt vitae totam ea reprehenderit
47+
voluptates.
48+
</p>
49+
<p>
50+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aliquid
51+
voluptate consequatur earum deleniti mollitia aliquam, quae distinctio
52+
culpa praesentium alias eveniet cum possimus! Sit saepe cupiditate
53+
adipisci optio dolorem qui.
54+
</p>
55+
<p>
56+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aliquid
57+
voluptate consequatur earum deleniti mollitia aliquam, quae distinctio
58+
culpa praesentium alias eveniet cum possimus! Sit saepe cupiditate
59+
adipisci optio dolorem qui.
60+
</p>
61+
<Button label='Open Modal' primary={true} onClick={() => reopenModal()} />
62+
<Modal open={showModal} onClose={() => cancel()}>
63+
<ModalHeader text='Do you want to continue?' />
64+
<ModalBody {...args}>
65+
<p>
66+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quae rerum
67+
consequatur accusamus fuga iure laudantium, inventore aliquam animi
68+
facilis soluta, exercitationem nihil quasi blanditiis ex aut error
69+
placeat dolorum voluptate.
70+
</p>
71+
</ModalBody>
72+
<ModalFooter {...args}>
73+
<div>
74+
<Button
75+
primary={true}
76+
label='Accept'
77+
onClick={() => processOnClick()}
78+
/>
79+
<Button primary={false} label='Cancel' onClick={() => cancel()} />
80+
</div>
81+
</ModalFooter>
82+
</Modal>
83+
</div>
84+
);
85+
};
86+
87+
Basic.args = {
88+
align: 'left'
89+
};
90+
91+
export const StaticBackground = (args) => {
92+
const [showModal, setShowModal] = useState(true);
93+
useEffect(() => {}, [showModal]);
94+
95+
function processOnClick() {
96+
setShowModal(false);
97+
alert('Accept button pressed');
98+
}
99+
100+
function cancel() {
101+
setShowModal(false);
102+
alert('Cancel button pressed');
103+
}
104+
105+
function reopenModal() {
106+
setShowModal(true);
107+
}
108+
return (
109+
<div>
110+
<p>
111+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Perferendis ut
112+
est repellendus ab. Eaque vero ipsa asperiores provident, sed dolore
113+
iste error nemo voluptatum, deserunt vitae totam ea reprehenderit
114+
voluptates.
115+
</p>
116+
<p>
117+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aliquid
118+
voluptate consequatur earum deleniti mollitia aliquam, quae distinctio
119+
culpa praesentium alias eveniet cum possimus! Sit saepe cupiditate
120+
adipisci optio dolorem qui.
121+
</p>
122+
<p>
123+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aliquid
124+
voluptate consequatur earum deleniti mollitia aliquam, quae distinctio
125+
culpa praesentium alias eveniet cum possimus! Sit saepe cupiditate
126+
adipisci optio dolorem qui.
127+
</p>
128+
<Button label='Open Modal' primary={true} onClick={() => reopenModal()} />
129+
<Modal open={showModal} onClose={() => cancel()} staticBackground={true}>
130+
<ModalHeader text='Do you want to continue?' />
131+
<ModalBody>
132+
<p>
133+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quae rerum
134+
consequatur accusamus fuga iure laudantium, inventore aliquam animi
135+
facilis soluta, exercitationem nihil quasi blanditiis ex aut error
136+
placeat dolorum voluptate.
137+
</p>
138+
</ModalBody>
139+
<ModalFooter>
140+
<div>
141+
<Button
142+
primary={true}
143+
label='Accept'
144+
onClick={() => processOnClick()}
145+
/>
146+
<Button primary={false} label='Cancel' onClick={() => cancel()} />
147+
</div>
148+
</ModalFooter>
149+
</Modal>
150+
</div>
151+
);
152+
};
153+
154+
StaticBackground.args = {
155+
align: 'left'
156+
};

src/components/Modal/Modal.tsx

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import React, { useState, useEffect, useRef } from 'react';
2+
import ReactDom from 'react-dom';
3+
import './modal.scss';
4+
5+
interface ModalProps {
6+
children?: JSX.Element[];
7+
onClose?: () => void;
8+
open: boolean;
9+
staticBackground?: boolean;
10+
}
11+
12+
interface ModalElementsProps {
13+
children: JSX.Element | JSX.Element[];
14+
text?: string;
15+
align?: string;
16+
}
17+
18+
interface ModalHeaderProps {
19+
text: string;
20+
closeModal?: () => void;
21+
}
22+
23+
class ErrorBoundary extends React.Component<{}, { hasError: boolean }> {
24+
constructor(props) {
25+
super(props);
26+
this.state = { hasError: false };
27+
}
28+
29+
static getDerivedStateFromError(error) {
30+
// Update state so the next render will show the fallback UI.
31+
return { hasError: true };
32+
}
33+
34+
render() {
35+
if (this.state.hasError) {
36+
// You can render any custom fallback UI
37+
return <h1>Something went wrong.</h1>;
38+
}
39+
40+
return this.props.children;
41+
}
42+
}
43+
44+
const Modal = ({
45+
children,
46+
onClose,
47+
open,
48+
staticBackground
49+
}: ModalProps): JSX.Element => {
50+
const [showModal, setShowModal] = useState(null);
51+
const wrapperRef = useRef(null);
52+
53+
useEffect(() => {
54+
if (!staticBackground) {
55+
function handleClickOutside(event) {
56+
if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
57+
onClose();
58+
setShowModal(null);
59+
}
60+
}
61+
// Bind the event listener
62+
document.addEventListener('mousedown', handleClickOutside);
63+
return () => {
64+
// Unbind the event listener on clean up
65+
document.removeEventListener('mousedown', handleClickOutside);
66+
};
67+
}
68+
}, [wrapperRef]);
69+
70+
useEffect(() => {
71+
if (open && !showModal) {
72+
setShowModal(true);
73+
} else if (!open && showModal) {
74+
setShowModal(null);
75+
}
76+
}, [open]);
77+
78+
function closeModal() {
79+
onClose();
80+
setShowModal(null);
81+
}
82+
83+
return showModal
84+
? ReactDom.createPortal(
85+
<ErrorBoundary>
86+
<div className='modal-background'>
87+
<div ref={wrapperRef} className='modal'>
88+
{React.Children.map(children, (child, i) => {
89+
if (child.type === ModalHeader) {
90+
return React.cloneElement(child, {
91+
...child.props,
92+
closeModal
93+
});
94+
}
95+
return child;
96+
})}
97+
</div>
98+
</div>
99+
</ErrorBoundary>,
100+
document.body
101+
)
102+
: null;
103+
};
104+
105+
const ModalHeader = ({ closeModal, text }: ModalHeaderProps): JSX.Element => {
106+
return (
107+
<div className='modal-header'>
108+
<strong>{text}</strong>
109+
<span className='modal-close' onClick={() => closeModal()}>
110+
&times;
111+
</span>
112+
</div>
113+
);
114+
};
115+
116+
const ModalBody = ({ children, align }: ModalElementsProps): JSX.Element => {
117+
const alignClass = align ? align : '';
118+
return <div className={`modal-body ${alignClass}`}>{children}</div>;
119+
};
120+
121+
const ModalFooter = ({ children, align }: ModalElementsProps): JSX.Element => {
122+
const alignClass = align ? align : '';
123+
return <div className={`modal-footer ${alignClass}`}>{children}</div>;
124+
};
125+
126+
export { Modal, ModalHeader, ModalBody, ModalFooter };

src/components/Modal/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { Modal } from './Modal';

0 commit comments

Comments
 (0)