diff --git a/src/shared/ui/index.ts b/src/shared/ui/index.ts index fa105d3..967860b 100644 --- a/src/shared/ui/index.ts +++ b/src/shared/ui/index.ts @@ -1,5 +1,5 @@ export { Button } from "./button"; -export { Input } from "./input"; +export { Input, Textarea, Select } from "./inputBox/inputBox"; export { DropdownMenu, SelectDropdown } from "./dropdown"; export { Pagination } from "./pagination/pagination"; export type { PaginationProps } from "./pagination/pagination"; @@ -10,5 +10,8 @@ export type { NavigationProps, NavItem, NavUser } from "./navigation/navigation" export * from "./form"; export * from "./checkbox"; export * from "./file-uploader"; + +// Modal components +export * from "./modal"; export { Tabs } from "./tabs/tabs"; export type { TabsProps, TabItem, TabVariant } from "./tabs/tabs"; diff --git a/src/shared/ui/lists/lists.stories.tsx b/src/shared/ui/lists/lists.stories.tsx index 874296f..5c9ba8e 100644 --- a/src/shared/ui/lists/lists.stories.tsx +++ b/src/shared/ui/lists/lists.stories.tsx @@ -155,8 +155,7 @@ const InteractiveTable = (): React.ReactElement => { const [data, setData] = useState(initialTableData); const selectableRows = data.filter((r) => !r.isDisabled); - const isAllChecked = - selectableRows.length > 0 && selectableRows.every((r) => r.isSelected); + const isAllChecked = selectableRows.length > 0 && selectableRows.every((r) => r.isSelected); const handleRowCheck = (id: string, isChecked: boolean) => { setData((prev) => prev.map((row) => (row.id === id ? { ...row, isSelected: isChecked } : row))); diff --git a/src/shared/ui/modal/index.ts b/src/shared/ui/modal/index.ts new file mode 100644 index 0000000..031608e --- /dev/null +++ b/src/shared/ui/modal/index.ts @@ -0,0 +1 @@ +export * from "./modal"; diff --git a/src/shared/ui/modal/modal.stories.tsx b/src/shared/ui/modal/modal.stories.tsx new file mode 100644 index 0000000..2d0568a --- /dev/null +++ b/src/shared/ui/modal/modal.stories.tsx @@ -0,0 +1,165 @@ +import { useState } from "react"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { Modal, ModalHeader, ModalContent, ModalFooter } from "./modal"; +import { Button } from "@/shared/ui/button/button"; +import { Checkbox } from "@/shared/ui/checkbox/checkbox"; +import { FormField, FormLabel } from "@/shared/ui/form"; +import { Input, Textarea, Select } from "@/shared/ui/inputBox/inputBox"; + +const meta = { + title: "Shared/UI/Modal", + component: Modal, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// --- 1. Portfolio Filter Modal Example --- +const FilterModalExample = ({ + isOpen: propsIsOpen, + onClose: propsOnClose, +}: { + isOpen?: boolean; + onClose?: () => void; +}) => { + const [internalIsOpen, setInternalIsOpen] = useState(false); + const isOpen = propsIsOpen ?? internalIsOpen; + const onClose = propsOnClose ?? (() => setInternalIsOpen(false)); + + const categories = ["웹 개발", "모바일 앱", "UI/UX 디자인", "데이터 분석", "AI/ML", "게임 개발"]; + const departments = ["소프트웨어학과", "미디어학과", "산업공학과", "경영학과"]; + + return ( + <> + {!propsIsOpen && } + + + +
+

+ 카테고리 +

+
+ + {categories.map((cat) => ( + + ))} +
+
+ +
+ +
+

학과

+
+ + {departments.map((dept) => ( + + ))} +
+
+ + + + + + + + + ); +}; + +export const FilterModal: Story = { + render: (args) => , + args: { + isOpen: false, + onClose: () => {}, + children: null, // 필수 속성 추가 + }, +}; + +// --- 2. Profile Edit Modal Example --- +const ProfileEditModalExample = ({ + isOpen: propsIsOpen, + onClose: propsOnClose, +}: { + isOpen?: boolean; + onClose?: () => void; +}) => { + const [internalIsOpen, setInternalIsOpen] = useState(false); + const isOpen = propsIsOpen ?? internalIsOpen; + const onClose = propsOnClose ?? (() => setInternalIsOpen(false)); + + return ( + <> + {!propsIsOpen && ( + + )} + + + + + 이름 + + + + + 이메일 + + + + + 소속 + + + + + 회원 종류 +