diff --git a/src/components/MUI/DataDisplay/Avatar.stories.tsx b/src/components/MUI/DataDisplay/Avatar.stories.tsx new file mode 100644 index 0000000..b7a87a3 --- /dev/null +++ b/src/components/MUI/DataDisplay/Avatar.stories.tsx @@ -0,0 +1,100 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Avatar } from "./Avatar"; +import { AvatarGroup } from "./AvatarGroup"; + +import { Stack } from "../Layout/Stack"; +import { AssignmentIcon } from "../Icons/AssignmentIcon"; +import { PageviewIcon } from "../Icons/PageviewIcon"; +import { FolderIcon } from "../Icons/FolderIcon"; + +const meta: Meta = { + title: "MUI/Data Display/Avatar", + component: Avatar, + tags: ["autodocs"], + argTypes: { + variant: { + control: "select", + options: ["circular", "rounded", "square"], + }, + children: { name: "initials", control: "text" }, + alt: { control: "text" }, + src: { control: "text" }, + imgProps: { control: false }, + sx: { control: "object" }, + }, + args: { + variant: "circular", + children: "AB", + alt: "User avatar", + src: "", + sx: { width: 56, height: 56, bgcolor: "primary.main" }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => {args.children}, +}; + +export const Variants: Story = { + render: (_args) => ( + + + AB + + + CD + + + EF + + + ), +}; + +export const WithIcon: Story = { + render: (_args) => ( + + + + + + + + + + + + ), +}; + +export const Grouped: Story = { + render: (_args) => ( + + AB + CD + EF + IJ + KL + + ), +}; + +export const Sizes: Story = { + render: (_args) => { + return ( + + {[40, 56, 80].map((size) => ( + + {size} + + ))} + + ); + }, +}; diff --git a/src/components/MUI/DataDisplay/Avatar.tsx b/src/components/MUI/DataDisplay/Avatar.tsx new file mode 100644 index 0000000..4916615 --- /dev/null +++ b/src/components/MUI/DataDisplay/Avatar.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import MuiAvatar, { AvatarProps as MuiAvatarProps } from "@mui/material/Avatar"; + +export type AvatarProps = MuiAvatarProps; + +export const Avatar = React.forwardRef( + ({ children, ...rest }, ref) => ( + + {children} + + ), +); + +Avatar.displayName = "Avatar"; diff --git a/src/components/MUI/DataDisplay/AvatarGroup.tsx b/src/components/MUI/DataDisplay/AvatarGroup.tsx new file mode 100644 index 0000000..1e1361d --- /dev/null +++ b/src/components/MUI/DataDisplay/AvatarGroup.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; +import MuiAvatarGroup, { + AvatarGroupProps as MuiAvatarGroupProps, +} from "@mui/material/AvatarGroup"; + +export type AvatarGroupProps = MuiAvatarGroupProps; + +export const AvatarGroup = React.forwardRef( + ({ children, ...rest }, ref) => ( + + {children} + + ), +); + +AvatarGroup.displayName = "AvatarGroup"; diff --git a/src/components/MUI/DataDisplay/Badge.stories.tsx b/src/components/MUI/DataDisplay/Badge.stories.tsx new file mode 100644 index 0000000..83e7108 --- /dev/null +++ b/src/components/MUI/DataDisplay/Badge.stories.tsx @@ -0,0 +1,95 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Badge } from "./Badge"; +import { Stack } from "../Layout/Stack"; +import { colourSet } from "../../../utils/diamond"; +import { WorkIcon } from "../Icons/WorkIcon"; +import { NotificationsIcon } from "../Icons/NotificationsIcon"; +import { MailIcon } from "../Icons/MailIcon"; + +const childMap = { + mail: , + notifications: , + work: , +} as const; + +const meta: Meta = { + title: "MUI/Data Display/Badge", + component: Badge, + tags: ["autodocs"], + argTypes: { + color: { + control: "select", + options: colourSet, + }, + variant: { control: "select", options: ["standard", "dot"] }, + badgeContent: { control: "number" }, + max: { control: "number" }, + invisible: { control: "boolean" }, + showZero: { control: "boolean" }, + overlap: { control: "select", options: ["rectangular", "circular"] }, + anchorOrigin: { control: false }, + children: { + name: "child", + control: "select", + options: Object.keys(childMap), + mapping: childMap, + }, + }, + args: { + color: "primary", + variant: "standard", + badgeContent: 4, + max: 99, + invisible: false, + showZero: false, + overlap: "circular", + children: "mail", + }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { render: (args) => }; + +export const Dot: Story = { + args: { variant: "dot", color: "secondary" }, + render: (args) => , +}; + +export const InvisibleNumber: Story = { + args: { variant: "dot", color: "secondary" }, + render: (args) => , +}; + +export const Colors: Story = { + args: { badgeContent: 7 }, + render: (args) => ( + <> + + + + + + + + ), +}; + +export const DifferentChildren: Story = { + parameters: { + docs: { + description: { + story: "Badge with different child icons.", + }, + }, + }, + render: (args) => ( + + {Object.entries(childMap).map(([key, child]) => ( + + {child} + + ))} + + ), +}; diff --git a/src/components/MUI/DataDisplay/Badge.tsx b/src/components/MUI/DataDisplay/Badge.tsx new file mode 100644 index 0000000..aa686af --- /dev/null +++ b/src/components/MUI/DataDisplay/Badge.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiBadge, { BadgeProps as MuiBadgeProps } from "@mui/material/Badge"; + +export type BadgeProps = MuiBadgeProps; + +export const Badge = React.forwardRef( + (props, ref) => , +); + +Badge.displayName = "Badge"; diff --git a/src/components/MUI/DataDisplay/Chip.stories.tsx b/src/components/MUI/DataDisplay/Chip.stories.tsx new file mode 100644 index 0000000..4209e59 --- /dev/null +++ b/src/components/MUI/DataDisplay/Chip.stories.tsx @@ -0,0 +1,146 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; +import { Chip } from "./Chip"; +import { Button } from "../Inputs/Button"; +import { Stack } from "../Layout/Stack"; +import { colourSet } from "../../../utils/diamond"; +import { SaveIcon } from "../Icons/SaveIcon"; +import { SendIcon } from "../Icons/SendIcon"; +import { DeleteIcon } from "../Icons/DeleteIcon"; +import { AddIcon } from "../Icons/AddIcon"; + +const iconMap = { + none: undefined, + save: , + send: , + delete: , + add: , +} as const; + +const meta: Meta = { + title: "MUI/Data Display/Chip", + component: Chip, + tags: ["autodocs"], + argTypes: { + label: { control: "text" }, + variant: { control: "select", options: ["filled", "outlined"] }, + color: { + control: "select", + options: colourSet, + }, + size: { control: "select", options: ["small", "medium"] }, + clickable: { control: "boolean" }, + disabled: { control: "boolean" }, + icon: { + control: "select", + options: Object.keys(iconMap), + mapping: iconMap, + }, + deleteIcon: { + control: "select", + options: Object.keys(iconMap), + mapping: iconMap, + }, + onDelete: { control: false }, + sx: { control: false }, + }, + args: { + label: "Chip", + variant: "filled", + color: "primary", + size: "medium", + clickable: false, + disabled: false, + icon: undefined, + deleteIcon: undefined, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => , +}; + +export const Variants: Story = { + render: (args) => ( + + + + + ), +}; + +export const Colours: Story = { + args: { variant: "filled" }, + render: (args) => ( + + {colourSet.map((colour) => ( + + ))} + + ), +}; + +export const WithIcons: Story = { + render: (args) => ( + + + + + + + ), +}; + +export const Deletable: Story = { + parameters: { + docs: { + description: { + story: + "Chips can be removed by the user. The Reset button restores the initial set.", + }, + }, + }, + render: (args) => { + const initialChips = [ + { key: 1, label: "Item A" }, + { key: 2, label: "Item B" }, + { key: 3, label: "Item C" }, + ]; + + const [chips, setChips] = React.useState(initialChips); + + const handleDelete = (key: number) => { + setChips((prev) => prev.filter((chip) => chip.key !== key)); + }; + + const handleReset = () => { + setChips(initialChips); + }; + + return ( + + {chips.map((chip) => ( + handleDelete(chip.key)} + /> + ))} + + + + ); + }, +}; diff --git a/src/components/MUI/DataDisplay/Chip.tsx b/src/components/MUI/DataDisplay/Chip.tsx new file mode 100644 index 0000000..c62561c --- /dev/null +++ b/src/components/MUI/DataDisplay/Chip.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiChip, { ChipProps as MuiChipProps } from "@mui/material/Chip"; + +export type ChipProps = MuiChipProps; + +export const Chip = React.forwardRef( + (props, ref) => , +); + +Chip.displayName = "Chip"; diff --git a/src/components/MUI/DataDisplay/Divider.stories.tsx b/src/components/MUI/DataDisplay/Divider.stories.tsx new file mode 100644 index 0000000..107b8b4 --- /dev/null +++ b/src/components/MUI/DataDisplay/Divider.stories.tsx @@ -0,0 +1,51 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Divider } from "./Divider"; +import { Stack } from "../Layout/Stack"; + +const meta: Meta = { + title: "MUI/Data Display/Divider", + component: Divider, + tags: ["autodocs"], + argTypes: { + variant: { control: "select", options: ["fullWidth", "inset", "middle"] }, + orientation: { control: "select", options: ["horizontal", "vertical"] }, + textAlign: { control: "select", options: ["center", "left", "right"] }, + flexItem: { control: "boolean" }, + children: { name: "label", control: "text" }, + }, + args: { + variant: "fullWidth", + orientation: "horizontal", + textAlign: "center", + flexItem: false, + children: "", + }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { render: (args) => }; + +export const WithLabel: Story = { + args: { children: "Section" }, + render: (args) => , +}; + +export const Vertical: Story = { + args: { orientation: "vertical", flexItem: true }, + render: (args) => ( + + + + ), +}; + +export const Variants: Story = { + render: (args) => ( + + + + + + ), +}; diff --git a/src/components/MUI/DataDisplay/Divider.tsx b/src/components/MUI/DataDisplay/Divider.tsx new file mode 100644 index 0000000..9b2a1a4 --- /dev/null +++ b/src/components/MUI/DataDisplay/Divider.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiDivider, { + DividerProps as MuiDividerProps, +} from "@mui/material/Divider"; + +export type DividerProps = MuiDividerProps; + +export const Divider = React.forwardRef( + (props, ref) => , +); + +Divider.displayName = "Divider"; diff --git a/src/components/MUI/DataDisplay/Icon.stories.tsx b/src/components/MUI/DataDisplay/Icon.stories.tsx new file mode 100644 index 0000000..40c6ec0 --- /dev/null +++ b/src/components/MUI/DataDisplay/Icon.stories.tsx @@ -0,0 +1,59 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Icon } from "./Icon"; +import { colourSet } from "../../../utils/diamond"; +import { DeleteIcon } from "../Icons/DeleteIcon"; +import { AddIcon } from "../Icons/AddIcon"; +import { SendIcon } from "../Icons/SendIcon"; + +const iconChildrenMap = { + add: , + send: , + delete: , +} as const; + +const meta: Meta = { + title: "MUI/Data Display/Icon", + component: Icon, + tags: ["autodocs"], + argTypes: { + color: { + control: "select", + options: colourSet, + }, + fontSize: { + control: "select", + options: ["inherit", "small", "medium", "large"], + }, + children: { + name: "icon", + control: "select", + options: Object.keys(iconChildrenMap), + mapping: iconChildrenMap, + }, + baseClassName: { control: false }, + }, + args: { + color: "primary", + fontSize: "medium", + children: "send", + }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { render: (args) => }; + +export const Colours: Story = { + render: (args) => ( + <> + + + + + + + + + + ), +}; diff --git a/src/components/MUI/DataDisplay/Icon.tsx b/src/components/MUI/DataDisplay/Icon.tsx new file mode 100644 index 0000000..8b76d08 --- /dev/null +++ b/src/components/MUI/DataDisplay/Icon.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiIcon, { IconProps as MuiIconProps } from "@mui/material/Icon"; + +export type IconProps = MuiIconProps; + +export const Icon = React.forwardRef( + (props, ref) => , +); + +Icon.displayName = "Icon"; diff --git a/src/components/MUI/DataDisplay/List.stories.tsx b/src/components/MUI/DataDisplay/List.stories.tsx new file mode 100644 index 0000000..fb0f51b --- /dev/null +++ b/src/components/MUI/DataDisplay/List.stories.tsx @@ -0,0 +1,144 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { List } from "./List"; +import { ListItem } from "./ListItem"; +import { ListItemButton } from "./ListItemButton"; +import { ListItemIcon } from "./ListItemIcon"; +import { ListItemText } from "./ListItemText"; +import { ListSubheader } from "./ListSubheader"; +import { Divider } from "./Divider"; +import { Avatar } from "../DataDisplay/Avatar"; +import { MailIcon } from "../Icons/MailIcon"; +import { InboxIcon } from "../Icons/InboxIcon"; + +const meta: Meta = { + title: "MUI/Data Display/List", + component: List, + tags: ["autodocs"], + argTypes: { + dense: { control: "boolean" }, + disablePadding: { control: "boolean" }, + }, + args: { + dense: false, + disablePadding: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + + + + + + + + ), +}; + +export const WithListItemIcons: Story = { + render: (args) => ( + + + + + + + + + + + + + + + ), +}; + +export const WithAvatars: Story = { + render: (args) => ( + + + + AB + + + + + + CD + + + + + ), +}; + +export const WithListSubheader: Story = { + render: (args) => ( + Folders} + > + + + + + + + + ), +}; + +export const WithListItemButton: Story = { + render: (args) => ( + + + + + + + + + + + + + ), +}; + +export const WithDividers: Story = { + render: (args) => ( + + + + + + + + + + + + + + ), +}; + +export const Dense: Story = { + args: { dense: true }, + render: (args) => ( + + + + + + + + + ), +}; diff --git a/src/components/MUI/DataDisplay/List.tsx b/src/components/MUI/DataDisplay/List.tsx new file mode 100644 index 0000000..c80ba89 --- /dev/null +++ b/src/components/MUI/DataDisplay/List.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiList, { ListProps as MuiListProps } from "@mui/material/List"; + +export type ListProps = MuiListProps; + +export const List = React.forwardRef( + (props, ref) => , +); + +List.displayName = "List"; diff --git a/src/components/MUI/DataDisplay/ListItem.tsx b/src/components/MUI/DataDisplay/ListItem.tsx new file mode 100644 index 0000000..f3dda62 --- /dev/null +++ b/src/components/MUI/DataDisplay/ListItem.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import MuiListItem, { + ListItemProps as MuiListItemProps, +} from "@mui/material/ListItem"; + +export type ListItemProps = MuiListItemProps; + +export const ListItem = React.forwardRef( + function ListItem(props, ref) { + return ; + }, +); +ListItem.displayName = "ListItem"; diff --git a/src/components/MUI/DataDisplay/ListItemAvatar.tsx b/src/components/MUI/DataDisplay/ListItemAvatar.tsx new file mode 100644 index 0000000..c84c43d --- /dev/null +++ b/src/components/MUI/DataDisplay/ListItemAvatar.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import MuiListItemAvatar, { + ListItemAvatarProps as MuiListItemAvatarProps, +} from "@mui/material/ListItemAvatar"; + +export type ListItemAvatarProps = MuiListItemAvatarProps; + +export const ListItemAvatar = React.forwardRef< + HTMLDivElement, + ListItemAvatarProps +>(function ListItemAvatar(props, ref) { + return ; +}); + +ListItemAvatar.displayName = "ListItemAvatar"; diff --git a/src/components/MUI/DataDisplay/ListItemButton.tsx b/src/components/MUI/DataDisplay/ListItemButton.tsx new file mode 100644 index 0000000..52893e9 --- /dev/null +++ b/src/components/MUI/DataDisplay/ListItemButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import MuiListItemButton, { + ListItemButtonProps as MuiListItemButtonProps, +} from "@mui/material/ListItemButton"; + +export type ListItemButtonProps = MuiListItemButtonProps; + +export const ListItemButton = React.forwardRef< + HTMLDivElement, + ListItemButtonProps +>(function ListItemButton(props, ref) { + return ; +}); + +ListItemButton.displayName = "ListItemButton"; diff --git a/src/components/MUI/DataDisplay/ListItemIcon.tsx b/src/components/MUI/DataDisplay/ListItemIcon.tsx new file mode 100644 index 0000000..2c4f2a7 --- /dev/null +++ b/src/components/MUI/DataDisplay/ListItemIcon.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import MuiListItemIcon, { + ListItemIconProps as MuiListItemIconProps, +} from "@mui/material/ListItemIcon"; + +export type ListItemIconProps = MuiListItemIconProps; + +export const ListItemIcon = React.forwardRef( + function ListItemIcon(props, ref) { + return ; + }, +); + +ListItemIcon.displayName = "ListItemIcon"; diff --git a/src/components/MUI/DataDisplay/ListItemText.tsx b/src/components/MUI/DataDisplay/ListItemText.tsx new file mode 100644 index 0000000..028b9a5 --- /dev/null +++ b/src/components/MUI/DataDisplay/ListItemText.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import MuiListItemText, { + ListItemTextProps as MuiListItemTextProps, +} from "@mui/material/ListItemText"; + +export type ListItemTextProps = MuiListItemTextProps; + +export const ListItemText = React.forwardRef< + HTMLSpanElement, + ListItemTextProps +>(function ListItemText(props, ref) { + return ; +}); + +ListItemText.displayName = "ListItemText"; diff --git a/src/components/MUI/DataDisplay/ListSubheader.tsx b/src/components/MUI/DataDisplay/ListSubheader.tsx new file mode 100644 index 0000000..18213bc --- /dev/null +++ b/src/components/MUI/DataDisplay/ListSubheader.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import MuiListSubheader, { + ListSubheaderProps as MuiListSubheaderProps, +} from "@mui/material/ListSubheader"; + +export type ListSubheaderProps = MuiListSubheaderProps; + +export const ListSubheader = React.forwardRef< + HTMLLIElement, + ListSubheaderProps +>(function ListSubheader(props, ref) { + return ; +}); +ListSubheader.displayName = "ListSubheader"; diff --git a/src/components/MUI/DataDisplay/SvgIcon.tsx b/src/components/MUI/DataDisplay/SvgIcon.tsx new file mode 100644 index 0000000..84dc6ec --- /dev/null +++ b/src/components/MUI/DataDisplay/SvgIcon.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiSvgIcon, { + SvgIconProps as MuiSvgIconProps, +} from "@mui/material/SvgIcon"; + +export type SvgIconProps = MuiSvgIconProps; + +export const SvgIcon = React.forwardRef( + (props, ref) => , +); + +SvgIcon.displayName = "SvgIcon"; diff --git a/src/components/MUI/DataDisplay/Table.stories.tsx b/src/components/MUI/DataDisplay/Table.stories.tsx new file mode 100644 index 0000000..239e83a --- /dev/null +++ b/src/components/MUI/DataDisplay/Table.stories.tsx @@ -0,0 +1,196 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; +import { Table } from "./Table"; +import { TableBody } from "./TableBody"; +import { TableCell } from "./TableCell"; +import { TableContainer } from "./TableContainer"; +import { TableFooter } from "./TableFooter"; +import { TableHead } from "./TableHead"; +import { TablePagination } from "./TablePagination"; +import { TableRow } from "./TableRow"; +import { TableSortLabel } from "./TableSortLabel"; + +type Request = { + id: string; + user: string; + status: "Queued" | "Running" | "Done"; +}; + +type Task = { + id: string; + name: string; + status: "Pending" | "Running" | "Done"; +}; + +const requests: Request[] = [ + { id: "REQ-101", user: "user1", status: "Done" }, + { id: "REQ-102", user: "user2", status: "Running" }, + { id: "REQ-103", user: "user3", status: "Queued" }, +]; + +const tasks: Task[] = [ + { id: "TASK-1", name: "Calibration", status: "Done" }, + { id: "TASK-2", name: "Process step 1", status: "Running" }, + { id: "TASK-3", name: "Process step 2", status: "Running" }, + { id: "TASK-4", name: "Process step 3", status: "Running" }, + { id: "TASK-5", name: "Process step 4", status: "Pending" }, + { id: "TASK-6", name: "Process step 5", status: "Pending" }, + { id: "TASK-7", name: "Generate output", status: "Pending" }, +]; + +const meta: Meta = { + title: "MUI/Data Display/Table", + component: Table, + tags: ["autodocs"], + argTypes: { + size: { control: "radio", options: ["small", "medium"] }, + stickyHeader: { control: "boolean" }, + }, + args: { + size: "medium", + stickyHeader: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + + + + Request + User + Status + + + + {requests.map((req) => ( + + {req.id} + {req.user} + {req.status} + + ))} + +
+
+ ), +}; + +export const StickyHeader: Story = { + args: { stickyHeader: true }, + render: (args) => ( + + + + + Request + Status + + + + {Array.from({ length: 8 }).map((_, i) => ( + + {`REQ-${200 + i}`} + Queued + + ))} + +
+
+ ), +}; + +export const TasksWithPagination: Story = { + render: (_args) => { + const [page, setPage] = React.useState(0); + const [rowsPerPage, setRowsPerPage] = React.useState(2); + + const slice = tasks.slice( + page * rowsPerPage, + page * rowsPerPage + rowsPerPage, + ); + + return ( + + + + + Task + Step + Status + + + + {slice.map((task) => ( + + {task.id} + {task.name} + {task.status} + + ))} + + + + setPage(newPage)} + onRowsPerPageChange={(e) => { + setRowsPerPage(parseInt(e.target.value, 10)); + setPage(0); + }} + /> + + +
+
+ ); + }, +}; + +export const SortableRequests: Story = { + render: (_args) => { + const [ascending, setAscending] = React.useState(true); + + const sorted = [...requests].sort((a, b) => + ascending ? a.id.localeCompare(b.id) : b.id.localeCompare(a.id), + ); + + return ( + + + + + + setAscending(!ascending)} + > + Request + + + User + Status + + + + {sorted.map((req) => ( + + {req.id} + {req.user} + {req.status} + + ))} + +
+
+ ); + }, +}; diff --git a/src/components/MUI/DataDisplay/Table.tsx b/src/components/MUI/DataDisplay/Table.tsx new file mode 100644 index 0000000..a0a96c2 --- /dev/null +++ b/src/components/MUI/DataDisplay/Table.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiTable, { TableProps as MuiTableProps } from "@mui/material/Table"; + +export type TableProps = MuiTableProps; + +export const Table = React.forwardRef( + (props, ref) => , +); + +Table.displayName = "Table"; diff --git a/src/components/MUI/DataDisplay/TableBody.tsx b/src/components/MUI/DataDisplay/TableBody.tsx new file mode 100644 index 0000000..02fefb9 --- /dev/null +++ b/src/components/MUI/DataDisplay/TableBody.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import MuiTableBody, { + TableBodyProps as MuiTableBodyProps, +} from "@mui/material/TableBody"; + +export type TableBodyProps = MuiTableBodyProps; + +export const TableBody = React.forwardRef< + HTMLTableSectionElement, + TableBodyProps +>(function TableBody(props, ref) { + return ; +}); + +TableBody.displayName = "TableBody"; diff --git a/src/components/MUI/DataDisplay/TableCell.tsx b/src/components/MUI/DataDisplay/TableCell.tsx new file mode 100644 index 0000000..a412200 --- /dev/null +++ b/src/components/MUI/DataDisplay/TableCell.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import MuiTableCell, { + TableCellProps as MuiTableCellProps, +} from "@mui/material/TableCell"; + +export type TableCellProps = MuiTableCellProps; + +export const TableCell = React.forwardRef( + function TableCell(props, ref) { + return ; + }, +); + +TableCell.displayName = "TableCell"; diff --git a/src/components/MUI/DataDisplay/TableContainer.tsx b/src/components/MUI/DataDisplay/TableContainer.tsx new file mode 100644 index 0000000..a100e56 --- /dev/null +++ b/src/components/MUI/DataDisplay/TableContainer.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import MuiTableContainer, { + TableContainerProps as MuiTableContainerProps, +} from "@mui/material/TableContainer"; + +export type TableContainerProps = MuiTableContainerProps; + +export const TableContainer = React.forwardRef< + HTMLDivElement, + TableContainerProps +>(function TableContainer(props, ref) { + return ; +}); + +TableContainer.displayName = "TableContainer"; diff --git a/src/components/MUI/DataDisplay/TableFooter.tsx b/src/components/MUI/DataDisplay/TableFooter.tsx new file mode 100644 index 0000000..e8db5f4 --- /dev/null +++ b/src/components/MUI/DataDisplay/TableFooter.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import MuiTableFooter, { + TableFooterProps as MuiTableFooterProps, +} from "@mui/material/TableFooter"; + +export type TableFooterProps = MuiTableFooterProps; + +export const TableFooter = React.forwardRef< + HTMLTableSectionElement, + TableFooterProps +>(function TableFooter(props, ref) { + return ; +}); + +TableFooter.displayName = "TableFooter"; diff --git a/src/components/MUI/DataDisplay/TableHead.tsx b/src/components/MUI/DataDisplay/TableHead.tsx new file mode 100644 index 0000000..765e047 --- /dev/null +++ b/src/components/MUI/DataDisplay/TableHead.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import MuiTableHead, { + TableHeadProps as MuiTableHeadProps, +} from "@mui/material/TableHead"; + +export type TableHeadProps = MuiTableHeadProps; + +export const TableHead = React.forwardRef< + HTMLTableSectionElement, + TableHeadProps +>(function TableHead(props, ref) { + return ; +}); + +TableHead.displayName = "TableHead"; diff --git a/src/components/MUI/DataDisplay/TablePagination.tsx b/src/components/MUI/DataDisplay/TablePagination.tsx new file mode 100644 index 0000000..29bbbab --- /dev/null +++ b/src/components/MUI/DataDisplay/TablePagination.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import MuiTablePagination, { + TablePaginationProps as MuiTablePaginationProps, +} from "@mui/material/TablePagination"; + +export type TablePaginationProps = MuiTablePaginationProps; + +export const TablePagination = React.forwardRef< + HTMLDivElement, + TablePaginationProps +>(function TablePagination(props, ref) { + return ; +}); + +TablePagination.displayName = "TablePagination"; diff --git a/src/components/MUI/DataDisplay/TableRow.tsx b/src/components/MUI/DataDisplay/TableRow.tsx new file mode 100644 index 0000000..a05de11 --- /dev/null +++ b/src/components/MUI/DataDisplay/TableRow.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import MuiTableRow, { + TableRowProps as MuiTableRowProps, +} from "@mui/material/TableRow"; + +export type TableRowProps = MuiTableRowProps; + +export const TableRow = React.forwardRef( + function TableRow(props, ref) { + return ; + }, +); +TableRow.displayName = "TableRow"; diff --git a/src/components/MUI/DataDisplay/TableSortLabel.tsx b/src/components/MUI/DataDisplay/TableSortLabel.tsx new file mode 100644 index 0000000..11ddad9 --- /dev/null +++ b/src/components/MUI/DataDisplay/TableSortLabel.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import MuiTableSortLabel, { + TableSortLabelProps as MuiTableSortLabelProps, +} from "@mui/material/TableSortLabel"; + +export type TableSortLabelProps = MuiTableSortLabelProps; + +export const TableSortLabel = React.forwardRef< + HTMLSpanElement, + TableSortLabelProps +>(function TableSortLabel(props, ref) { + return ; +}); + +TableSortLabel.displayName = "TableSortLabel"; diff --git a/src/components/MUI/DataDisplay/Typography.stories.tsx b/src/components/MUI/DataDisplay/Typography.stories.tsx new file mode 100644 index 0000000..53b68a5 --- /dev/null +++ b/src/components/MUI/DataDisplay/Typography.stories.tsx @@ -0,0 +1,101 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Typography, TypographyProps } from "./Typography"; +import { Stack } from "../Layout/Stack"; +import { colourSet } from "../../../utils/diamond"; + +const textSizes: TypographyProps["variant"][] = [ + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "subtitle1", + "subtitle2", + "body1", + "body2", + "caption", + "button", + "overline", +]; + +const meta: Meta = { + title: "MUI/Data Display/Typography", + component: Typography, + tags: ["autodocs"], + argTypes: { + variant: { + control: { type: "select" }, + options: textSizes, + }, + color: { + control: { type: "select" }, + options: colourSet, + }, + align: { + control: { type: "select" }, + options: ["inherit", "left", "center", "right", "justify"], + }, + gutterBottom: { control: "boolean" }, + noWrap: { control: "boolean" }, + paragraph: { control: "boolean" }, + children: { name: "text", control: "text" }, + sx: { control: false }, + }, + args: { + variant: "body1", + color: "inherit", + align: "inherit", + gutterBottom: false, + noWrap: false, + paragraph: false, + children: + "The quick brown fox jumps over the lazy dog and again, the quick brown fox jumps over the lazy dog.", + }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => , +}; + +export const TextSizes: Story = { + render: (args) => ( + + {textSizes.map((textSize) => ( + + {textSize} + + ))} + + ), +}; + +export const Alignment: Story = { + render: (args) => ( + + + Left aligned + + + Center aligned + + + Right aligned + + + ), +}; + +export const Colours: Story = { + render: (args) => ( + + {colourSet.map((colourOption) => ( + + {colourOption} + + ))} + + ), +}; diff --git a/src/components/MUI/DataDisplay/Typography.tsx b/src/components/MUI/DataDisplay/Typography.tsx new file mode 100644 index 0000000..581ab8e --- /dev/null +++ b/src/components/MUI/DataDisplay/Typography.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiTypography, { + TypographyProps as MuiTypographyProps, +} from "@mui/material/Typography"; + +export type TypographyProps = MuiTypographyProps; + +export const Typography = React.forwardRef( + (props, ref) => , +); + +Typography.displayName = "Typography"; diff --git a/src/components/MUI/Feedback/Alert.stories.tsx b/src/components/MUI/Feedback/Alert.stories.tsx new file mode 100644 index 0000000..122c931 --- /dev/null +++ b/src/components/MUI/Feedback/Alert.stories.tsx @@ -0,0 +1,84 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Alert } from "./Alert"; +import { Button } from "../Inputs/Button"; +import { Stack } from "../Layout/Stack"; + +const meta: Meta = { + title: "MUI/Feedback/Alert", + component: Alert, + tags: ["autodocs"], + argTypes: { + severity: { + control: "select", + options: ["success", "info", "warning", "error"], + }, + variant: { control: "select", options: ["standard", "outlined", "filled"] }, + children: { name: "message", control: "text" }, + icon: { control: false }, + action: { control: false }, + onClose: { control: false }, + }, + args: { + severity: "info", + variant: "standard", + children: "Alert message", + }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { render: (args) => }; + +export const Variants: Story = { + render: (args) => ( + + + Standard + + + Outlined + + + Filled + + + ), +}; + +export const SeverityLevels: Story = { + render: (args) => ( + + + This is a success alert + + + This is an info alert + + + This is a warning alert + + + This is an error alert + + + ), +}; + +export const WithActionAndClose: Story = { + render: (args) => ( + <> + + UNDO + + } + onClose={() => console.log("alert close")} + > + Warning with action + + + ), +}; diff --git a/src/components/MUI/Feedback/Alert.tsx b/src/components/MUI/Feedback/Alert.tsx new file mode 100644 index 0000000..3760b7d --- /dev/null +++ b/src/components/MUI/Feedback/Alert.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiAlert, { AlertProps as MuiAlertProps } from "@mui/material/Alert"; + +export type AlertProps = MuiAlertProps; + +export const Alert = React.forwardRef( + (props, ref) => , +); + +Alert.displayName = "Alert"; diff --git a/src/components/MUI/Feedback/Backdrop.stories.tsx b/src/components/MUI/Feedback/Backdrop.stories.tsx new file mode 100644 index 0000000..9cee4d1 --- /dev/null +++ b/src/components/MUI/Feedback/Backdrop.stories.tsx @@ -0,0 +1,47 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import * as React from "react"; +import { Backdrop } from "./Backdrop"; +import { CircularProgress } from "./CircularProgress"; +import { Button } from "../Inputs/Button"; +import { Stack } from "../Layout/Stack"; + +const meta: Meta = { + title: "MUI/Feedback/Backdrop", + component: Backdrop, + tags: ["autodocs"], + argTypes: { + invisible: { control: "boolean" }, + }, + args: { invisible: false }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => { + return ( + + + + + + ); + }, +}; + +/** + * Click show to open backdrop and click again to close. + */ +export const ControlledBackdrop: Story = { + render: (args) => { + const [open, setOpen] = React.useState(false); + return ( + + + setOpen(false)}> + + + + ); + }, +}; diff --git a/src/components/MUI/Feedback/Backdrop.tsx b/src/components/MUI/Feedback/Backdrop.tsx new file mode 100644 index 0000000..3686d04 --- /dev/null +++ b/src/components/MUI/Feedback/Backdrop.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiBackdrop, { + BackdropProps as MuiBackdropProps, +} from "@mui/material/Backdrop"; + +export type BackdropProps = MuiBackdropProps; + +export const Backdrop = React.forwardRef( + (props, ref) => , +); + +Backdrop.displayName = "Backdrop"; diff --git a/src/components/MUI/Feedback/CircularProgress.stories.tsx b/src/components/MUI/Feedback/CircularProgress.stories.tsx new file mode 100644 index 0000000..dfe8665 --- /dev/null +++ b/src/components/MUI/Feedback/CircularProgress.stories.tsx @@ -0,0 +1,97 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { CircularProgress } from "./CircularProgress"; +import { Stack } from "../Layout/Stack"; +import { Typography } from "../DataDisplay/Typography"; + +const meta: Meta = { + title: "MUI/Feedback/CircularProgress", + component: CircularProgress, + tags: ["autodocs"], + argTypes: { + color: { + control: "select", + options: [ + "primary", + "secondary", + "success", + "error", + "info", + "warning", + "inherit", + ], + }, + variant: { control: "select", options: ["indeterminate", "determinate"] }, + value: { control: { type: "number", min: 0, max: 100, step: 1 } }, + size: { control: { type: "number", min: 8, max: 200, step: 2 } }, + thickness: { control: { type: "number", min: 1, max: 10, step: 0.5 } }, + }, + args: { + color: "primary", + variant: "indeterminate", + value: 75, + size: 40, + thickness: 3.6, + }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => , +}; + +export const Determinate: Story = { + args: { variant: "determinate", value: 70 }, + render: (args) => , +}; + +export const DeterminateValues: Story = { + render: (args) => ( + + {[0, 25, 50, 75, 100].map((value) => ( + + + {value}% + + ))} + + ), +}; + +export const Sizes: Story = { + parameters: { + docs: { + description: { + story: "Circular Progress in different sizes.", + }, + }, + }, + render: (args) => ( + + {[16, 24, 40, 64, 80].map((size) => ( + + ))} + + ), +}; + +export const Thickness: Story = { + parameters: {}, + render: (args) => ( + + {[2, 3.6, 5, 7].map((thickness) => ( + + ))} + + ), +}; diff --git a/src/components/MUI/Feedback/CircularProgress.tsx b/src/components/MUI/Feedback/CircularProgress.tsx new file mode 100644 index 0000000..5a2aa25 --- /dev/null +++ b/src/components/MUI/Feedback/CircularProgress.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import MuiCircularProgress, { + CircularProgressProps as MuiCircularProgressProps, +} from "@mui/material/CircularProgress"; + +export type CircularProgressProps = MuiCircularProgressProps; + +export const CircularProgress = React.forwardRef< + HTMLSpanElement, + CircularProgressProps +>((props, ref) => ); + +CircularProgress.displayName = "CircularProgress"; diff --git a/src/components/MUI/Feedback/Dialog.stories.tsx b/src/components/MUI/Feedback/Dialog.stories.tsx new file mode 100644 index 0000000..1dadb38 --- /dev/null +++ b/src/components/MUI/Feedback/Dialog.stories.tsx @@ -0,0 +1,79 @@ +import React from "react"; +import type { Meta, StoryObj } from "@storybook/react"; +import { Dialog } from "./Dialog"; +import { Typography } from "../DataDisplay/Typography"; +import { Button } from "../Inputs/Button"; +import { DialogActions } from "./DialogActions"; +import { DialogContent } from "./DialogContent"; +import { DialogTitle } from "./DialogTitle"; + +const meta: Meta = { + title: "MUI/Feedback/Dialog", + component: Dialog, + tags: ["autodocs"], + argTypes: { + fullWidth: { control: "boolean" }, + maxWidth: { + control: "select", + options: ["xs", "sm", "md", "lg", "xl", false], + }, + fullScreen: { control: "boolean" }, + scroll: { control: "select", options: ["paper", "body"] }, + }, + args: { fullWidth: true, maxWidth: "sm", fullScreen: false, scroll: "paper" }, +}; +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => { + const [open, setOpen] = React.useState(false); + return ( +
+ + setOpen(false)}> + Title + + Content goes here. + + + + + + +
+ ); + }, +}; + +export const FullScreen: Story = { + args: { fullScreen: true }, + render: (args) => { + const [open, setOpen] = React.useState(false); + return ( +
+ + setOpen(false)} + > + Full Screen + + Full-screen content. + + + + + +
+ ); + }, +}; diff --git a/src/components/MUI/Feedback/Dialog.tsx b/src/components/MUI/Feedback/Dialog.tsx new file mode 100644 index 0000000..88c2811 --- /dev/null +++ b/src/components/MUI/Feedback/Dialog.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiDialog, { DialogProps as MuiDialogProps } from "@mui/material/Dialog"; + +export type DialogProps = MuiDialogProps; + +export const Dialog = React.forwardRef( + (props, ref) => , +); + +Dialog.displayName = "Dialog"; diff --git a/src/components/MUI/Feedback/DialogActions.tsx b/src/components/MUI/Feedback/DialogActions.tsx new file mode 100644 index 0000000..c9acd5a --- /dev/null +++ b/src/components/MUI/Feedback/DialogActions.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import MuiDialogActions, { + DialogActionsProps as MuiDialogActionsProps, +} from "@mui/material/DialogActions"; + +export type DialogActionsProps = MuiDialogActionsProps; + +export const DialogActions = React.forwardRef< + HTMLDivElement, + DialogActionsProps +>((props, ref) => ); + +DialogActions.displayName = "DialogActions"; diff --git a/src/components/MUI/Feedback/DialogContent.tsx b/src/components/MUI/Feedback/DialogContent.tsx new file mode 100644 index 0000000..c5b525a --- /dev/null +++ b/src/components/MUI/Feedback/DialogContent.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import MuiDialogContent, { + DialogContentProps as MuiDialogContentProps, +} from "@mui/material/DialogContent"; + +export type DialogContentProps = MuiDialogContentProps; + +export const DialogContent = React.forwardRef< + HTMLDivElement, + DialogContentProps +>((props, ref) => ); + +DialogContent.displayName = "DialogContent"; diff --git a/src/components/MUI/Feedback/DialogTitle.tsx b/src/components/MUI/Feedback/DialogTitle.tsx new file mode 100644 index 0000000..debc000 --- /dev/null +++ b/src/components/MUI/Feedback/DialogTitle.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiDialogTitle, { + DialogTitleProps as MuiDialogTitleProps, +} from "@mui/material/DialogTitle"; + +export type DialogTitleProps = MuiDialogTitleProps; + +export const DialogTitle = React.forwardRef( + (props, ref) => , +); + +DialogTitle.displayName = "DialogTitle"; diff --git a/src/components/MUI/Feedback/LinearProgress.stories.tsx b/src/components/MUI/Feedback/LinearProgress.stories.tsx new file mode 100644 index 0000000..5b01f54 --- /dev/null +++ b/src/components/MUI/Feedback/LinearProgress.stories.tsx @@ -0,0 +1,100 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { LinearProgress } from "./LinearProgress"; +import { Stack } from "../Layout/Stack"; +import { Typography } from "../DataDisplay/Typography"; + +const meta: Meta = { + title: "MUI/Feedback/LinearProgress", + component: LinearProgress, + tags: ["autodocs"], + argTypes: { + color: { + control: "select", + options: [ + "primary", + "secondary", + "success", + "error", + "info", + "warning", + "inherit", + ], + }, + variant: { + control: "select", + options: ["indeterminate", "determinate", "buffer", "query"], + }, + value: { control: { type: "number", min: 0, max: 100, step: 1 } }, + valueBuffer: { control: { type: "number", min: 0, max: 100, step: 1 } }, + }, + args: { + color: "primary", + variant: "indeterminate", + value: 40, + valueBuffer: 60, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => , +}; + +export const Determinate: Story = { + args: { variant: "determinate", value: 50 }, + render: (args) => , +}; + +export const Buffer: Story = { + args: { variant: "buffer", value: 30, valueBuffer: 60 }, + render: (args) => , +}; + +export const Query: Story = { + args: { variant: "query" }, + render: (args) => , +}; + +export const DeterminateValues: Story = { + parameters: { + docs: { + description: { + story: "Determinate linear progress with different values.", + }, + }, + }, + render: (args) => ( + + {[0, 25, 50, 75, 100].map((value) => ( + + ))} + + ), +}; + +export const WithLabelValues: Story = { + parameters: { + docs: { + description: { + story: "Linear progress with value labels.", + }, + }, + }, + render: (args) => ( + + {[0, 25, 50, 75, 100].map((value) => ( + + + {value}% + + ))} + + ), +}; diff --git a/src/components/MUI/Feedback/LinearProgress.tsx b/src/components/MUI/Feedback/LinearProgress.tsx new file mode 100644 index 0000000..d21d8df --- /dev/null +++ b/src/components/MUI/Feedback/LinearProgress.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import MuiLinearProgress, { + LinearProgressProps as MuiLinearProgressProps, +} from "@mui/material/LinearProgress"; + +export type LinearProgressProps = MuiLinearProgressProps; + +export const LinearProgress = React.forwardRef< + HTMLDivElement, + LinearProgressProps +>((props, ref) => ); + +LinearProgress.displayName = "LinearProgress"; diff --git a/src/components/MUI/Feedback/Skeleton.stories.tsx b/src/components/MUI/Feedback/Skeleton.stories.tsx new file mode 100644 index 0000000..d58fd2c --- /dev/null +++ b/src/components/MUI/Feedback/Skeleton.stories.tsx @@ -0,0 +1,80 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Skeleton } from "./Skeleton"; +import { Stack } from "../Layout/Stack"; +import { Typography } from "../DataDisplay/Typography"; + +const meta: Meta = { + title: "MUI/Feedback/Skeleton", + component: Skeleton, + tags: ["autodocs"], + parameters: { + layout: "padded", + }, + argTypes: { + variant: { + control: "select", + options: ["text", "rectangular", "circular", "rounded"], + }, + animation: { + control: "select", + options: ["pulse", "wave", false], + }, + width: { + control: { type: "number", min: 16, max: 600, step: 4 }, + }, + height: { + control: { type: "number", min: 16, max: 400, step: 4 }, + }, + }, + args: { + variant: "rectangular", + animation: "pulse", + width: 240, + height: 180, + }, +}; + +export default meta; +type Story = StoryObj; + +/** + * Skeleton represents elements while content is loading. + * Multiple Skeletons can be composed to represent complex layouts. + */ +export const Basic: Story = { + render: (args) => , +}; + +export const Variants: Story = { + render: (_args) => ( + + Text + + Circular + + Rectangular + + Rounded + + + ), +}; + +export const Animations: Story = { + render: (args) => ( + + + Pulse + + + + Wave + + + + None + + + + ), +}; diff --git a/src/components/MUI/Feedback/Skeleton.tsx b/src/components/MUI/Feedback/Skeleton.tsx new file mode 100644 index 0000000..dd7b5ea --- /dev/null +++ b/src/components/MUI/Feedback/Skeleton.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiSkeleton, { + SkeletonProps as MuiSkeletonProps, +} from "@mui/material/Skeleton"; + +export type SkeletonProps = MuiSkeletonProps; + +export const Skeleton = React.forwardRef( + (props, ref) => , +); + +Skeleton.displayName = "Skeleton"; diff --git a/src/components/MUI/Feedback/Snackbar.stories.tsx b/src/components/MUI/Feedback/Snackbar.stories.tsx new file mode 100644 index 0000000..73e77ce --- /dev/null +++ b/src/components/MUI/Feedback/Snackbar.stories.tsx @@ -0,0 +1,47 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; +import { Snackbar } from "./Snackbar"; +import { Button } from "../Inputs/Button"; +import { Stack } from "../Layout/Stack"; + +const meta: Meta = { + title: "MUI/Feedback/Snackbar", + component: Snackbar, + tags: ["autodocs"], + argTypes: { + message: { + control: "text", + }, + autoHideDuration: { + control: { type: "number", min: 1000, max: 10000, step: 500 }, + }, + }, + args: { + message: "Saved", + autoHideDuration: 3000, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => { + const [open, setOpen] = React.useState(false); + + return ( + + + + setOpen(false)} + /> + + ); + }, +}; diff --git a/src/components/MUI/Feedback/Snackbar.tsx b/src/components/MUI/Feedback/Snackbar.tsx new file mode 100644 index 0000000..c72a864 --- /dev/null +++ b/src/components/MUI/Feedback/Snackbar.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiSnackbar, { + SnackbarProps as MuiSnackbarProps, +} from "@mui/material/Snackbar"; + +export type SnackbarProps = MuiSnackbarProps; + +export const Snackbar = React.forwardRef( + (props, ref) => , +); + +Snackbar.displayName = "Snackbar"; diff --git a/src/components/MUI/Icons/AddIcon.tsx b/src/components/MUI/Icons/AddIcon.tsx new file mode 100644 index 0000000..220f1d9 --- /dev/null +++ b/src/components/MUI/Icons/AddIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiAddIcon from "@mui/icons-material/Add"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type AddIconProps = SvgIconProps; + +export const AddIcon = React.forwardRef( + (props, ref) => , +); + +AddIcon.displayName = "AddIcon"; diff --git a/src/components/MUI/Icons/AssignmentIcon.tsx b/src/components/MUI/Icons/AssignmentIcon.tsx new file mode 100644 index 0000000..becf3d9 --- /dev/null +++ b/src/components/MUI/Icons/AssignmentIcon.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiAssignmentIcon from "@mui/icons-material/Assignment"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type AssignmentIconProps = SvgIconProps; + +export const AssignmentIcon = React.forwardRef< + SVGSVGElement, + AssignmentIconProps +>((props, ref) => ); + +AssignmentIcon.displayName = "AssignmentIcon"; diff --git a/src/components/MUI/Icons/DeleteIcon.tsx b/src/components/MUI/Icons/DeleteIcon.tsx new file mode 100644 index 0000000..3a377c7 --- /dev/null +++ b/src/components/MUI/Icons/DeleteIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiDeleteIcon from "@mui/icons-material/Delete"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type DeleteIconProps = SvgIconProps; + +export const DeleteIcon = React.forwardRef( + (props, ref) => , +); + +DeleteIcon.displayName = "DeleteIcon"; diff --git a/src/components/MUI/Icons/ExpandMoreIcon.tsx b/src/components/MUI/Icons/ExpandMoreIcon.tsx new file mode 100644 index 0000000..9abb449 --- /dev/null +++ b/src/components/MUI/Icons/ExpandMoreIcon.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type ExpandMoreIconProps = SvgIconProps; + +export const ExpandMoreIcon = React.forwardRef< + SVGSVGElement, + ExpandMoreIconProps +>((props, ref) => ); + +ExpandMoreIcon.displayName = "ExpandMoreIcon"; diff --git a/src/components/MUI/Icons/FolderIcon.tsx b/src/components/MUI/Icons/FolderIcon.tsx new file mode 100644 index 0000000..20e6f50 --- /dev/null +++ b/src/components/MUI/Icons/FolderIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiFolderIcon from "@mui/icons-material/Folder"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type FolderIconProps = SvgIconProps; + +export const FolderIcon = React.forwardRef( + (props, ref) => , +); + +FolderIcon.displayName = "FolderIcon"; diff --git a/src/components/MUI/Icons/InboxIcon.tsx b/src/components/MUI/Icons/InboxIcon.tsx new file mode 100644 index 0000000..40660bc --- /dev/null +++ b/src/components/MUI/Icons/InboxIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiInboxIcon from "@mui/icons-material/Inbox"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type InboxIconProps = SvgIconProps; + +export const InboxIcon = React.forwardRef( + (props, ref) => , +); + +InboxIcon.displayName = "InboxIcon"; diff --git a/src/components/MUI/Icons/MailIcon.tsx b/src/components/MUI/Icons/MailIcon.tsx new file mode 100644 index 0000000..c753e7b --- /dev/null +++ b/src/components/MUI/Icons/MailIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiMailIcon from "@mui/icons-material/Mail"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type MailIconProps = SvgIconProps; + +export const MailIcon = React.forwardRef( + (props, ref) => , +); + +MailIcon.displayName = "MailIcon"; diff --git a/src/components/MUI/Icons/MenuIcon.tsx b/src/components/MUI/Icons/MenuIcon.tsx new file mode 100644 index 0000000..f076f76 --- /dev/null +++ b/src/components/MUI/Icons/MenuIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiMenuIcon from "@mui/icons-material/Menu"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type MenuIconProps = SvgIconProps; + +export const MenuIcon = React.forwardRef( + (props, ref) => , +); + +MenuIcon.displayName = "MenuIcon"; diff --git a/src/components/MUI/Icons/NotificationsIcon.tsx b/src/components/MUI/Icons/NotificationsIcon.tsx new file mode 100644 index 0000000..470346b --- /dev/null +++ b/src/components/MUI/Icons/NotificationsIcon.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiNotificationsIcon from "@mui/icons-material/Notifications"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type NotificationsIconProps = SvgIconProps; + +export const NotificationsIcon = React.forwardRef< + SVGSVGElement, + NotificationsIconProps +>((props, ref) => ); + +NotificationsIcon.displayName = "NotificationsIcon"; diff --git a/src/components/MUI/Icons/PageviewIcon.tsx b/src/components/MUI/Icons/PageviewIcon.tsx new file mode 100644 index 0000000..3e7c3b5 --- /dev/null +++ b/src/components/MUI/Icons/PageviewIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiPageviewIcon from "@mui/icons-material/Pageview"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type PageviewIconProps = SvgIconProps; + +export const PageviewIcon = React.forwardRef( + (props, ref) => , +); + +PageviewIcon.displayName = "PageviewIcon"; diff --git a/src/components/MUI/Icons/SaveIcon.tsx b/src/components/MUI/Icons/SaveIcon.tsx new file mode 100644 index 0000000..3c5662c --- /dev/null +++ b/src/components/MUI/Icons/SaveIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiSaveIcon from "@mui/icons-material/Save"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type SaveIconProps = SvgIconProps; + +export const SaveIcon = React.forwardRef( + (props, ref) => , +); + +SaveIcon.displayName = "SaveIcon"; diff --git a/src/components/MUI/Icons/SendIcon.tsx b/src/components/MUI/Icons/SendIcon.tsx new file mode 100644 index 0000000..3002e24 --- /dev/null +++ b/src/components/MUI/Icons/SendIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiSendIcon from "@mui/icons-material/Send"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type SendIconProps = SvgIconProps; + +export const SendIcon = React.forwardRef( + (props, ref) => , +); + +SendIcon.displayName = "SendIcon"; diff --git a/src/components/MUI/Icons/WorkIcon.tsx b/src/components/MUI/Icons/WorkIcon.tsx new file mode 100644 index 0000000..900da5c --- /dev/null +++ b/src/components/MUI/Icons/WorkIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import MuiWorkIcon from "@mui/icons-material/Work"; +import type { SvgIconProps } from "@mui/material/SvgIcon"; + +export type WorkIconProps = SvgIconProps; + +export const WorkIcon = React.forwardRef( + (props, ref) => , +); + +WorkIcon.displayName = "WorkIcon"; diff --git a/src/components/MUI/Inputs/Button.tsx b/src/components/MUI/Inputs/Button.tsx new file mode 100644 index 0000000..bd8d79c --- /dev/null +++ b/src/components/MUI/Inputs/Button.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import MuiButton, { ButtonProps as MuiButtonProps } from "@mui/material/Button"; + +export type ButtonProps = MuiButtonProps; + +export const Button = React.forwardRef( + ({ children, ...rest }, ref) => ( + + {children} + + ), +); + +Button.displayName = "Button"; diff --git a/src/components/MUI/Inputs/IconButton.tsx b/src/components/MUI/Inputs/IconButton.tsx new file mode 100644 index 0000000..52db5ef --- /dev/null +++ b/src/components/MUI/Inputs/IconButton.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiIconButton, { + IconButtonProps as MuiIconButtonProps, +} from "@mui/material/IconButton"; + +export type IconButtonProps = MuiIconButtonProps; + +export const IconButton = React.forwardRef( + (props, ref) => , +); + +IconButton.displayName = "IconButton"; diff --git a/src/components/MUI/Layout/Stack.tsx b/src/components/MUI/Layout/Stack.tsx new file mode 100644 index 0000000..2006df5 --- /dev/null +++ b/src/components/MUI/Layout/Stack.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiStack, { StackProps as MuiStackProps } from "@mui/material/Stack"; + +export type StackProps = MuiStackProps; + +export const Stack = React.forwardRef( + (props, ref) => , +); + +Stack.displayName = "Stack"; diff --git a/src/components/MUI/Layout/Toolbar.tsx b/src/components/MUI/Layout/Toolbar.tsx new file mode 100644 index 0000000..80b99a0 --- /dev/null +++ b/src/components/MUI/Layout/Toolbar.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiToolbar, { + ToolbarProps as MuiToolbarProps, +} from "@mui/material/Toolbar"; + +export type ToolbarProps = MuiToolbarProps; + +export const Toolbar = React.forwardRef( + (props, ref) => , +); + +Toolbar.displayName = "Toolbar"; diff --git a/src/components/MUI/Surfaces/Accordion.stories.tsx b/src/components/MUI/Surfaces/Accordion.stories.tsx new file mode 100644 index 0000000..a2c1b97 --- /dev/null +++ b/src/components/MUI/Surfaces/Accordion.stories.tsx @@ -0,0 +1,95 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Accordion } from "./Accordion"; +import { AccordionDetails } from "./AccordionDetails"; +import { AccordionSummary } from "./AccordionSummary"; +import { ExpandMoreIcon } from "../Icons/ExpandMoreIcon"; +import { Stack } from "../Layout/Stack"; +import { Typography } from "../DataDisplay/Typography"; + +const meta: Meta = { + title: "MUI/Surfaces/Accordion", + component: Accordion, + tags: ["autodocs"], + argTypes: { + disabled: { control: "boolean" }, + disableGutters: { control: "boolean" }, + square: { control: "boolean" }, + defaultExpanded: { control: "boolean" }, + expanded: { control: false }, + onChange: { control: false }, + sx: { control: false }, + }, + args: { + disabled: false, + disableGutters: false, + square: false, + defaultExpanded: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + }> + Summary + + + Details + + + ), +}; + +export const Multiple: Story = { + render: (args) => ( + + + }> + Item 1 + + + Content 1 + + + + }> + Item 2 + + + Content 2 + + + + ), +}; + +export const Disabled: Story = { + args: { disabled: true }, + render: (args) => ( + + }> + Disabled + + + Cannot expand + + + ), +}; + +export const DefaultExpanded: Story = { + args: { defaultExpanded: true }, + render: (args) => ( + + }> + Expanded by default + + + Initial content is visible + + + ), +}; diff --git a/src/components/MUI/Surfaces/Accordion.tsx b/src/components/MUI/Surfaces/Accordion.tsx new file mode 100644 index 0000000..56649a6 --- /dev/null +++ b/src/components/MUI/Surfaces/Accordion.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiAccordion, { + AccordionProps as MuiAccordionProps, +} from "@mui/material/Accordion"; + +export type AccordionProps = MuiAccordionProps; + +export const Accordion = React.forwardRef( + (props, ref) => , +); + +Accordion.displayName = "Accordion"; diff --git a/src/components/MUI/Surfaces/AccordionDetails.tsx b/src/components/MUI/Surfaces/AccordionDetails.tsx new file mode 100644 index 0000000..15a9d2f --- /dev/null +++ b/src/components/MUI/Surfaces/AccordionDetails.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import MuiAccordionDetails, { + AccordionDetailsProps as MuiAccordionDetailsProps, +} from "@mui/material/AccordionDetails"; + +export type AccordionDetailsProps = MuiAccordionDetailsProps; + +export const AccordionDetails = React.forwardRef< + HTMLDivElement, + AccordionDetailsProps +>((props, ref) => ); + +AccordionDetails.displayName = "AccordionDetails"; diff --git a/src/components/MUI/Surfaces/AccordionSummary.tsx b/src/components/MUI/Surfaces/AccordionSummary.tsx new file mode 100644 index 0000000..ae0a03a --- /dev/null +++ b/src/components/MUI/Surfaces/AccordionSummary.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import MuiAccordionSummary, { + AccordionSummaryProps as MuiAccordionSummaryProps, +} from "@mui/material/AccordionSummary"; + +export type AccordionSummaryProps = MuiAccordionSummaryProps; + +export const AccordionSummary = React.forwardRef< + HTMLDivElement, + AccordionSummaryProps +>((props, ref) => ); + +AccordionSummary.displayName = "AccordionSummary"; diff --git a/src/components/MUI/Surfaces/AppBar.stories.tsx b/src/components/MUI/Surfaces/AppBar.stories.tsx new file mode 100644 index 0000000..f15576e --- /dev/null +++ b/src/components/MUI/Surfaces/AppBar.stories.tsx @@ -0,0 +1,54 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { AppBar } from "./AppBar"; +import { Toolbar } from "../Layout/Toolbar"; +import { Typography } from "../DataDisplay/Typography"; +import { IconButton } from "../Inputs/IconButton"; +import { Button } from "../Inputs/Button"; +import { MenuIcon } from "../Icons/MenuIcon"; + +const meta: Meta = { + title: "MUI/Surfaces/AppBar", + component: AppBar, + tags: ["autodocs"], + parameters: { + layout: "fullscreen", + }, + argTypes: { + position: { + control: "select", + options: ["fixed", "absolute", "sticky", "static", "relative"], + }, + color: { + control: "select", + options: ["default", "primary", "secondary", "inherit", "transparent"], + }, + elevation: { + control: { type: "number", min: 0, max: 24, step: 1 }, + }, + sx: { control: false }, + }, + args: { + position: "static", + color: "primary", + elevation: 4, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + + + + + + Application title + + + + + ), +}; diff --git a/src/components/MUI/Surfaces/AppBar.tsx b/src/components/MUI/Surfaces/AppBar.tsx new file mode 100644 index 0000000..99b3971 --- /dev/null +++ b/src/components/MUI/Surfaces/AppBar.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiAppBar, { AppBarProps as MuiAppBarProps } from "@mui/material/AppBar"; + +export type AppBarProps = MuiAppBarProps; + +export const AppBar = React.forwardRef( + (props, ref) => , +); + +AppBar.displayName = "AppBar"; diff --git a/src/components/MUI/Surfaces/Card.stories.tsx b/src/components/MUI/Surfaces/Card.stories.tsx new file mode 100644 index 0000000..d0443dd --- /dev/null +++ b/src/components/MUI/Surfaces/Card.stories.tsx @@ -0,0 +1,74 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Card } from "./Card"; +import { Typography } from "../DataDisplay/Typography"; +import { Button } from "../Inputs/Button"; +import { CardContent } from "./CardContent"; +import { CardActions } from "./CardActions"; +import { CardMedia } from "./CardMedia"; +import Diamond from "../../../public/images/diamond.jpg"; + +const meta: Meta = { + title: "MUI/Surfaces/Card", + component: Card, + tags: ["autodocs"], + argTypes: { + variant: { + control: "select", + options: ["elevation", "outlined"], + }, + raised: { control: "boolean" }, + elevation: { + control: { type: "number", min: 0, max: 24, step: 1 }, + }, + sx: { control: false }, + }, + args: { + variant: "elevation", + raised: false, + elevation: 1, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + + + + Diamond Light Source + + + Diamond Light Source is the UK’s national synchrotron serving + scientists and researchers from around the world. + + + + + + + + ), +}; + +export const Outlined: Story = { + args: { + variant: "outlined", + elevation: 0, + }, + render: (args) => ( + + + Outlined card + + Uses the outlined variant with zero elevation. + + + + + + + ), +}; diff --git a/src/components/MUI/Surfaces/Card.tsx b/src/components/MUI/Surfaces/Card.tsx new file mode 100644 index 0000000..ab1ee4a --- /dev/null +++ b/src/components/MUI/Surfaces/Card.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiCard, { CardProps as MuiCardProps } from "@mui/material/Card"; + +export type CardProps = MuiCardProps; + +export const Card = React.forwardRef( + (props, ref) => , +); + +Card.displayName = "Card"; diff --git a/src/components/MUI/Surfaces/CardActions.tsx b/src/components/MUI/Surfaces/CardActions.tsx new file mode 100644 index 0000000..c912226 --- /dev/null +++ b/src/components/MUI/Surfaces/CardActions.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiCardActions, { + CardActionsProps as MuiCardActionsProps, +} from "@mui/material/CardActions"; + +export type CardActionsProps = MuiCardActionsProps; + +export const CardActions = React.forwardRef( + (props, ref) => , +); + +CardActions.displayName = "CardActions"; diff --git a/src/components/MUI/Surfaces/CardContent.tsx b/src/components/MUI/Surfaces/CardContent.tsx new file mode 100644 index 0000000..88a61e9 --- /dev/null +++ b/src/components/MUI/Surfaces/CardContent.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiCardContent, { + CardContentProps as MuiCardContentProps, +} from "@mui/material/CardContent"; + +export type CardContentProps = MuiCardContentProps; + +export const CardContent = React.forwardRef( + (props, ref) => , +); + +CardContent.displayName = "CardContent"; diff --git a/src/components/MUI/Surfaces/CardMedia.tsx b/src/components/MUI/Surfaces/CardMedia.tsx new file mode 100644 index 0000000..89271cf --- /dev/null +++ b/src/components/MUI/Surfaces/CardMedia.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; +import MuiCardMedia, { + CardMediaProps as MuiCardMediaProps, +} from "@mui/material/CardMedia"; + +export type CardMediaProps = MuiCardMediaProps; + +export const CardMedia = React.forwardRef( + (props, ref) => , +); + +CardMedia.displayName = "CardMedia"; diff --git a/src/components/MUI/Surfaces/Paper.stories.tsx b/src/components/MUI/Surfaces/Paper.stories.tsx new file mode 100644 index 0000000..04a4dba --- /dev/null +++ b/src/components/MUI/Surfaces/Paper.stories.tsx @@ -0,0 +1,80 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Paper } from "./Paper"; +import { Typography } from "../DataDisplay/Typography"; +import { Stack } from "../Layout/Stack"; + +const meta: Meta = { + title: "MUI/Surfaces/Paper", + component: Paper, + tags: ["autodocs"], + argTypes: { + variant: { + control: "select", + options: ["elevation", "outlined"], + }, + elevation: { + control: { + type: "number", + min: 0, + max: 24, + step: 1, + }, + }, + square: { control: "boolean" }, + sx: { control: false }, + }, + args: { + variant: "elevation", + elevation: 1, + square: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: (args) => ( + + Paper content + + ), +}; + +export const Variants: Story = { + render: (_args) => ( + + + Elevation + + + Outlined + + + ), +}; + +export const Elevations: Story = { + render: (_args) => ( + + {[0, 3, 8, 16].map((level) => ( + + Elevation {level} + + ))} + + ), +}; + +export const Corners: Story = { + render: (_args) => ( + + + Rounded (default) + + + Square + + + ), +}; diff --git a/src/components/MUI/Surfaces/Paper.tsx b/src/components/MUI/Surfaces/Paper.tsx new file mode 100644 index 0000000..ce0e7a6 --- /dev/null +++ b/src/components/MUI/Surfaces/Paper.tsx @@ -0,0 +1,10 @@ +import * as React from "react"; +import MuiPaper, { PaperProps as MuiPaperProps } from "@mui/material/Paper"; + +export type PaperProps = MuiPaperProps; + +export const Paper = React.forwardRef( + (props, ref) => , +); + +Paper.displayName = "Paper"; diff --git a/src/utils/diamond.ts b/src/utils/diamond.ts index a5d189e..c7cea0f 100644 --- a/src/utils/diamond.ts +++ b/src/utils/diamond.ts @@ -22,5 +22,15 @@ const regexToVisit = (parsedVisit: RegExpExecArray): Visit => { }; }; -export { regexToVisit, visitRegex, visitToText }; +const colourSet = [ + "default", + "primary", + "secondary", + "success", + "error", + "info", + "warning", +] as const; + +export { regexToVisit, visitRegex, visitToText, colourSet }; export type { Visit };