Skip to content

Commit

Permalink
feat(fab): update fab content
Browse files Browse the repository at this point in the history
  • Loading branch information
debsmita1 committed Jan 9, 2025
1 parent be55efe commit e88dfb9
Show file tree
Hide file tree
Showing 23 changed files with 401 additions and 98 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@red-hat-developer-hub/backstage-plugin-global-floating-action-button': patch
---

update fab content
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025 The Backstage Authors
* Copyright Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {
GitHubIcon,
Link,
Expand Down Expand Up @@ -96,28 +97,43 @@ export const Root = ({ children }: PropsWithChildren<{}>) => {
to: '/create',
},
{
slot: Slot.BOTTOM_CENTER,
slot: Slot.BOTTOM_LEFT,
icon: <LibraryBooks />,
label: 'Api Docs',
toolTip: 'Api Docs',
to: '/api-docs',
},
{
slot: Slot.BOTTOM_LEFT,
icon: <ExtensionIcon />,
showLabel: true,
label: 'Docs',
toolTip: 'Docs',
to: '/docs',
},
{
color: 'success',
icon: <SearchIcon />,
label: 'Search',
toolTip: 'Search',
onClick: toggleModal,
},
{
color: 'success',
showLabel: true,
icon: <GitHubIcon />,
label: 'RHDH plugins',
label: 'RHDH pluginsssssssssssssss',
showLabel: true,
toolTip: 'RHDH plugins',
to: 'https://github.com/redhat-developer/rhdh-plugins',
visibleOnPaths: ['/catalog'],
},
{
color: 'success',
icon: <GitHubIcon />,
label: 'RHDH pluginsssssssssssssss',
toolTip: 'External link',
to: 'https://github.com/redhat-developer/rhdh-plugins',
visibleOnPaths: ['/catalog'],
},
{
color: 'success',
icon: <UserSettingsSignInAvatar />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ This plugin has been added to the example app in this workspace, meaning it can
to: '/create',
},
{
slot: Slot.BOTTOM_CENTER,
slot: Slot.BOTTOM_LEFT,
icon: <LibraryBooks />,
label: 'Docs',
toolTip: 'Docs',
Expand All @@ -57,9 +57,9 @@ This plugin has been added to the example app in this workspace, meaning it can

| Name | Type | Description | Notes |
| ------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| **slot** | `enum` | The position where the fab will be placed. Valid values: `PAGE_END`, `BOTTOM_CENTER`. | [optional] default to `PAGE_END`. |
| **slot** | `enum` | The position where the fab will be placed. Valid values: `PAGE_END`, `BOTTOM_LEFT`. | [optional] default to `PAGE_END`. |
| **label** | `String` | A name for your action button. | required |
| **icon** | `String`<br>`React.ReactElement` | An icon for your floating button. | optional |
| **icon** | `String`<br>`React.ReactElement` | An icon for your floating button. Recommended to use **filled** icons from the [Material Design library](https://fonts.google.com/icons) | required |
| **showLabel** | `Boolean` | To display the label next to your icon. | optional |
| **size** | `'small'`<br>`'medium'`<br>`'large'` | A name for your action button. | [optional] default to `'medium'` |
| **color** | `'default'`<br>`'error'`<br>`'info'`<br>`'inherit'`<br>`'primary'`<br>`'secondary'`<br>`'success'`<br>`'warning'` | The color of the component. It supports both default and custom theme colors, which can be added as shown in the [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). | [optional] default to `'default'`. |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025 The Backstage Authors
* Copyright Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,6 +29,7 @@ import {
ContentHeader,
HeaderLabel,
SupportButton,
GitHubIcon,
} from '@backstage/core-components';
import { ExampleFetchComponent } from './ExampleFetchComponent';
import { GlobalFloatingActionButton, Slot } from '../../src';
Expand Down Expand Up @@ -77,7 +78,7 @@ export const ExampleComponent = () => (
icon: '<svg viewBox="0 0 250 300" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M200.134 0l55.555 117.514-55.555 117.518h-47.295l55.555-117.518L152.84 0h47.295zM110.08 99.836l20.056-38.092-2.29-8.868L102.847 0H55.552l48.647 102.898 5.881-3.062zm17.766 74.433l-17.333-39.034-6.314-3.101-48.647 102.898h47.295l25-52.88v-7.883z" fill="#40B4E5"/><path d="M152.842 235.032L97.287 117.514 152.842 0h47.295l-55.555 117.514 55.555 117.518h-47.295zm-97.287 0L0 117.514 55.555 0h47.296L47.295 117.514l55.556 117.518H55.555z" fill="#003764"/></svg>',
},
{
slot: Slot.BOTTOM_CENTER,
slot: Slot.BOTTOM_LEFT,
color: 'success',
icon: <AddIcon />,
label: 'Add',
Expand All @@ -86,10 +87,11 @@ export const ExampleComponent = () => (
priority: 100,
},
{
slot: Slot.BOTTOM_CENTER,
slot: Slot.BOTTOM_LEFT,
color: 'success',
label: 'Menu',
toolTip: 'Menu',
label: 'Github',
icon: <GitHubIcon />,
toolTip: 'Github',
to: 'https://github.com/xyz',
priority: 200,
visibleOnPaths: ['/test-global-floating-action'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025 The Backstage Authors
* Copyright Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025 The Backstage Authors
* Copyright Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type FloatingActionButton = {
slot?: Slot;
label: string;
showLabel?: boolean;
icon?: string | React.ReactElement;
icon: string | React.ReactElement;
size?: 'small' | 'medium' | 'large';
color?:
| 'default'
Expand Down Expand Up @@ -50,7 +50,7 @@ export const globalFloatingActionButtonPlugin: BackstagePlugin<{}, {}, {}>;

// @public
export enum Slot {
BOTTOM_CENTER = 'bottom-center',
BOTTOM_LEFT = 'bottom-left',
PAGE_END = 'page-end',
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { render, screen } from '@testing-library/react';
import GitIcon from '@mui/icons-material/GitHub';
import * as React from 'react';
import { FAB } from './FAB';

jest.mock('react-router-dom', () => ({
useNavigate: jest.fn(),
useLocation: jest.fn(() => ({
pathname: '/test-path',
})),
}));

jest.mock('@backstage/core-plugin-api', () => ({
useApp: jest.fn(() => ({
getSystemIcon: jest.fn(),
})),
usetheme: jest.fn(() => ({
theme: {
transitions: {
easing: {
easeOut: 'eo',
sharp: 's',
},
},
},
})),
}));

describe('Floating Action Button', () => {
it('should render the floating action button with icon and label', () => {
render(
<FAB
actionButton={{
color: 'success',
icon: <GitIcon />,
label: 'Git repo',
showLabel: true,
to: 'https://github.com/xyz',
toolTip: 'Git',
}}
/>,
);
expect(screen.getByTestId('git-repo')).toBeInTheDocument();
expect(screen.getByTestId('GitHubIcon')).toBeInTheDocument();
expect(screen.getByText('Git repo')).toBeInTheDocument();
expect(screen.getByTestId('OpenInNewIcon')).toBeInTheDocument();
});

it('should render the floating action button with icon', () => {
render(
<FAB
actionButton={{
color: 'success',
icon: <GitIcon />,
label: 'Git repo',
to: 'https://github.com/xyz',
toolTip: 'Git',
}}
/>,
);
expect(screen.getByTestId('git-repo')).toBeInTheDocument();
expect(screen.getByTestId('GitHubIcon')).toBeInTheDocument();
expect(screen.queryByText('Git repo')).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2025 The Backstage Authors
* Copyright Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,50 +16,126 @@

import * as React from 'react';
import { useNavigate } from 'react-router-dom';
import { makeStyles } from '@mui/styles';
import Fab from '@mui/material/Fab';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import { FabIcon } from './FabIcon';
import { FloatingActionButton, Slot } from '../types';
import { slotOptions } from '../utils';

const useStyles = makeStyles(() => ({
openInNew: { paddingBottom: '5px', paddingTop: '3px' },
}));

const FABLabel = ({
label,
slot,
showExternalIcon,
icon,
order,
}: {
label: string;
slot: Slot;
showExternalIcon: boolean;
icon: string | React.ReactElement;
order: { externalIcon?: number; icon?: number };
}) => {
const styles = useStyles();
const marginStyle = slotOptions[slot].margin;
return (
<Typography sx={{ display: 'flex' }}>
{showExternalIcon && (
<OpenInNewIcon
className={styles.openInNew}
sx={{ ...marginStyle, order: order.externalIcon }}
/>
)}
{label && (
<Typography
sx={{
...marginStyle,
color: '#151515',
order: 2,
}}
>
{label}
</Typography>
)}
<Typography sx={{ mb: -1, order: order.icon }}>
<FabIcon icon={icon} />
</Typography>
</Typography>
);
};

export const FAB = ({
actionButton,
size,
className,
}: {
actionButton: FloatingActionButton;
size?: 'small' | 'medium' | 'large';
className?: string;
}) => {
const navigate = useNavigate();
const isExternalUri = (uri: string) => /^([a-z+.-]+):/.test(uri);
const external = isExternalUri(actionButton.to!);
const newWindow = external && !!/^https?:/.exec(actionButton.to!);
const isExternal = isExternalUri(actionButton.to!);
const newWindow = isExternal && !!/^https?:/.exec(actionButton.to!);
const navigateTo = () =>
actionButton.to && !external ? navigate(actionButton.to) : '';
actionButton.to && !isExternal ? navigate(actionButton.to) : '';
const labelText =
actionButton.label.length > 20
? `${actionButton.label.slice(0, actionButton.label.length)}...`
: actionButton.label;
const getColor = () => {
if (actionButton.color) {
return actionButton.color;
}
if (!className) {
return 'info';
}
return undefined;
};

const displayOnRight =
actionButton.slot === Slot.PAGE_END || !actionButton.slot;

return (
<Tooltip
title={actionButton.toolTip}
placement={Slot.PAGE_END ? 'left' : 'right'}
placement={
slotOptions[actionButton.slot || Slot.PAGE_END].tooltipDirection
}
>
<div>
<div className={className}>
<Fab
{...(newWindow ? { target: '_blank', rel: 'noopener' } : {})}
style={{ color: '#1f1f1f' }}
variant={
actionButton.showLabel || !actionButton.icon
? 'extended'
: 'circular'
actionButton.showLabel || isExternal ? 'extended' : 'circular'
}
size={size || actionButton.size || 'medium'}
color={actionButton.color || 'default'}
color={getColor()}
aria-label={actionButton.label}
data-testid={actionButton.label
.replace(' ', '-')
.toLocaleLowerCase('en-US')}
onClick={actionButton.onClick || navigateTo}
{...(external ? { href: actionButton.to } : {})}
{...(isExternal ? { href: actionButton.to } : {})}
>
{actionButton.icon && <FabIcon icon={actionButton.icon} />}
{(actionButton.showLabel || !actionButton.icon) && (
<Typography sx={actionButton.icon ? { ml: 1 } : {}}>
{actionButton.label}
</Typography>
)}
<FABLabel
showExternalIcon={isExternal}
icon={actionButton.icon}
label={actionButton.showLabel ? labelText : ''}
order={
displayOnRight
? { externalIcon: isExternal ? 1 : -1, icon: 3 }
: { externalIcon: isExternal ? 3 : -1, icon: 1 }
}
slot={actionButton.slot || Slot.PAGE_END}
/>
</Fab>
</div>
</Tooltip>
Expand Down
Loading

0 comments on commit e88dfb9

Please sign in to comment.