Skip to content
Merged
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
92 changes: 92 additions & 0 deletions .storybook/modal.stories.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<div style={{ width: '100%' }}>
<ThemeProvider theme={theme}>
<GlobalStyles />
<Story />
</ThemeProvider>
</div>
),
],
} satisfies Meta<typeof Modal>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
title: 'Title',
onSubmit: () => console.log('Submit'),
onCancel: () => console.log('Cancel'),
close: () => console.log('Closing'),
},
render: (args) => (
<Modal {...args}>
<p>Message</p>
</Modal>
),
};

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<boolean>(false);
const [secondBox, setSecondBox] = useState<boolean>(false);
const [visible, setVisible] = useState<boolean>(false);

const checkRequirements = (): boolean => {
return firstBox && secondBox ? true : false;
};

return (
<>
<Button onClick={() => setVisible(true)}>Open</Button>
{visible && (
<Modal
{...args}
close={() => setVisible(false)}
requirementsMet={checkRequirements}
>
<p style={{ margin: 0 }}>
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.
</p>
<Checkbox
padding="16px 8px 4px 0"
value={firstBox}
onClick={() => setFirstBox(!firstBox)}
>
Accept the terms
</Checkbox>
<Checkbox
padding="4px 8px 16px 0"
value={secondBox}
onClick={() => setSecondBox(!secondBox)}
>
Accept the terms for marketing info
</Checkbox>
</Modal>
)}
</>
);
},
};
10 changes: 9 additions & 1 deletion src/components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const Checkbox = ({
name,
value,
onClick,
padding = '8px',
}: CheckboxProps) => {
const labelId = useId();

Expand All @@ -21,7 +22,14 @@ export const Checkbox = ({
};

return (
<div style={{ display: 'flex', flexDirection: 'row' }}>
<div
style={{
display: 'flex',
flexDirection: 'row',
padding: padding,
paddingLeft: 0,
}}
>
<CheckboxComponent
name={name}
aria-labelledby={labelId}
Expand Down
1 change: 1 addition & 0 deletions src/components/Checkbox/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface CheckboxProps {
name?: string;
value: boolean;
onClick: (value: boolean) => void;
padding?: string;
}

export type CheckboxSizeVariant = 'small' | 'medium' | 'big';
76 changes: 76 additions & 0 deletions src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -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<boolean>(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 (
<ModalBackground onClick={() => mandatory === false && close()}>
<ModalComponent
onClick={(e) => {
e.stopPropagation();
if (error) setError(false);
}}
$maxWidth={$maxWidth}
>
<ModalTitle>{title}</ModalTitle>
{children}
<ModalButtonContainer>
<Button
$variant="secondary"
$fullWidth
$size="small"
onClick={() => handleClick('cancel')}
>
{cancelMessage}
</Button>
<Button
$variant="primary"
$fullWidth
$size="small"
onClick={() => handleClick('submit')}
>
{submitMessage}
</Button>
</ModalButtonContainer>
{error && <ModalErrorMessage>{errorMessage}</ModalErrorMessage>}
</ModalComponent>
</ModalBackground>
);
};
52 changes: 52 additions & 0 deletions src/components/Modal/styles.ts
Original file line number Diff line number Diff line change
@@ -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;
`;
14 changes: 14 additions & 0 deletions src/components/Modal/types.ts
Original file line number Diff line number Diff line change
@@ -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;
}
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Loading