Merge pull request #28376 from 03DhruvJoshi/sidebar-fix

fix: fixed the bug on 'My Squads' section of the sidebar, to display the titles for the list of squads using displayName, instead of the current metadata name
This commit is contained in:
Ben Lambert
2025-01-28 09:28:26 +01:00
committed by GitHub
3 changed files with 123 additions and 73 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-org': patch
---
Added support for `spec.profile.displayName` to be used in the `MyGroupsSidebarItem` component via the `EntityDisplayName` component when you are a member of multiple Groups.
@@ -24,8 +24,15 @@ import { MyGroupsSidebarItem } from './MyGroupsSidebarItem';
import GroupIcon from '@material-ui/icons/People';
import { identityApiRef } from '@backstage/core-plugin-api';
import { Entity } from '@backstage/catalog-model';
import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react';
import {
catalogApiRef,
entityPresentationApiRef,
entityRouteRef,
} from '@backstage/plugin-catalog-react';
import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils';
import { DefaultEntityPresentationApi } from '@backstage/plugin-catalog';
import userEvent from '@testing-library/user-event';
import { screen } from '@testing-library/react';
describe('MyGroupsSidebarItem Test', () => {
describe('For guests or users with no groups', () => {
@@ -40,6 +47,10 @@ describe('MyGroupsSidebarItem Test', () => {
apis={[
[identityApiRef, identityApi],
[catalogApiRef, catalogApi],
[
entityPresentationApiRef,
DefaultEntityPresentationApi.create({ catalogApi }),
],
]}
>
<MyGroupsSidebarItem
@@ -88,6 +99,10 @@ describe('MyGroupsSidebarItem Test', () => {
apis={[
[identityApiRef, identityApi],
[catalogApiRef, catalogApi],
[
entityPresentationApiRef,
DefaultEntityPresentationApi.create({ catalogApi }),
],
]}
>
<MyGroupsSidebarItem
@@ -110,82 +125,104 @@ describe('MyGroupsSidebarItem Test', () => {
});
});
describe('For users that are members of multiple groups', () => {
it('MyGroupsSidebarItem should display a sub-menu with all their groups and a link to each group', async () => {
const identityApi = mockApis.identity({
userEntityRef: 'user:default/nigel.manning',
ownershipEntityRefs: ['user:default/nigel.manning'],
});
const catalogApi = catalogApiMock.mock({
getEntities: async () => ({
items: [
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'team-a',
title: 'Team A',
namespace: 'default',
},
spec: {
type: 'team',
children: [],
},
async function renderSideBarWithUserGroups() {
const identityApi = mockApis.identity({
userEntityRef: 'user:default/nigel.manning',
ownershipEntityRefs: ['user:default/nigel.manning'],
});
const catalogApi = catalogApiMock.mock({
getEntities: async () => ({
items: [
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'team-a',
namespace: 'default',
},
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'team-b',
title: 'Team B',
namespace: 'default',
},
spec: {
type: 'team',
children: [],
},
spec: {
type: 'team',
children: [],
},
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'team-c',
title: 'Team C',
namespace: 'default',
},
spec: {
type: 'team',
children: [],
},
},
] as Entity[],
}),
});
const rendered = await renderInTestApp(
<TestApiProvider
apis={[
[identityApiRef, identityApi],
[catalogApiRef, catalogApi],
]}
>
<MyGroupsSidebarItem
singularTitle="My Squad"
pluralTitle="My Squads"
icon={GroupIcon}
/>
</TestApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'team-b',
title: 'Team B',
namespace: 'default',
},
spec: {
type: 'team',
children: [],
},
},
{
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'team-c',
namespace: 'default',
},
spec: {
type: 'team',
children: [],
profile: {
displayName: 'Team C',
},
},
},
] as Entity[],
}),
});
expect(rendered.getByLabelText('My Squads')).toBeInTheDocument();
await renderInTestApp(
<TestApiProvider
apis={[
[identityApiRef, identityApi],
[catalogApiRef, catalogApi],
[
entityPresentationApiRef,
DefaultEntityPresentationApi.create({ catalogApi }),
],
]}
>
<MyGroupsSidebarItem
singularTitle="My Squad"
pluralTitle="My Squads"
icon={GroupIcon}
/>
</TestApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
}
describe('For users that are members of multiple groups', () => {
beforeEach(async () => {
await renderSideBarWithUserGroups();
});
it('MyGroupsSidebarItem should display a sub-menu with all their groups and a link to each group', async () => {
expect(screen.getByLabelText('My Squads')).toBeInTheDocument();
});
it('MyGroupsSidebarItem should display the displayName for each group in the list instead of the metadata name using the entityPresentationApi', async () => {
await userEvent.hover(screen.getByLabelText('My Squads'));
expect(screen.getByText('team-a')).toBeInTheDocument();
expect(screen.getByText('Team B')).toBeInTheDocument();
expect(screen.getByText('Team C')).toBeInTheDocument();
});
});
describe('When an additional filter is not provided', () => {
const entityPresentationApi = {
forEntity: jest.fn(),
};
it('catalogApi.getEntities() should be called with the default filter', async () => {
const identityApi = mockApis.identity({
userEntityRef: 'user:default/guest',
@@ -197,6 +234,7 @@ describe('MyGroupsSidebarItem Test', () => {
apis={[
[identityApiRef, identityApi],
[catalogApiRef, mockCatalogApi],
[entityPresentationApiRef, entityPresentationApi],
]}
>
<MyGroupsSidebarItem
@@ -224,6 +262,10 @@ describe('MyGroupsSidebarItem Test', () => {
});
describe('When an additional filter is provided', () => {
const entityPresentationApi = {
forEntity: jest.fn(),
};
it('catalogApi.getEntities() should be called with an additional filter item', async () => {
const identityApi = mockApis.identity({
userEntityRef: 'user:default/guest',
@@ -235,6 +277,7 @@ describe('MyGroupsSidebarItem Test', () => {
apis={[
[identityApiRef, identityApi],
[catalogApiRef, mockCatalogApi],
[entityPresentationApiRef, entityPresentationApi],
]}
>
<MyGroupsSidebarItem
@@ -38,6 +38,8 @@ import {
} from '@backstage/plugin-catalog-react';
import { getCompoundEntityRef } from '@backstage/catalog-model';
import { entityPresentationApiRef } from '@backstage/plugin-catalog-react';
/**
* MyGroupsSidebarItem can be added to your sidebar providing quick access to groups the logged in user is a member of
*
@@ -54,6 +56,7 @@ export const MyGroupsSidebarItem = (props: {
const identityApi = useApi(identityApiRef);
const catalogApi: CatalogApi = useApi(catalogApiRef);
const catalogEntityRoute = useRouteRef(entityRouteRef);
const entityPresentationApi = useApi(entityPresentationApiRef);
const { value: groups } = useAsync(async () => {
const profile = await identityApi.getBackstageIdentity();
@@ -68,11 +71,9 @@ export const MyGroupsSidebarItem = (props: {
],
fields: ['metadata', 'kind'],
});
return response.items;
}, []);
// Not a member of any groups
if (!groups?.length) {
return null;
}
@@ -93,10 +94,11 @@ export const MyGroupsSidebarItem = (props: {
return (
<SidebarItem icon={icon} text={pluralTitle}>
<SidebarSubmenu title={pluralTitle}>
{groups?.map(function groupsMap(group) {
{groups?.map(group => {
const entityDisplayName = entityPresentationApi.forEntity(group);
return (
<SidebarSubmenuItem
title={group.metadata.title || group.metadata.name}
title={entityDisplayName.snapshot.primaryTitle}
subtitle={
group.metadata.namespace !== DEFAULT_NAMESPACE
? group.metadata.namespace