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

Modal component #111

Open
wants to merge 4 commits into
base: frontend
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion ui/src/components/Icon/Icon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { IoMdHeart } from 'react-icons/io'
import { AiOutlineClockCircle } from 'react-icons/ai'
import { BsCircleFill } from 'react-icons/bs'
import { GrClose } from 'react-icons/gr'

import styles from './Icon.module.scss'
import classNames from 'classnames'
Expand All @@ -28,7 +29,8 @@ export const iconNames = {
clock: AiOutlineClockCircle,
timer: MdOutlineTimer,
flag: MdFlag,
dot: BsCircleFill
dot: BsCircleFill,
close: GrClose
}

export const iconColors = ['blue', 'navy', 'white', 'black', 'grey', 'red']
Expand Down
128 changes: 128 additions & 0 deletions ui/src/components/Modal/Modal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { useEffect, useRef } from 'react'
import styles from './Modal.module.scss'
import PropTypes from 'prop-types'
import { ButtonIcon } from '../ButtonIcon/ButtonIcon'
import Button from '../Button/Button'
import { createPortal } from 'react-dom'

/**
* ### example:
* ```
* import Modal from 'components/Modal/Modal'
* const [open, setOpen] = useState(false)
* return (
* <>
* <button
* onClick={() => {
* setOpen(true)
* }}
* >
* Open Modal
* </button>
* <Modal open={open} onClose={() => setOpen(false)} />
* </>
* )
* ```
* ### props:
* - title - title of the modal
* - children - content of the modal
* - variant - type of modal
* - default - standard modal
* - confirm - modal with confirm button
* - modal - boolean value that controls modal type
* - true - modal, default value
* - false - dialog, allows interaction with the rest of the page
* - open - state of the modal
* - onClose - function to change state of the modal
*
* ### reference:
* - [dialog](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)
* */
function Modal({ title, children, modal, variant, open, onClose }) {
let showConfirm
Copy link
Collaborator

Choose a reason for hiding this comment

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

Możesz tutaj użyć ternary operator i usunąć tego leta. Chyba, że masz jakiś głębszy zamysł z użyciem let w tym przypadku 😅

Suggested change
let showConfirm
const showConfirm = variant === "confirm" ? true : false;

Copy link
Member Author

Choose a reason for hiding this comment

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

Zrobiłem tak w razie wypadku gdyby w przyszłości miałoby się pojawić więcej wariantów, niżej jest switch, który by tym potem sterował

switch (variant) {
case 'confirm':
showConfirm = true
break
default:
showConfirm = false
break
}

const dialog = useRef(null)

const onCancel = () => {
onClose(false)
}

const close = () => {
onClose(false)
}

useEffect(() => {
if (open && !dialog.current.open) {
modal ? dialog.current.showModal() : dialog.current.show()
}
if (!open && dialog.current.open) {
dialog.current.close()
}
}, [open])

return (
<>
{createPortal(
<dialog
className={styles.modal}
ref={dialog}
onCancel={onCancel}
onClose={close}
>
<h3>{title}</h3>
<hr />
<p>{children}</p>
<form method="dialog">
<ButtonIcon
name={'close'}
className={styles.close}
size={'small'}
color={'white'}
/>
{showConfirm && (
<Button style={styles.confirm} variant={'light-red'}>
Confirm
</Button>
)}
</form>
</dialog>,
document.body
)}
</>
)
}

Modal.propTypes = {
/** title of the modal */
title: PropTypes.string.isRequired,
/** content of the modal */
children: PropTypes.string.isRequired,
/** modal or dialog, set to false allows interaction with the rest of the page */
modal: PropTypes.bool,
/** variant of the modal, if not set, modal will be default
* - default: standard modal
* - confirm: adds confirm button
*/
variant: PropTypes.oneOf(['confirm']),
/** state of the modal */
open: PropTypes.bool.isRequired,
/** function to change state of the modal */
onClose: PropTypes.func.isRequired
}

Modal.defaultProps = {
title: 'Tytuł modala',
children:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.',
modal: true
}

export default Modal
65 changes: 65 additions & 0 deletions ui/src/components/Modal/Modal.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
@use '../../assets/styles/abstracts/variables' as *;

.modal {
position: fixed;
z-index: 1;
border-radius: 0.5rem;
border: 0.375rem solid $grayscale-g700;
background-color: $alternative-a300;
box-shadow: -0.375rem 0.375rem 0.25rem 0px rgba(0, 0, 0, 0.25);
color: $base-black;
text-align: center;
inset: 0;
max-width: max(30%, 25rem);
font-size: calc($font-size-p * 1px);
font-weight: 600;
box-sizing: border-box;
padding: 2.25rem;

p {
line-height: 1.5rem;
margin-block: 2rem;
}

h3 {
font-size: calc($font-size-h3 * 1px);
margin-block: 0;
font-weight: 600;
}

hr {
color: #171717;
}
}

button.close {
position: absolute;
top: 0;
right: 0;
margin: 1.25rem;
padding: 0.3125rem;
outline: none;
border: 0.1875rem solid $alternative-a900;
border-radius: $border-radius-round;

background-color: $alternative-a900;

&:hover,
&:focus-visible {
background-color: $alternative-a500;
}

path {
stroke: $base-white;
}
}

button.confirm {
color: $alternative-a900;
font-family: Poppins;
font-size: calc($font-size-button-lg * 1px);
font-weight: 600;
border-radius: $border-radius-md;
padding: 0.5625rem 1.875rem;
min-width: 70%;
}
62 changes: 62 additions & 0 deletions ui/src/components/Modal/Modal.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useState } from 'react'
import Modal from './Modal'
import '../../assets/styles/index.scss'

export default {
component: Modal,

parameters: {
layout: 'centered',
componentSubtitle: 'A modal component using dialog element.'
},

argTypes: {
variant: {
control: false
},
open: {
control: false
},
onClose: {
control: false
}
}
}

const Template = (args) => {
const [open, setOpen] = useState(false)

return (
<>
<button
style={{
position: 'absolute',
top: '5rem',
left: '5rem'
}}
onClick={() => {
console.log('clicked')
}}
>
try clicking me
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ten button jest chyba niepotrzebny 😅

Copy link
Member Author

Choose a reason for hiding this comment

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

Chciałem jakoś pokazać, że można wchodzić w interakcję z resztą strony, kiedy prop modal ma wartość false

</button>
<button
onClick={() => {
setOpen(true)
}}
>
Open Modal
</button>
<Modal {...args} open={open} onClose={() => setOpen(false)} />
</>
)
}

export const Default = Template.bind({})
Default.storyName = 'Default modal'

export const Confirm = Template.bind({})
Confirm.args = {
variant: 'confirm'
}
Confirm.storyName = 'Modal with confirm button'
Loading