Improve Header and HeaderPage content
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
@@ -57,16 +57,22 @@
|
||||
.versionLinks {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.versionLinks a {
|
||||
color: var(--text-secondary);
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
a {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--secondary);
|
||||
transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out;
|
||||
border-radius: 48px;
|
||||
|
||||
.versionLinks a:hover {
|
||||
color: var(--text-primary);
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
background-color: var(--action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
@@ -78,7 +78,8 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--action);
|
||||
/* background-color: var(--action); */
|
||||
border: 1px solid var(--border2);
|
||||
border-radius: 48px;
|
||||
padding-inline: 20px;
|
||||
color: var(--primary);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { HeaderPageSnippet } from '@/snippets/stories-snippets';
|
||||
import {
|
||||
propDefs,
|
||||
usage,
|
||||
simple,
|
||||
defaultSnippet,
|
||||
withTabs,
|
||||
withBreadcrumbs,
|
||||
withHeaderPage,
|
||||
} from './header-page.props';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PageTitle
|
||||
type="Component"
|
||||
title="HeaderPage"
|
||||
description="A header page component for the plugin that should sit under the Header component."
|
||||
/>
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<HeaderPageSnippet story="WithEverything" />}
|
||||
code={defaultSnippet}
|
||||
/>
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={usage} />
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable data={propDefs} />
|
||||
|
||||
<Theming component="Header" />
|
||||
|
||||
<ChangelogComponent component="Header" />
|
||||
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
childrenPropDefs,
|
||||
classNamePropDefs,
|
||||
stylePropDefs,
|
||||
type PropDef,
|
||||
} from '@/utils/propDefs';
|
||||
|
||||
export const propDefs: Record<string, PropDef> = {
|
||||
title: {
|
||||
type: 'string',
|
||||
default: 'Your plugin',
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
};
|
||||
|
||||
export const usage = `import { HeaderPage } from '@backstage/ui';
|
||||
|
||||
<HeaderPage />`;
|
||||
|
||||
export const defaultSnippet = `<HeaderPage
|
||||
title="My plugin"
|
||||
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>}
|
||||
/>`;
|
||||
@@ -4,16 +4,13 @@ import { Snippet } from '@/components/Snippet';
|
||||
import { HeaderSnippet } from '@/snippets/stories-snippets';
|
||||
import {
|
||||
propDefs,
|
||||
flexUsageSnippet,
|
||||
flexDefaultSnippet,
|
||||
flexFAQ1Snippet,
|
||||
flexSimpleSnippet,
|
||||
flexResponsiveSnippet,
|
||||
flexAlignSnippet,
|
||||
usage,
|
||||
simple,
|
||||
defaultSnippet,
|
||||
withTabs,
|
||||
withBreadcrumbs,
|
||||
withHeaderPage,
|
||||
} from './header.props';
|
||||
import { spacingPropDefs } from '@/utils/propDefs';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
@@ -26,7 +23,7 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<HeaderSnippet story="WithAllComponents" />}
|
||||
preview={<HeaderSnippet story="WithAllOptionsAndTabs" />}
|
||||
code={defaultSnippet}
|
||||
/>
|
||||
|
||||
@@ -38,31 +35,50 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
|
||||
<PropsTable data={propDefs} />
|
||||
|
||||
The grid component also accepts all the spacing props from the Box component.
|
||||
|
||||
<PropsTable data={spacingPropDefs} />
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple
|
||||
### Simple header
|
||||
|
||||
A simple example of how to use the Flex component.
|
||||
A simple example of how to use the Header component.
|
||||
|
||||
<CodeBlock code={flexSimpleSnippet} />
|
||||
<Snippet
|
||||
py={4}
|
||||
preview={<HeaderSnippet story="WithAllOptions" />}
|
||||
code={simple}
|
||||
open
|
||||
/>
|
||||
|
||||
### Responsive
|
||||
### Header with tabs
|
||||
|
||||
The Flex component also supports responsive values, making it easy to create
|
||||
responsive designs.
|
||||
A simple example of how to use the Header component with tabs. All links are using React Router
|
||||
under the hood and will be active when you are on the corresponding page.
|
||||
|
||||
<CodeBlock code={flexResponsiveSnippet} />
|
||||
<Snippet
|
||||
|
||||
### Align
|
||||
preview={<HeaderSnippet story="WithAllOptionsAndTabs" />}
|
||||
code={withTabs}
|
||||
open
|
||||
/>
|
||||
|
||||
The Flex component also supports responsive alignment, making it easy to
|
||||
create responsive designs.
|
||||
### Header with breadcrumbs
|
||||
|
||||
<CodeBlock code={flexAlignSnippet} />
|
||||
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
|
||||
/>
|
||||
|
||||
### 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
|
||||
/>
|
||||
|
||||
<Theming component="Header" />
|
||||
|
||||
|
||||
@@ -91,6 +91,10 @@ export const propDefs: Record<string, PropDef> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
onTabSelectionChange: {
|
||||
type: 'enum',
|
||||
values: ['(key: string) => void'],
|
||||
},
|
||||
...childrenPropDefs,
|
||||
...classNamePropDefs,
|
||||
...stylePropDefs,
|
||||
@@ -128,20 +132,87 @@ export const defaultSnippet = `<Header
|
||||
}
|
||||
/>`;
|
||||
|
||||
export const flexSimpleSnippet = `<Flex gap="md">
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Flex>`;
|
||||
export const simple = `<Header
|
||||
title="My plugin"
|
||||
titleLink="/"
|
||||
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={
|
||||
<>
|
||||
<ButtonIcon variant="tertiary" icon={<RiCloudy2Line />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiEmotionHappyLine />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiHeartLine />} />
|
||||
</>
|
||||
}
|
||||
/>`;
|
||||
|
||||
export const flexResponsiveSnippet = `<Flex gap={{ xs: 'xs', md: 'md' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Flex>`;
|
||||
export const withTabs = `
|
||||
<Header
|
||||
title="My plugin"
|
||||
titleLink="/"
|
||||
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={
|
||||
<>
|
||||
<ButtonIcon variant="tertiary" icon={<RiCloudy2Line />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiEmotionHappyLine />} />
|
||||
<ButtonIcon variant="tertiary" icon={<RiHeartLine />} />
|
||||
</>
|
||||
}
|
||||
tabs={[
|
||||
{ id: 'overview', label: 'Overview' },
|
||||
{ id: 'checks', label: 'Checks' },
|
||||
{ id: 'tracks', label: 'Tracks' },
|
||||
{ id: 'campaigns', label: 'Campaigns' },
|
||||
{ id: 'integrations', label: 'Integrations' },
|
||||
]}
|
||||
/>
|
||||
`;
|
||||
|
||||
export const flexAlignSnippet = `<Flex align={{ xs: 'start', md: 'center' }}>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
<Box>Hello World</Box>
|
||||
</Flex>`;
|
||||
export const withBreadcrumbs = `<Header
|
||||
title="My plugin"
|
||||
titleLink="/"
|
||||
breadcrumbs={[
|
||||
{ label: 'Home', href: '/' },
|
||||
{ label: 'Dashboard', href: '/dashboard' },
|
||||
{ label: 'Settings', href: '/settings' },
|
||||
]}
|
||||
tabs={[
|
||||
{ id: 'overview', label: 'Overview' },
|
||||
{ id: 'checks', label: 'Checks' },
|
||||
{ id: 'tracks', label: 'Tracks' },
|
||||
{ id: 'campaigns', label: 'Campaigns' },
|
||||
{ id: 'integrations', label: 'Integrations' },
|
||||
]}
|
||||
/>`;
|
||||
|
||||
export const withHeaderPage = `<Header
|
||||
title="My plugin"
|
||||
titleLink="/"
|
||||
breadcrumbs={...}
|
||||
tabs={...}
|
||||
/>
|
||||
<HeaderPage
|
||||
title="Page title"
|
||||
menuItems={...}
|
||||
tabs={...}
|
||||
customActions={<Button>Custom action</Button>}
|
||||
/>`;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
--font-weight-regular: 400;
|
||||
--font-weight-bold: 600;
|
||||
--border: #e5e5e5;
|
||||
--border2: #cdcdcd;
|
||||
--surface-1: #f4f4f4;
|
||||
}
|
||||
|
||||
@@ -24,6 +25,7 @@
|
||||
--action: #202020;
|
||||
--link: #4856e8;
|
||||
--border: #2a2a2a;
|
||||
--border2: #2a2a2a;
|
||||
--surface-1: #282828;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,11 @@ export const components: Page[] = [
|
||||
slug: 'header',
|
||||
status: 'alpha',
|
||||
},
|
||||
{
|
||||
title: 'HeaderPage',
|
||||
slug: 'header-page',
|
||||
status: 'alpha',
|
||||
},
|
||||
{
|
||||
title: 'Icon',
|
||||
slug: 'icon',
|
||||
|
||||
Reference in New Issue
Block a user