Skip to content

Commit fa154ec

Browse files
github-actions[bot]CopilotCopilotIzumiSy
authored
[docs-update] Update docs for sidebar components and file-based routing (#29)
* docs: update API docs for sidebar components and file-based routing - Add WithGuard component documentation to api.md - Add usePageMeta hook documentation to api.md - Document breaking changes in module-resource-definition.md: - Guard/loader cascade removed - Module without component now requires guards - Update examples to reflect new API patterns Based on changesets: - custom-sidebar-api.md (SidebarItem, SidebarGroup, WithGuard, usePageMeta) - fs-routes.md (file-based routing, breaking changes) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove breaking changes from documentation (#30) * Initial plan * Remove breaking changes from docs and update workflow Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: IzumiSy <982850+IzumiSy@users.noreply.github.com>
1 parent a7f686f commit fa154ec

2 files changed

Lines changed: 134 additions & 0 deletions

File tree

.github/workflows/docs-update.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ In the PR body, list:
102102
- **Changesets are context, source code is truth**: Use changeset descriptions to understand intent, but always verify against actual source code for accuracy.
103103
- **Conservative updates**: Only modify sections directly affected by the changesets. Do not rewrite unrelated sections.
104104
- **No internal content**: Do not add architecture explanations, design decisions, or implementation details. Document only what library consumers need to know.
105+
- **No breaking changes**: Do not add "Breaking Changes" sections or document breaking changes in the documentation. Breaking changes should be documented in CHANGELOGs and release notes, not in user-facing documentation files.
105106

106107
## Safe Outputs
107108

docs/api.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
- [redirectTo](#redirectto)
1111
- [SidebarLayout](#sidebarlayout)
1212
- [DefaultSidebar](#defaultsidebar)
13+
- [WithGuard](#withguard)
1314
- [CommandPalette](#commandpalette)
1415
- [Badge](#badge)
1516
- [DescriptionCard](#descriptioncard)
1617
- [Layout](#layout)
1718
- [useAppShell](#useappshell)
1819
- [useAppShellConfig](#useappshellconfig)
1920
- [useAppShellData](#useappshelldata)
21+
- [usePageMeta](#usepagemeta)
2022
- [useTheme](#usetheme)
2123
- [useRouteError](#userouteerror)
2224
- [defineI18nLabels](#definei18nlabels)
@@ -460,6 +462,72 @@ const CustomLayout = () => (
460462
);
461463
```
462464

465+
## WithGuard
466+
467+
Conditionally renders children based on guard evaluation. Use this component to control visibility of UI elements (e.g., sidebar items) based on the same guard logic used in route definitions.
468+
469+
### Props
470+
471+
| Prop | Type | Required | Description |
472+
|------|------|----------|-------------|
473+
| `guards` | `Guard[]` | Yes | Array of guard functions. All must pass for children to render |
474+
| `children` | `React.ReactNode` | Yes | Content to render when all guards pass |
475+
| `fallback` | `React.ReactNode` | No | Content to render when any guard returns `hidden()` (default: `null`) |
476+
| `loading` | `React.ReactNode` | No | Content to render while async guards are being evaluated (default: `null`) |
477+
478+
### Behavior
479+
480+
- Guards are evaluated in order
481+
- If any guard returns `hidden()`, the fallback is rendered instead of children
482+
- Supports async guards with Suspense integration
483+
- Unlike route guards, `redirectTo()` is not supported in `WithGuard`
484+
- Use `hidden()` with a fallback that handles navigation if needed
485+
486+
### Example
487+
488+
```tsx
489+
import { WithGuard, pass, hidden, DefaultSidebar, SidebarItem } from "@tailor-platform/app-shell";
490+
import { Shield } from "lucide-react";
491+
492+
// Define guard functions
493+
const isAdminGuard = ({ context }) =>
494+
context.currentUser.role === "admin" ? pass() : hidden();
495+
496+
const hasRole = (role: string) => ({ context }) =>
497+
context.currentUser.role === role ? pass() : hidden();
498+
499+
// Use in sidebar
500+
<DefaultSidebar>
501+
<SidebarItem to="/dashboard" />
502+
503+
<WithGuard guards={[isAdminGuard]}>
504+
<SidebarItem to="/admin" />
505+
</WithGuard>
506+
507+
<WithGuard guards={[hasRole("manager")]}>
508+
<SidebarItem to="/reports" />
509+
</WithGuard>
510+
</DefaultSidebar>
511+
512+
// Use in page components with fallback
513+
<WithGuard guards={[isAdminGuard]} fallback={<UpgradePrompt />}>
514+
<AdminPanel />
515+
</WithGuard>
516+
517+
// With loading state for async guards
518+
<WithGuard
519+
guards={[checkSubscription]}
520+
loading={<Spinner />}
521+
fallback={<UpgradePrompt />}
522+
>
523+
<PremiumFeature />
524+
</WithGuard>
525+
```
526+
527+
**See also:**
528+
- [Sidebar Navigation - Access Control](./sidebar-navigation.md#access-control) for sidebar-specific usage
529+
- [Route Guards](#route-guards) for guard function reference
530+
463531
## CommandPalette
464532

465533
Keyboard-driven quick navigation component for searching and navigating between pages.
@@ -773,6 +841,71 @@ const UserProfile = () => {
773841
};
774842
```
775843

844+
## usePageMeta
845+
846+
Hook to retrieve page metadata (title and icon) for a given URL path. Useful for building custom navigation components or displaying page information.
847+
848+
### Signature
849+
850+
```typescript
851+
const usePageMeta: (path: string) => PageMeta | null;
852+
853+
type PageMeta = {
854+
title: string;
855+
icon?: ReactNode;
856+
};
857+
```
858+
859+
### Parameters
860+
861+
| Parameter | Type | Description |
862+
|-----------|------|-------------|
863+
| `path` | `string` | URL path to find meta for (e.g., "/products/all") |
864+
865+
### Returns
866+
867+
| Type | Description |
868+
|------|-------------|
869+
| `PageMeta \| null` | Object containing `title` and optional `icon` if found, or `null` for external links or when path is not found |
870+
871+
### Example
872+
873+
```tsx
874+
import { usePageMeta } from "@tailor-platform/app-shell";
875+
876+
// Display current page metadata
877+
const PageHeader = () => {
878+
const location = useLocation();
879+
const pageMeta = usePageMeta(location.pathname);
880+
881+
if (!pageMeta) {
882+
return <h1>Unknown Page</h1>;
883+
}
884+
885+
return (
886+
<h1>
887+
{pageMeta.icon}
888+
{pageMeta.title}
889+
</h1>
890+
);
891+
};
892+
893+
// Custom navigation item
894+
const CustomNavItem = ({ to }: { to: string }) => {
895+
const pageMeta = usePageMeta(to);
896+
897+
return (
898+
<Link to={to}>
899+
{pageMeta?.icon}
900+
<span>{pageMeta?.title || to}</span>
901+
</Link>
902+
);
903+
};
904+
```
905+
906+
**See also:**
907+
- [Sidebar Navigation - SidebarItem](./sidebar-navigation.md#sidebaritem) which uses this hook internally
908+
776909
## useTheme
777910

778911
Hook for managing application theme (light/dark/system).

0 commit comments

Comments
 (0)