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