From fd4057820724493e6026a2808c093801f0cf8045 Mon Sep 17 00:00:00 2001 From: rozwader Date: Thu, 2 Jul 2026 14:08:27 +0200 Subject: [PATCH 1/3] feat: added modal component, not finished tho --- .storybook/modal.stories.tsx | 45 +++++++++++++++++++++++ src/components/Checkbox/Checkbox.tsx | 3 +- src/components/Checkbox/types.ts | 1 + src/components/Modal/Modal.tsx | 53 ++++++++++++++++++++++++++++ src/components/Modal/styles.ts | 48 +++++++++++++++++++++++++ src/components/Modal/types.ts | 12 +++++++ src/index.tsx | 1 + 7 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 .storybook/modal.stories.tsx create mode 100644 src/components/Modal/Modal.tsx create mode 100644 src/components/Modal/styles.ts create mode 100644 src/components/Modal/types.ts diff --git a/.storybook/modal.stories.tsx b/.storybook/modal.stories.tsx new file mode 100644 index 0000000..8e0da43 --- /dev/null +++ b/.storybook/modal.stories.tsx @@ -0,0 +1,45 @@ +import { ThemeProvider } from "styled-components"; +import { Checkbox, Modal } from "../src"; +import { theme } from "../src/theme"; +import { GlobalStyles } from "../src/globalStyles"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import React 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"), + requirementsMet: () => false, + errorMessage: "There was an error!" + }, + render: (args) => ( + +

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.

+ console.log("changed")}> + Accept the terms + + console.log("changed")}> + Accept the terms for marketing info + +
+ ) +}; \ No newline at end of file diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index ff00eef..4425304 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,7 @@ 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..20deaf6 --- /dev/null +++ b/src/components/Modal/Modal.tsx @@ -0,0 +1,53 @@ +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 +}: ModalProps) => { + + const [error, setError] = useState(false); + + const handleClick = (event: "submit" | "cancel") => { + if(requirementsMet && event === "submit"){ + if(requirementsMet() === true){ + close(); + event === "submit" ? onSubmit() : onCancel(); + }else{ + setError(true); + } + + return; + } + + close(); + event === "submit" ? onSubmit() : onCancel(); + } + + return( + + { + e.stopPropagation(); + }}> + {title} + {children} + + + + + {error && ( + {errorMessage} + )} + + + ) +} \ No newline at end of file diff --git a/src/components/Modal/styles.ts b/src/components/Modal/styles.ts new file mode 100644 index 0000000..0760026 --- /dev/null +++ b/src/components/Modal/styles.ts @@ -0,0 +1,48 @@ +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}>` + z-index: ${({zIndex}) => zIndex ? zIndex+1 : 1001}; + + display: flex; + flex-direction: column; + + padding: 16px; + border-radius: 8px; + max-width: 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; +` \ No newline at end of file diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts new file mode 100644 index 0000000..d6a8589 --- /dev/null +++ b/src/components/Modal/types.ts @@ -0,0 +1,12 @@ +export interface ModalProps{ + title: string; + children?: React.ReactNode; + onSubmit: () => void; + onCancel: () => void; + close: () => void; + requirementsMet?: () => boolean; + errorMessage: string; + + submitMessage?: string; + cancelMessage?: string; +} \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index 5f21a82..c17f7ff 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'; \ No newline at end of file From 89ae02116136091d2fed68988cb42024bacd9690 Mon Sep 17 00:00:00 2001 From: rozwader Date: Thu, 2 Jul 2026 14:22:14 +0200 Subject: [PATCH 2/3] feat: finishing modal --- .storybook/modal.stories.tsx | 77 +++++++++++++----- src/components/Checkbox/Checkbox.tsx | 11 ++- src/components/Modal/Modal.tsx | 113 ++++++++++++++++----------- src/components/Modal/styles.ts | 88 +++++++++++---------- src/components/Modal/types.ts | 24 +++--- src/index.tsx | 2 +- 6 files changed, 194 insertions(+), 121 deletions(-) diff --git a/.storybook/modal.stories.tsx b/.storybook/modal.stories.tsx index 8e0da43..b770f4c 100644 --- a/.storybook/modal.stories.tsx +++ b/.storybook/modal.stories.tsx @@ -1,9 +1,9 @@ -import { ThemeProvider } from "styled-components"; -import { Checkbox, Modal } from "../src"; -import { theme } from "../src/theme"; -import { GlobalStyles } from "../src/globalStyles"; -import type { Meta, StoryObj } from "@storybook/react-vite"; -import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import { 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, @@ -24,22 +24,59 @@ type Story = StoryObj; export const Default: Story = { args: { - title: "Title", - onSubmit: () => console.log("Submit"), - onCancel: () => console.log("Cancel"), - close: () => console.log("Closing"), - requirementsMet: () => false, - errorMessage: "There was an error!" + title: 'Title', + onSubmit: () => console.log('Submit'), + onCancel: () => console.log('Cancel'), + close: () => console.log('Closing'), }, render: (args) => ( -

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.

