Skip to content

Commit

Permalink
feat(Panel): implement panel component (#850)
Browse files Browse the repository at this point in the history
* feat(panel): Implementation of the Panel component

* refactor(styles): refactor main component styles

* fix(a11y): fix a11y errors

* refactor(panel): add support for custom close button and write tests

* fix(panel): fix A11y errors

* refactor(panel): change prop name to closeButtonComponent

* refactor(panel): fix test snapshots

* refactor(panel): refactor Panel component based on code review comments
  • Loading branch information
masoudmanson authored Oct 1, 2024
1 parent 40afdc6 commit 486b2e7
Show file tree
Hide file tree
Showing 16 changed files with 700 additions and 0 deletions.
6 changes: 6 additions & 0 deletions packages/components/src/core/Panel/__storybook__/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const PANEL_EXCLUDED_CONTROLS = [
"sdsType",
"position",
"width",
"disableScrollLock",
];
71 changes: 71 additions & 0 deletions packages/components/src/core/Panel/__storybook__/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Args, Meta } from "@storybook/react";
import { BADGE } from "@geometricpanda/storybook-addon-badges";
import { Panel } from "./stories/default";
import { PANEL_EXCLUDED_CONTROLS } from "./constants";
import { TestDemo } from "./stories/test";
import { CustomHeaderAndCloseButtonDemo } from "./stories/customCloseButton";

export default {
argTypes: {
position: {
control: {
type: "select",
},
options: ["left", "right", "bottom"],
},
sdsType: {
control: {
type: "select",
},
options: ["basic", "overlay"],
},
width: {
control: {
type: "text",
},
},
},
component: Panel,
parameters: {
badges: [BADGE.BETA],
},
title: "Components/Panel",
} as Meta;

// Default

export const Default = {
args: {
position: "left",
sdsType: "basic",
width: "320px",
},
};

// With Custom Close Button and Header

export const CustomHeaderAndCloseButton = {
parameters: {
controls: {
exclude: PANEL_EXCLUDED_CONTROLS,
},
snapshot: {
skip: true,
},
},
render: (args: Args) => <CustomHeaderAndCloseButtonDemo {...args} />,
};

// Test

