diff --git a/.changeset/itchy-candles-type.md b/.changeset/itchy-candles-type.md index de77b6393b..1ddad1401e 100644 --- a/.changeset/itchy-candles-type.md +++ b/.changeset/itchy-candles-type.md @@ -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'; @@ -18,11 +19,11 @@ To use MyGroups you'll need to add it to your `Root.tsx` like this: {/* Global nav, not org-specific */} //... -+ ++ //... diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index f97d2642ee..b8430294a5 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -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<{}>) => ( }> {/* Global nav, not org-specific */} - diff --git a/plugins/org/README.md b/plugins/org/README.md index a117f36722..a590cb6fa7 100644 --- a/plugins/org/README.md +++ b/plugins/org/README.md @@ -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: + +![Group Page example](./docs/group-page-example.png) + +### User Profile + +Here's an example of what the User Profile looks like: + +![Group Page example](./docs/user-profile-example.png) + +### 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'; + + + + //... + }> + {/* Global nav, not org-specific */} + //... + ++ + //... + + + +``` + +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: + + ![MyGroupsSidebarItem single example](./docs/mygroupssidebaritem-single.png) + +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: + + ![MyGroupsSidebarItem multiple example](./docs/mygroupssidebaritem-multiple.png) diff --git a/plugins/org/api-report.md b/plugins/org/api-report.md index f010458333..dd8852edc4 100644 --- a/plugins/org/api-report.md +++ b/plugins/org/api-report.md @@ -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) diff --git a/plugins/org/docs/group-page-example.png b/plugins/org/docs/group-page-example.png new file mode 100644 index 0000000000..f9770bd494 Binary files /dev/null and b/plugins/org/docs/group-page-example.png differ diff --git a/plugins/org/docs/mygroupssidebaritem-multiple.png b/plugins/org/docs/mygroupssidebaritem-multiple.png new file mode 100644 index 0000000000..90cf709e23 Binary files /dev/null and b/plugins/org/docs/mygroupssidebaritem-multiple.png differ diff --git a/plugins/org/docs/mygroupssidebaritem-single.png b/plugins/org/docs/mygroupssidebaritem-single.png new file mode 100644 index 0000000000..6c2ed4a768 Binary files /dev/null and b/plugins/org/docs/mygroupssidebaritem-single.png differ diff --git a/plugins/org/docs/user-profile-example.png b/plugins/org/docs/user-profile-example.png new file mode 100644 index 0000000000..7679a03cf4 Binary files /dev/null and b/plugins/org/docs/user-profile-example.png differ diff --git a/plugins/org/package.json b/plugins/org/package.json index 8b8c6709dc..7931825ced 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -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", diff --git a/plugins/org/src/components/MyGroups/MyGroups.test.tsx b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx similarity index 78% rename from plugins/org/src/components/MyGroups/MyGroups.test.tsx rename to plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx index bb42e2b88f..93992d371d 100644 --- a/plugins/org/src/components/MyGroups/MyGroups.test.tsx +++ b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx @@ -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 = { getBackstageIdentity: async () => ({ type: 'user', @@ -46,12 +46,19 @@ describe('MyGroups Test', () => { [catalogApiRef, catalogApi], ]} > - , + { + 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 = { getBackstageIdentity: async () => ({ type: 'user', @@ -94,12 +101,19 @@ describe('MyGroups Test', () => { [catalogApiRef, catalogApi], ]} > - , + { + 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 = { getBackstageIdentity: async () => ({ type: 'user', @@ -171,12 +185,19 @@ describe('MyGroups Test', () => { [catalogApiRef, catalogApi], ]} > - , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, ); expect(rendered.getByLabelText('My Squads')).toBeInTheDocument(); diff --git a/plugins/org/src/components/MyGroups/MyGroups.tsx b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.tsx similarity index 69% rename from plugins/org/src/components/MyGroups/MyGroups.tsx rename to plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.tsx index 686bfc706e..bea58c766c 100644 --- a/plugins/org/src/components/MyGroups/MyGroups.tsx +++ b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.tsx @@ -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 ( ); } // Member of more than one group - // or not a member of any groups - return groups && groups.length > 1 ? ( + return ( {groups?.map(function groupsMap(group) { return ( @@ -79,7 +93,5 @@ export const MyGroups = ({ })} - ) : ( - <> ); }; diff --git a/plugins/org/src/components/MyGroups/index.ts b/plugins/org/src/components/MyGroupsSidebarItem/index.ts similarity index 90% rename from plugins/org/src/components/MyGroups/index.ts rename to plugins/org/src/components/MyGroupsSidebarItem/index.ts index 2858421213..088e768980 100644 --- a/plugins/org/src/components/MyGroups/index.ts +++ b/plugins/org/src/components/MyGroupsSidebarItem/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { MyGroups } from './MyGroups'; +export { MyGroupsSidebarItem } from './MyGroupsSidebarItem'; diff --git a/plugins/org/src/components/index.ts b/plugins/org/src/components/index.ts index 73e986519a..e8bb006171 100644 --- a/plugins/org/src/components/index.ts +++ b/plugins/org/src/components/index.ts @@ -15,4 +15,4 @@ */ export * from './Cards'; -export { MyGroups } from './MyGroups'; +export { MyGroupsSidebarItem } from './MyGroupsSidebarItem';