Skip to content

Commit

Permalink
feat(confirmation): design update, new type of error "overlimit" and …
Browse files Browse the repository at this point in the history
…blocking input (#841)

* style(confirmation): lint fix

* feat(confirmation): new type of error "overlimit" and blocking input

* style(confirmation): lint fix

* feat(confirmation): add overlimit fatar error type
  • Loading branch information
GeKiStolyarov committed Sep 27, 2021
1 parent e2cf965 commit 1ea5d31
Show file tree
Hide file tree
Showing 11 changed files with 489 additions and 17 deletions.
1 change: 1 addition & 0 deletions packages/confirmation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@alfalab/core-components-loader": "^2.0.1",
"@alfalab/hooks": "^1.0.0",
"@alfalab/utils": "^1.3.0",
"@alfalab/icons-glyph": "^1.150.0",
"classnames": "^2.2.6"
},
"peerDependencies": {
Expand Down
89 changes: 87 additions & 2 deletions packages/confirmation/src/Component.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { render, waitFor } from '@testing-library/react';
import { render, waitFor, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Confirmation } from './index';

Expand Down Expand Up @@ -77,11 +77,19 @@ describe('Confirmation', () => {
expect(container).toMatchSnapshot();
});

it('should math snapshot without smsCountdown', () => {
it('should match snapshot without smsCountdown', () => {
const { container } = render(<Confirmation {...baseProps} hasSmsCountdown={false} />);

expect(container).toMatchSnapshot();
});

it('should match snapshot with overlimit fatal error', () => {
const { container } = render(
<Confirmation {...baseProps} errorOverlimit={true} errorOverlimitIsFatal={true} />,
);

expect(container).toMatchSnapshot();
});
});

it('should set `data-test-id` attribute', () => {
Expand Down Expand Up @@ -433,4 +441,81 @@ describe('Confirmation', () => {
expect(onInputChange).toBeCalledTimes(2);
});
});

describe('Overlimit tests', () => {
it('should display custom passed overlimitTitle and overlimitText', () => {
const customOverlimitTitle = 'Some test title';
const customOverlimitText = 'Some overlimit text';
render(
<Confirmation
{...baseProps}
overlimitTitle={customOverlimitTitle}
overlimitText={customOverlimitText}
errorOverlimit={true}
/>,
);

expect(screen.getByText(customOverlimitTitle)).toBeInTheDocument();
expect(screen.getByText(customOverlimitText)).toBeInTheDocument();
});

it('should display button on overlimit countdown finished', async () => {
const buttonRetryText = 'test retry button';
const twoMinutesMs = 120 * 1000;
const realDate = Date.now();
render(
<Confirmation
{...baseProps}
overlimitCountdownDuration={30000}
errorOverlimit={true}
buttonRetryText={buttonRetryText}
/>,
);

expect(screen.queryByText(buttonRetryText)).toEqual(null);

const spyDateNow = jest
.spyOn(global.Date, 'now')
.mockImplementation(() => realDate + twoMinutesMs);

await screen.findByText(buttonRetryText);
expect(screen.getByText(buttonRetryText)).toBeInTheDocument();

spyDateNow.mockClear();
});

it('should call onOverlimitCountdownFinished and onOverlimitSmsRetryClick', async () => {
const buttonRetryText = 'test retry button';
const twoMinutesMs = 120 * 1000;
const realDate = Date.now();
const onOverlimitCountdownFinished = jest.fn();
const onOverlimitSmsRetryClick = jest.fn();
render(
<Confirmation
{...baseProps}
errorOverlimit={true}
buttonRetryText={buttonRetryText}
onOverlimitCountdownFinished={onOverlimitCountdownFinished}
onOverlimitSmsRetryClick={onOverlimitSmsRetryClick}
/>,
);

expect(screen.queryByText(buttonRetryText)).toEqual(null);
expect(onOverlimitCountdownFinished).toBeCalledTimes(0);

const spyDateNow = jest
.spyOn(global.Date, 'now')
.mockImplementation(() => realDate + twoMinutesMs);

await screen.findByText(buttonRetryText);
expect(screen.getByText(buttonRetryText)).toBeInTheDocument();
expect(onOverlimitCountdownFinished).toBeCalledTimes(1);

expect(onOverlimitSmsRetryClick).toBeCalledTimes(0);
userEvent.click(screen.getByText(buttonRetryText));
expect(onOverlimitSmsRetryClick).toBeCalledTimes(1);

spyDateNow.mockClear();
});
});
});
73 changes: 72 additions & 1 deletion packages/confirmation/src/__snapshots__/Component.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,77 @@ exports[`Confirmation Snapshot tests should match snapshot with nonFatal error 1
</div>
`;

exports[`Confirmation Snapshot tests should match snapshot with overlimit fatal error 1`] = `
<div>
<div
class="component left"
>
<div
class="component"
>
<div
class="alertIcon"
>
<svg
data-test-id="alert-icon"
fill="currentColor"
focusable="false"
height="64"
role="img"
viewBox="0 0 24 24"
width="64"
>
<path
clip-rule="evenodd"
d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zm-4-7.5l2.586-2.5L8 9.5 9.5 8l2.5 2.586L14.5 8 16 9.5 13.414 12 16 14.5 14.5 16 12 13.414 9.5 16 8 14.5z"
fill-rule="evenodd"
/>
</svg>
</div>
<span
class="title"
>
Превышено количество
попыток ввода кода
</span>
<div
class="description"
>
<div>
Повторное подтверждение кодом из SMS
будет возможно через
</div>
<div
class="countdown"
>
<svg
class="loader"
height="16"
viewBox="0 0 16 16"
width="16"
>
<circle
class="circle"
cx="8"
cy="8"
r="7"
stroke-dasharray="43.98"
stroke-dashoffset="0.00"
transform="rotate(-90 8 8)"
/>
</svg>
<div
class="timePassed"
>
01:00
</div>
</div>
</div>
</div>
</div>
</div>
`;

exports[`Confirmation Snapshot tests should match snapshot with signTitle as react node 1`] = `
<div>
<div
Expand Down Expand Up @@ -747,7 +818,7 @@ exports[`Confirmation Snapshot tests should match snapshot with unmasked phone 1
</div>
`;

exports[`Confirmation Snapshot tests should math snapshot without smsCountdown 1`] = `
exports[`Confirmation Snapshot tests should match snapshot without smsCountdown 1`] = `
<div>
<div
class="component left"
Expand Down
79 changes: 74 additions & 5 deletions packages/confirmation/src/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import cn from 'classnames';
import { Button } from '@alfalab/core-components-button';
import { Link } from '@alfalab/core-components-link';

import { SignConfirmation } from './components';
import { SignConfirmation, Overlimit } from './components';

import styles from './index.module.css';

Expand All @@ -30,6 +30,16 @@ export type ConfirmationProps = {
*/
error?: boolean;

/**
* Состояние ошибки лимитов - превышено кол-во попыток ввода или запросов кода
*/
errorOverlimit?: boolean;

/**
* Состояние критической ошибки лимитов - превышены все лимиты и попытки, пользователя блокируют
*/
errorOverlimitIsFatal?: boolean;

/**
* Текст ошибки подписания
*/
Expand Down Expand Up @@ -82,6 +92,21 @@ export type ConfirmationProps = {
*/
signTitle?: string | React.ReactNode;

/**
* Заголовок экрана ошибки лимитов
*/
overlimitTitle?: string;

/**
* Текстовое описание блокировки формы при превышении лимитов
*/
overlimitText?: string;

/**
* Длительно блокировки при превышении лимитов, в милисекундах
*/
overlimitCountdownDuration?: number;

/**
* Заголовок экрана блокирующей ошибки
*/
Expand Down Expand Up @@ -148,11 +173,21 @@ export type ConfirmationProps = {
*/
onSmsRetryClick: () => void;

/**
* Обработчик события нажатия на кнопку "Запросить код" в блоке превышение лимитов
*/
onOverlimitSmsRetryClick?: () => void;

/**
* Обработчик события завершения обратного отсчета для повторного запроса сообщения
*/
onCountdownFinished?: () => void;

/**
* Обработчик события завершения обратного отсчета для блокировки формы
*/
onOverlimitCountdownFinished?: () => void;

/**
* Обработчик события нажатия на ссылку "не приходит сообщение?"
*/
Expand All @@ -175,12 +210,17 @@ export const Confirmation = forwardRef<HTMLDivElement, ConfirmationProps>(
errorIsFatal,
errorTitle = 'Превышено количество попыток ввода кода',
error = false,
errorOverlimit = false,
errorOverlimitIsFatal = false,
errorText,
hasPhoneMask = true,
hasSmsCountdown = true,
phone,
requiredCharAmount = 5,
signTitle = 'Введите код из\xa0сообщения',
overlimitTitle = 'Превышено количество\n попыток ввода кода',
overlimitText = 'Повторное подтверждение кодом из SMS\n будет возможно через',
overlimitCountdownDuration,
code,
codeSending = false,
codeChecking = false,
Expand All @@ -194,20 +234,25 @@ export const Confirmation = forwardRef<HTMLDivElement, ConfirmationProps>(
countdownContent,
onInputFinished,
onSmsRetryClick,
onOverlimitSmsRetryClick,
onActionWithFatalError,
onCountdownFinished,
onOverlimitCountdownFinished,
onInputChange,
onSmsHintLinkClick,
},
ref,
) => {
const [showHint, setShowHint] = useState(false);

const shouldShowError = errorIsFatal && Boolean(errorText);
const shouldShowFatalError = errorIsFatal && Boolean(errorText);

const shouldShowSignComponent = !showHint && !shouldShowError;
const shouldShowOverlimitError = !errorIsFatal && !showHint && errorOverlimit;

const shouldShowHint = showHint && !shouldShowError;
const shouldShowSignComponent =
!showHint && !shouldShowFatalError && !shouldShowOverlimitError;

const shouldShowHint = showHint && !shouldShowFatalError && !shouldShowOverlimitError;

const nonFatalError = errorIsFatal ? '' : errorText;

Expand All @@ -217,6 +262,12 @@ export const Confirmation = forwardRef<HTMLDivElement, ConfirmationProps>(
onSmsRetryClick();
}, [onSmsRetryClick]);

const handleOverlimitSmsRetryClick = useCallback(() => {
if (onOverlimitSmsRetryClick) {
onOverlimitSmsRetryClick();
}
}, [onOverlimitSmsRetryClick]);

const handleSmsRetryFromHintClick = useCallback(() => {
setShowHint(false);

Expand All @@ -231,6 +282,12 @@ export const Confirmation = forwardRef<HTMLDivElement, ConfirmationProps>(
}
}, [onCountdownFinished]);

const handleOverlimitCountdownFinished = useCallback(() => {
if (onOverlimitCountdownFinished) {
onOverlimitCountdownFinished();
}
}, [onOverlimitCountdownFinished]);

const handleSmsHintLinkClick = useCallback(() => {
setShowHint(true);

Expand Down Expand Up @@ -289,7 +346,19 @@ export const Confirmation = forwardRef<HTMLDivElement, ConfirmationProps>(
/>
)}

{shouldShowError && (
{shouldShowOverlimitError && (
<Overlimit
duration={overlimitCountdownDuration}
title={overlimitTitle}
text={overlimitText}
hasFatalError={errorOverlimitIsFatal}
buttonRetryText={buttonRetryText}
onOverlimitRepeatSms={handleOverlimitSmsRetryClick}
onOverlimitCountdownFinished={handleOverlimitCountdownFinished}
/>
)}

{shouldShowFatalError && (
<div className={styles.error}>
<span className={styles.errorHeader}>{errorTitle}</span>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
@import '../../vars.css';
@import '../../../../vars/src/index.css' .component {
@import '../../../../vars/src/index.css';

.component {
display: flex;
}

Expand Down
1 change: 1 addition & 0 deletions packages/confirmation/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './countdown';
export * from './sign-confirmation';
export * from './countdown-loader';
export * from './overlimit';

0 comments on commit 1ea5d31

Please sign in to comment.