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:
Arnaud 2024-09-20 15:34:24 +02:00 committed by GitHub
parent ba52e9afa4
commit e81bc9ac42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 257 additions and 0 deletions

View File

@ -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>
</>
);
}

View File

@ -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;
}
}

View File

@ -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";

97
stories/Modal.stories.tsx Normal file
View File

@ -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({});