From e2e8e3d3df02eefa33edc52da29a0ac8857a5988 Mon Sep 17 00:00:00 2001 From: Andrew Sawers Date: Tue, 25 Nov 2025 13:43:47 +0000 Subject: [PATCH 1/4] Add .idea to .gitignore Signed-off-by: Andrew Sawers --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 763a38d8..ee49cd3f 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +.idea/ From 874a139a090b784b4544b782098dcca1be8f0912 Mon Sep 17 00:00:00 2001 From: Andrew Sawers Date: Tue, 25 Nov 2025 13:44:28 +0000 Subject: [PATCH 2/4] Enable CMD/CTRL-click navigation for sidebar items (#174) Signed-off-by: Andrew Sawers --- src/components/Navigation/ActivityNav.tsx | 27 ++++----------------- src/components/Navigation/BlockchainNav.tsx | 13 +++++----- src/components/Navigation/MyNodeNav.tsx | 9 +++---- src/components/Navigation/NavItem.tsx | 24 +++++++++++++++--- src/components/Navigation/NavSection.tsx | 7 ++++-- src/components/Navigation/Navigation.tsx | 4 +-- src/components/Navigation/NetworkNav.tsx | 13 +++++----- src/components/Navigation/OffChainNav.tsx | 15 ++++++------ src/components/Navigation/TokensNav.tsx | 13 +++++----- src/interfaces/navigation.ts | 10 ++++++-- 10 files changed, 70 insertions(+), 65 deletions(-) diff --git a/src/components/Navigation/ActivityNav.tsx b/src/components/Navigation/ActivityNav.tsx index 8ad05a38..f723e5f3 100644 --- a/src/components/Navigation/ActivityNav.tsx +++ b/src/components/Navigation/ActivityNav.tsx @@ -1,30 +1,13 @@ -// Copyright © 2022 Kaleido, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - import { InsertChartOutlined } from '@mui/icons-material'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { ApplicationContext } from '../../contexts/ApplicationContext'; import { FF_NAV_PATHS, INavItem } from '../../interfaces'; import { NavSection } from './NavSection'; export const ActivityNav = () => { const { t } = useTranslation(); - const navigate = useNavigate(); const { selectedNamespace } = useContext(ApplicationContext); const { pathname } = useLocation(); @@ -36,22 +19,22 @@ export const ActivityNav = () => { const navItems: INavItem[] = [ { name: t('timeline'), - action: () => navigate(timelinePath), + to: timelinePath, itemIsActive: pathname === timelinePath, }, { name: t('events'), - action: () => navigate(eventsPath), + to: eventsPath, itemIsActive: pathname === eventsPath, }, { name: t('transactions'), - action: () => navigate(txPath), + to: txPath, itemIsActive: pathname.startsWith(txPath), }, { name: t('operations'), - action: () => navigate(opsPath), + to: opsPath, itemIsActive: pathname === opsPath, }, ]; diff --git a/src/components/Navigation/BlockchainNav.tsx b/src/components/Navigation/BlockchainNav.tsx index 330b17a2..2ddaf248 100644 --- a/src/components/Navigation/BlockchainNav.tsx +++ b/src/components/Navigation/BlockchainNav.tsx @@ -17,14 +17,13 @@ import ViewInArIcon from '@mui/icons-material/ViewInAr'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { ApplicationContext } from '../../contexts/ApplicationContext'; import { FF_NAV_PATHS, INavItem } from '../../interfaces'; import { NavSection } from './NavSection'; export const BlockchainNav = () => { const { t } = useTranslation(); - const navigate = useNavigate(); const { selectedNamespace } = useContext(ApplicationContext); const { pathname } = useLocation(); @@ -38,27 +37,27 @@ export const BlockchainNav = () => { const navItems: INavItem[] = [ { name: t('dashboard'), - action: () => navigate(blockchainPath), + to: blockchainPath, itemIsActive: pathname === blockchainPath, }, { name: t('events'), - action: () => navigate(eventsPath), + to: eventsPath, itemIsActive: pathname === eventsPath, }, { name: t('apis'), - action: () => navigate(apiPath), + to: apiPath, itemIsActive: pathname === apiPath, }, { name: t('interfaces'), - action: () => navigate(interfacesPath), + to: interfacesPath, itemIsActive: pathname === interfacesPath, }, { name: t('listeners'), - action: () => navigate(listenersPath), + to: listenersPath, itemIsActive: pathname === listenersPath, }, ]; diff --git a/src/components/Navigation/MyNodeNav.tsx b/src/components/Navigation/MyNodeNav.tsx index 9e9d0070..45f2a2d4 100644 --- a/src/components/Navigation/MyNodeNav.tsx +++ b/src/components/Navigation/MyNodeNav.tsx @@ -17,14 +17,13 @@ import HexagonIcon from '@mui/icons-material/Hexagon'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { ApplicationContext } from '../../contexts/ApplicationContext'; import { FF_NAV_PATHS, INavItem } from '../../interfaces'; import { NavSection } from './NavSection'; export const MyNodeNav = () => { const { t } = useTranslation(); - const navigate = useNavigate(); const { selectedNamespace } = useContext(ApplicationContext); const { pathname } = useLocation(); @@ -37,17 +36,17 @@ export const MyNodeNav = () => { const navItems: INavItem[] = [ { name: t('dashboard'), - action: () => navigate(myNodePath), + to: myNodePath, itemIsActive: pathname === myNodePath, }, { name: t('subscriptions'), - action: () => navigate(myNodeSubscriptionsPath), + to: myNodeSubscriptionsPath, itemIsActive: pathname === myNodeSubscriptionsPath, }, { name: t('websockets'), - action: () => navigate(myNodeWebsocketsPath), + to: myNodeWebsocketsPath, itemIsActive: pathname === myNodeWebsocketsPath, }, ]; diff --git a/src/components/Navigation/NavItem.tsx b/src/components/Navigation/NavItem.tsx index aa045174..b32405a0 100644 --- a/src/components/Navigation/NavItem.tsx +++ b/src/components/Navigation/NavItem.tsx @@ -21,16 +21,20 @@ import { ListItemText, Typography, } from '@mui/material'; +import { Link as RouterLink } from 'react-router-dom'; interface Props { name: string; - action: () => void; - icon?: JSX.Element; itemIsActive: boolean; + icon?: JSX.Element; rightIcon?: JSX.Element; isRoot?: boolean; -} + // NEW: + to?: string; // internal route (React Router) + href?: string; // external link + action?: () => void; // fallback click handler (legacy) +} export const NavItem = ({ name, action, @@ -38,9 +42,23 @@ export const NavItem = ({ itemIsActive, rightIcon, isRoot = false, + to, + href, }: Props) => { + const linkProps = + to != null + ? { component: RouterLink, to } + : href != null + ? { + component: 'a' as const, + href, + target: '_blank', + rel: 'noopener noreferrer', + } + : {}; return ( { {navItems.map((item) => ( ))} diff --git a/src/components/Navigation/Navigation.tsx b/src/components/Navigation/Navigation.tsx index 0eabab85..5ab8fe91 100644 --- a/src/components/Navigation/Navigation.tsx +++ b/src/components/Navigation/Navigation.tsx @@ -30,7 +30,7 @@ export const Navigation: React.FC = () => { } - action={() => navigate(FF_NAV_PATHS.homePath(selectedNamespace))} + to={FF_NAV_PATHS.homePath(selectedNamespace)} // ← use `to` instead of action itemIsActive={pathname === FF_NAV_PATHS.homePath(selectedNamespace)} isRoot /> @@ -43,7 +43,7 @@ export const Navigation: React.FC = () => { } - action={() => window.open(FF_NAV_PATHS.docsPath, '_blank')} + href={FF_NAV_PATHS.docsPath} // ← external URL itemIsActive={false} rightIcon={} isRoot diff --git a/src/components/Navigation/NetworkNav.tsx b/src/components/Navigation/NetworkNav.tsx index a242d7c8..194e3f47 100644 --- a/src/components/Navigation/NetworkNav.tsx +++ b/src/components/Navigation/NetworkNav.tsx @@ -17,14 +17,13 @@ import LanguageIcon from '@mui/icons-material/Language'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { ApplicationContext } from '../../contexts/ApplicationContext'; import { FF_NAV_PATHS, INavItem } from '../../interfaces'; import { NavSection } from './NavSection'; export const NetworkNav = () => { const { t } = useTranslation(); - const navigate = useNavigate(); const { selectedNamespace } = useContext(ApplicationContext); const { pathname } = useLocation(); @@ -37,27 +36,27 @@ export const NetworkNav = () => { const navItems: INavItem[] = [ { name: t('dashboard'), - action: () => navigate(networkPath), + to: networkPath, itemIsActive: pathname === networkPath, }, { name: t('organizations'), - action: () => navigate(orgPath), + to: orgPath, itemIsActive: pathname === orgPath, }, { name: t('nodes'), - action: () => navigate(nodePath), + to: nodePath, itemIsActive: pathname === nodePath, }, { name: t('identities'), - action: () => navigate(identitiesPath), + to: identitiesPath, itemIsActive: pathname === identitiesPath, }, { name: t('namespaces'), - action: () => navigate(namespacesPath), + to: namespacesPath, itemIsActive: pathname === namespacesPath, }, ]; diff --git a/src/components/Navigation/OffChainNav.tsx b/src/components/Navigation/OffChainNav.tsx index 9ca5925a..203b3482 100644 --- a/src/components/Navigation/OffChainNav.tsx +++ b/src/components/Navigation/OffChainNav.tsx @@ -17,14 +17,13 @@ import InventoryIcon from '@mui/icons-material/Inventory'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { ApplicationContext } from '../../contexts/ApplicationContext'; import { FF_NAV_PATHS, INavItem } from '../../interfaces'; import { NavSection } from './NavSection'; export const OffChainNav = () => { const { t } = useTranslation(); - const navigate = useNavigate(); const { pathname } = useLocation(); const { selectedNamespace } = useContext(ApplicationContext); @@ -38,32 +37,32 @@ export const OffChainNav = () => { const navItems: INavItem[] = [ { name: t('dashboard'), - action: () => navigate(offchainPath), + to: offchainPath, itemIsActive: pathname === offchainPath, }, { name: t('messages'), - action: () => navigate(messagesPath), + to: messagesPath, itemIsActive: pathname === messagesPath, }, { name: t('data'), - action: () => navigate(dataPath), + to: dataPath, itemIsActive: pathname === dataPath, }, { name: t('batches'), - action: () => navigate(batchesPath), + to: batchesPath, itemIsActive: pathname === batchesPath, }, { name: t('datatypes'), - action: () => navigate(datatypesPath), + to: datatypesPath, itemIsActive: pathname === datatypesPath, }, { name: t('groups'), - action: () => navigate(groupsPath), + to: groupsPath, itemIsActive: pathname === groupsPath, }, ]; diff --git a/src/components/Navigation/TokensNav.tsx b/src/components/Navigation/TokensNav.tsx index 0aac4d94..dbd19b99 100644 --- a/src/components/Navigation/TokensNav.tsx +++ b/src/components/Navigation/TokensNav.tsx @@ -17,14 +17,13 @@ import AdjustIcon from '@mui/icons-material/Adjust'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { ApplicationContext } from '../../contexts/ApplicationContext'; import { FF_NAV_PATHS, INavItem } from '../../interfaces'; import { NavSection } from './NavSection'; export const TokensNav = () => { const { t } = useTranslation(); - const navigate = useNavigate(); const { pathname } = useLocation(); const { selectedNamespace } = useContext(ApplicationContext); @@ -37,27 +36,27 @@ export const TokensNav = () => { const navItems: INavItem[] = [ { name: t('dashboard'), - action: () => navigate(blockchainPath), + to: blockchainPath, itemIsActive: pathname === blockchainPath, }, { name: t('transfers'), - action: () => navigate(transfersPath), + to: transfersPath, itemIsActive: pathname === transfersPath, }, { name: t('pools'), - action: () => navigate(poolsPath), + to: poolsPath, itemIsActive: pathname.startsWith(poolsPath), }, { name: t('balances'), - action: () => navigate(balancesPath), + to: balancesPath, itemIsActive: pathname === balancesPath, }, { name: t('approvals'), - action: () => navigate(approvalsPath), + to: approvalsPath, itemIsActive: pathname === approvalsPath, }, ]; diff --git a/src/interfaces/navigation.ts b/src/interfaces/navigation.ts index 97f7c652..46add54f 100644 --- a/src/interfaces/navigation.ts +++ b/src/interfaces/navigation.ts @@ -18,9 +18,15 @@ import { FF_OPS } from './enums'; export interface INavItem { name: string; - action: () => void; - icon?: JSX.Element; itemIsActive: boolean; + icon?: JSX.Element; + + // NEW optional props for link-style behavior + to?: string; // internal React Router path + href?: string; // external URL + + // fallback for legacy click handlers + action?: () => void; } export const ACTIVITY_PATH = 'activity'; From afafda1ec0912d3c7ad023240babf8f5b5bfed4a Mon Sep 17 00:00:00 2001 From: Andrew Sawers Date: Thu, 4 Dec 2025 00:54:42 +0000 Subject: [PATCH 3/4] Address PR feedback: improve navigation components Signed-off-by: Andrew Sawers --- src/components/Navigation/ActivityNav.tsx | 16 ++++++++++++++++ src/components/Navigation/NavItem.tsx | 2 -- src/components/Navigation/Navigation.tsx | 3 +-- src/interfaces/navigation.ts | 5 +++-- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/components/Navigation/ActivityNav.tsx b/src/components/Navigation/ActivityNav.tsx index f723e5f3..21b8e437 100644 --- a/src/components/Navigation/ActivityNav.tsx +++ b/src/components/Navigation/ActivityNav.tsx @@ -1,3 +1,19 @@ +// Copyright © 2022 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import { InsertChartOutlined } from '@mui/icons-material'; import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/Navigation/NavItem.tsx b/src/components/Navigation/NavItem.tsx index b32405a0..6f5c6358 100644 --- a/src/components/Navigation/NavItem.tsx +++ b/src/components/Navigation/NavItem.tsx @@ -29,8 +29,6 @@ interface Props { icon?: JSX.Element; rightIcon?: JSX.Element; isRoot?: boolean; - - // NEW: to?: string; // internal route (React Router) href?: string; // external link action?: () => void; // fallback click handler (legacy) diff --git a/src/components/Navigation/Navigation.tsx b/src/components/Navigation/Navigation.tsx index 5ab8fe91..4260ecf6 100644 --- a/src/components/Navigation/Navigation.tsx +++ b/src/components/Navigation/Navigation.tsx @@ -4,7 +4,7 @@ import MenuBookIcon from '@mui/icons-material/MenuBook'; import { Drawer, List } from '@mui/material'; import { default as React, useContext } from 'react'; import { useTranslation } from 'react-i18next'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { ApplicationContext } from '../../contexts/ApplicationContext'; import { FF_NAV_PATHS } from '../../interfaces'; import { MenuLogo } from '../MenuLogo'; @@ -23,7 +23,6 @@ export const Navigation: React.FC = () => { const { selectedNamespace } = useContext(ApplicationContext); const { t } = useTranslation(); const { pathname } = useLocation(); - const navigate = useNavigate(); const makeDrawerContents = ( <> diff --git a/src/interfaces/navigation.ts b/src/interfaces/navigation.ts index 46add54f..dd6b5f18 100644 --- a/src/interfaces/navigation.ts +++ b/src/interfaces/navigation.ts @@ -21,11 +21,12 @@ export interface INavItem { itemIsActive: boolean; icon?: JSX.Element; - // NEW optional props for link-style behavior + // Preferred props for link/navigation behavior to?: string; // internal React Router path href?: string; // external URL - // fallback for legacy click handlers + // DEPRECATED: legacy click handler (prefer `to` or `href`) + /** @deprecated use `to` or `href` instead */ action?: () => void; } From ac200c5fdf7a16f6b6794632a84db3147010ca23 Mon Sep 17 00:00:00 2001 From: Andrew Sawers Date: Thu, 4 Dec 2025 00:54:58 +0000 Subject: [PATCH 4/4] Convert breadcrumbs into router links to support cmd/ctrl-click Signed-off-by: Andrew Sawers --- src/components/Breadcrumbs/FFBreadcrumb.tsx | 66 ++++++++++++++------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/components/Breadcrumbs/FFBreadcrumb.tsx b/src/components/Breadcrumbs/FFBreadcrumb.tsx index 9bbe2331..8759d6d1 100644 --- a/src/components/Breadcrumbs/FFBreadcrumb.tsx +++ b/src/components/Breadcrumbs/FFBreadcrumb.tsx @@ -1,5 +1,5 @@ import { Breadcrumbs, Link, Typography } from '@mui/material'; -import { useNavigate } from 'react-router-dom'; +import { Link as RouterLink } from 'react-router-dom'; import { IFFBreadcrumb } from '../../interfaces'; interface Props { @@ -7,31 +7,55 @@ interface Props { } export const FFBreadcrumb: React.FC = ({ breadcrumbs }) => { - const navigate = useNavigate(); - return ( - {breadcrumbs.map((b, idx) => ( - navigate(b.link ?? '') : undefined} - > - { + const isLast = idx === breadcrumbs.length - 1; + const link = b.link; + if (link && !isLast) { + return ( + + + {b.content} + + + ); + } + + return ( + - {b.content} - - - ))} + + {b.content} + + + ); + })} ); };