feat: add backstage/ui hooks documentation
Signed-off-by: Antony Bouyon <antony.bouyon@believe.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { hooks } from '@/utils/data';
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
|
||||
const { default: Component } = await import(`@/content/hooks/${slug}.mdx`);
|
||||
|
||||
return <Component />;
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
const list = [...hooks];
|
||||
|
||||
return list.map(hook => ({
|
||||
slug: hook.slug,
|
||||
}));
|
||||
}
|
||||
|
||||
export const dynamicParams = false;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { ComponentCard, ComponentCards } from '@/components/ComponentCards';
|
||||
|
||||
# Hooks
|
||||
|
||||
Backstage UI custom hooks provide easy access to design system features, style management, and responsive interface creation.
|
||||
|
||||
<ComponentCards>
|
||||
<ComponentCard
|
||||
title="useBreakpoint"
|
||||
description="Hook to detect current responsive breakpoints and manage adaptive behaviors."
|
||||
href="/hooks/use-breakpoint"
|
||||
/>
|
||||
<ComponentCard
|
||||
title="useMediaQuery"
|
||||
description="Hook to evaluate and react to CSS media queries programmatically."
|
||||
href="/hooks/use-media-query"
|
||||
/>
|
||||
<ComponentCard
|
||||
title="useStyles"
|
||||
description="Main hook to access and manipulate Backstage design system styles."
|
||||
href="/hooks/use-styles"
|
||||
/>
|
||||
<ComponentCard
|
||||
title="useSurface"
|
||||
description="Hook to manage surfaces and elevation levels in the interface."
|
||||
href="/hooks/use-surface"
|
||||
/>
|
||||
<ComponentCard
|
||||
title="useIsomorphicLayoutEffect"
|
||||
description="Isomorphic version of useLayoutEffect that works on both client and server."
|
||||
href="/hooks/use-isomorphic-layout-effect"
|
||||
/>
|
||||
</ComponentCards>
|
||||
@@ -11,13 +11,26 @@ import {
|
||||
RiServiceLine,
|
||||
RiStackLine,
|
||||
} from '@remixicon/react';
|
||||
import { components } from '@/utils/data';
|
||||
import { components, hooks } from '@/utils/data';
|
||||
import styles from './Navigation.module.css';
|
||||
|
||||
interface NavigationProps {
|
||||
onLinkClick?: () => void;
|
||||
}
|
||||
|
||||
const data = [
|
||||
{
|
||||
title: 'Components',
|
||||
content: components,
|
||||
url: '/components',
|
||||
},
|
||||
{
|
||||
title: 'Hooks',
|
||||
content: hooks,
|
||||
url: '/hooks',
|
||||
},
|
||||
];
|
||||
|
||||
export const Navigation = ({ onLinkClick }: NavigationProps) => {
|
||||
const pathname = usePathname();
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
'use client';
|
||||
|
||||
import { useBreakpoint } from '@backstage/ui';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export function UseBreakpointExample() {
|
||||
const { breakpoint, up, down } = useBreakpoint();
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
|
||||
// prevent hydration mismatch by rendering only on the client
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
}, []);
|
||||
|
||||
if (!isMounted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Current Breakpoint: {breakpoint}</p>
|
||||
{(up('md') && <p>The viewport is larger than 1024px.</p>) ||
|
||||
(down('sm') && <p>The viewport is smaller than 768px.</p>) || (
|
||||
<p>The viewport is between 768px and 1024px.</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { UseBreakpointExample } from './use-breakpoint.example';
|
||||
import {
|
||||
useBreakpointExampleSnippet,
|
||||
useBreakpointReturnDefs,
|
||||
} from './use-breakpoint.props';
|
||||
|
||||
<PageTitle
|
||||
title="useBreakpoint"
|
||||
description="A hook to detect responsive breakpoints and manage responsive layouts."
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
The `useBreakpoint` hook returns the active breakpoint and two functions, `up` and `down`, to check if the viewport is above, or below a given breakpoint, letting you adjust your UI responsively.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
height={150}
|
||||
preview={<UseBreakpointExample />}
|
||||
code={useBreakpointExampleSnippet}
|
||||
/>
|
||||
|
||||
## Breakpoints
|
||||
|
||||
The default breakpoints are:
|
||||
|
||||
- **initial**: < 640px
|
||||
- **xs**: ≥ 640px < 768px
|
||||
- **sm**: ≥ 768px < 1024px
|
||||
- **md**: ≥ 1024px < 1280px
|
||||
- **lg**: ≥ 1280px < 1536px
|
||||
- **xl**: ≥ 1536px
|
||||
|
||||
## Return Value
|
||||
|
||||
<PropsTable data={useBreakpointReturnDefs} isHook />
|
||||
|
||||
<ChangelogComponent component="use-breakpoint" />
|
||||
@@ -0,0 +1,37 @@
|
||||
import { type PropDef } from '@/utils/propDefs';
|
||||
|
||||
const Breakpoint = ['"initial"', '"xs"', '"sm"', '"md"', '"lg"', '"xl"'];
|
||||
|
||||
export const useBreakpointReturnDefs: Record<string, PropDef> = {
|
||||
breakpoint: {
|
||||
type: 'enum',
|
||||
values: Breakpoint,
|
||||
description: 'The current active breakpoint based on screen width',
|
||||
},
|
||||
up: {
|
||||
type: 'enum',
|
||||
values: [`(breakpoint: ${Breakpoint.join(' | ')}) => boolean`],
|
||||
description:
|
||||
'Function that takes a breakpoint and returns true if the screen width is at or above that breakpoint',
|
||||
},
|
||||
down: {
|
||||
type: 'enum',
|
||||
values: [`(breakpoint: ${Breakpoint.join(' | ')}) => boolean`],
|
||||
description:
|
||||
'Function that takes a breakpoint and returns true if the screen width is at or below that breakpoint',
|
||||
},
|
||||
};
|
||||
|
||||
export const useBreakpointExampleSnippet = `import { useBreakpoint } from '@backstage/ui';
|
||||
|
||||
function ResponsiveComponent() {
|
||||
const { breakpoint, up, down } = useBreakpoint();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Current Breakpoint: {breakpoint}</p>
|
||||
{up('md') && <p>The viewport is medium or larger.</p>}
|
||||
{down('sm') && <p>The viewport is small or smaller.</p>}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
@@ -0,0 +1,42 @@
|
||||
'use client';
|
||||
import { useMediaQuery } from '@backstage/ui/src/hooks/useMediaQuery';
|
||||
|
||||
export function UseMediaQueryThemeExample() {
|
||||
const isDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isDarkMode ? 'User prefers Dark mode' : 'User prefers Light mode'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function UseMediaQueryResponsiveExample() {
|
||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||
const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
|
||||
const isDesktop = useMediaQuery('(min-width: 1025px)');
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isMobile && '<MobileLayout />'}
|
||||
{isTablet && '<TabletLayout />'}
|
||||
{isDesktop && '<DesktopLayout />'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function UseMediaQueryPreferencesExample() {
|
||||
const prefersReducedMotion = useMediaQuery(
|
||||
'(prefers-reduced-motion: reduce)',
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={prefersReducedMotion ? 'no-animations' : ''}>Content</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function UseMediaQueryOrientationExample() {
|
||||
const isPortrait = useMediaQuery('(orientation: portrait)');
|
||||
|
||||
return <div>{isPortrait ? 'Portrait mode' : 'Landscape mode'}</div>;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import {
|
||||
useMediaQueryOrientationSnippet,
|
||||
useMediaQueryParamDefs,
|
||||
useMediaQueryPreferencesSnippet,
|
||||
useMediaQueryResponsiveSnippet,
|
||||
useMediaQueryReturnDefs,
|
||||
useMediaQueryUsageSnippet,
|
||||
} from './use-media-query.props';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import {
|
||||
UseMediaQueryThemeExample,
|
||||
UseMediaQueryPreferencesExample,
|
||||
UseMediaQueryResponsiveExample,
|
||||
UseMediaQueryOrientationExample,
|
||||
} from './use-media-query.example';
|
||||
|
||||
<PageTitle
|
||||
title="useMediaQuery"
|
||||
description="Hook to evaluate and react to CSS media queries programmatically."
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
The `useMediaQuery` hook allows you to evaluate CSS media queries in your components, enabling responsive design and behavior.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
height={150}
|
||||
open={true}
|
||||
preview={<UseMediaQueryThemeExample />}
|
||||
code={useMediaQueryUsageSnippet}
|
||||
/>
|
||||
|
||||
## Parameters
|
||||
|
||||
<PropsTable data={useMediaQueryParamDefs} isHook />
|
||||
|
||||
## Return Value
|
||||
|
||||
<PropsTable data={useMediaQueryReturnDefs} isHook />
|
||||
## Examples
|
||||
|
||||
### Responsive Layouts
|
||||
|
||||
Adapt layout based on different screen sizes.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
height={200}
|
||||
open={true}
|
||||
preview={<UseMediaQueryResponsiveExample />}
|
||||
code={useMediaQueryResponsiveSnippet}
|
||||
/>
|
||||
|
||||
### User Preferences
|
||||
|
||||
Respect user system preferences.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
height={150}
|
||||
open={true}
|
||||
preview={<UseMediaQueryPreferencesExample />}
|
||||
code={useMediaQueryPreferencesSnippet}
|
||||
/>
|
||||
|
||||
### Device Orientation
|
||||
|
||||
Detect screen orientation.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
height={150}
|
||||
open={true}
|
||||
preview={<UseMediaQueryOrientationExample />}
|
||||
code={useMediaQueryOrientationSnippet}
|
||||
/>
|
||||
|
||||
<ChangelogComponent component="use-media-query" />
|
||||
@@ -0,0 +1,90 @@
|
||||
import { type PropDef } from '@/utils/propDefs';
|
||||
|
||||
export const useMediaQueryParamDefs: Record<string, PropDef> = {
|
||||
query: {
|
||||
type: 'string',
|
||||
description: 'The CSS media query to evaluate',
|
||||
},
|
||||
options: {
|
||||
type: 'complex',
|
||||
complexType: {
|
||||
name: 'UseMediaQueryOptions',
|
||||
properties: {
|
||||
defaultValue: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
description:
|
||||
'Default value to use when rendering on the server, defaults to false',
|
||||
},
|
||||
initializeWithValue: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
description:
|
||||
'Whether to initialize with the current value of the media query, or use the default value initially',
|
||||
},
|
||||
},
|
||||
},
|
||||
default: 'defaultValue: false\ninitializeWithValue: true',
|
||||
},
|
||||
};
|
||||
|
||||
export const useMediaQueryReturnDefs: Record<string, PropDef> = {
|
||||
matches: {
|
||||
type: 'boolean',
|
||||
description: 'True if the media query currently matches',
|
||||
},
|
||||
};
|
||||
|
||||
export const useMediaQueryUsageSnippet = `import { useMediaQuery } from '@backstage/ui';
|
||||
|
||||
function MyComponent() {
|
||||
const isDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isDarkMode ? 'Dark mode enabled' : 'Light mode enabled'}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
export const useMediaQueryResponsiveSnippet = `import { useMediaQuery } from '@backstage/ui';
|
||||
|
||||
function ResponsiveLayout() {
|
||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||
const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
|
||||
const isDesktop = useMediaQuery('(min-width: 1025px)');
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isMobile && <MobileLayout />}
|
||||
{isTablet && <TabletLayout />}
|
||||
{isDesktop && <DesktopLayout />}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
export const useMediaQueryPreferencesSnippet = `import { useMediaQuery } from '@backstage/ui';
|
||||
|
||||
function AccessibleComponent() {
|
||||
const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
|
||||
|
||||
return (
|
||||
<div
|
||||
className={prefersReducedMotion ? 'no-animations' : ''}
|
||||
>
|
||||
Content
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
|
||||
export const useMediaQueryOrientationSnippet = `import { useMediaQuery } from '@backstage/ui';
|
||||
|
||||
function OrientationAware() {
|
||||
const isPortrait = useMediaQuery('(orientation: portrait)');
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isPortrait ? 'Portrait mode' : 'Landscape mode'}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
@@ -138,3 +138,26 @@ export const components: Page[] = [
|
||||
slug: 'visually-hidden',
|
||||
},
|
||||
];
|
||||
|
||||
export const hooks: Page[] = [
|
||||
{
|
||||
title: 'useBreakpoint',
|
||||
slug: 'use-breakpoint',
|
||||
},
|
||||
{
|
||||
title: 'useIsomorphicLayoutEffect',
|
||||
slug: 'use-isomorphic-layout-effect',
|
||||
},
|
||||
{
|
||||
title: 'useMediaQuery',
|
||||
slug: 'use-media-query',
|
||||
},
|
||||
{
|
||||
title: 'useStyles',
|
||||
slug: 'use-styles',
|
||||
},
|
||||
{
|
||||
title: 'useSurface',
|
||||
slug: 'use-surface',
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user