Skip to content

Commit 202975c

Browse files
feat(global-header): add help dropdown and support for quickstart button
1 parent d40963a commit 202975c

File tree

9 files changed

+217
-92
lines changed

9 files changed

+217
-92
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-global-header': minor
3+
---
4+
5+
**BREAKING**: `SupportButton` is now a `MenuItem` and `style` config prop can be used to update color, size and other required css properties.
6+
7+
Add `HelpDropdown` in global header plugin.

workspaces/global-header/plugins/global-header/app-config.dynamic.yaml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,22 @@ dynamicPlugins:
6565
title: RHDH Local
6666
icon: developerHub
6767
link: https://github.com/redhat-developer/rhdh-local
68-
68+
6969
- mountPoint: global.header/component
70-
importName: SupportButton
70+
importName: HelpDropdown
7171
config:
7272
priority: 80
73+
74+
- mountPoint: global.header/help
75+
importName: QuickstartButton
76+
config:
77+
priority: 100
78+
79+
- mountPoint: global.header/help
80+
importName: SupportButton
81+
config:
82+
priority: 10
83+
7384

7485
- mountPoint: global.header/component
7586
importName: NotificationButton
@@ -98,4 +109,4 @@ dynamicPlugins:
98109
- mountPoint: global.header/profile
99110
importName: LogoutButton
100111
config:
101-
priority: 10
112+
priority: 10

