diff --git a/packages/drawer/CHANGELOG.md b/packages/drawer/CHANGELOG.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/drawer/README.md b/packages/drawer/README.md
new file mode 100644
index 000000000..4a2558bf9
--- /dev/null
+++ b/packages/drawer/README.md
@@ -0,0 +1,61 @@
+# @availity/mui-drawer
+
+> Availity MUI Drawer component to be used with @availity/element design system.
+
+[](https://www.npmjs.com/package/@availity/mui-drawer)
+[](https://www.npmjs.com/package/@availity/mui-drawer)
+[](https://github.com/Availity/element/blob/main/packages/mui-drawer/package.json)
+
+## Documentation
+
+This package extends the MUI Drawer component: [MUI Drawer Docs](https://mui.com/components/drawer/)
+
+Live demo and documentation in our [Storybook](https://availity.github.io/element/?path=/docs/components-drawer-introduction--docs)
+
+Availity standards for design and usage can be found in the [Availity Design Guide](https://design.availity.com/2e36e50c7)
+
+## Installation
+
+### Import Through @availity/element (Recommended)
+
+#### NPM
+
+```bash
+npm install @availity/element
+```
+
+#### Yarn
+
+```bash
+yarn add @availity/element
+```
+
+### Direct Import
+
+#### NPM
+
+_This package has a few peer dependencies. Add `@mui/material` & `@emotion/react` to your project if not already installed._
+
+```bash
+npm install @availity/mui-drawer
+```
+
+#### Yarn
+
+```bash
+yarn add @availity/mui-drawer
+```
+
+### Usage
+
+#### Import through @availity/element
+
+```tsx
+import { Drawer } from '@availity/element';
+```
+
+#### Direct import
+
+```tsx
+import { Drawer } from '@availity/mui-drawer';
+```
diff --git a/packages/drawer/introduction.mdx b/packages/drawer/introduction.mdx
new file mode 100644
index 000000000..a3cae4b0c
--- /dev/null
+++ b/packages/drawer/introduction.mdx
@@ -0,0 +1,9 @@
+import { Markdown, Meta } from '@storybook/addon-docs/blocks';
+import ReadMe from './README.md?raw';
+import CHANGELOG from './CHANGELOG.md?raw';
+
+
+
+{ReadMe}
+
+{CHANGELOG}
diff --git a/packages/drawer/jest.config.js b/packages/drawer/jest.config.js
new file mode 100644
index 000000000..56f39cf44
--- /dev/null
+++ b/packages/drawer/jest.config.js
@@ -0,0 +1,7 @@
+const global = require('../../jest.config.global');
+
+module.exports = {
+ ...global,
+ displayName: 'drawer',
+ coverageDirectory: '../../coverage/drawer',
+};
diff --git a/packages/drawer/package.json b/packages/drawer/package.json
new file mode 100644
index 000000000..e53d59b80
--- /dev/null
+++ b/packages/drawer/package.json
@@ -0,0 +1,66 @@
+{
+ "name": "@availity/mui-drawer",
+ "version": "0.0.0",
+ "description": "Availity MUI Drawer Component - part of the @availity/element design system",
+ "keywords": [
+ "react",
+ "typescript",
+ "availity",
+ "mui"
+ ],
+ "homepage": "https://availity.github.io/element/?path=/docs/components-drawer-introduction--docs",
+ "bugs": {
+ "url": "https://github.com/Availity/element/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/Availity/element.git",
+ "directory": "packages/drawer"
+ },
+ "license": "MIT",
+ "author": "Availity Developers ",
+ "browser": "./dist/index.js",
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ "./package.json": "./package.json",
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.js"
+ }
+ },
+ "scripts": {
+ "build": "tsup src/index.ts --format esm,cjs --dts",
+ "dev": "tsup src/index.ts --format esm,cjs --watch --dts",
+ "clean": "rm -rf dist",
+ "clean:nm": "rm -rf node_modules",
+ "publish": "yarn npm publish --tolerate-republish --access public",
+ "publish:canary": "yarn npm publish --access public --tag canary"
+ },
+ "devDependencies": {
+ "@availity/mui-checkbox": "workspace:^",
+ "@availity/mui-form-utils": "workspace:^",
+ "@availity/mui-list": "workspace:^",
+ "@mui/material": "^7.3.4",
+ "react": "19.2.0",
+ "react-dom": "19.2.0",
+ "tsup": "^8.4.0",
+ "typescript": "^5.4.5"
+ },
+ "peerDependencies": {
+ "@mui/material": "^7.0.0",
+ "react": ">=17.0.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "dependencies": {
+ "@availity/mui-backdrop": "workspace:^",
+ "@availity/mui-button": "workspace:^",
+ "@availity/mui-icon": "workspace:^",
+ "@availity/mui-layout": "workspace:^",
+ "@availity/mui-typography": "workspace:^"
+ }
+}
diff --git a/packages/drawer/project.json b/packages/drawer/project.json
new file mode 100644
index 000000000..37f8e8ed6
--- /dev/null
+++ b/packages/drawer/project.json
@@ -0,0 +1,41 @@
+{
+ "name": "mui-drawer",
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
+ "sourceRoot": "packages/drawer/src",
+ "projectType": "library",
+ "tags": [],
+ "targets": {
+ "lint": {
+ "executor": "@nx/eslint:lint",
+ "options": {
+ "eslintConfig": ".eslintrc.json",
+ "silent": false,
+ "fix": false,
+ "cache": true,
+ "cacheLocation": "./node_modules/.cache/drawer/.eslintcache",
+ "maxWarnings": -1,
+ "quiet": false,
+ "noEslintrc": false,
+ "hasTypeAwareRules": true,
+ "cacheStrategy": "metadata"
+ }
+ },
+ "test": {
+ "executor": "@nx/jest:jest",
+ "outputs": ["{workspaceRoot}/coverage/drawer"],
+ "options": {
+ "jestConfig": "packages/drawer/jest.config.js"
+ }
+ },
+ "version": {
+ "executor": "@jscutlery/semver:version",
+ "options": {
+ "preset": "conventional",
+ "commitMessageFormat": "chore({projectName}): release version ${version} [skip ci]",
+ "tagPrefix": "@availity/{projectName}@",
+ "trackDeps": true,
+ "skipCommitTypes": ["docs"]
+ }
+ }
+ }
+}
diff --git a/packages/drawer/src/index.ts b/packages/drawer/src/index.ts
new file mode 100644
index 000000000..34475c9df
--- /dev/null
+++ b/packages/drawer/src/index.ts
@@ -0,0 +1 @@
+export * from './lib/Drawer';
diff --git a/packages/drawer/src/lib/Drawer.stories.tsx b/packages/drawer/src/lib/Drawer.stories.tsx
new file mode 100644
index 000000000..9efec4fb9
--- /dev/null
+++ b/packages/drawer/src/lib/Drawer.stories.tsx
@@ -0,0 +1,133 @@
+import { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react-vite';
+import { Button, IconButton } from '@availity/mui-button';
+import { Box } from '@availity/mui-layout';
+import { ArrowsRotateIcon, ChartPieIcon, InboxIcon, SendIcon, SettingsIcon } from '@availity/mui-icon';
+import { Checkbox } from '@availity/mui-checkbox';
+import { FormControl, FormControlLabel, FormGroup, FormLabel } from '@availity/mui-form-utils';
+import { List, ListItemButton, ListItemIcon, ListItemText } from '@availity/mui-list';
+import { Drawer, DrawerProps } from './Drawer';
+
+const meta: Meta = {
+ title: 'Components/Drawer/Drawer',
+ component: Drawer,
+ tags: ['autodocs'],
+};
+
+export default meta;
+
+export const _Drawer: StoryObj = {
+ render: (args: DrawerProps) => {
+ const [open, setOpen] = useState(false);
+
+ const toggleDrawer = (newOpen: boolean) => () => {
+ setOpen(newOpen);
+ };
+ return (
+
+
+
+
+ );
+ },
+ args: {
+ children: `Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos blanditiis tenetur unde suscipit, quam
+ beatae rerum inventore consectetur, neque doloribus, cupiditate numquam dignissimos laborum fugiat deleniti? Eum
+ quasi quidem quibusdam.`,
+ contentPadding: true,
+ header: 'Drawer Header',
+ actions: (
+
+
+
+ ),
+ },
+};
+
+export const _NavDrawer: StoryObj = {
+ render: (args: DrawerProps) => {
+ const [open, setOpen] = useState(false);
+
+ const toggleDrawer = (newOpen: boolean) => () => {
+ setOpen(newOpen);
+ };
+ return (
+
+
+
+
+ );
+ },
+ args: {
+ anchor: 'left',
+ children: (
+
+
+
+
+
+ Inbox
+
+
+
+
+
+ Sent
+
+
+
+
+
+ Reporting
+
+
+
+
+
+ Settings
+
+
+ ),
+ },
+};
+
+export const _PersistentDrawer: StoryObj = {
+ render: (args: DrawerProps) => {
+ const [open, setOpen] = useState(false);
+
+ const toggleDrawer = (newOpen: boolean) => () => {
+ setOpen(newOpen);
+ };
+ return (
+
+
+
+
+ );
+ },
+ args: {
+ children: (
+
+ Group Label
+
+ } label="Label" />
+ } label="Label 2" />
+ } label="Label 3" />
+
+
+ ),
+ contentPadding: true,
+ header: 'Filter',
+ variant: 'persistent',
+ slotProps: {
+ paper: {
+ variant: 'outlined',
+ },
+ },
+ actions: (
+
+
+
+ ),
+ },
+};
diff --git a/packages/drawer/src/lib/Drawer.test.tsx b/packages/drawer/src/lib/Drawer.test.tsx
new file mode 100644
index 000000000..fda58ab10
--- /dev/null
+++ b/packages/drawer/src/lib/Drawer.test.tsx
@@ -0,0 +1,66 @@
+import { useState } from 'react';
+import { fireEvent, render, waitFor } from '@testing-library/react';
+import { Drawer, DrawerProps } from './Drawer';
+
+const TestDrawer = (props: DrawerProps) => {
+ const [open, setOpen] = useState(true);
+
+ return setOpen(false)} />;
+};
+
+describe('Drawer', () => {
+ test('should render successfully', () => {
+ const { getByText } = render(Test);
+ expect(getByText('Test')).toBeTruthy();
+ });
+
+ test('should close when x is clicked', async () => {
+ const { getByText, getByLabelText, queryByText } = render(Test);
+ expect(getByText('Test')).toBeTruthy();
+ fireEvent.click(getByLabelText('close drawer'));
+ await waitFor(() => expect(queryByText('Test')).toBeFalsy());
+ });
+
+ test('should render header', () => {
+ const { getByText } = render(Test);
+ expect(getByText('Test Header')).toBeTruthy();
+ });
+
+ test('should render actions', () => {
+ const { getByText } = render(Test Action}>Test);
+ expect(getByText('Test Action')).toBeTruthy();
+ });
+
+ test('should apply content padding', () => {
+ const { getByText } = render(
+
+ Test
+
+ );
+ expect(getByText('Test').parentElement).toHaveStyle('padding: 0px 24px');
+ });
+
+ test('should conditionally render header and actions', () => {
+ const { queryByText, rerender } = render(Test);
+ expect(queryByText('Test Header')).toBeNull();
+
+ rerender(
+ Test Action}>
+ Test
+
+ );
+ expect(queryByText('Test Header')).toBeTruthy();
+ expect(queryByText('Test Action')).toBeTruthy();
+ });
+
+ test('should render with responsive width', () => {
+ const { getByRole, rerender } = render(Test);
+ expect(getByRole('dialog')).toHaveStyle('width: 326px');
+
+ rerender(Test);
+ expect(getByRole('dialog')).toHaveStyle('width: 400px');
+
+ rerender(Test);
+ expect(getByRole('dialog')).toHaveStyle('width: 600px');
+ });
+});
diff --git a/packages/drawer/src/lib/Drawer.tsx b/packages/drawer/src/lib/Drawer.tsx
new file mode 100644
index 000000000..80e97a186
--- /dev/null
+++ b/packages/drawer/src/lib/Drawer.tsx
@@ -0,0 +1,98 @@
+// For bundling purposes, always use the direct import for an mui component, i.e. '@mui/material/xxx'
+import MuiDrawer, { DrawerProps as MuiDrawerProps } from '@mui/material/Drawer';
+import { Backdrop } from '@availity/mui-backdrop';
+import { Box, Grid } from '@availity/mui-layout';
+import { Typography } from '@availity/mui-typography';
+import { IconButton } from '@availity/mui-button';
+import { CloseIcon } from '@availity/mui-icon';
+import { styled } from '@mui/material/styles';
+
+export interface DrawerProps extends Omit {
+ children?: React.ReactNode;
+ slots?: Omit;
+ actions?: React.ReactNode;
+ header?: string;
+ size?: 'small' | 'medium' | 'large';
+ contentPadding?: boolean;
+}
+
+const StyledDrawerHeader = styled(Grid, {
+ name: 'MuiDrawer',
+ slot: 'AvDrawerHeader',
+})();
+
+const StyledDrawerHeaderText = styled(Typography, {
+ name: 'MuiDrawer',
+ slot: 'AvDrawerHeaderText',
+})();
+
+const StyledDrawerContent = styled(Box, {
+ name: 'MuiDrawer',
+ slot: 'AvDrawerContent',
+})();
+
+export const Drawer = ({
+ anchor = 'right',
+ actions,
+ children,
+ contentPadding,
+ header,
+ onClose,
+ size = 'medium',
+ ...rest
+}: DrawerProps): React.JSX.Element => {
+ const drawerHeader = (
+
+ {header && {header}}
+
+ {actions && {actions}}
+ {onClose && (
+ onClose(!rest.open, 'backdropClick')}
+ >
+
+
+ )}
+
+
+ );
+ const sizes = {
+ small: { width: '326px', breakpoint: 350 },
+ medium: { width: '400px', breakpoint: 424 },
+ large: { width: '600px', breakpoint: 624 },
+ };
+ const { width, breakpoint } = sizes[size];
+
+ return (
+
+ {header || actions ? drawerHeader : null}
+ {children}
+
+ );
+};
diff --git a/packages/drawer/tsconfig.json b/packages/drawer/tsconfig.json
new file mode 100644
index 000000000..fcaa54641
--- /dev/null
+++ b/packages/drawer/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "include": ["."],
+ "exclude": ["dist", "build", "node_modules"]
+}
diff --git a/packages/drawer/tsconfig.spec.json b/packages/drawer/tsconfig.spec.json
new file mode 100644
index 000000000..e4d456740
--- /dev/null
+++ b/packages/drawer/tsconfig.spec.json
@@ -0,0 +1,10 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "module": "commonjs",
+ "types": ["jest", "node", "@testing-library/jest-dom"],
+ "allowJs": true
+ },
+ "include": ["**/*.test.js", "**/*.test.ts", "**/*.test.tsx", "**/*.d.ts"]
+}
diff --git a/packages/element/package.json b/packages/element/package.json
index 93e51006e..3f525e7ce 100644
--- a/packages/element/package.json
+++ b/packages/element/package.json
@@ -40,6 +40,7 @@
"@availity/mui-dialog": "workspace:*",
"@availity/mui-disclaimer": "workspace:*",
"@availity/mui-divider": "workspace:*",
+ "@availity/mui-drawer": "workspace:*",
"@availity/mui-empty-state": "workspace:*",
"@availity/mui-event-tracker": "workspace:*",
"@availity/mui-favorites": "workspace:*",
diff --git a/packages/element/src/index.ts b/packages/element/src/index.ts
index add3e4612..eeb2680d3 100644
--- a/packages/element/src/index.ts
+++ b/packages/element/src/index.ts
@@ -114,13 +114,7 @@ export { Datepicker } from '@availity/mui-datepicker';
export type { DatepickerProps } from '@availity/mui-datepicker';
// Dialog
-export {
- Dialog,
- DialogActions,
- DialogContent,
- DialogContentText,
- DialogTitle,
-} from '@availity/mui-dialog';
+export { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@availity/mui-dialog';
export type {
DialogProps,
DialogActionsProps,
@@ -137,6 +131,10 @@ export type { DisclaimerProps } from '@availity/mui-disclaimer';
export { Divider } from '@availity/mui-divider';
export type { DividerProps } from '@availity/mui-divider';
+// Drawer
+export { Drawer } from '@availity/mui-drawer';
+export type { DrawerProps } from '@availity/mui-drawer';
+
// Empty State
export { EmptyState, EmptyStateImages, EmptyStateImage } from '@availity/mui-empty-state';
export type { EmptyStateProps, EmptyStateImageProps } from '@availity/mui-empty-state';
diff --git a/packages/nx-generator/src/generators/nx-generator/files/introduction.stories.mdx b/packages/nx-generator/src/generators/nx-generator/files/introduction.mdx
similarity index 100%
rename from packages/nx-generator/src/generators/nx-generator/files/introduction.stories.mdx
rename to packages/nx-generator/src/generators/nx-generator/files/introduction.mdx
diff --git a/packages/nx-generator/src/generators/nx-generator/files/package.json__template__ b/packages/nx-generator/src/generators/nx-generator/files/package.json__template__
index 83538e103..2bb3f972c 100644
--- a/packages/nx-generator/src/generators/nx-generator/files/package.json__template__
+++ b/packages/nx-generator/src/generators/nx-generator/files/package.json__template__
@@ -40,10 +40,10 @@
"publish:canary": "yarn npm publish --access public --tag canary"
},
"devDependencies": {
- "@mui/material": "^7.0.0",
- "react": "18.2.0",
- "react-dom": "18.2.0",
- "tsup": "^8.0.2",
+ "@mui/material": "^7.3.4",
+ "react": "19.2.0",
+ "react-dom": "19.2.0",
+ "tsup": "^8.4.0",
"typescript": "^5.4.5"
},
"peerDependencies": {
diff --git a/packages/theme/src/lib/legacy-theme.ts b/packages/theme/src/lib/legacy-theme.ts
index 247ddad90..9d1785e48 100644
--- a/packages/theme/src/lib/legacy-theme.ts
+++ b/packages/theme/src/lib/legacy-theme.ts
@@ -1175,6 +1175,30 @@ export const legacyTheme = {
},
},
},
+ MuiDrawer: {
+ defaultProps: {
+ anchor: 'right',
+ contentPadding: false,
+ },
+ styleOverrides: {
+ avDrawerContent: {
+ overflowY: 'auto',
+ },
+ avDrawerHeader: {
+ padding: '16px 16px 24px 24px',
+ justifyContent: 'space-between',
+ alignItems: 'flex-start',
+ flexWrap: 'nowrap',
+ flexShrink: 0,
+ },
+ paper: {
+ overflow: 'hidden',
+ '&.MuiPaper-elevation:not(.MuiPaper-elevation0)': {
+ boxShadow: tokens.shadows16
+ }
+ }
+ },
+ },
MuiFormControl: {
defaultProps: {
size: 'small',
diff --git a/packages/theme/src/lib/light-theme.ts b/packages/theme/src/lib/light-theme.ts
index 301c6b435..a93c5a51d 100644
--- a/packages/theme/src/lib/light-theme.ts
+++ b/packages/theme/src/lib/light-theme.ts
@@ -1169,6 +1169,33 @@ export const lightTheme = {
},
},
},
+ MuiDrawer: {
+ defaultProps: {
+ anchor: 'right',
+ contentPadding: false,
+ },
+ styleOverrides: {
+ avDrawerContent: {
+ overflowY: 'auto',
+ },
+ avDrawerHeader: {
+ padding: '16px 16px 24px 24px',
+ justifyContent: 'space-between',
+ alignItems: 'flex-start',
+ flexWrap: 'nowrap',
+ flexShrink: 0,
+ },
+ avDrawerHeaderText: {
+ paddingTop: "12px"
+ },
+ paper: {
+ overflow: 'hidden',
+ '&.MuiPaper-elevation:not(.MuiPaper-elevation0)': {
+ boxShadow: tokens.shadows16
+ }
+ }
+ },
+ },
MuiFormControl: {
defaultProps: {
size: 'small',
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 81521e00f..2316500ac 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -42,6 +42,7 @@
"@availity/mui-dialog": ["packages/dialog/src/index.ts"],
"@availity/mui-disclaimer": ["packages/disclaimer/src/index.ts"],
"@availity/mui-divider": ["packages/divider/src/index.ts"],
+ "@availity/mui-drawer": ["packages/drawer/src/index.ts"],
"@availity/mui-empty-state": ["packages/empty-state/src/index.ts"],
"@availity/mui-event-tracker": ["packages/event-tracker/src/index.ts"],
"@availity/mui-favorites": ["packages/favorites/src/index.ts"],
diff --git a/yarn.lock b/yarn.lock
index 0dfe1a277..450a419b5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -107,6 +107,7 @@ __metadata:
"@availity/mui-dialog": "workspace:*"
"@availity/mui-disclaimer": "workspace:*"
"@availity/mui-divider": "workspace:*"
+ "@availity/mui-drawer": "workspace:*"
"@availity/mui-empty-state": "workspace:*"
"@availity/mui-event-tracker": "workspace:*"
"@availity/mui-favorites": "workspace:*"
@@ -281,7 +282,7 @@ __metadata:
languageName: unknown
linkType: soft
-"@availity/mui-backdrop@workspace:*, @availity/mui-backdrop@workspace:packages/backdrop":
+"@availity/mui-backdrop@workspace:*, @availity/mui-backdrop@workspace:^, @availity/mui-backdrop@workspace:packages/backdrop":
version: 0.0.0-use.local
resolution: "@availity/mui-backdrop@workspace:packages/backdrop"
dependencies:
@@ -531,6 +532,29 @@ __metadata:
languageName: unknown
linkType: soft
+"@availity/mui-drawer@workspace:*, @availity/mui-drawer@workspace:packages/drawer":
+ version: 0.0.0-use.local
+ resolution: "@availity/mui-drawer@workspace:packages/drawer"
+ dependencies:
+ "@availity/mui-backdrop": "workspace:^"
+ "@availity/mui-button": "workspace:^"
+ "@availity/mui-checkbox": "workspace:^"
+ "@availity/mui-form-utils": "workspace:^"
+ "@availity/mui-icon": "workspace:^"
+ "@availity/mui-layout": "workspace:^"
+ "@availity/mui-list": "workspace:^"
+ "@availity/mui-typography": "workspace:^"
+ "@mui/material": "npm:^7.3.4"
+ react: "npm:19.2.0"
+ react-dom: "npm:19.2.0"
+ tsup: "npm:^8.4.0"
+ typescript: "npm:^5.4.5"
+ peerDependencies:
+ "@mui/material": ^7.0.0
+ react: ">=17.0.0"
+ languageName: unknown
+ linkType: soft
+
"@availity/mui-empty-state@workspace:*, @availity/mui-empty-state@workspace:packages/empty-state":
version: 0.0.0-use.local
resolution: "@availity/mui-empty-state@workspace:packages/empty-state"