Skip to content

Commit

Permalink
feat(drawer): add component (#497)
Browse files Browse the repository at this point in the history
* refactor(modal): refactor, simplify, remove unused

* feat(modal): add component

* feat(modal): complete base version

* fix(modal): closer issue

* refactor(modal): some refactor

* feat(drawer): add component

* feat(modal): update stories, add var

* refactor(modal): remove backdrop, fix scroll

* refactor(drawer): backdrop

* feat(drawer): add tests, fix styles

* fix(modal): typo

* fix(modal): missing fullscreen prop

* fix(modal): review fixes

* fix(modal): let me speak from my heart

* refactor(modal): use context

* refactor(drawer): remove child components

* refactor(modal): change layout

* refactor(drawer): more flexibility

* refactor(modal): compound components way, done

* fix(modal): fixes

* fix(modal): entrypoints

* refactor(drawer): update

* fix(modal): styles

* chore(drawer): story

* fix(modal): closer styles

* fix(modal): typo

* fix(modal): style fixes

* fix(drawer): typo

* fix(modal): pr fixes
  • Loading branch information
reme3d2y committed Feb 19, 2021
1 parent 06cb8ba commit 5943188
Show file tree
Hide file tree
Showing 8 changed files with 401 additions and 0 deletions.
24 changes: 24 additions & 0 deletions packages/drawer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@alfalab/core-components-drawer",
"version": "1.0.0",
"description": "Drawer component",
"gitHead": "f054fef20200664c65e2501ef1f916c555cdf05d",
"keywords": [],
"license": "ISC",
"main": "dist/index.js",
"module": "./dist/esm/index.js",
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"react": "^16.9.0",
"react-dom": "^16.9.0"
},
"dependencies": {
"@alfalab/core-components-modal": "^1.0.0",
"classnames": "^2.2.6"
}
}
183 changes: 183 additions & 0 deletions packages/drawer/src/Component.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';
import { action } from '@storybook/addon-actions';
import { text, select, boolean } from '@storybook/addon-knobs';
import { ComponentHeader } from 'storybook/blocks/component-header';

import { Button } from '../../button/src';
import { Typography } from '../../typography/src';

import { Drawer, DrawerContext } from './Component';

import { name, version } from '../package.json';


<Meta
title='Компоненты'
component={Drawer}
parameters={{ 'theme-switcher': { themes: ['click'] } }}
/>

<!-- Canvas -->

export const Header = () => {
const { headerHighlighted, onClose, setHasHeader } = React.useContext(DrawerContext);
React.useEffect(() => setHasHeader(true), [setHasHeader]);
return (
<div style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
padding: '16px 32px',
background: '#feebea',
position: 'sticky',
top: 0,
}}>
<Typography.Title view="small">
{headerHighlighted ? 'Шапка при скролле' : 'Шапка'}
</Typography.Title>
<Button size="xs" onClick={onClose}>Закрыть</Button>
</div>
);
}

export const Footer = () => {
const { setHasFooter } = React.useContext(DrawerContext);
React.useEffect(() => setHasFooter(true), [setHasFooter]);
return (
<div style={{
padding: '16px 32px',
background: '#feebea',
}}>
<Typography.Title view="small">
Футер
</Typography.Title>
</div>
);
}

export const ContentWrapper = ({ children }) => {
const { contentRef } = React.useContext(DrawerContext);
return (
<div
style={{ padding: '16px 32px' }}
ref={contentRef}
>
{children}
</div>
)
}

export const Content = () => {
const [showMore, setShowMore] = React.useState(false);
const Text = () => (
<p>
Сейчас много говорят об отказах банков в проведении операций, блокировках интернет-банка.
Это связано с тем, что Центральный банк РФ обязывает банки выявлять операции своих клиентов,
потенциально нарушающие требования Федерального закона «О противодействии легализации (отмыванию) доходов,
полученных преступным путем, и финансированию терроризма» — 115-ФЗ
</p>
);
return (
<>
<Text/>
{showMore && (
<>
<Text/>
<Text/>
<Text/>
</>
)}
<Button size="xs" type="button" onClick={() => setShowMore(!showMore)}>
{showMore ? 'Hide' : 'Show'} more
</Button>
</>
);
};

<Story name='Drawer'>
{React.createElement(() => {
const [open, setOpen] = React.useState(false);
const handleModalOpen = () => setOpen(!open);
return (
<React.Fragment>
<Button size="xs" onClick={handleModalOpen}>Open Drawer</Button>
<Drawer
open={open}
onClose={handleModalOpen}
keepMounted={boolean('keepMounted', false)}
>
<ContentWrapper>
<Content/>
</ContentWrapper>
</Drawer>
</React.Fragment>
);
})}
</Story>


<!-- Docs -->

<ComponentHeader
name='Drawer'
version={version}
package='@alfalab/core-components-drawer'
stage={1}
design='https://www.figma.com/file/hqSP3L6qu8UcL3sf18Su1M/Web-Components?node-id=16215%3A0'
/>

```tsx
import { Drawer } from '@alfalab/core-components-drawer';
```

## Компонент Drawer

