Merge pull request #16587 from tudi2d/philipph/not-so-core-component

Tabs: Remove "not-so-core-component"
This commit is contained in:
Ben Lambert
2023-04-11 09:50:50 +02:00
committed by GitHub
13 changed files with 21 additions and 585 deletions
+21
View File
@@ -0,0 +1,21 @@
---
'@backstage/core-components': minor
---
**BREAKING:** Removing `Tabs` component from `core-components` as it is neither used in the core Backstage app nor in the monorepo plugins. If you are using this component in your instance please consider replacing it with the [Material UI `Tabs`](https://v4.mui.com/components/tabs/#tabs) component like the following:
```diff
- <Tabs
- tabs={[{
- icon: <AccessAlarmIcon />,
- content: <div>Label</div>,
- }]}
- />
+ <Tabs>
+ <Tab
+ label = "Label"
+ icon = { <AccessAlarmIcon /> }
+ />
+ </Tabs>
```
-24
View File
@@ -1318,11 +1318,6 @@ export type Tab = {
>;
};
// Warning: (ae-missing-release-tag) "TabBarClassKey" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type TabBarClassKey = 'indicator' | 'flexContainer' | 'root';
// Warning: (ae-forgotten-export) The symbol "Props_18" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "TabbedCard" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -1348,14 +1343,6 @@ export namespace TabbedLayout {
Route: (props: SubRoute) => null;
}
// @public (undocumented)
export type TabClassKey = 'root' | 'selected';
// Warning: (ae-missing-release-tag) "TabIconClassKey" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type TabIconClassKey = 'root';
// Warning: (ae-missing-release-tag) "Table" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -1438,17 +1425,6 @@ export type TableState = {
// @public (undocumented)
export type TableToolbarClassKey = 'root' | 'title' | 'searchField';
// Warning: (ae-forgotten-export) The symbol "TabsProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "Tabs" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function Tabs(props: TabsProps): JSX.Element;
// Warning: (ae-missing-release-tag) "TabsClassKey" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type TabsClassKey = 'root' | 'styledTabs' | 'appbar';
// Warning: (ae-missing-release-tag) "TrendLine" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -1,26 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 React from 'react';
import { renderInTestApp } from '@backstage/test-utils';
import { StyledTab } from './Tab';
describe('<Tab />', () => {
it('renders without exploding', async () => {
const rendered = await renderInTestApp(<StyledTab label="test" />);
expect(rendered.getByText('test')).toBeInTheDocument();
});
});
@@ -1,72 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Tab from '@material-ui/core/Tab';
import { BackstageTheme } from '@backstage/theme';
interface StyledTabProps {
label?: string;
icon?: any; // TODO: define type for material-ui icons
isFirstNav?: boolean;
isFirstIndex?: boolean;
value?: any;
}
const tabMarginLeft = (isFirstNav: boolean, isFirstIndex: boolean) => {
if (isFirstIndex) {
if (isFirstNav) {
return '20px';
}
return '0';
}
return '40px';
};
/** @public */
export type TabClassKey = 'root' | 'selected';
const useStyles = makeStyles<BackstageTheme, StyledTabProps>(
theme => ({
root: {
textTransform: 'none',
height: '64px',
fontWeight: theme.typography.fontWeightBold,
fontSize: theme.typography.pxToRem(13),
color: theme.palette.textSubtle,
marginLeft: props =>
tabMarginLeft(
props.isFirstNav as boolean,
props.isFirstIndex as boolean,
),
width: '130px',
minWidth: '130px',
'&:hover': {
outline: 'none',
backgroundColor: 'transparent',
color: theme.palette.textSubtle,
},
},
}),
{ name: 'BackstageTab' },
);
export const StyledTab = (props: StyledTabProps) => {
const classes = useStyles(props);
const { isFirstNav, isFirstIndex, ...rest } = props;
return <Tab classes={classes} disableRipple {...rest} />;
};
@@ -1,60 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 React, { PropsWithChildren } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Tabs from '@material-ui/core/Tabs';
import Typography from '@material-ui/core/Typography';
import { BackstageTheme } from '@backstage/theme';
interface StyledTabsProps {
value: number | boolean;
selectionFollowsFocus: boolean;
onChange: (event: React.ChangeEvent<{}>, newValue: number) => void;
}
export type TabBarClassKey = 'indicator' | 'flexContainer' | 'root';
const useStyles = makeStyles<BackstageTheme>(
theme => ({
indicator: {
display: 'flex',
justifyContent: 'center',
backgroundColor: theme.palette.tabbar.indicator,
height: theme.spacing(0.5),
},
flexContainer: {
alignItems: 'center',
},
root: {
'&:last-child': {
marginLeft: 'auto',
},
},
}),
{ name: 'BackstageTabBar' },
);
export const StyledTabs = (props: PropsWithChildren<StyledTabsProps>) => {
const classes = useStyles(props);
return (
<Tabs
classes={classes}
{...props}
TabIndicatorProps={{ children: <Typography component="span" /> }}
/>
);
};
@@ -1,66 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import IconButton from '@material-ui/core/IconButton';
import { BackstageTheme } from '@backstage/theme';
interface StyledIconProps {
ariaLabel: string;
children: any;
isNext?: boolean;
onClick: any;
}
export type TabIconClassKey = 'root';
const useStyles = makeStyles<BackstageTheme, StyledIconProps>(
theme => ({
root: {
color: '#6E6E6E',
overflow: 'visible',
fontSize: theme.typography.h5.fontSize,
textAlign: 'center',
borderRadius: '50%',
backgroundColor: '#E6E6E6',
marginLeft: props => (props.isNext ? 'auto' : '0'),
marginRight: props => (props.isNext ? '0' : theme.spacing(1.25)),
'&:hover': {
backgroundColor: '#E6E6E6',
opacity: '1',
},
},
}),
{ name: 'BackstageTabIcon' },
);
export const StyledIcon = (props: StyledIconProps) => {
const classes = useStyles(props);
const { ariaLabel, onClick } = props;
return (
<IconButton
onClick={onClick}
className={classes.root}
size="small"
disableRipple
disableFocusRipple
aria-label={ariaLabel}
>
{props.children}
</IconButton>
);
};
@@ -1,37 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 Box from '@material-ui/core/Box';
import React, { PropsWithChildren } from 'react';
export interface TabPanelProps {
value?: any;
index?: number;
}
export const TabPanel = (props: PropsWithChildren<TabPanelProps>) => {
const { children, value, index, ...other } = props;
return (
<Box
role="tabpanel"
hidden={value !== index}
aria-labelledby={`scrollable-auto-tab-${index}`}
{...other}
>
{value === index && <Box p={3}>{children}</Box>}
</Box>
);
};
@@ -1,71 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 React from 'react';
import { Tabs } from './Tabs';
import AccessAlarmIcon from '@material-ui/icons/AccessAlarm';
export default {
title: 'Navigation/Tabs',
component: Tabs,
};
const containerStyle = {};
export const Default = () => (
<div style={containerStyle}>
<Tabs
tabs={[...Array(4)].map((_, index) => ({
label: `ANOTHER TAB`,
content: <div>Content {index}</div>,
}))}
/>
</div>
);
export const Expandable = () => (
<div style={containerStyle}>
<Tabs
tabs={[...Array(31)].map((_, index) => ({
label: `ANOTHER TAB`,
content: <div>Content {index}</div>,
}))}
/>
</div>
);
export const Icons = () => (
<div style={containerStyle}>
<Tabs
tabs={[...Array(4)].map((_, index) => ({
icon: <AccessAlarmIcon />,
content: <div>Content {index}</div>,
}))}
/>
</div>
);
export const IconsAndLabels = () => (
<div style={containerStyle}>
<Tabs
tabs={[...Array(4)].map((_, index) => ({
icon: <AccessAlarmIcon />,
label: `ANOTHER TAB`,
content: <div>Content {index}</div>,
}))}
/>
</div>
);
@@ -1,170 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 React, { useRef, useEffect, MutableRefObject, useState } from 'react';
import { BackstageTheme } from '@backstage/theme';
import AppBar from '@material-ui/core/AppBar';
import { makeStyles } from '@material-ui/core/styles';
import NavigateBeforeIcon from '@material-ui/icons/NavigateBefore';
import NavigateNextIcon from '@material-ui/icons/NavigateNext';
import { chunkArray } from './utils';
import useWindowSize from 'react-use/lib/useWindowSize';
/* Import Components */
import { TabPanel } from './TabPanel';
import { StyledIcon } from './TabIcon';
import { StyledTab } from './Tab';
import { StyledTabs } from './TabBar';
import Box from '@material-ui/core/Box';
/* Props Types */
export interface TabProps {
content: any;
label?: string;
icon?: any; // TODO: define type for material-ui icons
}
export interface TabsProps {
tabs: TabProps[];
}
export type TabsClassKey = 'root' | 'styledTabs' | 'appbar';
const useStyles = makeStyles<BackstageTheme>(
theme => ({
root: {
flexGrow: 1,
width: '100%',
},
styledTabs: {
backgroundColor: theme.palette.background.paper,
},
appbar: {
boxShadow: 'none',
backgroundColor: theme.palette.background.paper,
paddingLeft: theme.spacing(1.25),
paddingRight: theme.spacing(1.25),
},
}),
{ name: 'BackstageTabs' },
);
export function Tabs(props: TabsProps) {
const { tabs } = props;
const classes = useStyles();
const [value, setValue] = useState([0, 0]); // [selectedChunkedNavIndex, selectedIndex]
const [navIndex, setNavIndex] = useState(0);
const [numberOfChunkedElement, setNumberOfChunkedElement] = useState(0);
const [chunkedTabs, setChunkedTabs] = useState<TabProps[][]>([[]]);
const wrapper = useRef() as MutableRefObject<HTMLDivElement>;
const { width } = useWindowSize();
const handleChange = (_: React.ChangeEvent<{}>, newValue: number) => {
setValue([navIndex, newValue]);
};
const navigateToPrevChunk = () => {
setNavIndex(navIndex - 1);
};
const navigateToNextChunk = () => {
setNavIndex(navIndex + 1);
};
const hasNextNavIndex = () => navIndex + 1 < chunkedTabs.length;
useEffect(() => {
// Each time the window is resized we calculate how many tabs we can render given the window width
const padding = 20; // The AppBar padding
const numberOfTabIcons = navIndex === 0 ? 1 : 2;
const wrapperWidth =
wrapper.current.offsetWidth - padding - numberOfTabIcons * 30;
const flattenIndex = value[0] * numberOfChunkedElement + value[1];
const newChunkedElementSize = Math.floor(wrapperWidth / 170);
setNumberOfChunkedElement(newChunkedElementSize);
setChunkedTabs(chunkArray(tabs, newChunkedElementSize));
setValue([
Math.floor(flattenIndex / newChunkedElementSize),
flattenIndex % newChunkedElementSize,
]);
// eslint-disable-next-line
}, [width, tabs]);
const currentIndex = navIndex === value[0] ? value[1] : false;
return (
<Box className={classes.root}>
<AppBar ref={wrapper} className={classes.appbar} position="static">
<Box>
<StyledTabs
value={currentIndex}
onChange={handleChange}
selectionFollowsFocus
>
{navIndex !== 0 && (
<StyledIcon
onClick={navigateToPrevChunk}
ariaLabel="navigate-before"
>
<NavigateBeforeIcon />
</StyledIcon>
)}
{chunkedTabs[navIndex].map((tab, index) => (
<StyledTab
value={index}
isFirstIndex={index === 0}
isFirstNav={navIndex === 0}
key={index}
icon={tab.icon || undefined}
label={tab.label || undefined}
/>
))}
{hasNextNavIndex() && (
<StyledIcon
isNext
onClick={navigateToNextChunk}
ariaLabel="navigate-next"
>
<NavigateNextIcon />
</StyledIcon>
)}
</StyledTabs>
</Box>
</AppBar>
{currentIndex !== false ? (
chunkedTabs[navIndex].map((tab, index) => (
<TabPanel key={index} value={index} index={currentIndex}>
{tab.content}
</TabPanel>
))
) : (
// Render if the selected tab index is outside the current rendered chunked array
<TabPanel
key="panel_outside_chunked_array"
value={value[1]}
index={value[1]}
>
{chunkedTabs[value[0]][value[1]].content}
</TabPanel>
)}
</Box>
);
}
@@ -1,22 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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.
*/
export { Tabs } from './Tabs';
export type { TabsClassKey } from './Tabs';
export type { TabClassKey } from './Tab';
export type { TabBarClassKey } from './TabBar';
export type { TabIconClassKey } from './TabIcon';
@@ -1,28 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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.
*/
export function chunkArray<T>(array: T[], chunkSize: number): T[][] {
if (chunkSize <= 0) {
return [array];
}
const result: T[][] = [];
for (let i = 0; i < array.length; i += chunkSize) {
result.push(array.slice(i, i + chunkSize));
}
return result;
}
@@ -44,6 +44,5 @@ export * from './StructuredMetadataTable';
export * from './SupportButton';
export * from './TabbedLayout';
export * from './Table';
export * from './Tabs';
export * from './TrendLine';
export * from './WarningPanel';
@@ -60,10 +60,6 @@ import {
TableToolbarClassKey,
FiltersContainerClassKey,
TableClassKey,
TabBarClassKey,
TabIconClassKey,
TabsClassKey,
TabClassKey,
WarningPanelClassKey,
} from './components';
@@ -141,10 +137,6 @@ type BackstageComponentsNameToClassKey = {
BackstageTableToolbar: TableToolbarClassKey;
BackstageTableFiltersContainer: FiltersContainerClassKey;
BackstageTable: TableClassKey;
BackstageTabBar: TabBarClassKey;
BackstageTabIcon: TabIconClassKey;
BackstageTabs: TabsClassKey;
BackstageTab: TabClassKey;
BackstageWarningPanel: WarningPanelClassKey;
BackstageBottomLink: BottomLinkClassKey;
BackstageBreadcrumbsClickableText: BreadcrumbsClickableTextClassKey;