- console.log("changed")}> - Accept the terms +

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 checkRequirements = (): boolean => { + return firstBox && secondBox ? true : false; + }; + + return ( + +

+ 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 - console.log("changed")}> - Accept the terms for marketing info + setSecondBox(!secondBox)} + > + Accept the terms for marketing info -
- ) -}; \ No newline at end of file + + ); + }, +}; diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 4425304..df3c212 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -13,7 +13,7 @@ export const Checkbox = ({ name, value, onClick, - padding = "8px" + padding = '8px', }: CheckboxProps) => { const labelId = useId(); @@ -22,7 +22,14 @@ export const Checkbox = ({ }; return ( -
+
{ + const [error, setError] = useState(false); - const [error, setError] = useState(false); - - const handleClick = (event: "submit" | "cancel") => { - if(requirementsMet && event === "submit"){ - if(requirementsMet() === true){ - close(); - event === "submit" ? onSubmit() : onCancel(); - }else{ - setError(true); - } - - return; - } - + const handleClick = (event: 'submit' | 'cancel') => { + if (requirementsMet && event === 'submit') { + if (requirementsMet() === true) { close(); - event === "submit" ? onSubmit() : onCancel(); + event === 'submit' ? onSubmit() : onCancel(); + } else { + setError(true); + } + + return; } - return( - - { - e.stopPropagation(); - }}> - {title} - {children} - - - - - {error && ( - {errorMessage} - )} - - - ) -} \ No newline at end of file + 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 index 0760026..a283898 100644 --- a/src/components/Modal/styles.ts +++ b/src/components/Modal/styles.ts @@ -1,48 +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}>` - z-index: ${({zIndex}) => zIndex ? zIndex+1 : 1001}; - - display: flex; - flex-direction: column; - - padding: 16px; - border-radius: 8px; - max-width: 50%; - - background-color: ${theme.colors.main}; - box-shadow: 0px 5px 5px -2px ${theme.colors.lightGray}; -` +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; -` + margin: 0; +`; export const ModalButtonContainer = styled.div` - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - gap: 5px; -` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 5px; +`; export const ModalErrorMessage = styled.p` - color: red; - margin-bottom: 0; -` \ No newline at end of file + color: red; + margin-bottom: 0; +`; diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index d6a8589..3df853e 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,12 +1,14 @@ -export interface ModalProps{ - title: string; - children?: React.ReactNode; - onSubmit: () => void; - onCancel: () => void; - close: () => void; - requirementsMet?: () => boolean; - errorMessage: string; +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; -} \ No newline at end of file + submitMessage?: string; + cancelMessage?: string; +} diff --git a/src/index.tsx b/src/index.tsx index c17f7ff..fc0188f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,4 +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'; \ No newline at end of file +export { Modal } from './components/Modal/Modal.js'; From 9be00727bf9ba949b2f25f1a64ad4aa3d6e264f8 Mon Sep 17 00:00:00 2001 From: rozwader Date: Fri, 3 Jul 2026 14:14:30 +0200 Subject: [PATCH 3/3] feat: little refactor and better story rendering --- .storybook/modal.stories.tsx | 56 ++++++++++++++++++++-------------- src/components/Modal/Modal.tsx | 4 +-- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/.storybook/modal.stories.tsx b/.storybook/modal.stories.tsx index b770f4c..ddfa495 100644 --- a/.storybook/modal.stories.tsx +++ b/.storybook/modal.stories.tsx @@ -1,5 +1,5 @@ import { ThemeProvider } from 'styled-components'; -import { Checkbox, Modal } from '../src'; +import { Button, Checkbox, Modal } from '../src'; import { theme } from '../src/theme'; import { GlobalStyles } from '../src/globalStyles'; import type { Meta, StoryObj } from '@storybook/react-vite'; @@ -49,34 +49,44 @@ export const Example: Story = { 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 ( - -

- 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 - -
+ <> + + {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/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 98cbb4a..c64cb0f 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -20,7 +20,7 @@ export const Modal = ({ requirementsMet, errorMessage, mandatory = false, - $maxWidth + $maxWidth, }: ModalProps) => { const [error, setError] = useState(false); @@ -28,7 +28,7 @@ export const Modal = ({ if (requirementsMet && event === 'submit') { if (requirementsMet() === true) { close(); - event === 'submit' ? onSubmit() : onCancel(); + onSubmit(); } else { setError(true); }