export const Test = {
parameters: {
controls: {
exclude: PANEL_EXCLUDED_CONTROLS,
},
snapshot: {
skip: true,
},
},
render: (args: Args) => <TestDemo {...args} />,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as React from "react";
import { Args } from "@storybook/react";
import RawPanel from "src/core/Panel";
import Button from "src/core/Button";
import { Box, Typography } from "@mui/material";
import { LONG_LOREM_IPSUM } from "src/common/storybook/loremIpsum";

export const CustomHeaderAndCloseButtonDemo = (props: Args): JSX.Element => {
const [open, setOpen] = React.useState(true);

return (
<>
<Box sx={{ padding: 4 }}>
<Button
sdsStyle="icon"
sdsSize="medium"
sdsType="primary"
icon="InfoCircle"
onClick={() => {
setOpen(true);
}}
/>
</Box>

<RawPanel
sdsType="overlay"
open={open}
closeButtonOnClick={() => {
setOpen(false);
}}
HeaderComponent={
<Typography
variant="h3"
sx={{ margin: "0 !important", padding: "6px 0 !important" }}
>
Panel Header
</Typography>
}
CloseButtonComponent={
<Button
sdsStyle="icon"
sdsSize="medium"
sdsType="secondary"
icon="ChevronLeft"
data-testid="panel-close-button"
aria-label="Panel Toggle"
/>
}
data-testid="panel"
{...props}
>
{LONG_LOREM_IPSUM}
</RawPanel>
</>
);
};
125 changes: 125 additions & 0 deletions packages/components/src/core/Panel/__storybook__/stories/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { Box, useTheme } from "@mui/material";
import { Args } from "@storybook/react";
import React, { useState } from "react";
import { LONG_LOREM_IPSUM } from "src/common/storybook/loremIpsum";
import Button from "src/core/Button";
import Callout from "src/core/Callout";
import CalloutTitle from "src/core/Callout/components/CalloutTitle";
import RawPanel, { PanelProps } from "src/core/Panel";

const InvalidBasicPanelPropsError = (
<Callout intent="negative">
<CalloutTitle>Invalid Props!</CalloutTitle>
<p>
The <strong>Basic</strong> Panel only supports <strong>left</strong> or{" "}
<strong>right</strong> positions. Please update the{" "}
<strong>position</strong> prop to one of these valid values.
</p>
</Callout>
);

const Main = (
props: PanelProps & { open: boolean; children?: React.ReactNode }
) => {
const { open, sdsType, position, children, width } = props;

const margin =
sdsType === "basic"
? position === "left"
? `0 0 0 ${width}`
: position === "right"
? `0 ${width} 0 0`
: "0" // Default to 0 if neither left nor right
: "0"; // Default to 0 for non-basic panels

return (
<Box
sx={{
margin: open ? margin : "none",
}}
>
{children}
</Box>
);
};

export const Panel = (props: Args): JSX.Element => {
const { sdsType, position } = props;

const theme = useTheme();
const [open, setOpen] = useState(false);

const toggleDrawer = (newOpen: boolean) => () => {
setOpen(newOpen);
};

const DrawerList = (
<Box
sx={{
alignItems: "center",
border: `dashed 1px ${theme?.palette?.sds?.base?.divider}`,
color: theme?.palette?.sds?.base?.textSecondary,
display: "flex",
height: "100%",
justifyContent: "center",
width: "100%",
}}
role="presentation"
>
[Panel Content]
</Box>
);

const HeaderComponent = (
<Box
sx={{
alignItems: "center",
border: `dashed 1px ${theme?.palette?.sds?.base?.divider}`,
color: theme?.palette?.sds?.base?.textSecondary,
display: "flex",
height: "100%",
justifyContent: "center",
width: "100%",
}}
>
[Panel Header]
</Box>
);

if (sdsType === "basic" && position === "bottom") {
return InvalidBasicPanelPropsError;
}

return (
<>
<RawPanel
sdsType={sdsType}
open={open}
closeButtonOnClick={toggleDrawer(false)}
HeaderComponent={HeaderComponent}
disableScrollLock={true}
ModalProps={{ disableScrollLock: true }}
{...props}
>
{DrawerList}
</RawPanel>

<Main open={open} {...props}>
<Button
sdsType="primary"
sdsStyle="icon"
icon="InfoCircle"
onClick={() => setOpen((prev) => !prev)}
aria-label="Panel Toggle"
>
Toggle Panel
</Button>

<p>{LONG_LOREM_IPSUM}</p>
<p>{LONG_LOREM_IPSUM}</p>
<p>{LONG_LOREM_IPSUM}</p>
<p>{LONG_LOREM_IPSUM}</p>
</Main>
</>
);
};
10 changes: 10 additions & 0 deletions packages/components/src/core/Panel/__storybook__/stories/test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Args } from "@storybook/react";
import RawPanel from "src/core/Panel";

export const TestDemo = (props: Args): JSX.Element => {
return (
<RawPanel open={true} data-testid="panel" {...props}>
[Panel Content]
</RawPanel>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import { Panel, PanelProps } from "@czi-sds/components";
import { noop } from "src/common/utils";

const PanelNameSpaceTest = (props: PanelProps) => {
return (
<Panel
sdsType="overlay"
width="400px"
position="left"
HeaderComponent={<p>Header</p>}
CloseButtonComponent={<button>Close</button>}
closeButtonOnClick={noop}
>
This is a Basic Panel!
</Panel>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Panel /> Default story renders snapshot 1`] = `
<div
class="MuiDrawer-root MuiDrawer-docked css-1mn3xem-MuiDrawer-docked"
headercomponent="[object Object]"
>
<div
class="MuiPaper-root MuiPaper-elevation MuiPaper-elevation0 MuiDrawer-paper MuiDrawer-paperAnchorLeft MuiDrawer-paperAnchorDockedLeft css-12i7wg6-MuiPaper-root-MuiDrawer-paper"
style="visibility: hidden; webkit-transform: translateX(-0px); transform: translateX(-0px);"
>
<div
class="MuiBox-root css-gblytq"
role="presentation"
>
[Panel Content]
</div>
</div>
</div>
`;
Loading

0 comments on commit 486b2e7

Please sign in to comment.