From 8d22a5a648ec006d67ba710a17099b556c9a69a4 Mon Sep 17 00:00:00 2001 From: kate-deriv <121025168+kate-deriv@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:55:56 +0300 Subject: [PATCH] DTRA-2153 / Kate / [DTrader-v2] Closed market fields (#17254) * 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 --- .../closed-market-message.tsx | 8 +-- .../__tests__/market-selector.spec.tsx | 61 ++++++++++++------- .../MarketSelector/market-selector.tsx | 12 +++- .../accumulators-information.spec.tsx | 26 +++++--- .../accumulators-information.tsx | 7 ++- .../__tests__/allow-equals.spec.tsx | 23 ++++--- .../AllowEquals/allow-equals.scss | 5 ++ .../AllowEquals/allow-equals.tsx | 22 +++++-- .../Barrier/__tests__/barrier.spec.tsx | 27 +++++--- .../TradeParameters/Barrier/barrier.tsx | 11 ++-- .../__tests__/barrier-info.spec.tsx | 21 +++++-- .../BarrierInfo/barrier-info.tsx | 7 ++- .../Duration/__tests__/duration.spec.tsx | 6 +- .../TradeParameters/Duration/duration.tsx | 14 ++--- .../GrowthRate/__tests__/growth-rate.spec.tsx | 22 ++++--- .../GrowthRate/growth-rate.tsx | 10 ++- .../__tests__/last-digit-prediction.spec.tsx | 14 +++-- .../LastDigitPrediction/digit.tsx | 4 +- .../last-digit-prediction.scss | 24 ++++++++ .../last-digit-prediction.tsx | 15 +++-- .../last-digit-selector.tsx | 10 ++- .../Multiplier/__tests__/multiplier.spec.tsx | 20 +++--- .../TradeParameters/Multiplier/multiplier.tsx | 12 ++-- ...ultipliers-deal-cancellation-info.spec.tsx | 17 ++++-- .../multipliers-deal-cancellation-info.tsx | 7 ++- .../multipliers-expiration-info.spec.tsx | 14 ++++- .../multipliers-expiration-info.tsx | 7 ++- .../PayoutInfo/__tests__/payout-info.spec.tsx | 17 ++++-- .../PayoutInfo/payout-info.tsx | 7 ++- .../__tests__/payout-per-point.spec.tsx | 21 ++++--- .../PayoutPerPoint/payout-per-point.tsx | 9 ++- .../__tests__/payout-per-point-info.spec.tsx | 17 ++++-- .../payout-per-point-info.tsx | 7 ++- .../__tests__/risk-management.spec.tsx | 36 ++++++----- .../RiskManagement/risk-management.tsx | 9 ++- .../Stake/__tests__/stake.spec.tsx | 35 ++++++----- .../TradeParameters/Stake/stake.tsx | 10 ++- .../Strike/__tests__/strike.spec.tsx | 19 ++++-- .../TradeParameters/Strike/strike.tsx | 9 ++- .../TakeProfit/__tests__/take-profit.spec.tsx | 11 +++- .../TakeProfit/take-profit.tsx | 11 ++-- .../TradeTypeTabs/trade-type-tabs.tsx | 12 ++-- .../__tests__/trade-parameters.spec.tsx | 37 ++++++----- .../TradeParameters/trade-parameters.scss | 4 ++ .../TradeParameters/trade-parameters.tsx | 4 +- 45 files changed, 450 insertions(+), 251 deletions(-) diff --git a/packages/trader/src/AppV2/Components/ClosedMarketMessage/closed-market-message.tsx b/packages/trader/src/AppV2/Components/ClosedMarketMessage/closed-market-message.tsx index f54ccda53e9f..69527cd7f04d 100644 --- a/packages/trader/src/AppV2/Components/ClosedMarketMessage/closed-market-message.tsx +++ b/packages/trader/src/AppV2/Components/ClosedMarketMessage/closed-market-message.tsx @@ -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(); @@ -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, @@ -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; @@ -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; diff --git a/packages/trader/src/AppV2/Components/MarketSelector/__tests__/market-selector.spec.tsx b/packages/trader/src/AppV2/Components/MarketSelector/__tests__/market-selector.spec.tsx index 57a5808ece4e..ca0165bf0893 100644 --- a/packages/trader/src/AppV2/Components/MarketSelector/__tests__/market-selector.spec.tsx +++ b/packages/trader/src/AppV2/Components/MarketSelector/__tests__/market-selector.spec.tsx @@ -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 ( - + ); }; - 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(); + }); }); diff --git a/packages/trader/src/AppV2/Components/MarketSelector/market-selector.tsx b/packages/trader/src/AppV2/Components/MarketSelector/market-selector.tsx index 9e6c60b00105..ad0c2fcccc77 100644 --- a/packages/trader/src/AppV2/Components/MarketSelector/market-selector.tsx +++ b/packages/trader/src/AppV2/Components/MarketSelector/market-selector.tsx @@ -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 ? ( + - + ) : ( + + ); + // For closed markets exchange_is_open === 0 if (typeof currentSymbol?.exchange_is_open === 'undefined') return ; @@ -42,7 +48,7 @@ const MarketSelector = observer(() => { {current_spot ? ( {current_spot} ) : ( - + current_spot_replacement )} diff --git a/packages/trader/src/AppV2/Components/TradeParameters/AccumulatorsInformation/__tests__/accumulators-information.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/AccumulatorsInformation/__tests__/accumulators-information.spec.tsx index acbaa5fafb7d..8d8b1e420c59 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/AccumulatorsInformation/__tests__/accumulators-information.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/AccumulatorsInformation/__tests__/accumulators-information.spec.tsx @@ -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; @@ -16,6 +19,7 @@ describe('AccumulatorsInformation', () => { ...mockStore({}), currency: 'USD', maximum_payout: 4000, + is_market_closed: false, }, }, })) @@ -30,7 +34,7 @@ describe('AccumulatorsInformation', () => { ); - 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, @@ -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'); }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/AccumulatorsInformation/accumulators-information.tsx b/packages/trader/src/AppV2/Components/TradeParameters/AccumulatorsInformation/accumulators-information.tsx index 50174dd18d71..a9b963765127 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/AccumulatorsInformation/accumulators-information.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/AccumulatorsInformation/accumulators-information.tsx @@ -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'; @@ -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 (
- + {maximum_payout ? ( - + ) : ( diff --git a/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/__tests__/allow-equals.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/__tests__/allow-equals.spec.tsx index 0404f2df9ed9..b18e730fa9fd 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/__tests__/allow-equals.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/__tests__/allow-equals.spec.tsx @@ -18,9 +18,7 @@ const title = 'Allow equals'; describe('AllowEquals', () => { let default_mock_store: ReturnType; - beforeEach(() => { - default_mock_store = mockStore({}); - }); + beforeEach(() => (default_mock_store = mockStore({}))); const mockAllowEquals = () => { return ( @@ -32,28 +30,28 @@ 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()); @@ -61,7 +59,7 @@ describe('AllowEquals', () => { 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')); @@ -69,7 +67,7 @@ describe('AllowEquals', () => { 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)); @@ -77,4 +75,11 @@ describe('AllowEquals', () => { 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(); + }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/allow-equals.scss b/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/allow-equals.scss index 4f2d004cd002..47aec1e553e6 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/allow-equals.scss +++ b/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/allow-equals.scss @@ -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 { diff --git a/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/allow-equals.tsx b/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/allow-equals.tsx index 3b740c3f684e..25dfb21edbdc 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/allow-equals.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/AllowEquals/allow-equals.tsx @@ -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'; @@ -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); @@ -26,6 +34,7 @@ const AllowEquals = observer(() => { }; const openDescription = (e?: React.MouseEvent | React.KeyboardEvent) => { + if (is_market_closed) return; clickAndKeyEventHandler(() => setIsOpen(true), e); }; @@ -36,10 +45,15 @@ const AllowEquals = observer(() => { return (
- + - +
diff --git a/packages/trader/src/AppV2/Components/TradeParameters/Barrier/__tests__/barrier.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/Barrier/__tests__/barrier.spec.tsx index 280d4e0018f2..348c0a0d7fa8 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/Barrier/__tests__/barrier.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/Barrier/__tests__/barrier.spec.tsx @@ -14,17 +14,18 @@ jest.mock('@deriv/quill-icons', () => ({ describe('Barrier Component', () => { let default_mock_store: ReturnType; - beforeEach(() => { - default_mock_store = mockStore({ - modules: { - trade: { - onChange: jest.fn(), - validation_errors: { barrier_1: [] }, - duration: 10, + beforeEach( + () => + (default_mock_store = mockStore({ + modules: { + trade: { + onChange: jest.fn(), + validation_errors: { barrier_1: [] }, + duration: 10, + }, }, - }, - }); - }); + })) + ); const mockBarriers = () => { render( @@ -44,6 +45,12 @@ describe('Barrier Component', () => { expect(screen.getByText('Barrier Input')).toBeInTheDocument(); }); + it('disables trade param if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockBarriers(); + expect(screen.getByRole('textbox')).toBeDisabled(); + }); + it('detects clicking outside the ActionSheet and closes it', async () => { mockBarriers(); userEvent.click(screen.getByRole('textbox')); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/Barrier/barrier.tsx b/packages/trader/src/AppV2/Components/TradeParameters/Barrier/barrier.tsx index 1922763cef8b..70f849dfaa8f 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/Barrier/barrier.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/Barrier/barrier.tsx @@ -8,16 +8,14 @@ import Carousel from 'AppV2/Components/Carousel'; import BarrierDescription from './barrier-description'; import BarrierInput from './barrier-input'; import CarouselHeader from 'AppV2/Components/Carousel/carousel-header'; +import { TTradeParametersProps } from '../trade-parameters'; -type TDurationProps = { - is_minimized?: boolean; -}; - -const Barrier = observer(({ is_minimized }: TDurationProps) => { +const Barrier = observer(({ is_minimized }: TTradeParametersProps) => { const { barrier_1, onChange, duration_unit, + is_market_closed, setV2ParamsInitialValues, v2_params_initial_values, validation_errors, @@ -91,13 +89,14 @@ const Barrier = observer(({ is_minimized }: TDurationProps) => { return ( <> } value={v2_params_initial_values.barrier_1 || barrier_1} onClick={() => setIsOpen(true)} - className={clsx('trade-params__option', is_minimized && 'trade-params__option--minimized')} status={has_error && !is_open ? 'error' : undefined} /> ', () => { let default_mock_store: ReturnType; @@ -28,7 +30,7 @@ describe('', () => { ); - it('should not render if there is API error ', () => { + it('does not render if there is an API error ', () => { default_mock_store.modules.trade.proposal_info = { [CONTRACT_TYPES.TURBOS.LONG]: { has_error: true, @@ -39,19 +41,28 @@ describe('', () => { expect(container).toBeEmptyDOMElement(); }); - it('should render loader if barrier_1 is falsy but there is no API error', () => { + it('renders loader if barrier_1 is falsy but there is no API error', () => { default_mock_store.modules.trade.barrier_1 = ''; mockedBarrierInfo(); - expect(screen.getByText('Barrier')).toBeInTheDocument(); + expect(screen.getByText(barrier_label)).toBeInTheDocument(); expect(screen.getByTestId('dt_skeleton')).toBeInTheDocument(); expect(screen.queryByText('1.2345')).not.toBeInTheDocument(); }); - it('should render correct barrier value', () => { + it('renders correct barrier value', () => { mockedBarrierInfo(); - expect(screen.getByText('Barrier')).toBeInTheDocument(); + const barrier = screen.getByText(barrier_label); + expect(barrier).toBeInTheDocument(); + expect(barrier).not.toHaveClass('trade-params__text--disabled'); expect(screen.getByText('1.2345')).toBeInTheDocument(); }); + + it('applies specific className if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockedBarrierInfo(); + + expect(screen.getByText(barrier_label)).toHaveClass('trade-params__text--disabled'); + }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/BarrierInfo/barrier-info.tsx b/packages/trader/src/AppV2/Components/TradeParameters/BarrierInfo/barrier-info.tsx index 610c28159ce7..c2e55d74fb66 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/BarrierInfo/barrier-info.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/BarrierInfo/barrier-info.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import clsx from 'clsx'; import { observer } from '@deriv/stores'; import { Skeleton } from '@deriv/components'; import { useTraderStore } from 'Stores/useTraderStores'; @@ -6,18 +7,18 @@ import { Text } from '@deriv-com/quill-ui'; import { Localize } from '@deriv/translations'; const BarrierInfo = observer(() => { - const { barrier_1, contract_type, proposal_info } = useTraderStore(); + const { barrier_1, contract_type, is_market_closed, proposal_info } = useTraderStore(); const contract_key = contract_type.toUpperCase(); const has_error = proposal_info[contract_key]?.has_error; if (has_error) return null; return (
- + {barrier_1 ? ( - + {barrier_1} ) : ( diff --git a/packages/trader/src/AppV2/Components/TradeParameters/Duration/__tests__/duration.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/Duration/__tests__/duration.spec.tsx index fb61dee63387..e0e37e32933d 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/Duration/__tests__/duration.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/Duration/__tests__/duration.spec.tsx @@ -120,16 +120,16 @@ describe('Duration', () => { expect(screen.getByRole('dialog')).toBeInTheDocument(); }); - it('should display a validation error message if there is a duration error', async () => { + it('should display a validation error message if there is a duration error', () => { default_trade_store.modules.trade.validation_errors.duration = [ { message: 'Invalid duration', error_field: 'duration' }, ]; mockDuration(); - await expect(mockAddSnackbar).toHaveBeenCalled(); + expect(mockAddSnackbar).toHaveBeenCalled(); }); it('should display the market closed message when the market is closed', () => { - default_trade_store.modules.trade.symbol = 'GBPUSD'; + default_trade_store.modules.trade.is_market_closed = true; mockDuration(); expect(screen.getByText(/duration/i)).toBeInTheDocument(); expect(screen.getByRole('textbox')).toBeDisabled(); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/Duration/duration.tsx b/packages/trader/src/AppV2/Components/TradeParameters/Duration/duration.tsx index 8c0e64548609..82ee839ba267 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/Duration/duration.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/Duration/duration.tsx @@ -2,26 +2,23 @@ import React, { useEffect, useRef, useState } from 'react'; import clsx from 'clsx'; import { observer } from 'mobx-react'; import { ActionSheet, TextField, useSnackbar } from '@deriv-com/quill-ui'; -import { getUnitMap, isMarketClosed } from '@deriv/shared'; +import { getUnitMap } from '@deriv/shared'; import { Localize, localize } from '@deriv/translations'; import { useTraderStore } from 'Stores/useTraderStores'; import DurationActionSheetContainer from './container'; import { getDisplayedContractTypes } from 'AppV2/Utils/trade-types-utils'; -import useActiveSymbols from 'AppV2/Hooks/useActiveSymbols'; import { getDatePickerStartDate, getSmallestDuration } from 'AppV2/Utils/trade-params-utils'; import { useStore } from '@deriv/stores'; +import { TTradeParametersProps } from '../trade-parameters'; -type TDurationProps = { - is_minimized?: boolean; -}; - -const Duration = observer(({ is_minimized }: TDurationProps) => { +const Duration = observer(({ is_minimized }: TTradeParametersProps) => { const { duration, duration_unit, expiry_time, expiry_type, contract_type, + is_market_closed, trade_types, proposal_info, trade_type_tab, @@ -47,7 +44,6 @@ const Duration = observer(({ is_minimized }: TDurationProps) => { (proposal_info[contract_type_object[0]]?.has_error && proposal_info[contract_type_object[0]]?.error_field === 'duration') || validation_errors.duration.length > 0; - const { activeSymbols } = useActiveSymbols(); const isInitialMount = useRef(true); const { common, client } = useStore(); const { is_logged_in } = client; @@ -173,7 +169,7 @@ const Duration = observer(({ is_minimized }: TDurationProps) => { label={} value={getInputValues()} noStatusIcon - disabled={isMarketClosed(activeSymbols, symbol)} + disabled={is_market_closed} className={clsx('trade-params__option', is_minimized && 'trade-params__option--minimized')} onClick={() => setOpen(true)} status={has_error ? 'error' : 'neutral'} diff --git a/packages/trader/src/AppV2/Components/TradeParameters/GrowthRate/__tests__/growth-rate.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/GrowthRate/__tests__/growth-rate.spec.tsx index f0b5e8f9d257..e72572562835 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/GrowthRate/__tests__/growth-rate.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/GrowthRate/__tests__/growth-rate.spec.tsx @@ -66,14 +66,14 @@ describe('GrowthRate', () => { ); - it('should render Skeleton loader if growth_rate is falsy', () => { + it('renders Skeleton loader if growth_rate is falsy', () => { default_mock_store.modules.trade.growth_rate = 0; mockGrowthRate(); expect(screen.getByTestId(skeleton_testid)).toBeInTheDocument(); expect(screen.queryByText(growth_rate_param_label)).not.toBeInTheDocument(); }); - it('should render trade param with "Growth rate" label and input with a value equal to the current growth_rate value in %', () => { + it('renders trade param with "Growth rate" label and input with a value equal to the current growth_rate value in %', () => { mockGrowthRate(); expect(screen.getByText(growth_rate_param_label)).toBeInTheDocument(); @@ -81,13 +81,19 @@ describe('GrowthRate', () => { `${getGrowthRatePercentage(default_mock_store.modules.trade.growth_rate)}%` ); }); - it('should disable trade param if has_open_accu_contract === true', () => { + it('disables trade param if has_open_accu_contract === true', () => { default_mock_store.modules.trade.has_open_accu_contract = true; mockGrowthRate(); expect(screen.getByRole('textbox')).toBeDisabled(); }); - it('should open ActionSheet with WheelPicker component, details, "Save" button and trade param definition if user clicks on "Growth rate" trade param', () => { + it('disables trade param if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockGrowthRate(); + + expect(screen.getByRole('textbox')).toBeDisabled(); + }); + it('opens ActionSheet with WheelPicker component, details, "Save" button and trade param definition if user clicks on "Growth rate" trade param', () => { default_mock_store.modules.trade.maximum_ticks = 55; mockGrowthRate(); @@ -106,7 +112,7 @@ describe('GrowthRate', () => { expect(screen.getByText('Save')).toBeInTheDocument(); expect(screen.getByText(mocked_definition)).toBeInTheDocument(); }); - it('should render skeleton instead of WheelPicker if accumulator_range_list is empty', () => { + it('renders skeleton instead of WheelPicker if accumulator_range_list is empty', () => { default_mock_store.modules.trade.accumulator_range_list = []; mockGrowthRate(); @@ -116,7 +122,7 @@ describe('GrowthRate', () => { expect(screen.queryByText('WheelPicker')).not.toBeInTheDocument(); expect(screen.getByTestId(skeleton_testid)).toBeInTheDocument(); }); - it('should render skeletons instead of details if proposal data is not available', () => { + it('renders skeletons instead of details if proposal data is not available', () => { default_mock_store.modules.trade.proposal_info = {}; default_mock_store.modules.trade.is_purchase_enabled = false; mockGrowthRate(); @@ -129,7 +135,7 @@ describe('GrowthRate', () => { expect(screen.queryByText(`${default_mock_store.modules.trade.maximum_ticks} ticks`)).not.toBeInTheDocument(); expect(screen.getAllByTestId(skeleton_testid)).toHaveLength(2); }); - it('should apply specific className if innerHeight is <= 640px', () => { + it('applies specific className if innerHeight is <= 640px', () => { const original_height = window.innerHeight; window.innerHeight = 640; mockGrowthRate(); @@ -139,7 +145,7 @@ describe('GrowthRate', () => { expect(screen.getByTestId(growth_rate_carousel_testid)).toHaveClass('growth-rate__carousel--small'); window.innerHeight = original_height; }); - it('should call onChange function if user changes selected value', async () => { + it('calls onChange function if user changes selected value', async () => { jest.useFakeTimers(); mockGrowthRate(); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/GrowthRate/growth-rate.tsx b/packages/trader/src/AppV2/Components/TradeParameters/GrowthRate/growth-rate.tsx index 4cffcf4f5589..d446afe66200 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/GrowthRate/growth-rate.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/GrowthRate/growth-rate.tsx @@ -11,17 +11,15 @@ import CarouselHeader from 'AppV2/Components/Carousel/carousel-header'; import TradeParamDefinition from 'AppV2/Components/TradeParamDefinition'; import { isSmallScreen } from 'AppV2/Utils/trade-params-utils'; import GrowthRatePicker from './growth-rate-picker'; +import { TTradeParametersProps } from '../trade-parameters'; -type TGrowthRateProps = { - is_minimized?: boolean; -}; - -const GrowthRate = observer(({ is_minimized }: TGrowthRateProps) => { +const GrowthRate = observer(({ is_minimized }: TTradeParametersProps) => { const { accumulator_range_list, growth_rate, is_purchase_enabled, is_trade_enabled, + is_market_closed, has_open_accu_contract, maximum_ticks, onChange, @@ -92,7 +90,7 @@ const GrowthRate = observer(({ is_minimized }: TGrowthRateProps) => { <> } diff --git a/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/__tests__/last-digit-prediction.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/__tests__/last-digit-prediction.spec.tsx index af539c7c92fa..5f4786ae9482 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/__tests__/last-digit-prediction.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/__tests__/last-digit-prediction.spec.tsx @@ -34,7 +34,7 @@ describe('LastDigitPrediction', () => { ); }; - it('should render 10 enabled buttons for each digit if digit_stats are available', () => { + it('renders 10 enabled buttons for each digit if digit_stats are available', () => { default_mock_store.modules.trade.digit_stats = digit_stats; render(mockLastDigitPrediction()); @@ -44,20 +44,26 @@ describe('LastDigitPrediction', () => { expect(button).toBeEnabled(); }); }); - it('should render component with correct last digit value when minimized', () => { + it('renders component with correct last digit value when minimized', () => { render(mockLastDigitPrediction({ is_minimized: true })); expect(screen.getByText(title)).toBeInTheDocument(); expect(screen.getByRole('textbox')).toHaveValue(default_mock_store.modules.trade.last_digit.toString()); }); - it('should show ActionSheet if user clicks on the minimized Last digit prediction param', () => { + it('disables component if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + render(mockLastDigitPrediction({ is_minimized: true })); + + expect(screen.getByRole('textbox')).toBeDisabled(); + }); + it('shows ActionSheet if user clicks on the minimized Last digit prediction param', () => { render(mockLastDigitPrediction({ is_minimized: true })); userEvent.click(screen.getByRole('textbox')); expect(screen.getByRole('dialog')).toHaveAttribute('data-state', 'open'); }); - it('should call onChange function if user opens ActionSheet, selects another digit and clicks on "Save" button', () => { + it('calls onChange function if user opens ActionSheet, selects another digit and clicks on "Save" button', () => { render(mockLastDigitPrediction({ is_minimized: true })); userEvent.click(screen.getByRole('textbox')); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/digit.tsx b/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/digit.tsx index 035287139427..ab1e49353c46 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/digit.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/digit.tsx @@ -26,7 +26,9 @@ const Digit = ({ digit, digit_stats = [], is_active, is_disabled, is_max, is_min onClick={() => onClick?.(digit)} name='last_digit' > - {digit} + + {digit} + {display_percentage ? ( { - const { digit_stats = [], last_digit, onChange } = useTraderStore(); +const LastDigitPrediction = observer(({ is_minimized }: TTradeParametersProps) => { + const { digit_stats = [], is_market_closed, last_digit, onChange } = useTraderStore(); const [is_open, setIsOpen] = React.useState(false); const [selected_digit, setSelectedDigit] = React.useState(last_digit); @@ -36,6 +33,8 @@ const LastDigitPrediction = observer(({ is_minimized }: TLastDigitSelectorProps) return ( <> } value={last_digit} - className={clsx('trade-params__option', 'trade-params__option--minimized')} onClick={() => setIsOpen(true)} /> +
@@ -87,6 +85,7 @@ const LastDigitPrediction = observer(({ is_minimized }: TLastDigitSelectorProps) digit_stats={digit_stats} onDigitSelect={handleLastDigitChange} selected_digit={last_digit} + is_disabled={is_market_closed} />
); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/last-digit-selector.tsx b/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/last-digit-selector.tsx index e6d225cb9a4d..3a33ad2a6fec 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/last-digit-selector.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/LastDigitPrediction/last-digit-selector.tsx @@ -4,11 +4,18 @@ import Digit from './digit'; type TLastDigitSelectorProps = { digits: number[]; digit_stats: number[]; + is_disabled?: boolean; onDigitSelect?: (digit: number) => void; selected_digit?: number; }; -const LastDigitSelector = ({ digits = [], digit_stats, onDigitSelect, selected_digit }: TLastDigitSelectorProps) => ( +const LastDigitSelector = ({ + digits = [], + digit_stats, + is_disabled, + onDigitSelect, + selected_digit, +}: TLastDigitSelectorProps) => (
{[...Array(2).keys()].map(row_key => (
@@ -18,6 +25,7 @@ const LastDigitSelector = ({ digits = [], digit_stats, onDigitSelect, selected_d digit={digit} digit_stats={digit_stats} is_active={selected_digit === digit} + is_disabled={is_disabled} is_max={digit_stats[digit] === Math.max(...digit_stats)} is_min={digit_stats[digit] === Math.min(...digit_stats)} onClick={onDigitSelect} diff --git a/packages/trader/src/AppV2/Components/TradeParameters/Multiplier/__tests__/multiplier.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/Multiplier/__tests__/multiplier.spec.tsx index 97c8adc520fc..7fa12e9d87fd 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/Multiplier/__tests__/multiplier.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/Multiplier/__tests__/multiplier.spec.tsx @@ -65,20 +65,26 @@ describe('', () => { ); - it('should render Skeleton loader if multiplier is falsy', () => { + it('renders Skeleton loader if multiplier is falsy', () => { default_mock_store.modules.trade.multiplier = 0; mockMultiplier(); expect(screen.getByTestId(skeleton_testid)).toBeInTheDocument(); expect(screen.queryByText(multiplier_param_label)).not.toBeInTheDocument(); }); - it('should render trade param with multiplier label and input with a value equal to the current multiplier value', () => { + it('renders trade param with multiplier label and input with a value equal to the current multiplier value', () => { mockMultiplier(); expect(screen.getByText(multiplier_param_label)).toBeInTheDocument(); expect(screen.getByRole('textbox')).toHaveValue('x1'); }); - it('should open ActionSheet with WheelPicker component, details, "Save" button and trade param definition if user clicks on multiplier trade param', () => { + it('disables trade param if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockMultiplier(); + + expect(screen.getByRole('textbox')).toBeDisabled(); + }); + it('opens ActionSheet with WheelPicker component, details, "Save" button and trade param definition if user clicks on multiplier trade param', () => { mockMultiplier(); expect(screen.queryByTestId('dt-actionsheet-overlay')).not.toBeInTheDocument(); @@ -92,7 +98,7 @@ describe('', () => { expect(screen.getByText('Commission')).toBeInTheDocument(); expect(screen.getByText('0.01')).toBeInTheDocument(); }); - it('should render skeleton instead of WheelPicker if multiplier_range_list is empty', () => { + it('renders skeleton instead of WheelPicker if multiplier_range_list is empty', () => { default_mock_store.modules.trade.multiplier_range_list = []; mockMultiplier(); @@ -102,7 +108,7 @@ describe('', () => { expect(screen.queryByText('WheelPicker')).not.toBeInTheDocument(); expect(screen.getByTestId(skeleton_testid)).toBeInTheDocument(); }); - it('should render skeleton instead of detail if commission not available', () => { + it('renders skeleton instead of detail if commission not available', () => { default_mock_store.modules.trade.commission = null; mockMultiplier(); @@ -110,7 +116,7 @@ describe('', () => { expect(screen.getByTestId(skeleton_testid)).toBeInTheDocument(); }); - it('should apply specific className if innerHeight is <= 640px', () => { + it('applies specific className if innerHeight is <= 640px', () => { const original_height = window.innerHeight; window.innerHeight = 640; mockMultiplier(); @@ -120,7 +126,7 @@ describe('', () => { expect(screen.getByTestId(multiplier_carousel_testid)).toHaveClass('multiplier__carousel--small'); window.innerHeight = original_height; }); - it('should call onChange function if user changes selected value', async () => { + it('calls onChange function if user changes selected value', async () => { jest.useFakeTimers(); mockMultiplier(); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/Multiplier/multiplier.tsx b/packages/trader/src/AppV2/Components/TradeParameters/Multiplier/multiplier.tsx index 3e1a732368dc..916e9b1f790a 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/Multiplier/multiplier.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/Multiplier/multiplier.tsx @@ -9,13 +9,10 @@ import CarouselHeader from 'AppV2/Components/Carousel/carousel-header'; import TradeParamDefinition from 'AppV2/Components/TradeParamDefinition'; import { isSmallScreen } from 'AppV2/Utils/trade-params-utils'; import MultiplierWheelPicker from './multiplier-wheel-picker'; +import { TTradeParametersProps } from '../trade-parameters'; -type TMultiplierProps = { - is_minimized?: boolean; -}; - -const Multiplier = observer(({ is_minimized }: TMultiplierProps) => { - const { multiplier, multiplier_range_list, commission, onChange, currency } = useTraderStore(); +const Multiplier = observer(({ is_minimized }: TTradeParametersProps) => { + const { multiplier, multiplier_range_list, commission, is_market_closed, onChange, currency } = useTraderStore(); const [isOpen, setIsOpen] = useState(false); const is_small_screen_device = isSmallScreen(); @@ -62,13 +59,14 @@ const Multiplier = observer(({ is_minimized }: TMultiplierProps) => { return ( } value={`x${multiplier}`} - className={classname} onClick={() => setIsOpen(true)} /> { ); - 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 = { MULTUP: { has_error: true, @@ -43,17 +43,26 @@ describe('MultipliersDealCancellationInfo', () => { expect(container).toBeEmptyDOMElement(); }); - it('should render skeleton, if proposal_info is empty', () => { + it('renders skeleton, if proposal_info is empty', () => { default_mock_store.modules.trade.proposal_info = {}; mockMultipliersDealCancellationInfo(); expect(screen.getByTestId('dt_skeleton')).toBeInTheDocument(); }); - it('should render component', () => { + it('renders component with title and value', () => { mockMultipliersDealCancellationInfo(); - expect(screen.getByText('Deal cancellation fee')).toBeInTheDocument(); + const title = screen.getByText('Deal cancellation fee'); + expect(title).toBeInTheDocument(); + expect(title).not.toHaveClass('trade-params__text--disabled'); expect(screen.getByText(/4.00 USD/)).toBeInTheDocument(); }); + + it('applies specific className if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockMultipliersDealCancellationInfo(); + + expect(screen.getByText('Deal cancellation fee')).toHaveClass('trade-params__text--disabled'); + }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/MultipliersDealCancellationInfo/multipliers-deal-cancellation-info.tsx b/packages/trader/src/AppV2/Components/TradeParameters/MultipliersDealCancellationInfo/multipliers-deal-cancellation-info.tsx index dccd0aa611cf..00fcd9f14218 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/MultipliersDealCancellationInfo/multipliers-deal-cancellation-info.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/MultipliersDealCancellationInfo/multipliers-deal-cancellation-info.tsx @@ -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'; @@ -7,7 +8,7 @@ import { useTraderStore } from 'Stores/useTraderStores'; import { CONTRACT_TYPES } from '@deriv/shared'; const MultipliersDealCancellationInfo = observer(() => { - const { currency, proposal_info } = useTraderStore(); + const { currency, is_market_closed, proposal_info } = useTraderStore(); const deal_cancellation_fee_value = proposal_info?.[CONTRACT_TYPES.MULTIPLIER.UP]?.cancellation?.ask_price; const has_error = proposal_info?.[CONTRACT_TYPES.MULTIPLIER.UP]?.has_error || @@ -17,10 +18,10 @@ const MultipliersDealCancellationInfo = observer(() => { return (
- + - + {deal_cancellation_fee_value ? ( ) : ( diff --git a/packages/trader/src/AppV2/Components/TradeParameters/MultipliersExpirationInfo/__tests__/multipliers-expiration-info.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/MultipliersExpirationInfo/__tests__/multipliers-expiration-info.spec.tsx index 0599d3c11667..063e8a0bed5d 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/MultipliersExpirationInfo/__tests__/multipliers-expiration-info.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/MultipliersExpirationInfo/__tests__/multipliers-expiration-info.spec.tsx @@ -18,6 +18,7 @@ describe('', () => { modules: { trade: { expiration: 1626188400, + is_market_closed: false, }, }, common: { @@ -27,9 +28,9 @@ describe('', () => { }, }; - const MockedMultipliersExpirationInfo = (mocked_store: TCoreStores) => { + const MockedMultipliersExpirationInfo = () => { return ( - + ); @@ -39,9 +40,16 @@ describe('', () => { (formatDuration as jest.Mock).mockReturnValue({ days: 1, timestamp: '14:00' }); (getDateFromNow as jest.Mock).mockReturnValue('13 Jul 2021'); (getDiffDuration as jest.Mock).mockReturnValue(14400); - render(MockedMultipliersExpirationInfo(mockStore(mock_store))); + render(MockedMultipliersExpirationInfo()); expect(screen.getByText('Expires on')).toBeInTheDocument(); expect(screen.getByText('13 Jul 2021 at 14:00')).toBeInTheDocument(); }); + + it('applies specific className if is_market_closed === true', () => { + mock_store.modules.trade.is_market_closed = true; + render(MockedMultipliersExpirationInfo()); + + expect(screen.getByText('Expires on')).toHaveClass('trade-params__text--disabled'); + }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/MultipliersExpirationInfo/multipliers-expiration-info.tsx b/packages/trader/src/AppV2/Components/TradeParameters/MultipliersExpirationInfo/multipliers-expiration-info.tsx index d90fdc6444d7..ce802352786e 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/MultipliersExpirationInfo/multipliers-expiration-info.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/MultipliersExpirationInfo/multipliers-expiration-info.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import clsx from 'clsx'; import { observer, useStore } from '@deriv/stores'; import { useTraderStore } from 'Stores/useTraderStores'; import { Text } from '@deriv-com/quill-ui'; @@ -6,7 +7,7 @@ import { Localize } from '@deriv/translations'; import { formatDuration, getDateFromNow, getDiffDuration } from '@deriv/shared'; const MultipliersExpirationInfo = observer(() => { - const { expiration } = useTraderStore(); + const { expiration, is_market_closed } = useTraderStore(); const { common } = useStore(); const { server_time: start_time } = common; const { days, timestamp } = formatDuration( @@ -17,10 +18,10 @@ const MultipliersExpirationInfo = observer(() => { return (
- + - +
diff --git a/packages/trader/src/AppV2/Components/TradeParameters/PayoutInfo/__tests__/payout-info.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/PayoutInfo/__tests__/payout-info.spec.tsx index 9394b7bc9f41..92c1ea605d01 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/PayoutInfo/__tests__/payout-info.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/PayoutInfo/__tests__/payout-info.spec.tsx @@ -4,6 +4,8 @@ import { mockStore } from '@deriv/stores'; import PayoutInfo from '../payout-info'; import TraderProviders from '../../../../../trader-providers'; +const label = 'Payout'; + describe('', () => { let default_mock_store: ReturnType; @@ -34,7 +36,7 @@ describe('', () => {
); - 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 = { ONETOUCH: { has_error: true, @@ -45,11 +47,11 @@ describe('', () => { expect(container).toBeEmptyDOMElement(); }); - it('should render loader if payout is falsy but there is no API error', () => { + it('renders loader if payout is falsy but there is no API error', () => { default_mock_store.modules.trade.proposal_info = {}; mockedPayoutInfo(); - expect(screen.getByText('Payout')).toBeInTheDocument(); + expect(screen.getByText(label)).toBeInTheDocument(); expect(screen.getByTestId('dt_skeleton')).toBeInTheDocument(); expect(screen.queryByText('123.00 USD')).not.toBeInTheDocument(); }); @@ -57,6 +59,13 @@ describe('', () => { mockedPayoutInfo(); expect(screen.getByText('123.00 USD')).toBeInTheDocument(); - expect(screen.getByText('Payout')).toBeInTheDocument(); + expect(screen.getByText(label)).toBeInTheDocument(); + expect(screen.getByText(label)).not.toHaveClass('trade-params__text--disabled'); + }); + it('applies specific className if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockedPayoutInfo(); + + expect(screen.getByText(label)).toHaveClass('trade-params__text--disabled'); }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/PayoutInfo/payout-info.tsx b/packages/trader/src/AppV2/Components/TradeParameters/PayoutInfo/payout-info.tsx index 354650dbaa51..44310e68a9fd 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/PayoutInfo/payout-info.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/PayoutInfo/payout-info.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import clsx from 'clsx'; import { observer } from '@deriv/stores'; import { useTraderStore } from 'Stores/useTraderStores'; import { Text } from '@deriv-com/quill-ui'; @@ -6,7 +7,7 @@ import { Money, Skeleton } from '@deriv/components'; import { Localize } from '@deriv/translations'; const PayoutInfo = observer(() => { - const { currency, proposal_info, trade_type_tab } = useTraderStore(); + const { currency, is_market_closed, proposal_info, trade_type_tab } = useTraderStore(); const { value: payout } = proposal_info[trade_type_tab]?.obj_contract_basis || {}; const has_error = proposal_info[trade_type_tab]?.has_error; @@ -14,11 +15,11 @@ const PayoutInfo = observer(() => { return (
- + {payout ? ( - + ) : ( diff --git a/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/__tests__/payout-per-point.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/__tests__/payout-per-point.spec.tsx index ab0b4e04b9ac..c10f5e62cf16 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/__tests__/payout-per-point.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/__tests__/payout-per-point.spec.tsx @@ -55,11 +55,11 @@ describe('PayoutPerPoint', () => { render( - + ); - it('should render Skeleton loader if payout_per_point is falsy', () => { + it('renders Skeleton loader if payout_per_point is falsy', () => { default_mock_store.modules.trade.payout_per_point = ''; mockPayoutPerPoint(); @@ -67,14 +67,21 @@ describe('PayoutPerPoint', () => { expect(screen.queryByText(payout_per_point_label)).not.toBeInTheDocument(); }); - it('should render trade param with "Payout per point" label and input with value equal to current payout_per_point value', () => { + it('renders trade param with "Payout per point" label and input with value equal to current payout_per_point value', () => { mockPayoutPerPoint(); expect(screen.getByText(payout_per_point_label)).toBeInTheDocument(); expect(screen.getByRole('textbox')).toHaveValue('3 USD'); }); - it('should open ActionSheet with WheelPicker component, barrier information, "Save" button and text content with definition if user clicks on trade param', () => { + it('disables trade param if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockPayoutPerPoint(); + + expect(screen.getByRole('textbox')).toBeDisabled(); + }); + + it('opens ActionSheet with WheelPicker component, barrier information, "Save" button and text content with definition if user clicks on trade param', () => { mockPayoutPerPoint(); expect(screen.queryByTestId('dt-actionsheet-overlay')).not.toBeInTheDocument(); @@ -93,7 +100,7 @@ describe('PayoutPerPoint', () => { ).toBeInTheDocument(); }); - it('should not render barrier information if barrier is not defined', () => { + it('does not render barrier information if barrier is not defined', () => { default_mock_store.modules.trade.barrier_1 = undefined; mockPayoutPerPoint(); @@ -103,7 +110,7 @@ describe('PayoutPerPoint', () => { expect(screen.queryByText('+1.80')).not.toBeInTheDocument(); }); - it('should apply specific className if innerHeight is <= 640px', () => { + it('applies specific className if innerHeight is <= 640px', () => { const original_height = window.innerHeight; window.innerHeight = 640; mockPayoutPerPoint(); @@ -114,7 +121,7 @@ describe('PayoutPerPoint', () => { window.innerHeight = original_height; }); - it('should call setPayoutPerPoint function if user changes selected value', async () => { + it('calls setPayoutPerPoint function if user changes selected value', async () => { jest.useFakeTimers(); mockPayoutPerPoint(); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/payout-per-point.tsx b/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/payout-per-point.tsx index 32ebeea831ff..d903207184de 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/payout-per-point.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/payout-per-point.tsx @@ -11,16 +11,14 @@ import Carousel from 'AppV2/Components/Carousel'; import CarouselHeader from 'AppV2/Components/Carousel/carousel-header'; import TradeParamDefinition from 'AppV2/Components/TradeParamDefinition'; import PayoutPerPointWheel from './payout-per-point-wheel'; +import { TTradeParametersProps } from '../trade-parameters'; -type TPayoutPerPointProps = { - is_minimized?: boolean; -}; - -const PayoutPerPoint = observer(({ is_minimized }: TPayoutPerPointProps) => { +const PayoutPerPoint = observer(({ is_minimized }: TTradeParametersProps) => { const [is_open, setIsOpen] = React.useState(false); const { barrier_1, currency, + is_market_closed, payout_choices, payout_per_point, setPayoutPerPoint, @@ -85,6 +83,7 @@ const PayoutPerPoint = observer(({ is_minimized }: TPayoutPerPointProps) => { return ( ', () => { let default_mock_store: ReturnType; @@ -34,7 +36,7 @@ describe('', () => { ); - 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 = { VANILLALONGCALL: { has_error: true, @@ -45,11 +47,11 @@ describe('', () => { expect(container).toBeEmptyDOMElement(); }); - it('should render loader if payout is falsy but there is no API error', () => { + it('renders loader if payout is falsy but there is no API error', () => { default_mock_store.modules.trade.proposal_info = {}; mockedPayoutPerPointInfo(); - expect(screen.getByText('Payout per point')).toBeInTheDocument(); + expect(screen.getByText(label)).toBeInTheDocument(); expect(screen.getByTestId('dt_skeleton')).toBeInTheDocument(); expect(screen.queryByText('123 USD')).not.toBeInTheDocument(); }); @@ -57,6 +59,13 @@ describe('', () => { mockedPayoutPerPointInfo(); expect(screen.getByText('123 USD')).toBeInTheDocument(); - expect(screen.getByText('Payout per point')).toBeInTheDocument(); + expect(screen.getByText(label)).toBeInTheDocument(); + expect(screen.getByText(label)).not.toHaveClass('trade-params__text--disabled'); + }); + it('applies specific className if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockedPayoutPerPointInfo(); + + expect(screen.getByText(label)).toHaveClass('trade-params__text--disabled'); }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPointInfo/payout-per-point-info.tsx b/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPointInfo/payout-per-point-info.tsx index df49d4f4a2c2..7e21dbef823a 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPointInfo/payout-per-point-info.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPointInfo/payout-per-point-info.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import clsx from 'clsx'; import { observer } from '@deriv/stores'; import { useTraderStore } from 'Stores/useTraderStores'; import { Text } from '@deriv-com/quill-ui'; @@ -6,7 +7,7 @@ import { Localize } from '@deriv/translations'; import { Skeleton } from '@deriv/components'; const PayoutPerPointInfo = observer(() => { - const { contract_type, currency, proposal_info } = useTraderStore(); + const { contract_type, currency, is_market_closed, proposal_info } = useTraderStore(); const contract_key = contract_type.toUpperCase(); const { value: payout_per_point } = proposal_info[contract_key]?.obj_contract_basis || {}; const has_error = proposal_info[contract_key]?.has_error; @@ -15,11 +16,11 @@ const PayoutPerPointInfo = observer(() => { return (
- + {payout_per_point ? ( - + {payout_per_point} {currency} ) : ( diff --git a/packages/trader/src/AppV2/Components/TradeParameters/RiskManagement/__tests__/risk-management.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/RiskManagement/__tests__/risk-management.spec.tsx index ecc1658061c5..3ae6b8647587 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/RiskManagement/__tests__/risk-management.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/RiskManagement/__tests__/risk-management.spec.tsx @@ -10,16 +10,17 @@ const risk_management = 'Risk Management'; describe('RiskManagement', () => { let default_mock_store: ReturnType; - beforeEach(() => { - default_mock_store = mockStore({ - modules: { - trade: { - ...mockStore({}).modules.trade, - currency: 'USD', + beforeEach( + () => + (default_mock_store = mockStore({ + modules: { + trade: { + ...mockStore({}).modules.trade, + currency: 'USD', + }, }, - }, - }); - }); + })) + ); afterEach(() => jest.clearAllMocks()); @@ -32,14 +33,14 @@ describe('RiskManagement', () => { ); - it('should render Risk Management trade param with correct value', () => { + it('renders Risk Management trade param with correct value', () => { mockRiskManagement(); expect(screen.getByText(risk_management)).toBeInTheDocument(); expect(screen.getByRole('textbox')).toHaveValue('-'); }); - it('should render Risk Management trade param with correct value for active DC', () => { + it('renders Risk Management trade param with correct value for active DC', () => { default_mock_store.modules.trade.has_cancellation = true; default_mock_store.modules.trade.cancellation_duration = '30m'; @@ -49,7 +50,7 @@ describe('RiskManagement', () => { expect(screen.getByRole('textbox')).toHaveValue('DC: 30 minutes'); }); - it('should render Risk Management trade param with correct value for both active TP&SL', () => { + it('renders Risk Management trade param with correct value for both active TP&SL', () => { default_mock_store.modules.trade.has_take_profit = true; default_mock_store.modules.trade.has_stop_loss = true; default_mock_store.modules.trade.take_profit = '5'; @@ -61,7 +62,7 @@ describe('RiskManagement', () => { expect(screen.getByRole('textbox')).toHaveValue('TP: 5 USD / SL: 1 USD'); }); - it('should render Risk Management trade param with correct value for active TP', () => { + it('renders Risk Management trade param with correct value for active TP', () => { default_mock_store.modules.trade.has_take_profit = true; default_mock_store.modules.trade.take_profit = '10'; @@ -71,7 +72,7 @@ describe('RiskManagement', () => { expect(screen.getByRole('textbox')).toHaveValue('TP: 10 USD'); }); - it('should render Risk Management trade param with correct value for active SL', () => { + it('renders Risk Management trade param with correct value for active SL', () => { default_mock_store.modules.trade.has_stop_loss = true; default_mock_store.modules.trade.stop_loss = '2'; @@ -80,4 +81,11 @@ describe('RiskManagement', () => { expect(screen.getByText(risk_management)).toBeInTheDocument(); expect(screen.getByRole('textbox')).toHaveValue('SL: 2 USD'); }); + + it('disables trade param if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockRiskManagement(); + + expect(screen.getByRole('textbox')).toBeDisabled(); + }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/RiskManagement/risk-management.tsx b/packages/trader/src/AppV2/Components/TradeParameters/RiskManagement/risk-management.tsx index e31303f32809..0252a3255627 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/RiskManagement/risk-management.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/RiskManagement/risk-management.tsx @@ -11,12 +11,9 @@ import TradeParamDefinition from 'AppV2/Components/TradeParamDefinition'; import { addUnit, isSmallScreen } from 'AppV2/Utils/trade-params-utils'; import RiskManagementPicker from './risk-management-picker'; import RiskManagementContent from './risk-management-content'; +import { TTradeParametersProps } from '../trade-parameters'; -type TRiskManagementProps = { - is_minimized?: boolean; -}; - -const RiskManagement = observer(({ is_minimized }: TRiskManagementProps) => { +const RiskManagement = observer(({ is_minimized }: TTradeParametersProps) => { const [is_open, setIsOpen] = React.useState(false); const { cancellation_range_list, @@ -25,6 +22,7 @@ const RiskManagement = observer(({ is_minimized }: TRiskManagementProps) => { has_cancellation, has_take_profit, has_stop_loss, + is_market_closed, take_profit, stop_loss, } = useTraderStore(); @@ -73,6 +71,7 @@ const RiskManagement = observer(({ is_minimized }: TRiskManagementProps) => { { ); - it('should switch basis to stake if it is different', () => { + it('switches basis to stake if it is different', () => { default_mock_store.modules.trade.basis = 'payout'; render(); @@ -93,14 +93,14 @@ describe('Stake', () => { }); }); - it('should render trade param with "Stake" label and input with a value equal to the current stake amount value', () => { + it('renders trade param with "Stake" label and input with a value equal to the current stake amount value', () => { render(); const { amount, currency } = default_mock_store.modules.trade; expect(screen.getByText(stake_param_label)).toBeInTheDocument(); expect(screen.getByRole('textbox')).toHaveValue(`${amount} ${currency}`); }); - it('should open ActionSheet with input, details and "Save" button if user clicks on "Stake" trade param', () => { + it('opens ActionSheet with input, details and "Save" button if user clicks on "Stake" trade param', () => { render(); expect(screen.queryByTestId('dt-actionsheet-overlay')).not.toBeInTheDocument(); @@ -113,7 +113,7 @@ describe('Stake', () => { expect(screen.getByRole('button', { name: save_button_label })).toBeInTheDocument(); }); - it('should call onChange when stake input changes', () => { + it('calls onChange when stake input changes', () => { render(); userEvent.click(screen.getByText(stake_param_label)); userEvent.type(screen.getByPlaceholderText(input_placeholder), '0'); @@ -122,28 +122,28 @@ describe('Stake', () => { }); }); - it('should not render payout details for Accumulators', () => { + it('does not render payout details for Accumulators', () => { default_mock_store.modules.trade.is_accumulator = true; render(); userEvent.click(screen.getByText(stake_param_label)); expect(screen.queryByText(/payout/i)).not.toBeInTheDocument(); }); - it('should not render payout details for Turbos', () => { + it('does not render payout details for Turbos', () => { default_mock_store.modules.trade.is_turbos = true; render(); userEvent.click(screen.getByText(stake_param_label)); expect(screen.queryByText(/payout/i)).not.toBeInTheDocument(); }); - it('should not render payout details for Vanillas', () => { + it('does not render payout details for Vanillas', () => { default_mock_store.modules.trade.is_vanilla = true; render(); userEvent.click(screen.getByText(stake_param_label)); expect(screen.queryByText(/payout/i)).not.toBeInTheDocument(); }); - it('should render Stop out and Comission details instead of payout details for Multipliers', () => { + it('renders Stop out and Commission details instead of payout details for Multipliers', () => { render( { expect(screen.getByText('Commission')).toBeInTheDocument(); }); - it('should call setV2ParamsInitialValues if v2_params_initial_values.stake !== amount on mount and on Save button click if no error', () => { + it('calls setV2ParamsInitialValues if v2_params_initial_values.stake !== amount on mount and on Save button click if no error', () => { default_mock_store.modules.trade.amount = '30'; render(); userEvent.click(screen.getByText(stake_param_label)); @@ -214,7 +214,7 @@ describe('Stake', () => { expect(default_mock_store.modules.trade.setV2ParamsInitialValues).toHaveBeenCalledTimes(2); }); - it('should call onChange on component mount if v2_params_initial_values.stake is not equal to amount', () => { + it('calls onChange on component mount if v2_params_initial_values.stake is not equal to amount', () => { default_mock_store.modules.trade.amount = '30'; render(); expect(default_mock_store.modules.trade.onChange).toHaveBeenCalledWith({ @@ -222,7 +222,7 @@ describe('Stake', () => { }); }); - it('should show error in case of a validation error if input is non-empty', () => { + it('shows error in case of a validation error if input is non-empty', () => { const error_text = "Please enter a stake amount that's at least 0.35."; default_mock_store.modules.trade.proposal_info = { PUT: { id: '', has_error: true, message: error_text }, @@ -237,7 +237,7 @@ describe('Stake', () => { expect(screen.getAllByText('- USD')).toHaveLength(2); }); - it('should show max payout error with the least current payout when both of the 2 contract types exceed max payout', () => { + it('shows max payout error with the least current payout when both of the 2 contract types exceed max payout', () => { const error_text_rise = 'Minimum stake of 0.35 and maximum payout of 50000.00. Current payout is 50631.97.'; const error_text_fall = 'Minimum stake of 0.35 and maximum payout of 50000.00. Current payout is 50513.21.'; default_mock_store.modules.trade.proposal_info = { @@ -254,7 +254,7 @@ describe('Stake', () => { expect(screen.queryByText('- USD')).not.toBeInTheDocument(); }); - it('should not show max payout error if one of the 2 contract types satisfies max payout', () => { + it('does not show max payout error if one of the 2 contract types satisfies max payout', () => { const error_text_rise = 'Minimum stake of 0.35 and maximum payout of 50000.00. Current payout is 50058.77.'; const success_text_fall = 'Win payout if Volatility 100 (1s) Index is strictly lower than entry spot at 5 minutes after contract start time.'; @@ -275,10 +275,17 @@ describe('Stake', () => { expect(screen.queryByText(error_text_rise)).not.toBeInTheDocument(); }); - it('should set default stake if available_contract_types object contains it ', () => { + it('sets default stake if available_contract_types object contains it', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.VANILLA.CALL; render(); expect(default_mock_store.modules.trade.setDefaultStake).toHaveBeenCalledWith(10); }); + + it('disables trade param if is_market_closed == true', () => { + default_mock_store.modules.trade.is_market_closed = true; + render(); + + expect(screen.getByRole('textbox')).toBeDisabled(); + }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/Stake/stake.tsx b/packages/trader/src/AppV2/Components/TradeParameters/Stake/stake.tsx index ca2a31ab3482..3cf22fe95fc1 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/Stake/stake.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/Stake/stake.tsx @@ -9,12 +9,9 @@ import { useTraderStore } from 'Stores/useTraderStores'; import { getDisplayedContractTypes } from 'AppV2/Utils/trade-types-utils'; import StakeDetails from './stake-details'; import useContractsForCompany from 'AppV2/Hooks/useContractsForCompany'; +import { TTradeParametersProps } from '../trade-parameters'; -type TStakeProps = { - is_minimized?: boolean; -}; - -const Stake = observer(({ is_minimized }: TStakeProps) => { +const Stake = observer(({ is_minimized }: TTradeParametersProps) => { const { amount, basis, @@ -27,6 +24,7 @@ const Stake = observer(({ is_minimized }: TStakeProps) => { is_multiplier, is_turbos, is_vanilla, + is_market_closed, onChange, proposal_info, setDefaultStake, @@ -240,6 +238,7 @@ const Stake = observer(({ is_minimized }: TStakeProps) => { return ( <> } @@ -248,7 +247,6 @@ const Stake = observer(({ is_minimized }: TStakeProps) => { value={`${v2_params_initial_values?.stake ?? amount} ${getCurrencyDisplayCode(currency)}`} className={clsx('trade-params__option', is_minimized && 'trade-params__option--minimized')} status={stake_error && !is_open ? 'error' : undefined} - disabled={has_open_accu_contract} /> { ); - it('should render Skeleton loader if strike (barrier_1) is falsy', () => { + it('renders Skeleton loader if strike (barrier_1) is falsy', () => { default_mock_store.modules.trade.barrier_1 = ''; mockStrike(); @@ -71,14 +71,14 @@ describe('Strike', () => { expect(screen.queryByText(strike_trade_param_label)).not.toBeInTheDocument(); }); - it('should render trade param with "Strike price" label and input with value equal to current strike value (barrier_1)', () => { + it('renders trade param with "Strike price" label and input with value equal to current strike value (barrier_1)', () => { mockStrike(); expect(screen.getByText(strike_trade_param_label)).toBeInTheDocument(); expect(screen.getByRole('textbox')).toHaveValue('+1.80'); }); - it('should open ActionSheet with WheelPicker component, Payout per point information, "Save" button and text content with definition if user clicks on trade param', () => { + it('opens ActionSheet with WheelPicker component, Payout per point information, "Save" button and text content with definition if user clicks on trade param', () => { mockStrike(); expect(screen.queryByTestId('dt-actionsheet-overlay')).not.toBeInTheDocument(); @@ -92,7 +92,7 @@ describe('Strike', () => { expect(screen.getByText('Save')).toBeInTheDocument(); }); - it('should not render Payout per point information if proposal_info is empty object', () => { + it('does not render Payout per point information if proposal_info is empty object', () => { default_mock_store.modules.trade.proposal_info = {}; mockStrike(); @@ -102,7 +102,7 @@ describe('Strike', () => { expect(screen.queryByText(/14.245555/)).not.toBeInTheDocument(); }); - it('should apply specific className if innerHeight is <= 640px', () => { + it('applies specific className if innerHeight is <= 640px', () => { const original_height = window.innerHeight; window.innerHeight = 640; mockStrike(); @@ -113,7 +113,7 @@ describe('Strike', () => { window.innerHeight = original_height; }); - it('should call onChange function if user changes selected value', async () => { + it('calls onChange function if user changes selected value', async () => { jest.useFakeTimers(); mockStrike(); @@ -129,4 +129,11 @@ describe('Strike', () => { expect(default_mock_store.modules.trade.onChange).toBeCalled(); jest.useRealTimers(); }); + + it('disables trade param if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockStrike(); + + expect(screen.getByRole('textbox')).toBeDisabled(); + }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/Strike/strike.tsx b/packages/trader/src/AppV2/Components/TradeParameters/Strike/strike.tsx index 6c842af8ea7c..3b5130b4f7ee 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/Strike/strike.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/Strike/strike.tsx @@ -11,18 +11,16 @@ import Carousel from 'AppV2/Components/Carousel'; import CarouselHeader from 'AppV2/Components/Carousel/carousel-header'; import { isSmallScreen } from 'AppV2/Utils/trade-params-utils'; import StrikeWheel from './strike-wheel'; +import { TTradeParametersProps } from '../trade-parameters'; -type TStrikeProps = { - is_minimized?: boolean; -}; - -const Strike = observer(({ is_minimized }: TStrikeProps) => { +const Strike = observer(({ is_minimized }: TTradeParametersProps) => { const [is_open, setIsOpen] = React.useState(false); const { barrier_1, barrier_choices: strike_price_choices, contract_type, currency, + is_market_closed, onChange, proposal_info, setV2ParamsInitialValues, @@ -97,6 +95,7 @@ const Strike = observer(({ is_minimized }: TStrikeProps) => { } onClick={() => setIsOpen(true)} readOnly diff --git a/packages/trader/src/AppV2/Components/TradeParameters/TakeProfit/__tests__/take-profit.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/TakeProfit/__tests__/take-profit.spec.tsx index eef5800da2b5..e04ac174ec48 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/TakeProfit/__tests__/take-profit.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/TakeProfit/__tests__/take-profit.spec.tsx @@ -34,14 +34,14 @@ describe('TakeProfit', () => { ); - it('should render TP trade parameter with correct take profit from trade store', () => { + it('renders TP trade parameter with correct take profit from trade store', () => { mockTakeProfit(); expect(screen.getByRole('textbox')).toHaveValue('5 USD'); expect(screen.getByText('Take profit')).toBeInTheDocument(); }); - it('should render TakeProfitAndStopLossInput and TradeParamDefinition when user clicks on TP input', () => { + it('renders TakeProfitAndStopLossInput and TradeParamDefinition when user clicks on TP input', () => { mockTakeProfit(); userEvent.click(screen.getByText('Take profit')); @@ -49,4 +49,11 @@ describe('TakeProfit', () => { expect(screen.getByText('TakeProfitAndStopLossInput')).toBeInTheDocument(); expect(screen.getByText('TradeParamDefinition')).toBeInTheDocument(); }); + + it('disables trade param if is_market_closed === true', () => { + default_mock_store.modules.trade.is_market_closed = true; + mockTakeProfit(); + + expect(screen.getByRole('textbox')).toBeDisabled(); + }); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/TakeProfit/take-profit.tsx b/packages/trader/src/AppV2/Components/TradeParameters/TakeProfit/take-profit.tsx index 880b4ea68e9c..902d7b13f3b7 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/TakeProfit/take-profit.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/TakeProfit/take-profit.tsx @@ -9,13 +9,10 @@ import Carousel from 'AppV2/Components/Carousel'; import CarouselHeader from 'AppV2/Components/Carousel/carousel-header'; import TakeProfitAndStopLossInput from '../RiskManagement/take-profit-and-stop-loss-input'; import TradeParamDefinition from 'AppV2/Components/TradeParamDefinition'; +import { TTradeParametersProps } from '../trade-parameters'; -type TTakeProfitProps = { - is_minimized?: boolean; -}; - -const TakeProfit = observer(({ is_minimized }: TTakeProfitProps) => { - const { currency, has_open_accu_contract, has_take_profit, take_profit } = useTraderStore(); +const TakeProfit = observer(({ is_minimized }: TTradeParametersProps) => { + const { currency, has_open_accu_contract, has_take_profit, is_market_closed, take_profit } = useTraderStore(); const [is_open, setIsOpen] = React.useState(false); @@ -42,7 +39,7 @@ const TakeProfit = observer(({ is_minimized }: TTakeProfitProps) => { } diff --git a/packages/trader/src/AppV2/Components/TradeParameters/TradeTypeTabs/trade-type-tabs.tsx b/packages/trader/src/AppV2/Components/TradeParameters/TradeTypeTabs/trade-type-tabs.tsx index 162564ee9ac6..23f0b129d2de 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/TradeTypeTabs/trade-type-tabs.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/TradeTypeTabs/trade-type-tabs.tsx @@ -4,13 +4,10 @@ import clsx from 'clsx'; import { SegmentedControlSingleChoice } from '@deriv-com/quill-ui'; import { useTraderStore } from 'Stores/useTraderStores'; import { getTradeTypeTabsList } from 'AppV2/Utils/trade-params-utils'; +import { TTradeParametersProps } from '../trade-parameters'; -type TTradeTypeTabsProps = { - is_minimized?: boolean; -}; - -const TradeTypeTabs = observer(({ is_minimized }: TTradeTypeTabsProps) => { - const { contract_type, onChange, trade_type_tab, setTradeTypeTab } = useTraderStore(); +const TradeTypeTabs = observer(({ is_minimized }: TTradeParametersProps) => { + const { contract_type, is_market_closed, onChange, trade_type_tab, setTradeTypeTab } = useTraderStore(); const tab_list = getTradeTypeTabsList(contract_type); let initial_index = 0; @@ -50,8 +47,9 @@ const TradeTypeTabs = observer(({ is_minimized }: TTradeTypeTabsProps) => { className={clsx('trade-params__option', is_minimized && 'trade-params__option--minimized')} hasContainerWidth onChange={handleTabChange} - options={tab_list.map(({ label }) => ({ label }))} + options={tab_list.map(({ label }) => ({ disabled: is_market_closed, label }))} selectedItemIndex={tab_index} + key={`${tab_index}${is_market_closed}`} /> ); }); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/__tests__/trade-parameters.spec.tsx b/packages/trader/src/AppV2/Components/TradeParameters/__tests__/trade-parameters.spec.tsx index 74a453dec866..e35aa8ef5aad 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/__tests__/trade-parameters.spec.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/__tests__/trade-parameters.spec.tsx @@ -51,9 +51,7 @@ jest.mock('../LastDigitPrediction', () => describe('TradeParameters', () => { let default_mock_store: ReturnType; - beforeEach(() => { - default_mock_store = mockStore({}); - }); + beforeEach(() => (default_mock_store = mockStore({}))); const mockTradeParameters = () => { return ( @@ -67,7 +65,7 @@ describe('TradeParameters', () => { ); }; - it('should render correct trade params for Accumulators', () => { + it('renders correct trade params for Accumulators', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.ACCUMULATOR; render(mockTradeParameters()); @@ -78,7 +76,7 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(4); }); - it('should render correct trade params for Vanillas', () => { + it('renders correct trade params for Vanillas', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.VANILLA.CALL; render(mockTradeParameters()); @@ -89,7 +87,7 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(4); }); - it('should render correct trade params for Turbos', () => { + it('renders correct trade params for Turbos', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.TURBOS.LONG; render(mockTradeParameters()); @@ -101,7 +99,7 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(5); }); - it('should render correct trade params for Multipliers if has_cancellation === false', () => { + it('renders correct trade params for Multipliers if has_cancellation === false', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.MULTIPLIER; render(mockTradeParameters()); @@ -112,7 +110,7 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(3); }); - it('should render correct trade params for Multipliers if has_cancellation === true', () => { + it('renders correct trade params for Multipliers if has_cancellation === true', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.MULTIPLIER; default_mock_store.modules.trade.has_cancellation = true; render(mockTradeParameters()); @@ -124,8 +122,19 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(4); }); - it('should render correct trade params for Rise/Fall', () => { + it('renders correct trade params for Rise/Fall', () => { + default_mock_store.modules.trade.contract_type = TRADE_TYPES.RISE_FALL; + render(mockTradeParameters()); + + expect(screen.getByText(TRADE_PARAMS.DURATION)).toBeInTheDocument(); + expect(screen.getByText(TRADE_PARAMS.STAKE)).toBeInTheDocument(); + expect(screen.getByText(TRADE_PARAMS.ALLOW_EQUALS)).toBeInTheDocument(); + expect(screen.getAllByTestId(data_test)).toHaveLength(3); + }); + + it('renders correct trade params for Rise/Fall if is_minimized and is_market_closed === true', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.RISE_FALL; + default_mock_store.modules.trade.is_market_closed = true; render(mockTradeParameters()); expect(screen.getByText(TRADE_PARAMS.DURATION)).toBeInTheDocument(); @@ -134,7 +143,7 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(3); }); - it('should render correct trade params for Higher/Lower', () => { + it('renders correct trade params for Higher/Lower', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.HIGH_LOW; render(mockTradeParameters()); @@ -146,7 +155,7 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(5); }); - it('should render correct trade params for Touch/No Touch', () => { + it('renders correct trade params for Touch/No Touch', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.TOUCH; render(mockTradeParameters()); @@ -158,7 +167,7 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(5); }); - it('should render correct trade params for Matches/Differs', () => { + it('renders correct trade params for Matches/Differs', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.MATCH_DIFF; render(mockTradeParameters()); @@ -168,7 +177,7 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(3); }); - it('should render correct trade params for Even/Odd', () => { + it('renders correct trade params for Even/Odd', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.EVEN_ODD; render(mockTradeParameters()); @@ -177,7 +186,7 @@ describe('TradeParameters', () => { expect(screen.getAllByTestId(data_test)).toHaveLength(2); }); - it('should render correct trade params for Over/Under', () => { + it('renders correct trade params for Over/Under', () => { default_mock_store.modules.trade.contract_type = TRADE_TYPES.OVER_UNDER; render(mockTradeParameters()); diff --git a/packages/trader/src/AppV2/Components/TradeParameters/trade-parameters.scss b/packages/trader/src/AppV2/Components/TradeParameters/trade-parameters.scss index 129c779226f9..c2c8fd5063f3 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/trade-parameters.scss +++ b/packages/trader/src/AppV2/Components/TradeParameters/trade-parameters.scss @@ -74,6 +74,10 @@ padding-bottom: 0; } } + + &__text--disabled { + color: var(--component-textIcon-normal-disabled); + } } .text-field--custom { diff --git a/packages/trader/src/AppV2/Components/TradeParameters/trade-parameters.tsx b/packages/trader/src/AppV2/Components/TradeParameters/trade-parameters.tsx index e63b95b1bbf5..9b0745e858a1 100644 --- a/packages/trader/src/AppV2/Components/TradeParameters/trade-parameters.tsx +++ b/packages/trader/src/AppV2/Components/TradeParameters/trade-parameters.tsx @@ -23,9 +23,7 @@ import BarrierInfo from './BarrierInfo'; import PayoutPerPointInfo from './PayoutPerPointInfo'; import PayoutInfo from './PayoutInfo'; -type TTradeParametersProps = { - is_minimized?: boolean; -}; +export type TTradeParametersProps = { is_minimized?: boolean }; const TradeParameters = observer(({ is_minimized }: TTradeParametersProps) => { const { contract_type, has_cancellation, symbol } = useTraderStore();