diff --git a/.changeset/eighty-crews-rhyme.md b/.changeset/eighty-crews-rhyme.md new file mode 100644 index 0000000000..5a8379acb6 --- /dev/null +++ b/.changeset/eighty-crews-rhyme.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': patch +--- + +Exposing Material UI extension point for tabs to be able to add additional information to them diff --git a/packages/core/src/components/TabbedLayout/RoutedTabs.tsx b/packages/core/src/components/TabbedLayout/RoutedTabs.tsx index b17c8afb3b..6b7b39329b 100644 --- a/packages/core/src/components/TabbedLayout/RoutedTabs.tsx +++ b/packages/core/src/components/TabbedLayout/RoutedTabs.tsx @@ -48,7 +48,12 @@ export const RoutedTabs = ({ routes }: { routes: SubRoute[] }) => { const navigate = useNavigate(); const { index, route, element } = useSelectedSubRoute(routes); const headerTabs = useMemo( - () => routes.map(t => ({ id: t.path, label: t.title })), + () => + routes.map(t => ({ + id: t.path, + label: t.title, + tabProps: t.tabProps, + })), [routes], ); diff --git a/packages/core/src/components/TabbedLayout/TabbedLayout.tsx b/packages/core/src/components/TabbedLayout/TabbedLayout.tsx index f181ee5980..5a1b190d00 100644 --- a/packages/core/src/components/TabbedLayout/TabbedLayout.tsx +++ b/packages/core/src/components/TabbedLayout/TabbedLayout.tsx @@ -23,11 +23,13 @@ import React, { ReactNode, } from 'react'; import { RoutedTabs } from './RoutedTabs'; +import { TabProps } from '@material-ui/core'; type SubRoute = { path: string; title: string; children: JSX.Element; + tabProps?: TabProps; }; const Route: (props: SubRoute) => null = () => null; @@ -60,8 +62,8 @@ export function createSubRoutesFromChildren( throw new Error('Child of TabbedLayout must be an TabbedLayout.Route'); } - const { path, title, children } = child.props; - return [{ path, title, children }]; + const { path, title, children, tabProps } = child.props; + return [{ path, title, children, tabProps }]; }); } diff --git a/packages/core/src/components/TabbedLayout/types.ts b/packages/core/src/components/TabbedLayout/types.ts index 29ade88dc0..24ee011933 100644 --- a/packages/core/src/components/TabbedLayout/types.ts +++ b/packages/core/src/components/TabbedLayout/types.ts @@ -14,8 +14,12 @@ * limitations under the License. */ +import { TabProps } from '@material-ui/core'; +import * as React from 'react'; + export type SubRoute = { path: string; title: string; children: JSX.Element; + tabProps?: TabProps; }; diff --git a/packages/core/src/layout/HeaderTabs/HeaderTabs.test.tsx b/packages/core/src/layout/HeaderTabs/HeaderTabs.test.tsx index 81d2123049..9a5093f6e9 100644 --- a/packages/core/src/layout/HeaderTabs/HeaderTabs.test.tsx +++ b/packages/core/src/layout/HeaderTabs/HeaderTabs.test.tsx @@ -17,6 +17,7 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import { HeaderTabs } from './'; +import { Badge, makeStyles } from '@material-ui/core'; const mockTabs = [ { id: 'overview', label: 'Overview' }, @@ -46,4 +47,35 @@ describe('', () => { 'true', ); }); + it('should render extension component to tab if one present', async () => { + const useStyles = makeStyles(() => ({ + badge: { + margin: '20px 20px 0 0', + }, + })); + + const TextualBadge = React.forwardRef((props, ref) => ( + + + {props.children} + + + )); + const iconTab = [ + { + id: 'icon-tab', + label: 'Alarms', + tabProps: { component: TextualBadge }, + }, + ]; + + const rendered = await renderInTestApp(); + + expect(rendered.getByText('Alarms')).toBeInTheDocument(); + expect(rendered.getByText('three new alarms')).toBeInTheDocument(); + }); }); diff --git a/packages/core/src/layout/HeaderTabs/HeaderTabs.tsx b/packages/core/src/layout/HeaderTabs/HeaderTabs.tsx index 718a103f00..1fd4a18ba3 100644 --- a/packages/core/src/layout/HeaderTabs/HeaderTabs.tsx +++ b/packages/core/src/layout/HeaderTabs/HeaderTabs.tsx @@ -18,7 +18,7 @@ // This is just a temporary solution to implementing tabs for now import React, { useState, useEffect } from 'react'; -import { makeStyles, Tabs, Tab as TabUI } from '@material-ui/core'; +import { makeStyles, Tabs, Tab as TabUI, TabProps } from '@material-ui/core'; const useStyles = makeStyles(theme => ({ tabsWrapper: { @@ -41,6 +41,7 @@ const useStyles = makeStyles(theme => ({ export type Tab = { id: string; label: string; + tabProps?: TabProps; }; type HeaderTabsProps = { @@ -82,6 +83,7 @@ export const HeaderTabs = ({ > {tabs.map((tab, index) => (