UNPKG

@payfit/unity-components

Version:

322 lines (261 loc) 9.84 kB
--- name: unity-navigation description: > Load when building Unity links, tabs, breadcrumbs, nav, or pagination. Use it to choose router-aware Tanstack Router components versus Raw* href primitives. type: core library: '@payfit/unity-components' library_version: '2.x' sources: - 'PayFit/hr-apps:libs/shared/unity/components/src/components/link/RawLink.tsx' - 'PayFit/hr-apps:libs/shared/unity/components/src/components/breadcrumbs/Breadcrumbs.tsx' - 'PayFit/hr-apps:libs/shared/unity/components/src/components/pagination/Pagination.tsx' - 'PayFit/hr-apps:libs/shared/unity/components/src/components/tabs/Tabs.tsx' - 'PayFit/hr-apps:libs/shared/unity/components/src/integrations/tanstack-router/index.ts' - 'PayFit/hr-apps:libs/shared/unity/components/src/providers/router/RouterProvider.tsx' - 'PayFit/hr-apps:libs/shared/unity/components/src/docs/concepts/navigation/Navigation Patterns Explained.mdx' --- Two entry points expose the same components in two flavors. The base entry (`@payfit/unity-components`) ships router-agnostic `Raw*` primitives that take a plain `href`. The Tanstack integration entry (`@payfit/unity-components/integrations/tanstack-router`) ships type-safe counterparts that take `to` and are wired via `createLink` from `@tanstack/react-router`. ## Setup In a Tanstack Router feature plugin, import every link/nav component from the integration entry. No `RouterProvider` is required — the integration uses Tanstack hooks directly. ```tsx import { Breadcrumb, BreadcrumbLink, Breadcrumbs, Link, Tab, TabList, TabPanel, Tabs, } from '@payfit/unity-components/integrations/tanstack-router' import { Outlet } from '@tanstack/react-router' export function EmployeesShell() { return ( <> <Breadcrumbs> <Breadcrumb> <BreadcrumbLink to="/">Home</BreadcrumbLink> </Breadcrumb> <Breadcrumb> <BreadcrumbLink to="/employees">Employees</BreadcrumbLink> </Breadcrumb> </Breadcrumbs> <Tabs> <TabList> <Tab to="/employees/active">Active</Tab> <Tab to="/employees/archived">Archived</Tab> </TabList> <TabPanel> <Outlet /> </TabPanel> </Tabs> <Link to="/employees/new">New employee</Link> </> ) } ``` ## Core Patterns ### Raw and integration map | Base entry (`@payfit/unity-components`) | Integration (`@payfit/unity-components/integrations/tanstack-router`) | Prop | | --------------------------------------- | --------------------------------------------------------------------- | --------------------------------------- | | `RawLink` | `Link` | `href` (raw) / `to` (integration) | | `RawNavItem` | `NavItem` | same | | `RawBreadcrumbLink` | `BreadcrumbLink` | same | | `RawPaginationLink` | `PaginationLink` | same | | `RawTab` (+ `Tabs`) | `Tab` (+ `Tabs`) | route-aware `to` selects the active tab | | `RawLinkButton` | `LinkButton` | same | The integration components are produced via `createLink(RawLink)`. They render exactly the same DOM as the raw versions but accept Tanstack's typed `to`, `params`, `search`, and `preload` props. ### Compose Breadcrumbs / Pagination / Tabs Each navigation composite has a strict child contract. Direct children of the container must be the wrapper part — loose children are silently filtered. ```tsx import { Breadcrumb, BreadcrumbLink, Breadcrumbs, Pagination, PaginationContent, PaginationItem, RawPaginationLink, RawTab, TabList, TabPanel, Tabs, } from '@payfit/unity-components' export function CompositionExamples({ page, pageCount, onPageChange, }: { page: number pageCount: number onPageChange: (next: number, prev: number, dir: -1 | 1) => void }) { return ( <> <Breadcrumbs> <Breadcrumb> <BreadcrumbLink href="/">Home</BreadcrumbLink> </Breadcrumb> <Breadcrumb> <BreadcrumbLink href="/employees">Employees</BreadcrumbLink> </Breadcrumb> </Breadcrumbs> <Pagination currentPage={page} pageCount={pageCount} onPageChange={onPageChange} > <PaginationContent> {Array.from({ length: pageCount }, (_, i) => i + 1).map(n => ( <PaginationItem key={n}> <RawPaginationLink value={n} isActive={n === page}> {n} </RawPaginationLink> </PaginationItem> ))} </PaginationContent> </Pagination> <Tabs> <TabList> <RawTab id="active">Active</RawTab> <RawTab id="archived">Archived</RawTab> </TabList> <TabPanel id="active">Active rows</TabPanel> <TabPanel id="archived">Archived rows</TabPanel> </Tabs> </> ) } ``` ### RouterProvider for Raw\* in non-Tanstack apps When you use `Raw*` components in an app that uses a different router (e.g. React Router), wrap the app once in `RouterProvider` so `isActive` and client-side navigation work. The Tanstack integration entry does NOT need this — it reads from Tanstack hooks directly. ```tsx import { RouterProvider } from '@payfit/unity-components' import { matchPath, useLocation, useNavigate } from 'react-router-dom' export function UnityRouterBridge({ children }: { children: React.ReactNode }) { const navigate = useNavigate() const location = useLocation() return ( <RouterProvider navigate={to => navigate(to)} isActive={(to, isExact) => Boolean( matchPath({ path: to, end: Boolean(isExact) }, location.pathname), ) } > {children} </RouterProvider> ) } ``` ## Common Mistakes ### HIGH Import Link from base entry in a Tanstack Router feature Wrong: ```tsx import { Link } from '@payfit/unity-components' ;<Link to="/dashboard">Go</Link> ``` Correct: ```tsx import { Link } from '@payfit/unity-components/integrations/tanstack-router' ;<Link to="/dashboard">Go</Link> ``` The base export "Link" does not exist; agents alias RawLink and pass to=. The prop is silently ignored and the link does not navigate. Source: integrations/tanstack-router/components/link/Link.tsx (createLink wraps RawLink) ### HIGH Pass to= prop to RawLink Wrong: ```tsx import { RawLink } from '@payfit/unity-components' ;<RawLink to="/dashboard">Go</RawLink> ``` Correct: ```tsx // Use the integration Link for type-safe routing: import { Link } from '@payfit/unity-components/integrations/tanstack-router' <Link to="/dashboard">Go</Link> // OR keep RawLink with a plain href: <RawLink href="/dashboard">Go</RawLink> ``` RawLink only accepts href. The to prop is silently ignored. Source: components/link/RawLink.tsx:113-163 ### HIGH Expect the v1 monolithic Pagination Wrong: ```tsx import { Pagination, ClientSidePagination } from '@payfit/unity-components' // ClientSidePagination is not exported; Pagination needs children. <Pagination currentPage={1} pageCount={10} onPageChange={} /> ``` Correct: ```tsx import { Pagination, PaginationContent, PaginationItem, RawPaginationLink } from '@payfit/unity-components' <Pagination currentPage={1} pageCount={10} onPageChange={}> <PaginationContent> <PaginationItem> <RawPaginationLink value={1}>1</RawPaginationLink> </PaginationItem> {/* … */} </PaginationContent> </Pagination> // Or, for tables, let DataTable handle pagination internally. ``` v2 Pagination is compositional; passing currentPage/pageCount with no children renders an empty container. The old monolithic component (ClientSidePagination) now lives only inside DataTable as an internal — there is no public monolithic export. For standalone pagination, compose it yourself; for paginated tables, use DataTable which has it built in. Source: components/pagination/Pagination.tsx; index.ts; maintainer interview (ClientSidePagination is internal to DataTable) ### MEDIUM Forget to wrap BreadcrumbLink in Breadcrumb Wrong: ```tsx <Breadcrumbs> <BreadcrumbLink href="/">Home</BreadcrumbLink> </Breadcrumbs> ``` Correct: ```tsx <Breadcrumbs> <Breadcrumb> <BreadcrumbLink href="/">Home</BreadcrumbLink> </Breadcrumb> </Breadcrumbs> ``` Breadcrumbs filters children to Breadcrumb type; loose BreadcrumbLinks are dropped silently. Source: components/breadcrumbs/Breadcrumbs.tsx:328-407 ### MEDIUM Use Raw\* components without RouterProvider in a non-Tanstack app Wrong: ```tsx <App> <RawLink href="/dashboard">Go</RawLink> </App> ``` Correct: ```tsx <RouterProvider isActive={path => router.isActive(path)} navigate={router.navigate} > <App> <RawLink href="/dashboard">Go</RawLink> </App> </RouterProvider> ``` Raw\* components consume useRouter() context. Without RouterProvider, isActive checks return undefined and active-link styling is lost. Source: components/link/RawLink.tsx:186; providers/router/RouterProvider.tsx ## See also - `unity-setup-feature-plugin` — where and how to mount `RouterProvider`, and when a feature plugin should switch from base to integration entry. - `unity-data-table` — DataTable has pagination built in; do not compose `Pagination` next to it.