-
Notifications
You must be signed in to change notification settings - Fork 319
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DTRA] Henry/dtra 1443/market closed message v2 (#16196)
* fix: stop scrolling to center on symbol selection for all category * fix: flex-start tabs list * fix: market is closed message * fix: type error * fix: add tests * fix: add tests * fix: add a mock for closedmarketmessage * fix: remove unused import * fix: comment
- Loading branch information
1 parent
56dea63
commit f2a33e4
Showing
18 changed files
with
457 additions
and
5 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
26 changes: 26 additions & 0 deletions
26
packages/trader/src/AppV2/Components/ClosedMarketMessage/closed-market-message.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,26 @@ | ||
.closed-market-message { | ||
&--container { | ||
display: flex; | ||
width: 100%; | ||
padding: var(--core-spacing-800); | ||
justify-content: space-between; | ||
align-items: center; | ||
background-color: var(--semantic-color-slate-solid-surface-inverse-highest); | ||
z-index: 3; | ||
bottom: 0; | ||
position: sticky; | ||
} | ||
&--left { | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
gap: var(--core-spacing-200); | ||
|
||
&-message { | ||
color: var(--semantic-color-slate-solid-surface-normal-lowest); | ||
} | ||
} | ||
&--loading { | ||
display: none; | ||
} | ||
} |
127 changes: 127 additions & 0 deletions
127
packages/trader/src/AppV2/Components/ClosedMarketMessage/closed-market-message.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,127 @@ | ||
import React from 'react'; | ||
import { useIsMounted, WS, isMarketClosed, toMoment } from '@deriv/shared'; | ||
import { Localize } from '@deriv/translations'; | ||
import { observer, useStore } from '@deriv/stores'; | ||
import { useTraderStore } from 'Stores/useTraderStores'; | ||
import { TradingTimesRequest } from '@deriv/api-types'; | ||
import useActiveSymbols from 'AppV2/Hooks/useActiveSymbols'; | ||
import MarketOpeningTimeBanner from '../MarketOpeningTimeBanner'; | ||
import MarketCountdownTimer from '../MarketCountdownTimer'; | ||
import { CaptionText } from '@deriv-com/quill-ui'; | ||
import clsx from 'clsx'; | ||
import { calculateTimeLeft, getSymbol } from 'AppV2/Utils/closed-market-message-utils'; | ||
|
||
type TWhenMarketOpens = { | ||
days_offset: number; | ||
opening_time: string; | ||
remaining_time_to_open: number; | ||
}; | ||
|
||
const days_to_check_before_exit = 7; | ||
|
||
const getTradingTimes = async (target_time: TradingTimesRequest['trading_times']) => { | ||
const data = await WS.tradingTimes(target_time); | ||
if (data.error) { | ||
return { api_initial_load_error: data.error.message }; | ||
} | ||
return data; | ||
}; | ||
|
||
const ClosedMarketMessage = observer(() => { | ||
const { common } = useStore(); | ||
const { current_language } = common; | ||
const { symbol, prepareTradeStore } = useTraderStore(); | ||
const { activeSymbols } = useActiveSymbols({}); | ||
|
||
const isMounted = useIsMounted(); | ||
const [when_market_opens, setWhenMarketOpens] = React.useState<TWhenMarketOpens>({} as TWhenMarketOpens); | ||
const [time_left, setTimeLeft] = React.useState(calculateTimeLeft(when_market_opens?.remaining_time_to_open)); | ||
const [is_loading, setLoading] = React.useState(true); | ||
|
||
React.useEffect(() => { | ||
if (isMarketClosed(activeSymbols, symbol)) { | ||
setLoading(true); | ||
const whenMarketOpens = async ( | ||
days_offset: number, | ||
target_symbol: string | ||
): Promise<void | Record<string, unknown>> => { | ||
if (days_offset > days_to_check_before_exit) return {}; | ||
let remaining_time_to_open; | ||
const target_date = toMoment(new Date()).add(days_offset, 'days'); | ||
const api_response = await getTradingTimes(target_date.format('YYYY-MM-DD')); | ||
if (!api_response.api_initial_load_error) { | ||
const returned_symbol = getSymbol(target_symbol, api_response.trading_times); | ||
const open = returned_symbol?.times.open as string[]; | ||
const close = returned_symbol?.times.close as string[]; | ||
const is_closed_all_day = open?.length === 1 && open[0] === '--' && close[0] === '--'; | ||
if (is_closed_all_day) { | ||
return whenMarketOpens(days_offset + 1, target_symbol); | ||
} | ||
const date_str = target_date.toISOString().substring(0, 11); | ||
const getUTCDate = (hour: string) => new Date(`${date_str}${hour}Z`); | ||
for (let i = 0; i < open?.length; i++) { | ||
const diff = +getUTCDate(open[i]) - Date.now(); | ||
if (diff > 0) { | ||
remaining_time_to_open = +getUTCDate(open[i]); | ||
if (isMounted() && target_symbol === symbol) { | ||
return setWhenMarketOpens({ | ||
days_offset, | ||
opening_time: open[i], | ||
remaining_time_to_open, | ||
}); | ||
} | ||
} | ||
} | ||
whenMarketOpens(days_offset + 1, target_symbol); | ||
} | ||
}; | ||
|
||
whenMarketOpens(0, symbol); | ||
} | ||
setTimeLeft({}); | ||
setWhenMarketOpens({} as TWhenMarketOpens); | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [symbol, activeSymbols]); | ||
|
||
React.useEffect(() => { | ||
let timer: ReturnType<typeof setTimeout>; | ||
if (when_market_opens?.remaining_time_to_open) { | ||
timer = setTimeout(() => { | ||
setTimeLeft(calculateTimeLeft(when_market_opens.remaining_time_to_open)); | ||
if (+new Date(when_market_opens.remaining_time_to_open) - +new Date() < 1000) { | ||
setLoading(true); | ||
prepareTradeStore(false); | ||
} | ||
}, 1000); | ||
} | ||
return () => { | ||
if (timer) { | ||
clearTimeout(timer); | ||
} | ||
}; | ||
}, [time_left, when_market_opens, prepareTradeStore]); | ||
|
||
if (!(when_market_opens && Object.keys(time_left).length)) return null; | ||
|
||
const { opening_time, days_offset } = when_market_opens; | ||
|
||
if (is_loading) setLoading(false); | ||
|
||
return ( | ||
<div className={clsx('closed-market-message--container', { 'closed-market-message--loading': is_loading })}> | ||
<div className='closed-market-message--left'> | ||
<CaptionText className='closed-market-message--left-message'> | ||
<Localize i18n_default_text='This market will reopen at' /> | ||
</CaptionText> | ||
<MarketOpeningTimeBanner | ||
opening_time={opening_time} | ||
days_offset={days_offset} | ||
current_language={current_language} | ||
/> | ||
</div> | ||
<MarketCountdownTimer time_left={time_left} /> | ||
</div> | ||
); | ||
}); | ||
|
||
export default ClosedMarketMessage; |
4 changes: 4 additions & 0 deletions
4
packages/trader/src/AppV2/Components/ClosedMarketMessage/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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import './closed-market-message.scss'; | ||
import ClosedMarketMessage from './closed-market-message'; | ||
|
||
export default ClosedMarketMessage; |
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
22 changes: 22 additions & 0 deletions
22
...rader/src/AppV2/Components/MarketCountdownTimer/__tests__/market-countdown-timer.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,22 @@ | ||
import React from 'react'; | ||
import { screen, render } from '@testing-library/react'; | ||
import '@testing-library/jest-dom/extend-expect'; // for the `toBeInTheDocument` matcher | ||
import MarketCountdownTimer from '..'; | ||
|
||
describe('MarketCountDownTimer', () => { | ||
it('renders correctly with given time left', () => { | ||
render(<MarketCountdownTimer time_left={{ days: 1, hours: 2, minutes: 3, seconds: 4 }} />); | ||
|
||
expect(screen.getByText('26:03:04')).toBeInTheDocument(); | ||
}); | ||
it('renders correctly when there is no time left', () => { | ||
render(<MarketCountdownTimer time_left={{ days: 0, hours: 0, minutes: 0, seconds: 0 }} />); | ||
|
||
expect(screen.getByText('00:00:00')).toBeInTheDocument(); | ||
}); | ||
it('renders correctly with edge case values', () => { | ||
render(<MarketCountdownTimer time_left={{ days: 0, hours: 1, minutes: 1, seconds: 1 }} />); | ||
|
||
expect(screen.getByText('01:01:01')).toBeInTheDocument(); | ||
}); | ||
}); |
4 changes: 4 additions & 0 deletions
4
packages/trader/src/AppV2/Components/MarketCountdownTimer/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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import './market-countdown-timer.scss'; | ||
import MarketCountdownTimer from './market-countdown-timer'; | ||
|
||
export default MarketCountdownTimer; |
8 changes: 8 additions & 0 deletions
8
packages/trader/src/AppV2/Components/MarketCountdownTimer/market-countdown-timer.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,8 @@ | ||
.market-countdown-timer { | ||
display: flex; | ||
gap: var(--core-spacing-200); | ||
|
||
&-text { | ||
color: var(--semantic-color-slate-solid-surface-normal-lowest); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
packages/trader/src/AppV2/Components/MarketCountdownTimer/market-countdown-timer.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,34 @@ | ||
import React from 'react'; | ||
import { Text } from '@deriv-com/quill-ui'; | ||
import { LabelPairedStopwatchMdBoldIcon } from '@deriv/quill-icons'; | ||
|
||
type TMarketCountDownTimer = { | ||
time_left: { | ||
days?: number; | ||
hours?: number; | ||
minutes?: number; | ||
seconds?: number; | ||
}; | ||
}; | ||
|
||
const MarketCountdownTimer = ({ time_left }: TMarketCountDownTimer) => { | ||
let timer_components = ''; | ||
|
||
if (Object.keys(time_left).length) { | ||
const hours = (Number(time_left.days) * 24 + Number(time_left.hours)).toString().padStart(2, '0'); | ||
const minutes = Number(time_left.minutes).toString().padStart(2, '0'); | ||
const seconds = Number(time_left.seconds).toString().padStart(2, '0'); | ||
timer_components = `${hours}:${minutes}:${seconds}`; | ||
} | ||
|
||
return ( | ||
<div className='market-countdown-timer'> | ||
<LabelPairedStopwatchMdBoldIcon fill='var(--semantic-color-slate-solid-surface-normal-lowest)' /> | ||
<Text bold size='md' className='market-countdown-timer-text'> | ||
{timer_components} | ||
</Text> | ||
</div> | ||
); | ||
}; | ||
|
||
export default MarketCountdownTimer; |
20 changes: 20 additions & 0 deletions
20
...rc/AppV2/Components/MarketOpeningTimeBanner/__tests__/market-opening-time-banner.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,20 @@ | ||
import React from 'react'; | ||
import { screen, render } from '@testing-library/react'; | ||
import MarketOpeningTimeBanner from '../market-opening-time-banner'; | ||
|
||
jest.mock('@deriv/shared', () => ({ | ||
convertTimeFormat: jest.fn(time => `Formatted: ${time}`), | ||
toMoment: jest.fn(() => ({ | ||
locale: jest.fn().mockReturnThis(), | ||
add: jest.fn().mockReturnThis(), | ||
format: jest.fn(() => '01 Jan 2024'), | ||
})), | ||
})); | ||
|
||
describe('<MarketOpeningTimeBanner />', () => { | ||
it('renders with correct formatted time and date', () => { | ||
render(<MarketOpeningTimeBanner opening_time='12:34' days_offset={1} current_language='en' />); | ||
|
||
expect(screen.getByText('Formatted: 12:34 (GMT), 01 Jan 2024')).toBeInTheDocument(); | ||
}); | ||
}); |
4 changes: 4 additions & 0 deletions
4
packages/trader/src/AppV2/Components/MarketOpeningTimeBanner/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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import './market-opening-time-banner.scss'; | ||
import MarketOpeningTimeBanner from './market-opening-time-banner'; | ||
|
||
export default MarketOpeningTimeBanner; |
3 changes: 3 additions & 0 deletions
3
packages/trader/src/AppV2/Components/MarketOpeningTimeBanner/market-opening-time-banner.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,3 @@ | ||
.market-opening-time-banner { | ||
color: var(--semantic-color-slate-solid-surface-normal-lowest); | ||
} |
30 changes: 30 additions & 0 deletions
30
packages/trader/src/AppV2/Components/MarketOpeningTimeBanner/market-opening-time-banner.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,30 @@ | ||
import React from 'react'; | ||
import { convertTimeFormat, toMoment } from '@deriv/shared'; | ||
import { Localize } from '@deriv/translations'; | ||
import { CaptionText } from '@deriv-com/quill-ui'; | ||
|
||
type TMarketOpeningTimeBanner = { | ||
opening_time: string; | ||
days_offset: number; | ||
current_language: string; | ||
}; | ||
|
||
const MarketOpeningTimeBanner = ({ opening_time, days_offset, current_language }: TMarketOpeningTimeBanner) => { | ||
const formatted_opening_time = convertTimeFormat(opening_time); | ||
const target_date = toMoment(new Date()).locale(current_language.toLowerCase()).add(days_offset, 'days'); | ||
const opening_date = target_date.format('DD MMM YYYY'); | ||
|
||
return ( | ||
<CaptionText bold className='market-opening-time-banner'> | ||
<Localize | ||
i18n_default_text='{{formatted_opening_time}} (GMT), {{opening_date}}' | ||
values={{ | ||
formatted_opening_time, | ||
opening_date, | ||
}} | ||
/> | ||
</CaptionText> | ||
); | ||
}; | ||
|
||
export default MarketOpeningTimeBanner; |
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
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
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
Oops, something went wrong.