Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Farabi/bot 399/hide bot too risky modal #9542

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@
}
}
&__animation {
margin-right: 0.5rem;
max-width: 35rem;
width: 35rem;
}
&__dialog {
&-text--second {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from 'react';
import { mockStore, StoreProvider } from '@deriv/stores';
// eslint-disable-next-line import/no-extraneous-dependencies
import { act, fireEvent, render, screen } from '@testing-library/react';
// eslint-disable-next-line import/no-extraneous-dependencies
import userEvent from '@testing-library/user-event';
import RootStore from 'Stores/index';
import { DBotStoreProvider, mockDBotStore } from 'Stores/useDBotStore';
import BotStopNotification from './bot-stop-notification';

jest.mock('@deriv/bot-skeleton/src/scratch/blockly', () => jest.fn());
jest.mock('@deriv/bot-skeleton/src/scratch/dbot', () => ({
saveRecentWorkspace: jest.fn(),
unHighlightAllBlocks: jest.fn(),
}));
jest.mock('@deriv/bot-skeleton/src/scratch/hooks/block_svg', () => jest.fn());

const mock_ws = {
authorized: {
subscribeProposalOpenContract: jest.fn(),
send: jest.fn(),
},
storage: {
send: jest.fn(),
},
contractUpdate: jest.fn(),
subscribeTicksHistory: jest.fn(),
forgetStream: jest.fn(),
activeSymbols: jest.fn(),
send: jest.fn(),
};
describe('BotStopNotification', () => {
farabi-deriv marked this conversation as resolved.
Show resolved Hide resolved
let wrapper: ({ children }: { children: JSX.Element }) => JSX.Element, mock_DBot_store: RootStore | undefined;

beforeEach(() => {
jest.resetModules();
const mock_store = mockStore({});
mock_DBot_store = mockDBotStore(mock_store, mock_ws);

wrapper = ({ children }: { children: JSX.Element }) => (
<StoreProvider store={mock_store}>
<DBotStoreProvider ws={mock_ws} mock={mock_DBot_store}>
{children}
</DBotStoreProvider>
</StoreProvider>
);
});

it('should clear the notification timer and hide message after timer expires', () => {
act(() => {
mock_DBot_store?.run_panel.setShowBotStopMessage(false);
});
jest.useFakeTimers();

render(<BotStopNotification />, {
wrapper,
});

// Advance timers to trigger notificationTimer
jest.advanceTimersByTime(6000);

// Expect that setShowBotStopMessage(false) was called
expect(screen.queryByText('You’ve just stopped the bot.')).not.toBeInTheDocument();
});

it('should render the toast component', () => {
const { container } = render(<BotStopNotification />, {
wrapper,
});
expect(container).toBeInTheDocument();
farabi-deriv marked this conversation as resolved.
Show resolved Hide resolved
});

it('should render to remove the toast component when clicking on close icon', () => {
act(() => {
mock_DBot_store?.run_panel.setShowBotStopMessage(false);
});

render(<BotStopNotification />, {
wrapper,
});
userEvent.click(screen.getByTestId('notification-close'));
expect(screen.queryByText('You’ve just stopped the bot.')).not.toBeInTheDocument();
});

it('should render toast', () => {
act(() => {
mock_DBot_store?.run_panel.setShowBotStopMessage(true);
});

render(<BotStopNotification />, {
wrapper,
});
fireEvent.mouseOver(screen.getByTestId('bot-stop-notification'));
jest.advanceTimersByTime(6000);
expect(screen.queryByText('You’ve just stopped the bot.')).not.toBeInTheDocument();

fireEvent.mouseLeave(screen.getByTestId('bot-stop-notification'));
jest.advanceTimersByTime(4000);
expect(screen.queryByText('You’ve just stopped the bot.')).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { useRef } from 'react';
import { observer } from 'mobx-react';
import { Icon, Toast } from '@deriv/components';
import { Localize } from '@deriv/translations';
import { useDBotStore } from 'Stores/useDBotStore';

const BotStopNotification = observer(() => {
const { run_panel } = useDBotStore();
const { setShowBotStopMessage } = run_panel;
const notification_timer = useRef(6000);
farabi-deriv marked this conversation as resolved.
Show resolved Hide resolved

const notificationTimer = setTimeout(() => {
if (notification_timer.current) {
setShowBotStopMessage(false);
}
}, notification_timer.current);

const resetTimer = () => {
setTimeout(() => {
setShowBotStopMessage(false);
}, 4000);
};

return (
<div
className='bot-stop-notification'
onMouseOver={e => {
clearTimeout(notificationTimer);
}}
onMouseLeave={e => {
resetTimer();
}}
data-testid='bot-stop-notification'
>
<Toast>
<div>
<Localize
i18n_default_text='You’ve just stopped the bot. Any open contracts can be viewed on the <0>Reports</0> page.'
components={[
<a
key={0}
style={{ color: 'var(--general-main-1)' }}
rel='noopener noreferrer'
target='_blank'
href={'/reports'}
/>,
]}
/>
</div>
<Icon
icon='IcCross'
className={'notification-close'}
data_testid={'notification-close'}
onClick={() => setShowBotStopMessage(false)}
/>
</Toast>
</div>
);
});

export default BotStopNotification;
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TradeAnimation from 'Components/trade-animation';

const RunStrategy = () => (
<div className='toolbar__section'>
<TradeAnimation should_show_overlay info_direction='left' />
<TradeAnimation className='toolbar__animation' should_show_overlay info_direction='left' />
</div>
);

Expand Down
14 changes: 11 additions & 3 deletions packages/bot-web-ui/src/components/dashboard/dashboard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -675,10 +675,18 @@
position: fixed;
z-index: 1;
left: 0;
right: 0;
bottom: 7rem;

.dc-toast__message {
background: var(--text-general);
color: var(--general-main-1);
.dc-toast {
width: 100%;
&__message {
background: var(--text-prominent);
color: var(--general-main-1);
}
}

@include mobile {
bottom: 10rem;
}
}
4 changes: 2 additions & 2 deletions packages/bot-web-ui/src/components/dashboard/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { DBOT_TABS, TAB_IDS } from 'Constants/bot-contents';
import { useDBotStore } from 'Stores/useDBotStore';
import RunPanel from '../run-panel';
import RunStrategy from './dashboard-component/run-strategy';
import BotNotification from './bot-notification';
import DashboardComponent from './dashboard-component';
import {
DBOT_ONBOARDING,
Expand All @@ -21,6 +20,7 @@ import {
tour_type,
} from './joyride-config';
import ReactJoyrideWrapper from './react-joyride-wrapper';
import StrategyNotification from './strategy-notification';
import TourSlider from './tour-slider';
import TourTriggrerDialog from './tour-trigger-dialog';
import Tutorial from './tutorial-tab';
Expand Down Expand Up @@ -264,7 +264,7 @@ const Dashboard = observer(() => {
>
{dialog_options.message}
</Dialog>
<BotNotification />
<StrategyNotification />
</React.Fragment>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { observer } from '@deriv/stores';
import { localize } from '@deriv/translations';
import { useDBotStore } from 'Stores/useDBotStore';

const BotNotification = observer(() => {
const StrategyNotification = observer(() => {
const { dashboard } = useDBotStore();
const { show_toast, toast_message, setOpenSettings } = dashboard;

Expand Down Expand Up @@ -33,4 +33,4 @@ const BotNotification = observer(() => {
return null;
});

export default BotNotification;
export default StrategyNotification;
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@
height: 6rem;
display: flex;
width: inherit;
padding-right: 8px;
}
&__stop-button,
&__run-button {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { Button, Icon, Modal, Text } from '@deriv/components';
import { isMobile } from '@deriv/shared';
import { Button, Icon } from '@deriv/components';
import { observer, useStore } from '@deriv/stores';
import { Localize, localize } from '@deriv/translations';
import ContractResultOverlay from 'Components/contract-result-overlay';
import BotStopNotification from 'Components/dashboard/bot-stop-notification';
import { contract_stages } from 'Constants/contract-stage';
import { useDBotStore } from 'Stores/useDBotStore';

Expand All @@ -16,44 +16,6 @@ const CircularWrapper = ({ className }) => (
</div>
);

const AnimationInfo = ({ toggleAnimationInfoModal }) => {
return (
<div className='animation__info' onClick={toggleAnimationInfoModal}>
<Icon icon='IcInfoOutline' id='db-animation__clear-stat' />
</div>
);
};

const AnimationInfoModal = ({ is_mobile, is_animation_info_modal_open, toggleAnimationInfoModal }) => {
return (
<Modal
className={classNames('animation__modal', { 'animation__modal--mobile': is_mobile })}
title={localize('Stopping the bot is risky')}
is_open={is_animation_info_modal_open}
toggleModal={toggleAnimationInfoModal}
width={'440px'}
>
<Modal.Body>
<div className={classNames({ 'animation__modal-body--mobile': is_mobile })}>
<Text as='p'>
{localize(
'Stopping the bot will prevent further trades. Any ongoing trades will be completed by our system.'
)}
</Text>
<Text as='p' className='animation__modal-body--content'>
{localize(
'Please be aware that some completed transactions may not be displayed in the transaction table if the bot is stopped while placing trades.'
)}
</Text>
<Text as='p' className='animation__modal-body--content'>
{localize('You may refer to the statement page for details of all completed transactions.')}
</Text>
</div>
</Modal.Body>
</Modal>
);
};

const ContractStageText = ({ contract_stage }) => {
switch (contract_stage) {
case contract_stages.NOT_RUNNING:
Expand All @@ -72,8 +34,8 @@ const ContractStageText = ({ contract_stage }) => {
}
};

const TradeAnimation = observer(({ className, info_direction }) => {
const { run_panel, toolbar, summary_card } = useDBotStore();
const TradeAnimation = observer(({ className }) => {
const { run_panel, summary_card } = useDBotStore();
const { client } = useStore();
const { is_contract_completed, profit } = summary_card;
const {
Expand All @@ -84,13 +46,13 @@ const TradeAnimation = observer(({ className, info_direction }) => {
onStopButtonClick,
performSelfExclusionCheck,
should_show_overlay,
show_bot_stop_message,
} = run_panel;
const { is_animation_info_modal_open, toggleAnimationInfoModal } = toolbar;
const { account_status } = client;
const cashier_validation = account_status?.cashier_validation;

const [is_button_disabled, updateIsButtonDisabled] = React.useState(false);
const is_unavailable_for_payment_agent = cashier_validation?.includes('WithdrawServiceUnavailableForPA');

// perform self-exclusion checks which will be stored under the self-exclusion-store
React.useEffect(() => {
performSelfExclusionCheck();
Expand Down Expand Up @@ -127,13 +89,12 @@ const TradeAnimation = observer(({ className, info_direction }) => {

return (
<div className={classNames('animation__wrapper', className)}>
{info_direction === 'left' && <AnimationInfo toggleAnimationInfoModal={toggleAnimationInfoModal} />}
<Button
is_disabled={(is_stop_button_disabled || is_button_disabled) && !is_unavailable_for_payment_agent}
className='animation__button'
id={is_stop_button_visible ? 'db-animation__stop-button' : 'db-animation__run-button'}
text={is_stop_button_visible ? localize('Stop') : localize('Run')}
icon={<Icon icon={is_stop_button_visible ? 'IcPause' : 'IcPlay'} color='active' />}
icon={<Icon icon={is_stop_button_visible ? 'IcBotStop' : 'IcPlay'} color='active' />}
onClick={() => {
updateIsButtonDisabled(true);
if (is_stop_button_visible) {
Expand All @@ -145,6 +106,7 @@ const TradeAnimation = observer(({ className, info_direction }) => {
has_effect
{...(is_stop_button_visible || !is_unavailable_for_payment_agent ? { primary: true } : { green: true })}
/>
{show_bot_stop_message && <BotStopNotification />}
<div
className={classNames('animation__container', className, {
'animation--running': contract_stage > 0,
Expand All @@ -164,19 +126,12 @@ const TradeAnimation = observer(({ className, info_direction }) => {
))}
</div>
</div>
{info_direction === 'right' && <AnimationInfo toggleAnimationInfoModal={toggleAnimationInfoModal} />}
<AnimationInfoModal
is_mobile={isMobile()}
is_animation_info_modal_open={is_animation_info_modal_open}
toggleAnimationInfoModal={toggleAnimationInfoModal}
/>
</div>
);
});

TradeAnimation.propTypes = {
className: PropTypes.string,
info_direction: PropTypes.string,
};

export default TradeAnimation;
Loading