Skip to content

Commit

Permalink
Use parentPath property to fix the issue
Browse files Browse the repository at this point in the history
I've manage to resolve the issue using `resolvePath` and `matchPath` functions
instead of `useMatch` and `useResolvedPath` hooks. For that I had to introduce a
new property - `parentPath` - that is passed to `SubNavigationPills` component
and will be used as a `pathnaemBase` for the `resolvePath` function.

If more than one pill is active then the one with the longest `pathnameBase`
from the `matchPath` function will be active. This will happen when there are
two paths with asterisk, for example:
`tBTC/*`,
`tBTC/how-it-work/*`.

For the path `tBTC/how-it-work/overview` we want only the second one to be
active so we are picking the one with the longest `pathnameBase`. In the first
case the pathnameBase is `tBTC` and in the second one it's `tBTC/how-it-works`.
  • Loading branch information
michalsmiarowski committed Jul 25, 2023
1 parent 1f37f71 commit 780e859
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 42 deletions.
22 changes: 18 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,22 @@ const Routing = () => {
<Routes>
<Route path="*" element={<Layout />}>
<Route index element={<Navigate to="overview" />} />
{pages.map(renderPageComponent)}
{pages.map((page) => renderPageComponent(page))}
<Route path="*" element={<Navigate to="overview" />} />
</Route>
</Routes>
)
}

const renderPageComponent = (PageComponent: PageComponent) => {
const renderPageComponent = (
PageComponent: PageComponent,
/** @see PageComponent type */
parentPathBase: string = ""
) => {
if (!PageComponent.route.isPageEnabled) return null
const updatedParentPathBase = PageComponent.route.path
? `${parentPathBase}/${PageComponent.route.path}`
: parentPathBase

return (
<Fragment key={PageComponent.route.path}>
Expand All @@ -217,9 +224,16 @@ 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) =>
renderPageComponent(page, updatedParentPathBase)
)}
</Route>
</Fragment>
)
Expand Down
93 changes: 61 additions & 32 deletions src/components/SubNavigationPills/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,33 @@ import {
Stack,
useColorModeValue,
} from "@threshold-network/components"
import {
matchPath,
resolvePath,
useLocation,
useMatch,
useResolvedPath,
} from "react-router-dom"
import { matchPath, resolvePath } from "react-router-dom"
import { RouteProps } from "../../types"
import Link from "../Link"

interface SubNavigationPillsProps {
links: RouteProps[]
/** @see PageComponent type */
parentPathBase: string
}

interface PathMatchResult {
index: number
path: string
pathOverride?: string
resolvedPath: string
match: any
}
interface NavPill extends RouteProps {
resolvedPaths: string[]
isActive?: boolean
}

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

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

const NavPill: FC<NavPill> = ({ path, pathOverride, title, resolvedPaths }) => {
let isActive = false
const resolved = useResolvedPath(pathOverride || path)
const match = useMatch({ path: resolved.pathname, end: true })
if (match) {
if (match.params["*"]) {
const lastPieceOfPath = match.pathname.replace(match.pathnameBase, "")
if (!resolvedPaths.includes(lastPieceOfPath)) isActive = true
} else {
isActive = 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 @@ -103,18 +99,51 @@ const NavPill: FC<NavPill> = ({ path, pathOverride, title, resolvedPaths }) => {
)
}

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

const getResolvedPathsFromPills = (pills: RouteProps[]) => {
const resolvedPaths: string[] = []
const getPathMatches = (pills: RouteProps[], parentPathBase: string = "") => {
const pathMatches: PathMatchResult[] = []
for (let i = 0; i < pills.length; i++) {
const { path, pathOverride } = pills[i]
const resolved = resolvePath(pathOverride || path)
resolvedPaths.push(resolved.pathname)
const location = window.location.pathname
const resolved = resolvePath(
pathOverride
? `${parentPathBase}/${pathOverride}`
: `${parentPathBase}/${path}`
)
const match = matchPath({ path: resolved.pathname, end: true }, location)
pathMatches.push({
index: i,
path,
pathOverride,
resolvedPath: resolved.pathname,
match,
})
}
return resolvedPaths
return pathMatches
}

const getActivePillIndex = (pills: RouteProps[], parentPathBase: string) => {
const pathMatches = getPathMatches(pills, parentPathBase)
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
8 changes: 7 additions & 1 deletion src/pages/Feedback/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import Settings from "./Settings"
import { featureFlags } from "../../constants"

const FeedbackPage: PageComponent = (props) => {
return <PageLayout title={props.title} pages={props.pages} />
return (
<PageLayout
title={props.title}
pages={props.pages}
parentPathBase={props.parentPathBase}
/>
)
}

FeedbackPage.route = {
Expand Down
7 changes: 6 additions & 1 deletion src/pages/PageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import useDocumentTitle from "../hooks/useDocumentTitle"
export interface PageLayoutProps extends ContainerProps {
pages?: PageComponent[]
title?: string
/** @see PageComponent type */
parentPathBase: string
}

const PageLayout: FC<PageLayoutProps> = ({
pages,
title,
children,
parentPathBase,
...restProps
}) => {
useDocumentTitle(`Threshold - ${title}`)
Expand All @@ -23,7 +26,9 @@ const PageLayout: FC<PageLayoutProps> = ({

return (
<>
{links.length > 0 && <SubNavigationPills links={links} />}
{links.length > 0 && (
<SubNavigationPills links={links} parentPathBase={parentPathBase} />
)}
<Container
maxW={{ base: "2xl", xl: "6xl" }}
mt="6.25rem"
Expand Down
8 changes: 7 additions & 1 deletion src/pages/Staking/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ const StakingPage: PageComponent = (props) => {
} = useAppSelector((state) => state.account)

return (
<PageLayout pages={props.pages} title={props.title} maxW={"100%"}>
<PageLayout
pages={props.pages}
title={props.title}
maxW={"100%"}
parentPathBase={props.parentPathBase}
>
<HStack
alignItems={{ base: "flex-end", lg: "flex-start" }}
w={"100%"}
Expand Down Expand Up @@ -186,6 +191,7 @@ const MainStakingPage: PageComponent = (props) => {
pages={props.pages}
title={props.title}
maxW={{ base: "2xl", lg: "4xl", xl: "6xl" }}
parentPathBase={props.parentPathBase}
/>
)
}
Expand Down
8 changes: 7 additions & 1 deletion src/pages/Upgrade/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import PageLayout from "../PageLayout"
import { PageComponent } from "../../types"

const UpgradePage: PageComponent = (props) => {
return <PageLayout title={props.title} pages={props.pages} />
return (
<PageLayout
title={props.title}
pages={props.pages}
parentPathBase={props.parentPathBase}
/>
)
}

UpgradePage.route = {
Expand Down
8 changes: 7 additions & 1 deletion src/pages/tBTC/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import { featureFlags } from "../../constants"
import { ExplorerPage } from "./Explorer"

const MainTBTCPage: PageComponent = (props) => {
return <PageLayout title={props.title} pages={props.pages} />
return (
<PageLayout
title={props.title}
pages={props.pages}
parentPathBase={props.parentPathBase}
/>
)
}

MainTBTCPage.route = {
Expand Down
9 changes: 8 additions & 1 deletion src/types/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@ export type RouteProps = {
isPageEnabled: boolean
}

export type PageComponent = FC<RouteProps> & { route: RouteProps }
export type PageComponent = FC<
RouteProps & {
// Paths combined from all Route parents of the current Route
parentPathBase: string
}
> & {
route: RouteProps
}

0 comments on commit 780e859

Please sign in to comment.