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 { Placeholder } from "./components/Placeholder/Placeholder";
|
||||||
export { Sheets } from "./components/Sheets/Sheets";
|
export { Sheets } from "./components/Sheets/Sheets";
|
||||||
export { Tabs } from "./components/Tabs/Tabs";
|
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