From 77b8b3e293488e855b9774a1cf64a69c28d32a4a Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Mon, 15 Jan 2024 16:36:02 +0100 Subject: [PATCH 1/7] feat: add evm explorer links to the header --- client/src/app/App.tsx | 3 +- client/src/app/AppUtils.tsx | 12 --- client/src/app/components/header/Header.tsx | 76 +++++++++++-------- .../src/app/components/header/HeaderProps.ts | 15 ---- .../src/app/components/header/HeaderState.ts | 2 +- 5 files changed, 46 insertions(+), 62 deletions(-) diff --git a/client/src/app/App.tsx b/client/src/app/App.tsx index 7e543fc55..fbfc0f3a7 100644 --- a/client/src/app/App.tsx +++ b/client/src/app/App.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from "react"; import { Helmet } from "react-helmet"; import { RouteComponentProps } from "react-router-dom"; import { AppRouteProps } from "./AppRouteProps"; -import { buildMetaLabel, buildUtilities, getFooterItems, getPages, getFaviconHelmet, networkContextWrapper } from "./AppUtils"; +import { buildMetaLabel, getFooterItems, getPages, getFaviconHelmet, networkContextWrapper } from "./AppUtils"; import Disclaimer from "./components/Disclaimer"; import Footer from "./components/footer/Footer"; import ShimmerFooter from "./components/footer/ShimmerFooter"; @@ -87,7 +87,6 @@ const App: React.FC> = ({ /> } pages={getPages(networkConfig, networks)} - utilities={buildUtilities(network ?? "", networks, identityResolverEnabled)} />
{networks.length > 0 ? ( diff --git a/client/src/app/AppUtils.tsx b/client/src/app/AppUtils.tsx index b75c349ee..7f41f2817 100644 --- a/client/src/app/AppUtils.tsx +++ b/client/src/app/AppUtils.tsx @@ -40,18 +40,6 @@ export const getPages = (currentNetwork: INetwork | undefined, networks: INetwor return pages; }; -export const buildUtilities = (currentNetwork: string, networks: INetwork[], identityResolverEnabled: boolean) => { - const utilities = []; - if (networks.length > 0 && currentNetwork !== CHRYSALIS_MAINNET) { - utilities.push({ label: "Streams v0", url: `/${currentNetwork}/streams/0/` }); - if (identityResolverEnabled) { - utilities.push({ label: "Decentralized Identifier", url: `/${currentNetwork}/identity-resolver/` }); - } - } - - return utilities; -}; - /** * Creates footer items. Excludes the Identity Resolver if the network is not supported. * @param currentNetwork The currently selected network. diff --git a/client/src/app/components/header/Header.tsx b/client/src/app/components/header/Header.tsx index 88e647d0f..45542d88b 100644 --- a/client/src/app/components/header/Header.tsx +++ b/client/src/app/components/header/Header.tsx @@ -38,7 +38,7 @@ class Header extends Component { this.state = { isNetworkSwitcherExpanded: false, - isUtilitiesExpanded: false, + isEvmDropdownExpanded: false, isMenuExpanded: false, darkMode: this._settingsService.get().darkMode ?? false, show: false, @@ -59,10 +59,24 @@ class Header extends Component { * @returns The node to render. */ public render(): ReactNode { - const { rootPath, currentNetwork, networks, history, action, search, utilities, pages } = this.props; + const { rootPath, currentNetwork, networks, history, action, search, pages } = this.props; const isShimmerUi = isShimmerUiTheme(currentNetwork?.uiTheme); const isMarketed = isMarketedNetwork(currentNetwork?.network); + const EVM_EXPLORER_DROPDOWN = { + label: "EVM Explorer", + routes: [ + { + label: "EVM Explorer", + url: "https://explorer.evm.shimmer.network/", + }, + { + label: "EVM Explorer Testnet", + url: "https://explorer.evm.testnet.shimmer.network/" + } + ] + } + return (
+
+ ); } - -export default Header; diff --git a/client/src/app/components/header/HeaderDropdown.tsx b/client/src/app/components/header/HeaderDropdown.tsx new file mode 100644 index 000000000..141410809 --- /dev/null +++ b/client/src/app/components/header/HeaderDropdown.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { IGroupRoute } from "./Header"; +import classNames from "classnames"; +import { Link } from "react-router-dom"; + +export default function GroupDropdown({ label, routes, isExpanded, setExpandedDropdownId }: IGroupRoute): React.JSX.Element { + function toggleOpen(): void { + setExpandedDropdownId(isExpanded ? undefined : label); + } + + function closeDropdown(e: React.MouseEvent): void { + setExpandedDropdownId(); + } + + return ( +
+
+
{label}
+
+ expand_more +
+
+ +
+
+
{label}
+ {routes.map((route) => ( +
+ + {route.label} + +
+ ))} +
+
+ {isExpanded &&
} +
+ ); +} From 4642a0f6b94ee2dcc6bbc30d029bf11fc55a67cf Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Thu, 25 Jan 2024 20:40:27 +0100 Subject: [PATCH 4/7] refactor: simplify header component --- client/src/app/App.tsx | 11 +- client/src/app/AppUtils.tsx | 69 +++++-- client/src/app/components/header/Header.scss | 27 ++- client/src/app/components/header/Header.tsx | 179 +++++++----------- .../app/components/header/HeaderDropdown.tsx | 161 ++++++++++++---- .../src/app/components/header/HeaderProps.ts | 53 ------ .../src/app/components/header/HeaderState.ts | 29 --- .../header/NavigationRouteHelper.tsx | 18 ++ client/src/app/lib/interfaces/index.ts | 1 + .../app/lib/interfaces/routes.interfaces.ts | 15 ++ 10 files changed, 291 insertions(+), 272 deletions(-) delete mode 100644 client/src/app/components/header/HeaderProps.ts delete mode 100644 client/src/app/components/header/HeaderState.ts create mode 100644 client/src/app/components/header/NavigationRouteHelper.tsx create mode 100644 client/src/app/lib/interfaces/index.ts create mode 100644 client/src/app/lib/interfaces/routes.interfaces.ts diff --git a/client/src/app/App.tsx b/client/src/app/App.tsx index ab94b1ee2..10ec286e4 100644 --- a/client/src/app/App.tsx +++ b/client/src/app/App.tsx @@ -8,7 +8,6 @@ import Disclaimer from "./components/Disclaimer"; import Footer from "./components/footer/Footer"; import ShimmerFooter from "./components/footer/ShimmerFooter"; import Header from "./components/header/Header"; -import SearchInput from "./components/SearchInput"; import buildAppRoutes from "./routes"; import { ServiceFactory } from "~factories/serviceFactory"; import { isShimmerUiTheme } from "~helpers/networkHelper"; @@ -62,6 +61,7 @@ const App: React.FC> = ({ } const routes = buildAppRoutes(networkConfig?.protocolVersion ?? "", withNetworkContext); + const pages = getPages(networkConfig, networks); const metaLabel = buildMetaLabel(currentNetworkName); const faviconHelmet = getFaviconHelmet(isShimmer); @@ -81,13 +81,8 @@ const App: React.FC> = ({ networks={networks} action={action} history={history} - search={ - history.push(`/${currentNetworkName}/search/${query}`)} - protocolVersion={networkConfig?.protocolVersion ?? STARDUST} - /> - } - pages={getPages(networkConfig, networks)} + protocolVersion={protocolVersion} + pages={pages} />
{networks.length > 0 ? ( diff --git a/client/src/app/AppUtils.tsx b/client/src/app/AppUtils.tsx index b53930ab1..d5bef62f1 100644 --- a/client/src/app/AppUtils.tsx +++ b/client/src/app/AppUtils.tsx @@ -5,6 +5,7 @@ import { INetwork } from "~models/config/INetwork"; import { ALPHANET, CHRYSALIS_MAINNET, DEVNET, LEGACY_MAINNET, MAINNET, NetworkType, SHIMMER, TESTNET } from "~models/config/networkType"; import { IOTA_UI, Theme } from "~models/config/uiTheme"; import { IReducedNodeInfo } from "~services/nodeInfoService"; +import { IDropdownRoute, NavigationRoute } from "./lib/interfaces"; export const networkContextWrapper = (currentNetwork: string | undefined, nodeInfo: IReducedNodeInfo | null, uiTheme: Theme | undefined) => function withNetworkContext(wrappedComponent: ReactNode) { @@ -24,30 +25,62 @@ export const networkContextWrapper = (currentNetwork: string | undefined, nodeIn ) : null; }; -export const getPages = (currentNetwork: INetwork | undefined, networks: INetwork[]) => { - const pages = []; +export const getPages = (currentNetwork: INetwork | undefined, networks: INetwork[]): NavigationRoute[] => { + const pages: NavigationRoute[] = []; + if (networks.length > 0 && currentNetwork !== undefined) { - pages.push({ label: "Explorer", url: `/${currentNetwork.network}/` }); - pages.push({ label: "Visualizer", url: `/${currentNetwork.network}/visualizer/` }); + const { network, hasStatisticsSupport } = currentNetwork; - if (currentNetwork.hasStatisticsSupport) { - pages.push({ label: "Statistics", url: `/${currentNetwork.network}/statistics/` }); - } + const networkRoutes: NavigationRoute[] = [ + { + label: "Explorer", + url: `/${network}/`, + }, + { + label: "Visualizer", + url: `/${network}/visualizer/`, + }, + { + label: "Statistics", + url: `/${network}/statistics/`, + disabled: !hasStatisticsSupport, + }, + { + label: "Utilities", + disabled: network !== CHRYSALIS_MAINNET, + routes: [ + { label: "Streams v0", url: `/${network}/streams/0/` }, + { + label: "Decentralized Identifier", + url: `/${network}/identity-resolver/`, + disabled: network !== CHRYSALIS_MAINNET, + }, + ], + }, + ]; + + pages.push(...networkRoutes); } - return pages; -}; + const EVM_EXPLORER_DROPDOWN: IDropdownRoute = { + label: "EVM Explorer", + routes: [ + { + label: "EVM Explorer", + url: "https://explorer.evm.shimmer.network/", + isExternal: true, + }, + { + label: "EVM Explorer Testnet", + url: "https://explorer.evm.testnet.shimmer.network/", + isExternal: true, + }, + ], + }; -export const buildUtilities = (currentNetwork: string, networks: INetwork[], identityResolverEnabled: boolean) => { - const utilities = []; - if (networks.length > 0) { - utilities.push({ label: "Streams v0", url: `/${currentNetwork}/streams/0/` }); - if (identityResolverEnabled) { - utilities.push({ label: "Decentralized Identifier", url: `/${currentNetwork}/identity-resolver/` }); - } - } + pages.push(EVM_EXPLORER_DROPDOWN); - return utilities; + return pages; }; /** diff --git a/client/src/app/components/header/Header.scss b/client/src/app/components/header/Header.scss index 339ff9022..cda81c264 100644 --- a/client/src/app/components/header/Header.scss +++ b/client/src/app/components/header/Header.scss @@ -150,21 +150,25 @@ header { } .navigation--item, - .utilities--wrapper { + .header-dropdown--wrapper { @include font-size(14px, 21px); display: flex; align-items: center; height: 100%; - margin-left: 40px; color: var(--navbar-color); font-family: $metropolis; font-weight: 600; letter-spacing: 0.01em; + margin-left: 32px; + + &:first-child { + margin-left: 40px; + } } - .utilities--wrapper { - .utilities--dropdown { + .header-dropdown--wrapper { + .header-dropdown--dropdown { display: flex; align-items: center; font-family: $metropolis; @@ -181,7 +185,7 @@ header { .icon { display: flex; align-items: center; - margin-left: 8px; + margin-left: 4px; span { margin-bottom: 3px; @@ -204,14 +208,14 @@ header { } } - .utilities { + .header-dropdown { padding: 64px 120px 120px 120px; & * { margin-bottom: 8px; } - .utilities--label { + .header-dropdown--label { color: var(--navbar-color); font-family: $metropolis; font-weight: 700; @@ -221,7 +225,7 @@ header { @include font-size(14px, 21px); } - .utilities--item a { + .header-dropdown--item a { color: $gray-7; font-family: $inter; letter-spacing: 0.5px; @@ -231,11 +235,14 @@ header { } } - .utilities--mobile { + .header-dropdown--mobile { transition: opacity 0.3s ease-in-out; opacity: 0; + height: 0; + overflow: hidden; &.opened { + height: auto; opacity: 1; } } @@ -296,7 +303,7 @@ header { @include desktop-down { .navigation--item, - .utilities--wrapper, + .header-dropdown--wrapper, .search-input { display: none; } diff --git a/client/src/app/components/header/Header.tsx b/client/src/app/components/header/Header.tsx index 171c7b9d2..61f50a265 100644 --- a/client/src/app/components/header/Header.tsx +++ b/client/src/app/components/header/Header.tsx @@ -3,6 +3,7 @@ import * as H from "history"; import classNames from "classnames"; import { Link } from "react-router-dom"; import Logo from "~assets/logo-header.svg?react"; +import { IDropdownRoute, IRoute } from "~/app/lib/interfaces"; import mainChrysalisMessage from "~assets/modals/chrysalis/search/main-header.json"; import mainLegacyMessage from "~assets/modals/legacy/search/main-header.json"; import mainStardustMessage from "~assets/modals/stardust/search/main-header.json"; @@ -16,23 +17,15 @@ import Modal from "../Modal"; import NetworkSwitcher from "../NetworkSwitcher"; import { INetwork } from "~/models/config/INetwork"; import SearchInput from "../SearchInput"; +import HeaderDropdown from "./HeaderDropdown"; import "./Header.scss"; -import GroupDropdown from "./HeaderDropdown"; const NETWORK_DROPDOWN_LABEL = "Network Switcher"; -const EVM_EXPLORER_DROPDOWN = { - label: "EVM Explorer", - routes: [ - { - label: "EVM Explorer", - url: "https://explorer.evm.shimmer.network/", - }, - { - label: "EVM Explorer Testnet", - url: "https://explorer.evm.testnet.shimmer.network/", - }, - ], +const MODAL_MESSAGE: Record = { + [LEGACY]: mainLegacyMessage, + [CHRYSALIS]: mainChrysalisMessage, + [STARDUST]: mainStardustMessage, }; interface IHeader { @@ -42,24 +35,10 @@ interface IHeader { history?: H.History; action?: string; protocolVersion: ProtocolVersion; - pages?: Route[]; + pages?: (IRoute | IDropdownRoute)[]; } -export type Route = IRoute | IGroupRoute; - -export interface IRoute { - label: string; - url: string; -} - -export interface IGroupRoute { - label: string; - isExpanded: boolean; - setExpandedDropdownId: (state?: string) => void; - routes: IRoute[]; -} - -export default function Header({ rootPath, currentNetwork, networks, history, action, protocolVersion, pages }: IHeader) { +export default function Header({ rootPath, currentNetwork, networks, history, action, protocolVersion, pages: routes }: IHeader) { const settingsService = ServiceFactory.get("settings"); const [isMenuExpanded, setIsMenuExpanded] = useState(false); @@ -67,7 +46,6 @@ export default function Header({ rootPath, currentNetwork, networks, history, ac const [show, setShow] = useState(false); const [expandedDropdownLabel, setExpandedDropdownLabel] = useState(); - const isEvmDropdownExpanded = expandedDropdownLabel === EVM_EXPLORER_DROPDOWN.label; const isNetworkSwitcherExpanded = expandedDropdownLabel === NETWORK_DROPDOWN_LABEL; const isShimmerUi = isShimmerUiTheme(currentNetwork?.uiTheme); const isMarketed = isMarketedNetwork(currentNetwork?.network); @@ -103,11 +81,7 @@ export default function Header({ rootPath, currentNetwork, networks, history, ac setExpandedDropdownLabel(undefined); } - function toggleNetworkSwitcher(): void { - setExpandedDropdownLabel(isNetworkSwitcherExpanded ? undefined : NETWORK_DROPDOWN_LABEL); - } - - function routeIsDropdown(route: Route): route is IGroupRoute { + function routeIsDropdown(route: IRoute | IDropdownRoute): route is IDropdownRoute { return Object.prototype.hasOwnProperty.call(route, "routes"); } @@ -128,48 +102,45 @@ export default function Header({ rootPath, currentNetwork, networks, history, ac )} - {pages && - pages.length > 0 && - pages.map( - (page) => - !routeIsDropdown(page) && ( - - {page.label} - - ), + {routes && + routes.length > 0 && + routes.map((route) => + !routeIsDropdown(route) ? ( + + {route.label} + + ) : ( + + ), )} - {/* EVM DROPDOWN */} - - {/* ----- Only visible in mobile ----- */} {isMarketed && (
)} - {/* ---------- */} history?.push(`/${currentNetwork?.network}/search/${query}`)} protocolVersion={protocolVersion} /> - {currentNetwork?.protocolVersion === LEGACY && } - {currentNetwork?.protocolVersion === CHRYSALIS && ( - - )} - {currentNetwork?.protocolVersion === STARDUST && ( - + + {currentNetwork?.protocolVersion && ( + )} {/* ----- Only visible in desktop ----- */} @@ -179,10 +150,13 @@ export default function Header({ rootPath, currentNetwork, networks, history, ac
)}
- {/* ---------- */} + + {/* Theme Button */} + + {/* Hamburger Menu */}
+ + {/* ----- Menu: Only visible in mobile ----- */}
    - {pages && - pages.length > 0 && - pages.map( - (page) => - !routeIsDropdown(page) && ( - -
  • - - {page.label} - -
  • - - ), + {routes && + routes.length > 0 && + routes.map((route) => + !routeIsDropdown(route) ? ( + +
  • + + {route.label} + +
  • + + ) : ( + + ), )} -
  • -
    {EVM_EXPLORER_DROPDOWN.label}
    -
    - expand_more -
    -
  • - {/* ----- EVM DROPDOWN MOBILE ----- */} -
    - {EVM_EXPLORER_DROPDOWN.routes.map((route) => ( - -
  • - {route.label} -
  • - - ))} -
    - {/* ---------- */}
+ + {/* ----- Network Switcher ----- */}
setExpandedDropdownLabel(isNetworkSwitcherExpanded ? undefined : NETWORK_DROPDOWN_LABEL)} onChange={(targetNetwork) => { history?.push( action === "streams" diff --git a/client/src/app/components/header/HeaderDropdown.tsx b/client/src/app/components/header/HeaderDropdown.tsx index 141410809..2405715d1 100644 --- a/client/src/app/components/header/HeaderDropdown.tsx +++ b/client/src/app/components/header/HeaderDropdown.tsx @@ -1,52 +1,131 @@ import React from "react"; -import { IGroupRoute } from "./Header"; +import { IDropdownRoute } from "~/app/lib/interfaces"; import classNames from "classnames"; -import { Link } from "react-router-dom"; +import NavigationRouteHelper from "./NavigationRouteHelper"; -export default function GroupDropdown({ label, routes, isExpanded, setExpandedDropdownId }: IGroupRoute): React.JSX.Element { - function toggleOpen(): void { - setExpandedDropdownId(isExpanded ? undefined : label); - } +interface INavigationDropdown extends IDropdownRoute { + isExpanded: boolean; + setExpandedDropdownId: (label?: string) => void; + setIsMenuExpanded?: (isExpanded: boolean) => void; +} - function closeDropdown(e: React.MouseEvent): void { - setExpandedDropdownId(); - } +interface IDropdownProps extends INavigationDropdown { + toggleDropdown: () => void; +} + +/** + * Dropdown component for header. + */ +export default function HeaderDropdown(props: INavigationDropdown & { mobileOnly?: boolean }): React.JSX.Element { + const { isExpanded, setExpandedDropdownId, mobileOnly, label } = props; + const DropdownComponent = mobileOnly ? MobileDropdown : DesktopDropdown; + + const toggleDropdown = (): void => setExpandedDropdownId(isExpanded ? undefined : label); + + return ; +} + +/** + * Dropdown component for desktop. + */ +const DesktopDropdown = ({ label, disabled, routes, isExpanded, toggleDropdown, setExpandedDropdownId }: IDropdownProps) => { + const closeDropdown = (e?: React.MouseEvent): void => setExpandedDropdownId(); return ( -
-
-
{label}
-
- expand_more + !disabled && ( +
+
+
{label}
+
+ expand_more +
-
-
-
-
{label}
- {routes.map((route) => ( -
- - {route.label} - -
- ))} +
+
+
{label}
+ {routes + .filter(({ disabled }) => !disabled) + .map((route) => ( +
+ + {route.label} + +
+ ))} +
+ {isExpanded &&
}
- {isExpanded &&
} -
+ ) ); -} +}; + +/** + * Dropdown component for mobile. + */ +const MobileDropdown = ({ + label, + disabled, + routes, + isExpanded, + toggleDropdown, + setExpandedDropdownId, + setIsMenuExpanded, +}: IDropdownProps) => { + function handleRouteClick(e?: React.MouseEvent): void { + setExpandedDropdownId(); + setIsMenuExpanded?.(false); + } + + return ( + !disabled && ( + <> +
  • +
    {label}
    +
    + expand_more +
    +
  • +
    + {routes + .filter(({ disabled }) => !disabled) + .map((route) => ( + +
  • + {route.label} +
  • +
    + ))} +
    + + ) + ); +}; diff --git a/client/src/app/components/header/HeaderProps.ts b/client/src/app/components/header/HeaderProps.ts deleted file mode 100644 index 0643b2240..000000000 --- a/client/src/app/components/header/HeaderProps.ts +++ /dev/null @@ -1,53 +0,0 @@ -import * as H from "history"; -import { ReactNode } from "react"; -import { INetwork } from "~models/config/INetwork"; - -/** - * The props for the Header component. - */ -export interface HeaderProps { - /** - * The root path. - */ - rootPath: string; - - /** - * The currently selected network. - */ - currentNetwork?: INetwork; - - /** - * The networks available. - */ - networks: INetwork[]; - - /** - * History for navigation. - */ - history?: H.History; - - /** - * Action for navigation. - */ - action?: string; - - /** - * The search elements to display as content. - */ - search?: ReactNode; - - /** - * Pages menu - */ - pages?: { - /** - * The label for the page. - */ - label: string; - - /** - * The link for the page. - */ - url: string; - }[]; -} diff --git a/client/src/app/components/header/HeaderState.ts b/client/src/app/components/header/HeaderState.ts deleted file mode 100644 index a843ef677..000000000 --- a/client/src/app/components/header/HeaderState.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * The state for the Header component. - */ -export interface HeaderState { - /** - * Is the network switcher menu expanded. - */ - isNetworkSwitcherExpanded: boolean; - - /** - * Is the utilities menu expanded. - */ - isEvmDropdownExpanded: boolean; - - /** - * Is the hamburger menu expanded. - */ - isMenuExpanded: boolean; - - /** - * Darkmode theme - */ - darkMode: boolean; - - /** - * Show info modal on full page. - */ - show: boolean; -} diff --git a/client/src/app/components/header/NavigationRouteHelper.tsx b/client/src/app/components/header/NavigationRouteHelper.tsx new file mode 100644 index 000000000..39ccc2128 --- /dev/null +++ b/client/src/app/components/header/NavigationRouteHelper.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import { IRoute } from "~/app/lib/interfaces"; +import { Link } from "react-router-dom"; + +type InternalRouteProps = React.ComponentPropsWithoutRef; +type ExternalRouteProps = React.DetailedHTMLProps, HTMLAnchorElement>; + +type Route = (InternalRouteProps | ExternalRouteProps) & { route: IRoute }; + +export default function NavigationRouteHelper({ children, route, ...linkProps }: React.PropsWithChildren) { + if (route.isExternal) { + const externalProps: ExternalRouteProps = { ...linkProps, href: route.url, target: "_blank", rel: "noopener noreferrer" }; + return {children}; + } else { + const internalProps: InternalRouteProps = { ...linkProps, to: route.url }; + return {children}; + } +} diff --git a/client/src/app/lib/interfaces/index.ts b/client/src/app/lib/interfaces/index.ts new file mode 100644 index 000000000..204986b69 --- /dev/null +++ b/client/src/app/lib/interfaces/index.ts @@ -0,0 +1 @@ +export * from "./routes.interfaces"; diff --git a/client/src/app/lib/interfaces/routes.interfaces.ts b/client/src/app/lib/interfaces/routes.interfaces.ts new file mode 100644 index 000000000..63655211e --- /dev/null +++ b/client/src/app/lib/interfaces/routes.interfaces.ts @@ -0,0 +1,15 @@ +export interface IBaseNavigationRoute { + label: string; + disabled?: boolean; +} + +export interface IRoute extends IBaseNavigationRoute { + url: string; + isExternal?: boolean; +} + +export interface IDropdownRoute extends IBaseNavigationRoute { + routes: IRoute[]; +} + +export type NavigationRoute = IRoute | IDropdownRoute; From 4ebfe15166110610605131b33582300419a47ab6 Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Thu, 25 Jan 2024 20:45:30 +0100 Subject: [PATCH 5/7] fix: type checking --- .../app/components/header/HeaderDropdown.tsx | 153 ++++++++++-------- 1 file changed, 82 insertions(+), 71 deletions(-) diff --git a/client/src/app/components/header/HeaderDropdown.tsx b/client/src/app/components/header/HeaderDropdown.tsx index 2405715d1..7a2e6f116 100644 --- a/client/src/app/components/header/HeaderDropdown.tsx +++ b/client/src/app/components/header/HeaderDropdown.tsx @@ -28,49 +28,58 @@ export default function HeaderDropdown(props: INavigationDropdown & { mobileOnly /** * Dropdown component for desktop. */ -const DesktopDropdown = ({ label, disabled, routes, isExpanded, toggleDropdown, setExpandedDropdownId }: IDropdownProps) => { +const DesktopDropdown = ({ + label, + disabled, + routes, + isExpanded, + toggleDropdown, + setExpandedDropdownId, +}: IDropdownProps): React.JSX.Element => { const closeDropdown = (e?: React.MouseEvent): void => setExpandedDropdownId(); return ( - !disabled && ( -
    -
    -
    {label}
    -
    - expand_more + <> + {!disabled && ( +
    +
    +
    {label}
    +
    + expand_more +
    -
    -
    -
    -
    {label}
    - {routes - .filter(({ disabled }) => !disabled) - .map((route) => ( -
    - - {route.label} - -
    - ))} +
    +
    +
    {label}
    + {routes + .filter(({ disabled }) => !disabled) + .map((route) => ( +
    + + {route.label} + +
    + ))} +
    + {isExpanded &&
    }
    - {isExpanded &&
    } -
    - ) + )} + ); }; @@ -85,47 +94,49 @@ const MobileDropdown = ({ toggleDropdown, setExpandedDropdownId, setIsMenuExpanded, -}: IDropdownProps) => { +}: IDropdownProps): React.JSX.Element => { function handleRouteClick(e?: React.MouseEvent): void { setExpandedDropdownId(); setIsMenuExpanded?.(false); } return ( - !disabled && ( - <> -
  • -
    {label}
    -
    - expand_more + <> + {disabled && ( + <> +
  • +
    {label}
    +
    + expand_more +
    +
  • +
    + {routes + .filter(({ disabled }) => !disabled) + .map((route) => ( + +
  • + {route.label} +
  • +
    + ))}
    - -
    - {routes - .filter(({ disabled }) => !disabled) - .map((route) => ( - -
  • - {route.label} -
  • -
    - ))} -
    - - ) + + )} + ); }; From 8ee9c4a14807054f55701b40808b8d5ebd7e797f Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Fri, 26 Jan 2024 10:26:11 +0100 Subject: [PATCH 6/7] fix: move evm explorer next to Explorer --- client/src/app/AppUtils.tsx | 107 +++++++++++++++++------------------- 1 file changed, 51 insertions(+), 56 deletions(-) diff --git a/client/src/app/AppUtils.tsx b/client/src/app/AppUtils.tsx index d5bef62f1..45e13981c 100644 --- a/client/src/app/AppUtils.tsx +++ b/client/src/app/AppUtils.tsx @@ -5,7 +5,7 @@ import { INetwork } from "~models/config/INetwork"; import { ALPHANET, CHRYSALIS_MAINNET, DEVNET, LEGACY_MAINNET, MAINNET, NetworkType, SHIMMER, TESTNET } from "~models/config/networkType"; import { IOTA_UI, Theme } from "~models/config/uiTheme"; import { IReducedNodeInfo } from "~services/nodeInfoService"; -import { IDropdownRoute, NavigationRoute } from "./lib/interfaces"; +import { NavigationRoute } from "./lib/interfaces"; export const networkContextWrapper = (currentNetwork: string | undefined, nodeInfo: IReducedNodeInfo | null, uiTheme: Theme | undefined) => function withNetworkContext(wrappedComponent: ReactNode) { @@ -26,61 +26,56 @@ export const networkContextWrapper = (currentNetwork: string | undefined, nodeIn }; export const getPages = (currentNetwork: INetwork | undefined, networks: INetwork[]): NavigationRoute[] => { - const pages: NavigationRoute[] = []; - - if (networks.length > 0 && currentNetwork !== undefined) { - const { network, hasStatisticsSupport } = currentNetwork; - - const networkRoutes: NavigationRoute[] = [ - { - label: "Explorer", - url: `/${network}/`, - }, - { - label: "Visualizer", - url: `/${network}/visualizer/`, - }, - { - label: "Statistics", - url: `/${network}/statistics/`, - disabled: !hasStatisticsSupport, - }, - { - label: "Utilities", - disabled: network !== CHRYSALIS_MAINNET, - routes: [ - { label: "Streams v0", url: `/${network}/streams/0/` }, - { - label: "Decentralized Identifier", - url: `/${network}/identity-resolver/`, - disabled: network !== CHRYSALIS_MAINNET, - }, - ], - }, - ]; - - pages.push(...networkRoutes); - } - - const EVM_EXPLORER_DROPDOWN: IDropdownRoute = { - label: "EVM Explorer", - routes: [ - { - label: "EVM Explorer", - url: "https://explorer.evm.shimmer.network/", - isExternal: true, - }, - { - label: "EVM Explorer Testnet", - url: "https://explorer.evm.testnet.shimmer.network/", - isExternal: true, - }, - ], - }; - - pages.push(EVM_EXPLORER_DROPDOWN); - - return pages; + const hasNetworks = networks.length > 0 && currentNetwork !== undefined; + + const { network, hasStatisticsSupport } = currentNetwork ?? { network: "", hasStatisticsSupport: false }; + + const routes: NavigationRoute[] = [ + { + label: "Explorer", + url: `/${network}/`, + disabled: !hasNetworks, + }, + { + label: "EVM Explorer", + routes: [ + { + label: "EVM Explorer", + url: "https://explorer.evm.shimmer.network/", + isExternal: true, + }, + { + label: "EVM Explorer Testnet", + url: "https://explorer.evm.testnet.shimmer.network/", + isExternal: true, + }, + ], + }, + { + label: "Visualizer", + url: `/${network}/visualizer/`, + disabled: !hasNetworks, + }, + { + label: "Statistics", + url: `/${network}/statistics/`, + disabled: !hasStatisticsSupport || !hasNetworks, + }, + { + label: "Utilities", + disabled: network !== CHRYSALIS_MAINNET || !hasNetworks, + routes: [ + { label: "Streams v0", url: `/${network}/streams/0/` }, + { + label: "Decentralized Identifier", + url: `/${network}/identity-resolver/`, + disabled: network !== CHRYSALIS_MAINNET, + }, + ], + }, + ]; + + return routes; }; /** From 28ab208631f933b8d43925f859c77c2185622b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bego=C3=B1a=20Alvarez?= Date: Wed, 31 Jan 2024 15:08:40 +0100 Subject: [PATCH 7/7] enhancement: polish CSS and rename evm navbar items --- client/src/app/AppUtils.tsx | 30 ++++++++++---------- client/src/app/components/header/Header.scss | 1 + 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/client/src/app/AppUtils.tsx b/client/src/app/AppUtils.tsx index 45e13981c..26fdab134 100644 --- a/client/src/app/AppUtils.tsx +++ b/client/src/app/AppUtils.tsx @@ -36,21 +36,6 @@ export const getPages = (currentNetwork: INetwork | undefined, networks: INetwor url: `/${network}/`, disabled: !hasNetworks, }, - { - label: "EVM Explorer", - routes: [ - { - label: "EVM Explorer", - url: "https://explorer.evm.shimmer.network/", - isExternal: true, - }, - { - label: "EVM Explorer Testnet", - url: "https://explorer.evm.testnet.shimmer.network/", - isExternal: true, - }, - ], - }, { label: "Visualizer", url: `/${network}/visualizer/`, @@ -73,6 +58,21 @@ export const getPages = (currentNetwork: INetwork | undefined, networks: INetwor }, ], }, + { + label: "EVM", + routes: [ + { + label: "ShimmerEVM Explorer", + url: "https://explorer.evm.shimmer.network/", + isExternal: true, + }, + { + label: "ShimmerEVM Testnet Testnet", + url: "https://explorer.evm.testnet.shimmer.network/", + isExternal: true, + }, + ], + }, ]; return routes; diff --git a/client/src/app/components/header/Header.scss b/client/src/app/components/header/Header.scss index cda81c264..d3deb62fb 100644 --- a/client/src/app/components/header/Header.scss +++ b/client/src/app/components/header/Header.scss @@ -180,6 +180,7 @@ header { color: var(--navbar-color); font-weight: 600; letter-spacing: 0.01em; + text-wrap: nowrap; } .icon {