Skip to content

Commit

Permalink
Merge pull request #524 from threshold-network/subnav-pills-fix
Browse files Browse the repository at this point in the history
Fix two active navigation pills

There was a UI bug where it displayed two navigation pills as active instead of
one. For example when clicking on `How it works` link in tbtc both `Bridge` and
`How it works` links were active.

We've fixed that by adding a new method - `addActiveStatusToPills` - which adds
`isActive` property to each of the link. If there are two or more links that
are active (based on `useMatch` hook) then we only keep the active status for
the last one.

Additionally I've also fixed the sub-navigation for `Staking` page because the
`Staking` pill was not active when being on authorization page.
  • Loading branch information
r-czajkowski authored Aug 16, 2023
2 parents 22643ad + 12b40c9 commit c62bddd
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 13 deletions.
17 changes: 15 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ const Routing = () => {

const renderPageComponent = (PageComponent: PageComponent) => {
if (!PageComponent.route.isPageEnabled) return null
const { parentPathBase: parentPathBaseFromRoute } = PageComponent.route
const parentPathBase = parentPathBaseFromRoute || ""
const updatedParentPathBase = PageComponent.route.path
? `${parentPathBase}/${PageComponent.route.path}`
: parentPathBase

return (
<Fragment key={PageComponent.route.path}>
Expand All @@ -224,9 +229,17 @@ const renderPageComponent = (PageComponent: PageComponent) => {
)}
<Route
path={PageComponent.route.path}
element={<PageComponent {...PageComponent.route} />}
element={
<PageComponent
{...PageComponent.route}
parentPathBase={updatedParentPathBase}
/>
}
>
{PageComponent.route.pages?.map(renderPageComponent)}
{PageComponent.route.pages?.map((page) => {
page.route.parentPathBase = updatedParentPathBase
return renderPageComponent(page)
})}
</Route>
</Fragment>
)
Expand Down
94 changes: 86 additions & 8 deletions src/components/SubNavigationPills/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,29 @@ import {
Stack,
useColorModeValue,
} from "@threshold-network/components"
import { useMatch, useResolvedPath } from "react-router-dom"
import { matchPath, resolvePath, useLocation } from "react-router-dom"
import { RouteProps } from "../../types"
import Link from "../Link"

interface Props {
interface SubNavigationPillsProps {
links: RouteProps[]
}

const SubNavigationPills: FC<Props> = ({ links }) => {
interface PathMatchResult {
index: number
path: string
pathOverride?: string
resolvedPath: string
match: any
}
interface NavPill extends RouteProps {
isActive?: boolean
}

const SubNavigationPills: FC<SubNavigationPillsProps> = ({ links }) => {
const { pathname } = useLocation()
const linksWithTitle = links.filter((link) => !!link.title)
const activePillIndex = getActivePillIndex(linksWithTitle, pathname)
const wrapperBorderColor = useColorModeValue("gray.100", "gray.700")

return (
Expand All @@ -38,16 +51,17 @@ const SubNavigationPills: FC<Props> = ({ links }) => {
height="28px"
as="ul"
>
{linksWithTitle.map(renderPill)}
{linksWithTitle.map((linkWithTitle, index) => {
const isActive = index === activePillIndex
return renderPill(linkWithTitle, isActive)
})}
</HStack>
</Box>
</>
)
}

const NavPill: FC<RouteProps> = ({ path, pathOverride, title }) => {
const resolved = useResolvedPath(pathOverride || path)
const isActive = useMatch({ path: resolved.pathname, end: true })
const NavPill: FC<NavPill> = ({ path, title, isActive = false }) => {
const activeColor = useColorModeValue("brand.500", "gray.100")
const underlineColor = useColorModeValue("brand.500", "white")

Expand Down Expand Up @@ -81,6 +95,70 @@ const NavPill: FC<RouteProps> = ({ path, pathOverride, title }) => {
)
}

const renderPill = (pill: RouteProps) => <NavPill key={pill.path} {...pill} />
const renderPill = (pill: RouteProps, isActive = false) => (
<NavPill key={pill.path} isActive={isActive} {...pill} />
)

const getPathMatches = (pills: RouteProps[], locationPathname: string) => {
const pathMatches: PathMatchResult[] = []
for (let i = 0; i < pills.length; i++) {
const { path, pathOverride, parentPathBase } = pills[i]
// This is a workaround for preview links. We have to remove the branch name
// from the pathname so first we check if `parentPathBase` is not an
// undefined. If it is then it means that this is the main page without any
// additional paths so we can just use an empty string here. If it's not
// undefined then we have to remove all the characters (if there are any)
// that are before the occurrence of the `parentPathBase`. If this is a
// preview then those removed characters should be a name of the branch. In
// other cases there should not be any character before the occurrence of
// the `parentPathBase` so nothing happens.
const currentPathname = parentPathBase
? locationPathname.includes(parentPathBase) &&
locationPathname.indexOf(parentPathBase) !== 0
? locationPathname.substring(
locationPathname.indexOf(parentPathBase),
locationPathname.length
)
: locationPathname
: ""
const resolved = resolvePath(
pathOverride
? `${parentPathBase}/${pathOverride}`
: `${parentPathBase}/${path}`
)
const match = matchPath(
{ path: resolved.pathname, end: true },
currentPathname
)
pathMatches.push({
index: i,
path,
pathOverride,
resolvedPath: resolved.pathname,
match,
})
}
return pathMatches
}

const getActivePillIndex = (pills: RouteProps[], locationPathname: string) => {
const pathMatches = getPathMatches(pills, locationPathname)
const matchedPaths = pathMatches.filter((_) => {
return !!_.match
})
if (matchedPaths.length === 0) return undefined
if (matchedPaths.length === 1) return matchedPaths[0].index

const matchedElementWithLongestPathnameBase = matchedPaths.reduce(
(maxElement, currentElement) => {
return currentElement.match.pathnameBase.length >
maxElement.match.pathnameBase.length
? currentElement
: maxElement
}
)

return matchedElementWithLongestPathnameBase.index
}

export default SubNavigationPills
1 change: 1 addition & 0 deletions src/pages/Staking/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ Auth.route = {
StakingPage.route = {
path: "",
index: false,
pathOverride: "*",
title: "Staking",
isPageEnabled: true,
}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/tBTC/Bridge/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from "react"
import { Grid, Box, Skeleton, Stack } from "@threshold-network/components"
import { Grid, Box } from "@threshold-network/components"
import { PageComponent } from "../../../types"
import { TbtcBalanceCard } from "./TbtcBalanceCard"
import { MintUnmintNav } from "./MintUnmintNav"
Expand Down Expand Up @@ -70,7 +70,7 @@ const TBTCBridge: PageComponent = (props) => {
TBTCBridge.route = {
path: "",
index: false,
pathOverride: "/tBTC/*",
pathOverride: "*",
pages: [MintPage, UnmintPage],
title: "Bridge",
isPageEnabled: true,
Expand Down
6 changes: 5 additions & 1 deletion src/types/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export type RouteProps = {
pages?: PageComponent[]
hideFromMenu?: boolean
isPageEnabled: boolean
// Paths combined from all Route parents of the current Route
parentPathBase?: string
}

export type PageComponent = FC<RouteProps> & { route: RouteProps }
export type PageComponent = FC<RouteProps> & {
route: RouteProps
}

0 comments on commit c62bddd

Please sign in to comment.