Improve docs

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-06-19 17:17:38 +01:00
parent 5c805d338e
commit 65993b0a74
15 changed files with 372 additions and 241 deletions
@@ -55,10 +55,6 @@
gap: 2px;
}
.playgroundSection {
position: absolute;
}
.sectionTitle {
font-size: var(--canon-font-size-3);
font-weight: var(--canon-font-weight-bold);
@@ -38,9 +38,7 @@ export const Playground = () => {
return (
<motion.div
className={`${styles.section} ${
isPlayground ? styles.playgroundSection : ''
}`}
className={styles.section}
animate={{
opacity: isPlayground ? 1 : 0,
x: isPlayground ? 0 : 20,
@@ -52,6 +50,7 @@ export const Playground = () => {
visibility: isPlayground ? 'visible' : 'hidden',
}}
transition={{ duration: 0.2 }}
style={{ position: 'absolute' }}
>
<div className={styles.sectionTitle}>Components</div>
{components.map(({ slug, title }) => (
@@ -0,0 +1,80 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { ButtonIconSnippet } from '@/snippets/stories-snippets';
import {
buttonIconPropDefs,
buttonIconUsageSnippet,
buttonIconDefaultSnippet,
buttonIconVariantsSnippet,
buttonIconSizesSnippet,
buttonIconDisabledSnippet,
buttonIconResponsiveSnippet,
buttonIconAsLinkSnippet,
} from './button-icon.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Icon Button
A button component with a single icon that can be used to trigger actions.
<Snippet
align="center"
py={4}
preview={<ButtonIconSnippet story="Variants" />}
code={buttonIconDefaultSnippet}
/>
<ComponentInfos
component="button-icon"
classNames={['canon-ButtonIcon']}
usageCode={buttonIconUsageSnippet}
/>
## API reference
<PropsTable data={buttonIconPropDefs} />
## Examples
### Variants
Here's a view when buttons have different variants.
<Snippet
align="center"
py={4}
open
preview={<ButtonIconSnippet story="Variants" />}
code={buttonIconVariantsSnippet}
/>
### Sizes
Here's a view when buttons have different sizes.
<Snippet
align="center"
py={4}
open
preview={<ButtonIconSnippet story="Sizes" />}
code={buttonIconSizesSnippet}
/>
### Disabled
Here's a view when buttons are disabled.
<Snippet
align="center"
py={4}
open
preview={<ButtonIconSnippet story="Disabled" />}
code={buttonIconDisabledSnippet}
/>
### Responsive
Here's a view when buttons are responsive.
<CodeBlock code={buttonIconResponsiveSnippet} />
@@ -0,0 +1,59 @@
import {
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
export const buttonIconPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['primary', 'secondary'],
default: 'primary',
responsive: true,
},
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'medium',
responsive: true,
},
icon: { type: 'enum', values: ['ReactNode'], responsive: false },
isDisabled: { type: 'boolean', default: 'false', responsive: false },
type: {
type: 'enum',
values: ['button', 'submit', 'reset'],
default: 'button',
responsive: false,
},
...classNamePropDefs,
...stylePropDefs,
};
export const buttonIconUsageSnippet = `import { ButtonIcon } from '@backstage/canon';
<ButtonIcon />`;
export const buttonIconDefaultSnippet = `<Flex align="center">
<ButtonIcon icon={<Icon name="cloud" />} variant="primary" />
<ButtonIcon icon={<Icon name="cloud" />} variant="secondary" />
</Flex>`;
export const buttonIconVariantsSnippet = `<Flex align="center">
<ButtonIcon icon={<Icon name="cloud" />} variant="primary" />
<ButtonIcon icon={<Icon name="cloud" />} variant="secondary" />
</Flex>`;
export const buttonIconSizesSnippet = `<Flex align="center">
<ButtonIcon icon={<Icon name="cloud" />} size="small" />
<ButtonIcon icon={<Icon name="cloud" />} size="medium" />
</Flex>`;
export const buttonIconDisabledSnippet = `<ButtonIcon icon={<Icon name="cloud" />} disabled />`;
export const buttonIconResponsiveSnippet = `<ButtonIcon icon={<Icon name="cloud" />} variant={{ initial: 'primary', lg: 'secondary' }} />`;
export const buttonIconAsLinkSnippet = `import { ButtonLink } from '@backstage/canon';
<ButtonLink href="https://canon.backstage.io" target="_blank">
Button
</ButtonLink>`;
@@ -0,0 +1,91 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { ButtonLinkSnippet } from '@/snippets/stories-snippets';
import {
buttonLinkPropDefs,
buttonLinkSnippetUsage,
buttonLinkVariantsSnippet,
buttonLinkSizesSnippet,
buttonLinkIconsSnippet,
buttonLinkDisabledSnippet,
buttonLinkResponsiveSnippet,
} from './button-link.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# ButtonLink
A button component that can be used as a link.
<Snippet
align="center"
py={4}
preview={<ButtonLinkSnippet story="Variants" />}
code={buttonLinkVariantsSnippet}
/>
<ComponentInfos
component="button-link"
classNames={['canon-ButtonLink']}
usageCode={buttonLinkSnippetUsage}
/>
## API reference
<PropsTable data={buttonLinkPropDefs} />
## Examples
### Variants
Here's a view when buttons have different variants.
<Snippet
align="center"
py={4}
open
preview={<ButtonLinkSnippet story="Variants" />}
code={buttonLinkVariantsSnippet}
/>
### Sizes
Here's a view when buttons have different sizes.
<Snippet
align="center"
py={4}
open
preview={<ButtonLinkSnippet story="Sizes" />}
code={buttonLinkSizesSnippet}
/>
### With Icons
Here's a view when buttons have icons.
<Snippet
align="center"
py={4}
open
preview={<ButtonLinkSnippet story="WithIcons" />}
code={buttonLinkIconsSnippet}
/>
### Disabled
Here's a view when buttons are disabled.
<Snippet
align="center"
py={4}
open
preview={<ButtonLinkSnippet story="Disabled" />}
code={buttonLinkDisabledSnippet}
/>
### Responsive
Here's a view when buttons are responsive.
<CodeBlock code={buttonLinkResponsiveSnippet} />
@@ -0,0 +1,73 @@
import { classNamePropDefs, stylePropDefs } from '../../utils/propDefs';
import type { PropDef } from '../../utils/propDefs';
export const buttonLinkPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['primary', 'secondary'],
default: 'primary',
responsive: true,
},
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'medium',
responsive: true,
},
iconStart: { type: 'enum', values: ['ReactNode'], responsive: false },
iconEnd: { type: 'enum', values: ['ReactNode'], responsive: false },
isDisabled: { type: 'boolean', default: 'false', responsive: false },
href: { type: 'string', responsive: false },
hrefLang: { type: 'string', responsive: false },
target: {
type: 'enum',
values: ['HTMLAttributeAnchorTarget'],
default: '_self',
responsive: false,
},
rel: { type: 'string', responsive: false },
children: { type: 'enum', values: ['ReactNode'], responsive: false },
...classNamePropDefs,
...stylePropDefs,
};
export const buttonLinkSnippetUsage = `import { ButtonLink } from '@backstage/canon';
<ButtonLink />`;
export const buttonLinkVariantsSnippet = `<Flex align="center">
<ButtonLink iconStart="cloud" variant="primary">
Button
</ButtonLink>
<ButtonLink iconStart="cloud" variant="secondary">
Button
</ButtonLink>
</Flex>`;
export const buttonLinkSizesSnippet = `<Flex align="center">
<ButtonLink size="small">Small</ButtonLink>
<ButtonLink size="medium">Medium</ButtonLink>
</Flex>`;
export const buttonLinkIconsSnippet = `<Flex align="center">
<ButtonLink iconStart={<Icon name="cloud" />}>Button</ButtonLink>
<ButtonLink iconEnd={<Icon name="chevronRight" />}>Button</ButtonLink>
<ButtonLink
iconStart={<Icon name="cloud" />}
iconEnd={<Icon name="chevronRight" />}>
Button
</ButtonLink>
</Flex>`;
export const buttonLinkDisabledSnippet = `<Flex gap="4">
<ButtonLink variant="primary" isDisabled>
Primary
</ButtonLink>
<ButtonLink variant="secondary" isDisabled>
Secondary
</ButtonLink>
</Flex>`;
export const buttonLinkResponsiveSnippet = `<ButtonLink variant={{ initial: 'primary', lg: 'secondary' }}>
Responsive Button
</ButtonLink>`;
+3 -16
View File
@@ -1,14 +1,13 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { ButtonSnippet } from '@/snippets/stories-snippets';
import { ButtonSnippet, ButtonLinkSnippet } from '@/snippets/stories-snippets';
import {
buttonPropDefs,
buttonSnippetUsage,
buttonVariantsSnippet,
buttonSizesSnippet,
buttonIconsSnippet,
buttonFullWidthSnippet,
buttonDisabledSnippet,
buttonResponsiveSnippet,
buttonAsLinkSnippet,
@@ -74,18 +73,6 @@ Here's a view when buttons have icons.
code={buttonIconsSnippet}
/>
### Full width
Here's a view when buttons are full width.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="FullWidth" />}
code={buttonFullWidthSnippet}
/>
### Disabled
Here's a view when buttons are disabled.
@@ -106,12 +93,12 @@ Here's a view when buttons are responsive.
### As Link
You can use the `as` prop to make a button act as a link.
If you want to use a button as a link, please use the `ButtonLink` component.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="AsLink" />}
preview={<ButtonLinkSnippet story="Variants" />}
code={buttonAsLinkSnippet}
/>
@@ -16,7 +16,14 @@ export const buttonPropDefs: Record<string, PropDef> = {
},
iconStart: { type: 'enum', values: ['ReactNode'], responsive: false },
iconEnd: { type: 'enum', values: ['ReactNode'], responsive: false },
isDisabled: { type: 'boolean', default: 'false', responsive: false },
children: { type: 'enum', values: ['ReactNode'], responsive: false },
type: {
type: 'enum',
values: ['button', 'submit', 'reset'],
default: 'button',
responsive: false,
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -32,9 +39,6 @@ export const buttonVariantsSnippet = `<Flex align="center">
<Button iconStart="cloud" variant="secondary">
Button
</Button>
<Button iconStart="cloud" variant="tertiary">
Button
</Button>
</Flex>`;
export const buttonSizesSnippet = `<Flex align="center">
@@ -43,20 +47,16 @@ export const buttonSizesSnippet = `<Flex align="center">
</Flex>`;
export const buttonIconsSnippet = `<Flex align="center">
<Button iconStart="cloud">Button</Button>
<Button iconEnd="chevronRight">Button</Button>
<Button iconStart="cloud" iconEnd="chevronRight">Button</Button>
</Flex>`;
export const buttonFullWidthSnippet = `<Flex direction="column" gap="4" style={{ width: '300px' }}>
<Button fullWidth>Full width</Button>
<Button iconStart={<Icon name="cloud" />}>Button</Button>
<Button iconEnd={<Icon name="chevronRight" />}>Button</Button>
<Button iconStart={<Icon name="cloud" />} iconEnd={<Icon name="chevronRight" />}>Button</Button>
</Flex>`;
export const buttonDisabledSnippet = `<Flex gap="4">
<Button variant="primary" disabled>
<Button variant="primary" isDisabled>
Primary
</Button>
<Button variant="secondary" disabled>
<Button variant="secondary" isDisabled>
Secondary
</Button>
</Flex>`;
@@ -65,12 +65,8 @@ export const buttonResponsiveSnippet = `<Button variant={{ initial: 'primary', l
Responsive Button
</Button>`;
export const buttonAsLinkSnippet = `// Using the \`as\` prop
<Button as="a" href="https://canon.backstage.io" target="_blank">
I am a link
</Button>
export const buttonAsLinkSnippet = `import { ButtonLink } from '@backstage/canon';
// Using a custom component
<Button as={Link} to="/">
I am a using a custom component
</Button>`;
<ButtonLink href="https://canon.backstage.io" target="_blank">
Button
</ButtonLink>`;
@@ -1,92 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { IconButtonSnippet } from '@/snippets/stories-snippets';
import {
iconButtonPropDefs,
iconButtonUsageSnippet,
iconButtonDefaultSnippet,
iconButtonVariantsSnippet,
iconButtonSizesSnippet,
iconButtonDisabledSnippet,
iconButtonResponsiveSnippet,
iconButtonAsLinkSnippet,
} from './icon-button.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Icon Button
A button component with a single icon that can be used to trigger actions.
<Snippet
align="center"
py={4}
preview={<IconButtonSnippet story="Variants" />}
code={iconButtonDefaultSnippet}
/>
<ComponentInfos
component="icon-button"
classNames={['canon-IconButton']}
usageCode={iconButtonUsageSnippet}
/>
## API reference
<PropsTable data={iconButtonPropDefs} />
## Examples
### Variants
Here's a view when buttons have different variants.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="Variants" />}
code={iconButtonVariantsSnippet}
/>
### Sizes
Here's a view when buttons have different sizes.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="Sizes" />}
code={iconButtonSizesSnippet}
/>
### Disabled
Here's a view when buttons are disabled.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="Disabled" />}
code={iconButtonDisabledSnippet}
/>
### Responsive
Here's a view when buttons are responsive.
<CodeBlock code={iconButtonResponsiveSnippet} />
### As Link
You can use the `as` prop to make a button act as a link.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="AsLink" />}
code={iconButtonAsLinkSnippet}
/>
@@ -1,57 +0,0 @@
import {
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
export const iconButtonPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['primary', 'secondary'],
default: 'primary',
responsive: true,
},
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'medium',
responsive: true,
},
icon: { type: 'enum', values: ['ReactNode'], responsive: false },
...classNamePropDefs,
...stylePropDefs,
};
export const iconButtonUsageSnippet = `import { IconButton } from '@backstage/canon';
<IconButton />`;
export const iconButtonDefaultSnippet = `<Flex align="center">
<IconButton icon={<Icon name="cloud" />} variant="primary" />
<IconButton icon={<Icon name="cloud" />} variant="secondary" />
</Flex>`;
export const iconButtonVariantsSnippet = `<Flex align="center">
<IconButton icon={<Icon name="cloud" />} variant="primary" />
<IconButton icon={<Icon name="cloud" />} variant="secondary" />
</Flex>`;
export const iconButtonSizesSnippet = `<Flex align="center">
<IconButton icon={<Icon name="cloud" />} size="small" />
<IconButton icon={<Icon name="cloud" />} size="medium" />
</Flex>`;
export const iconButtonDisabledSnippet = `<IconButton icon={<Icon name="cloud" />} disabled />`;
export const iconButtonResponsiveSnippet = `<IconButton icon={<Icon name="cloud" />} variant={{ initial: 'primary', lg: 'secondary' }} />`;
export const iconButtonAsLinkSnippet = `// Using the \`as\` prop
<IconButton
as="a"
href="https://canon.backstage.io"
target="_blank"
icon={<Icon name="cloud" />}
/>
// Using a custom component
<IconButton as={Link} to="/" icon={<Icon name="cloud" />} />`;
+24 -12
View File
@@ -3,11 +3,12 @@
import { composeStories } from '@storybook/react';
import * as BoxStories from '../../../packages/canon/src/components/Box/Box.stories';
import * as ButtonStories from '../../../packages/canon/src/components/Button/Button.stories';
import * as ButtonIconStories from '../../../packages/canon/src/components/ButtonIcon/ButtonIcon.stories';
import * as ButtonLinkStories from '../../../packages/canon/src/components/ButtonLink/ButtonLink.stories';
import * as CheckboxStories from '../../../packages/canon/src/components/Checkbox/Checkbox.stories';
import * as ContainerStories from '../../../packages/canon/src/components/Container/Container.stories';
import * as GridStories from '../../../packages/canon/src/components/Grid/Grid.stories';
import * as HeadingStories from '../../../packages/canon/src/components/Heading/Heading.stories';
import * as IconButtonStories from '../../../packages/canon/src/components/IconButton/IconButton.stories';
import * as IconStories from '../../../packages/canon/src/components/Icon/Icon.stories';
import * as TextFieldStories from '../../../packages/canon/src/components/TextField/TextField.stories';
import * as TextStories from '../../../packages/canon/src/components/Text/Text.stories';
@@ -38,6 +39,28 @@ export const ButtonSnippet = ({
return StoryComponent ? <StoryComponent /> : null;
};
export const ButtonIconSnippet = ({
story,
}: {
story: keyof typeof ButtonIconStories;
}) => {
const stories = composeStories(ButtonIconStories);
const StoryComponent = stories[story as keyof typeof stories];
return StoryComponent ? <StoryComponent /> : null;
};
export const ButtonLinkSnippet = ({
story,
}: {
story: keyof typeof ButtonLinkStories;
}) => {
const stories = composeStories(ButtonLinkStories);
const StoryComponent = stories[story as keyof typeof stories];
return StoryComponent ? <StoryComponent /> : null;
};
export const CheckboxSnippet = ({
story,
}: {
@@ -96,17 +119,6 @@ export const HeadingSnippet = ({
return StoryComponent ? <StoryComponent /> : null;
};
export const IconButtonSnippet = ({
story,
}: {
story: keyof typeof IconButtonStories;
}) => {
const stories = composeStories(IconButtonStories);
const StoryComponent = stories[story as keyof typeof stories];
return StoryComponent ? <StoryComponent /> : null;
};
export const IconSnippet = ({ story }: { story: keyof typeof IconStories }) => {
const stories = composeStories(IconStories);
const StoryComponent = stories[story as keyof typeof stories];
+10 -5
View File
@@ -76,6 +76,16 @@ export const components: Page[] = [
slug: 'button',
status: 'alpha',
},
{
title: 'ButtonIcon',
slug: 'button-icon',
status: 'alpha',
},
{
title: 'ButtonLink',
slug: 'button-link',
status: 'alpha',
},
{
title: 'Checkbox',
slug: 'checkbox',
@@ -96,11 +106,6 @@ export const components: Page[] = [
slug: 'icon',
status: 'alpha',
},
{
title: 'IconButton',
slug: 'icon-button',
status: 'alpha',
},
{
title: 'Link',
slug: 'link',
@@ -35,8 +35,6 @@ export const ButtonIcon = forwardRef(
const responsiveSize = useResponsiveValue(size);
const responsiveVariant = useResponsiveValue(variant);
console.log(clsx('canon-Button', 'canon-ButtonIcon', className));
return (
<RAButton
className={clsx('canon-Button', 'canon-ButtonIcon', className)}
@@ -45,22 +45,22 @@ export const Default: Story = {
};
export const Variants: Story = {
args: {
children: 'Button',
},
parameters: {
argTypes: {
variant: {
control: false,
},
},
},
render: () => (
<Flex align="center">
<ButtonLink iconStart={<Icon name="cloud" />} variant="primary">
<ButtonLink
iconStart={<Icon name="cloud" />}
variant="primary"
href="https://canon.backstage.io"
target="_blank"
>
Button
</ButtonLink>
<ButtonLink iconStart={<Icon name="cloud" />} variant="secondary">
<ButtonLink
iconStart={<Icon name="cloud" />}
variant="secondary"
href="https://canon.backstage.io"
target="_blank"
>
Button
</ButtonLink>
</Flex>
@@ -44,25 +44,9 @@ export const ButtonLink = forwardRef(
ref={ref}
{...rest}
>
{iconStart && (
<span
className="canon-ButtonIcon"
aria-hidden="true"
data-size={responsiveSize}
>
{iconStart}
</span>
)}
{iconStart}
{children}
{iconEnd && (
<span
className="canon-ButtonIcon"
aria-hidden="true"
data-size={responsiveSize}
>
{iconEnd}
</span>
)}
{iconEnd}
</RALink>
);
},