Skip to content
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
172 changes: 172 additions & 0 deletions src/components/Dialog/Dialog.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogList,
DialogTitle,
DialogListItem,
DialogListItemAvatar,
DialogListItemText,
} from './Dialog';
import React from 'react';
import { DialogProps } from './Dialog';
import Button from '../Button';
import Input from '../Input';
import Message from '../Message';
import Icon from '../Icon';
import { action } from '@storybook/addon-actions';
import { useState } from 'react';

export default {
title: 'Dialog',
component: Dialog,
};

const emails = ['[email protected]', '[email protected]'];

/**
*
* @returns A dialog component with a list of clickable items
*/
export const Simple = () => {
//Controlling the open and close of the dialog
const [open, setOpen] = useState(true);
const setModalOpen = () => {
setOpen(!open);
};

return (
<>
{/* A button to open back the dialog when closed */}
<Button btnSize="sm" btnType="primary" onClick={setModalOpen}>
Toggle Dialog
</Button>

<Dialog
onClose={action('Dialog closed')}
open={open}
setModalOpen={setModalOpen}
>
<DialogTitle>Set backup account</DialogTitle>
<DialogList>
{emails.map((email) => (
<DialogListItem key={email} handler={action('Account clicked')}>
<DialogListItemAvatar>
<Icon color="blue" name="heart" />
</DialogListItemAvatar>
<DialogListItemText>{email}</DialogListItemText>
</DialogListItem>
))}
<DialogListItem handler={action('Add button clicked')}>
<DialogListItemAvatar>
<Icon color="grey" name="plus" />
</DialogListItemAvatar>
<DialogListItemText>Add account</DialogListItemText>
</DialogListItem>
</DialogList>
</Dialog>
</>
);
};

/**
*
* @returns A dialog component which has a text description and two action buttons
*/
export const Alerts = () => {
//Controlling the open and close of the dialog
const [open, setOpen] = useState(true);
const setModalOpen = () => {
setOpen(!open);
};

return (
<>
{/* A button to open back the dialog when closed */}
<Button btnSize="sm" btnType="primary" onClick={setModalOpen}>
Toggle Dialog
</Button>
<Dialog
onClose={action('Dialog closed')}
open={open}
setModalOpen={setModalOpen}
>
<DialogTitle>Use Google's location service?</DialogTitle>
<DialogContent>
<DialogContentText>
Let Google help apps determine location. This means sending
anonymous location data to Google, even when no apps are running.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
btnSize="sm"
btnType="default"
onClick={action('Disagree button clicked')}
>
DISAGREE
</Button>
<Button
btnSize="sm"
btnType="default"
onClick={action('Agree button clicked')}
>
AGREE
</Button>
</DialogActions>
</Dialog>
</>
);
};

/**
*
* @returns A dialog component which has a text description, an input, and two action buttons
*/
export const Form = () => {
//Controlling the open and close of the dialog
const [open, setOpen] = useState(true);
const setModalOpen = () => {
setOpen(!open);
};

return (
<>
{/* A button to open back the dialog when closed */}
<Button btnSize="sm" btnType="primary" onClick={setModalOpen}>
Toggle Dialog
</Button>

<Dialog
onClose={action('Dialog closed')}
open={open}
setModalOpen={setModalOpen}
>
<DialogTitle>Subscribe</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here.
We will send updates occasionally.
</DialogContentText>
<Input fluid placeholder="Search..." inputSize="small" />
</DialogContent>
<DialogActions>
<Button
btnSize="sm"
btnType="default"
onClick={action('Cancel button clicked')}
>
CANCEL
</Button>
<Button
btnSize="sm"
btnType="default"
onClick={action('Subscribe button clicked')}
>
SUBSCRIBE
</Button>
</DialogActions>
</Dialog>
</>
);
};
29 changes: 29 additions & 0 deletions src/components/Dialog/Dialog.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Dialog from './Dialog';
import React from 'react';
import { render, screen } from '@testing-library/react';
import { Simple, Form, Alerts } from './Dialog.stories';

describe('Simple Dialog', () => {
test('renders the prebuilt simple dialog', () => {
render(<Simple />);
const title = screen.getByText('Set backup account');
expect(title).toBeInTheDocument();
});
});

