Skip to content

Commit

Permalink
Farabi/bot 399/hide bot too risky modal (#9542)
Browse files Browse the repository at this point in the history
* fix: removed bot too risky modal and updated stop icon

* fix: added toast notification when user stops bot

* fix: fixed snack bar for quick strategy run

* fix: added close function on snackbar

* fix: added clear and reset timeout on hovering on toast

* fix: added test case for bot-stop-notification

* fix: stop bot snack bar appearing with no trade again
  • Loading branch information
farabi-deriv committed Aug 29, 2023
1 parent 2e2699c commit 21e091a
Show file tree
Hide file tree
Showing 14 changed files with 235 additions and 103 deletions.

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', () => {
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();
});

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

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 @@ -647,10 +647,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 @@ -167,7 +167,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

1 comment on commit 21e091a

@vercel
Copy link

@vercel vercel bot commented on 21e091a Aug 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

deriv-app – ./

deriv-app.vercel.app
binary.sx
deriv-app.binary.sx
deriv-app-git-master.binary.sx

Please sign in to comment.