From e5ee00c56b836845dd6df7a6cff643be2e379748 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 20 Jun 2022 17:23:52 +0100 Subject: [PATCH 01/25] feat: Add datagrid story --- src/components/DataGrid/DataGrid.stories.tsx | 65 ++++++++++++++++++++ src/components/DataGrid/DataGrid.tsx | 29 +++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/components/DataGrid/DataGrid.stories.tsx create mode 100644 src/components/DataGrid/DataGrid.tsx diff --git a/src/components/DataGrid/DataGrid.stories.tsx b/src/components/DataGrid/DataGrid.stories.tsx new file mode 100644 index 0000000..3d6418f --- /dev/null +++ b/src/components/DataGrid/DataGrid.stories.tsx @@ -0,0 +1,65 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable react/jsx-props-no-spreading */ +import { DataGridProps } from "@mui/x-data-grid"; +import { DataGrid } from "./DataGrid"; + +export default { + title: "DataGrid", + component: DataGrid, +}; + +interface Props extends DataGridProps { + title: string; +} + +const columns = [ + { field: "id", headerName: "ID", width: 90, editable: false }, + { + field: "lastName", + headerName: "Last name", + width: 150, + editable: false, + hideable: false, // Used to make column unhideable even when you select hide all columns. Meaning you can reshow all the columns when you click the three dots. + }, + { + field: "firstName", + headerName: "First name", + width: 150, + editable: false, + }, + { + field: "age", + headerName: "Age", + type: "number", + width: 110, + editable: false, + }, +]; + +const rows = [ + { id: 1, lastName: "Snow", firstName: "Jon", age: 35 }, + { id: 2, lastName: "Lannister", firstName: "Cersei", age: 42 }, + { id: 3, lastName: "Lannister", firstName: "Jaime", age: 45 }, + { id: 4, lastName: "Stark", firstName: "Arya", age: 16 }, + { id: 5, lastName: "Targaryen", firstName: "Daenerys", age: null }, + { id: 6, lastName: "Snow", firstName: "Jon", age: 35 }, + { id: 7, lastName: "Lannister", firstName: "Cersei", age: 42 }, + { id: 8, lastName: "Lannister", firstName: "Jaime", age: 45 }, + { id: 9, lastName: "Stark", firstName: "Arya", age: 16 }, + { id: 10, lastName: "Targaryen", firstName: "Daenerys", age: null }, + { id: 11, lastName: "Lannister", firstName: "Jaime", age: 45 }, + { id: 12, lastName: "Stark", firstName: "Arya", age: 16 }, +]; +// A wrapper function from Storybook that allows you to pass in props to your component. We use this for showing controls +const Template = (args: Props) => ; + +export const Primary = Template.bind({}); + +Primary.args = { + title: "Example Datagrid", + columns: [columns], + rows: [rows], + sx: { height: "400px" }, + // eslint-disable-next-line @typescript-eslint/no-unsafe-return +}; diff --git a/src/components/DataGrid/DataGrid.tsx b/src/components/DataGrid/DataGrid.tsx new file mode 100644 index 0000000..82568bf --- /dev/null +++ b/src/components/DataGrid/DataGrid.tsx @@ -0,0 +1,29 @@ +import { DataGrid as MaterialDataGrid, DataGridProps } from "@mui/x-data-grid"; +import { ReactElement } from "react"; +import { Card } from "../Card/Card"; + +interface Props extends DataGridProps { + title: string; +} + +export function DataGrid({ + title, + pageSize = 60, + rowsPerPageOptions = [15], + checkboxSelection = true, + disableSelectionOnClick = true, + ...rest +}: Props): ReactElement { + return ( + + + + ); +} From 1feb943e6957626ac3e0837a425f6323e003fd38 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 22 Jun 2022 22:31:01 +0100 Subject: [PATCH 02/25] fix: display data in DataGrid --- src/components/DataGrid/DataGrid.stories.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/DataGrid/DataGrid.stories.tsx b/src/components/DataGrid/DataGrid.stories.tsx index 3d6418f..7ae5a52 100644 --- a/src/components/DataGrid/DataGrid.stories.tsx +++ b/src/components/DataGrid/DataGrid.stories.tsx @@ -58,8 +58,7 @@ export const Primary = Template.bind({}); Primary.args = { title: "Example Datagrid", - columns: [columns], - rows: [rows], + columns: [...columns], + rows: [...rows], sx: { height: "400px" }, - // eslint-disable-next-line @typescript-eslint/no-unsafe-return }; From 5dd70e7f8854efa43d40df44e2402bdad7fa6c42 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 22 Jun 2022 23:41:27 +0100 Subject: [PATCH 03/25] feat: add dialog story --- src/components/DataGrid/DataGrid.stories.tsx | 5 +- src/components/Dialog/Dialog.stories.tsx | 43 ++++++++++++ src/components/Dialog/Dialog.tsx | 74 ++++++++++++++++++++ 3 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 src/components/Dialog/Dialog.stories.tsx create mode 100644 src/components/Dialog/Dialog.tsx diff --git a/src/components/DataGrid/DataGrid.stories.tsx b/src/components/DataGrid/DataGrid.stories.tsx index 3d6418f..7ae5a52 100644 --- a/src/components/DataGrid/DataGrid.stories.tsx +++ b/src/components/DataGrid/DataGrid.stories.tsx @@ -58,8 +58,7 @@ export const Primary = Template.bind({}); Primary.args = { title: "Example Datagrid", - columns: [columns], - rows: [rows], + columns: [...columns], + rows: [...rows], sx: { height: "400px" }, - // eslint-disable-next-line @typescript-eslint/no-unsafe-return }; diff --git a/src/components/Dialog/Dialog.stories.tsx b/src/components/Dialog/Dialog.stories.tsx new file mode 100644 index 0000000..55e520e --- /dev/null +++ b/src/components/Dialog/Dialog.stories.tsx @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable react/jsx-props-no-spreading */ +import { Typography } from "@mui/material"; +import { IconButton } from "../IconButton/IconButton"; +import { Dialog } from "./Dialog"; + +const imgSrc = (src: string, type = "svg"): string => + new URL(`/src/assets/${src}.${type}`, import.meta.url).href; + +export default { + title: "Dialog", + component: Dialog, +}; + +// A wrapper function from Storybook that allows you to pass in props to your component. We use this for showing controls + +const Template = (title: any) => ( + + } + > + Content + +); + +export const StandardDialog = Template.bind({}); +export const ConfirmationDialog = Template.bind({}); +StandardDialog.args = { + title: "Standard Dialog", +}; +ConfirmationDialog.args = { + title: "Confirmation Dialog", + warningDialog: "true", +}; diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx new file mode 100644 index 0000000..b511548 --- /dev/null +++ b/src/components/Dialog/Dialog.tsx @@ -0,0 +1,74 @@ +import { ReactElement, useState, cloneElement, useEffect } from "react"; +import { Dialog as MaterialDialog } from "@mui/material"; +import { Card } from "../Card/Card"; + +interface Props { + children?: ReactElement | null; + TriggerButton: JSX.Element; + title: string; + warningDialog?: boolean; + close?: boolean; + afterClose?: () => void; +} + +export function Dialog({ + children, + title, + TriggerButton, + close, + warningDialog, + afterClose, +}: Props): ReactElement | null { + const [open, setOpen] = useState(false); + + const handleClick = (): void => { + setOpen(true); + }; + + const handleClose = (): void => { + setOpen(false); + // Reset defaults when Dialog is closed + if (afterClose) { + afterClose(); + } + }; + + // externally close the Dialog + useEffect(() => { + if (close) { + setOpen(false); + } + }, [close]); + + const dialogContent = ( + + {children} + + ); + + return ( + <> + {cloneElement(TriggerButton, { + onClick: () => { + handleClick(); + (TriggerButton.props as { onClick: () => void }).onClick(); + }, + })} + + {dialogContent} + + + ); +} + +Dialog.defaultProps = { + children: null, + close: null, + afterClose: null, + warningDialog: null, +}; From 42431b2f4d68d824cfe748b64e6d2996acdc0535 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 23 Jun 2022 16:57:27 +0100 Subject: [PATCH 04/25] feat: add Popover, Dialog and Loading Spinner stories --- examples/index.tsx | 2 +- src/components/Dialog/Dialog.stories.tsx | 4 +- src/components/Dialog/Dialog.tsx | 3 +- .../LoadingSpinner/LoadingSpinner.stories.tsx | 15 ++++ .../LoadingSpinner/LoadingSpinner.tsx | 19 +++++ src/components/Popover/Popover.stories.tsx | 40 ++++++++++ src/components/Popover/Popover.tsx | 77 +++++++++++++++++++ src/theme.tsx | 1 + 8 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 src/components/LoadingSpinner/LoadingSpinner.stories.tsx create mode 100644 src/components/LoadingSpinner/LoadingSpinner.tsx create mode 100644 src/components/Popover/Popover.stories.tsx create mode 100644 src/components/Popover/Popover.tsx diff --git a/examples/index.tsx b/examples/index.tsx index 2eeef67..aaf4208 100644 --- a/examples/index.tsx +++ b/examples/index.tsx @@ -85,7 +85,7 @@ const AdvancedDialogExample = (): ReactElement => { setOpen(true); }} /> - ; + ( +const Template = (args: any) => ( { handleClick(); - (TriggerButton.props as { onClick: () => void }).onClick(); + const { onClick } = TriggerButton.props as { onClick?: () => void }; + if (onClick) onClick(); }, })} diff --git a/src/components/LoadingSpinner/LoadingSpinner.stories.tsx b/src/components/LoadingSpinner/LoadingSpinner.stories.tsx new file mode 100644 index 0000000..43f99ac --- /dev/null +++ b/src/components/LoadingSpinner/LoadingSpinner.stories.tsx @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable react/jsx-props-no-spreading */ +import { LoadingSpinner } from "./LoadingSpinner"; + +export default { + title: "Loading Spinner", + component: LoadingSpinner, +}; + +// A wrapper function from Storybook that allows you to pass in props to your component. We use this for showing controls + +const Template = () => ; + +export const StandardPopover = Template.bind({}); diff --git a/src/components/LoadingSpinner/LoadingSpinner.tsx b/src/components/LoadingSpinner/LoadingSpinner.tsx new file mode 100644 index 0000000..09b57be --- /dev/null +++ b/src/components/LoadingSpinner/LoadingSpinner.tsx @@ -0,0 +1,19 @@ +import { ReactElement } from "react"; +import { CircularProgress, Box, BoxProps } from "@mui/material"; + +const LoadingSpinner = (props: BoxProps): ReactElement => ( + + + +); + +export { LoadingSpinner }; diff --git a/src/components/Popover/Popover.stories.tsx b/src/components/Popover/Popover.stories.tsx new file mode 100644 index 0000000..bc7c62a --- /dev/null +++ b/src/components/Popover/Popover.stories.tsx @@ -0,0 +1,40 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable react/jsx-props-no-spreading */ +import { Typography } from "@mui/material"; +import { IconButton } from "../IconButton/IconButton"; +import { Popover } from "./Popover"; + +const imgSrc = (src: string, type = "svg"): string => + new URL(`/src/assets/${src}.${type}`, import.meta.url).href; + +export default { + title: "Popover", + component: Popover, +}; + +// A wrapper function from Storybook that allows you to pass in props to your component. We use this for showing controls + +const Template = (args: any) => ( + + } + > + Content + +); + +export const StandardPopover = Template.bind({}); + +StandardPopover.args = { + title: "Standard Popover", +}; diff --git a/src/components/Popover/Popover.tsx b/src/components/Popover/Popover.tsx new file mode 100644 index 0000000..e1ea5de --- /dev/null +++ b/src/components/Popover/Popover.tsx @@ -0,0 +1,77 @@ +import { ReactElement, useState, MouseEvent, cloneElement } from "react"; +import { Popover as MaterialPopover, PopoverOrigin } from "@mui/material"; +import { Card } from "../Card/Card"; + +export const imgSrc = (src: string, type = "svg"): string => + new URL(`/src/assets/${src}.${type}`, import.meta.url).href; + +interface Props { + children?: ReactElement | null; + anchorOrigin?: PopoverOrigin; + transformOrigin?: PopoverOrigin; + TriggerButton: JSX.Element; + title: string; +} + +export function Popover({ + children, + anchorOrigin, + transformOrigin, + title, + TriggerButton, +}: Props): ReactElement | null { + const [anchorEl, setAnchorEl] = useState(null); + + const handleClick = (event: MouseEvent): void => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = (): void => { + setAnchorEl(null); + }; + + const popoverContent = ( + + ); + return ( + <> + {cloneElement(TriggerButton, { + onClick: (event: MouseEvent) => { + handleClick(event); + const { onClick } = TriggerButton.props as { + onClick?: () => void; + }; + if (onClick) onClick(); + }, + fill: Boolean(anchorEl), + })} + + {popoverContent} + + + ); +} + +Popover.defaultProps = { + children: null, + anchorOrigin: { + vertical: "top", + horizontal: "left", + }, + transformOrigin: { + vertical: "top", + horizontal: "left", + }, +}; diff --git a/src/theme.tsx b/src/theme.tsx index a0ddbfa..526942b 100644 --- a/src/theme.tsx +++ b/src/theme.tsx @@ -67,6 +67,7 @@ export const theme = createTheme({ styleOverrides: { paper: { borderRadius: "6px", + backgroundColor: "transparent", }, }, }, From 3af815b61d836d5a57cee7fe55bcb28c9bedcb21 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 24 Jun 2022 13:13:41 +0100 Subject: [PATCH 05/25] feat: add logo and loading spinner stories --- src/components/Dialog/Dialog.stories.tsx | 8 ++--- .../IconButton/IconButton.stories.tsx | 13 +++++++++ .../LoadingSpinner/LoadingSpinner.stories.tsx | 5 +--- .../LoadingSpinner/LoadingSpinner.tsx | 29 ++++++++++--------- src/components/Logo/Logo.stories.tsx | 12 ++++++++ src/components/Logo/Logo.tsx | 15 ++++++++++ 6 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 src/components/Logo/Logo.stories.tsx create mode 100644 src/components/Logo/Logo.tsx diff --git a/src/components/Dialog/Dialog.stories.tsx b/src/components/Dialog/Dialog.stories.tsx index 70a9978..af56e3f 100644 --- a/src/components/Dialog/Dialog.stories.tsx +++ b/src/components/Dialog/Dialog.stories.tsx @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable react/jsx-props-no-spreading */ -import { Typography } from "@mui/material"; import { IconButton } from "../IconButton/IconButton"; import { Dialog } from "./Dialog"; @@ -14,7 +13,6 @@ export default { }; // A wrapper function from Storybook that allows you to pass in props to your component. We use this for showing controls - const Template = (args: any) => ( ( size="medium" /> } - > - Content - + /> ); export const StandardDialog = Template.bind({}); export const ConfirmationDialog = Template.bind({}); StandardDialog.args = { title: "Standard Dialog", + children: "Content", }; ConfirmationDialog.args = { title: "Confirmation Dialog", + children: "Content", warningDialog: "true", }; diff --git a/src/components/IconButton/IconButton.stories.tsx b/src/components/IconButton/IconButton.stories.tsx index b7db523..4a87b9a 100644 --- a/src/components/IconButton/IconButton.stories.tsx +++ b/src/components/IconButton/IconButton.stories.tsx @@ -9,6 +9,19 @@ const imgSrc = (src: string, type = "svg"): string => export default { title: "IconButton", component: IconButton, + argTypes: { + // remove some property from the controls UI + to: { + table: { + disable: true, + }, + }, + component: { + table: { + disable: true, + }, + }, + }, }; // A wrapper function from Storybook that allows you to pass in props to your component. We use this for showing controls diff --git a/src/components/LoadingSpinner/LoadingSpinner.stories.tsx b/src/components/LoadingSpinner/LoadingSpinner.stories.tsx index 43f99ac..acfb7e4 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.stories.tsx +++ b/src/components/LoadingSpinner/LoadingSpinner.stories.tsx @@ -1,6 +1,4 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable react/jsx-props-no-spreading */ import { LoadingSpinner } from "./LoadingSpinner"; export default { @@ -9,7 +7,6 @@ export default { }; // A wrapper function from Storybook that allows you to pass in props to your component. We use this for showing controls - const Template = () => ; -export const StandardPopover = Template.bind({}); +export const Spinner = Template.bind({}); diff --git a/src/components/LoadingSpinner/LoadingSpinner.tsx b/src/components/LoadingSpinner/LoadingSpinner.tsx index 09b57be..53e8c28 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.tsx +++ b/src/components/LoadingSpinner/LoadingSpinner.tsx @@ -1,19 +1,22 @@ import { ReactElement } from "react"; -import { CircularProgress, Box, BoxProps } from "@mui/material"; +import { CircularProgress, Box, BoxProps, ThemeProvider } from "@mui/material"; +import { theme } from "../../theme"; const LoadingSpinner = (props: BoxProps): ReactElement => ( - - - + + + + + ); export { LoadingSpinner }; diff --git a/src/components/Logo/Logo.stories.tsx b/src/components/Logo/Logo.stories.tsx new file mode 100644 index 0000000..e222618 --- /dev/null +++ b/src/components/Logo/Logo.stories.tsx @@ -0,0 +1,12 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { Logo } from "./Logo"; + +export default { + title: "Logo", + component: Logo, +}; + +// A wrapper function from Storybook that allows you to pass in props to your component. We use this for showing controls +const Template = () => ; + +export const GliffLogo = Template.bind({}); diff --git a/src/components/Logo/Logo.tsx b/src/components/Logo/Logo.tsx new file mode 100644 index 0000000..44ea07a --- /dev/null +++ b/src/components/Logo/Logo.tsx @@ -0,0 +1,15 @@ +import { ReactElement } from "react"; + +export const imgSrc = (src: string, type = "svg"): string => + new URL(`/src/assets/${src}.${type}`, import.meta.url).href; + +const Logo = (): ReactElement => ( + gliff logo +); + +export { Logo }; From 5a3dfd1f0346a2a90775b7fdc282f2d78628041a Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 27 Jun 2022 01:20:34 +0100 Subject: [PATCH 06/25] feat: add button story WIP --- src/components/Button/Button.stories.tsx | 19 +++++++++++ src/components/Button/Button.tsx | 43 ++++++++++++++++++++++++ src/theme.tsx | 1 + 3 files changed, 63 insertions(+) create mode 100644 src/components/Button/Button.stories.tsx create mode 100644 src/components/Button/Button.tsx diff --git a/src/components/Button/Button.stories.tsx b/src/components/Button/Button.stories.tsx new file mode 100644 index 0000000..b528aad --- /dev/null +++ b/src/components/Button/Button.stories.tsx @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable react/jsx-props-no-spreading */ +import { Button } from "./Button"; + +export default { + title: "Button", + component: Button, +}; + +// A wrapper function from Storybook that allows you to pass in props to your component. We use this for showing controls +const Template = (args: any) =>