Merge pull request #33004 from backstage/rugvip/fix-entity-tab-group-ordering
catalog: fix entity page tab group ordering
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Fixed entity page tab groups not respecting the ordering from the `groups` configuration.
|
||||
@@ -0,0 +1 @@
|
||||
Fixes entity page tab groups not respecting the ordering from the groups configuration.
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
import { screen } from '@testing-library/react';
|
||||
import { useSelectedSubRoute } from './EntityTabs';
|
||||
import { EntityTabsList } from './EntityTabsList';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { render } from '@testing-library/react';
|
||||
import { renderInTestApp } from '@backstage/frontend-test-utils';
|
||||
|
||||
function TestSubRouteHook(props: {
|
||||
subRoutes: Array<{
|
||||
@@ -37,6 +39,83 @@ function TestSubRouteHook(props: {
|
||||
);
|
||||
}
|
||||
|
||||
describe('EntityTabsList', () => {
|
||||
it('should render groups in the order defined by groupDefinitions', () => {
|
||||
const tabs = [
|
||||
{ id: '/cicd', label: 'CI/CD', path: 'cicd', group: 'cicd' },
|
||||
{
|
||||
id: '/overview',
|
||||
label: 'Overview',
|
||||
path: 'overview',
|
||||
group: 'overview',
|
||||
},
|
||||
{
|
||||
id: '/techdocs',
|
||||
label: 'TechDocs',
|
||||
path: 'techdocs',
|
||||
group: 'techdocs',
|
||||
},
|
||||
];
|
||||
|
||||
const groupDefinitions = {
|
||||
overview: { title: 'Overview' },
|
||||
techdocs: { title: 'TechDocs' },
|
||||
cicd: { title: 'CI/CD' },
|
||||
};
|
||||
|
||||
renderInTestApp(
|
||||
<EntityTabsList
|
||||
tabs={tabs}
|
||||
groupDefinitions={groupDefinitions}
|
||||
selectedIndex={0}
|
||||
/>,
|
||||
);
|
||||
|
||||
const tabElements = screen.getAllByRole('tab');
|
||||
expect(tabElements).toHaveLength(3);
|
||||
expect(tabElements[0]).toHaveTextContent('Overview');
|
||||
expect(tabElements[1]).toHaveTextContent('TechDocs');
|
||||
expect(tabElements[2]).toHaveTextContent('CI/CD');
|
||||
});
|
||||
|
||||
it('should place ungrouped tabs after defined groups', () => {
|
||||
const tabs = [
|
||||
{ id: '/standalone', label: 'Standalone', path: 'standalone' },
|
||||
{
|
||||
id: '/overview',
|
||||
label: 'Overview',
|
||||
path: 'overview',
|
||||
group: 'overview',
|
||||
},
|
||||
{
|
||||
id: '/techdocs',
|
||||
label: 'TechDocs',
|
||||
path: 'techdocs',
|
||||
group: 'techdocs',
|
||||
},
|
||||
];
|
||||
|
||||
const groupDefinitions = {
|
||||
overview: { title: 'Overview' },
|
||||
techdocs: { title: 'TechDocs' },
|
||||
};
|
||||
|
||||
renderInTestApp(
|
||||
<EntityTabsList
|
||||
tabs={tabs}
|
||||
groupDefinitions={groupDefinitions}
|
||||
selectedIndex={0}
|
||||
/>,
|
||||
);
|
||||
|
||||
const tabElements = screen.getAllByRole('tab');
|
||||
expect(tabElements).toHaveLength(3);
|
||||
expect(tabElements[0]).toHaveTextContent('Overview');
|
||||
expect(tabElements[1]).toHaveTextContent('TechDocs');
|
||||
expect(tabElements[2]).toHaveTextContent('Standalone');
|
||||
});
|
||||
});
|
||||
|
||||
describe('EntityTabs', () => {
|
||||
const subRoutes = [
|
||||
{
|
||||
|
||||
@@ -88,20 +88,34 @@ export function EntityTabsList(props: EntityTabsListProps) {
|
||||
|
||||
const { tabs: items, selectedIndex = 0, showIcons, groupDefinitions } = props;
|
||||
|
||||
const groups = useMemo(
|
||||
() =>
|
||||
items.reduce((result, tab) => {
|
||||
const group = tab.group ? groupDefinitions[tab.group] : undefined;
|
||||
const groupOrId = group && tab.group ? tab.group : tab.id;
|
||||
result[groupOrId] = result[groupOrId] ?? {
|
||||
group,
|
||||
items: [],
|
||||
};
|
||||
result[groupOrId].items.push(tab);
|
||||
return result;
|
||||
}, {} as Record<string, TabGroup>),
|
||||
[items, groupDefinitions],
|
||||
);
|
||||
const groups = useMemo(() => {
|
||||
const byKey = items.reduce((result, tab) => {
|
||||
const group = tab.group ? groupDefinitions[tab.group] : undefined;
|
||||
const groupOrId = group && tab.group ? tab.group : tab.id;
|
||||
result[groupOrId] = result[groupOrId] ?? {
|
||||
group,
|
||||
items: [],
|
||||
};
|
||||
result[groupOrId].items.push(tab);
|
||||
return result;
|
||||
}, {} as Record<string, TabGroup>);
|
||||
|
||||
const groupOrder = Object.keys(groupDefinitions);
|
||||
return Object.entries(byKey).sort(([a], [b]) => {
|
||||
const ai = groupOrder.indexOf(a);
|
||||
const bi = groupOrder.indexOf(b);
|
||||
if (ai !== -1 && bi !== -1) {
|
||||
return ai - bi;
|
||||
}
|
||||
if (ai !== -1) {
|
||||
return -1;
|
||||
}
|
||||
if (bi !== -1) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}, [items, groupDefinitions]);
|
||||
|
||||
const selectedItem = items[selectedIndex];
|
||||
return (
|
||||
@@ -115,7 +129,7 @@ export function EntityTabsList(props: EntityTabsListProps) {
|
||||
aria-label={t('entityTabs.tabsAriaLabel')}
|
||||
value={selectedItem?.group ?? selectedItem?.id}
|
||||
>
|
||||
{Object.entries(groups).map(([id, tabGroup]) => (
|
||||
{groups.map(([id, tabGroup]) => (
|
||||
<EntityTabsGroup
|
||||
data-testid={`header-tab-${id}`}
|
||||
className={styles.defaultTab}
|
||||
|
||||
Reference in New Issue
Block a user