Skip to content

Commit 7b59c31

Browse files
authored
[DTRA] Maryia/DTRA-739/Add tooltips for Take profit & Stop loss in ContractUpdateForm (#13103)
* chore: add tooltips for take profit & stop loss * fix: styles and bug with apply button unnecessarily enabled * fix: currency styles in input * chore: get rid of non-existing callback connectWithContractUpdate * fix: total profit/loss update issue in contract update form on mobile * fix: tooltip styles for mobile * fix: type setCurrentFocus * revert: extra alignment of readme * test: add tests * fix: typo * feat: take profit text unification * chore: remove unused var
1 parent b37a9ff commit 7b59c31

File tree

20 files changed

+324
-303
lines changed

20 files changed

+324
-303
lines changed

packages/components/src/components/contract-card/contract-card-items/__tests__/accumulator-card-body.spec.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,25 @@ describe('<AccumulatorCardBody />', () => {
2929
setCurrentFocus: jest.fn(),
3030
currency: 'USD',
3131
removeToast: jest.fn(),
32+
totalProfit: 111,
3233
};
3334
it('should display all contract card items, label, and values', () => {
3435
render(<AccumulatorCardBody {...mock_props} />);
35-
expect(screen.getByText('Initial stake:')).toBeInTheDocument();
36+
expect(screen.getByText(getCardLabels().INITIAL_STAKE)).toBeInTheDocument();
3637
expect(screen.getByText('123.00')).toBeInTheDocument();
37-
expect(screen.getByText('Current stake:')).toBeInTheDocument();
38+
expect(screen.getByText(getCardLabels().CURRENT_STAKE)).toBeInTheDocument();
3839
expect(screen.getByText('234.00')).toBeInTheDocument();
39-
expect(screen.getByText('Total profit/loss:')).toBeInTheDocument();
40+
expect(screen.getByText(getCardLabels().TOTAL_PROFIT_LOSS)).toBeInTheDocument();
4041
expect(screen.getByText('111.00')).toBeInTheDocument();
41-
expect(screen.getByText('Take profit:')).toBeInTheDocument();
42+
expect(screen.getByText(getCardLabels().TAKE_PROFIT)).toBeInTheDocument();
4243
expect(screen.getByText('300.00')).toBeInTheDocument();
4344
});
4445

4546
it('should display Take profit: label and - as value when take_profit is not available', () => {
4647
if (mock_props?.contract_update?.take_profit?.order_amount)
4748
mock_props.contract_update.take_profit.order_amount = null;
4849
render(<AccumulatorCardBody {...mock_props} />);
49-
expect(screen.getByText('Take profit:')).toBeInTheDocument();
50+
expect(screen.getByText(getCardLabels().TAKE_PROFIT)).toBeInTheDocument();
5051
expect(screen.getByText('-')).toBeInTheDocument();
5152
});
5253

packages/components/src/components/contract-card/contract-card-items/__tests__/contract-update-form.spec.tsx

+33-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import React from 'react';
22
import { configure, render, screen } from '@testing-library/react';
33
import userEvent from '@testing-library/user-event';
4-
import { CONTRACT_TYPES, mockContractInfo, getCardLabels } from '@deriv/shared';
4+
import { CONTRACT_TYPES, mockContractInfo, getCardLabels, isMobile } from '@deriv/shared';
55
import ContractUpdateForm from '../contract-update-form';
66

7+
jest.mock('@deriv/shared', () => ({
8+
...jest.requireActual('@deriv/shared'),
9+
isMobile: jest.fn(() => false),
10+
}));
11+
712
const contract_info = mockContractInfo({
813
contract_id: 1,
914
contract_type: CONTRACT_TYPES.ACCUMULATOR,
@@ -43,11 +48,16 @@ describe('ContractUpdateForm', () => {
4348
removeToast: jest.fn(),
4449
setCurrentFocus: jest.fn(),
4550
toggleDialog: jest.fn(),
51+
totalProfit: 2.43,
4652
};
53+
const popoverTestid = 'dt_popover_wrapper';
4754
beforeAll(() => {
4855
el_modal.setAttribute('id', 'modal_root');
4956
document.body.appendChild(el_modal);
5057
});
58+
afterEach(() => {
59+
configure({ testIdAttribute: 'data-testid' });
60+
});
5161
afterAll(() => {
5262
document.body.removeChild(el_modal);
5363
});
@@ -165,8 +175,8 @@ describe('ContractUpdateForm', () => {
165175
userEvent.type(take_profit_input, '5');
166176
expect(new_props.contract.onChange).toHaveBeenCalled();
167177
});
168-
it(`should render unchecked Take profit & Stop loss inputs with checkboxes and disabled Apply button
169-
for Multipliers when neither take profit, nor stop loss is selected or applied`, () => {
178+
it(`should render unchecked Take profit & Stop loss checkboxes with popover icons, inputs and disabled Apply button
179+
for Multipliers when neither take profit, nor stop loss is selected or applied in desktop`, () => {
170180
const new_props = {
171181
...mock_props,
172182
contract: {
@@ -185,7 +195,27 @@ describe('ContractUpdateForm', () => {
185195
const apply_button = screen.getByRole('button', { name: getCardLabels().APPLY });
186196
expect(stop_loss_checkbox).not.toBeChecked();
187197
expect(take_profit_checkbox).not.toBeChecked();
198+
expect(screen.getAllByTestId(popoverTestid)).toHaveLength(2);
188199
expect(inputs).toHaveLength(2);
189200
expect(apply_button).toBeDisabled();
190201
});
202+
it('should render correct Total profit/loss, unchecked checkboxes without inputs & popover icons on mobile', () => {
203+
(isMobile as jest.Mock).mockReturnValue(true);
204+
const newProps = {
205+
...mock_props,
206+
contract: {
207+
...contract,
208+
contract_info: mockContractInfo({
209+
...contract_info,
210+
contract_type: CONTRACT_TYPES.MULTIPLIER.DOWN,
211+
}),
212+
},
213+
is_accumulator: false,
214+
};
215+
render(<ContractUpdateForm {...newProps} isMobile />);
216+
expect(screen.getByText(getCardLabels().TOTAL_PROFIT_LOSS)).toBeInTheDocument();
217+
expect(screen.getByText('2.43 USD')).toBeInTheDocument();
218+
expect(screen.getAllByTestId(popoverTestid)).toHaveLength(2);
219+
expect(screen.queryByRole('textbox')).not.toBeInTheDocument();
220+
});
191221
});

packages/components/src/components/contract-card/contract-card-items/__tests__/multiplier-card-body.spec.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ describe('MultiplierCardBody', () => {
4646
setCurrentFocus: jest.fn(),
4747
should_show_cancellation_warning: false,
4848
toggleCancellationWarning: jest.fn(),
49+
totalProfit: -0.44,
4950
};
5051
});
5152

@@ -61,6 +62,7 @@ describe('MultiplierCardBody', () => {
6162
};
6263

6364
it('should render the correct content for a Cancelled contract with Deal cancel.fee and negative Total profit/loss', () => {
65+
// @ts-expect-error Check if error is gone after migrating MultiplierCardBody to TS
6466
render(<MultiplierCardBody {...mock_props} />);
6567

6668
testCardContent();
@@ -76,7 +78,7 @@ describe('MultiplierCardBody', () => {
7678
mock_props.contract_info.status = 'open';
7779
mock_props.is_sold = false;
7880
delete mock_props.contract_info.sell_price;
79-
81+
// @ts-expect-error Check if error is gone after migrating MultiplierCardBody to TS
8082
render(<MultiplierCardBody {...mock_props} />);
8183

8284
testCardContent();
@@ -91,19 +93,22 @@ describe('MultiplierCardBody', () => {
9193
delete mock_props.contract_info.cancellation;
9294
delete mock_props.contract_info.sell_price;
9395

96+
// @ts-expect-error Check if error is gone after migrating MultiplierCardBody to TS
9497
render(<MultiplierCardBody {...mock_props} />);
9598

9699
expect(screen.getByText(mock_props.getCardLabels().NOT_AVAILABLE)).toBeInTheDocument();
97100
expect(screen.getByText(progress_slider)).toBeInTheDocument();
98101
});
99102

100103
it('should not render arrow indicator if the contract was sold (is_sold === true)', () => {
104+
// @ts-expect-error Check if error is gone after migrating MultiplierCardBody to TS
101105
render(<MultiplierCardBody {...mock_props} />);
102106

103107
expect(screen.queryByTestId('dt_arrow_indicator')).not.toBeInTheDocument();
104108
});
105109

106110
it('should render arrow indicator if the contract is not sold (is_sold === false)', () => {
111+
// @ts-expect-error Check if error is gone after migrating MultiplierCardBody to TS
107112
render(<MultiplierCardBody {...mock_props} is_sold={false} />);
108113

109114
expect(screen.getAllByTestId('dt_arrow_indicator')).not.toHaveLength(0);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import { render, screen } from '@testing-library/react';
4+
import userEvent from '@testing-library/user-event';
5+
import { getCardLabels } from '@deriv/shared';
6+
import ToggleCardDialog from '../toggle-card-dialog';
7+
8+
const contractUpdateForm = 'ContractUpdateForm';
9+
10+
jest.mock('../contract-update-form', () => jest.fn(() => <div>ContractUpdateForm</div>));
11+
jest.mock('../../../icon', () => jest.fn((props: { icon: string }) => <div>{props.icon}</div>));
12+
13+
describe('ToggleCardDialog', () => {
14+
const mockProps = {
15+
addToast: jest.fn(),
16+
contract_id: 1,
17+
current_focus: null,
18+
error_message_alignment: 'left',
19+
getCardLabels: () => getCardLabels(),
20+
getContractById: jest.fn(),
21+
is_valid_to_cancel: false,
22+
onMouseLeave: jest.fn(),
23+
removeToast: jest.fn(),
24+
setCurrentFocus: jest.fn(),
25+
totalProfit: 50,
26+
};
27+
beforeAll(() => {
28+
(ReactDOM.createPortal as jest.Mock) = jest.fn(component => {
29+
return component;
30+
});
31+
});
32+
afterAll(() => {
33+
(ReactDOM.createPortal as jest.Mock).mockClear();
34+
});
35+
it('should render ContractUpdateForm when edit icon is clicked', () => {
36+
render(<ToggleCardDialog {...mockProps} />);
37+
expect(screen.queryByText(contractUpdateForm)).not.toBeInTheDocument();
38+
const editIcon = screen.getByText('IcEdit');
39+
userEvent.click(editIcon);
40+
expect(screen.getByText(contractUpdateForm)).toBeInTheDocument();
41+
});
42+
it('should not render ContractUpdateForm when edit icon is clicked if is_valid_to_cancel === true', () => {
43+
render(<ToggleCardDialog {...mockProps} is_valid_to_cancel />);
44+
expect(screen.queryByText(contractUpdateForm)).not.toBeInTheDocument();
45+
const editIcon = screen.getByText('IcEdit');
46+
userEvent.click(editIcon);
47+
expect(screen.queryByText(contractUpdateForm)).not.toBeInTheDocument();
48+
});
49+
});

packages/components/src/components/contract-card/contract-card-items/__tests__/turbos-card-body.spec.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ const contract_info = mockContractInfo({
1717
describe('TurbosCardBody', () => {
1818
const mock_props = {
1919
addToast: jest.fn(),
20-
connectWithContractUpdate: jest.fn(),
2120
contract_info,
2221
contract_update: {
2322
take_profit: {
@@ -37,6 +36,7 @@ describe('TurbosCardBody', () => {
3736
setCurrentFocus: jest.fn(),
3837
status: 'profit',
3938
progress_slider_mobile_el: false,
39+
totalProfit: 50,
4040
};
4141
beforeAll(() => {
4242
(ReactDOM.createPortal as jest.Mock) = jest.fn(component => {

packages/components/src/components/contract-card/contract-card-items/accumulator-card-body.tsx

+4-18
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import ArrowIndicator from '../../arrow-indicator';
1414

1515
type TAccumulatorCardBody = {
1616
addToast: (toast_config: TToastConfig) => void;
17-
connectWithContractUpdate?: React.ComponentProps<typeof ToggleCardDialog>['connectWithContractUpdate'];
1817
contract_info: TContractInfo;
1918
contract_update?: ContractUpdate;
2019
currency: Required<TContractInfo>['currency'];
@@ -26,26 +25,20 @@ type TAccumulatorCardBody = {
2625
is_sold: boolean;
2726
onMouseLeave?: () => void;
2827
removeToast: (toast_id: string) => void;
29-
setCurrentFocus: (value: string) => void;
28+
setCurrentFocus: (value: string | null) => void;
29+
totalProfit: number;
3030
is_positions?: boolean;
3131
};
3232

3333
const AccumulatorCardBody = ({
34-
addToast,
35-
connectWithContractUpdate,
3634
contract_info,
3735
contract_update,
3836
currency,
39-
current_focus,
40-
error_message_alignment,
4137
getCardLabels,
42-
getContractById,
4338
indicative,
4439
is_sold,
45-
onMouseLeave,
46-
removeToast,
47-
setCurrentFocus,
4840
is_positions,
41+
...toggle_card_dialog_props
4942
}: TAccumulatorCardBody) => {
5043
const { buy_price, profit, limit_order, sell_price } = contract_info;
5144
const { take_profit } = getLimitOrderAmount(contract_update || limit_order);
@@ -92,17 +85,10 @@ const AccumulatorCardBody = ({
9285
{take_profit ? <Money amount={take_profit} currency={currency} /> : <strong>-</strong>}
9386
{is_valid_to_sell && (
9487
<ToggleCardDialog
95-
addToast={addToast}
96-
connectWithContractUpdate={connectWithContractUpdate}
9788
contract_id={contract_info.contract_id}
98-
current_focus={current_focus}
99-
error_message_alignment={error_message_alignment}
10089
getCardLabels={getCardLabels}
101-
getContractById={getContractById}
10290
is_accumulator
103-
onMouseLeave={onMouseLeave}
104-
removeToast={removeToast}
105-
setCurrentFocus={setCurrentFocus}
91+
{...toggle_card_dialog_props}
10692
/>
10793
)}
10894
</ContractCardItem>

packages/components/src/components/contract-card/contract-card-items/contract-card-body.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import classNames from 'classnames';
3-
import { isCryptocurrency, getIndicativePrice, getCurrentTick, getDisplayStatus } from '@deriv/shared';
3+
import { isCryptocurrency, getIndicativePrice, getCurrentTick, getDisplayStatus, getTotalProfit } from '@deriv/shared';
44
import ContractCardItem from './contract-card-item';
55
import CurrencyBadge from '../../currency-badge';
66
import DesktopWrapper from '../../desktop-wrapper';
@@ -25,7 +25,6 @@ export type TContractCardBodyProps = {
2525

2626
const ContractCardBody = ({
2727
addToast,
28-
connectWithContractUpdate,
2928
contract_info,
3029
contract_update,
3130
currency,
@@ -68,13 +67,13 @@ const ContractCardBody = ({
6867

6968
const toggle_card_dialog_props = {
7069
addToast,
71-
connectWithContractUpdate,
7270
current_focus,
7371
error_message_alignment,
7472
getContractById,
7573
onMouseLeave,
7674
removeToast,
7775
setCurrentFocus,
76+
totalProfit: is_multiplier && !isNaN(Number(profit)) ? getTotalProfit(contract_info) : Number(profit),
7877
};
7978

8079
let card_body;

0 commit comments

Comments
 (0)