diff --git a/packages/components/src/components/contract-card/contract-card-items/__tests__/tick-counter-bar.spec.js b/packages/components/src/components/contract-card/contract-card-items/__tests__/tick-counter-bar.spec.js new file mode 100644 index 000000000000..31326a484c88 --- /dev/null +++ b/packages/components/src/components/contract-card/contract-card-items/__tests__/tick-counter-bar.spec.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import TickCounterBar from '../tick-counter-bar'; + +describe('TickCounterBar', () => { + const mock_props = { + current_tick: 12345, + label: 'Ticks', + max_ticks_duration: 67890, + }; + it('should render properly', () => { + render(); + + const ticks_info_el = screen.getByText('12345/67890 Ticks'); + expect(ticks_info_el).toHaveClass('dc-tick-counter-bar__text'); + }); +}); diff --git a/packages/components/src/components/contract-card/contract-card-items/accumulator-card-body.jsx b/packages/components/src/components/contract-card/contract-card-items/accumulator-card-body.jsx new file mode 100644 index 000000000000..d984ef1df89a --- /dev/null +++ b/packages/components/src/components/contract-card/contract-card-items/accumulator-card-body.jsx @@ -0,0 +1,129 @@ +import classNames from 'classnames'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { isCryptocurrency, getLimitOrderAmount, isValidToSell } from '@deriv/shared'; +import ContractCardItem from './contract-card-item.jsx'; +import ToggleCardDialog from './toggle-card-dialog.jsx'; +import Icon from '../../icon'; +import MobileWrapper from '../../mobile-wrapper'; +import Money from '../../money'; +import { ResultStatusIcon } from '../result-overlay/result-overlay.jsx'; + +const AccumulatorCardBody = ({ + addToast, + connectWithContractUpdate, + contract_info, + contract_update, + currency, + current_focus, + error_message_alignment, + getCardLabels, + getContractById, + indicative, + is_sold, + onMouseLeave, + removeToast, + setCurrentFocus, + status, + is_positions, +}) => { + const { buy_price, profit, limit_order, sell_price } = contract_info; + const { take_profit } = getLimitOrderAmount(contract_update || limit_order); + const is_valid_to_sell = isValidToSell(contract_info); + const { CURRENT_STAKE, STAKE, TAKE_PROFIT, TOTAL_PROFIT_LOSS } = getCardLabels(); + + return ( + +
+ + + + +
0, + 'dc-contract-card--loss': +profit < 0, + })} + > + +
+
+ {status === 'profit' && } + {status === 'loss' && } +
+
+ 0} + > + +
+ {status === 'profit' && } + {status === 'loss' && } +
+
+ + {take_profit ? : -} + {is_valid_to_sell && ( + + )} + +
+ {!!is_sold && ( + +
+ 0} /> +
+
+ )} +
+ ); +}; + +AccumulatorCardBody.propTypes = { + addToast: PropTypes.func, + connectWithContractUpdate: PropTypes.func, + contract_info: PropTypes.object, + contract_update: PropTypes.object, + currency: PropTypes.string, + current_focus: PropTypes.string, + error_message_alignment: PropTypes.string, + getCardLabels: PropTypes.func, + getContractById: PropTypes.func, + indicative: PropTypes.number, + is_positions: PropTypes.bool, + is_sold: PropTypes.bool, + onMouseLeave: PropTypes.func, + removeToast: PropTypes.func, + setCurrentFocus: PropTypes.func, + status: PropTypes.string, +}; + +export default React.memo(AccumulatorCardBody); diff --git a/packages/components/src/components/contract-card/contract-card-items/contract-card-body.jsx b/packages/components/src/components/contract-card/contract-card-items/contract-card-body.jsx index d92a15eead0c..577dc781b3aa 100644 --- a/packages/components/src/components/contract-card/contract-card-items/contract-card-body.jsx +++ b/packages/components/src/components/contract-card/contract-card-items/contract-card-body.jsx @@ -1,19 +1,8 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; import React from 'react'; -import { - isCryptocurrency, - getCancellationPrice, - getIndicativePrice, - getLimitOrderAmount, - getCurrentTick, - getDisplayStatus, - isValidToCancel, - isValidToSell, - shouldShowCancellation, -} from '@deriv/shared'; +import { isCryptocurrency, getIndicativePrice, getCurrentTick, getDisplayStatus } from '@deriv/shared'; import ContractCardItem from './contract-card-item.jsx'; -import ToggleCardDialog from './toggle-card-dialog.jsx'; import CurrencyBadge from '../../currency-badge'; import DesktopWrapper from '../../desktop-wrapper'; import Icon from '../../icon'; @@ -21,224 +10,9 @@ import MobileWrapper from '../../mobile-wrapper'; import Money from '../../money'; import { ResultStatusIcon } from '../result-overlay/result-overlay.jsx'; import ProgressSliderMobile from '../../progress-slider-mobile'; - -const VanillaOptionsCardBody = ({ contract_info, currency, getCardLabels, is_sold, progress_slider, status }) => { - const { buy_price, bid_price, entry_spot_display_value, barrier, sell_price, profit } = contract_info; - const contract_value = is_sold ? sell_price : bid_price; - - return ( - - -
- - - - - - - - - - - - - - - -
- 0} - > - -
- {status === 'profit' && } - {status === 'loss' && } -
-
-
- -
-
- - - - - - - -
- -
- - - - - - - -
- - {is_sold ? ( - - ) : ( - progress_slider - )} - 0} - > - -
- {status === 'profit' && } - {status === 'loss' && } -
-
-
-
-
- ); -}; - -const MultiplierCardBody = ({ - addToast, - contract_info, - contract_update, - connectWithContractUpdate, - currency, - current_focus, - error_message_alignment, - getCardLabels, - getContractById, - has_progress_slider, - progress_slider, - is_mobile, - is_sold, - onMouseLeave, - removeToast, - setCurrentFocus, - should_show_cancellation_warning, - status, - toggleCancellationWarning, -}) => { - const { buy_price, bid_price, profit, limit_order, underlying } = contract_info; - - const { take_profit, stop_loss } = getLimitOrderAmount(contract_update || limit_order); - const cancellation_price = getCancellationPrice(contract_info); - const is_valid_to_cancel = isValidToCancel(contract_info); - const is_valid_to_sell = isValidToSell(contract_info); - - return ( - -
- - - - -
0, - 'dc-contract-card--loss': +profit < 0, - })} - > - -
-
- - {cancellation_price ? ( - - ) : ( - - {shouldShowCancellation(underlying) ? - : getCardLabels().NOT_AVAILABLE} - - )} - - - - - {has_progress_slider && is_mobile && !is_sold && ( - {progress_slider} - )} -
- - {take_profit ? : -} - - - {stop_loss ? ( - - - - - - ) : ( - - - )} - - {(is_valid_to_sell || is_valid_to_cancel) && ( - - )} -
-
- 0} - > - -
- {status === 'profit' && } - {status === 'loss' && } -
-
-
- ); -}; +import AccumulatorCardBody from './accumulator-card-body.jsx'; +import MultiplierCardBody from './multiplier-card-body.jsx'; +import VanillaOptionsCardBody from './vanilla-options-card-body.jsx'; const ContractCardBody = ({ addToast, @@ -251,8 +25,10 @@ const ContractCardBody = ({ getCardLabels, getContractById, has_progress_slider, + is_accumulator, is_mobile, is_multiplier, + is_positions, is_sold, is_vanilla, onMouseLeave, @@ -266,6 +42,8 @@ const ContractCardBody = ({ const indicative = getIndicativePrice(contract_info); const { buy_price, sell_price, payout, profit, tick_count, date_expiry, purchase_time } = contract_info; const current_tick = tick_count ? getCurrentTick(contract_info) : null; + const { INDICATIVE_PRICE, PAYOUT, POTENTIAL_PAYOUT, POTENTIAL_PROFIT_LOSS, PROFIT_LOSS, PURCHASE_PRICE } = + getCardLabels(); const progress_slider_mobile_el = ( ); - const card_body = is_multiplier ? ( - - ) : is_vanilla ? ( - - ) : ( - -
- 0} - > - -
- {status === 'profit' && } - {status === 'loss' && } -
-
- - -
+ ); + } else if (is_accumulator) { + card_body = ( + + ); + } else if (is_vanilla) { + card_body = ( + + ); + } else { + card_body = ( + +
+ 0} > - {status === 'profit' && } - {status === 'loss' && } -
- - - - - - - -
- -
- {is_sold ? ( - - ) : ( - progress_slider_mobile_el - )} + +
+ {status === 'profit' && } + {status === 'loss' && } +
+ + + +
+ {status === 'profit' && } + {status === 'loss' && } +
+
+ + + + + +
-
- - ); + +
+ {is_sold ? ( + + ) : ( + progress_slider_mobile_el + )} +
+
+ + ); + } return ( @@ -393,9 +200,12 @@ ContractCardBody.propTypes = { error_message_alignment: PropTypes.string, getCardLabels: PropTypes.func, getContractById: PropTypes.func, + is_accumulator: PropTypes.bool, is_mobile: PropTypes.bool, is_multiplier: PropTypes.bool, + is_positions: PropTypes.bool, is_sold: PropTypes.bool, + is_vanilla: PropTypes.bool, onMouseLeave: PropTypes.func, removeToast: PropTypes.func, server_time: PropTypes.object, diff --git a/packages/components/src/components/contract-card/contract-card-items/contract-card-footer.jsx b/packages/components/src/components/contract-card/contract-card-items/contract-card-footer.jsx index b9a9084a7765..c58cca53cfc3 100644 --- a/packages/components/src/components/contract-card/contract-card-items/contract-card-footer.jsx +++ b/packages/components/src/components/contract-card/contract-card-items/contract-card-footer.jsx @@ -12,6 +12,7 @@ const CardFooter = ({ is_multiplier, is_positions, is_sell_requested, + measure, onClickCancel, onClickSell, onFooterEntered, @@ -70,6 +71,7 @@ const CardFooter = ({ contract_info={contract_info} is_sell_requested={is_sell_requested} getCardLabels={getCardLabels} + measure={measure} onClickSell={onClickSell} />
@@ -85,6 +87,7 @@ CardFooter.propTypes = { is_multiplier: PropTypes.bool, is_positions: PropTypes.bool, is_sell_requested: PropTypes.bool, + measure: PropTypes.func, onClickCancel: PropTypes.func, onClickSell: PropTypes.func, onFooterEntered: PropTypes.func, diff --git a/packages/components/src/components/contract-card/contract-card-items/contract-card-header.jsx b/packages/components/src/components/contract-card/contract-card-items/contract-card-header.jsx index eca978b4915e..48953bf84814 100644 --- a/packages/components/src/components/contract-card/contract-card-items/contract-card-header.jsx +++ b/packages/components/src/components/contract-card/contract-card-items/contract-card-header.jsx @@ -2,7 +2,7 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; import React from 'react'; import { CSSTransition } from 'react-transition-group'; -import { isHighLow, getCurrentTick, isBot } from '@deriv/shared'; +import { isHighLow, getCurrentTick, getGrowthRatePercentage, isBot, isAccumulatorContract } from '@deriv/shared'; import ContractTypeCell from './contract-type-cell.jsx'; import Button from '../../button'; import Icon from '../../icon'; @@ -10,6 +10,7 @@ import Text from '../../text'; import ProgressSlider from '../../progress-slider'; import DesktopWrapper from '../../desktop-wrapper'; import MobileWrapper from '../../mobile-wrapper'; +import TickCounterBar from './tick-counter-bar.jsx'; const ContractCardHeader = ({ contract_info, @@ -20,21 +21,48 @@ const ContractCardHeader = ({ id, is_mobile, is_sell_requested, + is_sold: is_contract_sold, is_valid_to_sell, onClickSell, server_time, }) => { const current_tick = contract_info.tick_count ? getCurrentTick(contract_info) : null; - const { underlying, multiplier, contract_type, shortcode, purchase_time, date_expiry, tick_count, is_sold } = - contract_info; + const { + growth_rate, + underlying, + multiplier, + contract_type, + shortcode, + purchase_time, + date_expiry, + tick_count, + tick_passed, + } = contract_info; const { is_pathname_bot } = isBot(); + const is_sold = !!contract_info.is_sold || is_contract_sold; + const is_accumulator = isAccumulatorContract(contract_type); + const contract_type_list_info = [ + { + is_param_displayed: multiplier, + displayed_param: `x${multiplier}`, + }, + { + is_param_displayed: is_accumulator, + displayed_param: `${getGrowthRatePercentage(growth_rate)}%`, + }, + ]; + const displayed_trade_param = + contract_type_list_info.find(contract_type_item_info => contract_type_item_info.is_param_displayed) + ?.displayed_param || ''; return ( <>
@@ -43,11 +71,16 @@ const ContractCardHeader = ({ {display_name || contract_info.display_name}
-
+
@@ -78,13 +111,20 @@ const ContractCardHeader = ({ ) : null} + {!is_sold && is_accumulator && ( + + )}
{(!has_progress_slider || !!is_sold) &&
} - {has_progress_slider && !is_sold && ( + {has_progress_slider && !is_sold && !is_accumulator && ( { +const ContractCardSell = ({ contract_info, getCardLabels, is_sell_requested, measure, onClickSell }) => { const is_valid_to_sell = isValidToSell(contract_info); const should_show_sell = hasContractEntered(contract_info) && isOpen(contract_info); @@ -14,6 +14,10 @@ const ContractCardSell = ({ contract_info, getCardLabels, is_sell_requested, onC ev.preventDefault(); }; + React.useEffect(() => { + measure?.(); + }, [should_show_sell, measure]); + return ( should_show_sell && ( @@ -39,6 +43,7 @@ ContractCardSell.propTypes = { contract_info: PropTypes.object, getCardLabels: PropTypes.func, is_sell_requested: PropTypes.any, + measure: PropTypes.func, onClickSell: PropTypes.func, }; diff --git a/packages/components/src/components/contract-card/contract-card-items/contract-type-cell.jsx b/packages/components/src/components/contract-card/contract-card-items/contract-type-cell.jsx index a5f2983e5e26..678b9a57c16d 100644 --- a/packages/components/src/components/contract-card/contract-card-items/contract-type-cell.jsx +++ b/packages/components/src/components/contract-card/contract-card-items/contract-type-cell.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { isVanillaContract } from '@deriv/shared'; -const ContractTypeCell = ({ getContractTypeDisplay, is_high_low, multiplier, type }) => ( +const ContractTypeCell = ({ displayed_trade_param, getContractTypeDisplay, is_high_low, type }) => (
{getContractTypeDisplay(type, is_high_low) || ''}
- {multiplier &&
x{multiplier}
} + {displayed_trade_param && ( +
{displayed_trade_param}
+ )}
); ContractTypeCell.propTypes = { + displayed_trade_param: PropTypes.string, getContractTypeDisplay: PropTypes.func, is_high_low: PropTypes.bool, - multiplier: PropTypes.number, type: PropTypes.string, }; diff --git a/packages/components/src/components/contract-card/contract-card-items/contract-update-form.jsx b/packages/components/src/components/contract-card/contract-card-items/contract-update-form.jsx index 6bd9097f114a..8d421265d63f 100644 --- a/packages/components/src/components/contract-card/contract-card-items/contract-update-form.jsx +++ b/packages/components/src/components/contract-card/contract-card-items/contract-update-form.jsx @@ -23,6 +23,7 @@ const ContractUpdateForm = props => { current_focus, error_message_alignment, getCardLabels, + is_accumulator, onMouseLeave, removeToast, setCurrentFocus, @@ -63,7 +64,9 @@ const ContractUpdateForm = props => { const is_take_profit_valid = has_contract_update_take_profit ? contract_update_take_profit > 0 : isValid(stop_loss); const is_stop_loss_valid = has_contract_update_stop_loss ? contract_update_stop_loss > 0 : isValid(take_profit); - const is_valid_contract_update = is_valid_to_cancel ? false : !!(is_take_profit_valid || is_stop_loss_valid); + const is_valid_accu_contract_update = is_accumulator && !!is_take_profit_valid; + const is_valid_contract_update = + is_valid_accu_contract_update || (is_valid_to_cancel ? false : !!(is_take_profit_valid || is_stop_loss_valid)); const getStateToCompare = _state => { const props_to_pick = [ @@ -116,7 +119,7 @@ const ContractUpdateForm = props => { onChange={onChange} error_message_alignment={error_message_alignment || 'right'} value={contract_profit_or_loss.contract_update_take_profit} - is_disabled={!!is_valid_to_cancel} + is_disabled={!is_accumulator && !!is_valid_to_cancel} setCurrentFocus={setCurrentFocus} /> ); @@ -173,9 +176,13 @@ const ContractUpdateForm = props => {
-
+
{take_profit_input}
-
{stop_loss_input}
+ {!is_accumulator &&
{stop_loss_input}
}