From ad81ac269f7e99decfe6ea3ed806a182103b5d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cyauheni-kryzhyk-deriv=E2=80=9D?= <“yauheni@deriv.me”> Date: Fri, 17 Feb 2023 12:33:30 +0300 Subject: [PATCH 1/5] refactor: evgeniy/76943/verticaltab ts migration --- packages/account/src/Containers/account.jsx | 27 ---- .../AccountLimits/account-limits-info.jsx | 51 ------- .../src/containers/cashier/cashier.tsx | 11 +- .../cashier/src/types/shared/routes.types.ts | 7 +- .../vertical-tab/{index.js => index.ts} | 2 +- ...jsx => vertical-tab-content-container.tsx} | 79 +++++------ ...roup.jsx => vertical-tab-header-group.tsx} | 23 ++- ...itle.jsx => vertical-tab-header-title.tsx} | 4 +- ...tab-header.jsx => vertical-tab-header.tsx} | 55 ++++++-- ...b-headers.jsx => vertical-tab-headers.tsx} | 53 +++++-- ...tab-layout.jsx => vertical-tab-layout.tsx} | 6 +- ...b-wrapper.jsx => vertical-tab-wrapper.tsx} | 9 +- .../{vertical-tab.jsx => vertical-tab.tsx} | 132 ++++++++---------- .../vertical-tabs/vertical-tabs.stories.js | 34 ----- .../Layout/Footer/toggle-settings.jsx | 2 +- packages/reports/src/Containers/reports.jsx | 3 - packages/shared/src/utils/route/route.ts | 3 + .../shared/src/utils/string/string_util.ts | 2 +- 18 files changed, 223 insertions(+), 280 deletions(-) delete mode 100644 packages/account/src/Sections/Security/AccountLimits/account-limits-info.jsx rename packages/components/src/components/vertical-tab/{index.js => index.ts} (56%) rename packages/components/src/components/vertical-tab/{vertical-tab-content-container.jsx => vertical-tab-content-container.tsx} (59%) rename packages/components/src/components/vertical-tab/{vertical-tab-header-group.jsx => vertical-tab-header-group.tsx} (81%) rename packages/components/src/components/vertical-tab/{vertical-tab-header-title.jsx => vertical-tab-header-title.tsx} (66%) rename packages/components/src/components/vertical-tab/{vertical-tab-header.jsx => vertical-tab-header.tsx} (63%) rename packages/components/src/components/vertical-tab/{vertical-tab-headers.jsx => vertical-tab-headers.tsx} (74%) rename packages/components/src/components/vertical-tab/{vertical-tab-layout.jsx => vertical-tab-layout.tsx} (61%) rename packages/components/src/components/vertical-tab/{vertical-tab-wrapper.jsx => vertical-tab-wrapper.tsx} (63%) rename packages/components/src/components/vertical-tab/{vertical-tab.jsx => vertical-tab.tsx} (60%) diff --git a/packages/account/src/Containers/account.jsx b/packages/account/src/Containers/account.jsx index aae57fba274d..3fc62e1cdfa7 100644 --- a/packages/account/src/Containers/account.jsx +++ b/packages/account/src/Containers/account.jsx @@ -6,7 +6,6 @@ import { routes as shared_routes, isMobile, matchRoute, getSelectedRoute, Platfo import { localize } from '@deriv/translations'; import { connect } from 'Stores/connect'; import { flatten } from '../Helpers/flatten'; -import AccountLimitInfo from '../Sections/Security/AccountLimits/account-limits-info.jsx'; import 'Styles/account.scss'; import { useHistory } from 'react-router'; @@ -76,12 +75,10 @@ const PageOverlayWrapper = ({ { - routeBackInApp(history); - }, - icon: 'IcCross', - title: localize('Close'), - }, - ]; - - const is_account_limits_route = selected_content.path === routes.account_limits; - - if (is_account_limits_route) { - action_bar_items.push({ - // eslint-disable-next-line react/display-name - component: () => , - }); - } - if (!is_logged_in && is_logging_in) { return ; } @@ -221,7 +196,6 @@ const Account = ({ Account.propTypes = { active_account_landing_company: PropTypes.string, - currency: PropTypes.string, history: PropTypes.object, is_from_derivgo: PropTypes.bool, is_logged_in: PropTypes.bool, @@ -241,7 +215,6 @@ Account.propTypes = { export default connect(({ client, common, ui }) => ({ active_account_landing_company: client.landing_company_shortcode, - currency: client.currency, is_from_derivgo: common.is_from_derivgo, is_logged_in: client.is_logged_in, is_logging_in: client.is_logging_in, diff --git a/packages/account/src/Sections/Security/AccountLimits/account-limits-info.jsx b/packages/account/src/Sections/Security/AccountLimits/account-limits-info.jsx deleted file mode 100644 index f16f6e453b31..000000000000 --- a/packages/account/src/Sections/Security/AccountLimits/account-limits-info.jsx +++ /dev/null @@ -1,51 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { Icon, Text } from '@deriv/components'; -import { localize, Localize } from '@deriv/translations'; - -const currency_name_map = { - BTC: { display_code: 'BTC', name: localize('Bitcoin') }, - BCH: { display_code: 'BCH', name: localize('Bitcoin Cash') }, - ETH: { display_code: 'ETH', name: localize('Ether') }, - ETC: { display_code: 'ETC', name: localize('Ether Classic') }, - LTC: { display_code: 'LTC', name: localize('Litecoin') }, - UST: { display_code: 'USDT', name: localize('Tether') }, - USB: { display_code: 'USB', name: localize('Binary Coin') }, - USD: { display_code: 'USD', name: localize('US Dollar') }, - AUD: { display_code: 'AUD', name: localize('Australian Dollar') }, - EUR: { display_code: 'EUR', name: localize('Euro') }, - GBP: { display_code: 'GBP', name: localize('Pound Sterling') }, -}; - -const AccountLimitsInfo = ({ currency, is_virtual }) => ( - <> - {!is_virtual && ( - <> - - - {currency ? ( - - ) : ( - - )} - - - )} - -); - -AccountLimitsInfo.propTypes = { - currency: PropTypes.string, - is_virtual: PropTypes.bool, -}; - -export default AccountLimitsInfo; diff --git a/packages/cashier/src/containers/cashier/cashier.tsx b/packages/cashier/src/containers/cashier/cashier.tsx index 8a94cf6eea93..ba7f630ffe4b 100644 --- a/packages/cashier/src/containers/cashier/cashier.tsx +++ b/packages/cashier/src/containers/cashier/cashier.tsx @@ -37,10 +37,10 @@ type TCashierOptions = { count?: number; default?: boolean; has_side_note: boolean; - icon?: string; + icon: string; label: string; path?: string; - value: TRoute['component']; + value?: typeof React.Component; }; const Cashier = observer(({ history, location, routes: routes_config }: TCashierProps) => { @@ -138,7 +138,7 @@ const Cashier = observer(({ history, location, routes: routes_config }: TCashier const getHeaderTitle = () => { if (!isMobile() || (is_default_route && (is_loading || is_cashier_onboarding))) return localize('Cashier'); - return selected_route.getTitle(); + return selected_route.getTitle?.(); }; return ( @@ -149,9 +149,6 @@ const Cashier = observer(({ history, location, routes: routes_config }: TCashier - {selected_route && ( + {selected_route?.component && ( JSX.Element) | TPage404 | typeof Redirect; + component?: typeof React.Component; getTitle: () => string; }; diff --git a/packages/components/src/components/vertical-tab/index.js b/packages/components/src/components/vertical-tab/index.ts similarity index 56% rename from packages/components/src/components/vertical-tab/index.js rename to packages/components/src/components/vertical-tab/index.ts index f1066be56766..b502faffa0a3 100644 --- a/packages/components/src/components/vertical-tab/index.js +++ b/packages/components/src/components/vertical-tab/index.ts @@ -1,4 +1,4 @@ -import VerticalTab from './vertical-tab.jsx'; +import VerticalTab from './vertical-tab'; import './vertical-tab.scss'; export default VerticalTab; diff --git a/packages/components/src/components/vertical-tab/vertical-tab-content-container.jsx b/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx similarity index 59% rename from packages/components/src/components/vertical-tab/vertical-tab-content-container.jsx rename to packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx index ee07afb3bd09..ed8860861159 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-content-container.jsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx @@ -2,12 +2,33 @@ import classNames from 'classnames'; import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { usePrevious } from '../../hooks'; -import Icon from '../icon/icon'; +import { TItem } from './vertical-tab-header'; -const SideNotes = ({ class_name, side_notes }) => { +type TSideNotes = { + class_name?: string; + side_notes: React.ReactNode[] | null; +}; + +type TContentWrapper = { + has_side_note?: boolean; +}; + +type TContent = { + is_routed?: boolean; + items: TItem[]; + selected: TItem; +}; + +type TVerticalTabContentContainer = TContent & { + className?: string; + is_floating?: boolean; + tab_container_classname?: string; +}; + +const SideNotes = ({ class_name, side_notes }: TSideNotes) => { return (
{side_notes?.map((note, i) => ( @@ -19,29 +40,29 @@ const SideNotes = ({ class_name, side_notes }) => { ); }; -const ContentWrapper = ({ children, has_side_note }) => { +const ContentWrapper = ({ children, has_side_note }: React.PropsWithChildren) => { if (has_side_note) { return
{children}
; } - return children; + return children as JSX.Element; }; -const Content = ({ is_routed, items, selected }) => { +const Content = ({ is_routed, items, selected }: TContent) => { const selected_item = items.find(item => item.label === selected.label); const previous_selected_item = usePrevious(selected_item); - const TabContent = selected_item.value; - const [side_notes, setSideNotes] = React.useState(null); + const TabContent = selected_item?.value as React.ElementType; + const [side_notes, setSideNotes] = React.useState(null); - const notes_array = []; + const notes_array: React.ReactNode[] = []; - const addToNotesQueue = React.useCallback(notes => { + const addToNotesQueue = React.useCallback((notes: React.ReactNode) => { notes_array.unshift(notes); - setSideNotes(notes); + setSideNotes(notes_array); }, []); React.useEffect(() => { if (selected_item?.label !== previous_selected_item?.label) { - setSideNotes(notes_array[0] ?? null); + setSideNotes(notes_array[0] ? notes_array : null); notes_array.splice(0, notes_array.length); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -52,7 +73,7 @@ const Content = ({ is_routed, items, selected }) => { {is_routed ? ( {items.map(({ value, component, path, icon }, idx) => { - const Component = value || component; + const Component = (value as React.ElementType) || (component as React.ElementType); return ( { })} ) : ( - + )} {selected.has_side_note && ( // for components that have side note, even if no note is passed currently, // we want to keep the column space for side note - + )} ); }; const VerticalTabContentContainer = ({ - action_bar, - action_bar_classname, className, - id, is_floating, is_routed, items, selected, tab_container_classname, -}) => { +}: TVerticalTabContentContainer) => { return (
- {!is_floating && action_bar && ( -
- {action_bar.map(({ component, icon, onClick }, idx) => { - const Component = component; - return component ? ( - - ) : ( -
- -
- ); - })} -
- )}
diff --git a/packages/components/src/components/vertical-tab/vertical-tab-header-group.jsx b/packages/components/src/components/vertical-tab/vertical-tab-header-group.tsx similarity index 81% rename from packages/components/src/components/vertical-tab/vertical-tab-header-group.jsx rename to packages/components/src/components/vertical-tab/vertical-tab-header-group.tsx index 3dcec386b826..a5be24467cde 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-header-group.jsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-header-group.tsx @@ -1,9 +1,20 @@ -import classNames from 'classnames'; import React from 'react'; +import classNames from 'classnames'; import { NavLink } from 'react-router-dom'; import Icon from '../icon/icon'; +import { THeader, THeaderIcon, TItem } from './vertical-tab-header'; + +type TVerticalTabHeaderGroup = { + className?: string; + group: TItem; + selected: boolean; + is_collapsible?: boolean; + is_routed?: boolean; + onChange: (group: TItem) => void; + onToggle: (toggle: boolean) => void; +}; -const HeaderIcon = ({ icon, is_active }) => ( +const HeaderIcon = ({ icon, is_active }: THeaderIcon) => ( ( /> ); -const Header = ({ text }) =>
{text}
; +const Header = ({ text }: THeader) =>
{text}
; const VerticalTabHeaderGroup = ({ children, @@ -23,16 +34,16 @@ const VerticalTabHeaderGroup = ({ is_routed, onChange, onToggle, -}) => { +}: React.PropsWithChildren) => { const [show_items, setShowItems] = React.useState(true); React.useEffect(() => { onToggle(true); }, [show_items, onToggle]); - const label = typeof group.getTitle === 'function' ? group.getTitle() : group.label || group.title; + const label = group.getTitle ? group.getTitle() : group.label || group.title; const handleClick = () => { - if (!group.subitems && typeof onChange === 'function') { + if (!group.subitems) { onChange(group); } else if (is_collapsible && !selected) { setShowItems(!show_items); diff --git a/packages/components/src/components/vertical-tab/vertical-tab-header-title.jsx b/packages/components/src/components/vertical-tab/vertical-tab-header-title.tsx similarity index 66% rename from packages/components/src/components/vertical-tab/vertical-tab-header-title.jsx rename to packages/components/src/components/vertical-tab/vertical-tab-header-title.tsx index 657cda74dc0c..1a6d775396e8 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-header-title.jsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-header-title.tsx @@ -1,7 +1,9 @@ import React from 'react'; import Text from '../text'; -const VerticalTabHeaderTitle = ({ header_title }) => ( +type TVerticalTabHeaderTitle = { header_title: string }; + +const VerticalTabHeaderTitle = ({ header_title }: TVerticalTabHeaderTitle) => (
{header_title} diff --git a/packages/components/src/components/vertical-tab/vertical-tab-header.jsx b/packages/components/src/components/vertical-tab/vertical-tab-header.tsx similarity index 63% rename from packages/components/src/components/vertical-tab/vertical-tab-header.jsx rename to packages/components/src/components/vertical-tab/vertical-tab-header.tsx index 26fff2aa0d2f..99d6683b2653 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-header.jsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-header.tsx @@ -1,11 +1,46 @@ -import classNames from 'classnames'; import React from 'react'; +import classNames from 'classnames'; import { NavLink } from 'react-router-dom'; import { getKebabCase } from '@deriv/shared'; import Counter from '../counter'; import Icon from '../icon/icon'; -const HeaderIcon = ({ icon, is_active }) => ( +export type THeaderIcon = { + icon: string; + is_active: boolean; +}; + +export type THeader = { + text?: string; + path?: string; +}; + +export type TItem = { + component?: typeof React.Component; + count?: number; + default?: boolean; + getTitle?: () => string; + has_side_note?: boolean; + icon: string; + is_hidden?: boolean; + is_disabled?: boolean; + label?: string; + path?: string; + subitems?: number[]; + title?: string; + value?: typeof React.Component; +}; +type TVerticalTabHeader = { + className?: string; + is_floating?: boolean; + is_routed?: boolean; + item: TItem; + onChange: (item: TItem) => void; + selected: TItem; + selectedKey: string; +}; + +const HeaderIcon = ({ icon, is_active }: THeaderIcon) => ( ( /> ); -const Header = ({ text, path }) => ( +const Header = ({ text, path }: THeader) => (
{text}
@@ -29,9 +64,9 @@ const VerticalTabHeader = ({ onChange, selected, selectedKey = 'label', -}) => { +}: React.PropsWithChildren) => { const label = item.label || item.title || item.getTitle?.(); - const is_active = selected && selected[selectedKey] === item[selectedKey]; + const is_active = selected && selected[selectedKey as keyof TItem] === item[selectedKey as keyof TItem]; const handleClick = () => onChange(item); const id = `dc_${getKebabCase(label)}_link`; const is_disabled = !!item.is_disabled; @@ -67,10 +102,12 @@ const VerticalTabHeader = ({ })} onClick={handleClick} > - -
- {children} - {item.component} + <> + +
+ {children} + {item.component} +
); }; diff --git a/packages/components/src/components/vertical-tab/vertical-tab-headers.jsx b/packages/components/src/components/vertical-tab/vertical-tab-headers.tsx similarity index 74% rename from packages/components/src/components/vertical-tab/vertical-tab-headers.jsx rename to packages/components/src/components/vertical-tab/vertical-tab-headers.tsx index 1f974dfb3fa3..b859ddfdb954 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-headers.jsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-headers.tsx @@ -1,21 +1,43 @@ -import classNames from 'classnames'; import React from 'react'; -import VerticalTabWrapper from './vertical-tab-wrapper.jsx'; -import VerticalTabHeader from './vertical-tab-header.jsx'; -import VerticalTabHeaderGroup from './vertical-tab-header-group.jsx'; -import VerticalTabHeaderTitle from './vertical-tab-header-title.jsx'; +import classNames from 'classnames'; +import VerticalTabWrapper from './vertical-tab-wrapper'; +import VerticalTabHeader, { TItem } from './vertical-tab-header'; +import VerticalTabHeaderGroup from './vertical-tab-header-group'; +import VerticalTabHeaderTitle from './vertical-tab-header-title'; + +type TVerticalTabHeaders = { + className?: string; + extra_offset?: number; + has_mixed_dimensions?: boolean; + header_title?: string; + is_collapsible?: boolean; + is_floating?: boolean; + is_routed?: boolean; + item_groups?: TItem[]; + items: TItem[]; + onChange: (item: TItem) => void; + selected: TItem; + selectedKey?: string; +}; -const offsetTop = (extra_offset, is_floating, ref, selected) => { +const offsetTop = ( + extra_offset: number | undefined, + is_floating: boolean | undefined, + ref: React.RefObject, + selected: Partial +) => { let calculated_offset = 0; let item_offset = 0; - const headers = ref.current.querySelectorAll( + let selected_el: HTMLDivElement | undefined; + const headers = ref?.current?.querySelectorAll( '.dc-vertical-tab__header__link, .dc-vertical-tab__header-group__link' ); - const selected_el = [...headers].find(header => - typeof selected.getTitle === 'function' - ? header.innerText === selected.getTitle() - : header.innerText === selected.label - ); + + if (headers) { + selected_el = [...headers].find(header => + selected.getTitle ? header.innerText === selected.getTitle() : header.innerText === selected.label + ); + } if (selected_el) { item_offset = is_floating ? extra_offset || 18 : 10; @@ -38,13 +60,15 @@ const VerticalTabHeaders = ({ onChange, selected, selectedKey = 'label', -}) => { +}: TVerticalTabHeaders) => { const ref = React.useRef(null); const [top, setTop] = React.useState(0); const [should_skip_animation, setShouldSkipAnimation] = React.useState(false); React.useEffect(() => { - const selected_item = items.find(item => item[selectedKey] === selected[selectedKey]); + const selected_item = items.find( + item => item[selectedKey as keyof TItem] === selected[selectedKey as keyof TItem] + ); if (selected_item?.label) setTop(offsetTop(extra_offset, is_floating, ref, { label: selected_item.label })); }, [selected, is_floating, extra_offset, selectedKey, items]); return ( @@ -68,7 +92,6 @@ const VerticalTabHeaders = ({ )) || (!group.subitems && group === selected) } - items={items} is_collapsible={is_collapsible} is_routed={is_routed} group={group} diff --git a/packages/components/src/components/vertical-tab/vertical-tab-layout.jsx b/packages/components/src/components/vertical-tab/vertical-tab-layout.tsx similarity index 61% rename from packages/components/src/components/vertical-tab/vertical-tab-layout.jsx rename to packages/components/src/components/vertical-tab/vertical-tab-layout.tsx index 28e3df77ece5..f56b5c823b14 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-layout.jsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-layout.tsx @@ -1,7 +1,11 @@ import React from 'react'; import classNames from 'classnames'; -const VerticalTabLayout = ({ children, is_full_width }) => ( +type TVerticalTabLayout = { + is_full_width?: boolean; +}; + +const VerticalTabLayout = ({ children, is_full_width }: React.PropsWithChildren) => (
; + className?: string; +}; -const VerticalTabWrapper = ({ wrapper_ref, children, className }) => ( +const VerticalTabWrapper = ({ wrapper_ref, children, className }: React.PropsWithChildren) => (
{children}
diff --git a/packages/components/src/components/vertical-tab/vertical-tab.jsx b/packages/components/src/components/vertical-tab/vertical-tab.tsx similarity index 60% rename from packages/components/src/components/vertical-tab/vertical-tab.jsx rename to packages/components/src/components/vertical-tab/vertical-tab.tsx index 5e3df7ef1c68..ba94efa4dd2e 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab.jsx +++ b/packages/components/src/components/vertical-tab/vertical-tab.tsx @@ -1,23 +1,63 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; import React from 'react'; +import classNames from 'classnames'; import { matchRoute } from '@deriv/shared'; -import VerticalTabContentContainer from './vertical-tab-content-container.jsx'; -import VerticalTabHeader from './vertical-tab-header.jsx'; -import VerticalTabHeaderGroup from './vertical-tab-header-group.jsx'; -import VerticalTabHeaders from './vertical-tab-headers.jsx'; -import VerticalTabHeaderTitle from './vertical-tab-header-title.jsx'; -import VerticalTabLayout from './vertical-tab-layout.jsx'; -import VerticalTabWrapper from './vertical-tab-wrapper.jsx'; +import VerticalTabContentContainer from './vertical-tab-content-container'; +import VerticalTabHeader, { TItem } from './vertical-tab-header'; +import VerticalTabHeaderGroup from './vertical-tab-header-group'; +import VerticalTabHeaders from './vertical-tab-headers'; +import VerticalTabHeaderTitle from './vertical-tab-header-title'; +import VerticalTabLayout from './vertical-tab-layout'; +import VerticalTabWrapper from './vertical-tab-wrapper'; import Icon from '../icon/icon'; -const setSelectedIndex = ({ current_path, list, is_routed, selected_index, setCurrTabIndex, setVerticalTabIndex }) => { - let index; +type TSetSelectedIndex = { + current_path: string; + list: TItem[]; + is_routed?: boolean; + selected_index?: number | TItem | undefined; + setCurrTabIndex: (index: number) => void; + setVerticalTabIndex?: (index: number) => void; +}; + +type TVerticalTab = { + className?: string; + current_path: string; + extra_content?: React.ReactNode | React.ReactNode[]; + extra_offset?: number; + has_mixed_dimensions?: boolean; + header_classname?: string; + header_title?: string; + is_collapsible?: boolean; + is_floating?: boolean; + is_grid?: boolean; + is_full_width?: boolean; + is_routed?: boolean; + is_sidebar_enabled?: boolean; + list: TItem[]; + list_groups?: TItem[]; + onClickClose?: () => void; + setVerticalTabIndex?: (index: number) => void; + tab_headers_note: React.ReactNode | React.ReactNode[]; + title?: string; + vertical_tab_index?: number; +}; + +const setSelectedIndex = ({ + current_path, + list, + is_routed, + selected_index, + setCurrTabIndex, + setVerticalTabIndex, +}: TSetSelectedIndex) => { + let index: number; if (typeof selected_index === 'undefined') { index = is_routed ? Math.max( - list.indexOf(list.find(item => matchRoute(item, current_path)) || list.find(item => item.default)), + list.indexOf( + (list.find(item => matchRoute(item, current_path)) || list.find(item => item.default)) as TItem + ), 0 ) : 0; @@ -26,15 +66,10 @@ const setSelectedIndex = ({ current_path, list, is_routed, selected_index, setCu } setCurrTabIndex(index); - - if (typeof setVerticalTabIndex === 'function') { - setVerticalTabIndex(index); - } + setVerticalTabIndex?.(index); }; const VerticalTab = ({ - action_bar, - action_bar_classname, className, current_path, extra_content, @@ -47,7 +82,7 @@ const VerticalTab = ({ is_grid, is_full_width, is_routed, - is_sidebar_enabled, + is_sidebar_enabled = true, list, list_groups, onClickClose, @@ -55,11 +90,12 @@ const VerticalTab = ({ tab_headers_note, title, vertical_tab_index, -}) => { +}: TVerticalTab) => { const [curr_tab_index, setCurrTabIndex] = React.useState(vertical_tab_index || 0); - const changeSelected = e => { + const changeSelected = (e: TItem) => { setSelectedIndex({ + current_path, list, selected_index: e, setCurrTabIndex, @@ -119,7 +155,6 @@ const VerticalTab = ({ is_floating={is_floating} is_routed={is_routed} header_title={header_title} - tab_headers_note={tab_headers_note} /> {is_floating && tab_headers_note && (
{tab_headers_note}
@@ -128,8 +163,6 @@ const VerticalTab = ({
)} (
); -const action_bar_items = [ - { - onClick: () => { - /* TODO document why this method 'onClick' is empty */ - }, - icon: 'IcCross', - title: 'Close', - }, - { - component: () => ( -
- Lorem ipsum dolor sit amet -
- ), - title: 'Test', - }, - { - component: () => ( -
- -
- ), - title: '', - }, -]; - const list = [ { default: true, @@ -98,8 +66,6 @@ stories.add(
{ ); } - return ; + return ; }; const ToggleSettings = ({ diff --git a/packages/reports/src/Containers/reports.jsx b/packages/reports/src/Containers/reports.jsx index 6d55e1a02d8a..87b8f1e76cc6 100644 --- a/packages/reports/src/Containers/reports.jsx +++ b/packages/reports/src/Containers/reports.jsx @@ -70,10 +70,7 @@ const Reports = ({ { export const numberToString = (n: number) => (typeof n === 'number' ? String(n) : n); -export const getKebabCase = (str: string) => { +export const getKebabCase = (str?: string) => { if (!str) return str; return str .replace(/([a-z0-9])([A-Z])/g, '$1-$2') // get all lowercase letters that are near to uppercase ones From 524d823c662cc3b168d00c2fb6c20ea74f8210d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cyauheni-kryzhyk-deriv=E2=80=9D?= <“yauheni@deriv.me”> Date: Mon, 20 Feb 2023 13:49:01 +0300 Subject: [PATCH 2/5] refactor: setSelectedIndex props types fix --- .../components/src/components/vertical-tab/vertical-tab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/vertical-tab/vertical-tab.tsx b/packages/components/src/components/vertical-tab/vertical-tab.tsx index ba94efa4dd2e..78d002432dea 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab.tsx +++ b/packages/components/src/components/vertical-tab/vertical-tab.tsx @@ -14,7 +14,7 @@ type TSetSelectedIndex = { current_path: string; list: TItem[]; is_routed?: boolean; - selected_index?: number | TItem | undefined; + selected_index?: number | TItem; setCurrTabIndex: (index: number) => void; setVerticalTabIndex?: (index: number) => void; }; From 44d5113e7f8cdefa1e60f61d564bd927376412bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cyauheni-kryzhyk-deriv=E2=80=9D?= <“yauheni@deriv.me”> Date: Mon, 20 Feb 2023 14:19:05 +0300 Subject: [PATCH 3/5] refactor: side notes array is set incorrectly --- .../vertical-tab/vertical-tab-content-container.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx b/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx index ed8860861159..bd6bb2b77729 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx @@ -53,16 +53,16 @@ const Content = ({ is_routed, items, selected }: TContent) => { const TabContent = selected_item?.value as React.ElementType; const [side_notes, setSideNotes] = React.useState(null); - const notes_array: React.ReactNode[] = []; + const notes_array: React.ReactNode[][] = []; - const addToNotesQueue = React.useCallback((notes: React.ReactNode) => { + const addToNotesQueue = React.useCallback((notes: React.ReactNode[]) => { notes_array.unshift(notes); - setSideNotes(notes_array); + setSideNotes(notes); }, []); React.useEffect(() => { if (selected_item?.label !== previous_selected_item?.label) { - setSideNotes(notes_array[0] ? notes_array : null); + setSideNotes(notes_array[0] ?? null); notes_array.splice(0, notes_array.length); } // eslint-disable-next-line react-hooks/exhaustive-deps From 71c38f547cc81110e03750444e306401c32b38f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cyauheni-kryzhyk-deriv=E2=80=9D?= <“yauheni@deriv.me”> Date: Tue, 21 Feb 2023 15:42:25 +0300 Subject: [PATCH 4/5] refactor: review suggestion implementation --- .../vertical-tab-content-container.tsx | 37 +++++++++++++++++++ .../vertical-tab-header-group.tsx | 2 +- .../vertical-tab/vertical-tab-header.tsx | 14 +++---- .../components/vertical-tab/vertical-tab.tsx | 8 +++- .../vertical-tabs/vertical-tabs.stories.js | 33 +++++++++++++++++ 5 files changed, 84 insertions(+), 10 deletions(-) diff --git a/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx b/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx index bd6bb2b77729..b478119191a7 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx @@ -2,6 +2,7 @@ import classNames from 'classnames'; import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { usePrevious } from '../../hooks'; +import Icon from '../icon/icon'; import { TItem } from './vertical-tab-header'; type TSideNotes = { @@ -19,8 +20,18 @@ type TContent = { selected: TItem; }; +export type TAction_bar = { + component?: typeof React.Component; + onClick?: () => void; + icon: string; + title?: string; +}; + type TVerticalTabContentContainer = TContent & { + action_bar?: TAction_bar[]; + action_bar_classname?: string; className?: string; + id?: string; is_floating?: boolean; tab_container_classname?: string; }; @@ -96,7 +107,10 @@ const Content = ({ is_routed, items, selected }: TContent) => { }; const VerticalTabContentContainer = ({ + action_bar, + action_bar_classname, className, + id, is_floating, is_routed, items, @@ -110,6 +124,29 @@ const VerticalTabContentContainer = ({ [`dc-vertical-tab__content--${className}`]: className, })} > + {!is_floating && action_bar && ( +
+ {action_bar.map(({ component, icon, onClick }, idx) => { + const Component = component; + return Component ? ( + + ) : ( +
+ +
+ ); + })} +
+ )}
diff --git a/packages/components/src/components/vertical-tab/vertical-tab-header-group.tsx b/packages/components/src/components/vertical-tab/vertical-tab-header-group.tsx index a5be24467cde..db6280d8ea19 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-header-group.tsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-header-group.tsx @@ -41,7 +41,7 @@ const VerticalTabHeaderGroup = ({ onToggle(true); }, [show_items, onToggle]); - const label = group.getTitle ? group.getTitle() : group.label || group.title; + const label = group.getTitle ? group.getTitle() : group.label || group.title || ''; const handleClick = () => { if (!group.subitems) { onChange(group); diff --git a/packages/components/src/components/vertical-tab/vertical-tab-header.tsx b/packages/components/src/components/vertical-tab/vertical-tab-header.tsx index 99d6683b2653..766f8b32b624 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-header.tsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-header.tsx @@ -11,7 +11,7 @@ export type THeaderIcon = { }; export type THeader = { - text?: string; + text: string; path?: string; }; @@ -65,7 +65,7 @@ const VerticalTabHeader = ({ selected, selectedKey = 'label', }: React.PropsWithChildren) => { - const label = item.label || item.title || item.getTitle?.(); + const label = item.label || item.title || item.getTitle?.() || ''; const is_active = selected && selected[selectedKey as keyof TItem] === item[selectedKey as keyof TItem]; const handleClick = () => onChange(item); const id = `dc_${getKebabCase(label)}_link`; @@ -102,12 +102,10 @@ const VerticalTabHeader = ({ })} onClick={handleClick} > - <> - -
- {children} - {item.component} - + +
+ {children} + <>{item.component}
); }; diff --git a/packages/components/src/components/vertical-tab/vertical-tab.tsx b/packages/components/src/components/vertical-tab/vertical-tab.tsx index 78d002432dea..4ed62b915777 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab.tsx +++ b/packages/components/src/components/vertical-tab/vertical-tab.tsx @@ -1,7 +1,7 @@ import React from 'react'; import classNames from 'classnames'; import { matchRoute } from '@deriv/shared'; -import VerticalTabContentContainer from './vertical-tab-content-container'; +import VerticalTabContentContainer, { TAction_bar } from './vertical-tab-content-container'; import VerticalTabHeader, { TItem } from './vertical-tab-header'; import VerticalTabHeaderGroup from './vertical-tab-header-group'; import VerticalTabHeaders from './vertical-tab-headers'; @@ -20,6 +20,8 @@ type TSetSelectedIndex = { }; type TVerticalTab = { + action_bar?: TAction_bar[]; + action_bar_classname?: string; className?: string; current_path: string; extra_content?: React.ReactNode | React.ReactNode[]; @@ -70,6 +72,8 @@ const setSelectedIndex = ({ }; const VerticalTab = ({ + action_bar, + action_bar_classname, className, current_path, extra_content, @@ -163,6 +167,8 @@ const VerticalTab = ({
)} (
); +const action_bar_items = [ + { + onClick: () => { + /* TODO document why this method 'onClick' is empty */ + }, + icon: 'IcCross', + title: 'Close', + }, + { + component: () => ( +
+ Lorem ipsum dolor sit amet +
+ ), + title: 'Test', + }, + { + component: () => ( +
+ +
+ ), + title: '', + }, +]; + const list = [ { default: true, @@ -66,6 +98,7 @@ stories.add(
Date: Wed, 22 Feb 2023 12:43:42 +0300 Subject: [PATCH 5/5] refactor: exclamation mark replacing --- .../components/vertical-tab/vertical-tab-content-container.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx b/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx index b478119191a7..0e6951b742af 100644 --- a/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx +++ b/packages/components/src/components/vertical-tab/vertical-tab-content-container.tsx @@ -95,7 +95,7 @@ const Content = ({ is_routed, items, selected }: TContent) => { })} ) : ( - + )} {selected.has_side_note && ( // for components that have side note, even if no note is passed currently,