Migrate all components to their new format
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -1,119 +0,0 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const accordionPropDefs: Record<string, PropDef> = {
|
||||
children: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode', '(state: { isExpanded: boolean }) => ReactNode'],
|
||||
},
|
||||
defaultExpanded: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
isExpanded: {
|
||||
type: 'boolean',
|
||||
},
|
||||
onExpandedChange: {
|
||||
type: 'enum',
|
||||
values: ['(isExpanded: boolean) => void'],
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const accordionTriggerPropDefs: Record<string, PropDef> = {
|
||||
level: {
|
||||
type: 'enum',
|
||||
values: ['1', '2', '3', '4', '5', '6'],
|
||||
default: '3',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
subtitle: {
|
||||
type: 'string',
|
||||
},
|
||||
children: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const accordionPanelPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const accordionGroupPropDefs: Record<string, PropDef> = {
|
||||
allowsMultiple: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const accordionUsageSnippet = `import { Accordion, AccordionTrigger, AccordionPanel } from '@backstage/ui';
|
||||
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Toggle Panel" />
|
||||
<AccordionPanel>Your content</AccordionPanel>
|
||||
</Accordion>`;
|
||||
|
||||
export const accordionWithSubtitleSnippet = `<Accordion>
|
||||
<AccordionTrigger
|
||||
title="Advanced Settings"
|
||||
subtitle="Configure additional options"
|
||||
/>
|
||||
<AccordionPanel>
|
||||
<Text>Your content here</Text>
|
||||
</AccordionPanel>
|
||||
</Accordion>`;
|
||||
|
||||
export const accordionCustomTriggerSnippet = `<Accordion>
|
||||
<AccordionTrigger>
|
||||
<Box>
|
||||
<Text as="div" weight="bold">Custom Multi-line Trigger</Text>
|
||||
<Text as="div" size="small" color="secondary">
|
||||
Click to expand additional details
|
||||
</Text>
|
||||
</Box>
|
||||
</AccordionTrigger>
|
||||
<AccordionPanel>
|
||||
<Text>Your content here</Text>
|
||||
</AccordionPanel>
|
||||
</Accordion>`;
|
||||
|
||||
export const accordionDefaultExpandedSnippet = `<Accordion defaultExpanded>
|
||||
<AccordionTrigger title="Toggle Panel" />
|
||||
<AccordionPanel>
|
||||
<Text>Your content here</Text>
|
||||
</AccordionPanel>
|
||||
</Accordion>`;
|
||||
|
||||
export const accordionGroupSingleOpenSnippet = `<AccordionGroup>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="First Panel" />
|
||||
<AccordionPanel>Content 1</AccordionPanel>
|
||||
</Accordion>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Second Panel" />
|
||||
<AccordionPanel>Content 2</AccordionPanel>
|
||||
</Accordion>
|
||||
</AccordionGroup>`;
|
||||
|
||||
export const accordionGroupMultipleOpenSnippet = `<AccordionGroup allowsMultiple>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="First Panel" />
|
||||
<AccordionPanel>Content 1</AccordionPanel>
|
||||
</Accordion>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Second Panel" />
|
||||
<AccordionPanel>Content 2</AccordionPanel>
|
||||
</Accordion>
|
||||
</AccordionGroup>`;
|
||||
@@ -0,0 +1,130 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Accordion,
|
||||
AccordionTrigger,
|
||||
AccordionPanel,
|
||||
AccordionGroup,
|
||||
} from '../../../../../packages/ui/src/components/Accordion/Accordion';
|
||||
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
|
||||
import { Text } from '../../../../../packages/ui/src/components/Text/Text';
|
||||
|
||||
const Content = () => (
|
||||
<Box>
|
||||
<Text as="p">
|
||||
It's the edge of the world and all of Western civilization
|
||||
</Text>
|
||||
<Text as="p">
|
||||
The sun may rise in the East, at least it settled in a final location
|
||||
</Text>
|
||||
<Text as="p">
|
||||
It's understood that Hollywood sells Californication
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Toggle Panel" />
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithSubtitle = () => {
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionTrigger
|
||||
title="Advanced Settings"
|
||||
subtitle="Configure additional options"
|
||||
/>
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomTrigger = () => {
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionTrigger>
|
||||
<Box>
|
||||
<Text as="div" variant="body-large" weight="bold">
|
||||
Custom Multi-line Trigger
|
||||
</Text>
|
||||
<Text as="div" variant="body-medium" color="secondary">
|
||||
Click to expand additional details and configuration options
|
||||
</Text>
|
||||
</Box>
|
||||
</AccordionTrigger>
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const DefaultExpanded = () => {
|
||||
return (
|
||||
<Accordion defaultExpanded>
|
||||
<AccordionTrigger title="Toggle Panel" />
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const GroupSingleOpen = () => {
|
||||
return (
|
||||
<AccordionGroup>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="First Panel" />
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Second Panel" />
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Third Panel" />
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export const GroupMultipleOpen = () => {
|
||||
return (
|
||||
<AccordionGroup allowsMultiple>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="First Panel" />
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Second Panel" />
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Third Panel" />
|
||||
<AccordionPanel>
|
||||
<Content />
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,21 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import {
|
||||
accordionPropDefs,
|
||||
accordionTriggerPropDefs,
|
||||
accordionPanelPropDefs,
|
||||
accordionGroupPropDefs,
|
||||
} from './props-definition';
|
||||
import {
|
||||
accordionUsageSnippet,
|
||||
defaultSnippet,
|
||||
withSubtitleSnippet,
|
||||
customTriggerSnippet,
|
||||
defaultExpandedSnippet,
|
||||
groupSingleOpenSnippet,
|
||||
groupMultipleOpenSnippet,
|
||||
} from './snippets';
|
||||
import {
|
||||
Default,
|
||||
WithSubtitle,
|
||||
@@ -8,19 +23,7 @@ import {
|
||||
DefaultExpanded,
|
||||
GroupSingleOpen,
|
||||
GroupMultipleOpen,
|
||||
} from './stories';
|
||||
import {
|
||||
accordionPropDefs,
|
||||
accordionTriggerPropDefs,
|
||||
accordionPanelPropDefs,
|
||||
accordionGroupPropDefs,
|
||||
accordionUsageSnippet,
|
||||
accordionWithSubtitleSnippet,
|
||||
accordionCustomTriggerSnippet,
|
||||
accordionDefaultExpandedSnippet,
|
||||
accordionGroupSingleOpenSnippet,
|
||||
accordionGroupMultipleOpenSnippet,
|
||||
} from './accordion.props';
|
||||
} from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { AccordionDefinition } from '../../../utils/definitions';
|
||||
@@ -36,7 +39,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
py={4}
|
||||
height={240}
|
||||
preview={<Default />}
|
||||
code={accordionUsageSnippet}
|
||||
code={defaultSnippet}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
@@ -80,7 +83,7 @@ Here's a view when using both title and subtitle props.
|
||||
py={4}
|
||||
height={240}
|
||||
preview={<WithSubtitle />}
|
||||
code={accordionWithSubtitleSnippet}
|
||||
code={withSubtitleSnippet}
|
||||
/>
|
||||
|
||||
### Custom Trigger
|
||||
@@ -92,7 +95,7 @@ Here's a view when providing custom multi-line content as children.
|
||||
py={4}
|
||||
height={280}
|
||||
preview={<CustomTrigger />}
|
||||
code={accordionCustomTriggerSnippet}
|
||||
code={customTriggerSnippet}
|
||||
/>
|
||||
|
||||
### Default Expanded
|
||||
@@ -104,7 +107,7 @@ Here's a view when the panel is expanded by default.
|
||||
py={4}
|
||||
height={280}
|
||||
preview={<DefaultExpanded />}
|
||||
code={accordionDefaultExpandedSnippet}
|
||||
code={defaultExpandedSnippet}
|
||||
/>
|
||||
|
||||
### Group Single Open
|
||||
@@ -116,7 +119,7 @@ Here's a view when only one accordion can be open at a time.
|
||||
py={4}
|
||||
height={280}
|
||||
preview={<GroupSingleOpen />}
|
||||
code={accordionGroupSingleOpenSnippet}
|
||||
code={groupSingleOpenSnippet}
|
||||
/>
|
||||
|
||||
### Group Multiple Open
|
||||
@@ -128,7 +131,7 @@ Here's a view when multiple accordions can be open simultaneously.
|
||||
py={4}
|
||||
height={280}
|
||||
preview={<GroupMultipleOpen />}
|
||||
code={accordionGroupMultipleOpenSnippet}
|
||||
code={groupMultipleOpenSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={AccordionDefinition} />
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const accordionPropDefs: Record<string, PropDef> = {
|
||||
children: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode', '(state: { isExpanded: boolean }) => ReactNode'],
|
||||
},
|
||||
defaultExpanded: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
isExpanded: {
|
||||
type: 'boolean',
|
||||
},
|
||||
onExpandedChange: {
|
||||
type: 'enum',
|
||||
values: ['(isExpanded: boolean) => void'],
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const accordionTriggerPropDefs: Record<string, PropDef> = {
|
||||
level: {
|
||||
type: 'enum',
|
||||
values: ['1', '2', '3', '4', '5', '6'],
|
||||
default: '3',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
subtitle: {
|
||||
type: 'string',
|
||||
},
|
||||
children: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const accordionPanelPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const accordionGroupPropDefs: Record<string, PropDef> = {
|
||||
allowsMultiple: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
export const accordionUsageSnippet = `import { Accordion, AccordionTrigger, AccordionPanel } from '@backstage/ui';
|
||||
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Toggle Panel" />
|
||||
<AccordionPanel>Your content</AccordionPanel>
|
||||
</Accordion>`;
|
||||
|
||||
export const defaultSnippet = `<Accordion>
|
||||
<AccordionTrigger title="Toggle Panel" />
|
||||
<AccordionPanel>
|
||||
<Text>Your content here</Text>
|
||||
</AccordionPanel>
|
||||
</Accordion>`;
|
||||
|
||||
export const withSubtitleSnippet = `<Accordion>
|
||||
<AccordionTrigger
|
||||
title="Advanced Settings"
|
||||
subtitle="Configure additional options"
|
||||
/>
|
||||
<AccordionPanel>
|
||||
<Text>Your content here</Text>
|
||||
</AccordionPanel>
|
||||
</Accordion>`;
|
||||
|
||||
export const customTriggerSnippet = `<Accordion>
|
||||
<AccordionTrigger>
|
||||
<Box>
|
||||
<Text as="div" variant="body-large" weight="bold">
|
||||
Custom Multi-line Trigger
|
||||
</Text>
|
||||
<Text as="div" variant="body-medium" color="secondary">
|
||||
Click to expand additional details and configuration options
|
||||
</Text>
|
||||
</Box>
|
||||
</AccordionTrigger>
|
||||
<AccordionPanel>
|
||||
<Text>Your content here</Text>
|
||||
</AccordionPanel>
|
||||
</Accordion>`;
|
||||
|
||||
export const defaultExpandedSnippet = `<Accordion defaultExpanded>
|
||||
<AccordionTrigger title="Toggle Panel" />
|
||||
<AccordionPanel>
|
||||
<Text>Your content here</Text>
|
||||
</AccordionPanel>
|
||||
</Accordion>`;
|
||||
|
||||
export const groupSingleOpenSnippet = `<AccordionGroup>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="First Panel" />
|
||||
<AccordionPanel>Content 1</AccordionPanel>
|
||||
</Accordion>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Second Panel" />
|
||||
<AccordionPanel>Content 2</AccordionPanel>
|
||||
</Accordion>
|
||||
</AccordionGroup>`;
|
||||
|
||||
export const groupMultipleOpenSnippet = `<AccordionGroup allowsMultiple>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="First Panel" />
|
||||
<AccordionPanel>Content 1</AccordionPanel>
|
||||
</Accordion>
|
||||
<Accordion>
|
||||
<AccordionTrigger title="Second Panel" />
|
||||
<AccordionPanel>Content 2</AccordionPanel>
|
||||
</Accordion>
|
||||
</AccordionGroup>`;
|
||||
@@ -1,19 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Accordion/Accordion.stories';
|
||||
|
||||
const {
|
||||
Default: DefaultStory,
|
||||
WithSubtitle: WithSubtitleStory,
|
||||
CustomTrigger: CustomTriggerStory,
|
||||
DefaultExpanded: DefaultExpandedStory,
|
||||
GroupSingleOpen: GroupSingleOpenStory,
|
||||
GroupMultipleOpen: GroupMultipleOpenStory,
|
||||
} = stories;
|
||||
|
||||
export const Default = () => <DefaultStory.Component />;
|
||||
export const WithSubtitle = () => <WithSubtitleStory.Component />;
|
||||
export const CustomTrigger = () => <CustomTriggerStory.Component />;
|
||||
export const DefaultExpanded = () => <DefaultExpandedStory.Component />;
|
||||
export const GroupSingleOpen = () => <GroupSingleOpenStory.Component />;
|
||||
export const GroupMultipleOpen = () => <GroupMultipleOpenStory.Component />;
|
||||
@@ -1,89 +0,0 @@
|
||||
import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
|
||||
import type { PropDef } from '@/utils/propDefs';
|
||||
|
||||
export const avatarPropDefs: Record<string, PropDef> = {
|
||||
src: {
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
size: {
|
||||
type: 'enum',
|
||||
values: ['x-small', 'small', 'medium', 'large', 'x-large'],
|
||||
default: 'medium',
|
||||
responsive: true,
|
||||
},
|
||||
purpose: {
|
||||
type: 'enum',
|
||||
values: ['informative', 'decoration'],
|
||||
default: 'informative',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const snippetUsage = `import { Avatar } from '@backstage/ui';
|
||||
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
/>`;
|
||||
|
||||
export const snippetSizes = `<Flex gap="4" direction="column">
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille" size="x-small"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille" size="small"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille" size="medium"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille" size="large"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille" size="x-large"
|
||||
/>
|
||||
</Flex>`;
|
||||
|
||||
export const snippetFallback = `<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/15406AAAAAAAAA"
|
||||
name="Charles de Dreuille"
|
||||
/>`;
|
||||
|
||||
export const snippetPurpose = `<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="1">
|
||||
<Text variant="title-x-small">Informative (default)</Text>
|
||||
<Text variant="body-medium">
|
||||
Use when avatar appears alone. Announced as "Charles de Dreuille" to screen readers:
|
||||
</Text>
|
||||
<Flex gap="2" align="center">
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
purpose="informative"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="1">
|
||||
<Text variant="title-x-small">Decoration</Text>
|
||||
<Text variant="body-medium">
|
||||
Use when name appears adjacent to avatar. Hidden from screen readers to avoid redundancy:
|
||||
</Text>
|
||||
<Flex gap="2" align="center">
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
purpose="decoration"
|
||||
/>
|
||||
<Text>Charles de Dreuille</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>`;
|
||||
@@ -0,0 +1,100 @@
|
||||
'use client';
|
||||
|
||||
import { Avatar } from '../../../../../packages/ui/src/components/Avatar/Avatar';
|
||||
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
|
||||
import { Text } from '../../../../../packages/ui/src/components/Text/Text';
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Fallback = () => {
|
||||
return (
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/15406AAAAAAAAA"
|
||||
name="Charles de Dreuille"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sizes = () => {
|
||||
return (
|
||||
<Flex direction="column" gap="6">
|
||||
<Flex>
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
size="x-small"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
size="small"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
size="medium"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
size="large"
|
||||
/>
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
size="x-large"
|
||||
/>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Avatar name="Charles de Dreuille" size="x-small" src="" />
|
||||
<Avatar name="Charles de Dreuille" size="small" src="" />
|
||||
<Avatar name="Charles de Dreuille" size="medium" src="" />
|
||||
<Avatar name="Charles de Dreuille" size="large" src="" />
|
||||
<Avatar name="Charles de Dreuille" size="x-large" src="" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const Purpose = () => {
|
||||
return (
|
||||
<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="1">
|
||||
<Text variant="title-x-small">Informative (default)</Text>
|
||||
<Text variant="body-medium">
|
||||
Use when avatar appears alone. Announced as "Charles de
|
||||
Dreuille" to screen readers:
|
||||
</Text>
|
||||
<Flex gap="2" align="center">
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
purpose="informative"
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="1">
|
||||
<Text variant="title-x-small">Decoration</Text>
|
||||
<Text variant="body-medium">
|
||||
Use when avatar appears with adjacent text. Hidden from screen
|
||||
readers:
|
||||
</Text>
|
||||
<Flex gap="2" align="center">
|
||||
<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
purpose="decoration"
|
||||
/>
|
||||
<Text>Charles de Dreuille</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -1,14 +1,15 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Default, Sizes, Fallback, Purpose } from './stories';
|
||||
import { avatarPropDefs } from './props-definition';
|
||||
import {
|
||||
avatarPropDefs,
|
||||
snippetUsage,
|
||||
snippetSizes,
|
||||
snippetFallback,
|
||||
snippetPurpose,
|
||||
} from './avatar.props';
|
||||
avatarUsageSnippet,
|
||||
defaultSnippet,
|
||||
sizesSnippet,
|
||||
fallbackSnippet,
|
||||
purposeSnippet,
|
||||
} from './snippets';
|
||||
import { Default, Sizes, Fallback, Purpose } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { AvatarDefinition } from '../../../utils/definitions';
|
||||
@@ -19,16 +20,11 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="An avatar component with a fallback for initials."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={`<Avatar src="https://avatars.githubusercontent.com/u/1540635?v=4" name="Charles de Dreuille" />`}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={snippetUsage} />
|
||||
<CodeBlock code={avatarUsageSnippet} />
|
||||
|
||||
## API reference
|
||||
|
||||
@@ -45,7 +41,7 @@ Avatar sizes can be set using the `size` prop.
|
||||
py={4}
|
||||
open
|
||||
preview={<Sizes />}
|
||||
code={snippetSizes}
|
||||
code={sizesSnippet}
|
||||
/>
|
||||
|
||||
### Fallback
|
||||
@@ -57,7 +53,7 @@ If the image is not available, the avatar will show the initials of the name.
|
||||
py={4}
|
||||
open
|
||||
preview={<Fallback />}
|
||||
code={snippetFallback}
|
||||
code={fallbackSnippet}
|
||||
/>
|
||||
|
||||
### The `purpose` prop
|
||||
@@ -68,7 +64,7 @@ Control how the avatar is announced to screen readers using the `purpose` prop.
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Purpose />}
|
||||
code={snippetPurpose}
|
||||
code={purposeSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={AvatarDefinition} />
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const avatarPropDefs: Record<string, PropDef> = {
|
||||
src: {
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
size: {
|
||||
type: 'enum',
|
||||
values: ['x-small', 'small', 'medium', 'large', 'x-large'],
|
||||
default: 'medium',
|
||||
responsive: true,
|
||||
},
|
||||
purpose: {
|
||||
type: 'enum',
|
||||
values: ['informative', 'decoration'],
|
||||
default: 'informative',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
export const avatarUsageSnippet = `import { Avatar } from '@backstage/ui';
|
||||
|
||||
<Avatar />`;
|
||||
|
||||
export const defaultSnippet = `<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/1540635?v=4"
|
||||
name="Charles de Dreuille"
|
||||
/>`;
|
||||
|
||||
export const fallbackSnippet = `<Avatar
|
||||
src="https://avatars.githubusercontent.com/u/15406AAAAAAAAA"
|
||||
name="Charles de Dreuille"
|
||||
/>`;
|
||||
|
||||
export const sizesSnippet = `<Flex direction="column" gap="6">
|
||||
<Flex>
|
||||
<Avatar src="..." name="Charles de Dreuille" size="x-small" />
|
||||
<Avatar src="..." name="Charles de Dreuille" size="small" />
|
||||
<Avatar src="..." name="Charles de Dreuille" size="medium" />
|
||||
<Avatar src="..." name="Charles de Dreuille" size="large" />
|
||||
<Avatar src="..." name="Charles de Dreuille" size="x-large" />
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Avatar name="Charles de Dreuille" size="x-small" src="" />
|
||||
<Avatar name="Charles de Dreuille" size="small" src="" />
|
||||
<Avatar name="Charles de Dreuille" size="medium" src="" />
|
||||
<Avatar name="Charles de Dreuille" size="large" src="" />
|
||||
<Avatar name="Charles de Dreuille" size="x-large" src="" />
|
||||
</Flex>
|
||||
</Flex>`;
|
||||
|
||||
export const purposeSnippet = `<Flex direction="column" gap="4">
|
||||
<Flex direction="column" gap="1">
|
||||
<Text variant="title-x-small">Informative (default)</Text>
|
||||
<Text variant="body-medium">
|
||||
Use when avatar appears alone. Announced as "Charles de Dreuille" to screen readers:
|
||||
</Text>
|
||||
<Flex gap="2" align="center">
|
||||
<Avatar src="..." name="Charles de Dreuille" purpose="informative" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap="1">
|
||||
<Text variant="title-x-small">Decoration</Text>
|
||||
<Text variant="body-medium">
|
||||
Use when avatar appears with adjacent text. Hidden from screen readers:
|
||||
</Text>
|
||||
<Flex gap="2" align="center">
|
||||
<Avatar src="..." name="Charles de Dreuille" purpose="decoration" />
|
||||
<Text>Charles de Dreuille</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>`;
|
||||
@@ -1,15 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Avatar/Avatar.stories';
|
||||
|
||||
const {
|
||||
Default: DefaultStory,
|
||||
Sizes: SizesStory,
|
||||
Fallback: FallbackStory,
|
||||
Purpose: PurposeStory,
|
||||
} = stories;
|
||||
|
||||
export const Default = () => <DefaultStory.Component />;
|
||||
export const Sizes = () => <SizesStory.Component />;
|
||||
export const Fallback = () => <FallbackStory.Component />;
|
||||
export const Purpose = () => <PurposeStory.Component />;
|
||||
@@ -0,0 +1,34 @@
|
||||
'use client';
|
||||
|
||||
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
|
||||
|
||||
const diagonalStripePattern = (() => {
|
||||
const svg = `
|
||||
<svg width="6" height="6" viewBox="0 0 6 6" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#2563eb" fill-opacity="0.6" fill-rule="evenodd">
|
||||
<path d="M5 0h1L0 6V5zM6 5v1H5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
`.trim();
|
||||
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
||||
})();
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<Box
|
||||
width="64px"
|
||||
height="64px"
|
||||
style={{
|
||||
background: '#eaf2fd',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid #2563eb',
|
||||
backgroundImage: `url("${diagonalStripePattern}")`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontWeight: 'bold',
|
||||
color: '#2563eb',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,14 +1,14 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Default } from './stories';
|
||||
import { boxPropDefs } from './props-definition';
|
||||
import {
|
||||
boxPropDefs,
|
||||
snippetUsage,
|
||||
boxPreviewSnippet,
|
||||
defaultSnippet,
|
||||
boxSimpleSnippet,
|
||||
boxResponsiveSnippet,
|
||||
} from './box.props';
|
||||
} from './snippets';
|
||||
import { Default } from './components';
|
||||
import { spacingPropDefs } from '@/utils/propDefs';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
@@ -21,12 +21,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="Box is the lowest-level component in Backstage UI. It provides a consistent API for styling and layout."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={boxPreviewSnippet}
|
||||
align="center"
|
||||
/>
|
||||
<Snippet py={4} preview={<Default />} code={defaultSnippet} align="center" />
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
-16
@@ -22,19 +22,3 @@ export const boxPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const snippetUsage = `import { Box } from '@backstage/ui';
|
||||
|
||||
<Box />`;
|
||||
|
||||
export const boxPreviewSnippet = `<Box>
|
||||
<DecorativeBox />
|
||||
</Box>`;
|
||||
|
||||
export const boxSimpleSnippet = `<Box padding="md" borderRadius="md">Hello World</Box>`;
|
||||
|
||||
export const boxResponsiveSnippet = `<Box
|
||||
padding={{ xs: 'sm', md: 'md' }}
|
||||
borderRadius={{ xs: 'sm', md: 'md' }}>
|
||||
Hello World
|
||||
</Box>`;
|
||||
@@ -0,0 +1,21 @@
|
||||
export const snippetUsage = `import { Box } from '@backstage/ui';
|
||||
|
||||
<Box />`;
|
||||
|
||||
export const defaultSnippet = `<Box
|
||||
width="64px"
|
||||
height="64px"
|
||||
style={{
|
||||
background: '#eaf2fd',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid #2563eb',
|
||||
}}
|
||||
/>`;
|
||||
|
||||
export const boxSimpleSnippet = `<Box padding="md" borderRadius="md">Hello World</Box>`;
|
||||
|
||||
export const boxResponsiveSnippet = `<Box
|
||||
padding={{ xs: 'sm', md: 'md' }}
|
||||
borderRadius={{ xs: 'sm', md: 'md' }}>
|
||||
Hello World
|
||||
</Box>`;
|
||||
@@ -1,7 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Box/Box.stories';
|
||||
|
||||
const { Default: DefaultStory } = stories;
|
||||
|
||||
export const Default = () => <DefaultStory.Component />;
|
||||
@@ -1,62 +0,0 @@
|
||||
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 },
|
||||
loading: { 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/ui';
|
||||
|
||||
<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" />} isDisabled />`;
|
||||
|
||||
export const buttonIconLoadingSnippet = `<ButtonIcon icon={<Icon name="cloud" />} variant="primary" loading={isLoading} onPress={handleClick} />`;
|
||||
|
||||
export const buttonIconResponsiveSnippet = `<ButtonIcon icon={<Icon name="cloud" />} variant={{ initial: 'primary', lg: 'secondary' }} />`;
|
||||
|
||||
export const buttonIconAsLinkSnippet = `import { ButtonLink } from '@backstage/ui';
|
||||
|
||||
<ButtonLink href="https://ui.backstage.io" target="_blank">
|
||||
Button
|
||||
</ButtonLink>`;
|
||||
@@ -0,0 +1,68 @@
|
||||
'use client';
|
||||
|
||||
import { ButtonIcon } from '../../../../../packages/ui/src/components/ButtonIcon/ButtonIcon';
|
||||
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
|
||||
import { RiCloudLine } from '@remixicon/react';
|
||||
|
||||
export const Variants = () => {
|
||||
return (
|
||||
<Flex align="center" gap="2">
|
||||
<ButtonIcon icon={<RiCloudLine />} variant="primary" aria-label="Cloud" />
|
||||
<ButtonIcon
|
||||
icon={<RiCloudLine />}
|
||||
variant="secondary"
|
||||
aria-label="Cloud"
|
||||
/>
|
||||
<ButtonIcon
|
||||
icon={<RiCloudLine />}
|
||||
variant="tertiary"
|
||||
aria-label="Cloud"
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sizes = () => {
|
||||
return (
|
||||
<Flex align="center" gap="2">
|
||||
<ButtonIcon icon={<RiCloudLine />} size="small" aria-label="Cloud" />
|
||||
<ButtonIcon icon={<RiCloudLine />} size="medium" aria-label="Cloud" />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const Disabled = () => {
|
||||
return (
|
||||
<Flex direction="row" gap="2">
|
||||
<ButtonIcon
|
||||
isDisabled
|
||||
icon={<RiCloudLine />}
|
||||
variant="primary"
|
||||
aria-label="Cloud"
|
||||
/>
|
||||
<ButtonIcon
|
||||
isDisabled
|
||||
icon={<RiCloudLine />}
|
||||
variant="secondary"
|
||||
aria-label="Cloud"
|
||||
/>
|
||||
<ButtonIcon
|
||||
isDisabled
|
||||
icon={<RiCloudLine />}
|
||||
variant="tertiary"
|
||||
aria-label="Cloud"
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const Loading = () => {
|
||||
return (
|
||||
<ButtonIcon
|
||||
icon={<RiCloudLine />}
|
||||
variant="primary"
|
||||
loading
|
||||
aria-label="Cloud"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,18 +1,15 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Variants, Sizes, Disabled, Loading } from './stories';
|
||||
import { buttonIconPropDefs } from './props-definition';
|
||||
import {
|
||||
buttonIconPropDefs,
|
||||
buttonIconUsageSnippet,
|
||||
buttonIconDefaultSnippet,
|
||||
buttonIconVariantsSnippet,
|
||||
buttonIconSizesSnippet,
|
||||
buttonIconDisabledSnippet,
|
||||
buttonIconLoadingSnippet,
|
||||
buttonIconResponsiveSnippet,
|
||||
buttonIconAsLinkSnippet,
|
||||
} from './button-icon.props';
|
||||
variantsSnippet,
|
||||
sizesSnippet,
|
||||
disabledSnippet,
|
||||
loadingSnippet,
|
||||
} from './snippets';
|
||||
import { Variants, Sizes, Disabled, Loading } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ButtonIconDefinition } from '../../../utils/definitions';
|
||||
@@ -23,12 +20,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="A button component with a single icon that can be used to trigger actions."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Variants />}
|
||||
code={buttonIconDefaultSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Variants />} code={variantsSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -49,7 +41,7 @@ Here's a view when buttons have different variants.
|
||||
py={4}
|
||||
open
|
||||
preview={<Variants />}
|
||||
code={buttonIconVariantsSnippet}
|
||||
code={variantsSnippet}
|
||||
/>
|
||||
|
||||
### Sizes
|
||||
@@ -61,7 +53,7 @@ Here's a view when buttons have different sizes.
|
||||
py={4}
|
||||
open
|
||||
preview={<Sizes />}
|
||||
code={buttonIconSizesSnippet}
|
||||
code={sizesSnippet}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
@@ -73,7 +65,7 @@ Here's a view when buttons are disabled.
|
||||
py={4}
|
||||
open
|
||||
preview={<Disabled />}
|
||||
code={buttonIconDisabledSnippet}
|
||||
code={disabledSnippet}
|
||||
/>
|
||||
|
||||
### Loading
|
||||
@@ -85,15 +77,9 @@ Here's a view when buttons are in a loading state.
|
||||
py={4}
|
||||
open
|
||||
preview={<Loading />}
|
||||
code={buttonIconLoadingSnippet}
|
||||
code={loadingSnippet}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
|
||||
Here's a view when buttons are responsive.
|
||||
|
||||
<CodeBlock code={buttonIconResponsiveSnippet} />
|
||||
|
||||
<Theming definition={ButtonIconDefinition} />
|
||||
|
||||
<ChangelogComponent component="button-icon" />
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const buttonIconPropDefs: Record<string, PropDef> = {
|
||||
variant: {
|
||||
type: 'enum',
|
||||
values: ['primary', 'secondary', 'tertiary'],
|
||||
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 },
|
||||
loading: { type: 'boolean', default: 'false', responsive: false },
|
||||
type: {
|
||||
type: 'enum',
|
||||
values: ['button', 'submit', 'reset'],
|
||||
default: 'button',
|
||||
responsive: false,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
export const buttonIconUsageSnippet = `import { ButtonIcon } from '@backstage/ui';
|
||||
|
||||
<ButtonIcon />`;
|
||||
|
||||
export const variantsSnippet = `<Flex align="center" gap="2">
|
||||
<ButtonIcon icon={<RiCloudLine />} variant="primary" aria-label="Cloud" />
|
||||
<ButtonIcon icon={<RiCloudLine />} variant="secondary" aria-label="Cloud" />
|
||||
<ButtonIcon icon={<RiCloudLine />} variant="tertiary" aria-label="Cloud" />
|
||||
</Flex>`;
|
||||
|
||||
export const sizesSnippet = `<Flex align="center" gap="2">
|
||||
<ButtonIcon icon={<RiCloudLine />} size="small" aria-label="Cloud" />
|
||||
<ButtonIcon icon={<RiCloudLine />} size="medium" aria-label="Cloud" />
|
||||
</Flex>`;
|
||||
|
||||
export const disabledSnippet = `<Flex direction="row" gap="2">
|
||||
<ButtonIcon isDisabled icon={<RiCloudLine />} variant="primary" aria-label="Cloud" />
|
||||
<ButtonIcon isDisabled icon={<RiCloudLine />} variant="secondary" aria-label="Cloud" />
|
||||
<ButtonIcon isDisabled icon={<RiCloudLine />} variant="tertiary" aria-label="Cloud" />
|
||||
</Flex>`;
|
||||
|
||||
export const loadingSnippet = `<ButtonIcon icon={<RiCloudLine />} variant="primary" loading aria-label="Cloud" />`;
|
||||
@@ -1,15 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/ButtonIcon/ButtonIcon.stories';
|
||||
|
||||
const {
|
||||
Variants: VariantsStory,
|
||||
Sizes: SizesStory,
|
||||
Disabled: DisabledStory,
|
||||
Loading: LoadingStory,
|
||||
} = stories;
|
||||
|
||||
export const Variants = () => <VariantsStory.Component />;
|
||||
export const Sizes = () => <SizesStory.Component />;
|
||||
export const Disabled = () => <DisabledStory.Component />;
|
||||
export const Loading = () => <LoadingStory.Component />;
|
||||
@@ -1,73 +0,0 @@
|
||||
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/ui';
|
||||
|
||||
<ButtonLink />`;
|
||||
|
||||
export const buttonLinkVariantsSnippet = `<Flex align="center">
|
||||
<ButtonLink iconStart={<Icon name="cloud" />} variant="primary">
|
||||
Button
|
||||
</ButtonLink>
|
||||
<ButtonLink iconStart={<Icon name="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>`;
|
||||
@@ -0,0 +1,122 @@
|
||||
'use client';
|
||||
|
||||
import { ButtonLink } from '../../../../../packages/ui/src/components/ButtonLink/ButtonLink';
|
||||
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RiArrowRightSLine, RiCloudLine } from '@remixicon/react';
|
||||
|
||||
export const Variants = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Flex align="center">
|
||||
<ButtonLink
|
||||
iconStart={<RiCloudLine />}
|
||||
variant="primary"
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
iconStart={<RiCloudLine />}
|
||||
variant="secondary"
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
iconStart={<RiCloudLine />}
|
||||
variant="tertiary"
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
</Flex>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sizes = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Flex align="center">
|
||||
<ButtonLink size="small" href="https://ui.backstage.io" target="_blank">
|
||||
Small
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
size="medium"
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Medium
|
||||
</ButtonLink>
|
||||
</Flex>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithIcons = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Flex align="center">
|
||||
<ButtonLink
|
||||
iconStart={<RiCloudLine />}
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
iconEnd={<RiArrowRightSLine />}
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
iconStart={<RiCloudLine />}
|
||||
iconEnd={<RiArrowRightSLine />}
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
</Flex>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export const Disabled = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Flex gap="4">
|
||||
<ButtonLink
|
||||
variant="primary"
|
||||
isDisabled
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Primary
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
variant="secondary"
|
||||
isDisabled
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Secondary
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
variant="tertiary"
|
||||
isDisabled
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Tertiary
|
||||
</ButtonLink>
|
||||
</Flex>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +1,15 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Variants, Sizes, WithIcons, Disabled } from './stories';
|
||||
import { buttonLinkPropDefs } from './props-definition';
|
||||
import {
|
||||
buttonLinkPropDefs,
|
||||
buttonLinkSnippetUsage,
|
||||
buttonLinkVariantsSnippet,
|
||||
buttonLinkSizesSnippet,
|
||||
buttonLinkIconsSnippet,
|
||||
buttonLinkDisabledSnippet,
|
||||
buttonLinkResponsiveSnippet,
|
||||
} from './button-link.props';
|
||||
buttonLinkUsageSnippet,
|
||||
variantsSnippet,
|
||||
sizesSnippet,
|
||||
withIconsSnippet,
|
||||
disabledSnippet,
|
||||
} from './snippets';
|
||||
import { Variants, Sizes, WithIcons, Disabled } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ButtonLinkDefinition } from '../../../utils/definitions';
|
||||
@@ -25,12 +24,12 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Variants />}
|
||||
code={buttonLinkVariantsSnippet}
|
||||
code={variantsSnippet}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={buttonLinkSnippetUsage} />
|
||||
<CodeBlock code={buttonLinkUsageSnippet} />
|
||||
|
||||
## API reference
|
||||
|
||||
@@ -47,7 +46,7 @@ Here's a view when buttons have different variants.
|
||||
py={4}
|
||||
open
|
||||
preview={<Variants />}
|
||||
code={buttonLinkVariantsSnippet}
|
||||
code={variantsSnippet}
|
||||
/>
|
||||
|
||||
### Sizes
|
||||
@@ -59,7 +58,7 @@ Here's a view when buttons have different sizes.
|
||||
py={4}
|
||||
open
|
||||
preview={<Sizes />}
|
||||
code={buttonLinkSizesSnippet}
|
||||
code={sizesSnippet}
|
||||
/>
|
||||
|
||||
### With Icons
|
||||
@@ -71,7 +70,7 @@ Here's a view when buttons have icons.
|
||||
py={4}
|
||||
open
|
||||
preview={<WithIcons />}
|
||||
code={buttonLinkIconsSnippet}
|
||||
code={withIconsSnippet}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
@@ -83,15 +82,9 @@ Here's a view when buttons are disabled.
|
||||
py={4}
|
||||
open
|
||||
preview={<Disabled />}
|
||||
code={buttonLinkDisabledSnippet}
|
||||
code={disabledSnippet}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
|
||||
Here's a view when buttons are responsive.
|
||||
|
||||
<CodeBlock code={buttonLinkResponsiveSnippet} />
|
||||
|
||||
<Theming definition={ButtonLinkDefinition} />
|
||||
|
||||
<ChangelogComponent component="button-link" />
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
childrenPropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const buttonLinkPropDefs: Record<string, PropDef> = {
|
||||
variant: {
|
||||
type: 'enum',
|
||||
values: ['primary', 'secondary', 'tertiary'],
|
||||
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', required: true },
|
||||
target: {
|
||||
type: 'enum',
|
||||
values: ['_self', '_blank', '_parent', '_top'],
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
export const buttonLinkUsageSnippet = `import { ButtonLink } from '@backstage/ui';
|
||||
|
||||
<ButtonLink href="https://ui.backstage.io">Button</ButtonLink>`;
|
||||
|
||||
export const variantsSnippet = `<Flex align="center">
|
||||
<ButtonLink
|
||||
iconStart={<RiCloudLine />}
|
||||
variant="primary"
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
iconStart={<RiCloudLine />}
|
||||
variant="secondary"
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
iconStart={<RiCloudLine />}
|
||||
variant="tertiary"
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
</Flex>`;
|
||||
|
||||
export const sizesSnippet = `<Flex align="center">
|
||||
<ButtonLink size="small" href="https://ui.backstage.io" target="_blank">
|
||||
Small
|
||||
</ButtonLink>
|
||||
<ButtonLink size="medium" href="https://ui.backstage.io" target="_blank">
|
||||
Medium
|
||||
</ButtonLink>
|
||||
</Flex>`;
|
||||
|
||||
export const withIconsSnippet = `<Flex align="center">
|
||||
<ButtonLink iconStart={<RiCloudLine />} href="https://ui.backstage.io" target="_blank">
|
||||
Button
|
||||
</ButtonLink>
|
||||
<ButtonLink iconEnd={<RiArrowRightSLine />} href="https://ui.backstage.io" target="_blank">
|
||||
Button
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
iconStart={<RiCloudLine />}
|
||||
iconEnd={<RiArrowRightSLine />}
|
||||
href="https://ui.backstage.io"
|
||||
target="_blank"
|
||||
>
|
||||
Button
|
||||
</ButtonLink>
|
||||
</Flex>`;
|
||||
|
||||
export const disabledSnippet = `<Flex gap="4">
|
||||
<ButtonLink variant="primary" isDisabled href="https://ui.backstage.io" target="_blank">
|
||||
Primary
|
||||
</ButtonLink>
|
||||
<ButtonLink variant="secondary" isDisabled href="https://ui.backstage.io" target="_blank">
|
||||
Secondary
|
||||
</ButtonLink>
|
||||
<ButtonLink variant="tertiary" isDisabled href="https://ui.backstage.io" target="_blank">
|
||||
Tertiary
|
||||
</ButtonLink>
|
||||
</Flex>`;
|
||||
@@ -1,15 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/ButtonLink/ButtonLink.stories';
|
||||
|
||||
const {
|
||||
Variants: VariantsStory,
|
||||
Sizes: SizesStory,
|
||||
WithIcons: WithIconsStory,
|
||||
Disabled: DisabledStory,
|
||||
} = stories;
|
||||
|
||||
export const Variants = () => <VariantsStory.Component />;
|
||||
export const Sizes = () => <SizesStory.Component />;
|
||||
export const WithIcons = () => <WithIconsStory.Component />;
|
||||
export const Disabled = () => <DisabledStory.Component />;
|
||||
@@ -1,77 +0,0 @@
|
||||
import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
|
||||
import type { PropDef } from '@/utils/propDefs';
|
||||
|
||||
export const buttonPropDefs: 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 },
|
||||
loading: { 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,
|
||||
};
|
||||
|
||||
export const buttonSnippetUsage = `import { Button } from '@backstage/ui';
|
||||
|
||||
<Button />`;
|
||||
|
||||
export const buttonVariantsSnippet = `<Flex align="center">
|
||||
<Button iconStart="cloud" variant="primary">
|
||||
Button
|
||||
</Button>
|
||||
<Button iconStart="cloud" variant="secondary">
|
||||
Button
|
||||
</Button>
|
||||
</Flex>`;
|
||||
|
||||
export const buttonSizesSnippet = `<Flex align="center">
|
||||
<Button size="small">Small</Button>
|
||||
<Button size="medium">Medium</Button>
|
||||
</Flex>`;
|
||||
|
||||
export const buttonIconsSnippet = `<Flex align="center">
|
||||
<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" isDisabled>
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="secondary" isDisabled>
|
||||
Secondary
|
||||
</Button>
|
||||
</Flex>`;
|
||||
|
||||
export const buttonResponsiveSnippet = `<Button variant={{ initial: 'primary', lg: 'secondary' }}>
|
||||
Responsive Button
|
||||
</Button>`;
|
||||
|
||||
export const buttonLoadingSnippet = `<Button variant="primary" loading={isLoading} onPress={handleClick}>
|
||||
Load more items
|
||||
</Button>`;
|
||||
|
||||
export const buttonAsLinkSnippet = `import { ButtonLink } from '@backstage/ui';
|
||||
|
||||
<ButtonLink href="https://ui.backstage.io" target="_blank">
|
||||
Button
|
||||
</ButtonLink>`;
|
||||
@@ -0,0 +1,78 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
|
||||
import { ButtonLink } from '../../../../../packages/ui/src/components/ButtonLink/ButtonLink';
|
||||
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
|
||||
import { RiArrowRightSLine, RiCloudLine } from '@remixicon/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
export const Variants = () => {
|
||||
return (
|
||||
<Flex>
|
||||
<Button variant="primary" iconStart={<RiCloudLine />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="secondary" iconStart={<RiCloudLine />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" iconStart={<RiCloudLine />}>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sizes = () => {
|
||||
return (
|
||||
<Flex align="center">
|
||||
<Button size="small">Small</Button>
|
||||
<Button size="medium">Medium</Button>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithIcons = () => {
|
||||
return (
|
||||
<Flex align="center">
|
||||
<Button iconStart={<RiCloudLine />}>Button</Button>
|
||||
<Button iconEnd={<RiArrowRightSLine />}>Button</Button>
|
||||
<Button iconStart={<RiCloudLine />} iconEnd={<RiArrowRightSLine />}>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const Disabled = () => {
|
||||
return (
|
||||
<Flex gap="4">
|
||||
<Button variant="primary" isDisabled>
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="secondary" isDisabled>
|
||||
Secondary
|
||||
</Button>
|
||||
<Button variant="tertiary" isDisabled>
|
||||
Tertiary
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const Loading = () => {
|
||||
return (
|
||||
<Button variant="primary" loading={true}>
|
||||
Load more items
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export const AsLink = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<ButtonLink href="https://ui.backstage.io" target="_blank">
|
||||
Button
|
||||
</ButtonLink>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
@@ -1,35 +1,36 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Variants, Sizes, WithIcons, Disabled, Loading } from './stories';
|
||||
import { Variants as ButtonLinkVariants } from '../button-link/stories';
|
||||
import { buttonPropDefs } from './props-definition';
|
||||
import {
|
||||
buttonPropDefs,
|
||||
variantsSnippet,
|
||||
sizesSnippet,
|
||||
withIconsSnippet,
|
||||
disabledSnippet,
|
||||
loadingSnippet,
|
||||
asLinkSnippet,
|
||||
buttonSnippetUsage,
|
||||
buttonVariantsSnippet,
|
||||
buttonSizesSnippet,
|
||||
buttonIconsSnippet,
|
||||
buttonDisabledSnippet,
|
||||
buttonLoadingSnippet,
|
||||
buttonResponsiveSnippet,
|
||||
buttonAsLinkSnippet,
|
||||
} from './button.props';
|
||||
} from './snippets';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ButtonDefinition } from '../../../utils/definitions';
|
||||
import {
|
||||
Variants,
|
||||
Sizes,
|
||||
WithIcons,
|
||||
Disabled,
|
||||
Loading,
|
||||
AsLink,
|
||||
} from './components';
|
||||
|
||||
<PageTitle
|
||||
title="Button"
|
||||
description="A button component that can be used to trigger actions."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Variants />}
|
||||
code={buttonVariantsSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Variants />} code={variantsSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -50,20 +51,14 @@ Here's a view when buttons have different variants.
|
||||
py={4}
|
||||
open
|
||||
preview={<Variants />}
|
||||
code={buttonVariantsSnippet}
|
||||
code={variantsSnippet}
|
||||
/>
|
||||
|
||||
### Sizes
|
||||
|
||||
Here's a view when buttons have different sizes.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<Sizes />}
|
||||
code={buttonSizesSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} open preview={<Sizes />} code={sizesSnippet} />
|
||||
|
||||
### With Icons
|
||||
|
||||
@@ -74,7 +69,7 @@ Here's a view when buttons have icons.
|
||||
py={4}
|
||||
open
|
||||
preview={<WithIcons />}
|
||||
code={buttonIconsSnippet}
|
||||
code={withIconsSnippet}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
@@ -86,7 +81,7 @@ Here's a view when buttons are disabled.
|
||||
py={4}
|
||||
open
|
||||
preview={<Disabled />}
|
||||
code={buttonDisabledSnippet}
|
||||
code={disabledSnippet}
|
||||
/>
|
||||
|
||||
### Loading
|
||||
@@ -98,7 +93,7 @@ Here's a view when buttons are in a loading state.
|
||||
py={4}
|
||||
open
|
||||
preview={<Loading />}
|
||||
code={buttonLoadingSnippet}
|
||||
code={loadingSnippet}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
@@ -111,13 +106,7 @@ Here's a view when buttons are responsive.
|
||||
|
||||
If you want to use a button as a link, please use the `ButtonLink` component.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonLinkVariants />}
|
||||
code={buttonAsLinkSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} open preview={<AsLink />} code={asLinkSnippet} />
|
||||
|
||||
<Theming definition={ButtonDefinition} />
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
|
||||
import type { PropDef } from '@/utils/propDefs';
|
||||
|
||||
export const buttonPropDefs: 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 },
|
||||
loading: { 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,
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
export const buttonSnippetUsage = `import { Button } from '@backstage/ui';
|
||||
|
||||
<Button />`;
|
||||
|
||||
export const buttonResponsiveSnippet = `<Button variant={{ initial: 'primary', lg: 'secondary' }}>
|
||||
Responsive Button
|
||||
</Button>`;
|
||||
|
||||
export const variantsSnippet = `<Flex>
|
||||
<Button variant="primary" iconStart={<RiCloudLine />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="secondary" iconStart={<RiCloudLine />}>
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="tertiary" iconStart={<RiCloudLine />}>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>`;
|
||||
|
||||
export const sizesSnippet = `<Flex align="center">
|
||||
<Button size="small">Small</Button>
|
||||
<Button size="medium">Medium</Button>
|
||||
</Flex>`;
|
||||
|
||||
export const withIconsSnippet = `<Flex align="center">
|
||||
<Button iconStart={<RiCloudLine />}>Button</Button>
|
||||
<Button iconEnd={<RiArrowRightSLine />}>Button</Button>
|
||||
<Button iconStart={<RiCloudLine />} iconEnd={<RiArrowRightSLine />}>
|
||||
Button
|
||||
</Button>
|
||||
</Flex>`;
|
||||
|
||||
export const disabledSnippet = `<Flex gap="4">
|
||||
<Button variant="primary" isDisabled>
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant="secondary" isDisabled>
|
||||
Secondary
|
||||
</Button>
|
||||
<Button variant="tertiary" isDisabled>
|
||||
Tertiary
|
||||
</Button>
|
||||
</Flex>`;
|
||||
|
||||
export const loadingSnippet = `<Button variant="primary" loading={true}>
|
||||
Load more items
|
||||
</Button>`;
|
||||
|
||||
export const asLinkSnippet = `<MemoryRouter>
|
||||
<ButtonLink href="https://ui.backstage.io" target="_blank">
|
||||
Button
|
||||
</ButtonLink>
|
||||
</MemoryRouter>`;
|
||||
@@ -1,17 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Button/Button.stories';
|
||||
|
||||
const {
|
||||
Variants: VariantsStory,
|
||||
Sizes: SizesStory,
|
||||
WithIcons: WithIconsStory,
|
||||
Disabled: DisabledStory,
|
||||
Loading: LoadingStory,
|
||||
} = stories;
|
||||
|
||||
export const Variants = () => <VariantsStory.Component />;
|
||||
export const Sizes = () => <SizesStory.Component />;
|
||||
export const WithIcons = () => <WithIconsStory.Component />;
|
||||
export const Disabled = () => <DisabledStory.Component />;
|
||||
export const Loading = () => <LoadingStory.Component />;
|
||||
@@ -1,83 +0,0 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const cardPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const cardHeaderPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const cardBodyPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const cardFooterPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const cardUsageSnippet = `import { card } from '@backstage/ui';
|
||||
|
||||
<Card>
|
||||
<CardHeader>Header</CardHeader>
|
||||
<CardBody>Body</CardBody>
|
||||
<CardFooter>Footer</CardFooter>
|
||||
</Card>`;
|
||||
|
||||
export const cardDefaultSnippet = `<Card>
|
||||
<CardHeader>Header</CardHeader>
|
||||
<CardBody>Body</CardBody>
|
||||
<CardFooter>Footer</CardFooter>
|
||||
</Card>`;
|
||||
|
||||
export const cardLongBodySnippet = `<Card style={{ width: '300px', height: '200px' }}>
|
||||
<CardHeader>
|
||||
<Text>Header</Text>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Text>
|
||||
This is the first paragraph of a long body text that demonstrates how
|
||||
the Card component handles extensive content. The card should adjust
|
||||
accordingly to display all the text properly while maintaining its
|
||||
structure.
|
||||
</Text>
|
||||
<Text>
|
||||
Here's a second paragraph that adds more content to our card body.
|
||||
Having multiple paragraphs helps to visualize how spacing works within
|
||||
the card component.
|
||||
</Text>
|
||||
<Text>
|
||||
This third paragraph continues to add more text to ensure we have a
|
||||
proper demonstration of a card with significant content. This makes it
|
||||
easier to test scrolling behavior and overall layout when content
|
||||
exceeds the initial view.
|
||||
</Text>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Text>Footer</Text>
|
||||
</CardFooter>
|
||||
</Card>`;
|
||||
|
||||
export const cardListRowSnippet = `<Card style={{ width: '300px', height: '200px' }}>
|
||||
<CardHeader>
|
||||
<Text>Header</Text>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<ListRow>Hello world</ListRow>
|
||||
<ListRow>Hello world</ListRow>
|
||||
<ListRow>Hello world</ListRow>
|
||||
<ListRow>Hello world</ListRow>
|
||||
...
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Text>Footer</Text>
|
||||
</CardFooter>
|
||||
</Card>`;
|
||||
@@ -0,0 +1,66 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardBody,
|
||||
CardFooter,
|
||||
} from '../../../../../packages/ui/src/components/Card/Card';
|
||||
import { Text } from '../../../../../packages/ui/src/components/Text/Text';
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>Header</CardHeader>
|
||||
<CardBody>Body</CardBody>
|
||||
<CardFooter>Footer</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomSize = () => {
|
||||
return (
|
||||
<Card
|
||||
style={{
|
||||
width: '300px',
|
||||
height: '200px',
|
||||
}}
|
||||
>
|
||||
<CardHeader>Header</CardHeader>
|
||||
<CardBody>Body</CardBody>
|
||||
<CardFooter>Footer</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithLongBody = () => {
|
||||
return (
|
||||
<Card style={{ width: '300px', height: '200px' }}>
|
||||
<CardHeader>
|
||||
<Text>Header</Text>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Text>
|
||||
This is the first paragraph of a long body text that demonstrates how
|
||||
the Card component handles extensive content. The card should adjust
|
||||
accordingly to display all the text properly while maintaining its
|
||||
structure.
|
||||
</Text>
|
||||
<Text>
|
||||
Here's a second paragraph that adds more content to our card
|
||||
body. Having multiple paragraphs helps to visualize how spacing works
|
||||
within the card component.
|
||||
</Text>
|
||||
<Text>
|
||||
This third paragraph continues to add more text to ensure we have a
|
||||
proper demonstration of a card with significant content. This makes it
|
||||
easier to test scrolling behavior and overall layout when content
|
||||
exceeds the initial view.
|
||||
</Text>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Text>Footer</Text>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -1,17 +1,19 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CustomSize, WithLongBody, WithListRow } from './stories';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import {
|
||||
cardPropDefs,
|
||||
cardUsageSnippet,
|
||||
cardDefaultSnippet,
|
||||
cardHeaderPropDefs,
|
||||
cardBodyPropDefs,
|
||||
cardFooterPropDefs,
|
||||
cardLongBodySnippet,
|
||||
cardListRowSnippet,
|
||||
} from './card.props';
|
||||
} from './props-definition';
|
||||
import {
|
||||
cardUsageSnippet,
|
||||
defaultSnippet,
|
||||
customSizeSnippet,
|
||||
withLongBodySnippet,
|
||||
} from './snippets';
|
||||
import { Default, CustomSize, WithLongBody } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { CardDefinition } from '../../../utils/definitions';
|
||||
@@ -25,8 +27,8 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<CustomSize />}
|
||||
code={cardDefaultSnippet}
|
||||
preview={<Default />}
|
||||
code={defaultSnippet}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
@@ -61,6 +63,18 @@ To display a footer in a card, use the `CardFooter` component. This will be fixe
|
||||
|
||||
## Examples
|
||||
|
||||
### Custom size
|
||||
|
||||
Here's a view when card has a custom size.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<CustomSize />}
|
||||
code={customSizeSnippet}
|
||||
open
|
||||
/>
|
||||
|
||||
### With long body
|
||||
|
||||
Here's a view when card has a long body.
|
||||
@@ -69,19 +83,7 @@ Here's a view when card has a long body.
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<WithLongBody />}
|
||||
code={cardLongBodySnippet}
|
||||
open
|
||||
/>
|
||||
|
||||
### With list
|
||||
|
||||
Here's a view when card has a list.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<WithListRow />}
|
||||
code={cardListRowSnippet}
|
||||
code={withLongBodySnippet}
|
||||
open
|
||||
/>
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
childrenPropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const cardPropDefs: Record<string, PropDef> = {
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const cardHeaderPropDefs: Record<string, PropDef> = {
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const cardBodyPropDefs: Record<string, PropDef> = {
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const cardFooterPropDefs: Record<string, PropDef> = {
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
export const cardUsageSnippet = `import { Card, CardHeader, CardBody, CardFooter } from '@backstage/ui';
|
||||
|
||||
<Card>
|
||||
<CardHeader>Header</CardHeader>
|
||||
<CardBody>Body</CardBody>
|
||||
<CardFooter>Footer</CardFooter>
|
||||
</Card>`;
|
||||
|
||||
export const defaultSnippet = `<Card>
|
||||
<CardHeader>Header</CardHeader>
|
||||
<CardBody>Body</CardBody>
|
||||
<CardFooter>Footer</CardFooter>
|
||||
</Card>`;
|
||||
|
||||
export const customSizeSnippet = `<Card style={{ width: '300px', height: '200px' }}>
|
||||
<CardHeader>Header</CardHeader>
|
||||
<CardBody>Body</CardBody>
|
||||
<CardFooter>Footer</CardFooter>
|
||||
</Card>`;
|
||||
|
||||
export const withLongBodySnippet = `<Card style={{ width: '300px', height: '200px' }}>
|
||||
<CardHeader>
|
||||
<Text>Header</Text>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Text>
|
||||
This is the first paragraph of a long body text that demonstrates how
|
||||
the Card component handles extensive content.
|
||||
</Text>
|
||||
<Text>
|
||||
Here's a second paragraph that adds more content to our card body.
|
||||
</Text>
|
||||
<Text>
|
||||
This third paragraph continues to add more text to ensure we have a
|
||||
proper demonstration of a card with significant content.
|
||||
</Text>
|
||||
</CardBody>
|
||||
<CardFooter>
|
||||
<Text>Footer</Text>
|
||||
</CardFooter>
|
||||
</Card>`;
|
||||
@@ -1,13 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Card/Card.stories';
|
||||
|
||||
const {
|
||||
CustomSize: CustomSizeStory,
|
||||
WithLongBody: WithLongBodyStory,
|
||||
WithListRow: WithListRowStory,
|
||||
} = stories;
|
||||
|
||||
export const CustomSize = () => <CustomSizeStory.Component />;
|
||||
export const WithLongBody = () => <WithLongBodyStory.Component />;
|
||||
export const WithListRow = () => <WithListRowStory.Component />;
|
||||
@@ -0,0 +1,21 @@
|
||||
'use client';
|
||||
|
||||
import { Checkbox } from '../../../../../packages/ui/src/components/Checkbox/Checkbox';
|
||||
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
|
||||
|
||||
export const Default = () => {
|
||||
return <Checkbox>Accept terms and conditions</Checkbox>;
|
||||
};
|
||||
|
||||
export const AllVariants = () => {
|
||||
return (
|
||||
<Flex direction="column" gap="2">
|
||||
<Checkbox>Unchecked</Checkbox>
|
||||
<Checkbox isSelected>Checked</Checkbox>
|
||||
<Checkbox isDisabled>Disabled</Checkbox>
|
||||
<Checkbox isSelected isDisabled>
|
||||
Checked & Disabled
|
||||
</Checkbox>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -1,13 +1,13 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Default, AllVariants } from './stories';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { checkboxPropDefs } from './props-definition';
|
||||
import {
|
||||
checkboxPropDefs,
|
||||
checkboxUsageSnippet,
|
||||
checkboxDefaultSnippet,
|
||||
checkboxVariantsSnippet,
|
||||
} from './checkbox.props';
|
||||
defaultSnippet,
|
||||
allVariantsSnippet,
|
||||
} from './snippets';
|
||||
import { Default, AllVariants } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { CheckboxDefinition } from '../../../utils/definitions';
|
||||
@@ -18,12 +18,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="A checkbox component that can be used to trigger actions."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={checkboxDefaultSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -44,7 +39,7 @@ Here's a view when checkboxes have different variants.
|
||||
py={4}
|
||||
open
|
||||
preview={<AllVariants />}
|
||||
code={checkboxVariantsSnippet}
|
||||
code={allVariantsSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={CheckboxDefinition} />
|
||||
|
||||
-13
@@ -46,16 +46,3 @@ export const checkboxPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const checkboxUsageSnippet = `import { Checkbox } from '@backstage/ui';
|
||||
|
||||
<Checkbox>Accept terms</Checkbox>`;
|
||||
|
||||
export const checkboxDefaultSnippet = `<Checkbox>Accept terms and conditions</Checkbox>`;
|
||||
|
||||
export const checkboxVariantsSnippet = `<Flex direction="column" gap="2">
|
||||
<Checkbox>Unchecked</Checkbox>
|
||||
<Checkbox isSelected>Checked</Checkbox>
|
||||
<Checkbox isDisabled>Disabled</Checkbox>
|
||||
<Checkbox isSelected isDisabled>Checked & Disabled</Checkbox>
|
||||
</Flex>`;
|
||||
@@ -0,0 +1,12 @@
|
||||
export const checkboxUsageSnippet = `import { Checkbox } from '@backstage/ui';
|
||||
|
||||
<Checkbox>Accept terms</Checkbox>`;
|
||||
|
||||
export const defaultSnippet = `<Checkbox>Accept terms and conditions</Checkbox>`;
|
||||
|
||||
export const allVariantsSnippet = `<Flex direction="column" gap="2">
|
||||
<Checkbox>Unchecked</Checkbox>
|
||||
<Checkbox isSelected>Checked</Checkbox>
|
||||
<Checkbox isDisabled>Disabled</Checkbox>
|
||||
<Checkbox isSelected isDisabled>Checked & Disabled</Checkbox>
|
||||
</Flex>`;
|
||||
@@ -1,8 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Checkbox/Checkbox.stories';
|
||||
|
||||
const { Default: DefaultStory, AllVariants: AllVariantsStory } = stories;
|
||||
|
||||
export const Default = () => <DefaultStory.Component />;
|
||||
export const AllVariants = () => <AllVariantsStory.Component />;
|
||||
@@ -0,0 +1,24 @@
|
||||
'use client';
|
||||
|
||||
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
|
||||
|
||||
const DecorativeBox = () => (
|
||||
<Box
|
||||
style={{
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid #2563eb',
|
||||
backgroundImage:
|
||||
'url("data:image/svg+xml,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22%232563eb%22%20fill-opacity%3D%220.3%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M5%200h1L0%206V5zM6%205v1H5z%22/%3E%3C/g%3E%3C/svg%3E")',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export const Preview = () => {
|
||||
return (
|
||||
<div style={{ maxWidth: '600px', margin: '0 auto' }}>
|
||||
<DecorativeBox />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,14 +1,14 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Preview } from './stories';
|
||||
import { containerPropDefs } from './props-definition';
|
||||
import {
|
||||
containerPropDefs,
|
||||
containerUsageSnippet,
|
||||
containerDefaultSnippet,
|
||||
previewSnippet,
|
||||
containerSimpleSnippet,
|
||||
containerResponsiveSnippet,
|
||||
} from './container.props';
|
||||
} from './snippets';
|
||||
import { Preview } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ContainerDefinition } from '../../../utils/definitions';
|
||||
@@ -20,11 +20,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="The container component let you use our default max-width and center the content on the page."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<Preview />}
|
||||
code={containerDefaultSnippet}
|
||||
/>
|
||||
<Snippet py={4} preview={<Preview />} code={previewSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
gapPropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const containerPropDefs: Record<string, PropDef> = {
|
||||
...gapPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
+2
-15
@@ -1,23 +1,10 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
gapPropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const containerPropDefs: Record<string, PropDef> = {
|
||||
...gapPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const containerUsageSnippet = `import { Container } from "@backstage/ui";
|
||||
|
||||
<Container>Hello World!</Container>`;
|
||||
|
||||
export const containerDefaultSnippet = `<Container>
|
||||
export const previewSnippet = `<div style={{ maxWidth: '600px', margin: '0 auto' }}>
|
||||
<DecorativeBox />
|
||||
</Container>`;
|
||||
</div>`;
|
||||
|
||||
export const containerSimpleSnippet = `<Container>
|
||||
<Box>Hello World</Box>
|
||||
@@ -1,7 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Container/Container.stories';
|
||||
|
||||
const { Preview: PreviewStory } = stories;
|
||||
|
||||
export const Preview = () => <PreviewStory.Component />;
|
||||
@@ -0,0 +1,104 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
DialogHeader,
|
||||
DialogBody,
|
||||
DialogFooter,
|
||||
} from '../../../../../packages/ui/src/components/Dialog/Dialog';
|
||||
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
|
||||
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
|
||||
import { Text } from '../../../../../packages/ui/src/components/Text/Text';
|
||||
import { TextField } from '../../../../../packages/ui/src/components/TextField/TextField';
|
||||
import { Select } from '../../../../../packages/ui/src/components/Select/Select';
|
||||
|
||||
export const Default = () => (
|
||||
<DialogTrigger>
|
||||
<Button variant="secondary">Open Dialog</Button>
|
||||
<Dialog>
|
||||
<DialogHeader>Example Dialog</DialogHeader>
|
||||
<DialogBody>
|
||||
<Text>This is a basic dialog example.</Text>
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
<Button variant="secondary" slot="close">
|
||||
Close
|
||||
</Button>
|
||||
<Button variant="primary" slot="close">
|
||||
Save
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
</DialogTrigger>
|
||||
);
|
||||
|
||||
export const PreviewFixedWidthAndHeight = () => (
|
||||
<DialogTrigger>
|
||||
<Button variant="secondary">Scrollable Dialog</Button>
|
||||
<Dialog width={600} height={400}>
|
||||
<DialogHeader>Long Content Dialog</DialogHeader>
|
||||
<DialogBody>
|
||||
<Flex direction="column" gap="3">
|
||||
<Text>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
|
||||
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
||||
aliquip ex ea commodo consequat.
|
||||
</Text>
|
||||
<Text>
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse
|
||||
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
||||
cupidatat non proident, sunt in culpa qui officia deserunt mollit
|
||||
anim id est laborum.
|
||||
</Text>
|
||||
<Text>
|
||||
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
|
||||
accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
|
||||
quae ab illo inventore veritatis et quasi architecto beatae vitae
|
||||
dicta sunt explicabo.
|
||||
</Text>
|
||||
</Flex>
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
<Button variant="secondary" slot="close">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" slot="close">
|
||||
Accept
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
</DialogTrigger>
|
||||
);
|
||||
|
||||
export const PreviewWithForm = () => (
|
||||
<DialogTrigger>
|
||||
<Button variant="secondary">Create User</Button>
|
||||
<Dialog>
|
||||
<DialogHeader>Create New User</DialogHeader>
|
||||
<DialogBody>
|
||||
<Flex direction="column" gap="3">
|
||||
<TextField label="Name" placeholder="Enter full name" />
|
||||
<TextField label="Email" placeholder="Enter email address" />
|
||||
<Select
|
||||
label="Role"
|
||||
options={[
|
||||
{ value: 'admin', label: 'Admin' },
|
||||
{ value: 'user', label: 'User' },
|
||||
{ value: 'viewer', label: 'Viewer' },
|
||||
]}
|
||||
/>
|
||||
</Flex>
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
<Button variant="secondary" slot="close">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" slot="close">
|
||||
Create User
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
</DialogTrigger>
|
||||
);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Default, PreviewFixedWidthAndHeight, PreviewWithForm } from './stories';
|
||||
import { Default, PreviewFixedWidthAndHeight, PreviewWithForm } from './components';
|
||||
import {
|
||||
dialogPropDefs,
|
||||
dialogTriggerPropDefs,
|
||||
@@ -9,13 +9,15 @@ import {
|
||||
dialogBodyPropDefs,
|
||||
dialogFooterPropDefs,
|
||||
dialogClosePropDefs,
|
||||
} from './props-definition';
|
||||
import {
|
||||
dialogUsageSnippet,
|
||||
dialogDefaultSnippet,
|
||||
dialogFixedWidthAndHeightSnippet,
|
||||
dialogWithFormSnippet,
|
||||
dialogWithNoTriggerSnippet,
|
||||
dialogCloseSnippet,
|
||||
} from './dialog.props';
|
||||
} from './snippets';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { DialogDefinition } from '../../../utils/definitions';
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const dialogTriggerPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
isOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the overlay is open by default (controlled).',
|
||||
},
|
||||
defaultOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the overlay is open by default (uncontrolled).',
|
||||
},
|
||||
onOpenChange: {
|
||||
type: 'enum',
|
||||
values: ['(isOpen: boolean) => void'],
|
||||
description:
|
||||
"Handler that is called when the overlay's open state changes.",
|
||||
},
|
||||
};
|
||||
|
||||
export const dialogPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
isOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the overlay is open by default (controlled).',
|
||||
},
|
||||
defaultOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the overlay is open by default (uncontrolled).',
|
||||
},
|
||||
onOpenChange: {
|
||||
type: 'enum',
|
||||
values: ['(isOpen: boolean) => void'],
|
||||
description:
|
||||
"Handler that is called when the overlay's open state changes.",
|
||||
},
|
||||
width: {
|
||||
type: 'enum',
|
||||
values: ['number', 'string'],
|
||||
responsive: false,
|
||||
},
|
||||
height: {
|
||||
type: 'enum',
|
||||
values: ['number', 'string'],
|
||||
responsive: false,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const dialogHeaderPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const dialogBodyPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
height: {
|
||||
type: 'enum',
|
||||
values: ['number', 'string'],
|
||||
responsive: false,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const dialogFooterPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const dialogClosePropDefs: Record<string, PropDef> = {
|
||||
variant: {
|
||||
type: 'enum',
|
||||
values: ['primary', 'secondary', 'tertiary'],
|
||||
default: 'secondary',
|
||||
responsive: false,
|
||||
},
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
+2
-91
@@ -1,92 +1,3 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const dialogTriggerPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
isOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the overlay is open by default (controlled).',
|
||||
},
|
||||
defaultOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the overlay is open by default (uncontrolled).',
|
||||
},
|
||||
onOpenChange: {
|
||||
type: 'enum',
|
||||
values: ['(isOpen: boolean) => void'],
|
||||
description:
|
||||
"Handler that is called when the overlay's open state changes.",
|
||||
},
|
||||
};
|
||||
|
||||
export const dialogPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
isOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the overlay is open by default (controlled).',
|
||||
},
|
||||
defaultOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the overlay is open by default (uncontrolled).',
|
||||
},
|
||||
onOpenChange: {
|
||||
type: 'enum',
|
||||
values: ['(isOpen: boolean) => void'],
|
||||
description:
|
||||
"Handler that is called when the overlay's open state changes.",
|
||||
},
|
||||
width: {
|
||||
type: 'enum',
|
||||
values: ['number', 'string'],
|
||||
responsive: false,
|
||||
},
|
||||
height: {
|
||||
type: 'enum',
|
||||
values: ['number', 'string'],
|
||||
responsive: false,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const dialogHeaderPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const dialogBodyPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
height: {
|
||||
type: 'enum',
|
||||
values: ['number', 'string'],
|
||||
responsive: false,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const dialogFooterPropDefs: Record<string, PropDef> = {
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const dialogClosePropDefs: Record<string, PropDef> = {
|
||||
variant: {
|
||||
type: 'enum',
|
||||
values: ['primary', 'secondary', 'tertiary'],
|
||||
default: 'secondary',
|
||||
responsive: false,
|
||||
},
|
||||
children: { type: 'enum', values: ['ReactNode'], responsive: false },
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const dialogUsageSnippet = `import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
@@ -122,9 +33,9 @@ export const dialogDefaultSnippet = `<DialogTrigger>
|
||||
|
||||
export const dialogFixedWidthAndHeightSnippet = `<DialogTrigger>
|
||||
<Button variant="secondary">Scrollable Dialog</Button>
|
||||
<Dialog>
|
||||
<Dialog width={600} height={400}>
|
||||
<DialogHeader>Long Content Dialog</DialogHeader>
|
||||
<DialogBody width={600} height={400}>
|
||||
<DialogBody>
|
||||
...
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
@@ -1,15 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Dialog/Dialog.stories';
|
||||
|
||||
const {
|
||||
Default: DefaultStory,
|
||||
PreviewFixedWidthAndHeight: PreviewFixedWidthAndHeightStory,
|
||||
PreviewWithForm: PreviewWithFormStory,
|
||||
} = stories;
|
||||
|
||||
export const Default = () => <DefaultStory.Component />;
|
||||
export const PreviewFixedWidthAndHeight = () => (
|
||||
<PreviewFixedWidthAndHeightStory.Component />
|
||||
);
|
||||
export const PreviewWithForm = () => <PreviewWithFormStory.Component />;
|
||||
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
|
||||
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<Flex gap="4">
|
||||
<Box
|
||||
style={{
|
||||
width: '64px',
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
border: '1px solid #2563eb',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
style={{
|
||||
width: '64px',
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
border: '1px solid #2563eb',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
style={{
|
||||
width: '64px',
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
border: '1px solid #2563eb',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -1,55 +0,0 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const flexPropDefs: Record<string, PropDef> = {
|
||||
align: {
|
||||
type: 'enum',
|
||||
values: ['start', 'center', 'end', 'baseline', 'stretch'],
|
||||
responsive: true,
|
||||
},
|
||||
direction: {
|
||||
type: 'enum',
|
||||
values: ['row', 'column', 'row-reverse', 'column-reverse'],
|
||||
responsive: true,
|
||||
},
|
||||
justify: {
|
||||
type: 'enum',
|
||||
values: ['start', 'center', 'end', 'between'],
|
||||
responsive: true,
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const flexUsageSnippet = `import { Flex } from '@backstage/ui';
|
||||
|
||||
<Flex />`;
|
||||
|
||||
export const flexDefaultSnippet = `<Flex>
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
</Flex>`;
|
||||
|
||||
export const flexSimpleSnippet = `<Flex gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Flex>`;
|
||||
|
||||
export const flexResponsiveSnippet = `<Flex gap={{ xs: 'xs', md: 'md' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Flex>`;
|
||||
|
||||
export const flexAlignSnippet = `<Flex align={{ xs: 'start', md: 'center' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Flex>`;
|
||||
@@ -1,16 +1,15 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Default } from './stories';
|
||||
import { flexPropDefs } from './props-definition';
|
||||
import {
|
||||
flexPropDefs,
|
||||
flexUsageSnippet,
|
||||
flexDefaultSnippet,
|
||||
flexFAQ1Snippet,
|
||||
defaultSnippet,
|
||||
flexSimpleSnippet,
|
||||
flexResponsiveSnippet,
|
||||
flexAlignSnippet,
|
||||
} from './flex.props';
|
||||
} from './snippets';
|
||||
import { Default } from './components';
|
||||
import { spacingPropDefs } from '@/utils/propDefs';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
@@ -23,12 +22,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="A responsive flex container component for vertical stacking with customizable gaps."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
align="center"
|
||||
preview={<Default />}
|
||||
code={flexDefaultSnippet}
|
||||
/>
|
||||
<Snippet py={4} align="center" preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const flexPropDefs: Record<string, PropDef> = {
|
||||
align: {
|
||||
type: 'enum',
|
||||
values: ['start', 'center', 'end', 'baseline', 'stretch'],
|
||||
responsive: true,
|
||||
},
|
||||
direction: {
|
||||
type: 'enum',
|
||||
values: ['row', 'column', 'row-reverse', 'column-reverse'],
|
||||
responsive: true,
|
||||
},
|
||||
justify: {
|
||||
type: 'enum',
|
||||
values: ['start', 'center', 'end', 'between'],
|
||||
responsive: true,
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
export const flexUsageSnippet = `import { Flex } from '@backstage/ui';
|
||||
|
||||
<Flex />`;
|
||||
|
||||
export const defaultSnippet = `<Flex gap="4">
|
||||
<Box style={{ width: '64px', height: '64px' }} />
|
||||
<Box style={{ width: '64px', height: '64px' }} />
|
||||
<Box style={{ width: '64px', height: '64px' }} />
|
||||
</Flex>`;
|
||||
|
||||
export const flexSimpleSnippet = `<Flex gap="4">
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Flex>`;
|
||||
|
||||
export const flexResponsiveSnippet = `<Flex gap={{ initial: '2', md: '4' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Flex>`;
|
||||
|
||||
export const flexAlignSnippet = `<Flex align={{ initial: 'start', md: 'center' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Flex>`;
|
||||
@@ -1,7 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Flex/Flex.stories';
|
||||
|
||||
const { Default: DefaultStory } = stories;
|
||||
|
||||
export const Default = () => <DefaultStory.Component />;
|
||||
@@ -0,0 +1,59 @@
|
||||
'use client';
|
||||
|
||||
import { Grid } from '../../../../../packages/ui/src/components/Grid/Grid';
|
||||
import { Box } from '../../../../../packages/ui/src/components/Box/Box';
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<Grid.Root columns="3" gap="4">
|
||||
<Box
|
||||
style={{
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
border: '1px solid #2563eb',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
style={{
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
border: '1px solid #2563eb',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
style={{
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
border: '1px solid #2563eb',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
style={{
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
border: '1px solid #2563eb',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
style={{
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
border: '1px solid #2563eb',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
style={{
|
||||
height: '64px',
|
||||
background: '#eaf2fd',
|
||||
border: '1px solid #2563eb',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
</Grid.Root>
|
||||
);
|
||||
};
|
||||
@@ -1,111 +0,0 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
const columnsValues = [
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
'4',
|
||||
'5',
|
||||
'6',
|
||||
'7',
|
||||
'8',
|
||||
'9',
|
||||
'10',
|
||||
'11',
|
||||
'12',
|
||||
];
|
||||
|
||||
export const gridPropDefs: Record<string, PropDef> = {
|
||||
columns: {
|
||||
type: 'enum | string',
|
||||
values: [...columnsValues, 'auto'],
|
||||
responsive: true,
|
||||
default: 'auto',
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const gridItemPropDefs: Record<string, PropDef> = {
|
||||
colSpan: {
|
||||
type: 'enum | string',
|
||||
values: [...columnsValues, 'full'],
|
||||
responsive: true,
|
||||
},
|
||||
rowSpan: {
|
||||
type: 'enum | string',
|
||||
values: [...columnsValues, 'full'],
|
||||
responsive: true,
|
||||
},
|
||||
colStart: {
|
||||
type: 'enum | string',
|
||||
values: [...columnsValues, 'auto'],
|
||||
responsive: true,
|
||||
},
|
||||
colEnd: {
|
||||
type: 'enum | string',
|
||||
values: [...columnsValues, 'auto'],
|
||||
responsive: true,
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const gridUsageSnippet = `import { Grid } from '@backstage/ui';
|
||||
|
||||
<Grid.Root />`;
|
||||
|
||||
export const gridDefaultSnippet = `<Grid.Root>
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
<DecorativeBox />
|
||||
</Grid.Root>`;
|
||||
|
||||
export const gridSimpleSnippet = `<Grid.Root columns="3" gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Grid.Root>`;
|
||||
|
||||
export const gridComplexSnippet = `<Grid.Root columns="3" gap="md">
|
||||
<Grid.Item colSpan="1">
|
||||
Hello World
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan="2">
|
||||
Hello World
|
||||
</Grid.Item>
|
||||
</Grid.Root>`;
|
||||
|
||||
export const gridMixingRowsSnippet = `<Grid.Root columns="3" gap="md">
|
||||
<Grid.Item colSpan="1" rowSpan="2">
|
||||
Hello World
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan="2">
|
||||
Hello World
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan="2">
|
||||
Hello World
|
||||
</Grid.Item>
|
||||
</Grid.Root>`;
|
||||
|
||||
export const gridResponsiveSnippet = `<Grid.Root columns={{ xs: 1, md: 3 }} gap={{ xs: 'xs', md: 'md' }}>
|
||||
<Grid.Item colSpan={{ xs: 1, md: 2 }}>
|
||||
<Hello World
|
||||
</Grid.Item>
|
||||
<Grid.Item colSpan={{ xs: 1, md: 1 }}>
|
||||
Hello World
|
||||
</Grid.Item>
|
||||
</Grid.Root>`;
|
||||
|
||||
export const gridStartEndSnippet = `<Grid.Root columns="3" gap="md">
|
||||
<Grid.Item colStart="2" colEnd="4">
|
||||
Hello World
|
||||
</Grid.Item>
|
||||
</Grid.Root>`;
|
||||
@@ -1,19 +1,10 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { Default } from './stories';
|
||||
import { gridPropDefs } from './props-definition';
|
||||
import { gridUsageSnippet, defaultSnippet } from './snippets';
|
||||
import { Default } from './components';
|
||||
import { spacingPropDefs } from '@/utils/propDefs';
|
||||
import {
|
||||
gridPropDefs,
|
||||
gridItemPropDefs,
|
||||
gridUsageSnippet,
|
||||
gridDefaultSnippet,
|
||||
gridSimpleSnippet,
|
||||
gridComplexSnippet,
|
||||
gridMixingRowsSnippet,
|
||||
gridResponsiveSnippet,
|
||||
gridStartEndSnippet,
|
||||
} from './grid.props';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { GridDefinition } from '../../../utils/definitions';
|
||||
@@ -25,11 +16,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="A layout component that helps to create simple column-based layouts as well as more complex ones."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={gridDefaultSnippet}
|
||||
/>
|
||||
<Snippet py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -49,50 +36,6 @@ The grid component also accepts all the spacing props from the Box component.
|
||||
|
||||
<PropsTable data={spacingPropDefs} />
|
||||
|
||||
### Grid.Item
|
||||
|
||||
If you need more control over the columns, you can use the grid item
|
||||
component. This will give you access to `rowSpan`, `colSpan`, `start` and
|
||||
`end`. All values are responsive. This component is optional, you can use any
|
||||
elements directly if you prefer.
|
||||
|
||||
<PropsTable data={gridItemPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple grid
|
||||
|
||||
A simple grid with 3 columns and a gap of md.
|
||||
|
||||
<CodeBlock code={gridSimpleSnippet} />
|
||||
|
||||
### Complex grid
|
||||
|
||||
You can also use the grid item to create more complex layouts. In this example
|
||||
the first column will span 1 column and the second column will span 2 columns.
|
||||
|
||||
<CodeBlock code={gridComplexSnippet} />
|
||||
|
||||
### Mixing rows and columns
|
||||
|
||||
The grid item component also supports the `rowSpan` prop, which allows you to
|
||||
span multiple rows within the grid layout. In this example, the first item
|
||||
will span 2 rows to achieve a dynamic and flexible grid structure.
|
||||
|
||||
<CodeBlock code={gridMixingRowsSnippet} />
|
||||
|
||||
### Responsive grid
|
||||
|
||||
The grid component also supports responsive values, making it easy to create
|
||||
responsive designs.
|
||||
|
||||
<CodeBlock code={gridResponsiveSnippet} />
|
||||
|
||||
### Start and End
|
||||
|
||||
The start and end props can be used to position the item in the grid.
|
||||
|
||||
<CodeBlock code={gridStartEndSnippet} />
|
||||
|
||||
<Theming definition={GridDefinition} />
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
gapPropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const gridPropDefs: Record<string, PropDef> = {
|
||||
columns: {
|
||||
type: 'string',
|
||||
responsive: true,
|
||||
},
|
||||
rows: {
|
||||
type: 'string',
|
||||
responsive: true,
|
||||
},
|
||||
...gapPropDefs,
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
export const gridUsageSnippet = `import { Grid } from '@backstage/ui';
|
||||
|
||||
<Grid />`;
|
||||
|
||||
export const defaultSnippet = `<Grid.Root columns="3" gap="4">
|
||||
<Box style={{ height: '64px' }} />
|
||||
<Box style={{ height: '64px' }} />
|
||||
<Box style={{ height: '64px' }} />
|
||||
<Box style={{ height: '64px' }} />
|
||||
<Box style={{ height: '64px' }} />
|
||||
<Box style={{ height: '64px' }} />
|
||||
</Grid.Root>`;
|
||||
@@ -1,7 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Grid/Grid.stories';
|
||||
|
||||
const { Default: DefaultStory } = stories;
|
||||
|
||||
export const Default = () => <DefaultStory.Component />;
|
||||
@@ -0,0 +1,107 @@
|
||||
'use client';
|
||||
|
||||
import { HeaderPage } from '../../../../../packages/ui/src/components/HeaderPage/HeaderPage';
|
||||
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
|
||||
import { ButtonIcon } from '../../../../../packages/ui/src/components/ButtonIcon/ButtonIcon';
|
||||
import {
|
||||
MenuTrigger,
|
||||
Menu,
|
||||
MenuItem,
|
||||
} from '../../../../../packages/ui/src/components/Menu/Menu';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RiMore2Line } from '@remixicon/react';
|
||||
|
||||
const tabs = [
|
||||
{ id: 'overview', label: 'Overview', href: '/overview' },
|
||||
{ id: 'checks', label: 'Checks', href: '/checks' },
|
||||
{ id: 'tracks', label: 'Tracks', href: '/tracks' },
|
||||
{ id: 'campaigns', label: 'Campaigns', href: '/campaigns' },
|
||||
{ id: 'integrations', label: 'Integrations', href: '/integrations' },
|
||||
];
|
||||
|
||||
const menuItems = [
|
||||
{ label: 'Settings', value: 'settings', href: '/settings' },
|
||||
{
|
||||
label: 'Invite new members',
|
||||
value: 'invite-new-members',
|
||||
href: '/invite-new-members',
|
||||
},
|
||||
{
|
||||
label: 'Logout',
|
||||
value: 'logout',
|
||||
onClick: () => {
|
||||
alert('logout');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const breadcrumbs = [
|
||||
{ label: 'Home', href: '/' },
|
||||
{ label: 'Long Breadcrumb Name', href: '/long-breadcrumb' },
|
||||
{ label: 'Another Long Breadcrumb', href: '/another-long-breadcrumb' },
|
||||
{
|
||||
label: 'Yet Another Long Breadcrumb',
|
||||
href: '/yet-another-long-breadcrumb',
|
||||
},
|
||||
];
|
||||
|
||||
export const WithEverything = () => (
|
||||
<MemoryRouter>
|
||||
<HeaderPage
|
||||
title="Page Title"
|
||||
tabs={tabs}
|
||||
breadcrumbs={breadcrumbs}
|
||||
customActions={
|
||||
<>
|
||||
<Button variant="secondary">Secondary</Button>
|
||||
<Button variant="primary">Primary</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const WithLongBreadcrumbs = () => (
|
||||
<MemoryRouter>
|
||||
<HeaderPage title="Page Title" breadcrumbs={breadcrumbs} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const WithTabs = () => (
|
||||
<MemoryRouter>
|
||||
<HeaderPage title="Page Title" tabs={tabs} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const WithCustomActions = () => (
|
||||
<MemoryRouter>
|
||||
<HeaderPage
|
||||
title="Page Title"
|
||||
customActions={<Button>Custom action</Button>}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const WithMenuItems = () => (
|
||||
<MemoryRouter>
|
||||
<HeaderPage
|
||||
title="Page Title"
|
||||
customActions={
|
||||
<MenuTrigger>
|
||||
<ButtonIcon variant="tertiary" icon={<RiMore2Line />} />
|
||||
<Menu placement="bottom end">
|
||||
{menuItems.map(option => (
|
||||
<MenuItem
|
||||
key={option.value}
|
||||
onAction={option.onClick}
|
||||
href={option.href}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</MenuTrigger>
|
||||
}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
@@ -1,17 +1,16 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { WithEverything, WithLongBreadcrumbs, WithTabs, WithCustomActions, WithMenuItems } from './stories';
|
||||
import { WithEverything, WithLongBreadcrumbs, WithTabs, WithCustomActions, WithMenuItems } from './components';
|
||||
import { propDefs } from './props-definition';
|
||||
import {
|
||||
propDefs,
|
||||
usage,
|
||||
simple,
|
||||
defaultSnippet,
|
||||
withTabs,
|
||||
withBreadcrumbs,
|
||||
withCustomActions,
|
||||
withMenuItems,
|
||||
} from './header-page.props';
|
||||
} from './snippets';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { HeaderPageDefinition } from '../../../utils/definitions';
|
||||
|
||||
-52
@@ -87,55 +87,3 @@ export const propDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const usage = `import { HeaderPage } from '@backstage/ui';
|
||||
|
||||
<HeaderPage />`;
|
||||
|
||||
export const defaultSnippet = `<HeaderPage
|
||||
title="Page Title"
|
||||
tabs={[
|
||||
{ id: 'overview', label: 'Overview' },
|
||||
{ id: 'checks', label: 'Checks' },
|
||||
{ id: 'tracks', label: 'Tracks' },
|
||||
{ id: 'campaigns', label: 'Campaigns' },
|
||||
{ id: 'integrations', label: 'Integrations' },
|
||||
]}
|
||||
menuItems={[
|
||||
{ label: 'Settings', value: 'settings' },
|
||||
{ label: 'Invite new members', value: 'invite-new-members' },
|
||||
]}
|
||||
customActions={<Button>Custom action</Button>}
|
||||
/>`;
|
||||
|
||||
export const withBreadcrumbs = `<HeaderPage
|
||||
title="Page Title"
|
||||
breadcrumbs={[
|
||||
{ label: 'Home', href: '/' },
|
||||
{ label: 'Long Breadcrumb Name', href: '/long-breadcrumb' },
|
||||
]}
|
||||
/>`;
|
||||
|
||||
export const withTabs = `<HeaderPage
|
||||
title="Page Title"
|
||||
tabs={[
|
||||
{ id: 'overview', label: 'Overview', href: '/overview' },
|
||||
{ id: 'checks', label: 'Checks', href: '/checks' },
|
||||
{ id: 'tracks', label: 'Tracks', href: '/tracks' },
|
||||
{ id: 'campaigns', label: 'Campaigns', href: '/campaigns' },
|
||||
{ id: 'integrations', label: 'Integrations', href: '/integrations' },
|
||||
]}
|
||||
/>`;
|
||||
|
||||
export const withCustomActions = `<HeaderPage
|
||||
title="Page Title"
|
||||
customActions={<Button>Custom action</Button>}
|
||||
/>`;
|
||||
|
||||
export const withMenuItems = `<HeaderPage
|
||||
title="Page Title"
|
||||
menuItems={[
|
||||
{ label: 'Settings', value: 'settings', onClick: () => {} },
|
||||
{ label: 'Invite new members', value: 'invite-new-members', onClick: () => {} },
|
||||
]}
|
||||
/>`;
|
||||
@@ -0,0 +1,51 @@
|
||||
export const usage = `import { HeaderPage } from '@backstage/ui';
|
||||
|
||||
<HeaderPage />`;
|
||||
|
||||
export const defaultSnippet = `<HeaderPage
|
||||
title="Page Title"
|
||||
tabs={[
|
||||
{ id: 'overview', label: 'Overview' },
|
||||
{ id: 'checks', label: 'Checks' },
|
||||
{ id: 'tracks', label: 'Tracks' },
|
||||
{ id: 'campaigns', label: 'Campaigns' },
|
||||
{ id: 'integrations', label: 'Integrations' },
|
||||
]}
|
||||
menuItems={[
|
||||
{ label: 'Settings', value: 'settings' },
|
||||
{ label: 'Invite new members', value: 'invite-new-members' },
|
||||
]}
|
||||
customActions={<Button>Custom action</Button>}
|
||||
/>`;
|
||||
|
||||
export const withBreadcrumbs = `<HeaderPage
|
||||
title="Page Title"
|
||||
breadcrumbs={[
|
||||
{ label: 'Home', href: '/' },
|
||||
{ label: 'Long Breadcrumb Name', href: '/long-breadcrumb' },
|
||||
]}
|
||||
/>`;
|
||||
|
||||
export const withTabs = `<HeaderPage
|
||||
title="Page Title"
|
||||
tabs={[
|
||||
{ id: 'overview', label: 'Overview', href: '/overview' },
|
||||
{ id: 'checks', label: 'Checks', href: '/checks' },
|
||||
{ id: 'tracks', label: 'Tracks', href: '/tracks' },
|
||||
{ id: 'campaigns', label: 'Campaigns', href: '/campaigns' },
|
||||
{ id: 'integrations', label: 'Integrations', href: '/integrations' },
|
||||
]}
|
||||
/>`;
|
||||
|
||||
export const withCustomActions = `<HeaderPage
|
||||
title="Page Title"
|
||||
customActions={<Button>Custom action</Button>}
|
||||
/>`;
|
||||
|
||||
export const withMenuItems = `<HeaderPage
|
||||
title="Page Title"
|
||||
menuItems={[
|
||||
{ label: 'Settings', value: 'settings', onClick: () => {} },
|
||||
{ label: 'Invite new members', value: 'invite-new-members', onClick: () => {} },
|
||||
]}
|
||||
/>`;
|
||||
@@ -1,17 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/HeaderPage/HeaderPage.stories';
|
||||
|
||||
const {
|
||||
WithEverything: WithEverythingStory,
|
||||
WithLongBreadcrumbs: WithLongBreadcrumbsStory,
|
||||
WithTabs: WithTabsStory,
|
||||
WithCustomActions: WithCustomActionsStory,
|
||||
WithMenuItems: WithMenuItemsStory,
|
||||
} = stories;
|
||||
|
||||
export const WithEverything = () => <WithEverythingStory.Component />;
|
||||
export const WithLongBreadcrumbs = () => <WithLongBreadcrumbsStory.Component />;
|
||||
export const WithTabs = () => <WithTabsStory.Component />;
|
||||
export const WithCustomActions = () => <WithCustomActionsStory.Component />;
|
||||
export const WithMenuItems = () => <WithMenuItemsStory.Component />;
|
||||
@@ -0,0 +1,90 @@
|
||||
'use client';
|
||||
|
||||
import { Header } from '../../../../../packages/ui/src/components/Header/Header';
|
||||
import { HeaderPage } from '../../../../../packages/ui/src/components/HeaderPage/HeaderPage';
|
||||
import { ButtonIcon } from '../../../../../packages/ui/src/components/ButtonIcon/ButtonIcon';
|
||||
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import {
|
||||
RiHeartLine,
|
||||
RiEmotionHappyLine,
|
||||
RiCloudy2Line,
|
||||
} from '@remixicon/react';
|
||||
|
||||
const tabs = [
|
||||
{ id: 'overview', label: 'Overview', href: '/overview' },
|
||||
{ id: 'checks', label: 'Checks', href: '/checks' },
|
||||
{ id: 'tracks', label: 'Tracks', href: '/tracks' },
|
||||
{ id: 'campaigns', label: 'Campaigns', href: '/campaigns' },
|
||||
{ id: 'integrations', label: 'Integrations', href: '/integrations' },
|
||||
];
|
||||
|
||||
const tabs2 = [
|
||||
{ id: 'Banana', label: 'Banana', href: '/banana' },
|
||||
{ id: 'Apple', label: 'Apple', href: '/apple' },
|
||||
{ id: 'Orange', label: 'Orange', href: '/orange' },
|
||||
];
|
||||
|
||||
const breadcrumbs = [
|
||||
{ label: 'Home', href: '/' },
|
||||
{ label: 'Dashboard', href: '/dashboard' },
|
||||
{ label: 'Settings', href: '/settings' },
|
||||
];
|
||||
|
||||
export const WithAllOptionsAndTabs = () => (
|
||||
<MemoryRouter>
|
||||
<Header
|
||||
tabs={tabs}
|
||||
customActions={
|
||||
<>
|
||||
<ButtonIcon variant="tertiary" icon={<RiCloudy2Line />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiEmotionHappyLine />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiHeartLine />} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const WithAllOptions = () => (
|
||||
<MemoryRouter>
|
||||
<Header
|
||||
customActions={
|
||||
<>
|
||||
<ButtonIcon variant="tertiary" icon={<RiCloudy2Line />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiEmotionHappyLine />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiHeartLine />} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const WithBreadcrumbs = () => (
|
||||
<MemoryRouter>
|
||||
<Header tabs={tabs} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const WithHeaderPage = () => (
|
||||
<MemoryRouter>
|
||||
<>
|
||||
<Header
|
||||
tabs={tabs}
|
||||
customActions={
|
||||
<>
|
||||
<ButtonIcon variant="tertiary" icon={<RiCloudy2Line />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiEmotionHappyLine />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiHeartLine />} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<HeaderPage
|
||||
title="Page title"
|
||||
tabs={tabs2}
|
||||
customActions={<Button>Custom action</Button>}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
</>
|
||||
</MemoryRouter>
|
||||
);
|
||||
@@ -1,16 +1,16 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { WithAllOptionsAndTabs, WithAllOptions, WithBreadcrumbs, WithHeaderPage } from './stories';
|
||||
import { WithAllOptionsAndTabs, WithAllOptions, WithBreadcrumbs, WithHeaderPage } from './components';
|
||||
import { propDefs } from './props-definition';
|
||||
import {
|
||||
propDefs,
|
||||
usage,
|
||||
simple,
|
||||
defaultSnippet,
|
||||
withTabs,
|
||||
withBreadcrumbs,
|
||||
withHeaderPage,
|
||||
} from './header.props';
|
||||
} from './snippets';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { HeaderDefinition } from '../../../utils/definitions';
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const propDefs: Record<string, PropDef> = {
|
||||
icon: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
default: 'Your plugin',
|
||||
},
|
||||
titleLink: {
|
||||
type: 'string',
|
||||
default: '/',
|
||||
},
|
||||
customActions: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
},
|
||||
menuItems: {
|
||||
type: 'complex',
|
||||
complexType: {
|
||||
name: 'MenuItem[]',
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Display text for the menu item',
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Unique value for the menu item',
|
||||
},
|
||||
onClick: {
|
||||
type: '() => void',
|
||||
required: false,
|
||||
description: 'Callback function when menu item is clicked',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
tabs: {
|
||||
type: 'complex',
|
||||
complexType: {
|
||||
name: 'HeaderTab[]',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Unique identifier for the tab',
|
||||
},
|
||||
label: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Display text for the tab',
|
||||
},
|
||||
href: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'URL to navigate to when tab is clicked',
|
||||
},
|
||||
matchStrategy: {
|
||||
type: "'exact' | 'prefix'",
|
||||
required: false,
|
||||
description: 'How to match the current route to highlight the tab',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
onTabSelectionChange: {
|
||||
type: 'enum',
|
||||
values: ['(key: string) => void'],
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
-84
@@ -1,87 +1,3 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const propDefs: Record<string, PropDef> = {
|
||||
icon: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
default: 'Your plugin',
|
||||
},
|
||||
titleLink: {
|
||||
type: 'string',
|
||||
default: '/',
|
||||
},
|
||||
customActions: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
},
|
||||
menuItems: {
|
||||
type: 'complex',
|
||||
complexType: {
|
||||
name: 'MenuItem[]',
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Display text for the menu item',
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Unique value for the menu item',
|
||||
},
|
||||
onClick: {
|
||||
type: '() => void',
|
||||
required: false,
|
||||
description: 'Callback function when menu item is clicked',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
tabs: {
|
||||
type: 'complex',
|
||||
complexType: {
|
||||
name: 'HeaderTab[]',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Unique identifier for the tab',
|
||||
},
|
||||
label: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Display text for the tab',
|
||||
},
|
||||
href: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'URL to navigate to when tab is clicked',
|
||||
},
|
||||
matchStrategy: {
|
||||
type: "'exact' | 'prefix'",
|
||||
required: false,
|
||||
description: 'How to match the current route to highlight the tab',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
onTabSelectionChange: {
|
||||
type: 'enum',
|
||||
values: ['(key: string) => void'],
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const usage = `import { Header } from '@backstage/ui';
|
||||
|
||||
<Header />`;
|
||||
@@ -1,17 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Header/Header.stories';
|
||||
|
||||
const {
|
||||
WithAllOptionsAndTabs: WithAllOptionsAndTabsStory,
|
||||
WithAllOptions: WithAllOptionsStory,
|
||||
WithBreadcrumbs: WithBreadcrumbsStory,
|
||||
WithHeaderPage: WithHeaderPageStory,
|
||||
} = stories;
|
||||
|
||||
export const WithAllOptionsAndTabs = () => (
|
||||
<WithAllOptionsAndTabsStory.Component />
|
||||
);
|
||||
export const WithAllOptions = () => <WithAllOptionsStory.Component />;
|
||||
export const WithBreadcrumbs = () => <WithBreadcrumbsStory.Component />;
|
||||
export const WithHeaderPage = () => <WithHeaderPageStory.Component />;
|
||||
@@ -0,0 +1,111 @@
|
||||
'use client';
|
||||
|
||||
import { Link } from '../../../../../packages/ui/src/components/Link/Link';
|
||||
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Link href="/">Sign up for Backstage</Link>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExternalLink = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Link href="https://backstage.io" target="_blank">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export const AllVariants = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Flex gap="4" direction="column">
|
||||
<Link href="https://ui.backstage.io" variant="title-large">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="title-medium">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="title-small">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="title-x-small">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="body-large">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="body-medium">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="body-small">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="body-x-small">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
</Flex>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export const AllColors = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Flex gap="4" direction="column">
|
||||
<Link
|
||||
href="https://ui.backstage.io"
|
||||
variant="title-small"
|
||||
color="primary"
|
||||
>
|
||||
I am primary
|
||||
</Link>
|
||||
<Link
|
||||
href="https://ui.backstage.io"
|
||||
variant="title-small"
|
||||
color="secondary"
|
||||
>
|
||||
I am secondary
|
||||
</Link>
|
||||
<Link
|
||||
href="https://ui.backstage.io"
|
||||
variant="title-small"
|
||||
color="tertiary"
|
||||
>
|
||||
I am tertiary
|
||||
</Link>
|
||||
<Link
|
||||
href="https://ui.backstage.io"
|
||||
variant="title-small"
|
||||
color="inherit"
|
||||
>
|
||||
I am inherit
|
||||
</Link>
|
||||
</Flex>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export const Underline = () => {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<Flex gap="4" direction="column">
|
||||
<Link href="https://ui.backstage.io" underline="always">
|
||||
Always underlined
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" underline="hover">
|
||||
Underlined on hover
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" underline="none">
|
||||
Never underlined
|
||||
</Link>
|
||||
</Flex>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const linkPropDefs: Record<string, PropDef> = {
|
||||
href: {
|
||||
type: 'string',
|
||||
},
|
||||
variant: {
|
||||
type: 'enum',
|
||||
values: [
|
||||
'title-large',
|
||||
'title-medium',
|
||||
'title-small',
|
||||
'title-x-small',
|
||||
'body-large',
|
||||
'body-medium',
|
||||
'body-small',
|
||||
'body-x-small',
|
||||
],
|
||||
default: 'body-medium',
|
||||
responsive: true,
|
||||
},
|
||||
weight: {
|
||||
type: 'enum',
|
||||
values: ['regular', 'bold'],
|
||||
default: 'regular',
|
||||
responsive: true,
|
||||
},
|
||||
color: {
|
||||
type: 'enum',
|
||||
values: ['primary', 'secondary', 'danger', 'warning', 'success'],
|
||||
default: 'primary',
|
||||
responsive: true,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const linkUsageSnippet = `import { Link } from '@backstage/ui';
|
||||
|
||||
<Link href="/sign-up">Sign up for Backstage</Link>`;
|
||||
|
||||
export const linkDefaultSnippet = `<Link href="/">Sign up for Backstage</Link>`;
|
||||
|
||||
export const linkVariantsSnippet = `<Flex gap="4" direction="column">
|
||||
<Link href="/" variant="title-large">...</Link>
|
||||
<Link href="/" variant="title-medium">...</Link>
|
||||
<Link href="/" variant="title-small">...</Link>
|
||||
<Link href="/" variant="title-x-small">...</Link>
|
||||
<Link href="/" variant="body-large">...</Link>
|
||||
<Link href="/" variant="body-medium">...</Link>
|
||||
<Link href="/" variant="body-small">...</Link>
|
||||
<Link href="/" variant="body-x-small">...</Link>
|
||||
</Flex>`;
|
||||
|
||||
export const linkWeightsSnippet = `<Flex gap="4" direction="column">
|
||||
<Link href="/" weight="regular" />
|
||||
<Link href="/" weight="bold" />
|
||||
</Flex>`;
|
||||
|
||||
export const linkColorsSnippet = `<Flex gap="4" direction="column">
|
||||
<Link href="/" color="primary">I am primary</Link>
|
||||
<Link href="/" color="secondary">I am secondary</Link>
|
||||
<Link href="/" color="danger">I am danger</Link>
|
||||
<Link href="/" color="warning">I am warning</Link>
|
||||
<Link href="/" color="success">I am success</Link>
|
||||
</Flex>`;
|
||||
|
||||
export const linkRouterSnippet = `import { Link } from '@backstage/ui';
|
||||
|
||||
// Internal route
|
||||
<Link href="/home">Home</Link>
|
||||
|
||||
// External URL
|
||||
<Link href="https://backstage.io">Backstage</Link>
|
||||
`;
|
||||
|
||||
export const linkTruncateSnippet = `<Link href="/" truncate>...</Link>`;
|
||||
@@ -1,17 +1,16 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Default, AllVariants, AllWeights, AllColors, Truncate } from './stories';
|
||||
import { linkPropDefs } from './props-definition';
|
||||
import {
|
||||
linkPropDefs,
|
||||
linkUsageSnippet,
|
||||
linkDefaultSnippet,
|
||||
linkVariantsSnippet,
|
||||
linkWeightsSnippet,
|
||||
linkColorsSnippet,
|
||||
linkRouterSnippet,
|
||||
linkTruncateSnippet,
|
||||
} from './link.props';
|
||||
defaultSnippet,
|
||||
externalLinkSnippet,
|
||||
allVariantsSnippet,
|
||||
allColorsSnippet,
|
||||
underlineSnippet,
|
||||
} from './snippets';
|
||||
import { Default, ExternalLink, AllVariants, AllColors, Underline } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
@@ -22,12 +21,7 @@ import { LinkDefinition } from '../../../utils/definitions';
|
||||
description="A link component that renders a `<a>` element."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={linkDefaultSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -37,17 +31,20 @@ import { LinkDefinition } from '../../../utils/definitions';
|
||||
|
||||
<PropsTable data={linkPropDefs} />
|
||||
|
||||
## Router Integration
|
||||
|
||||
The `Link` component handles both internal and external navigation. It automatically detects whether the provided URL is internal (relative path) or external (absolute URL with protocol) and renders the appropriate element:
|
||||
|
||||
- **Internal routes**: Uses `react-router-dom`'s `Link` component for client-side navigation
|
||||
- **External URLs**: Renders a standard `<a>` element for traditional navigation
|
||||
|
||||
<CodeBlock language="tsx" code={linkRouterSnippet} />
|
||||
|
||||
## Examples
|
||||
|
||||
### External Link
|
||||
|
||||
Here's how to create an external link that opens in a new tab.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ExternalLink />}
|
||||
code={externalLinkSnippet}
|
||||
/>
|
||||
|
||||
### Variants
|
||||
|
||||
Here's a view when links have different variants.
|
||||
@@ -57,19 +54,7 @@ Here's a view when links have different variants.
|
||||
py={4}
|
||||
open
|
||||
preview={<AllVariants />}
|
||||
code={linkVariantsSnippet}
|
||||
/>
|
||||
|
||||
### Weights
|
||||
|
||||
Here's a view when links have different weights.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<AllWeights />}
|
||||
code={linkWeightsSnippet}
|
||||
code={allVariantsSnippet}
|
||||
/>
|
||||
|
||||
### Colors
|
||||
@@ -81,17 +66,19 @@ Here's a view when links have different colors.
|
||||
py={4}
|
||||
open
|
||||
preview={<AllColors />}
|
||||
code={linkColorsSnippet}
|
||||
code={allColorsSnippet}
|
||||
/>
|
||||
|
||||
### Truncate
|
||||
### Underline
|
||||
|
||||
The `Link` component has a `truncate` prop that can be used to truncate the text.
|
||||
Here's a view when links have different underline styles.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<Truncate />}
|
||||
code={linkTruncateSnippet}
|
||||
preview={<Underline />}
|
||||
code={underlineSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={LinkDefinition} />
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const linkPropDefs: Record<string, PropDef> = {
|
||||
href: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
target: {
|
||||
type: 'enum',
|
||||
values: ['_self', '_blank', '_parent', '_top'],
|
||||
},
|
||||
variant: {
|
||||
type: 'enum',
|
||||
values: [
|
||||
'title-large',
|
||||
'title-medium',
|
||||
'title-small',
|
||||
'title-x-small',
|
||||
'body-large',
|
||||
'body-medium',
|
||||
'body-small',
|
||||
'body-x-small',
|
||||
],
|
||||
default: 'body-medium',
|
||||
responsive: true,
|
||||
},
|
||||
color: {
|
||||
type: 'enum',
|
||||
values: ['primary', 'secondary', 'tertiary', 'inherit'],
|
||||
default: 'primary',
|
||||
},
|
||||
underline: {
|
||||
type: 'enum',
|
||||
values: ['always', 'hover', 'none'],
|
||||
default: 'hover',
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
export const linkUsageSnippet = `import { Link } from '@backstage/ui';
|
||||
|
||||
<Link href="/">Sign up for Backstage</Link>`;
|
||||
|
||||
export const defaultSnippet = `<Link href="/">Sign up for Backstage</Link>`;
|
||||
|
||||
export const externalLinkSnippet = `<Link href="https://backstage.io" target="_blank">
|
||||
Sign up for Backstage
|
||||
</Link>`;
|
||||
|
||||
export const allVariantsSnippet = `<Flex gap="4" direction="column">
|
||||
<Link href="https://ui.backstage.io" variant="title-large">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="title-medium">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="title-small">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="body-large">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="body-medium">
|
||||
Sign up for Backstage
|
||||
</Link>
|
||||
</Flex>`;
|
||||
|
||||
export const allColorsSnippet = `<Flex gap="4" direction="column">
|
||||
<Link href="https://ui.backstage.io" variant="title-small" color="primary">
|
||||
I am primary
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="title-small" color="secondary">
|
||||
I am secondary
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="title-small" color="tertiary">
|
||||
I am tertiary
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" variant="title-small" color="inherit">
|
||||
I am inherit
|
||||
</Link>
|
||||
</Flex>`;
|
||||
|
||||
export const underlineSnippet = `<Flex gap="4" direction="column">
|
||||
<Link href="https://ui.backstage.io" underline="always">
|
||||
Always underlined
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" underline="hover">
|
||||
Underlined on hover
|
||||
</Link>
|
||||
<Link href="https://ui.backstage.io" underline="none">
|
||||
Never underlined
|
||||
</Link>
|
||||
</Flex>`;
|
||||
@@ -1,17 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Link/Link.stories';
|
||||
|
||||
const {
|
||||
Default: DefaultStory,
|
||||
AllVariants: AllVariantsStory,
|
||||
AllWeights: AllWeightsStory,
|
||||
AllColors: AllColorsStory,
|
||||
Truncate: TruncateStory,
|
||||
} = stories;
|
||||
|
||||
export const Default = () => <DefaultStory.Component />;
|
||||
export const AllVariants = () => <AllVariantsStory.Component />;
|
||||
export const AllWeights = () => <AllWeightsStory.Component />;
|
||||
export const AllColors = () => <AllColorsStory.Component />;
|
||||
export const Truncate = () => <TruncateStory.Component />;
|
||||
@@ -0,0 +1,143 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
MenuTrigger,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuSection,
|
||||
MenuSeparator,
|
||||
SubmenuTrigger,
|
||||
MenuAutocomplete,
|
||||
MenuListBoxItem,
|
||||
} from '../../../../../packages/ui/src/components/Menu/Menu';
|
||||
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { RiFileLine, RiFolderLine, RiImageLine } from '@remixicon/react';
|
||||
|
||||
export const Preview = () => (
|
||||
<MemoryRouter>
|
||||
<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem>New File</MenuItem>
|
||||
<MenuItem>Open File</MenuItem>
|
||||
<MenuItem>Save</MenuItem>
|
||||
<MenuItem>Save As...</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const PreviewSubmenu = () => (
|
||||
<MemoryRouter>
|
||||
<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem>New File</MenuItem>
|
||||
<SubmenuTrigger>
|
||||
<MenuItem>Open Recent</MenuItem>
|
||||
<Menu>
|
||||
<MenuItem>File 1.txt</MenuItem>
|
||||
<MenuItem>File 2.txt</MenuItem>
|
||||
<MenuItem>File 3.txt</MenuItem>
|
||||
</Menu>
|
||||
</SubmenuTrigger>
|
||||
<MenuItem>Save</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const PreviewIcons = () => (
|
||||
<MemoryRouter>
|
||||
<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem icon={<RiFileLine />}>New File</MenuItem>
|
||||
<MenuItem icon={<RiFolderLine />}>New Folder</MenuItem>
|
||||
<MenuItem icon={<RiImageLine />}>New Image</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const PreviewLinks = () => (
|
||||
<MemoryRouter>
|
||||
<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem href="/home">Home</MenuItem>
|
||||
<MenuItem href="/about">About</MenuItem>
|
||||
<MenuItem href="/contact">Contact</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const PreviewSections = () => (
|
||||
<MemoryRouter>
|
||||
<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuSection title="File">
|
||||
<MenuItem>New</MenuItem>
|
||||
<MenuItem>Open</MenuItem>
|
||||
</MenuSection>
|
||||
<MenuSection title="Edit">
|
||||
<MenuItem>Cut</MenuItem>
|
||||
<MenuItem>Copy</MenuItem>
|
||||
<MenuItem>Paste</MenuItem>
|
||||
</MenuSection>
|
||||
</Menu>
|
||||
</MenuTrigger>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const PreviewSeparators = () => (
|
||||
<MemoryRouter>
|
||||
<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem>New</MenuItem>
|
||||
<MenuItem>Open</MenuItem>
|
||||
<MenuSeparator />
|
||||
<MenuItem>Save</MenuItem>
|
||||
<MenuItem>Save As...</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const PreviewAutocompleteMenu = () => (
|
||||
<MemoryRouter>
|
||||
<MenuAutocomplete label="Search" placeholder="Type to search...">
|
||||
<MenuItem>Option 1</MenuItem>
|
||||
<MenuItem>Option 2</MenuItem>
|
||||
<MenuItem>Option 3</MenuItem>
|
||||
</MenuAutocomplete>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const PreviewAutocompleteListbox = () => (
|
||||
<MemoryRouter>
|
||||
<MenuAutocomplete label="Select an option" placeholder="Type to filter...">
|
||||
<MenuListBoxItem>Option 1</MenuListBoxItem>
|
||||
<MenuListBoxItem>Option 2</MenuListBoxItem>
|
||||
<MenuListBoxItem>Option 3</MenuListBoxItem>
|
||||
</MenuAutocomplete>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
export const PreviewAutocompleteListboxMultiple = () => (
|
||||
<MemoryRouter>
|
||||
<MenuAutocomplete
|
||||
label="Select multiple options"
|
||||
placeholder="Type to filter..."
|
||||
selectionMode="multiple"
|
||||
>
|
||||
<MenuListBoxItem>Option 1</MenuListBoxItem>
|
||||
<MenuListBoxItem>Option 2</MenuListBoxItem>
|
||||
<MenuListBoxItem>Option 3</MenuListBoxItem>
|
||||
</MenuAutocomplete>
|
||||
</MemoryRouter>
|
||||
);
|
||||
@@ -1,372 +0,0 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
const placementValues = [
|
||||
'bottom',
|
||||
'bottom left',
|
||||
'bottom right',
|
||||
'bottom start',
|
||||
'bottom end',
|
||||
'top',
|
||||
'top left',
|
||||
'top right',
|
||||
'top start',
|
||||
'top end',
|
||||
'left',
|
||||
'left top',
|
||||
'left bottom',
|
||||
'start',
|
||||
'start top',
|
||||
'start bottom',
|
||||
'right',
|
||||
'right top',
|
||||
'right bottom',
|
||||
'end',
|
||||
'end top',
|
||||
'end bottom',
|
||||
];
|
||||
|
||||
export const menuTriggerPropDefs: Record<string, PropDef> = {
|
||||
isOpen: {
|
||||
type: 'boolean',
|
||||
},
|
||||
defaultOpen: {
|
||||
type: 'boolean',
|
||||
},
|
||||
onOpenChange: {
|
||||
type: 'enum',
|
||||
values: ['(isOpen: boolean) => void'],
|
||||
},
|
||||
};
|
||||
|
||||
export const submenuTriggerPropDefs: Record<string, PropDef> = {
|
||||
delay: {
|
||||
type: 'number',
|
||||
default: '200',
|
||||
},
|
||||
};
|
||||
|
||||
export const menuPropDefs: Record<string, PropDef> = {
|
||||
disabledKeys: {
|
||||
type: 'enum',
|
||||
values: ['Iterable<Key>'],
|
||||
},
|
||||
selectionMode: {
|
||||
type: 'enum',
|
||||
values: ['none', 'single', 'multiple'],
|
||||
},
|
||||
selectedKeys: {
|
||||
type: 'enum',
|
||||
values: ['all', 'Iterable<Key>'],
|
||||
},
|
||||
defaultSelectedKeys: {
|
||||
type: 'enum',
|
||||
values: ['all', 'Iterable<Key>'],
|
||||
},
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: placementValues,
|
||||
},
|
||||
virtualized: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
maxWidth: {
|
||||
type: 'number',
|
||||
},
|
||||
maxHeight: {
|
||||
type: 'number',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuListBoxPropDefs: Record<string, PropDef> = {
|
||||
disabledKeys: {
|
||||
type: 'enum',
|
||||
values: ['Iterable<Key>'],
|
||||
},
|
||||
selectionMode: {
|
||||
type: 'enum',
|
||||
values: ['none', 'single', 'multiple'],
|
||||
},
|
||||
selectedKeys: {
|
||||
type: 'enum',
|
||||
values: ['all', 'Iterable<Key>'],
|
||||
},
|
||||
defaultSelectedKeys: {
|
||||
type: 'enum',
|
||||
values: ['all', 'Iterable<Key>'],
|
||||
},
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: placementValues,
|
||||
},
|
||||
virtualized: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
maxWidth: {
|
||||
type: 'number',
|
||||
},
|
||||
maxHeight: {
|
||||
type: 'number',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuAutocompletePropDefs: Record<string, PropDef> = {
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: placementValues,
|
||||
},
|
||||
virtualized: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
maxWidth: {
|
||||
type: 'number',
|
||||
},
|
||||
maxHeight: {
|
||||
type: 'number',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuAutocompleteListboxPropDefs: Record<string, PropDef> = {
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: placementValues,
|
||||
},
|
||||
virtualized: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
maxWidth: {
|
||||
type: 'number',
|
||||
},
|
||||
maxHeight: {
|
||||
type: 'number',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuItemPropDefs: Record<string, PropDef> = {
|
||||
id: {
|
||||
type: 'enum',
|
||||
values: ['Key'],
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
},
|
||||
textValue: {
|
||||
type: 'string',
|
||||
},
|
||||
isDisabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
href: {
|
||||
type: 'string',
|
||||
},
|
||||
onAction: {
|
||||
type: 'enum',
|
||||
values: ['(event) => void'],
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuListBoxItemPropDefs: Record<string, PropDef> = {
|
||||
id: {
|
||||
type: 'enum',
|
||||
values: ['Key'],
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
},
|
||||
textValue: {
|
||||
type: 'string',
|
||||
},
|
||||
isDisabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuSectionPropDefs: Record<string, PropDef> = {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuSeparatorPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const usage = `import { MenuTrigger, Menu, MenuItem } from '@backstage/ui';
|
||||
|
||||
<MenuTrigger>
|
||||
<Button>Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem id="apple">Apple</MenuItem>
|
||||
<MenuItem id="banana">Banana</MenuItem>
|
||||
<MenuItem id="blueberry">Blueberry</MenuItem>
|
||||
<MenuSeparator />
|
||||
<SubmenuTrigger>
|
||||
<MenuItem>Vegetables</MenuItem>
|
||||
<Menu>
|
||||
<MenuItem id="carrot">Carrot</MenuItem>
|
||||
<MenuItem id="tomato">Tomato</MenuItem>
|
||||
<MenuItem id="potato">Potato</MenuItem>
|
||||
</Menu>
|
||||
</SubmenuTrigger>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const preview = `<MenuTrigger>
|
||||
<Button>Menu</Button>
|
||||
<Menu>
|
||||
{options.map(option => (
|
||||
<MenuItem key={option.value}>{option.label}</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const submenu = `<MenuTrigger>
|
||||
<Button aria-label="Menu">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem>Edit</MenuItem>
|
||||
<MenuItem>Duplicate</MenuItem>
|
||||
<SubmenuTrigger>
|
||||
<MenuItem>Submenu</MenuItem>
|
||||
<Menu placement="right top">
|
||||
<MenuItem>Edit</MenuItem>
|
||||
<MenuItem>Duplicate</MenuItem>
|
||||
<MenuItem>Rename</MenuItem>
|
||||
<MenuSeparator />
|
||||
<MenuItem>Share</MenuItem>
|
||||
<MenuItem>Move</MenuItem>
|
||||
<MenuSeparator />
|
||||
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
|
||||
</Menu>
|
||||
</SubmenuTrigger>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const icons = `<MenuTrigger>
|
||||
<Button aria-label="Menu">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem iconStart={<RiFileCopyLine />}>Copy</MenuItem>
|
||||
<MenuItem iconStart={<RiEdit2Line />}>Rename</MenuItem>
|
||||
<MenuItem iconStart={<RiChat1Line />}>Send feedback</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const sections = `<MenuTrigger>
|
||||
<Button aria-label="Menu">Menu</Button>
|
||||
<Menu>
|
||||
<MenuSection title="My Account">
|
||||
<MenuItem iconStart={<RiUserLine />}>Profile</MenuItem>
|
||||
<MenuItem iconStart={<RiSettingsLine />}>Settings</MenuItem>
|
||||
</MenuSection>
|
||||
<MenuSection title="Support">
|
||||
<MenuItem iconStart={<RiQuestionLine />}>Help Center</MenuItem>
|
||||
<MenuItem iconStart={<RiCustomerService2Line />}>
|
||||
Contact Support
|
||||
</MenuItem>
|
||||
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
|
||||
</MenuSection>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const separators = `<MenuTrigger>
|
||||
<Button aria-label="Menu">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem>Edit</MenuItem>
|
||||
<MenuItem>Duplicate</MenuItem>
|
||||
<MenuItem>Rename</MenuItem>
|
||||
<MenuSeparator />
|
||||
<MenuItem>Share</MenuItem>
|
||||
<MenuItem>Move</MenuItem>
|
||||
<MenuSeparator />
|
||||
<MenuItem iconStart={<RiChat1Line />}>Feedback</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const links = `<MenuTrigger>
|
||||
<Button aria-label="Menu">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem href="/home">Internal link</MenuItem>
|
||||
<MenuItem href="https://www.google.com" target="_blank">
|
||||
External link
|
||||
</MenuItem>
|
||||
<MenuItem href="mailto:test@test.com">Email link</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const autocomplete = `<MenuTrigger isOpen>
|
||||
<Button aria-label="Menu">Menu</Button>
|
||||
<MenuAutocomplete placeholder="Filter">
|
||||
<MenuItem>Create new file...</MenuItem>
|
||||
<MenuItem>Create new folder...</MenuItem>
|
||||
<MenuItem>Assign to...</MenuItem>
|
||||
<MenuItem>Assign to me</MenuItem>
|
||||
<MenuItem>Change status...</MenuItem>
|
||||
<MenuItem>Change priority...</MenuItem>
|
||||
<MenuItem>Add label...</MenuItem>
|
||||
<MenuItem>Remove label...</MenuItem>
|
||||
</MenuAutocomplete>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const autocompleteListbox = `const [selected, setSelected] = useState<Selection>(
|
||||
new Set(['blueberry']),
|
||||
);
|
||||
|
||||
<Flex direction="column" gap="2" align="start">
|
||||
<Text>Selected: {Array.from(selected).join(', ')}</Text>
|
||||
<MenuTrigger>
|
||||
<Button aria-label="Menu">Menu</Button>
|
||||
<MenuAutocompleteListbox
|
||||
selectedKeys={selected}
|
||||
onSelectionChange={setSelected}
|
||||
>
|
||||
{options.map(option => (
|
||||
<MenuListBoxItem key={option.value} id={option.value}>
|
||||
{option.label}
|
||||
</MenuListBoxItem>
|
||||
))}
|
||||
</MenuAutocompleteListbox>
|
||||
</MenuTrigger>
|
||||
</Flex>`;
|
||||
|
||||
export const autocompleteListboxMultiple = `const [selected, setSelected] = useState<Selection>(
|
||||
new Set(['blueberry', 'cherry']),
|
||||
);
|
||||
|
||||
<Flex direction="column" gap="2" align="start">
|
||||
<Text>Selected: {Array.from(selected).join(', ')}</Text>
|
||||
<MenuTrigger>
|
||||
<Button aria-label="Menu">Menu</Button>
|
||||
<MenuAutocompleteListbox
|
||||
selectionMode="multiple"
|
||||
selectedKeys={selected}
|
||||
onSelectionChange={setSelected}
|
||||
>
|
||||
{options.map(option => (
|
||||
<MenuListBoxItem key={option.value} id={option.value}>
|
||||
{option.label}
|
||||
</MenuListBoxItem>
|
||||
))}
|
||||
</MenuAutocompleteListbox>
|
||||
</MenuTrigger>
|
||||
</Flex>`;
|
||||
@@ -1,7 +1,19 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Preview, PreviewSubmenu, PreviewIcons, PreviewLinks, PreviewSections, PreviewSeparators, PreviewAutocompleteMenu, PreviewAutocompleteListbox, PreviewAutocompleteListboxMultiple } from './stories';
|
||||
import { Preview, PreviewSubmenu, PreviewIcons, PreviewLinks, PreviewSections, PreviewSeparators, PreviewAutocompleteMenu, PreviewAutocompleteListbox, PreviewAutocompleteListboxMultiple } from './components';
|
||||
import {
|
||||
menuTriggerPropDefs,
|
||||
submenuTriggerPropDefs,
|
||||
menuPropDefs,
|
||||
menuListBoxPropDefs,
|
||||
menuAutocompletePropDefs,
|
||||
menuAutocompleteListboxPropDefs,
|
||||
menuItemPropDefs,
|
||||
menuListBoxItemPropDefs,
|
||||
menuSectionPropDefs,
|
||||
menuSeparatorPropDefs,
|
||||
} from './props-definition';
|
||||
import {
|
||||
usage,
|
||||
preview,
|
||||
@@ -13,17 +25,7 @@ import {
|
||||
autocomplete,
|
||||
autocompleteListbox,
|
||||
autocompleteListboxMultiple,
|
||||
menuTriggerPropDefs,
|
||||
submenuTriggerPropDefs,
|
||||
menuPropDefs,
|
||||
menuListBoxPropDefs,
|
||||
menuAutocompletePropDefs,
|
||||
menuAutocompleteListboxPropDefs,
|
||||
menuItemPropDefs,
|
||||
menuListBoxItemPropDefs,
|
||||
menuSectionPropDefs,
|
||||
menuSeparatorPropDefs,
|
||||
} from './menu.props';
|
||||
} from './snippets';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const menuTriggerPropDefs: Record<string, PropDef> = {
|
||||
isOpen: {
|
||||
type: 'boolean',
|
||||
},
|
||||
defaultOpen: {
|
||||
type: 'boolean',
|
||||
},
|
||||
onOpenChange: {
|
||||
type: 'enum',
|
||||
values: ['(isOpen: boolean) => void'],
|
||||
},
|
||||
};
|
||||
|
||||
export const submenuTriggerPropDefs: Record<string, PropDef> = {
|
||||
delay: {
|
||||
type: 'number',
|
||||
default: '200',
|
||||
},
|
||||
};
|
||||
|
||||
export const menuPropDefs: Record<string, PropDef> = {
|
||||
disabledKeys: {
|
||||
type: 'enum',
|
||||
values: ['Iterable<Key>'],
|
||||
},
|
||||
selectionMode: {
|
||||
type: 'enum',
|
||||
values: ['none', 'single', 'multiple'],
|
||||
},
|
||||
selectedKeys: {
|
||||
type: 'enum',
|
||||
values: ['all', 'Iterable<Key>'],
|
||||
},
|
||||
defaultSelectedKeys: {
|
||||
type: 'enum',
|
||||
values: ['all', 'Iterable<Key>'],
|
||||
},
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: [
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
'right',
|
||||
'top start',
|
||||
'top end',
|
||||
'bottom start',
|
||||
'bottom end',
|
||||
'left start',
|
||||
'left end',
|
||||
'right start',
|
||||
'right end',
|
||||
],
|
||||
},
|
||||
virtualized: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
maxWidth: {
|
||||
type: 'number',
|
||||
},
|
||||
maxHeight: {
|
||||
type: 'number',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuListBoxPropDefs: Record<string, PropDef> = {
|
||||
disabledKeys: {
|
||||
type: 'enum',
|
||||
values: ['Iterable<Key>'],
|
||||
},
|
||||
selectionMode: {
|
||||
type: 'enum',
|
||||
values: ['none', 'single', 'multiple'],
|
||||
},
|
||||
selectedKeys: {
|
||||
type: 'enum',
|
||||
values: ['all', 'Iterable<Key>'],
|
||||
},
|
||||
defaultSelectedKeys: {
|
||||
type: 'enum',
|
||||
values: ['all', 'Iterable<Key>'],
|
||||
},
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: [
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
'right',
|
||||
'top start',
|
||||
'top end',
|
||||
'bottom start',
|
||||
'bottom end',
|
||||
'left start',
|
||||
'left end',
|
||||
'right start',
|
||||
'right end',
|
||||
],
|
||||
},
|
||||
virtualized: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
maxWidth: {
|
||||
type: 'number',
|
||||
},
|
||||
maxHeight: {
|
||||
type: 'number',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuAutocompletePropDefs: Record<string, PropDef> = {
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: [
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
'right',
|
||||
'top start',
|
||||
'top end',
|
||||
'bottom start',
|
||||
'bottom end',
|
||||
'left start',
|
||||
'left end',
|
||||
'right start',
|
||||
'right end',
|
||||
],
|
||||
},
|
||||
virtualized: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
maxWidth: {
|
||||
type: 'number',
|
||||
},
|
||||
maxHeight: {
|
||||
type: 'number',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuAutocompleteListboxPropDefs: Record<string, PropDef> = {
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: [
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
'right',
|
||||
'top start',
|
||||
'top end',
|
||||
'bottom start',
|
||||
'bottom end',
|
||||
'left start',
|
||||
'left end',
|
||||
'right start',
|
||||
'right end',
|
||||
],
|
||||
},
|
||||
virtualized: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
},
|
||||
maxWidth: {
|
||||
type: 'number',
|
||||
},
|
||||
maxHeight: {
|
||||
type: 'number',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuItemPropDefs: Record<string, PropDef> = {
|
||||
id: {
|
||||
type: 'enum',
|
||||
values: ['Key'],
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
},
|
||||
textValue: {
|
||||
type: 'string',
|
||||
},
|
||||
isDisabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
href: {
|
||||
type: 'string',
|
||||
},
|
||||
onAction: {
|
||||
type: 'enum',
|
||||
values: ['(event) => void'],
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuListBoxItemPropDefs: Record<string, PropDef> = {
|
||||
id: {
|
||||
type: 'enum',
|
||||
values: ['Key'],
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
},
|
||||
textValue: {
|
||||
type: 'string',
|
||||
},
|
||||
isDisabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuSectionPropDefs: Record<string, PropDef> = {
|
||||
title: {
|
||||
type: 'string',
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const menuSeparatorPropDefs: Record<string, PropDef> = {
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
export const usage = `import { MenuTrigger, Menu, MenuItem } from '@backstage/ui';
|
||||
|
||||
<MenuTrigger>
|
||||
<Button>Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem>Item 1</MenuItem>
|
||||
<MenuItem>Item 2</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const preview = `<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem>New File</MenuItem>
|
||||
<MenuItem>Open File</MenuItem>
|
||||
<MenuItem>Save</MenuItem>
|
||||
<MenuItem>Save As...</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const submenu = `<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem>New File</MenuItem>
|
||||
<SubmenuTrigger>
|
||||
<MenuItem>Open Recent</MenuItem>
|
||||
<Menu>
|
||||
<MenuItem>File 1.txt</MenuItem>
|
||||
<MenuItem>File 2.txt</MenuItem>
|
||||
</Menu>
|
||||
</SubmenuTrigger>
|
||||
<MenuItem>Save</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const icons = `<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem icon={<RiFileLine />}>New File</MenuItem>
|
||||
<MenuItem icon={<RiFolderLine />}>New Folder</MenuItem>
|
||||
<MenuItem icon={<RiImageLine />}>New Image</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const sections = `<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuSection title="File">
|
||||
<MenuItem>New</MenuItem>
|
||||
<MenuItem>Open</MenuItem>
|
||||
</MenuSection>
|
||||
<MenuSection title="Edit">
|
||||
<MenuItem>Cut</MenuItem>
|
||||
<MenuItem>Copy</MenuItem>
|
||||
<MenuItem>Paste</MenuItem>
|
||||
</MenuSection>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const separators = `<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem>New</MenuItem>
|
||||
<MenuItem>Open</MenuItem>
|
||||
<MenuSeparator />
|
||||
<MenuItem>Save</MenuItem>
|
||||
<MenuItem>Save As...</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const links = `<MenuTrigger>
|
||||
<Button variant="secondary">Menu</Button>
|
||||
<Menu>
|
||||
<MenuItem href="/home">Home</MenuItem>
|
||||
<MenuItem href="/about">About</MenuItem>
|
||||
<MenuItem href="/contact">Contact</MenuItem>
|
||||
</Menu>
|
||||
</MenuTrigger>`;
|
||||
|
||||
export const autocomplete = `<MenuAutocomplete label="Search" placeholder="Type to search...">
|
||||
<MenuItem>Option 1</MenuItem>
|
||||
<MenuItem>Option 2</MenuItem>
|
||||
<MenuItem>Option 3</MenuItem>
|
||||
</MenuAutocomplete>`;
|
||||
|
||||
export const autocompleteListbox = `<MenuAutocomplete label="Select an option" placeholder="Type to filter...">
|
||||
<MenuListBoxItem>Option 1</MenuListBoxItem>
|
||||
<MenuListBoxItem>Option 2</MenuListBoxItem>
|
||||
<MenuListBoxItem>Option 3</MenuListBoxItem>
|
||||
</MenuAutocomplete>`;
|
||||
|
||||
export const autocompleteListboxMultiple = `<MenuAutocomplete
|
||||
label="Select multiple options"
|
||||
placeholder="Type to filter..."
|
||||
selectionMode="multiple"
|
||||
>
|
||||
<MenuListBoxItem>Option 1</MenuListBoxItem>
|
||||
<MenuListBoxItem>Option 2</MenuListBoxItem>
|
||||
<MenuListBoxItem>Option 3</MenuListBoxItem>
|
||||
</MenuAutocomplete>`;
|
||||
@@ -1,31 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/Menu/Menu.stories';
|
||||
|
||||
const {
|
||||
Preview: PreviewStory,
|
||||
PreviewSubmenu: PreviewSubmenuStory,
|
||||
PreviewIcons: PreviewIconsStory,
|
||||
PreviewLinks: PreviewLinksStory,
|
||||
PreviewSections: PreviewSectionsStory,
|
||||
PreviewSeparators: PreviewSeparatorsStory,
|
||||
PreviewAutocompleteMenu: PreviewAutocompleteMenuStory,
|
||||
PreviewAutocompleteListbox: PreviewAutocompleteListboxStory,
|
||||
PreviewAutocompleteListboxMultiple: PreviewAutocompleteListboxMultipleStory,
|
||||
} = stories;
|
||||
|
||||
export const Preview = () => <PreviewStory.Component />;
|
||||
export const PreviewSubmenu = () => <PreviewSubmenuStory.Component />;
|
||||
export const PreviewIcons = () => <PreviewIconsStory.Component />;
|
||||
export const PreviewLinks = () => <PreviewLinksStory.Component />;
|
||||
export const PreviewSections = () => <PreviewSectionsStory.Component />;
|
||||
export const PreviewSeparators = () => <PreviewSeparatorsStory.Component />;
|
||||
export const PreviewAutocompleteMenu = () => (
|
||||
<PreviewAutocompleteMenuStory.Component />
|
||||
);
|
||||
export const PreviewAutocompleteListbox = () => (
|
||||
<PreviewAutocompleteListboxStory.Component />
|
||||
);
|
||||
export const PreviewAutocompleteListboxMultiple = () => (
|
||||
<PreviewAutocompleteListboxMultipleStory.Component />
|
||||
);
|
||||
@@ -0,0 +1,34 @@
|
||||
'use client';
|
||||
|
||||
import { PasswordField } from '../../../../../packages/ui/src/components/PasswordField/PasswordField';
|
||||
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
|
||||
|
||||
export const WithLabel = () => {
|
||||
return (
|
||||
<PasswordField
|
||||
name="password"
|
||||
label="Password"
|
||||
style={{ maxWidth: '300px' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sizes = () => {
|
||||
return (
|
||||
<Flex direction="row" gap="4" style={{ width: '100%', maxWidth: '600px' }}>
|
||||
<PasswordField name="password" label="Password" size="small" />
|
||||
<PasswordField name="password" label="Password" size="medium" />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithDescription = () => {
|
||||
return (
|
||||
<PasswordField
|
||||
name="password"
|
||||
label="Password"
|
||||
description="Enter your password"
|
||||
style={{ maxWidth: '300px' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,30 +1,25 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { inputPropDefs } from './props-definition';
|
||||
import {
|
||||
inputPropDefs,
|
||||
passwordFieldUsageSnippet,
|
||||
passwordFieldDefaultSnippet,
|
||||
passwordFieldSizesSnippet,
|
||||
passwordFieldDescriptionSnippet,
|
||||
} from './password-field.props';
|
||||
withLabelSnippet,
|
||||
sizesSnippet,
|
||||
withDescriptionSnippet,
|
||||
} from './snippets';
|
||||
import { WithLabel, Sizes, WithDescription } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PasswordFieldDefinition } from '../../../utils/definitions';
|
||||
import { WithLabel, Sizes, WithDescription } from './stories';
|
||||
|
||||
<PageTitle
|
||||
title="PasswordField"
|
||||
description="A password field component for your forms."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<WithLabel />}
|
||||
code={passwordFieldDefaultSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<WithLabel />} code={withLabelSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -45,7 +40,7 @@ We support two different sizes: `small`, `medium`.
|
||||
py={4}
|
||||
open
|
||||
preview={<Sizes />}
|
||||
code={passwordFieldSizesSnippet}
|
||||
code={sizesSnippet}
|
||||
/>
|
||||
|
||||
### With description
|
||||
@@ -57,7 +52,7 @@ Here's a simple PasswordField with a description.
|
||||
py={4}
|
||||
open
|
||||
preview={<WithDescription />}
|
||||
code={passwordFieldDescriptionSnippet}
|
||||
code={withDescriptionSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={PasswordFieldDefinition} />
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const inputPropDefs: Record<string, PropDef> = {
|
||||
size: {
|
||||
type: 'enum',
|
||||
values: ['small', 'medium'],
|
||||
default: 'small',
|
||||
responsive: true,
|
||||
},
|
||||
label: {
|
||||
type: 'string',
|
||||
},
|
||||
icon: {
|
||||
type: 'enum',
|
||||
values: ['ReactNode'],
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const passwordFieldUsageSnippet = `import { PasswordField } from '@backstage/ui';
|
||||
|
||||
<PasswordField />`;
|
||||
|
||||
export const passwordFieldDefaultSnippet = `<PasswordField label="Label" placeholder="Enter a secret" />`;
|
||||
|
||||
export const passwordFieldSizesSnippet = `<Flex direction="row" gap="4">
|
||||
<PasswordField size="small" placeholder="Small" icon={<Icon name="sparkling" />} />
|
||||
<PasswordField size="medium" placeholder="Medium" icon={<Icon name="sparkling" />} />
|
||||
</Flex>`;
|
||||
|
||||
export const passwordFieldDescriptionSnippet = `<PasswordField label="Label" description="Description" placeholder="Enter a secret" />`;
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const inputPropDefs: Record<string, PropDef> = {
|
||||
size: {
|
||||
type: 'enum',
|
||||
values: ['small', 'medium'],
|
||||
default: 'small',
|
||||
responsive: true,
|
||||
},
|
||||
label: {
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
export const passwordFieldUsageSnippet = `import { PasswordField } from '@backstage/ui';
|
||||
|
||||
<PasswordField />`;
|
||||
|
||||
export const withLabelSnippet = `<PasswordField
|
||||
name="password"
|
||||
label="Password"
|
||||
/>`;
|
||||
|
||||
export const sizesSnippet = `<Flex direction="row" gap="4">
|
||||
<PasswordField name="password" label="Password" size="small" />
|
||||
<PasswordField name="password" label="Password" size="medium" />
|
||||
</Flex>`;
|
||||
|
||||
export const withDescriptionSnippet = `<PasswordField
|
||||
name="password"
|
||||
label="Password"
|
||||
description="Enter your password"
|
||||
/>`;
|
||||
@@ -1,13 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import * as stories from '@backstage/ui/src/components/PasswordField/PasswordField.stories';
|
||||
|
||||
const {
|
||||
WithLabel: WithLabelStory,
|
||||
Sizes: SizesStory,
|
||||
WithDescription: WithDescriptionStory,
|
||||
} = stories;
|
||||
|
||||
export const WithLabel = () => <WithLabelStory.Component />;
|
||||
export const Sizes = () => <SizesStory.Component />;
|
||||
export const WithDescription = () => <WithDescriptionStory.Component />;
|
||||
@@ -0,0 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import { Popover } from '../../../../../packages/ui/src/components/Popover/Popover';
|
||||
import { DialogTrigger } from '../../../../../packages/ui/src/components/Dialog/Dialog';
|
||||
import { Button } from '../../../../../packages/ui/src/components/Button/Button';
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<DialogTrigger>
|
||||
<Button>Open Popover</Button>
|
||||
<Popover>
|
||||
<div style={{ padding: '16px' }}>
|
||||
<p>This is a popover</p>
|
||||
</div>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
);
|
||||
};
|
||||
@@ -1,16 +1,9 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Default } from './stories';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import {
|
||||
popoverPropDefs,
|
||||
popoverUsageSnippet,
|
||||
popoverDefaultSnippet,
|
||||
popoverPlacementsSnippet,
|
||||
popoverNoArrowSnippet,
|
||||
popoverRichContentSnippet,
|
||||
popoverNonModalSnippet,
|
||||
} from './popover.props';
|
||||
import { dialogTriggerPropDefs, popoverPropDefs } from './props-definition';
|
||||
import { popoverUsageSnippet, defaultSnippet } from './snippets';
|
||||
import { Default } from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { PopoverDefinition } from '../../../utils/definitions';
|
||||
@@ -25,7 +18,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={popoverDefaultSnippet}
|
||||
code={defaultSnippet}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
@@ -34,69 +27,18 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
## API reference
|
||||
|
||||
### DialogTrigger
|
||||
|
||||
The trigger component that wraps both the trigger button and the popover content.
|
||||
|
||||
<PropsTable data={dialogTriggerPropDefs} />
|
||||
|
||||
### Popover
|
||||
|
||||
The main popover component that displays floating content with automatic positioning.
|
||||
|
||||
<PropsTable data={popoverPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Placements
|
||||
|
||||
Popover supports multiple placement options to position the content relative to the trigger.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={popoverPlacementsSnippet}
|
||||
/>
|
||||
|
||||
### Without Arrow
|
||||
|
||||
You can hide the arrow by setting the `hideArrow` prop.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={popoverNoArrowSnippet}
|
||||
/>
|
||||
|
||||
### Rich Content
|
||||
|
||||
Popovers can contain complex content including buttons, forms, and other interactive elements.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={popoverRichContentSnippet}
|
||||
/>
|
||||
|
||||
### Non-Modal
|
||||
|
||||
Non-modal popovers allow interaction with other elements on the page while the popover is open.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={popoverNonModalSnippet}
|
||||
/>
|
||||
|
||||
### Long Content
|
||||
|
||||
Popovers automatically handle scrolling when content exceeds the available viewport space.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<Default />}
|
||||
code={popoverDefaultSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={PopoverDefinition} />
|
||||
|
||||
<ChangelogComponent component="popover" />
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const popoverPropDefs: Record<string, PropDef> = {
|
||||
hideArrow: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description:
|
||||
'Whether to hide the arrow pointing to the trigger element. Arrow is also automatically hidden for MenuTrigger and SubmenuTrigger contexts.',
|
||||
},
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: [
|
||||
'top',
|
||||
'top start',
|
||||
'top end',
|
||||
'bottom',
|
||||
'bottom start',
|
||||
'bottom end',
|
||||
'left',
|
||||
'left start',
|
||||
'left end',
|
||||
'right',
|
||||
'right start',
|
||||
'right end',
|
||||
],
|
||||
default: 'bottom',
|
||||
description: 'The placement of the popover relative to the trigger.',
|
||||
},
|
||||
containerPadding: {
|
||||
type: 'number',
|
||||
default: '12',
|
||||
description:
|
||||
'The padding between the popover and the edge of the viewport.',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
default: '0',
|
||||
description: 'The offset from the trigger element.',
|
||||
},
|
||||
crossOffset: {
|
||||
type: 'number',
|
||||
default: '0',
|
||||
description: 'The cross-axis offset from the trigger element.',
|
||||
},
|
||||
shouldFlip: {
|
||||
type: 'boolean',
|
||||
default: 'true',
|
||||
description:
|
||||
'Whether the popover should flip to the opposite side when there is not enough space.',
|
||||
},
|
||||
isOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the popover is open (controlled).',
|
||||
},
|
||||
defaultOpen: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the popover is open by default (uncontrolled).',
|
||||
},
|
||||
onOpenChange: {
|
||||
type: 'enum',
|
||||
values: ['(isOpen: boolean) => void'],
|
||||
description:
|
||||
"Handler that is called when the popover's open state changes.",
|
||||
},
|
||||
isNonModal: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description:
|
||||
'Whether the popover is non-modal. Non-modal popovers do not block interaction with the rest of the page.',
|
||||
},
|
||||
isKeyboardDismissDisabled: {
|
||||
type: 'boolean',
|
||||
default: 'false',
|
||||
description:
|
||||
'Whether pressing the escape key to close the popover should be disabled.',
|
||||
},
|
||||
shouldCloseOnBlur: {
|
||||
type: 'boolean',
|
||||
default: 'true',
|
||||
description:
|
||||
'Whether to close the popover when the user interacts outside it.',
|
||||
},
|
||||
triggerRef: {
|
||||
type: 'enum',
|
||||
values: ['RefObject<Element | null>'],
|
||||
description: 'A ref for the trigger element.',
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const popoverUsageSnippet = `import { Popover, DialogTrigger, Button } from '@backstage/ui';
|
||||
|
||||
<DialogTrigger>
|
||||
<Button>Open Popover</Button>
|
||||
<Popover>
|
||||
<Text>This is a popover</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>`;
|
||||
|
||||
export const popoverDefaultSnippet = `<DialogTrigger>
|
||||
<Button>Open Popover</Button>
|
||||
<Popover>
|
||||
<Text>This is a popover</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>`;
|
||||
|
||||
export const popoverPlacementsSnippet = `<DialogTrigger>
|
||||
<Button>Top</Button>
|
||||
<Popover placement="top">
|
||||
<Text>Top placement</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogTrigger>
|
||||
<Button>Right</Button>
|
||||
<Popover placement="right">
|
||||
<Text>Right placement</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogTrigger>
|
||||
<Button>Bottom</Button>
|
||||
<Popover placement="bottom">
|
||||
<Text>Bottom placement</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogTrigger>
|
||||
<Button>Left</Button>
|
||||
<Popover placement="left">
|
||||
<Text>Left placement</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>`;
|
||||
|
||||
export const popoverNoArrowSnippet = `<DialogTrigger>
|
||||
<Button>Open Popover</Button>
|
||||
<Popover hideArrow>
|
||||
<Text>Popover without arrow</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>`;
|
||||
|
||||
export const popoverRichContentSnippet = `<DialogTrigger>
|
||||
<Button>Open Popover</Button>
|
||||
<Popover>
|
||||
<Flex direction="column" gap="3">
|
||||
<Text style={{ fontWeight: 'bold' }}>Popover Title</Text>
|
||||
<Text>
|
||||
This is a popover with rich content. It can contain multiple
|
||||
elements and formatted text.
|
||||
</Text>
|
||||
<Flex gap="2" justify="end">
|
||||
<Button variant="tertiary" size="small">Cancel</Button>
|
||||
<Button variant="primary" size="small">Confirm</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Popover>
|
||||
</DialogTrigger>`;
|
||||
|
||||
export const popoverNonModalSnippet = `<DialogTrigger>
|
||||
<Button>Open Non-Modal Popover</Button>
|
||||
<Popover isNonModal>
|
||||
<Text>
|
||||
This is a non-modal popover. You can interact with other
|
||||
elements on the page while it's open.
|
||||
</Text>
|
||||
</Popover>
|
||||
</DialogTrigger>`;
|
||||
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
} from '@/utils/propDefs';
|
||||
import type { PropDef } from '@/utils/propDefs';
|
||||
|
||||
export const dialogTriggerPropDefs: Record<string, PropDef> = {
|
||||
isDisabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
delay: {
|
||||
type: 'number',
|
||||
default: '0',
|
||||
},
|
||||
closeDelay: {
|
||||
type: 'number',
|
||||
default: '0',
|
||||
},
|
||||
isOpen: {
|
||||
type: 'boolean',
|
||||
},
|
||||
defaultOpen: {
|
||||
type: 'boolean',
|
||||
},
|
||||
...childrenPropDefs,
|
||||
};
|
||||
|
||||
export const popoverPropDefs: Record<string, PropDef> = {
|
||||
triggerRef: {
|
||||
type: 'enum',
|
||||
values: ['RefObject<Element | null>'],
|
||||
},
|
||||
isEntering: {
|
||||
type: 'boolean',
|
||||
},
|
||||
isExiting: {
|
||||
type: 'boolean',
|
||||
},
|
||||
placement: {
|
||||
type: 'enum',
|
||||
values: ['top', 'right', 'bottom', 'left'],
|
||||
},
|
||||
containerPadding: {
|
||||
type: 'number',
|
||||
default: '12',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
default: '8',
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user