Extract general popups markup to its own component

Split popups controllers into separate components
Implemented storybooks for all project's popups
This commit is contained in:
ErickSkrauch
2020-07-06 19:29:56 +03:00
parent 28ccab8a98
commit 82abe0a746
39 changed files with 834 additions and 534 deletions

View File

@@ -0,0 +1,37 @@
import React, { ComponentType, MouseEventHandler, ReactNode } from 'react';
import clsx from 'clsx';
import styles from './popup.scss';
interface Props {
title: ReactNode;
wrapperClassName?: string;
popupClassName?: string;
bodyClassName?: string;
isClosable?: boolean;
onClose?: MouseEventHandler<HTMLSpanElement>;
}
const Popup: ComponentType<Props> = ({
title,
wrapperClassName,
popupClassName,
bodyClassName,
isClosable = true,
onClose,
children,
...props // Passthrough the data-params for testing purposes
}) => (
<div className={clsx(styles.popupWrapper, wrapperClassName)} {...props}>
<div className={clsx(styles.popup, popupClassName)}>
<div className={styles.header}>
<h2 className={styles.headerTitle}>{title}</h2>
{isClosable ? <span className={styles.close} onClick={onClose} data-testid="popup-close" /> : ''}
</div>
<div className={clsx(styles.body, bodyClassName)}>{children}</div>
</div>
</div>
);
export default Popup;

View File

@@ -110,7 +110,7 @@ describe('<PopupStack />', () => {
const event = new Event('keyup');
// @ts-ignore
event.which = 27;
event.code = 'Escape';
document.dispatchEvent(event);
uxpect(props.destroy, 'was called once');
@@ -135,7 +135,7 @@ describe('<PopupStack />', () => {
const event = new Event('keyup');
// @ts-ignore
event.which = 27;
event.code = 'Escape';
document.dispatchEvent(event);
uxpect(props.destroy, 'was called once');
@@ -157,7 +157,7 @@ describe('<PopupStack />', () => {
const event = new Event('keyup');
// @ts-ignore
event.which = 27;
event.code = 'Escape';
document.dispatchEvent(event);
uxpect(props.destroy, 'was not called');

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { ReactNode } from 'react';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import { browserHistory } from 'app/services/history';
import { connect } from 'react-redux';
@@ -17,17 +17,17 @@ interface Props {
export class PopupStack extends React.Component<Props> {
unlistenTransition: () => void;
componentDidMount() {
componentDidMount(): void {
document.addEventListener('keyup', this.onKeyPress);
this.unlistenTransition = browserHistory.listen(this.onRouteLeave);
}
componentWillUnmount() {
componentWillUnmount(): void {
document.removeEventListener('keyup', this.onKeyPress);
this.unlistenTransition();
}
render() {
render(): ReactNode {
const { popups } = this.props;
return (
@@ -57,11 +57,11 @@ export class PopupStack extends React.Component<Props> {
}
onClose(popup: PopupConfig) {
return () => this.props.destroy(popup);
return (): void => this.props.destroy(popup);
}
onOverlayClick(popup: PopupConfig) {
return (event: React.MouseEvent<HTMLDivElement>) => {
return (event: React.MouseEvent<HTMLDivElement>): void => {
if (event.target !== event.currentTarget || popup.disableOverlayClose) {
return;
}
@@ -72,7 +72,7 @@ export class PopupStack extends React.Component<Props> {
};
}
popStack() {
popStack(): void {
const [popup] = this.props.popups.slice(-1);
if (popup && !popup.disableOverlayClose) {
@@ -80,14 +80,14 @@ export class PopupStack extends React.Component<Props> {
}
}
onKeyPress = (event: KeyboardEvent) => {
if (event.which === 27) {
onKeyPress = (event: KeyboardEvent): void => {
if (event.code === 'Escape') {
// ESC key
this.popStack();
}
};
onRouteLeave = (nextLocation: Location) => {
onRouteLeave = (nextLocation: Location): void => {
if (nextLocation) {
this.popStack();
}

View File

@@ -0,0 +1,2 @@
export { default } from './Popup';
export { default as PopupStack } from './PopupStack';

View File

@@ -1,8 +1,8 @@
@import '~app/components/ui/colors.scss';
@import '~app/components/ui/fonts.scss';
$popupPadding: 20px; // Отступ контента внутри попапа
$popupMargin: 20px; // Отступ попапа от краёв окна
$popupPadding: 20px; // Default content padding
$popupMargin: 20px; // Outer popup margins
@mixin popupBounding($width, $padding: null) {
@if ($padding == null) {
@@ -88,10 +88,12 @@ $popupMargin: 20px; // Отступ попапа от краёв окна
}
.body {
padding: $popupPadding;
}
.close {
composes: close from '~app/components/ui/icons.scss';
position: absolute;
right: 0;
top: 0;
@@ -107,13 +109,20 @@ $popupMargin: 20px; // Отступ попапа от краёв окна
color: rgba(#000, 0.6);
background: rgba(#fff, 0.75);
}
}
@media (min-width: 655px) {
.close {
@media (min-width: 655px) {
position: fixed;
padding: 35px;
}
.trEnter &,
.trExit & {
// don't show the close during transition, because transform forces "position: fixed"
// to layout relative container, instead of body
opacity: 0;
transform: translate(100%);
transition: 0s;
}
}
/**
@@ -161,14 +170,3 @@ $popupInitPosition: translateY(10%) rotateX(-8deg);
}
}
}
.trEnter,
.trExit {
.close {
// do not show close during transition, because transform forces position: fixed
// to layout relative container, instead of body
opacity: 0;
transform: translate(100%);
transition: 0s;
}
}