mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-05-31 14:11:58 +05:30
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:
37
packages/app/components/ui/popup/Popup.tsx
Normal file
37
packages/app/components/ui/popup/Popup.tsx
Normal 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;
|
||||
@@ -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');
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
2
packages/app/components/ui/popup/index.ts
Normal file
2
packages/app/components/ui/popup/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default } from './Popup';
|
||||
export { default as PopupStack } from './PopupStack';
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user