forked from deriv-com/deriv-app
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DTRA-1474 / Kate / Implement Payout per point trade param functionali…
…ty (deriv-com#16783) * feat: create payout per point initial component * refactor: add initial values to localstorage * refactor: add tests * refactor: show unchanged value in the trade param
- Loading branch information
1 parent
01ad785
commit e2270e8
Showing
7 changed files
with
362 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
...r/src/AppV2/Components/TradeParameters/PayoutPerPoint/__tests__/payout-per-point.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import React from 'react'; | ||
import { render, screen, waitFor } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { mockStore } from '@deriv/stores'; | ||
import ModulesProvider from 'Stores/Providers/modules-providers'; | ||
import TraderProviders from '../../../../../trader-providers'; | ||
import PayoutPerPoint from '../payout-per-point'; | ||
|
||
const payout_per_point_label = 'Payout per point'; | ||
|
||
jest.mock('@deriv-com/quill-ui', () => ({ | ||
...jest.requireActual('@deriv-com/quill-ui'), | ||
WheelPicker: jest.fn(({ data, setSelectedValue }) => ( | ||
<div> | ||
<p>WheelPicker</p> | ||
<ul> | ||
{data.map(({ value }: { value: string }) => ( | ||
<li key={value}> | ||
<button onClick={() => setSelectedValue(value)}>{value}</button> | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
)), | ||
})); | ||
|
||
jest.mock('lodash.debounce', () => | ||
jest.fn(fn => { | ||
fn.cancel = () => null; | ||
return fn; | ||
}) | ||
); | ||
|
||
describe('PayoutPerPoint', () => { | ||
let default_mock_store: ReturnType<typeof mockStore>; | ||
|
||
beforeEach( | ||
() => | ||
(default_mock_store = mockStore({ | ||
modules: { | ||
trade: { | ||
...mockStore({}), | ||
barrier_1: '+1.80', | ||
payout_choices: ['6', '5', '4', '3', '2', '1'], | ||
currency: 'USD', | ||
payout_per_point: '3', | ||
}, | ||
}, | ||
})) | ||
); | ||
|
||
afterEach(() => jest.clearAllMocks()); | ||
|
||
const mockPayoutPerPoint = () => | ||
render( | ||
<TraderProviders store={default_mock_store}> | ||
<ModulesProvider store={default_mock_store}> | ||
<PayoutPerPoint is_minimized /> | ||
</ModulesProvider> | ||
</TraderProviders> | ||
); | ||
it('should render Skeleton loader if payout_per_point is falsy', () => { | ||
default_mock_store.modules.trade.payout_per_point = ''; | ||
mockPayoutPerPoint(); | ||
|
||
expect(screen.getByTestId('dt_skeleton')).toBeInTheDocument(); | ||
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', () => { | ||
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', () => { | ||
mockPayoutPerPoint(); | ||
|
||
expect(screen.queryByTestId('dt-actionsheet-overlay')).not.toBeInTheDocument(); | ||
|
||
userEvent.click(screen.getByText(payout_per_point_label)); | ||
|
||
expect(screen.getByTestId('dt-actionsheet-overlay')).toBeInTheDocument(); | ||
expect(screen.getByText('WheelPicker')).toBeInTheDocument(); | ||
expect(screen.getByText('Barrier')).toBeInTheDocument(); | ||
expect(screen.getByText('+1.80')).toBeInTheDocument(); | ||
expect(screen.getByText('Save')).toBeInTheDocument(); | ||
expect( | ||
screen.getByText( | ||
'The amount you choose to receive at expiry for every point of change between the final price and the barrier.' | ||
) | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it('should not render barrier information if barrier is not defined', () => { | ||
default_mock_store.modules.trade.barrier_1 = undefined; | ||
mockPayoutPerPoint(); | ||
|
||
userEvent.click(screen.getByText(payout_per_point_label)); | ||
|
||
expect(screen.getByText('Barrier')).toBeInTheDocument(); | ||
expect(screen.queryByText('+1.80')).not.toBeInTheDocument(); | ||
}); | ||
|
||
it('should apply specific className if innerHeight is <= 640px', () => { | ||
const original_height = window.innerHeight; | ||
window.innerHeight = 640; | ||
mockPayoutPerPoint(); | ||
|
||
userEvent.click(screen.getByText(payout_per_point_label)); | ||
|
||
expect(screen.getByTestId('dt_carousel')).toHaveClass('payout-per-point__carousel--small'); | ||
window.innerHeight = original_height; | ||
}); | ||
|
||
it('should call setPayoutPerPoint function if user changes selected value', async () => { | ||
jest.useFakeTimers(); | ||
mockPayoutPerPoint(); | ||
|
||
const new_selected_value = default_mock_store.modules.trade.payout_choices[1]; | ||
userEvent.click(screen.getByText(payout_per_point_label)); | ||
userEvent.click(screen.getByText(new_selected_value)); | ||
userEvent.click(screen.getByText('Save')); | ||
|
||
await waitFor(() => jest.advanceTimersByTime(200)); | ||
|
||
expect(default_mock_store.modules.trade.setPayoutPerPoint).toBeCalled(); | ||
jest.useRealTimers(); | ||
}); | ||
}); |
1 change: 1 addition & 0 deletions
1
packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import './payout-per-point.scss'; | ||
import PayoutPerPoint from './payout-per-point'; | ||
|
||
export default PayoutPerPoint; |
87 changes: 87 additions & 0 deletions
87
...ges/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/payout-per-point-wheel.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import React from 'react'; | ||
import debounce from 'lodash.debounce'; | ||
import { ActionSheet, Text, WheelPicker } from '@deriv-com/quill-ui'; | ||
import { Skeleton } from '@deriv/components'; | ||
import { Localize } from '@deriv/translations'; | ||
import type { TV2ParamsInitialValues } from 'Stores/Modules/Trading/trade-store'; | ||
|
||
type TPayoutPerPointWheelProps = { | ||
barrier?: string | number; | ||
current_payout_per_point: string; | ||
onPayoutPerPointSelect: (new_value: string | number) => void; | ||
payout_per_point_list: { | ||
value: string; | ||
}[]; | ||
setV2ParamsInitialValues: ({ value, name }: { value: number | string; name: keyof TV2ParamsInitialValues }) => void; | ||
}; | ||
|
||
const onWheelPickerScrollDebounced = debounce( | ||
(new_value: string | number, callback: TPayoutPerPointWheelProps['onPayoutPerPointSelect']) => callback(new_value), | ||
200 | ||
); | ||
|
||
const PayoutPerPointWheel = ({ | ||
barrier, | ||
current_payout_per_point, | ||
onPayoutPerPointSelect, | ||
setV2ParamsInitialValues, | ||
payout_per_point_list, | ||
}: TPayoutPerPointWheelProps) => { | ||
const initial_value_ref = React.useRef<string | number>(); | ||
const selected_value_ref = React.useRef<string | number>(current_payout_per_point); | ||
|
||
const onSave = () => { | ||
initial_value_ref.current = selected_value_ref.current; | ||
setV2ParamsInitialValues({ value: selected_value_ref.current, name: 'payout_per_point' }); | ||
}; | ||
|
||
React.useEffect(() => { | ||
if (!initial_value_ref.current && current_payout_per_point) { | ||
initial_value_ref.current = current_payout_per_point; | ||
setV2ParamsInitialValues({ value: current_payout_per_point, name: 'payout_per_point' }); | ||
} | ||
|
||
return () => { | ||
if (initial_value_ref.current && initial_value_ref.current !== selected_value_ref.current) { | ||
onPayoutPerPointSelect(initial_value_ref.current); | ||
} | ||
onWheelPickerScrollDebounced.cancel(); | ||
}; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, []); | ||
|
||
return ( | ||
<React.Fragment> | ||
<ActionSheet.Content className='payout-per-point__wrapper' data-testid='dt_payout-per-point_wrapper'> | ||
<div className='payout-per-point__wheel-picker'> | ||
<WheelPicker | ||
data={payout_per_point_list} | ||
selectedValue={selected_value_ref.current} | ||
setSelectedValue={(new_value: string | number) => { | ||
if (new_value === selected_value_ref.current) return; | ||
selected_value_ref.current = new_value; | ||
onWheelPickerScrollDebounced(new_value, onPayoutPerPointSelect); | ||
}} | ||
/> | ||
</div> | ||
<div className='payout-per-point__barrier'> | ||
<Text color='quill-typography__color--subtle' size='sm'> | ||
<Localize i18n_default_text='Barrier' /> | ||
</Text> | ||
<Text size='sm' as='div' className='payout-per-point__barrier__content'> | ||
{barrier ?? <Skeleton width={90} height={14} />} | ||
</Text> | ||
</div> | ||
</ActionSheet.Content> | ||
<ActionSheet.Footer | ||
alignment='vertical' | ||
primaryAction={{ | ||
content: <Localize i18n_default_text='Save' />, | ||
onAction: onSave, | ||
}} | ||
/> | ||
</React.Fragment> | ||
); | ||
}; | ||
|
||
export default PayoutPerPointWheel; |
51 changes: 51 additions & 0 deletions
51
packages/trader/src/AppV2/Components/TradeParameters/PayoutPerPoint/payout-per-point.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
$HANDLEBAR_HEIGHT: var(--core-size-1000); | ||
$HEADER_TITLE_HEIGHT: var(--core-size-3200); | ||
$FOOTER_BUTTON_HEIGHT: var(--core-size-4000); | ||
|
||
.payout-per-point { | ||
&__carousel { | ||
& > .carousel__item { | ||
height: 100%; | ||
} | ||
.trade-param-definition { | ||
height: calc(480px - $HANDLEBAR_HEIGHT - $HEADER_TITLE_HEIGHT); | ||
} | ||
&--small { | ||
.payout-per-point__wrapper { | ||
height: calc(90dvh - $HANDLEBAR_HEIGHT - $HEADER_TITLE_HEIGHT - $FOOTER_BUTTON_HEIGHT); | ||
max-height: calc(480px - $HANDLEBAR_HEIGHT - $HEADER_TITLE_HEIGHT - $FOOTER_BUTTON_HEIGHT); | ||
} | ||
.trade-param-definition { | ||
height: calc(90dvh - $HANDLEBAR_HEIGHT - $HEADER_TITLE_HEIGHT); | ||
max-height: calc(480px - $HANDLEBAR_HEIGHT - $HEADER_TITLE_HEIGHT); | ||
} | ||
} | ||
} | ||
|
||
&__wrapper { | ||
height: calc(480px - $HANDLEBAR_HEIGHT - $HEADER_TITLE_HEIGHT - $FOOTER_BUTTON_HEIGHT); | ||
padding-block: 0; | ||
display: flex; | ||
flex-direction: column; | ||
gap: var(--core-spacing-400); | ||
} | ||
|
||
&__wheel-picker { | ||
flex-grow: 1; | ||
min-height: 0; | ||
} | ||
|
||
&__barrier { | ||
min-height: var(--core-size-2200); | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
|
||
&__content { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
flex-grow: 1; | ||
} | ||
} | ||
} |
Oops, something went wrong.