Change prettier rules

This commit is contained in:
ErickSkrauch
2020-05-24 02:08:24 +03:00
parent 73f0c37a6a
commit f85b9d8d35
382 changed files with 24137 additions and 26046 deletions

View File

@@ -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');
});
});

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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);
});
});
});

View File

@@ -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;
}
}