Skip to content

Commit

Permalink
feat(backdrop): add component
Browse files Browse the repository at this point in the history
  • Loading branch information
reme3d2y committed Apr 9, 2021
1 parent 3fbc73a commit 2b87958
Show file tree
Hide file tree
Showing 15 changed files with 428 additions and 0 deletions.
24 changes: 24 additions & 0 deletions packages/backdrop/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@alfalab/core-components-backdrop",
"version": "1.0.0",
"description": "Backdrop component",
"keywords": [],
"license": "MIT",
"main": "dist/index.js",
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"dependencies": {
"classnames": "^2.2.6",
"react-transition-group": "^4.3.0"
},
"devDependencies": {
"@types/react-transition-group": "^4.2.4"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.1"
}
}
43 changes: 43 additions & 0 deletions packages/backdrop/src/Component.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Meta, Props, Preview, Story } from '@storybook/addon-docs/blocks';
import {boolean, select, text} from '@storybook/addon-knobs';
import { Container, Row, Col } from 'storybook/blocks/grid';
import { ComponentHeader } from 'storybook/blocks/component-header';

import { Backdrop } from './Component';
import { version } from '../package.json';

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

<Story name="Backdrop">
<Backdrop
open={boolean('open', false)}
invisible={boolean('invisible', false)}
unmountOnExit={boolean('unmountOnExit', false)}
>
{text('children', '')}
</Backdrop>
</Story>


<!-- Docs -->

<ComponentHeader
name='Backdrop'
version={version}
package='@alfalab/core-components-backdrop'
stage={1}
/>


```tsx
import { Backdrop } from '@alfalab/core-components-backdrop';
```

