Skip to content

Commit

Permalink
Hamza/90545/responsive user awareness (#7922)
Browse files Browse the repository at this point in the history
* feat: responsive user awareness v1.0

* feat: responsive user awareness v1.1

* feat: responsive user awareness v1.2

* feat: responsive user awareness v1.3

* feat: responsive user awareness v1.3

* feat: responsive user awareness v1.4

* feat: responsive user awareness v1.5

* feat: responsive user awareness v1.6

* refactor: review code + tsmigration

* refactor: removed the unused props

* refactor: removed the unused props v1.2

* refactor: alias added v1.3

* fix: successfully added in the text

* fix: successfully added in the text + Standardization V1.2

* fix: successfully added in the text + Standardization V1.3

* fix: successfully added in the text + Standardization V1.3.1

* fix: language switcher css (#8084)

Co-authored-by: yashim-deriv <yashim@deriv.com>

* fix: placeholder signup modal typo (#8095)

* thisyahlen/fix: disable language button till language is fully loaded (#8089)

* fix: disable language button till language is fully loaded

* fix: add mockstore

* fix: responsive

* fix: remove unused css

* fix: css

* fix: 🐛 button not disabled for account signup (#8103)

* Amina/fix: set citizen on account (#8102)

* fix: set citizen on account

* chore: trigger circleci

* fix: set citizen on account

---------

Co-authored-by: Matin shafiei <matin@deriv.com>
Co-authored-by: thisyahlen <104053934+thisyahlen-deriv@users.noreply.github.com>
Co-authored-by: yashim-deriv <yashim@deriv.com>
Co-authored-by: Aizad Ridzo <103104395+aizad-deriv@users.noreply.github.com>
Co-authored-by: Likhith Kolayari <98398322+likhith-deriv@users.noreply.github.com>
Co-authored-by: amina-deriv <84661147+amina-deriv@users.noreply.github.com>
  • Loading branch information
7 people committed Apr 5, 2023
1 parent 002d125 commit 3cebbba
Show file tree
Hide file tree
Showing 14 changed files with 217 additions and 10 deletions.
6 changes: 6 additions & 0 deletions packages/components/src/components/toast/toast.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
}
}
}
&__notification {
.dc-toast__message {
background: var(--general-active);
padding: 0.9rem 1.2rem;
}
}
&--enter,
&--exit {
transform: scale(1, 0);
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/components/toast/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type TToast = {
is_open?: boolean;
onClick?: React.MouseEventHandler<HTMLDivElement>;
onClose?: () => void;
type?: 'error' | 'info';
type?: 'error' | 'info' | 'notification';
timeout?: number;
};

Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/sass/app/_common/layout/app-layouts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@
position: absolute;
right: 1.6rem;
}
.dc-toast-popup-mobile {
display: inherit;
max-width: calc(100vw - 1.6rem);
position: absolute;
width: calc(100vw - 1.2rem);
top: 0.4rem;
left: 1.2rem;
right: 1.2rem;
}
}
}

Expand Down
33 changes: 33 additions & 0 deletions packages/trader/@deriv-stores.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { TRootStore } from '@deriv/stores/types';
import type TradeStore from './src/Stores/Modules/Trading/trade-store';

type TToastBoxListItem = {
contract_category: string;
contract_types: [
{
text: string;
value: string;
}
];
};

type TToastBoxObject = {
key?: boolean;
buy_price?: number;
currency?: string;
contract_type?: string;
list?: TToastBoxListItem[];
};

type TOverrideTradeStore = Omit<TradeStore, 'clearContractPurchaseToastBox' | 'contract_purchase_toast_box'> & {
clearContractPurchaseToastBox: () => void;
contract_purchase_toast_box: TToastBoxObject;
};

declare module '@deriv/stores' {
export function useStore(): TRootStore & {
modules: {
trade: TOverrideTradeStore;
};
};
}
3 changes: 0 additions & 3 deletions packages/trader/build/loaders-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

const js_loaders = [
{
loader: '@deriv/shared/src/loaders/react-import-loader.js',
},
{
loader: '@deriv/shared/src/loaders/deriv-account-loader.js',
},
Expand Down
1 change: 1 addition & 0 deletions packages/trader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"@deriv/deriv-charts": "1.1.8",
"@deriv/reports": "^1.0.0",
"@deriv/shared": "^1.0.0",
"@deriv/stores": "^1.0.0",
"@deriv/translations": "^1.0.0",
"@types/classnames": "^2.2.11",
"@types/react-loadable": "^5.5.6",
Expand Down
5 changes: 3 additions & 2 deletions packages/trader/src/App/app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import React from 'react';
import Loadable from 'react-loadable';
import { StoreProvider } from '@deriv/stores';
import Routes from 'App/Containers/Routes/routes.jsx';
import TradeHeaderExtensions from 'App/Containers/trade-header-extensions.jsx';
import TradeFooterExtensions from 'App/Containers/trade-footer-extensions.jsx';
Expand Down Expand Up @@ -30,14 +31,14 @@ const App = ({ passthrough }: Apptypes) => {

return (
<MobxContentProvider store={root_store}>
<React.Fragment>
<StoreProvider store={root_store}>
<Routes />
<TradeModals />
<NetworkStatusToastErrorPopup />
<TradeHeaderExtensions store={root_store} />
<TradeFooterExtensions />
<TradeSettingsExtensions store={root_store} />
</React.Fragment>
</StoreProvider>
</MobxContentProvider>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import { render } from '@testing-library/react';
import BuyToastNotification from '../../buy-toast-notification';
import { mockStore, StoreProvider } from '@deriv/stores';

describe('BuyToastNotification component', () => {
const mockActionToastbox = {
buy_price: '100',
currency: 'USD',
contract_type: 'rise_fall',
list: [
{ contract_category: 'up_down', contract_types: [{ text: 'Rise/Fall', value: 'rise_fall' }] },
{ contract_category: 'high_low', contract_types: [{ text: 'Higher/Lower', value: 'higher_lower' }] },
],
key: true,
};

const mock = mockStore({
modules: {
trade: {
contract_purchase_toast_box: mockActionToastbox,
clearContractPurchaseToastBox: jest.fn(),
},
},
});

let modal_root_el: HTMLDivElement;

beforeAll(() => {
modal_root_el = document.createElement('div');
modal_root_el.setAttribute('id', 'popup_root');
document.body.appendChild(modal_root_el);
});

afterAll(() => {
document.body.removeChild(modal_root_el);
});

beforeEach(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.useRealTimers();
});

it('should render the notification ', () => {
render(<BuyToastNotification />, {
wrapper: ({ children }) => <StoreProvider store={mock}>{children}</StoreProvider>,
});

expect(modal_root_el).toBeInTheDocument();
});

it('should call clearContractPurchaseToastBox after 4 seconds', () => {
render(<BuyToastNotification />, {
wrapper: ({ children }) => <StoreProvider store={mock}>{children}</StoreProvider>,
});

jest.advanceTimersByTime(4000);
expect(mock.modules.trade.clearContractPurchaseToastBox).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { MobileWrapper, Toast, Text } from '@deriv/components';
import { Localize } from '@deriv/translations';
import { findContractCategory } from 'Modules/Trading/Helpers/contract-type';
import { observer, useStore } from '@deriv/stores';

type TContractType = {
text: string;
value: string;
};

const BuyToastNotification = observer(() => {
const portal = document.getElementById('popup_root');
const { modules } = useStore();
const { trade } = modules;
const { contract_purchase_toast_box, clearContractPurchaseToastBox } = trade;

React.useEffect(() => {
const timeout = setTimeout(() => {
clearContractPurchaseToastBox();
}, 4000);

return () => {
clearTimeout(timeout);
};
}, [clearContractPurchaseToastBox, contract_purchase_toast_box]);

if (!portal || !contract_purchase_toast_box) return <React.Fragment />;

const { buy_price, currency, contract_type, list } = contract_purchase_toast_box;
const active_trade_type = { value: contract_type };

const trade_type_name = findContractCategory(list, active_trade_type)?.contract_types?.find(
(item: TContractType) => item.value === contract_type
).text;

return ReactDOM.createPortal(
<MobileWrapper>
<Toast
className='dc-toast-popup-mobile'
is_open={!!contract_purchase_toast_box.key}
timeout={0}
type='notification'
>
<Text as='p' size='xxs' className='dc-toast__notification'>
<Localize
i18n_default_text='The purchase of <0>{{trade_type_name}} contract</0> has been completed successfully for the amount of <0> {{buy_price}} {{currency}}</0>'
components={[<strong key={0} />]}
values={{ trade_type_name, buy_price, currency }}
shouldUnescape
/>
</Text>
</Toast>
</MobileWrapper>,
portal
);
});

export default BuyToastNotification;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { localize } from '@deriv/translations';
import { isEnded, isAccumulatorContract, isDigitContract } from '@deriv/shared';
import { connect } from 'Stores/connect';
import { ChartTitle } from 'Modules/SmartChart';
import BuyToastNotification from './buy-toast-notification';

const TradeInfo = ({ markers_array, granularity }) => {
const latest_tick_contract = markers_array[markers_array.length - 1];
Expand Down Expand Up @@ -64,6 +65,7 @@ const TopWidgets = ({
width: `calc(100% - ${y_axis_width ? y_axis_width + 5 : 0}px)`,
}}
>
{is_mobile && <BuyToastNotification />}
{ChartTitleLocal}
{!is_digits_widget_active && <RecentTradeInfo />}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ const ContractTypeWidget = ({ is_equal, name, value, list, onChange, languageCha
.filter(type => type.value !== 'rise_fall_equal')
.findIndex(type => type.value === item?.value);
};

return (
<div
data-testid='dt_contract_widget'
Expand Down
28 changes: 27 additions & 1 deletion packages/trader/src/Stores/Modules/Trading/trade-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ import {
showDigitalOptionsUnavailableError,
showMxMltUnavailableError,
showUnavailableLocationError,
formatMoney,
getCurrencyDisplayCode,
unsupported_contract_types_list,
} from '@deriv/shared';
import { localize } from '@deriv/translations';
import { getValidationRules, getMultiplierValidationRules } from 'Stores/Modules/Trading/Constants/validation-rules';
import { ContractType } from 'Stores/Modules/Trading/Helpers/contract-type';
import { isDigitContractType, isDigitTradeType } from 'Modules/Trading/Helpers/digits';
import ServerTime from '_common/base/server_time';
import { processPurchase } from './Actions/purchase';

import { getAvailableContractTypes } from 'Modules/Trading/Helpers/contract-type';
import { getUpdatedTicksHistoryStats } from './Helpers/accumulator';
import { processTradeParams } from './Helpers/process';
import { action, computed, makeObservable, observable, override, reaction, runInAction, toJS, when } from 'mobx';
Expand Down Expand Up @@ -148,6 +151,9 @@ export default class TradeStore extends BaseStore {
// Mobile
is_trade_params_expanded = true;

//Toastbox
contract_purchase_toast_box;

addTickByProposal = () => null;
debouncedProposal = debounce(this.requestProposal, 500);
proposal_requests = {};
Expand Down Expand Up @@ -324,6 +330,9 @@ export default class TradeStore extends BaseStore {
updateLimitOrderBarriers: action.bound,
updateStore: action.bound,
updateSymbol: action.bound,
contract_purchase_toast_box: observable,
clearContractPurchaseToastBox: action.bound,
setContractPurchaseToastbox: action.bound,
});

// Adds intercept to change min_max value of duration validation
Expand Down Expand Up @@ -837,6 +846,7 @@ export default class TradeStore extends BaseStore {
this.debouncedProposal();
this.clearLimitOrderBarriers();
this.pushPurchaseDataToGtm(contract_data);
this.setContractPurchaseToastbox(response.buy);
this.is_purchasing_contract = false;
return;
}
Expand Down Expand Up @@ -1560,6 +1570,22 @@ export default class TradeStore extends BaseStore {
return this.contract_type === 'vanilla';
}

setContractPurchaseToastbox(response) {
const list = getAvailableContractTypes(this.contract_types_list, unsupported_contract_types_list);

return (this.contract_purchase_toast_box = {
key: true,
buy_price: formatMoney(this.root_store.client.currency, response.buy_price, true, 0, 0),
contract_type: this.contract_type,
currency: getCurrencyDisplayCode(this.root_store.client.currency),
list,
});
}

clearContractPurchaseToastBox() {
this.contract_purchase_toast_box = undefined;
}

async getFirstOpenMarket(markets_to_search) {
if (this.active_symbols?.length) {
return findFirstOpenMarket(this.active_symbols, markets_to_search);
Expand Down
2 changes: 1 addition & 1 deletion packages/trader/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
"outDir": "./dist",
"baseUrl": "./"
},
"include": ["src", "../../utils.d.ts"]
"include": ["src", "../../globals.d.ts", "../../utils.d.ts", "@deriv-stores.d.ts"]
}
12 changes: 10 additions & 2 deletions packages/translations/src/components/localize.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ import PropTypes from 'prop-types';
import React from 'react';
import { Trans } from 'react-i18next';

const Localize = ({ i18n_default_text, values, components, options, i18n }) => (
<Trans i18n={i18n} defaults={i18n_default_text} values={values} components={components} tOptions={options} />
const Localize = ({ i18n_default_text, values, components, options, i18n, shouldUnescape }) => (
<Trans
i18n={i18n}
defaults={i18n_default_text}
values={values}
components={components}
tOptions={options}
shouldUnescape={shouldUnescape}
/>
);

// Trans needs to have the i18n instance in scope
Expand All @@ -16,6 +23,7 @@ Localize.propTypes = {
i18n_default_text: PropTypes.string,
options: PropTypes.object,
values: PropTypes.object,
shouldUnescape: PropTypes.bool,
};

export default withI18n;

0 comments on commit 3cebbba

Please sign in to comment.