Компонент, представляющим собой заготовку для реализации сайд-панелей. В основе компонента лежит `Modal`.

<Props of={Drawer} />

<Preview>
{React.createElement(() => {
const [open, setOpen] = React.useState(false);
const handleModalOpen = () => setOpen(!open);
return (
<React.Fragment>
<Button size="xs" onClick={handleModalOpen}>Open Drawer</Button>
<Drawer
open={open}
onClose={handleModalOpen}
>
<ContentWrapper>
<Content/>
</ContentWrapper>
</Drawer>
</React.Fragment>
);
})}
</Preview>


## Кастомные шапка и футер

<Preview>
{React.createElement(() => {
const [open, setOpen] = React.useState(false);
const handleModalOpen = () => setOpen(!open);
return (
<React.Fragment>
<Button size="xs" onClick={handleModalOpen}>Open Drawer</Button>
<Drawer
open={open}
onClose={handleModalOpen}
>
<Header />
<ContentWrapper>
<Content/>
</ContentWrapper>
<Footer />
</Drawer>
</React.Fragment>
);
})}
</Preview>

44 changes: 44 additions & 0 deletions packages/drawer/src/Component.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';

import { Drawer } from './Component';

describe('Drawer', () => {
let savedBodyStyle: CSSStyleDeclaration;

beforeAll(() => {
savedBodyStyle = document.body.style;
});

beforeEach(() => {
// eslint-disable-next-line
// @ts-ignore
document.body.setAttribute('style', savedBodyStyle);
});

afterAll(() => {
cleanup();
});

it('should match snapshot', () => {
const { queryByTestId } = render(
<Drawer open={true} dataTestId='drawer'>
<span>header</span>
<span>content</span>
<span>footer</span>
</Drawer>,
);

expect(queryByTestId('drawer')).toMatchSnapshot();
});

it('should not render anything when open=false', () => {
const { queryByTestId } = render(
<Drawer open={false} dataTestId='drawer'>
content
</Drawer>,
);

expect(queryByTestId('drawer')).not.toBeInTheDocument();
});
});
59 changes: 59 additions & 0 deletions packages/drawer/src/Component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { forwardRef, useMemo } from 'react';
import cn from 'classnames';

import { Modal, ModalProps, ModalContext } from '@alfalab/core-components-modal';

import styles from './index.module.css';

export const ANIMATION_DURATION = 600;

export type DrawerProps = Omit<
ModalProps,
'hideBackdrop' | 'fullscreen' | 'container' | 'targetHandleExited'
>;

export const DrawerContext = ModalContext;

export const Drawer = forwardRef<HTMLDivElement, DrawerProps>(
({ open, className, children, ...restProps }, ref) => {
const transitionProps = useMemo(
() => ({
classNames: styles,
timeout: ANIMATION_DURATION,
...restProps.transitionProps,
}),
[restProps.transitionProps],
);

const backdropProps = useMemo(
() => ({
classNames: {
enter: styles.backdropEnter,
enterActive: styles.backdropEnterActive,
appear: styles.backdropAppear,
appearActive: styles.backdropAppearActive,
exit: styles.backdropExit,
exitActive: styles.backdropExitActive,
},
timeout: ANIMATION_DURATION,
...restProps.backdropProps,
}),
[restProps.backdropProps],
);

return (
<Modal
{...restProps}
ref={ref}
open={open}
className={cn(styles.component, className)}
fullscreen={true}
targetHandleExited='children'
transitionProps={transitionProps}
backdropProps={backdropProps}
>
{children}
</Modal>
);
},
);
24 changes: 24 additions & 0 deletions packages/drawer/src/__snapshots__/Component.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Drawer should match snapshot 1`] = `
<div
class="wrapper wrapperFullscreen backdropAppear backdropAppearActive"
data-test-id="drawer"
role="dialog"
tabindex="-1"
>
<div
class="component fullscreen appear appearActive"
>
<span>
header
</span>
<span>
content
</span>
<span>
footer
</span>
</div>
</div>
`;
53 changes: 53 additions & 0 deletions packages/drawer/src/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@import '../../themes/src/default.css';

:root {
--drawer-width: 500px;
}

.component {
width: var(--drawer-width);
right: 0;
top: 0;
will-change: transform;
}

.appear,
.enter {
transform: translateX(100%);
}

.appearActive,
.enterActive {
transition: transform 400ms ease-in-out 200ms;
transform: translateX(0);
}

.exit {
transform: translateX(0);
}

.exitActive,
.exitDone {
transition: transform 400ms ease-in-out;
transform: translateX(100%);
}

.backdropAppear,
.backdropEnter {
background-color: var(--modal-backdrop-hidden-background);
}

.backdropAppearActive,
.backdropEnterActive {
transition: background 200ms ease-in-out;
background-color: var(--modal-backdrop-visible-background);
}

.backdropExit {
background-color: var(--modal-backdrop-visible-background);
}

.backdropExitActive {
transition: background 200ms ease-in-out 400ms;
background-color: var(--modal-backdrop-hidden-background);
}
1 change: 1 addition & 0 deletions packages/drawer/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Component';

0 comments on commit 5943188

Please sign in to comment.