Компонент для затемнения фона.
Поддерживает все параметры из [CSSTransition](http://reactcommunity.org/react-transition-group/css-transition).

<Props of={Backdrop} />
131 changes: 131 additions & 0 deletions packages/backdrop/src/Component.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React from 'react';
import { fireEvent, render } from '@testing-library/react';

import { Backdrop } from './Component';

describe('Backdrop', () => {
const waitForTransition = async () => new Promise(res => setTimeout(res, 1000));

it('should set `data-test-id` attribute', () => {
const dataTestId = 'test-id';
const { queryByTestId } = render(<Backdrop open={true} dataTestId={dataTestId} />);

expect(queryByTestId(dataTestId)).toBeInTheDocument();
});

it('should match snapshot', async () => {
const { queryByTestId, rerender } = render(
<Backdrop open={true} dataTestId='Backdrop'>
<span>children</span>
</Backdrop>,
);

// appear
expect(queryByTestId('Backdrop')).toMatchSnapshot();

// appear done
await waitForTransition();

expect(queryByTestId('Backdrop')).toMatchSnapshot();

// close
rerender(
<Backdrop open={false} dataTestId='Backdrop'>
<span>children</span>
</Backdrop>,
);

// exit
expect(queryByTestId('Backdrop')).toMatchSnapshot();

// exit done, unmount
await waitForTransition();

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

it('should use custom classes', async () => {
const classNames = {
appear: 'my-appear',
appearActive: 'my-active-appear',
appearDone: 'my-done-appear',
enter: 'my-enter',
enterActive: 'my-active-enter',
enterDone: 'my-done-enter',
exit: 'my-exit',
exitActive: 'my-active-exit',
exitDone: 'my-done-exit',
};

const { queryByTestId, rerender } = render(
<Backdrop
open={true}
unmountOnExit={false}
transitionClassNames={classNames}
dataTestId='Backdrop'
/>,
);

// appear
expect(queryByTestId('Backdrop')).toHaveClass(classNames.appear, classNames.appearActive);

// appear done
await waitForTransition();

expect(queryByTestId('Backdrop')).toHaveClass(classNames.appearDone, classNames.enterDone);

// close
rerender(
<Backdrop
open={false}
unmountOnExit={false}
transitionClassNames={classNames}
dataTestId='Backdrop'
/>,
);

// exit
expect(queryByTestId('Backdrop')).toHaveClass(classNames.exit, classNames.exitActive);

// exit done
await waitForTransition();

expect(queryByTestId('Backdrop')).toHaveClass(classNames.exitDone);

// reopen
rerender(
<Backdrop
open={true}
unmountOnExit={false}
transitionClassNames={classNames}
dataTestId='Backdrop'
/>,
);

// enter
expect(queryByTestId('Backdrop')).toHaveClass(classNames.enter, classNames.enterActive);

// enter done
await waitForTransition();

expect(queryByTestId('Backdrop')).toHaveClass(classNames.enterDone);
});

it('should call `onClick` prop', () => {
const cb = jest.fn();
const dataTestId = 'test-id';
const { getByTestId } = render(
<Backdrop open={true} onClick={cb} dataTestId={dataTestId} />,
);

fireEvent.click(getByTestId(dataTestId));

expect(cb).toBeCalledTimes(1);
});

it('should unmount without errors', () => {
const { unmount } = render(<Backdrop open={true} />);

expect(unmount).not.toThrowError();
});
});
75 changes: 75 additions & 0 deletions packages/backdrop/src/Component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { MouseEvent } from 'react';
import cn from 'classnames';
import { TransitionProps } from 'react-transition-group/Transition';
import { CSSTransition } from 'react-transition-group';

import { CSSTransitionClassNames } from 'react-transition-group/CSSTransition';
import styles from './index.module.css';

export type BackdropProps = Partial<TransitionProps> & {
/**
* Прозрачный бэкдроп
*/
invisible?: boolean;

/**
* Управляет видимостью компонента
*/
open: boolean;

/**
* Обработчик клика по бэкдропу
*/
onClose?: (event: MouseEvent<HTMLElement>) => void;

/**
* Дополнительный класс
*/
className?: string;

/**
* Классы анимации
*
* http://reactcommunity.org/react-transition-group/css-transition#CSSTransition-prop-classNames
*/
transitionClassNames?: string | CSSTransitionClassNames;

/**
* Идентификатор для систем автоматизированного тестирования
*/
dataTestId?: string;
};

export const Backdrop: React.FC<BackdropProps> = ({
className,
open = false,
invisible = false,
timeout = 200,
children,
onClose,
dataTestId,
transitionClassNames = styles,
...restProps
}) => {
return (
<CSSTransition
timeout={timeout}
unmountOnExit={true}
classNames={transitionClassNames}
in={open}
appear={true}
{...restProps}
>
<div
aria-hidden={true}
onClick={onClose}
data-test-id={dataTestId}
className={cn(styles.backdrop, className, {
[styles.invisible]: invisible,
})}
>
{children}
</div>
</CSSTransition>
);
};
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions packages/backdrop/src/__snapshots__/Component.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Backdrop should match snapshot 1`] = `
<div
aria-hidden="true"
class="backdrop appear appearActive"
data-test-id="Backdrop"
>
<span>
children
</span>
</div>
`;

exports[`Backdrop should match snapshot 2`] = `
<div
aria-hidden="true"
class="backdrop appearDone enterDone"
data-test-id="Backdrop"
>
<span>
children
</span>
</div>
`;

exports[`Backdrop should match snapshot 3`] = `
<div
aria-hidden="true"
class="backdrop exit exitActive"
data-test-id="Backdrop"
>
<span>
children
</span>
</div>
`;

exports[`Backdrop should match snapshot 4`] = `null`;
40 changes: 40 additions & 0 deletions packages/backdrop/src/component.screenshots.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
setupScreenshotTesting,
generateTestCases,
customSnapshotIdentifier,
} from '../../screenshot-utils';

const screenshotTesting = setupScreenshotTesting({
it,
beforeAll,
afterAll,
expect,
});

describe('Backdrop', () => {
const testCase = (theme: string) =>
screenshotTesting({
cases: generateTestCases({
componentName: 'Backdrop',
knobs: {
open: [true],
invisible: [false, true],
},
testStory: false,
}),
viewport: {
width: 100,
height: 100,
},
screenshotOpts: {
clip: { x: 0, y: 0, width: 100, height: 100 },
},
matchImageSnapshotOptions: {
customSnapshotIdentifier: (...args) =>
`${theme}-${customSnapshotIdentifier(...args)}`,
},
theme,
})();

['default', 'click'].map(testCase);
});

0 comments on commit 2b87958

Please sign in to comment.