Skip to content

Commit

Permalink
DTRA-2153 / Kate / [DTrader-v2] Closed market fields (deriv-com#17254)
Browse files Browse the repository at this point in the history
* feat: add disabling functionality for all trade params

* feat: add current spot replacement

* fix: test

* refactor: logic of rendering and passing props in trade params

* refactor: how we apply disable style

* refactor: acc info tests

* refactor: update tests

* refactor: remove props drilling

* chore: empty commit

* refactor: change array with dependencies

* fix: remove extra prop

* chore: extrade close martket from store

* revert: extra non-aligned file from master

* fix: dark theme and trade type tabs issue
  • Loading branch information
kate-deriv authored Nov 4, 2024
1 parent 5c3a038 commit 8d22a5a
Show file tree
Hide file tree
Showing 45 changed files with 450 additions and 251 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const getTradingTimes = async (target_time: TradingTimesRequest['trading_times']
const ClosedMarketMessage = observer(() => {
const { common } = useStore();
const { current_language } = common;
const { symbol, prepareTradeStore } = useTraderStore();
const { symbol, prepareTradeStore, is_market_closed } = useTraderStore();
const { activeSymbols } = useActiveSymbols();

const isMounted = useIsMounted();
Expand All @@ -39,7 +39,7 @@ const ClosedMarketMessage = observer(() => {
const [is_loading, setLoading] = React.useState(true);

React.useEffect(() => {
if (isMarketClosed(activeSymbols, symbol)) {
if (is_market_closed) {
setLoading(true);
const whenMarketOpens = async (
days_offset: number,
Expand Down Expand Up @@ -81,7 +81,7 @@ const ClosedMarketMessage = observer(() => {
setTimeLeft({});
setWhenMarketOpens({} as TWhenMarketOpens);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [symbol, activeSymbols]);
}, [activeSymbols, symbol]);

React.useEffect(() => {
let timer: ReturnType<typeof setTimeout>;
Expand All @@ -101,7 +101,7 @@ const ClosedMarketMessage = observer(() => {
};
}, [time_left, when_market_opens, prepareTradeStore]);

if (!(when_market_opens && Object.keys(time_left).length)) return null;
if (!(when_market_opens && Object.keys(time_left).length) || !is_market_closed) return null;

const { opening_time, days_offset } = when_market_opens;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,62 @@ jest.mock('AppV2/Hooks/useActiveSymbols', () => ({
jest.mock('AppV2/Components/ActiveSymbolsList', () => jest.fn(() => 'MockedActiveSymbolsList'));

describe('MarketSelector', () => {
const mock_store = {
modules: {
trade: {
symbol: 'EURUSD',
tick_data: {
quote: 1234.23,
pip_size: 2,
let default_trade_store: TCoreStores;

beforeEach(() => {
default_trade_store = mockStore({
modules: {
trade: {
symbol: 'EURUSD',
tick_data: {
quote: 1234.23,
pip_size: 2,
},
},
},
},
};
const MockedMarketSelector = (mocked_store: TCoreStores) => {
});
});

const MockedMarketSelector = () => {
return (
<TraderProviders store={mocked_store}>
<TraderProviders store={default_trade_store}>
<MarketSelector />
</TraderProviders>
);
};

it('should render storeSymbol when storeSymbol is set', () => {
render(MockedMarketSelector(mockStore(mock_store)));
it('renders storeSymbol when storeSymbol is set', () => {
render(MockedMarketSelector());

expect(screen.getByText('EUR/USD')).toBeInTheDocument();
expect(screen.getByText(mock_store.modules.trade.tick_data.quote)).toBeInTheDocument();
expect(screen.getByText(default_trade_store.modules.trade.tick_data.quote)).toBeInTheDocument();
});
it('should render CLOSED when current symbol exchange_is_open is 0', () => {
mock_store.modules.trade.symbol = 'GBPUSD';
render(MockedMarketSelector(mockStore(mock_store)));
it('renders CLOSED when current symbol exchange_is_open is 0', () => {
default_trade_store.modules.trade.symbol = 'GBPUSD';
render(MockedMarketSelector());

expect(screen.getByText('GBP/USD')).toBeInTheDocument();
expect(screen.getByText(mock_store.modules.trade.tick_data.quote)).toBeInTheDocument();
expect(screen.getByText(default_trade_store.modules.trade.tick_data.quote)).toBeInTheDocument();
expect(screen.getByText('CLOSED')).toBeInTheDocument();
});
it('should render loader when current symbol exchange_is_open is not defined (is not among active symbols list)', () => {
mock_store.modules.trade.symbol = 'USDJPY';
render(MockedMarketSelector(mockStore(mock_store)));
it('renders loader when current symbol exchange_is_open is not defined (is not among active symbols list)', () => {
default_trade_store.modules.trade.symbol = 'USDJPY';
render(MockedMarketSelector());

expect(screen.getByTestId('square-skeleton')).toBeInTheDocument();
});
it('renders loader instead of current spot when tick_data is empty', () => {
default_trade_store.modules.trade.tick_data = {};
render(MockedMarketSelector());

expect(screen.getByTestId('square-skeleton')).toBeInTheDocument();
});
it('renders dash instead of current spot when market is closed and tick_data is empty', () => {
default_trade_store.modules.trade.symbol = 'GBPUSD';
default_trade_store.modules.trade.is_market_closed = true;
default_trade_store.modules.trade.tick_data = {};
render(MockedMarketSelector());

expect(screen.getByText('-')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ import { useTraderStore } from 'Stores/useTraderStores';
const MarketSelector = observer(() => {
const [isOpen, setIsOpen] = useState(false);
const { activeSymbols } = useActiveSymbols();
const { symbol: storeSymbol, tick_data } = useTraderStore();
const currentSymbol = activeSymbols.find(({ symbol }) => symbol === storeSymbol);
const { symbol: storeSymbol, tick_data, is_market_closed } = useTraderStore();

const currentSymbol = activeSymbols.find(({ symbol }) => symbol === storeSymbol);
const { pip_size, quote } = tick_data ?? {};
const current_spot = quote?.toFixed(pip_size);
const current_spot_replacement = is_market_closed ? (
<Text>-</Text>
) : (
<Skeleton.Square height={18} width={64} rounded />
);

// For closed markets exchange_is_open === 0
if (typeof currentSymbol?.exchange_is_open === 'undefined')
return <Skeleton.Square height={42} width={240} rounded />;
Expand All @@ -42,7 +48,7 @@ const MarketSelector = observer(() => {
{current_spot ? (
<CaptionText className='market-selector-info__price'>{current_spot}</CaptionText>
) : (
<Skeleton.Square height={18} width={64} rounded />
current_spot_replacement
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import AccumulatorsInformation from '../accumulators-information';
import ModulesProvider from 'Stores/Providers/modules-providers';
import TraderProviders from '../../../../../trader-providers';

const payout_text = 'Max. payout';
const payout_value = '4,000.00 USD';

describe('AccumulatorsInformation', () => {
let default_mock_store: ReturnType<typeof mockStore>;

Expand All @@ -16,6 +19,7 @@ describe('AccumulatorsInformation', () => {
...mockStore({}),
currency: 'USD',
maximum_payout: 4000,
is_market_closed: false,
},
},
}))
Expand All @@ -30,7 +34,7 @@ describe('AccumulatorsInformation', () => {
</TraderProviders>
);

it('should not render if there is an API error ', () => {
it('does not render if there is an API error ', () => {
default_mock_store.modules.trade.proposal_info = {
ACCU: {
has_error: true,
Expand All @@ -41,19 +45,27 @@ describe('AccumulatorsInformation', () => {
expect(container).toBeEmptyDOMElement();
});

it('should render loader if maximum_payout is falsy but there is no API error', () => {
it('renders loader if maximum_payout is falsy but there is no API error', () => {
default_mock_store.modules.trade.maximum_payout = 0;
mockAccumulatorsInformation();

expect(screen.getByText('Max. payout')).toBeInTheDocument();
expect(screen.getByText(payout_text)).toBeInTheDocument();
expect(screen.getByTestId('dt_skeleton')).toBeInTheDocument();
expect(screen.queryByText('4,000.00 USD')).not.toBeInTheDocument();
expect(screen.queryByText(payout_value)).not.toBeInTheDocument();
});

it('renders description that is provided', () => {
mockAccumulatorsInformation();

expect(screen.getByText(payout_text)).toBeInTheDocument();
expect(screen.getByText(payout_value)).toBeInTheDocument();
expect(screen.getByText(payout_text)).not.toHaveClass('trade-params__text--disabled');
});

it('should render description that is provided', () => {
it('applies specific className if is_market_closed === true', () => {
default_mock_store.modules.trade.is_market_closed = true;
mockAccumulatorsInformation();

expect(screen.getByText('Max. payout')).toBeInTheDocument();
expect(screen.getByText('4,000.00 USD')).toBeInTheDocument();
expect(screen.getByText(payout_text)).toHaveClass('trade-params__text--disabled');
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import clsx from 'clsx';
import { observer } from 'mobx-react';
import { Localize } from '@deriv/translations';
import { Money, Skeleton } from '@deriv/components';
Expand All @@ -7,18 +8,18 @@ import { useTraderStore } from 'Stores/useTraderStores';
import { CONTRACT_TYPES } from '@deriv/shared';

const AccumulatorsInformation = observer(() => {
const { currency, maximum_payout, proposal_info } = useTraderStore();
const { currency, is_market_closed, maximum_payout, proposal_info } = useTraderStore();
const has_error = proposal_info[CONTRACT_TYPES.ACCUMULATOR]?.has_error;

if (has_error) return null;

return (
<div className='accumulators-info__wrapper'>
<Text size='sm'>
<Text size='sm' className={clsx(is_market_closed && 'trade-params__text--disabled')}>
<Localize i18n_default_text='Max. payout' />
</Text>
{maximum_payout ? (
<Text size='sm' bold>
<Text size='sm' bold className={clsx(is_market_closed && 'trade-params__text--disabled')}>
<Money amount={maximum_payout} show_currency currency={currency} />
</Text>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ const title = 'Allow equals';
describe('AllowEquals', () => {
let default_mock_store: ReturnType<typeof mockStore>;

beforeEach(() => {
default_mock_store = mockStore({});
});
beforeEach(() => (default_mock_store = mockStore({})));

const mockAllowEquals = () => {
return (
Expand All @@ -32,49 +30,56 @@ describe('AllowEquals', () => {
);
};

it('should not render component if hasCallPutEqual return false', () => {
it('does not render component if hasCallPutEqual return false', () => {
(hasCallPutEqual as jest.Mock).mockReturnValueOnce(false);
const { container } = render(mockAllowEquals());

expect(container).toBeEmptyDOMElement();
});

it('should not render component if hasDurationForCallPutEqual return false', () => {
it('does not render component if hasDurationForCallPutEqual return false', () => {
(hasDurationForCallPutEqual as jest.Mock).mockReturnValueOnce(false);
const { container } = render(mockAllowEquals());

expect(container).toBeEmptyDOMElement();
});

it('should render component with correct ToggleSwitch state value if is_equal is 0', () => {
it('renders component with ToggleSwitch state value aria-pressed === false if is_equal is 0', () => {
render(mockAllowEquals());

expect(screen.getByText(title)).toBeInTheDocument();
expect(screen.getByRole('button')).toHaveAttribute('aria-pressed', 'false');
});

it('should render component with correct ToggleSwitch state value if is_equal is 1', () => {
it('renders component with ToggleSwitch state value aria-pressed === true if is_equal is 1', () => {
default_mock_store.modules.trade.is_equal = 1;
render(mockAllowEquals());

expect(screen.getByText(title)).toBeInTheDocument();
expect(screen.getByRole('button')).toHaveAttribute('aria-pressed', 'true');
});

it('should call onChange function if user clicks on ToggleSwitch', () => {
it('calls onChange function if user clicks on ToggleSwitch', () => {
render(mockAllowEquals());

userEvent.click(screen.getByRole('button'));

expect(default_mock_store.modules.trade.onChange).toBeCalled();
});

it('should render ActionSheet with definition if user clicks on "Allow equal" term', () => {
it('renders ActionSheet with definition if user clicks on "Allow equal" term', () => {
render(mockAllowEquals());

userEvent.click(screen.getByText(title));

expect(screen.getByText('Win payout if exit spot is also equal to entry spot.')).toBeInTheDocument();
expect(screen.getByText('Got it')).toBeInTheDocument();
});

it('renders disabled ToggleSwitch is is_market_closed === true', () => {
default_mock_store.modules.trade.is_market_closed = true;
render(mockAllowEquals());

expect(screen.getByRole('button')).toBeDisabled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

&__title {
border-bottom: var(--core-borderWidth-75) dotted var(--component-textIcon-normal-default);

&--disabled {
color: var(--component-textIcon-normal-disabled);
border-bottom: var(--core-borderWidth-75) dotted var(--component-textIcon-normal-disabled);
}
}

&__definition {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import clsx from 'clsx';
import { observer } from 'mobx-react';
import { ActionSheet, ToggleSwitch, Text, Heading } from '@deriv-com/quill-ui';
import { Localize } from '@deriv/translations';
Expand All @@ -7,8 +8,15 @@ import { hasCallPutEqual, hasDurationForCallPutEqual } from 'Stores/Modules/Trad
import { useTraderStore } from 'Stores/useTraderStores';

const AllowEquals = observer(() => {
const { contract_types_list, contract_start_type, duration_unit, expiry_type, is_equal, onChange } =
useTraderStore();
const {
contract_types_list,
contract_start_type,
duration_unit,
expiry_type,
is_equal,
is_market_closed,
onChange,
} = useTraderStore();

const [is_open, setIsOpen] = React.useState(false);

Expand All @@ -26,6 +34,7 @@ const AllowEquals = observer(() => {
};

const openDescription = (e?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => {
if (is_market_closed) return;
clickAndKeyEventHandler(() => setIsOpen(true), e);
};

Expand All @@ -36,10 +45,15 @@ const AllowEquals = observer(() => {
return (
<React.Fragment>
<div className='allow-equals__wrapper'>
<Text size='sm' className='allow-equals__title' onClick={openDescription} onKeyDown={openDescription}>
<Text
size='sm'
className={clsx('allow-equals__title', is_market_closed && 'allow-equals__title--disabled')}
onClick={openDescription}
onKeyDown={openDescription}
>
<Localize i18n_default_text='Allow equals' />
</Text>
<ToggleSwitch checked={!!is_equal} onChange={onToggleSwitch} />
<ToggleSwitch checked={!!is_equal} onChange={onToggleSwitch} disabled={is_market_closed} />
</div>
<ActionSheet.Root isOpen={is_open} onClose={closeDescription} position='left' expandable={false}>
<ActionSheet.Portal shouldCloseOnDrag>
Expand Down
Loading

0 comments on commit 8d22a5a

Please sign in to comment.