Refactoring based on recent feedback
Signed-off-by: Andre Wanlin <awanlin@rapidrtc.com>
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
---
|
||||
'example-app': patch
|
||||
'@backstage/plugin-org': patch
|
||||
---
|
||||
|
||||
Introduced a new MyGroups SidebarItem that links to one or more groups based on the logged in user's membership.
|
||||
Introduced a new MyGroupsSidebarItem SidebarItem that links to one or more groups based on the logged in user's membership.
|
||||
|
||||
To use MyGroups you'll need to add it to your `Root.tsx` like this:
|
||||
To use MyGroupsSidebarItem you'll need to add it to your `Root.tsx` like this:
|
||||
|
||||
```diff
|
||||
// app/src/components/Root/Root.tsx
|
||||
+ import { MyGroups } from '@backstage/plugin-org';
|
||||
+ import { MyGroupsSidebarItem } from '@backstage/plugin-org';
|
||||
+ import GroupIcon from '@material-ui/icons/People';
|
||||
|
||||
<SidebarPage>
|
||||
@@ -18,11 +19,11 @@ To use MyGroups you'll need to add it to your `Root.tsx` like this:
|
||||
{/* Global nav, not org-specific */}
|
||||
//...
|
||||
<SidebarItem icon={HomeIcon} to="catalog" text="Home" />
|
||||
+ <MyGroups
|
||||
+ singularTitle="My Squad"
|
||||
+ pluralTitle="My Squads"
|
||||
+ icon={GroupIcon}
|
||||
+ />
|
||||
+ <MyGroupsSidebarItem props={{
|
||||
+ singularTitle: "My Squad",
|
||||
+ pluralTitle: "My Squads",
|
||||
+ icon: GroupIcon}}
|
||||
+ />
|
||||
//...
|
||||
</SidebarGroup>
|
||||
</ Sidebar>
|
||||
|
||||
@@ -47,7 +47,7 @@ import {
|
||||
SidebarScrollWrapper,
|
||||
SidebarSpace,
|
||||
} from '@backstage/core-components';
|
||||
import { MyGroups } from '@backstage/plugin-org';
|
||||
import { MyGroupsSidebarItem } from '@backstage/plugin-org';
|
||||
import GroupIcon from '@material-ui/icons/People';
|
||||
|
||||
const useSidebarLogoStyles = makeStyles({
|
||||
@@ -94,10 +94,12 @@ export const Root = ({ children }: PropsWithChildren<{}>) => (
|
||||
<SidebarGroup label="Menu" icon={<MenuIcon />}>
|
||||
{/* Global nav, not org-specific */}
|
||||
<SidebarItem icon={HomeIcon} to="catalog" text="Home" />
|
||||
<MyGroups
|
||||
singularTitle="My Squad"
|
||||
pluralTitle="My Squads"
|
||||
icon={GroupIcon}
|
||||
<MyGroupsSidebarItem
|
||||
props={{
|
||||
singularTitle: 'My Squad',
|
||||
pluralTitle: 'My Squads',
|
||||
icon: GroupIcon,
|
||||
}}
|
||||
/>
|
||||
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />
|
||||
<SidebarItem icon={LibraryBooks} to="docs" text="Docs" />
|
||||
|
||||
@@ -4,3 +4,55 @@
|
||||
|
||||
- Show Group Page
|
||||
- Show User Profile
|
||||
- Quick access to Groups
|
||||
|
||||
### Group Page
|
||||
|
||||
Here's an example of what the Group Page looks like:
|
||||
|
||||

|
||||
|
||||
### User Profile
|
||||
|
||||
Here's an example of what the User Profile looks like:
|
||||
|
||||

|
||||
|
||||
### MyGroupsSidebarItem
|
||||
|
||||
The MyGroupsSidebarItem provides quick access to the group(s) the logged in user is a member of directly in the sidebar.
|
||||
|
||||
To use the MyGroupsSidebarItem you'll need to add it to your `Root.tsx` - found at `packages\app\src\components\Root` - like this:
|
||||
|
||||
```diff
|
||||
+ import { MyGroupsSidebarItem } from '@backstage/plugin-org';
|
||||
+ import GroupIcon from '@material-ui/icons/People';
|
||||
|
||||
<SidebarPage>
|
||||
<Sidebar>
|
||||
//...
|
||||
<SidebarGroup label="Menu" icon={<MenuIcon />}>
|
||||
{/* Global nav, not org-specific */}
|
||||
//...
|
||||
<SidebarItem icon={HomeIcon} to="catalog" text="Home" />
|
||||
+ <MyGroupsSidebarItem props={{
|
||||
+ singularTitle: "My Squad",
|
||||
+ pluralTitle: "My Squads",
|
||||
+ icon: GroupIcon}}
|
||||
+ />
|
||||
//...
|
||||
</SidebarGroup>
|
||||
</ Sidebar>
|
||||
</SidebarPage>
|
||||
```
|
||||
|
||||
Once added MyGroupsSidebarItem will work in three ways:
|
||||
|
||||
1. The user is not logged in or the logged in user is not a member of any group: the MyGroupsSidebarItem will not display anything in the sidebar
|
||||
2. The user is logged in and a member of only one group: the MyGroupsSidebarItem will display a single item in the sidebar like this:
|
||||
|
||||

|
||||
|
||||
3. The user is logged in and a member of more than one group: the MyGroupsSidebarItem will display a single items with a flyout menu with all the related groups like this:
|
||||
|
||||

|
||||
|
||||
@@ -64,17 +64,15 @@ export const MembersListCard: (_props: {
|
||||
pageSize?: number;
|
||||
}) => JSX.Element;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "MyGroups" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const MyGroups: ({
|
||||
singularTitle,
|
||||
pluralTitle,
|
||||
icon,
|
||||
// @public
|
||||
export const MyGroupsSidebarItem: ({
|
||||
props,
|
||||
}: {
|
||||
singularTitle: string;
|
||||
pluralTitle: string;
|
||||
icon: IconComponent;
|
||||
props: {
|
||||
singularTitle: string;
|
||||
pluralTitle: string;
|
||||
icon: IconComponent;
|
||||
};
|
||||
}) => JSX.Element;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "orgPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 153 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
@@ -13,6 +13,11 @@
|
||||
"backstage": {
|
||||
"role": "frontend-plugin"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/org"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"start": "backstage-cli package start",
|
||||
|
||||
+39
-18
@@ -16,16 +16,16 @@
|
||||
|
||||
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { MyGroups } from '.';
|
||||
import { MyGroupsSidebarItem } from './MyGroupsSidebarItem';
|
||||
import GroupIcon from '@material-ui/icons/People';
|
||||
import { IdentityApi, identityApiRef } from '@backstage/core-plugin-api';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react';
|
||||
|
||||
describe('MyGroups Test', () => {
|
||||
describe('MyGroupsSidebarItem Test', () => {
|
||||
describe('For guests or users with no groups', () => {
|
||||
it('MyGroups should be empty', async () => {
|
||||
it('MyGroupsSidebarItem should be empty', async () => {
|
||||
const identityApi: Partial<IdentityApi> = {
|
||||
getBackstageIdentity: async () => ({
|
||||
type: 'user',
|
||||
@@ -46,12 +46,19 @@ describe('MyGroups Test', () => {
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<MyGroups
|
||||
singularTitle="My Squad"
|
||||
pluralTitle="My Squads"
|
||||
icon={GroupIcon}
|
||||
<MyGroupsSidebarItem
|
||||
props={{
|
||||
singularTitle: 'My Squad',
|
||||
pluralTitle: 'My Squads',
|
||||
icon: GroupIcon,
|
||||
}}
|
||||
/>
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/': entityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(rendered.container).toBeEmptyDOMElement();
|
||||
@@ -59,7 +66,7 @@ describe('MyGroups Test', () => {
|
||||
});
|
||||
|
||||
describe('For users that are members of a single group', () => {
|
||||
it('MyGroups should display a single item that links to their group', async () => {
|
||||
it('MyGroupsSidebarItem should display a single item that links to their group', async () => {
|
||||
const identityApi: Partial<IdentityApi> = {
|
||||
getBackstageIdentity: async () => ({
|
||||
type: 'user',
|
||||
@@ -94,12 +101,19 @@ describe('MyGroups Test', () => {
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<MyGroups
|
||||
singularTitle="My Squad"
|
||||
pluralTitle="My Squads"
|
||||
icon={GroupIcon}
|
||||
<MyGroupsSidebarItem
|
||||
props={{
|
||||
singularTitle: 'My Squad',
|
||||
pluralTitle: 'My Squads',
|
||||
icon: GroupIcon,
|
||||
}}
|
||||
/>
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(rendered.getByLabelText('My Squad')).toBeInTheDocument();
|
||||
expect(rendered.getByLabelText('My Squad')).toHaveAttribute(
|
||||
@@ -110,7 +124,7 @@ describe('MyGroups Test', () => {
|
||||
});
|
||||
|
||||
describe('For users that are members of multiple groups', () => {
|
||||
it('MyGroups should display a sub-menu with all their groups and a link to each group', async () => {
|
||||
it('MyGroupsSidebarItem should display a sub-menu with all their groups and a link to each group', async () => {
|
||||
const identityApi: Partial<IdentityApi> = {
|
||||
getBackstageIdentity: async () => ({
|
||||
type: 'user',
|
||||
@@ -171,12 +185,19 @@ describe('MyGroups Test', () => {
|
||||
[catalogApiRef, catalogApi],
|
||||
]}
|
||||
>
|
||||
<MyGroups
|
||||
singularTitle="My Squad"
|
||||
pluralTitle="My Squads"
|
||||
icon={GroupIcon}
|
||||
<MyGroupsSidebarItem
|
||||
props={{
|
||||
singularTitle: 'My Squad',
|
||||
pluralTitle: 'My Squads',
|
||||
icon: GroupIcon,
|
||||
}}
|
||||
/>
|
||||
</TestApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(rendered.getByLabelText('My Squads')).toBeInTheDocument();
|
||||
+28
-16
@@ -24,21 +24,30 @@ import {
|
||||
IconComponent,
|
||||
identityApiRef,
|
||||
useApi,
|
||||
useRouteRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog-react';
|
||||
import {
|
||||
catalogApiRef,
|
||||
CatalogApi,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { getCompoundEntityRef } from '@backstage/catalog-model';
|
||||
|
||||
export const MyGroups = ({
|
||||
singularTitle,
|
||||
pluralTitle,
|
||||
icon,
|
||||
/**
|
||||
* MyGroupsSidebarItem can be added to your sidebar providing quick access to groups the logged in user is a member of
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const MyGroupsSidebarItem = ({
|
||||
props,
|
||||
}: {
|
||||
singularTitle: string;
|
||||
pluralTitle: string;
|
||||
icon: IconComponent;
|
||||
props: { singularTitle: string; pluralTitle: string; icon: IconComponent };
|
||||
}) => {
|
||||
const { singularTitle, pluralTitle, icon } = props;
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const catalogApi: CatalogApi = useApi(catalogApiRef);
|
||||
const catalogEntityRoute = useRouteRef(entityRouteRef);
|
||||
|
||||
const { value: groups } = useAsync(async () => {
|
||||
const profile = await identityApi.getBackstageIdentity();
|
||||
@@ -47,31 +56,36 @@ export const MyGroups = ({
|
||||
filter: [{ kind: 'group', 'relations.hasMember': profile.userEntityRef }],
|
||||
fields: ['metadata', 'kind'],
|
||||
});
|
||||
|
||||
return response.items;
|
||||
}, []);
|
||||
|
||||
if (groups && groups.length === 1) {
|
||||
// Only member of one group
|
||||
// Not a member of any groups
|
||||
if (!groups?.length) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
// Only member of one group
|
||||
if (groups.length === 1) {
|
||||
const group = groups[0];
|
||||
return (
|
||||
<SidebarItem
|
||||
text={singularTitle}
|
||||
to={`/catalog/${group.metadata.namespace}/${group.kind}/${group.metadata.name}`}
|
||||
to={catalogEntityRoute(getCompoundEntityRef(group))}
|
||||
icon={icon}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Member of more than one group
|
||||
// or not a member of any groups
|
||||
return groups && groups.length > 1 ? (
|
||||
return (
|
||||
<SidebarItem icon={icon} to="catalog" text={pluralTitle}>
|
||||
<SidebarSubmenu title={pluralTitle}>
|
||||
{groups?.map(function groupsMap(group) {
|
||||
return (
|
||||
<SidebarSubmenuItem
|
||||
title={group.metadata.title || group.metadata.name}
|
||||
to={`/catalog/${group.metadata.namespace}/${group.kind}/${group.metadata.name}`}
|
||||
to={catalogEntityRoute(getCompoundEntityRef(group))}
|
||||
icon={icon}
|
||||
key={group.metadata.name}
|
||||
/>
|
||||
@@ -79,7 +93,5 @@ export const MyGroups = ({
|
||||
})}
|
||||
</SidebarSubmenu>
|
||||
</SidebarItem>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -14,4 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { MyGroups } from './MyGroups';
|
||||
export { MyGroupsSidebarItem } from './MyGroupsSidebarItem';
|
||||
@@ -15,4 +15,4 @@
|
||||
*/
|
||||
|
||||
export * from './Cards';
|
||||
export { MyGroups } from './MyGroups';
|
||||
export { MyGroupsSidebarItem } from './MyGroupsSidebarItem';
|
||||
|
||||
Reference in New Issue
Block a user