Merge remote-tracking branch 'origin/master' into techdocs-nfs
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-app-backend': patch
|
||||
'@backstage/plugin-app-node': patch
|
||||
---
|
||||
|
||||
Updated plugin metadata.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Added missing `aria-label` attributes to `SearchField` components in `Select`, `MenuAutocomplete`, and `MenuAutocompleteListbox` to fix accessibility warnings.
|
||||
|
||||
Affected components: Select, MenuAutocomplete, MenuAutocompleteListbox
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
---
|
||||
|
||||
Updated documentation for `createApiRef` to clarify the role of the ID in specifying the owning plugin of an API.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Fixes disabled state in primary and secondary buttons in Backstage UI.
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/plugin-app-react': patch
|
||||
---
|
||||
|
||||
Moved the following blueprints from `@backstage/frontend-plugin-api`:
|
||||
|
||||
- `IconBundleBlueprint`
|
||||
- `NavContentBlueprint`
|
||||
- `RouterBlueprint`
|
||||
- `SignInPageBlueprint`
|
||||
- `SwappableComponentBlueprint`
|
||||
- `ThemeBlueprint`
|
||||
- `TranslationBlueprint`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Bumped create-app version.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Remove unused abort controller in debug:wait action
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-app-react': minor
|
||||
---
|
||||
|
||||
Initial release of this web library for `@backstage/plugin-app`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
Clean up process event listeners on backend stop to prevent leaks
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Added new Popover component for Backstage UI with automatic overflow handling, and full placement support. Also introduced `--bui-shadow` token for consistent elevation styling across overlay components (Popover, Tooltip, Menu).
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Header in EntityLayout should always be shown.
|
||||
Monitoring the loading status caused flickering when the refresh() method of the Async Entity was invoked.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Fixed Table sorting indicator not being visible when a column is actively sorted.
|
||||
|
||||
Affected components: Table, Column
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Fixed Menu component trigger button not toggling correctly. Removed custom click-outside handler that was interfering with React Aria's built-in state management, allowing the menu to properly open and close when clicking the trigger button.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-org': patch
|
||||
---
|
||||
|
||||
Adds the Resource kind to the `useGetEntities` fallback so that Resources are included by default when no specific kinds are provided in the group ownership card.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
Update colour token again in ItemCardHeader to respect theme definition.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Added support for column width configuration in Table component. Columns now accept `width`, `defaultWidth`, `minWidth`, and `maxWidth` props for responsive layout control.
|
||||
|
||||
Affected components: Table, Column
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-mui-to-bui': patch
|
||||
---
|
||||
|
||||
Fix installation command
|
||||
+19
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"mode": "pre",
|
||||
"mode": "exit",
|
||||
"tag": "next",
|
||||
"initialVersions": {
|
||||
"example-app": "0.2.116",
|
||||
@@ -207,22 +207,40 @@
|
||||
"@backstage/plugin-user-settings-common": "0.0.1"
|
||||
},
|
||||
"changesets": [
|
||||
"action-filtering-feature",
|
||||
"better-ghosts-tell",
|
||||
"chilly-maps-heal",
|
||||
"clever-streets-roll",
|
||||
"cold-dogs-study",
|
||||
"create-app-1766498726",
|
||||
"create-app-1767101113",
|
||||
"create-app-1768306186",
|
||||
"dependabot-1e2091a",
|
||||
"dependabot-aed8e01",
|
||||
"early-boxes-work",
|
||||
"easy-maps-appear",
|
||||
"every-bears-add",
|
||||
"every-lions-pay",
|
||||
"few-teams-punch",
|
||||
"fluffy-bushes-remain",
|
||||
"hot-colts-float",
|
||||
"hungry-mugs-fall",
|
||||
"major-mangos-shine",
|
||||
"mighty-flowers-travel",
|
||||
"modern-drinks-battle",
|
||||
"new-windows-reply",
|
||||
"polite-pillows-accept",
|
||||
"proud-streets-switch",
|
||||
"quick-cars-poke",
|
||||
"renovate-ccf3e66",
|
||||
"rude-trains-marry",
|
||||
"shaggy-bats-film",
|
||||
"slick-laws-push",
|
||||
"slow-clouds-tie",
|
||||
"ten-vans-agree",
|
||||
"thin-flies-travel",
|
||||
"thin-teams-roll",
|
||||
"tricky-tips-invite",
|
||||
"twenty-candles-open",
|
||||
"weak-lemons-create",
|
||||
"wild-toys-retire"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Added support for custom pagination options in `useTable` hook and `Table` component. You can now configure `pageSizeOptions` to customize the page size dropdown, and hook into pagination events via `onPageSizeChange`, `onNextPage`, and `onPreviousPage` callbacks. When `pageSize` doesn't match any option, the first option is used and a warning is logged.
|
||||
|
||||
Affected components: Table, TablePagination
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': minor
|
||||
---
|
||||
|
||||
BREAKING: The ability for plugins to override APIs has been restricted to only allow overrides of APIs within the same plugin. For example, a plugin can no longer override any of the core APIs provided by the `app` plugin, this must be done with an `app` module instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/ui': minor
|
||||
---
|
||||
|
||||
Introduce new `ToggleButton` & `ToggleButtonGroup` components in Backstage UI
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
---
|
||||
|
||||
The following blueprints are being restricted to only be used in app plugin overrides and modules. They are being moved to the `@backstage/plugin-app-react` package and have been deprecated:
|
||||
|
||||
- `IconBundleBlueprint`
|
||||
- `NavContentBlueprint`
|
||||
- `RouterBlueprint`
|
||||
- `SignInPageBlueprint`
|
||||
- `SwappableComponentBlueprint`
|
||||
- `ThemeBlueprint`
|
||||
- `TranslationBlueprint`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/repo-tools': patch
|
||||
---
|
||||
|
||||
Fixed help text for `backstage-repo-tools package schema openapi generate` command.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-auth-node': patch
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Upgrade `zod-validation-error` to version 4
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Added `className` and `style` props to the `Table` component.
|
||||
|
||||
Affected components: Table
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Fixed bug in RepoUrlPickerComponent component where repository names were not being autocompleted.
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/plugin-app': patch
|
||||
---
|
||||
|
||||
The following blueprints are being restricted to only be used in app plugin overrides and modules. They will now produce a deprecation warning when used outside of the app plugin:
|
||||
|
||||
- `IconBundleBlueprint`
|
||||
- `NavContentBlueprint`
|
||||
- `RouterBlueprint`
|
||||
- `SignInPageBlueprint`
|
||||
- `SwappableComponentBlueprint`
|
||||
- `ThemeBlueprint`
|
||||
- `TranslationBlueprint`
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs-node': minor
|
||||
---
|
||||
|
||||
**BREAKING:** It's now possible to use the credentials from the `integrations.awsS3` config to authenticate with AWS S3. The new priority is:
|
||||
|
||||
1. `aws.accounts`
|
||||
2. `techdocs.publisher.awsS3.credentials`
|
||||
3. `integrations.awsS3`
|
||||
4. Default credential chain
|
||||
|
||||
In case of multiple `integrations.awsS3` are present, the target integration is determined by the `accessKeyId` in `techdocs.publisher.awsS3.credentials` if provided. Otherwise, the default credential chain is used.
|
||||
|
||||
This means that depending on your setup, this feature may break your existing setup.
|
||||
In general:
|
||||
|
||||
- if you are configuring `aws.accounts`, no action is required
|
||||
- if you are configuring `techdocs.publisher.awsS3.credentials`, no action is required
|
||||
- if you are configuring multiple integrations under `integrations.awsS3`, no action is required
|
||||
- if you are configuring a single integration under `integrations.awsS3`, make sure that the integration has access to the bucket you are using for TechDocs
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
'@backstage/backend-test-utils': patch
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/backend-defaults': patch
|
||||
'@backstage/plugin-permission-common': patch
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
'@backstage/plugin-permission-node': patch
|
||||
'@backstage/plugin-scaffolder-node': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
'@backstage/plugin-auth-node': patch
|
||||
---
|
||||
|
||||
Upgrade `zod-to-json-schema` to latest version
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-home': patch
|
||||
---
|
||||
|
||||
Updated WidgetOverlay color to use `alpha(theme.palette.background.paper, 0.93)` for better theme alignment instead of hardcoded RGBA
|
||||
+2
-2
@@ -10,7 +10,7 @@ yarn.lock @backstage/maintainers @backst
|
||||
/.changeset @backstage/operations-maintainers
|
||||
/.changeset/*.md
|
||||
/.github @backstage/operations-maintainers
|
||||
/.github/vale @backstage/documentation-maintainers
|
||||
/.github/vale @backstage/maintainers @backstage/documentation-maintainers
|
||||
/.patches @backstage/operations-maintainers
|
||||
/beps/0001-notifications-system @backstage/maintainers @backstage/notifications-maintainers
|
||||
/docs @backstage/maintainers @backstage/documentation-maintainers
|
||||
@@ -25,7 +25,7 @@ yarn.lock @backstage/maintainers @backst
|
||||
/docs/plugins/integrating-search-into-plugins.md @backstage/search-maintainers
|
||||
/docs/releases @backstage/operations-maintainers
|
||||
/docs/tooling @backstage/tooling-maintainers
|
||||
/microsite @backstage/documentation-maintainers
|
||||
/microsite @backstage/maintainers @backstage/documentation-maintainers
|
||||
/microsite/data/plugins @backstage/maintainers
|
||||
/microsite/static @backstage/maintainers @backstage/documentation-maintainers
|
||||
/packages @backstage/framework-maintainers
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
registry-url: https://registry.npmjs.org/ # Needed for auth
|
||||
|
||||
- name: setup python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ jobs:
|
||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||
with:
|
||||
node-version: 22.x
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
||||
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
||||
|
||||
@@ -219,11 +219,12 @@
|
||||
--bui-bg-solid: #1ed760;
|
||||
--bui-bg-solid-hover: #3be477;
|
||||
--bui-bg-solid-pressed: #1abc54;
|
||||
--bui-bg-solid-disabled: var(--bui-gray-2);
|
||||
--bui-bg-solid-disabled: #0f6c30;
|
||||
|
||||
--bui-fg-primary: var(--bui-black);
|
||||
--bui-fg-secondary: var(--bui-gray-7);
|
||||
--bui-fg-solid: var(--bui-black);
|
||||
--bui-fg-solid-disabled: #62ab7c;
|
||||
|
||||
--bui-border: var(--bui-gray-3);
|
||||
--bui-border-hover: rgba(0, 0, 0, 0.3);
|
||||
@@ -246,14 +247,12 @@
|
||||
|
||||
[data-theme-mode='dark'][data-theme-name='spotify'] {
|
||||
--bui-bg-surface-0: var(--bui-black);
|
||||
|
||||
--bui-bg-solid: #1ed760;
|
||||
--bui-bg-solid-hover: #3be477;
|
||||
--bui-bg-solid-pressed: #1abc54;
|
||||
--bui-bg-solid-disabled: #0f6c30;
|
||||
--bui-bg-tint: #242424;
|
||||
--bui-bg-tint-hover: #202020;
|
||||
--bui-bg-tint-pressed: #292929;
|
||||
--bui-bg-tint-disabled: rgba(255, 255, 255, 0.8);
|
||||
|
||||
--bui-bg-danger: #3b1219;
|
||||
--bui-bg-warning: #302008;
|
||||
--bui-bg-success: #132d21;
|
||||
@@ -264,7 +263,7 @@
|
||||
--bui-fg-link-hover: var(--bui-white);
|
||||
--bui-fg-disabled: var(--bui-gray-5);
|
||||
--bui-fg-solid: var(--bui-black);
|
||||
--bui-fg-solid-disabled: #083618;
|
||||
--bui-fg-solid-disabled: #072f15;
|
||||
--bui-fg-tint: var(--bui-white);
|
||||
--bui-fg-tint-disabled: var(--bui-gray-5);
|
||||
--bui-fg-danger: #e22b2b;
|
||||
|
||||
@@ -189,15 +189,9 @@ catalog:
|
||||
target: ../catalog-model/examples/acme-corp.yaml
|
||||
rules:
|
||||
- allow: [User, Group]
|
||||
# Example component for github-actions and TechDocs
|
||||
- type: file
|
||||
target: ../../plugins/github-actions/examples/sample.yaml
|
||||
# Example component for TechDocs
|
||||
- type: file
|
||||
target: ../../plugins/techdocs-backend/examples/documented-component/catalog-info.yaml
|
||||
# Example component for ADRs
|
||||
- type: file
|
||||
target: ../../plugins/adr/examples/component/catalog-info.yaml
|
||||
# Backstage example templates
|
||||
- type: file
|
||||
target: ../../plugins/scaffolder-backend/sample-templates/all-templates.yaml
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@next/mdx": "15.5.7",
|
||||
"@remixicon/react": "^4.6.0",
|
||||
"@storybook/react": "^8.6.12",
|
||||
"@uiw/codemirror-themes": "^4.23.7",
|
||||
"@uiw/react-codemirror": "^4.23.7",
|
||||
"clsx": "^2.1.1",
|
||||
@@ -37,8 +36,7 @@
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "19.1.1",
|
||||
"react-dom": "19.1.1",
|
||||
"shiki": "^3.13.0",
|
||||
"storybook": "^8.6.15"
|
||||
"shiki": "^3.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/rest": "^22.0.1",
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,23 +0,0 @@
|
||||
import { components, layoutComponents } from '@/utils/data';
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
|
||||
const { default: Component } = await import(`@/content/${slug}.mdx`);
|
||||
|
||||
return <Component />;
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
const list = [...components, ...layoutComponents];
|
||||
|
||||
return list.map(component => ({
|
||||
slug: component.slug,
|
||||
}));
|
||||
}
|
||||
|
||||
export const dynamicParams = false;
|
||||
@@ -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,22 +1,32 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { AccordionSnippet } from '@/snippets/stories-snippets';
|
||||
import {
|
||||
accordionPropDefs,
|
||||
accordionTriggerPropDefs,
|
||||
accordionPanelPropDefs,
|
||||
accordionGroupPropDefs,
|
||||
} from './props-definition';
|
||||
import {
|
||||
accordionUsageSnippet,
|
||||
accordionWithSubtitleSnippet,
|
||||
accordionCustomTriggerSnippet,
|
||||
accordionDefaultExpandedSnippet,
|
||||
accordionGroupSingleOpenSnippet,
|
||||
accordionGroupMultipleOpenSnippet,
|
||||
} from './accordion.props';
|
||||
defaultSnippet,
|
||||
withSubtitleSnippet,
|
||||
customTriggerSnippet,
|
||||
defaultExpandedSnippet,
|
||||
groupSingleOpenSnippet,
|
||||
groupMultipleOpenSnippet,
|
||||
} from './snippets';
|
||||
import {
|
||||
Default,
|
||||
WithSubtitle,
|
||||
CustomTrigger,
|
||||
DefaultExpanded,
|
||||
GroupSingleOpen,
|
||||
GroupMultipleOpen,
|
||||
} from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { AccordionDefinition } from '../utils/definitions';
|
||||
import { AccordionDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -28,8 +38,8 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
align="center"
|
||||
py={4}
|
||||
height={240}
|
||||
preview={<AccordionSnippet story="Default" />}
|
||||
code={accordionUsageSnippet}
|
||||
preview={<Default />}
|
||||
code={defaultSnippet}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
@@ -72,8 +82,8 @@ Here's a view when using both title and subtitle props.
|
||||
align="center"
|
||||
py={4}
|
||||
height={240}
|
||||
preview={<AccordionSnippet story="WithSubtitle" />}
|
||||
code={accordionWithSubtitleSnippet}
|
||||
preview={<WithSubtitle />}
|
||||
code={withSubtitleSnippet}
|
||||
/>
|
||||
|
||||
### Custom Trigger
|
||||
@@ -84,8 +94,8 @@ Here's a view when providing custom multi-line content as children.
|
||||
align="center"
|
||||
py={4}
|
||||
height={280}
|
||||
preview={<AccordionSnippet story="CustomTrigger" />}
|
||||
code={accordionCustomTriggerSnippet}
|
||||
preview={<CustomTrigger />}
|
||||
code={customTriggerSnippet}
|
||||
/>
|
||||
|
||||
### Default Expanded
|
||||
@@ -96,8 +106,8 @@ Here's a view when the panel is expanded by default.
|
||||
align="center"
|
||||
py={4}
|
||||
height={280}
|
||||
preview={<AccordionSnippet story="DefaultExpanded" />}
|
||||
code={accordionDefaultExpandedSnippet}
|
||||
preview={<DefaultExpanded />}
|
||||
code={defaultExpandedSnippet}
|
||||
/>
|
||||
|
||||
### Group Single Open
|
||||
@@ -108,8 +118,8 @@ Here's a view when only one accordion can be open at a time.
|
||||
align="center"
|
||||
py={4}
|
||||
height={280}
|
||||
preview={<AccordionSnippet story="GroupSingleOpen" />}
|
||||
code={accordionGroupSingleOpenSnippet}
|
||||
preview={<GroupSingleOpen />}
|
||||
code={groupSingleOpenSnippet}
|
||||
/>
|
||||
|
||||
### Group Multiple Open
|
||||
@@ -120,8 +130,8 @@ Here's a view when multiple accordions can be open simultaneously.
|
||||
align="center"
|
||||
py={4}
|
||||
height={280}
|
||||
preview={<AccordionSnippet story="GroupMultipleOpen" />}
|
||||
code={accordionGroupMultipleOpenSnippet}
|
||||
preview={<GroupMultipleOpen />}
|
||||
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>`;
|
||||
@@ -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,17 +1,18 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { AvatarSnippet } from '@/snippets/stories-snippets';
|
||||
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';
|
||||
import { AvatarDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -19,16 +20,11 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="An avatar component with a fallback for initials."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<AvatarSnippet story="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
|
||||
|
||||
@@ -40,13 +36,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
Avatar sizes can be set using the `size` prop.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<AvatarSnippet story="Sizes" />}
|
||||
code={snippetSizes}
|
||||
/>
|
||||
<Snippet align="center" py={4} open preview={<Sizes />} code={sizesSnippet} />
|
||||
|
||||
### Fallback
|
||||
|
||||
@@ -56,20 +46,15 @@ If the image is not available, the avatar will show the initials of the name.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<AvatarSnippet story="Fallback" />}
|
||||
code={snippetFallback}
|
||||
preview={<Fallback />}
|
||||
code={fallbackSnippet}
|
||||
/>
|
||||
|
||||
### The `purpose` prop
|
||||
|
||||
Control how the avatar is announced to screen readers using the `purpose` prop.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<AvatarSnippet story="Purpose" />}
|
||||
code={snippetPurpose}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Purpose />} 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>`;
|
||||
@@ -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,18 +1,18 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { BoxSnippet } from '@/snippets/stories-snippets';
|
||||
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';
|
||||
import { BoxDefinition } from '../utils/definitions';
|
||||
import { BoxDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -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={<BoxSnippet story="Default" />}
|
||||
code={boxPreviewSnippet}
|
||||
align="center"
|
||||
/>
|
||||
<Snippet py={4} preview={<Default />} code={defaultSnippet} align="center" />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -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>`;
|
||||
@@ -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"
|
||||
/>
|
||||
);
|
||||
};
|
||||
+16
-36
@@ -1,21 +1,18 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { ButtonIconSnippet } from '@/snippets/stories-snippets';
|
||||
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';
|
||||
import { ButtonIconDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -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={<ButtonIconSnippet story="Variants" />}
|
||||
code={buttonIconDefaultSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Variants />} code={variantsSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -48,21 +40,15 @@ Here's a view when buttons have different variants.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonIconSnippet story="Variants" />}
|
||||
code={buttonIconVariantsSnippet}
|
||||
preview={<Variants />}
|
||||
code={variantsSnippet}
|
||||
/>
|
||||
|
||||
### Sizes
|
||||
|
||||
Here's a view when buttons have different sizes.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonIconSnippet story="Sizes" />}
|
||||
code={buttonIconSizesSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} open preview={<Sizes />} code={sizesSnippet} />
|
||||
|
||||
### Disabled
|
||||
|
||||
@@ -72,8 +58,8 @@ Here's a view when buttons are disabled.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonIconSnippet story="Disabled" />}
|
||||
code={buttonIconDisabledSnippet}
|
||||
preview={<Disabled />}
|
||||
code={disabledSnippet}
|
||||
/>
|
||||
|
||||
### Loading
|
||||
@@ -84,16 +70,10 @@ Here's a view when buttons are in a loading state.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonIconSnippet story="Loading" />}
|
||||
code={buttonIconLoadingSnippet}
|
||||
preview={<Loading />}
|
||||
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" />`;
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { buttonLinkPropDefs } from './props-definition';
|
||||
import {
|
||||
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';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
title="ButtonLink"
|
||||
description="A button component that can be used as a link."
|
||||
/>
|
||||
|
||||
<Snippet align="center" py={4} preview={<Variants />} code={variantsSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={buttonLinkUsageSnippet} />
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable data={buttonLinkPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Variants
|
||||
|
||||
Here's a view when buttons have different variants.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<Variants />}
|
||||
code={variantsSnippet}
|
||||
/>
|
||||
|
||||
### Sizes
|
||||
|
||||
Here's a view when buttons have different sizes.
|
||||
|
||||
<Snippet align="center" py={4} open preview={<Sizes />} code={sizesSnippet} />
|
||||
|
||||
### With Icons
|
||||
|
||||
Here's a view when buttons have icons.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<WithIcons />}
|
||||
code={withIconsSnippet}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
|
||||
Here's a view when buttons are disabled.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<Disabled />}
|
||||
code={disabledSnippet}
|
||||
/>
|
||||
|
||||
<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>`;
|
||||
@@ -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,34 +1,36 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { ButtonSnippet, ButtonLinkSnippet } from '@/snippets/stories-snippets';
|
||||
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 { 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={<ButtonSnippet story="Variants" />}
|
||||
code={buttonVariantsSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Variants />} code={variantsSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -48,21 +50,15 @@ Here's a view when buttons have different variants.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonSnippet story="Variants" />}
|
||||
code={buttonVariantsSnippet}
|
||||
preview={<Variants />}
|
||||
code={variantsSnippet}
|
||||
/>
|
||||
|
||||
### Sizes
|
||||
|
||||
Here's a view when buttons have different sizes.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonSnippet story="Sizes" />}
|
||||
code={buttonSizesSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} open preview={<Sizes />} code={sizesSnippet} />
|
||||
|
||||
### With Icons
|
||||
|
||||
@@ -72,8 +68,8 @@ Here's a view when buttons have icons.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonSnippet story="WithIcons" />}
|
||||
code={buttonIconsSnippet}
|
||||
preview={<WithIcons />}
|
||||
code={withIconsSnippet}
|
||||
/>
|
||||
|
||||
### Disabled
|
||||
@@ -84,8 +80,8 @@ Here's a view when buttons are disabled.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonSnippet story="Disabled" />}
|
||||
code={buttonDisabledSnippet}
|
||||
preview={<Disabled />}
|
||||
code={disabledSnippet}
|
||||
/>
|
||||
|
||||
### Loading
|
||||
@@ -96,8 +92,8 @@ Here's a view when buttons are in a loading state.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<ButtonSnippet story="Loading" />}
|
||||
code={buttonLoadingSnippet}
|
||||
preview={<Loading />}
|
||||
code={loadingSnippet}
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
@@ -110,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={<ButtonLinkSnippet story="Variants" />}
|
||||
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>`;
|
||||
@@ -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 style={{ width: '300px', height: '200px' }}>
|
||||
<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,20 +1,22 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CardSnippet } from '@/snippets/stories-snippets';
|
||||
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';
|
||||
import { CardDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -22,12 +24,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="A card component that can be used to display content in a box."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<CardSnippet story="CustomSize" />}
|
||||
code={cardDefaultSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -61,6 +58,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.
|
||||
@@ -68,20 +77,8 @@ Here's a view when card has a long body.
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<CardSnippet story="WithLongBody" />}
|
||||
code={cardLongBodySnippet}
|
||||
open
|
||||
/>
|
||||
|
||||
### With list
|
||||
|
||||
Here's a view when card has a list.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<CardSnippet story="WithListRow" />}
|
||||
code={cardListRowSnippet}
|
||||
preview={<WithLongBody />}
|
||||
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>`;
|
||||
@@ -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,16 +1,16 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CheckboxSnippet } from '@/snippets/stories-snippets';
|
||||
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';
|
||||
import { CheckboxDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -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={<CheckboxSnippet story="Default" />}
|
||||
code={checkboxDefaultSnippet}
|
||||
/>
|
||||
<Snippet align="center" py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -43,8 +38,8 @@ Here's a view when checkboxes have different variants.
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<CheckboxSnippet story="AllVariants" />}
|
||||
code={checkboxVariantsSnippet}
|
||||
preview={<AllVariants />}
|
||||
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>`;
|
||||
@@ -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,17 +1,17 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { ContainerSnippet } from '@/snippets/stories-snippets';
|
||||
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';
|
||||
import { ContainerDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -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={<ContainerSnippet story="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>
|
||||
@@ -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,11 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { DialogSnippet } from '@/snippets/stories-snippets';
|
||||
import {
|
||||
Default,
|
||||
PreviewFixedWidthAndHeight,
|
||||
PreviewWithForm,
|
||||
} from './components';
|
||||
import {
|
||||
dialogPropDefs,
|
||||
dialogTriggerPropDefs,
|
||||
@@ -9,16 +13,18 @@ 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';
|
||||
import { DialogDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -29,7 +35,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<DialogSnippet story="Default" />}
|
||||
preview={<Default />}
|
||||
code={dialogDefaultSnippet}
|
||||
/>
|
||||
|
||||
@@ -82,7 +88,7 @@ Dialog with a fixed height body that scrolls when content overflows.
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<DialogSnippet story="PreviewFixedWidthAndHeight" />}
|
||||
preview={<PreviewFixedWidthAndHeight />}
|
||||
code={dialogFixedWidthAndHeightSnippet}
|
||||
/>
|
||||
|
||||
@@ -93,7 +99,7 @@ Dialog containing form elements for user input.
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
preview={<DialogSnippet story="PreviewWithForm" />}
|
||||
preview={<PreviewWithForm />}
|
||||
code={dialogWithFormSnippet}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
@@ -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,20 +1,19 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { FlexSnippet } from '@/snippets/stories-snippets';
|
||||
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';
|
||||
import { FlexDefinition } from '../utils/definitions';
|
||||
import { FlexDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -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={<FlexSnippet story="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>`;
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { gridPropDefs } from './props-definition';
|
||||
import { gridUsageSnippet, defaultSnippet } from './snippets';
|
||||
import { Default } from './components';
|
||||
import { spacingPropDefs } from '@/utils/propDefs';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { GridDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
type="Layout"
|
||||
title="Grid"
|
||||
description="A layout component that helps to create simple column-based layouts as well as more complex ones."
|
||||
/>
|
||||
|
||||
<Snippet py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={gridUsageSnippet} />
|
||||
|
||||
## API reference
|
||||
|
||||
### Grid.Root
|
||||
|
||||
This is the grid container component. It will help to define the number of
|
||||
columns that will be used in the grid. You can also define the gap between the
|
||||
columns. All values are responsive.
|
||||
|
||||
<PropsTable data={gridPropDefs} />
|
||||
|
||||
The grid component also accepts all the spacing props from the Box component.
|
||||
|
||||
<PropsTable data={spacingPropDefs} />
|
||||
|
||||
<Theming definition={GridDefinition} />
|
||||
|
||||
<ChangelogComponent component="grid" />
|
||||
@@ -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>`;
|
||||
@@ -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>
|
||||
);
|
||||
+15
-30
@@ -1,20 +1,25 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { HeaderPageSnippet } from '@/snippets/stories-snippets';
|
||||
import {
|
||||
propDefs,
|
||||
WithEverything,
|
||||
WithLongBreadcrumbs,
|
||||
WithTabs,
|
||||
WithCustomActions,
|
||||
WithMenuItems,
|
||||
} from './components';
|
||||
import { propDefs } from './props-definition';
|
||||
import {
|
||||
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';
|
||||
import { HeaderPageDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -23,11 +28,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="A header page component for the plugin that should sit under the Header component."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<HeaderPageSnippet story="WithEverything" />}
|
||||
code={defaultSnippet}
|
||||
/>
|
||||
<Snippet py={4} preview={<WithEverything />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -44,44 +45,28 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
You can add breadcrumbs to the header page to help users navigate to the previous page. The `breadcrumbs`
|
||||
prop is an array of objects with a `label` and `href` property. By default we truncate the breadcrumb label to 240px.
|
||||
|
||||
<Snippet
|
||||
open
|
||||
preview={<HeaderPageSnippet story="WithLongBreadcrumbs" />}
|
||||
code={withBreadcrumbs}
|
||||
/>
|
||||
<Snippet open preview={<WithLongBreadcrumbs />} code={withBreadcrumbs} />
|
||||
|
||||
### With Tabs
|
||||
|
||||
You can add tabs to the header page to help users navigate to the different sections of the page. The `tabs`
|
||||
prop is an array of objects with a `label` and `href` property.
|
||||
|
||||
<Snippet
|
||||
open
|
||||
preview={<HeaderPageSnippet story="WithTabs" />}
|
||||
code={withTabs}
|
||||
/>
|
||||
<Snippet open preview={<WithTabs />} code={withTabs} />
|
||||
|
||||
### With Custom Actions
|
||||
|
||||
You can add custom actions to the header page to help users navigate to the different sections of the page. The `customActions`
|
||||
prop is a React node.
|
||||
|
||||
<Snippet
|
||||
open
|
||||
preview={<HeaderPageSnippet story="WithCustomActions" />}
|
||||
code={withCustomActions}
|
||||
/>
|
||||
<Snippet open preview={<WithCustomActions />} code={withCustomActions} />
|
||||
|
||||
### With Menu Items
|
||||
|
||||
You can add menu items to the header page to help users navigate to the different sections of the page. The `menuItems`
|
||||
prop is an array of objects with a `label`, `value` and `onClick` property.
|
||||
|
||||
<Snippet
|
||||
open
|
||||
preview={<HeaderPageSnippet story="WithMenuItems" />}
|
||||
code={withMenuItems}
|
||||
/>
|
||||
<Snippet open preview={<WithMenuItems />} code={withMenuItems} />
|
||||
|
||||
<Theming definition={HeaderPageDefinition} />
|
||||
|
||||
-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: () => {} },
|
||||
]}
|
||||
/>`;
|
||||
@@ -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,19 +1,24 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { HeaderSnippet } from '@/snippets/stories-snippets';
|
||||
import {
|
||||
propDefs,
|
||||
WithAllOptionsAndTabs,
|
||||
WithAllOptions,
|
||||
WithBreadcrumbs,
|
||||
WithHeaderPage,
|
||||
} from './components';
|
||||
import { propDefs } from './props-definition';
|
||||
import {
|
||||
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';
|
||||
import { HeaderDefinition } from '../../../utils/definitions';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
@@ -22,11 +27,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
description="A header component for the plugin."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<HeaderSnippet story="WithAllOptionsAndTabs" />}
|
||||
code={defaultSnippet}
|
||||
/>
|
||||
<Snippet py={4} preview={<WithAllOptionsAndTabs />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -42,12 +43,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
A simple example of how to use the Header component.
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<HeaderSnippet story="WithAllOptions" />}
|
||||
code={simple}
|
||||
open
|
||||
/>
|
||||
<Snippet py={4} preview={<WithAllOptions />} code={simple} open />
|
||||
|
||||
### Header with tabs
|
||||
|
||||
@@ -56,7 +52,7 @@ under the hood and will be active when you are on the corresponding page.
|
||||
|
||||
<Snippet
|
||||
|
||||
preview={<HeaderSnippet story="WithAllOptionsAndTabs" />}
|
||||
preview={<WithAllOptionsAndTabs />}
|
||||
code={withTabs}
|
||||
open
|
||||
/>
|
||||
@@ -65,21 +61,13 @@ open
|
||||
|
||||
Breacrumbs should appear when you scroll down (and not directly visible as it is in the demo below).
|
||||
|
||||
<Snippet
|
||||
preview={<HeaderSnippet story="WithBreadcrumbs" />}
|
||||
code={withBreadcrumbs}
|
||||
open
|
||||
/>
|
||||
<Snippet preview={<WithBreadcrumbs />} code={withBreadcrumbs} open />
|
||||
|
||||
### Header with HeaderPage
|
||||
|
||||
You can use the `Header` component inside the [HeaderPage](/components/header-page) component to compose your multi-level navigation.
|
||||
|
||||
<Snippet
|
||||
preview={<HeaderSnippet story="WithHeaderPage" />}
|
||||
code={withHeaderPage}
|
||||
open
|
||||
/>
|
||||
<Snippet preview={<WithHeaderPage />} code={withHeaderPage} open />
|
||||
|
||||
<Theming definition={HeaderDefinition} />
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 />`;
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { linkPropDefs } from './props-definition';
|
||||
import {
|
||||
linkUsageSnippet,
|
||||
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';
|
||||
import { LinkDefinition } from '../../../utils/definitions';
|
||||
|
||||
<PageTitle
|
||||
title="Link"
|
||||
description="A link component that renders a `<a>` element."
|
||||
/>
|
||||
|
||||
<Snippet align="center" py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={linkUsageSnippet} />
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable data={linkPropDefs} />
|
||||
|
||||
## 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.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<AllVariants />}
|
||||
code={allVariantsSnippet}
|
||||
/>
|
||||
|
||||
### Colors
|
||||
|
||||
Here's a view when links have different colors.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<AllColors />}
|
||||
code={allColorsSnippet}
|
||||
/>
|
||||
|
||||
### Underline
|
||||
|
||||
Here's a view when links have different underline styles.
|
||||
|
||||
<Snippet
|
||||
align="center"
|
||||
py={4}
|
||||
open
|
||||
preview={<Underline />}
|
||||
code={underlineSnippet}
|
||||
/>
|
||||
|
||||
<Theming definition={LinkDefinition} />
|
||||
|
||||
<ChangelogComponent component="link" />
|
||||
@@ -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>`;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user