diff --git a/.storybook/modal.stories.tsx b/.storybook/modal.stories.tsx new file mode 100644 index 0000000..ddfa495 --- /dev/null +++ b/.storybook/modal.stories.tsx @@ -0,0 +1,92 @@ +import { ThemeProvider } from 'styled-components'; +import { Button, Checkbox, Modal } from '../src'; +import { theme } from '../src/theme'; +import { GlobalStyles } from '../src/globalStyles'; +import type { Meta, StoryObj } from '@storybook/react-vite'; +import React, { useState } from 'react'; + +const meta = { + component: Modal, + decorators: [ + (Story) => ( +
+ + + + +
+ ), + ], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + title: 'Title', + onSubmit: () => console.log('Submit'), + onCancel: () => console.log('Cancel'), + close: () => console.log('Closing'), + }, + render: (args) => ( + +

Message

+
+ ), +}; + +export const Example: Story = { + args: { + title: 'Title', + onSubmit: () => console.log('Submit'), + onCancel: () => console.log('Cancel'), + close: () => console.log('Closing'), + requirementsMet: () => false, + errorMessage: 'There was an error!', + $maxWidth: '20%', + }, + render: (args) => { + const [firstBox, setFirstBox] = useState(false); + const [secondBox, setSecondBox] = useState(false); + const [visible, setVisible] = useState(false); + + const checkRequirements = (): boolean => { + return firstBox && secondBox ? true : false; + }; + + return ( + <> + + {visible && ( + setVisible(false)} + requirementsMet={checkRequirements} + > +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent + sollicitudin sem lectus, vitae convallis urna ultricies at. Sed + hendrerit bibendum urna. Praesent arcu sapien, porta blandit neque + cursus, scelerisque ullamcorper ligula. +

+ setFirstBox(!firstBox)} + > + Accept the terms + + setSecondBox(!secondBox)} + > + Accept the terms for marketing info + +
+ )} + + ); + }, +}; diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index ff00eef..df3c212 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -13,6 +13,7 @@ export const Checkbox = ({ name, value, onClick, + padding = '8px', }: CheckboxProps) => { const labelId = useId(); @@ -21,7 +22,14 @@ export const Checkbox = ({ }; return ( -
+
void; + padding?: string; } export type CheckboxSizeVariant = 'small' | 'medium' | 'big'; diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx new file mode 100644 index 0000000..c64cb0f --- /dev/null +++ b/src/components/Modal/Modal.tsx @@ -0,0 +1,76 @@ +import { Button } from '../../index.js'; +import { + ModalBackground, + ModalButtonContainer, + ModalComponent, + ModalErrorMessage, + ModalTitle, +} from './styles.js'; +import type { ModalProps } from './types.js'; +import React, { useState } from 'react'; + +export const Modal = ({ + title, + children, + onSubmit, + onCancel, + close, + submitMessage = 'Submit', + cancelMessage = 'Cancel', + requirementsMet, + errorMessage, + mandatory = false, + $maxWidth, +}: ModalProps) => { + const [error, setError] = useState(false); + + const handleClick = (event: 'submit' | 'cancel') => { + if (requirementsMet && event === 'submit') { + if (requirementsMet() === true) { + close(); + onSubmit(); + } else { + setError(true); + } + + return; + } + + close(); + event === 'submit' ? onSubmit() : onCancel(); + }; + + return ( + mandatory === false && close()}> + { + e.stopPropagation(); + if (error) setError(false); + }} + $maxWidth={$maxWidth} + > + {title} + {children} + + + + + {error && {errorMessage}} + + + ); +}; diff --git a/src/components/Modal/styles.ts b/src/components/Modal/styles.ts new file mode 100644 index 0000000..a283898 --- /dev/null +++ b/src/components/Modal/styles.ts @@ -0,0 +1,52 @@ +import { styled } from 'styled-components'; +import { theme } from '../../theme.js'; + +export const ModalBackground = styled.div<{ zIndex?: number }>` + display: flex; + justify-content: center; + align-items: center; + + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.2); + + z-index: ${({ zIndex }) => (zIndex ? zIndex : 1000)}; +`; + +export const ModalComponent = styled.div<{ + zIndex?: number; + $maxWidth?: string; +}>` + z-index: ${({ zIndex }) => (zIndex ? zIndex + 1 : 1001)}; + + display: flex; + flex-direction: column; + + padding: 16px; + border-radius: 8px; + max-width: ${({ $maxWidth }) => + $maxWidth !== undefined ? $maxWidth : '50%'}; + + background-color: ${theme.colors.main}; + box-shadow: 0px 5px 5px -2px ${theme.colors.lightGray}; +`; + +export const ModalTitle = styled.h1` + margin: 0; +`; + +export const ModalButtonContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 5px; +`; + +export const ModalErrorMessage = styled.p` + color: red; + margin-bottom: 0; +`; diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts new file mode 100644 index 0000000..3df853e --- /dev/null +++ b/src/components/Modal/types.ts @@ -0,0 +1,14 @@ +export interface ModalProps { + title: string; + children?: React.ReactNode; + onSubmit: () => void; + onCancel: () => void; + close: () => void; + requirementsMet?: () => boolean; + errorMessage?: string; + mandatory?: boolean; + $maxWidth?: string; + + submitMessage?: string; + cancelMessage?: string; +} diff --git a/src/index.tsx b/src/index.tsx index 5f21a82..fc0188f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,3 +4,4 @@ export { Input } from './components/Input/Input.js'; export { Select } from './components/Select/Select.js'; export { SelectChild } from './components/Select/SelectChild.js'; export { Checkbox } from './components/Checkbox/Checkbox.js'; +export { Modal } from './components/Modal/Modal.js';