workspaces/global-header/plugins/global-header/report.api.md

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
```ts
66
import { BackstagePlugin } from '@backstage/core-plugin-api';
77
import type { ComponentType } from 'react';
8-
import type { CSSProperties } from 'react';
8+
import { CSSProperties } from 'react';
99
import { JSX as JSX_2 } from 'react/jsx-runtime';
1010

1111
// @public
@@ -343,29 +343,18 @@ export const StarredDropdown: () => JSX_2.Element;
343343
// @public (undocumented)
344344
export const SupportButton: ({
345345
title,
346-
tooltip,
347-
color,
348-
size,
349346
to,
350-
layout,
347+
icon,
348+
tooltip,
349+
style,
351350
}: SupportButtonProps) => JSX_2.Element | null;
352351

353352
// @public (undocumented)
354353
export interface SupportButtonProps {
355354
// (undocumented)
356-
color?:
357-
| 'inherit'
358-
| 'default'
359-
| 'primary'
360-
| 'secondary'
361-
| 'error'
362-
| 'info'
363-
| 'success'
364-
| 'warning';
365-
// (undocumented)
366-
layout?: CSSProperties;
355+
icon?: string;
367356
// (undocumented)
368-
size?: 'small' | 'medium' | 'large';
357+
style?: CSSProperties;
369358
// (undocumented)
370359
title?: string;
371360
// (undocumented)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { useMemo } from 'react';
18+
import type { CSSProperties } from 'react';
19+
import { HeaderDropdownComponent } from './HeaderDropdownComponent';
20+
import { useDropdownManager } from '../../hooks';
21+
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
22+
import { useHelpDropdownMountPoints } from '../../hooks/useHelpDropdownMountPoints';
23+
import { MenuSection } from './MenuSection';
24+
25+
/**
26+
* @public
27+
* Props for Help Dropdown
28+
*/
29+
export interface HelpDropdownProps {
30+
layout?: CSSProperties;
31+
}
32+
33+
export const HelpDropdown = ({ layout }: HelpDropdownProps) => {
34+
const { anchorEl, handleOpen, handleClose } = useDropdownManager();
35+
36+
const helpDropdownMountPoints = useHelpDropdownMountPoints();
37+
38+
const menuItems = useMemo(() => {
39+
return (helpDropdownMountPoints ?? [])
40+
.map(mp => ({
41+
Component: mp.Component,
42+
icon: mp.config?.props?.icon,
43+
label: mp.config?.props?.title,
44+
link: mp.config?.props?.link,
45+
tooltip: mp.config?.props?.tooltip,
46+
style: mp.config?.style,
47+
priority: mp.config?.priority ?? 0,
48+
}))
49+
.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
50+
}, [helpDropdownMountPoints]);
51+
52+
if (menuItems.length === 0) {
53+
return null;
54+
}
55+
56+
return (
57+
<HeaderDropdownComponent
58+
isIconButton
59+
tooltip="Help"
60+
buttonContent={<HelpOutlineIcon />}
61+
buttonProps={{
62+
color: 'inherit',
63+
sx: layout,
64+
}}
65+
onOpen={handleOpen}
66+
onClose={handleClose}
67+
anchorEl={anchorEl}
68+
>
69+
<MenuSection hideDivider items={menuItems} handleClose={handleClose} />
70+
</HeaderDropdownComponent>
71+
);
72+
};

workspaces/global-header/plugins/global-header/src/components/SupportButton/SupportButton.test.tsx

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,18 @@ const configWithoutSupportUrl = mockApis.config({
4141

4242
describe('SupportButton', () => {
4343
it('renders a button when the support url is defined', async () => {
44-
const { getByRole } = await renderInTestApp(
44+
const { getByTestId } = await renderInTestApp(
4545
<TestApiProvider apis={[[configApiRef, configWithSupportUrl]]}>
4646
<SupportButton />
4747
</TestApiProvider>,
4848
);
49-
expect(getByRole('link')).toBeInTheDocument();
50-
expect(getByRole('link').getAttribute('aria-label')).toEqual(
51-
'Support, Opens in a new window',
52-
);
53-
expect(getByRole('link').getAttribute('href')).toEqual(
49+
expect(getByTestId('support-button')).toBeInTheDocument();
50+
expect(getByTestId('support-button').getAttribute('href')).toEqual(
5451
'https://access.redhat.com/products/red-hat-developer-hub',
5552
);
56-
expect(getByRole('link').getAttribute('target')).toEqual('_blank');
53+
expect(getByTestId('support-button').getAttribute('target')).toEqual(
54+
'_blank',
55+
);
5756
});
5857

5958
it('renders no button when the support url is not defined', async () => {
@@ -66,34 +65,30 @@ describe('SupportButton', () => {
6665
});
6766

6867
it('uses the to prop also when the support url is defined', async () => {
69-
const { getByRole } = await renderInTestApp(
68+
const { getByTestId } = await renderInTestApp(
7069
<TestApiProvider apis={[[configApiRef, configWithSupportUrl]]}>
7170
<SupportButton to="https://access.redhat.com/documentation/en-us/red_hat_developer_hub" />
7271
</TestApiProvider>,
7372
);
74-
expect(getByRole('link')).toBeInTheDocument();
75-
expect(getByRole('link').getAttribute('aria-label')).toEqual(
76-
'Support, Opens in a new window',
77-
);
78-
expect(getByRole('link').getAttribute('href')).toEqual(
73+
expect(getByTestId('support-button').getAttribute('href')).toEqual(
7974
'https://access.redhat.com/documentation/en-us/red_hat_developer_hub',
8075
);
81-
expect(getByRole('link').getAttribute('target')).toEqual('_blank');
76+
expect(getByTestId('support-button').getAttribute('target')).toEqual(
77+
'_blank',
78+
);
8279
});
8380

8481
it('uses the to prop also when the support url is not defined', async () => {
85-
const { getByRole } = await renderInTestApp(
82+
const { getByTestId } = await renderInTestApp(
8683
<TestApiProvider apis={[[configApiRef, configWithoutSupportUrl]]}>
8784
<SupportButton to="https://access.redhat.com/documentation/en-us/red_hat_developer_hub" />
8885
</TestApiProvider>,
8986
);
90-
expect(getByRole('link')).toBeInTheDocument();
91-
expect(getByRole('link').getAttribute('aria-label')).toEqual(
92-
'Support, Opens in a new window',
93-
);
94-
expect(getByRole('link').getAttribute('href')).toEqual(
87+
expect(getByTestId('support-button').getAttribute('href')).toEqual(
9588
'https://access.redhat.com/documentation/en-us/red_hat_developer_hub',
9689
);
97-
expect(getByRole('link').getAttribute('target')).toEqual('_blank');
90+
expect(getByTestId('support-button').getAttribute('target')).toEqual(
91+
'_blank',
92+
);
9893
});
9994
});

workspaces/global-header/plugins/global-header/src/components/SupportButton/SupportButton.tsx

Lines changed: 23 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,50 +14,31 @@
1414
* limitations under the License.
1515
*/
1616

17-
import type { CSSProperties } from 'react';
1817
import { configApiRef, useApiHolder } from '@backstage/core-plugin-api';
19-
import { Link as BackstageLink } from '@backstage/core-components';
20-
21-
import Box from '@mui/material/Box';
22-
import IconButton from '@mui/material/IconButton';
23-
import Tooltip from '@mui/material/Tooltip';
24-
import HelpIcon from '@mui/icons-material/HelpOutline';
18+
import { Link } from '@backstage/core-components';
19+
import MenuItem from '@mui/material/MenuItem';
20+
import { MenuItemLink } from '../MenuItemLink/MenuItemLink';
21+
import { CSSProperties } from 'react';
2522

2623
/**
2724
* @public
2825
*/
2926
export interface SupportButtonProps {
27+
icon?: string;
3028
title?: string;
31-
tooltip?: string;
32-
color?:
33-
| 'inherit'
34-
| 'default'
35-
| 'primary'
36-
| 'secondary'
37-
| 'error'
38-
| 'info'
39-
| 'success'
40-
| 'warning';
41-
size?: 'small' | 'medium' | 'large';
4229
to?: string;
43-
layout?: CSSProperties;
30+
tooltip?: string;
31+
style?: CSSProperties;
4432
}
45-
46-
// Backstage Link automatically detects external links and emits analytic events.
47-
const Link = (props: any) => (
48-
<BackstageLink {...props} color="inherit" externalLinkIcon={false} />
49-
);
50-
5133
/**
5234
* @public
5335
*/
5436
export const SupportButton = ({
5537
title = 'Support',
56-
tooltip,
57-
color = 'inherit',
58-
size = 'small',
5938
to,
60-
layout,
39+
icon = 'support',
40+
tooltip,
41+
style,
6142
}: SupportButtonProps) => {
6243
const apiHolder = useApiHolder();
6344
const config = apiHolder.get(configApiRef);
@@ -67,26 +48,19 @@ export const SupportButton = ({
6748
return null;
6849
}
6950

70-
const isExternalLink =
71-
supportUrl.startsWith('http://') || supportUrl.startsWith('https://');
72-
7351
return (
74-
<Box sx={layout}>
75-
<Tooltip
76-
title={tooltip ?? `${title}${isExternalLink ? ' (external link)' : ''}`}
77-
>
78-
<div>
79-
<IconButton
80-
component={Link}
81-
color={color}
82-
size={size}
83-
to={supportUrl}
84-
aria-label={title}
85-
>
86-
<HelpIcon fontSize={size} />
87-
</IconButton>
88-
</div>
89-
</Tooltip>
90-
</Box>
52+
<MenuItem
53+
to={supportUrl}
54+
component={Link}
55+
sx={{ cursor: 'pointer', width: '100%', color: 'inherit', ...style }}
56+
data-testid="support-button"
57+
>
58+
<MenuItemLink
59+
to={supportUrl}
60+
title={title}
61+
icon={icon}
62+
tooltip={tooltip}
63+
/>
64+
</MenuItem>
9165
);
9266
};

workspaces/global-header/plugins/global-header/src/defaultMountPoints/defaultMountPoints.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
ApplicationLauncherDropdownMountPoint,
2626
CreateDropdownMountPoint,
2727
GlobalHeaderComponentMountPoint,
28+
HelpDropdownMountPoint,
2829
ProfileDropdownMountPoint,
2930
} from '../types';
3031
import { NotificationButton } from '../components/NotificationButton/NotificationButton';
@@ -34,6 +35,7 @@ import { Spacer } from '../components/Spacer/Spacer';
3435
import { StarredDropdown } from '../components/HeaderDropdownComponent/StarredDropdown';
3536
import { ApplicationLauncherDropdown } from '../components/HeaderDropdownComponent/ApplicationLauncherDropdown';
3637
import { CompanyLogo } from '../components/CompanyLogo/CompanyLogo';
38+
import { HelpDropdown } from '../components/HeaderDropdownComponent/HelpDropdown';
3739

3840
/**
3941
* default Global Header Components mount points
@@ -98,7 +100,7 @@ export const defaultGlobalHeaderComponentsMountPoints: GlobalHeaderComponentMoun
98100
},
99101
},
100102
{
101-
Component: SupportButton,
103+
Component: HelpDropdown,
102104
config: {
103105
priority: 80,
104106
},
@@ -158,6 +160,26 @@ export const defaultProfileDropdownMountPoints: ProfileDropdownMountPoint[] = [
158160
},
159161
];
160162

163+
export const defaultHelpDropdownMountPoints: HelpDropdownMountPoint[] = [
164+
{
165+
Component: MenuItemLink as ComponentType,
166+
config: {
167+
priority: 100,
168+
props: {
169+
title: 'Quickstart',
170+
icon: 'quickstart',
171+
link: 'https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.6/',
172+
},
173+
},
174+
},
175+
{
176+
Component: SupportButton,
177+
config: {
178+
priority: 10,
179+
},
180+
},
181+
];
182+
161183
export const defaultApplicationLauncherDropdownMountPoints: ApplicationLauncherDropdownMountPoint[] =
162184
[
163185
{

0 commit comments

Comments
 (0)