Modal component (#20)
* Add input name props * Rename classname attribute and add attributes for input component * Use value attribute for select * Remove unused import * Improve Sheets component * Add Modal component * Add displayActionButton and provide no button story
This commit is contained in:
parent
ba52e9afa4
commit
e81bc9ac42
|
@ -0,0 +1,103 @@
|
|||
import { ReactNode } from "react";
|
||||
import { Backdrop } from "../Backdrop/Backdrop";
|
||||
import { Button } from "../Button/Button";
|
||||
import { classnames } from "../utils/classnames";
|
||||
import "./modal.css";
|
||||
|
||||
type Props = {
|
||||
open: boolean;
|
||||
|
||||
/**
|
||||
* Event triggered whenever the close button is clicked.
|
||||
*/
|
||||
onClose: () => void;
|
||||
|
||||
/**
|
||||
* If true the close button will be displayed
|
||||
* Default: true
|
||||
*/
|
||||
displayCloseButton?: boolean;
|
||||
|
||||
/**
|
||||
* If true, the action button will be disabled.
|
||||
* Default: false
|
||||
*/
|
||||
displayActionButton?: boolean;
|
||||
|
||||
/**
|
||||
* Event triggered whenever the action button is clicked.
|
||||
* The action button should be considered the "primary" action.
|
||||
*/
|
||||
onAction?: () => void;
|
||||
|
||||
/**
|
||||
* Change the label of the close button.
|
||||
* Default: Close
|
||||
*/
|
||||
labelCloseButton?: string;
|
||||
|
||||
/**
|
||||
* If true, the disable button will be disabled.
|
||||
*/
|
||||
disableCloseButton?: boolean;
|
||||
|
||||
/**
|
||||
* Change the label of the close button.
|
||||
* Default: Action
|
||||
*/
|
||||
labelActionButton?: string;
|
||||
|
||||
/**
|
||||
* If true, the action button will be disabled.
|
||||
*/
|
||||
disableActionButton?: boolean;
|
||||
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export function Modal({
|
||||
open,
|
||||
onClose,
|
||||
disableActionButton,
|
||||
disableCloseButton,
|
||||
displayCloseButton = true,
|
||||
displayActionButton = false,
|
||||
labelActionButton = "Action",
|
||||
labelCloseButton = "Close",
|
||||
children,
|
||||
onAction,
|
||||
}: Props) {
|
||||
return (
|
||||
<>
|
||||
<Backdrop open={open} onClose={onClose} removeScroll={true} />
|
||||
|
||||
<div className={classnames(["modal"], ["modal--open", open])}>
|
||||
<div className="modal-body">{children}</div>
|
||||
|
||||
<div
|
||||
className={classnames(
|
||||
["modal-buttons--between", !!onAction],
|
||||
["modal-buttons--center", !onAction]
|
||||
)}
|
||||
>
|
||||
{displayCloseButton && (
|
||||
<Button
|
||||
label={labelCloseButton}
|
||||
variant="outline"
|
||||
onClick={onClose}
|
||||
disabled={disableCloseButton}
|
||||
/>
|
||||
)}
|
||||
|
||||
{displayActionButton && (
|
||||
<Button
|
||||
label={labelActionButton}
|
||||
onClick={onAction}
|
||||
disabled={disableActionButton}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
.modal {
|
||||
transition: transform 0.15s;
|
||||
max-width: 800px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
max-height: 100%;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
position: fixed;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--codex-background);
|
||||
padding: 1.5rem;
|
||||
border-radius: var(--codex-border-radius);
|
||||
}
|
||||
|
||||
.modal--open {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
|
||||
.modal--open {
|
||||
opacity: 1;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.modal-buttons--center {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-buttons--between {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
@media (min-width: 801px) {
|
||||
.modal {
|
||||
min-width: 500px;
|
||||
}
|
||||
}
|
|
@ -34,3 +34,4 @@ export { Collapse } from "./components/Collapse/Collapse";
|
|||
export { Placeholder } from "./components/Placeholder/Placeholder";
|
||||
export { Sheets } from "./components/Sheets/Sheets";
|
||||
export { Tabs } from "./components/Tabs/Tabs";
|
||||
export { Modal } from "./components/Modal/Modal";
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import type { Meta } from "@storybook/react";
|
||||
import { useState } from "react";
|
||||
import { Modal } from "../src/components/Modal/Modal";
|
||||
import { fn } from "@storybook/test";
|
||||
|
||||
const meta = {
|
||||
title: "Overlays/Modal",
|
||||
component: Modal,
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
inlineStories: false,
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
argTypes: {},
|
||||
args: {
|
||||
onAction: fn(),
|
||||
onClose: fn(),
|
||||
},
|
||||
} satisfies Meta<typeof Modal>;
|
||||
|
||||
export default meta;
|
||||
|
||||
type Props = {
|
||||
onAction: () => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const Template = (props: Props) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const onOpen = () => setOpen(true);
|
||||
|
||||
const onClose = () => {
|
||||
props.onClose();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: "6rem" }}>
|
||||
<button onClick={onOpen}>Make Modal</button>
|
||||
<Modal onClose={onClose} open={open}>
|
||||
<p>Hello world</p>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
const ActionTemplate = (props: Props) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const onOpen = () => setOpen(true);
|
||||
|
||||
const onClose = () => {
|
||||
props.onClose();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const onAction = () => {
|
||||
props.onAction();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: "6rem" }}>
|
||||
<button onClick={onOpen}>Make Modal</button>
|
||||
<Modal onClose={onClose} open={open} onAction={onAction}>
|
||||
<p>Hello world</p>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Action = ActionTemplate.bind({});
|
||||
|
||||
const NoButtonTemplate = (props: Props) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const onOpen = () => setOpen(true);
|
||||
|
||||
const onClose = () => {
|
||||
props.onClose();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: "6rem" }}>
|
||||
<button onClick={onOpen}>Make Modal</button>
|
||||
<Modal onClose={onClose} open={open} displayCloseButton={false}>
|
||||
<p>Hello world</p>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const NoButton = NoButtonTemplate.bind({});
|
Loading…
Reference in New Issue