Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

henry/dtra-1563/fix: asset/symbol selection bug fix #16333

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8ee67ac
fix: make market-category-item clickable area bigger
henry-deriv Aug 2, 2024
fe85b05
fix: correctly sort market categories
henry-deriv Aug 2, 2024
be7901d
fix: automatic scroll to center on all tab
henry-deriv Aug 5, 2024
2ce1707
fix: update quill-ui packages
henry-deriv Aug 5, 2024
8fd0f88
fix: remove searchbar hiding when scrolling down
henry-deriv Aug 5, 2024
3b45b4b
fix: add role to get rid of sonarcloud error
henry-deriv Aug 5, 2024
2646ffb
fix: remove snackbar on scroll demo
henry-deriv Aug 6, 2024
60d1a9d
fix: remove dismiss snackbar on scroll
henry-deriv Aug 6, 2024
5cacfef
fix: code smell
henry-deriv Aug 6, 2024
f91fd3e
Merge branch 'master' of github.com:binary-com/deriv-app into henry/d…
henry-deriv Aug 6, 2024
be9bb88
fix: convert span to button
henry-deriv Aug 6, 2024
a6d00f4
fix: handle accessibility
henry-deriv Aug 6, 2024
f93d057
fix: remove display name
henry-deriv Aug 6, 2024
4ce17fe
fix: remove unused imports
henry-deriv Aug 6, 2024
b682fef
fix: turn element into div
henry-deriv Aug 6, 2024
dec5ac2
fix: remove unused imports
henry-deriv Aug 6, 2024
280061d
fix: add keyboard event handler
henry-deriv Aug 6, 2024
a60919d
fix: build fail and refactor clickkeyboardeventhandler
henry-deriv Aug 7, 2024
03cc033
fix: build error
henry-deriv Aug 7, 2024
de175a9
fix: merge conflict
henry-deriv Aug 7, 2024
01999dd
fix: merge conflict
henry-deriv Aug 13, 2024
fa103ba
fix: scroll to center
henry-deriv Aug 14, 2024
3a4247d
fix: remove extra active_symbol calls for v2
henry-deriv Aug 14, 2024
140a3d6
fix: failing tests
henry-deriv Aug 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const ActiveSymbolsList = observer(({ isOpen, setIsOpen }: TActiveSymbolsList) =
<ActionSheet.Root isOpen={isOpen} onClose={() => setIsOpen(false)}>
<ActionSheet.Portal shouldCloseOnDrag fullHeightOnOpen>
<SymbolsSearchField
marketCategoriesRef={marketCategoriesRef}
searchValue={searchValue}
setSearchValue={setSearchValue}
isSearching={isSearching}
Expand Down Expand Up @@ -62,8 +61,8 @@ const ActiveSymbolsList = observer(({ isOpen, setIsOpen }: TActiveSymbolsList) =
selectedSymbol={selectedSymbol}
setSelectedSymbol={setSelectedSymbol}
setIsOpen={setIsOpen}
ref={marketCategoriesRef}
isOpen={isOpen}
marketCategoriesRef={marketCategoriesRef}
/>
)}
</Tab.Container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ describe('<MarketCategories />', () => {
setSelectedSymbol: jest.fn(),
setIsOpen: jest.fn(),
isOpen: false,
marketCategoriesRef: {
current: document.createElement('div'),
},
};
beforeEach(() => {
jest.clearAllMocks();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
import React, { forwardRef, Ref } from 'react';
import React from 'react';
import { Tab } from '@deriv-com/quill-ui';
import useActiveSymbols from 'AppV2/Hooks/useActiveSymbols';
import { categorizeSymbols } from 'AppV2/Utils/symbol-categories-utils';
import MarketCategory from '../MarketCategory';
import MarketCategoryTab from '../MarketCategoryTab/market-category-tab';
import { observer } from '@deriv/stores';

type TMarketCategories = {
selectedSymbol: string;
setSelectedSymbol: (input: string) => void;
setIsOpen: (input: boolean) => void;
isOpen: boolean;
marketCategoriesRef: React.RefObject<HTMLDivElement>;
};

const MarketCategories = forwardRef(
({ selectedSymbol, setSelectedSymbol, setIsOpen, isOpen }: TMarketCategories, ref: Ref<HTMLDivElement>) => {
const MarketCategories = observer(
({ selectedSymbol, setSelectedSymbol, setIsOpen, isOpen, marketCategoriesRef }: TMarketCategories) => {
const { activeSymbols } = useActiveSymbols();
const categorizedSymbols = categorizeSymbols(activeSymbols);

return (
<React.Fragment>
<Tab.List>
{Object.values(categorizedSymbols).map(category => (
<MarketCategoryTab key={category.market} category={category} />
))}
</Tab.List>
<Tab.Content className='market-categories__list' ref={ref}>
<Tab.Content className='market-categories__list' ref={marketCategoriesRef}>
{Object.values(categorizedSymbols).map(category => (
<MarketCategory
key={category.market}
Expand All @@ -40,6 +43,4 @@ const MarketCategories = forwardRef(
}
);

MarketCategories.displayName = 'MarketCategories';

export default MarketCategories;
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@ const MarketCategory = ({ category, selectedSymbol, setSelectedSymbol, setIsOpen
const prevSymbol = usePrevious(selectedSymbol);

useEffect(() => {
if (
isOpen &&
category.market === 'all' &&
selectedSymbol &&
itemRefs.current[selectedSymbol] &&
prevSymbol === selectedSymbol
) {
if (isOpen && category.market === 'all' && selectedSymbol && itemRefs.current[selectedSymbol] && !prevSymbol) {
timerRef.current = setTimeout(() => {
itemRefs.current[selectedSymbol]?.scrollIntoView({ block: 'center' });
}, 50);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@
&--selected {
background-color: var(--semantic-color-slate-solid-surface-inverse-lowest);
}
&-left {
display: flex;
flex: 1 0 0;
align-items: center;
gap: var(--semantic-spacing-gap-lg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import clsx from 'clsx';
import { observer, useStore } from '@deriv/stores';
import { useTraderStore } from 'Stores/useTraderStores';
import { useModulesStore } from 'Stores/useModulesStores';
import { clickAndKeyEventHandler } from '@deriv/shared';

type TMarketCategoryItem = {
item: ActiveSymbols[0];
Expand All @@ -30,15 +31,13 @@ const MarketCategoryItem = forwardRef(
setIsFavorite(favoriteSymbols.includes(item.symbol));
}, [favoriteSymbols, item.symbol]);

const handleSelect = async (e: React.MouseEvent<HTMLSpanElement>) => {
const symbol = (e.target as HTMLSpanElement).getAttribute('data-symbol');
setSelectedSymbol(symbol ?? '');
const handleSelect = async (symbol: string) => {
setSelectedSymbol(symbol);
await onSymbolChange({ target: { name: 'symbol', value: symbol } });
setIsOpen(false);
};

const toggleFavorites = (e: React.MouseEvent<HTMLSpanElement>) => {
const symbol = (e.currentTarget as HTMLSpanElement).getAttribute('data-symbol');
const toggleFavorites = (symbol: string) => {
if (!symbol) return;
const symbolIndex = favoriteSymbols.indexOf(symbol);

Expand Down Expand Up @@ -69,33 +68,48 @@ const MarketCategoryItem = forwardRef(
setIsFavorite(favoriteSymbols.includes(symbol));
};

const handleSelectDecorator = (e?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => {
const symbol = (e?.currentTarget as HTMLElement).getAttribute('data-symbol') || '';
clickAndKeyEventHandler(() => handleSelect(symbol), e);
};

const toggleFavoritesDecorator = (e?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => {
const symbol = (e?.currentTarget as HTMLElement).getAttribute('data-symbol') || '';
clickAndKeyEventHandler(() => toggleFavorites(symbol), e);
};

return (
<div
className={clsx('market-category-item', {
'market-category-item--selected': selectedSymbol === item.symbol,
})}
ref={ref}
>
<SymbolIconsMapper symbol={item.symbol} />
<Text
size='sm'
className={clsx('market-category-item-symbol', {
'market-category-item-symbol--selected': selectedSymbol === item.symbol,
})}
<span
className='market-category-item-left'
data-symbol={item.symbol}
onClick={handleSelectDecorator}
onKeyDown={handleSelectDecorator}
>
<span onClick={handleSelect} data-symbol={item.symbol}>
{item.display_name}
</span>
</Text>
{!item.exchange_is_open && (
<Tag
label={<Localize key='exchange-closed' i18n_default_text='CLOSED' />}
color='error'
variant={selectedSymbol === item.symbol ? 'outline' : 'fill'}
showIcon={false}
/>
)}
<span onClick={toggleFavorites} data-symbol={item.symbol}>
<SymbolIconsMapper symbol={item.symbol} />
<Text
size='sm'
className={clsx('market-category-item-symbol', {
'market-category-item-symbol--selected': selectedSymbol === item.symbol,
})}
>
<span>{item.display_name}</span>
</Text>
{!item.exchange_is_open && (
<Tag
label={<Localize key='exchange-closed' i18n_default_text='CLOSED' />}
color='error'
variant={selectedSymbol === item.symbol ? 'outline' : 'fill'}
showIcon={false}
/>
)}
</span>
<span onClick={toggleFavoritesDecorator} onKeyDown={toggleFavoritesDecorator} data-symbol={item.symbol}>
{isFavorite ? (
<StandaloneStarFillIcon fill='var(--core-color-solid-mustard-700)' iconSize='sm' />
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ describe('<SymbolsSearchField />', () => {
setIsSearching: jest.fn(),
searchValue: '',
setSearchValue: jest.fn(),
marketCategoriesRef: {
current: document.createElement('div'),
},
};
jest.clearAllMocks();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,4 @@
&--is-focused {
padding: var(--core-spacing-400) var(--core-spacing-400) var(--core-spacing-400) var(--core-spacing-800);
}
&--is-hidden {
display: none;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef, useEffect, useState, forwardRef, Ref } from 'react';
import React, { useRef, useEffect } from 'react';
import { Button, SearchField } from '@deriv-com/quill-ui';
import clsx from 'clsx';
import { observer, useStore } from '@deriv/stores';
Expand All @@ -11,32 +11,15 @@ export type TSymbolsSearchField = {
setIsSearching: (input: boolean) => void;
searchValue: string;
setSearchValue: (input: string) => void;
marketCategoriesRef: React.MutableRefObject<HTMLDivElement | null>;
};
henry-deriv marked this conversation as resolved.
Show resolved Hide resolved

const SymbolsSearchField = observer(
({ isSearching, setIsSearching, searchValue, setSearchValue, marketCategoriesRef }: TSymbolsSearchField) => {
({ isSearching, setIsSearching, searchValue, setSearchValue }: TSymbolsSearchField) => {
const { ui } = useStore();
const { is_dark_mode_on } = ui;
const { contract_type } = useTraderStore();
const contract_name = getContractTypesConfig()[contract_type]?.title;
const inputRef = useRef<HTMLInputElement | null>(null);
const [prevScrollY, setPrevScrollY] = useState(marketCategoriesRef.current?.scrollTop);
const [isSearchFieldVisible, setIsSearchFieldVisible] = useState(true);

useEffect(() => {
const handleScroll = () => {
const currentScrollY = marketCategoriesRef.current?.scrollTop;

if (prevScrollY && currentScrollY !== null && currentScrollY !== undefined)
setIsSearchFieldVisible(prevScrollY > currentScrollY);
setPrevScrollY(currentScrollY);
};
marketCategoriesRef.current?.addEventListener('scroll', handleScroll);
return () => {
marketCategoriesRef.current?.removeEventListener('scroll', handleScroll);
};
}, [prevScrollY, marketCategoriesRef]);

useEffect(() => {
const inputElement = inputRef.current;
Expand All @@ -61,7 +44,6 @@ const SymbolsSearchField = observer(
<div
className={clsx('symbols-search-field__container', {
'symbols-search-field--is-focused': isSearching,
'symbols-search-field--is-hidden': !isSearchFieldVisible,
})}
>
<SearchField
Expand Down
3 changes: 2 additions & 1 deletion packages/trader/src/AppV2/Hooks/useActiveSymbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ const useActiveSymbols = () => {

if (
(isVanillaContract(previous_contract_type) && is_vanilla) ||
(isTurbosContract(previous_contract_type) && is_turbos)
(isTurbosContract(previous_contract_type) && is_turbos) ||
getContractTypesList().length === 0
) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/trader/src/AppV2/Utils/sort-symbols-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const sortSymbols = (symbolsList: ActiveSymbols) => {
{}
);

return symbolsList.sort((a, b) => {
return symbolsList.slice().sort((a, b) => {
henry-deriv marked this conversation as resolved.
Show resolved Hide resolved
const marketOrderA = marketOrderMap[a.market] !== undefined ? marketOrderMap[a.market] : symbolsList.length;
const marketOrderB = marketOrderMap[b.market] !== undefined ? marketOrderMap[b.market] : symbolsList.length;
if (marketOrderA !== marketOrderB) {
Expand Down
5 changes: 3 additions & 2 deletions packages/trader/src/AppV2/Utils/symbol-categories-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ActiveSymbols } from '@deriv/api-types';
import { localize } from '@deriv/translations';
import sortSymbols from './sort-symbols-utils';

type SubmarketGroup = {
submarket_display_name: string;
Expand All @@ -22,7 +23,8 @@ export const categorizeSymbols = (symbols: ActiveSymbols): Record<string, Market
return {};
}
// Categorize ActiveSymbols array into object categorized by markets
let categorizedSymbols = symbols.reduce((acc: Record<string, MarketGroup>, symbol: ActiveSymbols[0]) => {
const sortedSymbols = sortSymbols(symbols);
let categorizedSymbols = sortedSymbols.reduce((acc: Record<string, MarketGroup>, symbol: ActiveSymbols[0]) => {
const { market, market_display_name, subgroup, subgroup_display_name, submarket, submarket_display_name } =
symbol;

Expand All @@ -42,7 +44,6 @@ export const categorizeSymbols = (symbols: ActiveSymbols): Record<string, Market

return acc;
}, {});

// Sort categorizedSymbols by submarket_display_name
Object.keys(categorizedSymbols).forEach(market => {
Object.keys(categorizedSymbols[market].subgroups).forEach(subgroup => {
Expand Down
19 changes: 17 additions & 2 deletions packages/trader/src/Stores/Modules/Trading/trade-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ export default class TradeStore extends BaseStore {
is_accumulator: computed,
is_chart_loading: observable,
is_digits_widget_active: observable,
is_dtrader_v2_enabled: computed,
is_equal: observable,
is_market_closed: observable,
is_mobile_digit_view_selected: observable,
Expand Down Expand Up @@ -664,8 +665,11 @@ export default class TradeStore extends BaseStore {
async loadActiveSymbols(should_set_default_symbol = true, should_show_loading = true) {
this.should_show_active_symbols_loading = should_show_loading;

await this.setActiveSymbols();
await this.root_store.active_symbols.setActiveSymbols();
if (!this.is_dtrader_v2_enabled) {
await this.setActiveSymbols();
await this.root_store.active_symbols.setActiveSymbols();
}

const { symbol, showModal } = getTradeURLParams({ active_symbols: this.active_symbols });
if (showModal && should_show_loading && !this.root_store.client.is_logging_in) {
this.root_store.ui.toggleUrlUnavailableModal(true);
Expand Down Expand Up @@ -1282,6 +1286,16 @@ export default class TradeStore extends BaseStore {
}
}

get is_dtrader_v2_enabled() {
const is_dtrader_v2 = JSON.parse(localStorage.getItem('FeatureFlagsStore') ?? '{}')?.data?.dtrader_v2;

return (
is_dtrader_v2 &&
this.root_store.ui.is_mobile &&
(window.location.pathname.startsWith(routes.trade) || window.location.pathname.startsWith('/contract/'))
);
}

get is_synthetics_available() {
return !!this.active_symbols?.find(item => item.market === 'synthetic_index');
}
Expand Down Expand Up @@ -1805,6 +1819,7 @@ export default class TradeStore extends BaseStore {
});
}
if ('active_symbols' in req) {
if (this.is_dtrader_v2_enabled) return;
if (this.root_store.client.is_logged_in) {
return WS.authorized.activeSymbols('brief');
}
Expand Down
Loading