Merge pull request #32340 from Believe-SA/docs/hooks

feat(docs-ui): document BUI's hooks
This commit is contained in:
Fredrik Adelöw
2026-03-10 16:29:22 +01:00
committed by GitHub
13 changed files with 215 additions and 11 deletions
+7
View File
@@ -0,0 +1,7 @@
import { HookGrid } from '@/components/HookGrid';
# Hooks
Backstage UI custom hooks provide easy access to design system features, style management, and responsive interface creation.
<HookGrid />
@@ -0,0 +1,17 @@
'use client';
import { useBreakpoint } from '@backstage/ui';
export function UseBreakpointExample() {
const { breakpoint, up, down } = useBreakpoint();
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,47 @@
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { PageTitle } from '@/components/PageTitle';
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { Banner } from '@/components/Banner';
import { useBreakpointReturnDefs } from './props-definition';
import { useBreakpointExampleSnippet } from './snippets';
import { UseBreakpointExample } from './components';
<PageTitle
title="useBreakpoint"
description="A hook to detect responsive breakpoints and manage responsive layouts."
/>
## Usage
<Banner
variant="warning"
text="This hook has performance issues and should be used with caution."
/>
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 hook="use-breakpoint" />
@@ -0,0 +1,23 @@
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',
},
};
@@ -0,0 +1,13 @@
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>
);
}`;
@@ -16,13 +16,13 @@
.info {
background-color: #f2f2f2;
border-color: #cdcdcd;
color: #888888;
color: #4b4b4b;
}
.warning {
background-color: #fff2b9;
border-color: #ffd000;
color: #d79927;
border-color: #d79927;
color: #8a3d09;
}
.icon {
@@ -1,16 +1,24 @@
import { changelog } from '@/utils/changelog';
import { MDXRemote } from 'next-mdx-remote-client/rsc';
import { formattedMDXComponents } from '@/mdx-components';
import type { Component } from '@/utils/changelog';
import type { AtLeastOne, Component, Hook } from '@/utils/changelog';
import {
Badge,
BreakingBadge,
generateChangelogMarkdown,
} from '../Changelog/utils';
export const ChangelogComponent = ({ component }: { component: Component }) => {
const componentChangelog = changelog.filter(c =>
c.components.includes(component),
type ChangelogComponentProps = AtLeastOne<{
component: Component;
hook: Hook;
}>;
export const ChangelogComponent = ({
component,
hook,
}: Readonly<ChangelogComponentProps>) => {
const componentChangelog = changelog.filter(
c => c.components?.includes(component) || c.hooks?.includes(hook),
);
const content = `## Changelog
@@ -0,0 +1,29 @@
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
margin-top: 1rem;
margin-bottom: 2rem;
}
@media (min-width: 768px) {
.grid {
grid-template-columns: repeat(4, 1fr);
}
}
.item {
display: flex;
align-items: center;
gap: 6px;
padding: 10px 0;
font-size: 14px;
font-weight: 400;
color: var(--primary);
text-decoration: none;
&:hover {
color: var(--link);
text-decoration: underline;
text-underline-offset: 2px;
}
}
@@ -0,0 +1,21 @@
'use client';
import Link from 'next/link';
import { hooks } from '@/utils/data';
import styles from './HookGrid.module.css';
export const HookGrid = () => {
return (
<div className={styles.grid}>
{hooks.map(item => (
<Link
key={item.slug}
href={`/hooks/${item.slug}`}
className={styles.item}
>
{item.title}
</Link>
))}
</div>
);
};
+1
View File
@@ -0,0 +1 @@
export * from './HookGrid';
@@ -11,7 +11,7 @@ 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 {
@@ -92,6 +92,29 @@ export const Navigation = ({ onLinkClick }: NavigationProps) => {
</Link>
);
})}
<div className={styles.sectionTitle}>
<RiCollageLine size={20} /> <span>Hooks</span>
</div>
{hooks.map(item => {
const isActive = pathname === `/hooks/${item.slug}`;
return (
<Link
href={`/hooks/${item.slug}`}
key={item.slug}
className={clsx(styles.line, { [styles.active]: isActive })}
onClick={onLinkClick}
>
<div className={styles.lineTitle}>{item.title}</div>
<div className={styles.lineStatus}>
{item.status === 'alpha' && 'Alpha'}
{item.status === 'beta' && 'Beta'}
{item.status === 'inProgress' && 'In Progress'}
{item.status === 'stable' && 'Stable'}
{item.status === 'deprecated' && 'Deprecated'}
</div>
</Link>
);
})}
</>
);
};
+7
View File
@@ -138,3 +138,10 @@ export const components: Page[] = [
slug: 'visually-hidden',
},
];
export const hooks: Page[] = [
{
title: 'useBreakpoint',
slug: 'use-breakpoint',
},
];
+11 -3
View File
@@ -35,14 +35,22 @@ export type Component =
| 'tooltip'
| 'visually-hidden';
export type Hook = 'use-breakpoint';
export type Version = `${number}.${number}.${number}`;
export interface ChangelogProps {
components: Component[];
export type AtLeastOne<T, K extends keyof T = keyof T> = K extends string
? Pick<T, K> & Partial<Omit<T, K>>
: never;
export type ChangelogProps = {
description: string;
version: Version;
prs: string[];
breaking?: boolean;
commitSha?: string;
migration?: string;
}
} & AtLeastOne<{
components: Component[];
hooks: Hook[];
}>;