diff --git a/app/demo/footer/page.tsx b/app/demo/footer/page.tsx new file mode 100644 index 0000000..e18c8b2 --- /dev/null +++ b/app/demo/footer/page.tsx @@ -0,0 +1,158 @@ +"use client"; + +import * as React from "react"; +import { Footer, FooterWithBuildInfo } from "@/components/footer"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + +export default function FooterDemo() { + return ( +
+
+

Footer Component Demo

+ + + + Basic Footer + With Build Info + Custom Styling + + + + + + Basic Footer + + +

+ The default footer with branding and navigation links. +

+
+
+
+
+
+
+ + + + + Footer with Build Information + + +

+ Footer displaying build ID for version tracking. +

+
+
+
+
+
+ +
+
+
+
+
+ + + + + Custom Styled Footer + + +

+ Footer with custom background and styling. +

+
+
+
+
+
+
+
+
+
+
+
+
+ + + + Responsive Behavior + + +

+ The footer adapts to different screen sizes. Try resizing your + browser window to see the responsive layout in action. +

+
+

+ • Desktop (≥768px): Horizontal layout with + left-aligned branding and right-aligned navigation +

+

+ • Mobile (<768px): Stacked layout with + centered content +

+
+
+
+ + + + Usage Example + + +
+              {`// Basic footer
+
+ +// Footer with build ID +
+ +// Footer with environment-based build ID + + +// Footer with custom styling +
`} +
+
+
+ + + + Features + + +

+ ✓ Responsive layout (horizontal on desktop, stacked on mobile) +

+

✓ Branding with Ghost icon and company name

+

✓ Copyright notice with current year

+

✓ Navigation links (GitHub, Privacy, Terms)

+

✓ Optional build/version display

+

✓ Proper semantic HTML structure

+

✓ Accessible navigation with ARIA labels

+

✓ Theme-aware styling

+

✓ External links open in new tab with security attributes

+
+
+
+ + {/* Example of footer at page bottom */} +
+
+
+ ); +} diff --git a/app/layout.tsx b/app/layout.tsx index 4619dfc..6848faf 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import { ThemeProvider } from "@/components/theme-provider"; import { Header } from "@/components/header"; +import { FooterWithBuildInfo } from "@/components/footer"; import { ErrorBoundary } from "@/components/error-boundary"; import { Toaster } from "sonner"; import "./globals.css"; @@ -43,6 +44,7 @@ export default function RootLayout({
{children}
+ diff --git a/components/footer.test.tsx b/components/footer.test.tsx new file mode 100644 index 0000000..fdc3f70 --- /dev/null +++ b/components/footer.test.tsx @@ -0,0 +1,130 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"; +import { Footer, FooterWithBuildInfo } from "./footer"; + +// Mock next/link +vi.mock("next/link", () => ({ + default: ({ + children, + ...props + }: React.PropsWithChildren< + React.AnchorHTMLAttributes + >) => {children}, +})); + +describe("Footer", () => { + it("renders branding elements", () => { + render(