describe('Form Dialog', () => {
test('renders the prebuilt form dialog', () => {
render(<Form />);

const title = screen.getByText('Subscribe');
expect(title).toBeInTheDocument();
});
});

describe('Alerts Dialog', () => {
test('renders the prebuilt alerts dialog', () => {
render(<Alerts />);
const title = screen.getByText("Use Google's location service?");
expect(title).toBeInTheDocument();
});
});
141 changes: 141 additions & 0 deletions src/components/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { BasePrivateKeyEncodingOptions } from 'crypto';
import React, { ReactNode, useEffect, useState } from 'react';
import Button from '../Button';
import './_Dialog.scss';

export interface DialogProps {
onClose: () => void;
setModalOpen: () => void;
open: boolean;
children: ReactNode;
}

interface DialogTitleProps {
children: string;
}

interface DialogContentProps {
children?: ReactNode;
}

interface DialogContentTextProps {
children: string;
}

interface DialogActionsProps {
children: ReactNode;
}

interface DialogListProps {
children: ReactNode;
}

interface DialogListItemProps {
handler: () => void;
children: ReactNode;
}

interface DialogListItemAvatarProps {
children: ReactNode;
}

interface DialogListItemTextProps {
children: string;
}

const Dialog = ({ children, open, onClose, setModalOpen }: DialogProps) => {
useEffect(() => {
if (open) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'unset';
onClose();
}
}, [open]);

return (
<div className="backdrop" onClick={setModalOpen}>
{open && (
<div className="card" onClick={(e) => e.stopPropagation()}>
{children}
</div>
)}
</div>
);
};

/**
* This is the heading of the dialog
* @param props with a string prop
* @returns a title with the specified text given in children
*/
export const DialogTitle = ({ children }: DialogTitleProps) => {
return <p className="title">{children}</p>;
};

/**
* This is the content of the dialog
* @param props with children prop
* @returns the children with styling
*/
export const DialogContent = ({ children }: DialogContentProps) => {
return <div className="content">{children}</div>;
};

/**
* This is the text within the content of the dialog
* @param props with a string prop
* @returns a text given in children
*/
export const DialogContentText = ({ children }: DialogContentTextProps) => {
return <p>{children}</p>;
};

/**
* This is where the action buttons for the dialog are placed
* @param props with children prop
* @returns the buttons wrapped in a div with styling
*/
export const DialogActions = ({ children }: DialogActionsProps) => {
return <div className="actions">{children}</div>;
};

/**
* This is used for showing a list
* @param props with children prop
* @returns the list wrapped in ul with styling
*/
export const DialogList = ({ children }: DialogListProps) => {
return <ul className="list">{children}</ul>;
};

/**
* This is used for each list item in a list
* @param props with children prop and its handler
* @returns a list item with a handler along with its children
*/
export const DialogListItem = ({ handler, children }: DialogListItemProps) => {
return <li onClick={handler}>{children}</li>;
};

/**
* This is for adding an icon with each list item
* @param props with children
* @returns a children which are icons
*/
export const DialogListItemAvatar = ({
children,
}: DialogListItemAvatarProps) => {
return <>{children}</>;
};

/**
* This is for the text of each list item
* @param props with children
* @returns a children of text
*/
export const DialogListItemText = ({ children }: DialogListItemTextProps) => {
return <p>{children}</p>;
};

export default Dialog;
49 changes: 49 additions & 0 deletions src/components/Dialog/_Dialog.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.card {
padding: 16px 24px;
border-radius: 4px;
display: inline-block;
max-width: 600px;

background: #ffffff;
box-shadow: 0px 11px 15px rgba(0, 0, 0, 0.2),
0px 24px 38px rgba(0, 0, 0, 0.14), 0px 9px 46px rgba(0, 0, 0, 0.12);
border-radius: 4px;

.title {
width: 100%;
font-size: 20px;
font-weight: 500;
line-height: 32px;
}

.list {
margin: 0;
padding: 0;

li {
cursor: pointer;
padding-top: 5px;
display: flex;
gap: 15px;
list-style: none;
}
}

.content {
p {
margin-top: 24px;
margin-bottom: 24px;
font-weight: 400;
font-size: 16px;
line-height: 24px;
color: rgba(0, 0, 0, 0.54);
}
}

.actions {
margin-top: 24px;
display: flex;
gap: 5px;
justify-content: end;
}
}