mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-05-31 14:11:58 +05:30
Change prettier rules
This commit is contained in:
@@ -6,164 +6,160 @@ import React from 'react';
|
||||
import { PopupStack } from './PopupStack';
|
||||
|
||||
function DummyPopup({ onClose }: Record<string, any>) {
|
||||
return (
|
||||
<div>
|
||||
<button type="button" onClick={onClose}>
|
||||
close
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<button type="button" onClick={onClose}>
|
||||
close
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
describe('<PopupStack />', () => {
|
||||
it('renders all popup components', () => {
|
||||
const props: any = {
|
||||
destroy: () => {},
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
};
|
||||
it('renders all popup components', () => {
|
||||
const props: any = {
|
||||
destroy: () => {},
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
render(<PopupStack {...props} />);
|
||||
render(<PopupStack {...props} />);
|
||||
|
||||
const popups = screen.getAllByRole('dialog');
|
||||
const popups = screen.getAllByRole('dialog');
|
||||
|
||||
uxpect(popups, 'to have length', 2);
|
||||
});
|
||||
|
||||
it('should hide popup, when onClose called', async () => {
|
||||
const destroy = sinon.stub().named('props.destroy');
|
||||
const props: any = {
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
destroy,
|
||||
};
|
||||
|
||||
const { rerender } = render(<PopupStack {...props} />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'close' }));
|
||||
|
||||
uxpect(destroy, 'was called once');
|
||||
uxpect(destroy, 'to have a call satisfying', [
|
||||
uxpect.it('to be', props.popups[0]),
|
||||
]);
|
||||
|
||||
rerender(<PopupStack popups={[]} destroy={destroy} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
uxpect(popups, 'to have length', 2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should hide popup, when overlay clicked', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
};
|
||||
it('should hide popup, when onClose called', async () => {
|
||||
const destroy = sinon.stub().named('props.destroy');
|
||||
const props: any = {
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
destroy,
|
||||
};
|
||||
|
||||
render(<PopupStack {...props} />);
|
||||
const { rerender } = render(<PopupStack {...props} />);
|
||||
|
||||
fireEvent.click(screen.getByRole('dialog'));
|
||||
fireEvent.click(screen.getByRole('button', { name: 'close' }));
|
||||
|
||||
uxpect(props.destroy, 'was called once');
|
||||
});
|
||||
uxpect(destroy, 'was called once');
|
||||
uxpect(destroy, 'to have a call satisfying', [uxpect.it('to be', props.popups[0])]);
|
||||
|
||||
it('should not hide popup on overlay click if disableOverlayClose', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
disableOverlayClose: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
rerender(<PopupStack popups={[]} destroy={destroy} />);
|
||||
|
||||
render(<PopupStack {...props} />);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole('dialog'));
|
||||
it('should hide popup, when overlay clicked', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
uxpect(props.destroy, 'was not called');
|
||||
});
|
||||
render(<PopupStack {...props} />);
|
||||
|
||||
it('should hide popup, when esc pressed', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
};
|
||||
fireEvent.click(screen.getByRole('dialog'));
|
||||
|
||||
render(<PopupStack {...props} />);
|
||||
uxpect(props.destroy, 'was called once');
|
||||
});
|
||||
|
||||
const event = new Event('keyup');
|
||||
// @ts-ignore
|
||||
event.which = 27;
|
||||
document.dispatchEvent(event);
|
||||
it('should not hide popup on overlay click if disableOverlayClose', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
disableOverlayClose: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
uxpect(props.destroy, 'was called once');
|
||||
});
|
||||
render(<PopupStack {...props} />);
|
||||
|
||||
it('should hide first popup in stack if esc pressed', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
};
|
||||
fireEvent.click(screen.getByRole('dialog'));
|
||||
|
||||
render(<PopupStack {...props} />);
|
||||
uxpect(props.destroy, 'was not called');
|
||||
});
|
||||
|
||||
const event = new Event('keyup');
|
||||
// @ts-ignore
|
||||
event.which = 27;
|
||||
document.dispatchEvent(event);
|
||||
it('should hide popup, when esc pressed', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
uxpect(props.destroy, 'was called once');
|
||||
uxpect(props.destroy, 'to have a call satisfying', [
|
||||
uxpect.it('to be', props.popups[1]),
|
||||
]);
|
||||
});
|
||||
render(<PopupStack {...props} />);
|
||||
|
||||
it('should NOT hide popup on esc pressed if disableOverlayClose', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
disableOverlayClose: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
const event = new Event('keyup');
|
||||
// @ts-ignore
|
||||
event.which = 27;
|
||||
document.dispatchEvent(event);
|
||||
|
||||
render(<PopupStack {...props} />);
|
||||
uxpect(props.destroy, 'was called once');
|
||||
});
|
||||
|
||||
const event = new Event('keyup');
|
||||
// @ts-ignore
|
||||
event.which = 27;
|
||||
document.dispatchEvent(event);
|
||||
it('should hide first popup in stack if esc pressed', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
uxpect(props.destroy, 'was not called');
|
||||
});
|
||||
render(<PopupStack {...props} />);
|
||||
|
||||
const event = new Event('keyup');
|
||||
// @ts-ignore
|
||||
event.which = 27;
|
||||
document.dispatchEvent(event);
|
||||
|
||||
uxpect(props.destroy, 'was called once');
|
||||
uxpect(props.destroy, 'to have a call satisfying', [uxpect.it('to be', props.popups[1])]);
|
||||
});
|
||||
|
||||
it('should NOT hide popup on esc pressed if disableOverlayClose', () => {
|
||||
const props: any = {
|
||||
destroy: sinon.stub().named('props.destroy'),
|
||||
popups: [
|
||||
{
|
||||
Popup: DummyPopup,
|
||||
disableOverlayClose: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
render(<PopupStack {...props} />);
|
||||
|
||||
const event = new Event('keyup');
|
||||
// @ts-ignore
|
||||
event.which = 27;
|
||||
document.dispatchEvent(event);
|
||||
|
||||
uxpect(props.destroy, 'was not called');
|
||||
});
|
||||
});
|
||||
|
@@ -10,99 +10,95 @@ import { destroy } from './actions';
|
||||
import styles from './popup.scss';
|
||||
|
||||
interface Props {
|
||||
popups: PopupConfig[];
|
||||
destroy: (popup: PopupConfig) => void;
|
||||
popups: PopupConfig[];
|
||||
destroy: (popup: PopupConfig) => void;
|
||||
}
|
||||
|
||||
export class PopupStack extends React.Component<Props> {
|
||||
unlistenTransition: () => void;
|
||||
unlistenTransition: () => void;
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('keyup', this.onKeyPress);
|
||||
this.unlistenTransition = browserHistory.listen(this.onRouteLeave);
|
||||
}
|
||||
componentDidMount() {
|
||||
document.addEventListener('keyup', this.onKeyPress);
|
||||
this.unlistenTransition = browserHistory.listen(this.onRouteLeave);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keyup', this.onKeyPress);
|
||||
this.unlistenTransition();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keyup', this.onKeyPress);
|
||||
this.unlistenTransition();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { popups } = this.props;
|
||||
render() {
|
||||
const { popups } = this.props;
|
||||
|
||||
return (
|
||||
<TransitionGroup>
|
||||
{popups.map((popup, index) => {
|
||||
const { Popup } = popup;
|
||||
return (
|
||||
<TransitionGroup>
|
||||
{popups.map((popup, index) => {
|
||||
const { Popup } = popup;
|
||||
|
||||
return (
|
||||
<CSSTransition
|
||||
key={index}
|
||||
classNames={{
|
||||
enter: styles.trEnter,
|
||||
enterActive: styles.trEnterActive,
|
||||
exit: styles.trExit,
|
||||
exitActive: styles.trExitActive,
|
||||
}}
|
||||
timeout={500}
|
||||
>
|
||||
<div
|
||||
className={styles.overlay}
|
||||
role="dialog"
|
||||
onClick={this.onOverlayClick(popup)}
|
||||
>
|
||||
<Popup onClose={this.onClose(popup)} />
|
||||
</div>
|
||||
</CSSTransition>
|
||||
);
|
||||
})}
|
||||
</TransitionGroup>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<CSSTransition
|
||||
key={index}
|
||||
classNames={{
|
||||
enter: styles.trEnter,
|
||||
enterActive: styles.trEnterActive,
|
||||
exit: styles.trExit,
|
||||
exitActive: styles.trExitActive,
|
||||
}}
|
||||
timeout={500}
|
||||
>
|
||||
<div className={styles.overlay} role="dialog" onClick={this.onOverlayClick(popup)}>
|
||||
<Popup onClose={this.onClose(popup)} />
|
||||
</div>
|
||||
</CSSTransition>
|
||||
);
|
||||
})}
|
||||
</TransitionGroup>
|
||||
);
|
||||
}
|
||||
|
||||
onClose(popup: PopupConfig) {
|
||||
return () => this.props.destroy(popup);
|
||||
}
|
||||
onClose(popup: PopupConfig) {
|
||||
return () => this.props.destroy(popup);
|
||||
}
|
||||
|
||||
onOverlayClick(popup: PopupConfig) {
|
||||
return (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (event.target !== event.currentTarget || popup.disableOverlayClose) {
|
||||
return;
|
||||
}
|
||||
onOverlayClick(popup: PopupConfig) {
|
||||
return (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (event.target !== event.currentTarget || popup.disableOverlayClose) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.preventDefault();
|
||||
|
||||
this.props.destroy(popup);
|
||||
this.props.destroy(popup);
|
||||
};
|
||||
}
|
||||
|
||||
popStack() {
|
||||
const [popup] = this.props.popups.slice(-1);
|
||||
|
||||
if (popup && !popup.disableOverlayClose) {
|
||||
this.props.destroy(popup);
|
||||
}
|
||||
}
|
||||
|
||||
onKeyPress = (event: KeyboardEvent) => {
|
||||
if (event.which === 27) {
|
||||
// ESC key
|
||||
this.popStack();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
popStack() {
|
||||
const [popup] = this.props.popups.slice(-1);
|
||||
|
||||
if (popup && !popup.disableOverlayClose) {
|
||||
this.props.destroy(popup);
|
||||
}
|
||||
}
|
||||
|
||||
onKeyPress = (event: KeyboardEvent) => {
|
||||
if (event.which === 27) {
|
||||
// ESC key
|
||||
this.popStack();
|
||||
}
|
||||
};
|
||||
|
||||
onRouteLeave = (nextLocation: Location) => {
|
||||
if (nextLocation) {
|
||||
this.popStack();
|
||||
}
|
||||
};
|
||||
onRouteLeave = (nextLocation: Location) => {
|
||||
if (nextLocation) {
|
||||
this.popStack();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state: RootState) => ({
|
||||
...state.popup,
|
||||
}),
|
||||
{
|
||||
destroy,
|
||||
},
|
||||
(state: RootState) => ({
|
||||
...state.popup,
|
||||
}),
|
||||
{
|
||||
destroy,
|
||||
},
|
||||
)(PopupStack);
|
||||
|
@@ -2,27 +2,27 @@ import { Action as ReduxPopup } from 'redux';
|
||||
import { PopupConfig } from './reducer';
|
||||
|
||||
interface PopupCreateAction extends ReduxPopup {
|
||||
type: 'POPUP_CREATE';
|
||||
payload: PopupConfig;
|
||||
type: 'POPUP_CREATE';
|
||||
payload: PopupConfig;
|
||||
}
|
||||
|
||||
export function create(popup: PopupConfig): PopupCreateAction {
|
||||
return {
|
||||
type: 'POPUP_CREATE',
|
||||
payload: popup,
|
||||
};
|
||||
return {
|
||||
type: 'POPUP_CREATE',
|
||||
payload: popup,
|
||||
};
|
||||
}
|
||||
|
||||
interface PopupDestroyAction extends ReduxPopup {
|
||||
type: 'POPUP_DESTROY';
|
||||
payload: PopupConfig;
|
||||
type: 'POPUP_DESTROY';
|
||||
payload: PopupConfig;
|
||||
}
|
||||
|
||||
export function destroy(popup: PopupConfig): PopupDestroyAction {
|
||||
return {
|
||||
type: 'POPUP_DESTROY',
|
||||
payload: popup,
|
||||
};
|
||||
return {
|
||||
type: 'POPUP_DESTROY',
|
||||
payload: popup,
|
||||
};
|
||||
}
|
||||
|
||||
export type Action = PopupCreateAction | PopupDestroyAction;
|
||||
|
@@ -5,115 +5,115 @@ $popupPadding: 20px; // Отступ контента внутри попапа
|
||||
$popupMargin: 20px; // Отступ попапа от краёв окна
|
||||
|
||||
@mixin popupBounding($width, $padding: null) {
|
||||
@if ($padding == null) {
|
||||
$padding: $popupMargin;
|
||||
}
|
||||
@if ($padding == null) {
|
||||
$padding: $popupMargin;
|
||||
}
|
||||
|
||||
@if ($padding != $popupMargin) {
|
||||
margin: $padding auto;
|
||||
padding: 0 $padding;
|
||||
}
|
||||
@if ($padding != $popupMargin) {
|
||||
margin: $padding auto;
|
||||
padding: 0 $padding;
|
||||
}
|
||||
|
||||
max-width: $width;
|
||||
max-width: $width;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
z-index: 200;
|
||||
z-index: 200;
|
||||
|
||||
background: rgba(#fff, 0.8);
|
||||
background: rgba(#fff, 0.8);
|
||||
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
perspective: 600px;
|
||||
perspective: 600px;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
}
|
||||
&:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.popupWrapper {
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto;
|
||||
padding: $popupMargin;
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto;
|
||||
padding: $popupMargin;
|
||||
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
|
||||
transition: max-width 0.3s;
|
||||
transition: max-width 0.3s;
|
||||
}
|
||||
|
||||
.popup {
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
|
||||
background: #fff;
|
||||
box-shadow: 0 0 10px rgba(#000, 0.2);
|
||||
background: #fff;
|
||||
box-shadow: 0 0 10px rgba(#000, 0.2);
|
||||
|
||||
color: #666;
|
||||
color: #666;
|
||||
|
||||
:invalid {
|
||||
button {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
:invalid {
|
||||
button {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
background: $white;
|
||||
padding: 15px $popupPadding;
|
||||
border-bottom: 1px solid #dedede;
|
||||
position: relative;
|
||||
background: $white;
|
||||
padding: 15px $popupPadding;
|
||||
border-bottom: 1px solid #dedede;
|
||||
}
|
||||
|
||||
.headerTitle {
|
||||
font-size: 20px;
|
||||
font-family: $font-family-title;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-family: $font-family-title;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: $popupPadding;
|
||||
padding: $popupPadding;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 15px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
color: rgba(#000, 0.4);
|
||||
background: rgba(#000, 0);
|
||||
transition: 0.25s;
|
||||
transform: translateX(0);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 15px;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
color: rgba(#000, 0.4);
|
||||
background: rgba(#000, 0);
|
||||
transition: 0.25s;
|
||||
transform: translateX(0);
|
||||
|
||||
&:hover {
|
||||
color: rgba(#000, 0.6);
|
||||
background: rgba(#fff, 0.75);
|
||||
}
|
||||
&:hover {
|
||||
color: rgba(#000, 0.6);
|
||||
background: rgba(#fff, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 655px) {
|
||||
.close {
|
||||
position: fixed;
|
||||
padding: 35px;
|
||||
}
|
||||
.close {
|
||||
position: fixed;
|
||||
padding: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,53 +122,53 @@ $popupMargin: 20px; // Отступ попапа от краёв окна
|
||||
$popupInitPosition: translateY(10%) rotateX(-8deg);
|
||||
|
||||
.trEnter {
|
||||
opacity: 0;
|
||||
|
||||
.popup {
|
||||
opacity: 0;
|
||||
transform: $popupInitPosition;
|
||||
}
|
||||
|
||||
&Active {
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease-in;
|
||||
|
||||
.popup {
|
||||
opacity: 1;
|
||||
transform: translateY(0) rotateX(0deg);
|
||||
transition: 0.3s cubic-bezier(0.23, 1, 0.32, 1); // easeOutQuint
|
||||
opacity: 0;
|
||||
transform: $popupInitPosition;
|
||||
}
|
||||
|
||||
&Active {
|
||||
opacity: 1;
|
||||
transition: opacity 0.2s ease-in;
|
||||
|
||||
.popup {
|
||||
opacity: 1;
|
||||
transform: translateY(0) rotateX(0deg);
|
||||
transition: 0.3s cubic-bezier(0.23, 1, 0.32, 1); // easeOutQuint
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trExit {
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
|
||||
.popup {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&Active {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-in;
|
||||
overflow: hidden;
|
||||
|
||||
.popup {
|
||||
opacity: 0;
|
||||
transform: $popupInitPosition;
|
||||
transition: 0.3s cubic-bezier(0.165, 0.84, 0.44, 1); // easeOutQuart
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&Active {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-in;
|
||||
|
||||
.popup {
|
||||
opacity: 0;
|
||||
transform: $popupInitPosition;
|
||||
transition: 0.3s cubic-bezier(0.165, 0.84, 0.44, 1); // easeOutQuart
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
@@ -8,91 +8,91 @@ import { create, destroy } from './actions';
|
||||
const FakeComponent: ComponentType = () => <span />;
|
||||
|
||||
describe('popup/reducer', () => {
|
||||
it('should have no popups by default', () => {
|
||||
const actual = reducer(undefined, {
|
||||
type: 'init',
|
||||
it('should have no popups by default', () => {
|
||||
const actual = reducer(undefined, {
|
||||
type: 'init',
|
||||
});
|
||||
|
||||
expect(actual.popups, 'to be an', 'array');
|
||||
expect(actual.popups, 'to be empty');
|
||||
});
|
||||
|
||||
expect(actual.popups, 'to be an', 'array');
|
||||
expect(actual.popups, 'to be empty');
|
||||
});
|
||||
describe('#create', () => {
|
||||
it('should create popup', () => {
|
||||
const actual = reducer(
|
||||
undefined,
|
||||
create({
|
||||
Popup: FakeComponent,
|
||||
}),
|
||||
);
|
||||
|
||||
describe('#create', () => {
|
||||
it('should create popup', () => {
|
||||
const actual = reducer(
|
||||
undefined,
|
||||
create({
|
||||
Popup: FakeComponent,
|
||||
}),
|
||||
);
|
||||
expect(actual.popups[0], 'to equal', {
|
||||
Popup: FakeComponent,
|
||||
});
|
||||
});
|
||||
|
||||
expect(actual.popups[0], 'to equal', {
|
||||
Popup: FakeComponent,
|
||||
});
|
||||
it('should create multiple popups', () => {
|
||||
let actual = reducer(
|
||||
undefined,
|
||||
create({
|
||||
Popup: FakeComponent,
|
||||
}),
|
||||
);
|
||||
actual = reducer(
|
||||
actual,
|
||||
create({
|
||||
Popup: FakeComponent,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(actual.popups[1], 'to equal', {
|
||||
Popup: FakeComponent,
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when no type provided', () => {
|
||||
expect(
|
||||
() =>
|
||||
reducer(
|
||||
undefined,
|
||||
// @ts-ignore
|
||||
create({}),
|
||||
),
|
||||
'to throw',
|
||||
'Popup is required',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should create multiple popups', () => {
|
||||
let actual = reducer(
|
||||
undefined,
|
||||
create({
|
||||
Popup: FakeComponent,
|
||||
}),
|
||||
);
|
||||
actual = reducer(
|
||||
actual,
|
||||
create({
|
||||
Popup: FakeComponent,
|
||||
}),
|
||||
);
|
||||
describe('#destroy', () => {
|
||||
let state: State;
|
||||
let popup: PopupConfig;
|
||||
|
||||
expect(actual.popups[1], 'to equal', {
|
||||
Popup: FakeComponent,
|
||||
});
|
||||
beforeEach(() => {
|
||||
state = reducer(state, create({ Popup: FakeComponent }));
|
||||
[popup] = state.popups;
|
||||
});
|
||||
|
||||
it('should remove popup', () => {
|
||||
expect(state.popups, 'to have length', 1);
|
||||
|
||||
state = reducer(state, destroy(popup));
|
||||
|
||||
expect(state.popups, 'to have length', 0);
|
||||
});
|
||||
|
||||
it('should not remove something, that it should not', () => {
|
||||
state = reducer(
|
||||
state,
|
||||
create({
|
||||
Popup: FakeComponent,
|
||||
}),
|
||||
);
|
||||
|
||||
state = reducer(state, destroy(popup));
|
||||
|
||||
expect(state.popups, 'to have length', 1);
|
||||
expect(state.popups[0], 'not to be', popup);
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when no type provided', () => {
|
||||
expect(
|
||||
() =>
|
||||
reducer(
|
||||
undefined,
|
||||
// @ts-ignore
|
||||
create({}),
|
||||
),
|
||||
'to throw',
|
||||
'Popup is required',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#destroy', () => {
|
||||
let state: State;
|
||||
let popup: PopupConfig;
|
||||
|
||||
beforeEach(() => {
|
||||
state = reducer(state, create({ Popup: FakeComponent }));
|
||||
[popup] = state.popups;
|
||||
});
|
||||
|
||||
it('should remove popup', () => {
|
||||
expect(state.popups, 'to have length', 1);
|
||||
|
||||
state = reducer(state, destroy(popup));
|
||||
|
||||
expect(state.popups, 'to have length', 0);
|
||||
});
|
||||
|
||||
it('should not remove something, that it should not', () => {
|
||||
state = reducer(
|
||||
state,
|
||||
create({
|
||||
Popup: FakeComponent,
|
||||
}),
|
||||
);
|
||||
|
||||
state = reducer(state, destroy(popup));
|
||||
|
||||
expect(state.popups, 'to have length', 1);
|
||||
expect(state.popups[0], 'not to be', popup);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -4,31 +4,31 @@ import { combineReducers } from 'redux';
|
||||
import { Action } from './actions';
|
||||
|
||||
export interface PopupConfig {
|
||||
Popup: React.ElementType;
|
||||
props?: Record<string, any>;
|
||||
// Don't allow hiding popup
|
||||
disableOverlayClose?: boolean;
|
||||
Popup: React.ElementType;
|
||||
props?: Record<string, any>;
|
||||
// Don't allow hiding popup
|
||||
disableOverlayClose?: boolean;
|
||||
}
|
||||
|
||||
export type State = {
|
||||
popups: Array<PopupConfig>;
|
||||
popups: Array<PopupConfig>;
|
||||
};
|
||||
|
||||
export default combineReducers<State>({
|
||||
popups,
|
||||
popups,
|
||||
});
|
||||
|
||||
function popups(state: Array<PopupConfig> = [], { type, payload }: Action) {
|
||||
switch (type) {
|
||||
case 'POPUP_CREATE':
|
||||
if (!payload.Popup) {
|
||||
throw new Error('Popup is required');
|
||||
}
|
||||
switch (type) {
|
||||
case 'POPUP_CREATE':
|
||||
if (!payload.Popup) {
|
||||
throw new Error('Popup is required');
|
||||
}
|
||||
|
||||
return state.concat(payload);
|
||||
case 'POPUP_DESTROY':
|
||||
return state.filter((popup) => popup !== payload);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
return state.concat(payload);
|
||||
case 'POPUP_DESTROY':
|
||||
return state.filter((popup) => popup !== payload);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user