Skip to content

Commit

Permalink
Drawer implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
KaiHotz committed Jul 4, 2024
1 parent 4710139 commit 360f04b
Show file tree
Hide file tree
Showing 21 changed files with 710 additions and 41 deletions.
1 change: 0 additions & 1 deletion .storybook/preview.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@import 'highcharts/css/highcharts.css';
@import '../src/styles/index.scss';

*,
Expand Down
21 changes: 12 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,24 @@
"build-storybook": "storybook build"
},
"peerDependencies": {
"@popperjs/core": "^2.x",
"@popperjs/core": "2.x",
"ag-grid-community": "31.x",
"ag-grid-react": "31.x",
"clsx": "2.x",
"date-fns": "3.x",
"highcharts": "^11.x",
"highcharts-react-official": "^3.x",
"lodash": "^4.x",
"highcharts": "11.x",
"highcharts-react-official": "3.x",
"lodash": "4.x",
"react": "18.x",
"react-dom": "18.x",
"react-hook-form": "7.x",
"react-icons": "5.x",
"react-popper": "^2.x",
"react-popper": "2.x",
"react-transition-group": "4.x",
"yup": "^1.x"
},
"dependencies": {
"@hookform/resolvers": "^3.6.0",
"@hookform/resolvers": "^3.7.0",
"react-collapsed": "^4.1.2",
"react-color": "^2.19.3",
"react-datepicker": "^6.9.0",
Expand Down Expand Up @@ -84,6 +85,7 @@
"@types/react-color": "^3.0.12",
"@types/react-datepicker": "^6.2.0",
"@types/react-dom": "^18.3.0",
"@types/react-transition-group": "^4.4.10",
"@typescript-eslint/eslint-plugin": "^7.15.0",
"@typescript-eslint/parser": "^7.15.0",
"@vitejs/plugin-react": "^4.3.1",
Expand All @@ -101,7 +103,7 @@
"eslint-plugin-react-refresh": "^0.4.7",
"eslint-plugin-storybook": "^0.8.0",
"gh-pages": "^6.1.1",
"highcharts": "^11.4.3",
"highcharts": "^11.4.4",
"highcharts-react-official": "^3.2.1",
"jsdom": "^24.1.0",
"lodash": "^4.17.21",
Expand All @@ -111,9 +113,10 @@
"prettier": "3.3.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.0",
"react-hook-form": "^7.52.1",
"react-icons": "5.2.1",
"react-popper": "^2.3.0",
"react-transition-group": "^4.4.5",
"regenerator-runtime": "^0.14.1",
"rimraf": "^5.0.7",
"rollup": "^4.18.0",
Expand All @@ -129,7 +132,7 @@
"stylelint-scss": "^6.3.2",
"tslib": "^2.6.3",
"typescript": "5.5.3",
"vite": "^5.3.2",
"vite": "^5.3.3",
"vite-plugin-svgr": "^4.2.0",
"vitest": "^1.6.0",
"yup": "^1.4.0"
Expand Down
2 changes: 2 additions & 0 deletions src/components/Accordion/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './Accordion';
export * from './AccordionSection';
14 changes: 12 additions & 2 deletions src/components/Backdrop/Backdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@ import './Backdrop.scss';

interface IBackdropProps {
isFrosted?: boolean;
onClose?: () => void;
testId?: string;
}
export const Backdrop: FC<IBackdropProps> = ({ isFrosted }) => {
return <div className={cx('ui-backdrop', { 'ui-backdrop--frosted': isFrosted })} role="presentation" />;

export const Backdrop: FC<IBackdropProps> = ({ isFrosted, onClose, testId = 'ui-backdrop' }) => {
return (
<div
className={cx('ui-backdrop', { 'ui-backdrop--frosted': isFrosted })}
role="presentation"
onClick={onClose}
data-testid={testId}
/>
);
};
3 changes: 3 additions & 0 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface IButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
iconPosition?: 'left' | 'right';
small?: boolean;
variant?: 'primary' | 'secondary' | 'tertiary' | 'ghost' | 'danger';
testId?: string;
}

export const Button: FC<IButtonProps> = ({
Expand All @@ -20,6 +21,7 @@ export const Button: FC<IButtonProps> = ({
children,
onClick,
variant = 'primary',
testId = 'ui-btn',
...rest
}) => {
return (
Expand All @@ -37,6 +39,7 @@ export const Button: FC<IButtonProps> = ({
type={type}
onClick={disabled ? undefined : onClick}
disabled={disabled}
data-testid={testId}
>
{icon && iconPosition === 'left' && icon}
{children}
Expand Down
90 changes: 90 additions & 0 deletions src/components/Drawer/Drawer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { FaCircleInfo, FaTriangleExclamation } from 'react-icons/fa6';

import { Button } from '../Button';
import { Drawer } from './Drawer';

const iconOptions = {
none: undefined,
exclamation: <FaTriangleExclamation size={13} />,
infoIcon: <FaCircleInfo size={13} />,
};

const iconMap = {
none: 'None',
exclamation: 'Exclamation',
infoIcon: 'Information',
};

const meta: Meta<typeof Drawer> = {
title: 'Components/Drawer',
component: Drawer,
argTypes: {
className: {
control: false,
},
onClose: {
control: false,
},
onExpand: {
control: false,
},
toggleFullwidth: {
control: false,
},
toggleMinimize: {
control: false,
},
actions: {
control: false,
},
testId: {
control: false,
},
icon: {
options: Object.keys(iconOptions),
mapping: iconOptions,
control: {
type: 'select',
labels: iconMap,
},
},
},
};
type Story = StoryObj<typeof Drawer>;

export const Default: Story = {
render: function useDrawerStory(args) {
const [open, setOpen] = useState(false);
const toggleOpen = () => setOpen((prevOpen) => !prevOpen);

return (
<div style={{ padding: '20px', display: 'flex' }}>
<div style={{ padding: '10px' }}>
<Button variant="primary" onClick={toggleOpen}>
Click to open Drawer
</Button>
</div>
{open && (
<Drawer {...args} onClose={toggleOpen}>
Content goes here
</Drawer>
)}
</div>
);
},
args: {
title: 'Drawer Title',
anchor: 'right',
isBackdropDisabled: false,
width: '700',
offset: 0,
shouldCloseByEsc: true,
shouldDisplayHeader: true,
isMinimized: false,
isFullWidth: false,
},
};

export default meta;
76 changes: 76 additions & 0 deletions src/components/Drawer/Drawer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { act, render, renderHook, screen } from '@testing-library/react';

import { Drawer } from './Drawer';
import { useDrawerClose } from '../../hooks';

const defaultProps = {
title: 'This is a title',
onClose: vi.fn(),
children: 'dwadaw',
};

describe('Drawer Component', () => {
describe('useDrawerClose', () => {
describe('when user has not clicked', () => {
it('should return closing =false', () => {
const close = vi.fn();
const { result } = renderHook(() => useDrawerClose(close));

expect(result.current.closing).toEqual(false);
});
});
describe('when user has clicked', () => {
it('should return closing = true', () => {
const close = vi.fn();
const { result } = renderHook(() => useDrawerClose(close));

const { closeWithDelay } = result.current;
act(() => {
closeWithDelay();
});

expect(result.current.closing).toEqual(true);
});
it('should call onClick fn after reaching the threshold', () => {
const close = vi.fn();
vi.useFakeTimers();

const { result } = renderHook(() => useDrawerClose(close));

const { closeWithDelay } = result.current;
act(() => {
closeWithDelay();
});
vi.runAllTimers();

expect(close.mock.calls.length).toEqual(1);
});
});
});
describe('Drawer', () => {
it('should render', () => {
render(<Drawer {...defaultProps} />);
expect(screen.getByTestId('ui-drawer-portal')).toBeInTheDocument();
});

it('should show children', () => {
const { children } = defaultProps;
render(<Drawer {...defaultProps} />);

expect(screen.getByText(children)).toBeInTheDocument();
});

it('should with backdrop', () => {
render(<Drawer {...defaultProps} />);

expect(screen.getByTestId('ui-drawer-portal-backdrop')).toBeInTheDocument();
});

it('should render without backdrop', () => {
render(<Drawer isBackdropDisabled {...defaultProps} />);
expect(screen.queryByTestId('ui-drawer-portal-backdrop')).toBeFalsy();
});
});
});
80 changes: 80 additions & 0 deletions src/components/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { ReactNode } from 'react';
import { noop } from 'lodash';

import { DrawerPortal } from './DrawerPortal';
import { DrawerLayout, IDrawerAction } from './DrawerLayout';
import { useDrawerClose } from '../../hooks';

interface IDrawerProps {
title: ReactNode;
children: ReactNode;
anchor?: 'right' | 'left';
icon?: ReactNode;
width?: string;
offset?: number;
shouldCloseByEsc?: boolean;
shouldDisplayHeader?: boolean;
isBackdropDisabled?: boolean;
actions?: IDrawerAction[];
className?: string;
isMinimized?: boolean;
isFullWidth?: boolean;
onClose?: () => void;
toggleMinimize?: () => void;
toggleFullwidth?: () => void;
onExpand?: () => void;
testId?: string;
}

export const Drawer: React.FC<IDrawerProps> = ({
children,
title,
icon,
offset = 0,
width = '700',
shouldDisplayHeader = true,
shouldCloseByEsc = true,
isBackdropDisabled = false,
onClose = noop,
anchor = 'right',
actions,
className,
toggleMinimize,
isMinimized,
toggleFullwidth,
isFullWidth,
onExpand,
testId = 'ui-drawer',
}) => {
const { closing, closeWithDelay } = useDrawerClose(onClose);

return (
<DrawerPortal
onClose={closeWithDelay}
closing={closing}
width={width}
offset={offset}
anchor={anchor}
shouldCloseByEsc={shouldCloseByEsc}
isBackdropDisabled={isBackdropDisabled}
className={className}
toggleMinimize={toggleMinimize}
isMinimized={isMinimized}
toggleFullwidth={toggleFullwidth}
isFullWidth={isFullWidth}
onExpand={onExpand}
testId={`${testId}-portal`}
>
<DrawerLayout
onClose={closeWithDelay}
title={title}
shouldDisplayHeader={shouldDisplayHeader}
icon={icon}
actions={actions}
testId={`${testId}-layout`}
>
{children}
</DrawerLayout>
</DrawerPortal>
);
};
Loading

0 comments on commit 360f04b

Please sign in to comment.