[TechDocs] TechDocs custom homepage with user owned filter support (#5994)

* add emptystate to docs card grid

Signed-off-by: Emma Indal <emma.indahl@gmail.com>

* adjust filterpredicate to accept a string, adjust custom home page based on owned documents filter

Signed-off-by: Emma Indal <emma.indahl@gmail.com>

* update the techdocs home page to use the new ownedByUser filter

Signed-off-by: Emma Indal <emma.indahl@gmail.com>

* fix tests

Signed-off-by: Emma Indal <emma.indahl@gmail.com>

* use useOwnUser hook from catalog react plugin and delete duplicate one

Signed-off-by: Emma Indal <emma.indahl@gmail.com>

* add changeset

Signed-off-by: Emma Indal <emma.indahl@gmail.com>
This commit is contained in:
Emma Indal
2021-06-11 16:52:46 +02:00
committed by GitHub
parent c7e0c9e16b
commit 667656c8b5
7 changed files with 82 additions and 100 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs': patch
---
Adding support for user owned document filter for TechDocs custom Homepage
@@ -21,12 +21,32 @@ import { screen } from '@testing-library/react';
import React from 'react';
import { TechDocsCustomHome, PanelType } from './TechDocsCustomHome';
describe('TechDocsCustomHome', () => {
const catalogApi: Partial<CatalogApi> = {
getEntities: async () => ({ items: [] }),
jest.mock('@backstage/plugin-catalog-react', () => {
const actual = jest.requireActual('@backstage/plugin-catalog-react');
return {
...actual,
useOwnUser: () => 'test-user',
};
});
const apiRegistry = ApiRegistry.with(catalogApiRef, catalogApi);
const mockCatalogApi = {
getEntityByName: jest.fn(),
getEntities: async () => ({
items: [
{
apiVersion: 'version',
kind: 'User',
metadata: {
name: 'owned',
namespace: 'default',
},
},
],
}),
} as Partial<CatalogApi>;
describe('TechDocsCustomHome', () => {
const apiRegistry = ApiRegistry.with(catalogApiRef, mockCatalogApi);
it('should render a TechDocs home page', async () => {
const tabsConfig = [
@@ -18,7 +18,12 @@ import React, { useState } from 'react';
import { useAsync } from 'react-use';
import { makeStyles } from '@material-ui/core';
import { CSSProperties } from '@material-ui/styles';
import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog-react';
import {
catalogApiRef,
CatalogApi,
isOwnerOf,
useOwnUser,
} from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
import {
CodeSnippet,
@@ -49,7 +54,7 @@ export interface PanelConfig {
description: string;
panelType: PanelType;
panelCSS?: CSSProperties;
filterPredicate: (entity: Entity) => boolean;
filterPredicate: ((entity: Entity) => boolean) | string;
}
export interface TabConfig {
@@ -75,8 +80,24 @@ const CustomPanel = ({
},
});
const classes = useStyles();
const { value: user } = useOwnUser();
const Panel = panels[config.panelType];
const shownEntities = entities.filter(config.filterPredicate);
const shownEntities = entities.filter(entity => {
if (config.filterPredicate === 'ownedByUser') {
if (!user) {
return false;
}
return isOwnerOf(user, entity);
}
return (
typeof config.filterPredicate === 'function' &&
config.filterPredicate(entity)
);
});
return (
<>
<ContentHeader title={config.title} description={config.description}>
@@ -27,45 +27,41 @@ import { screen } from '@testing-library/react';
import React from 'react';
import { TechDocsHome } from './TechDocsHome';
jest.mock('../hooks', () => ({
useOwnUser: () => {
return {
value: {
jest.mock('@backstage/plugin-catalog-react', () => {
const actual = jest.requireActual('@backstage/plugin-catalog-react');
return {
...actual,
useOwnUser: () => 'test-user',
};
});
const mockCatalogApi = {
getEntityByName: jest.fn(),
getEntities: async () => ({
items: [
{
apiVersion: 'version',
kind: 'User',
metadata: {
name: 'owned',
namespace: 'default',
},
relations: [
{
target: {
kind: 'TestKind',
name: 'testName',
},
type: 'ownerOf',
},
],
},
};
},
}));
],
}),
} as Partial<CatalogApi>;
describe('TechDocs Home', () => {
const catalogApi: Partial<CatalogApi> = {
getEntities: async () => ({ items: [] }),
};
const configApi: ConfigApi = new ConfigReader({
organization: {
name: 'My Company',
},
});
const apiRegistry = ApiRegistry.with(catalogApiRef, catalogApi).with(
configApiRef,
configApi,
);
const apiRegistry = ApiRegistry.from([
[catalogApiRef, mockCatalogApi],
[configApiRef, configApi],
]);
it('should render a TechDocs home page', async () => {
await renderInTestApp(
@@ -15,14 +15,9 @@
*/
import React from 'react';
import { Entity } from '@backstage/catalog-model';
import { useOwnUser } from '../hooks';
import { isOwnerOf } from '@backstage/plugin-catalog-react';
import { PanelType, TechDocsCustomHome } from './TechDocsCustomHome';
export const TechDocsHome = () => {
const { value: user } = useOwnUser();
const tabsConfig = [
{
label: 'Overview',
@@ -34,6 +29,13 @@ export const TechDocsHome = () => {
panelType: 'DocsCardGrid' as PanelType,
filterPredicate: () => true,
},
// uncomment this if you would like to have a secondary panel with owned documents
// {
// title: 'Owned',
// description: 'Explore your owned internal documentation.',
// panelType: 'DocsCardGrid' as PanelType,
// filterPredicate: 'ownedByUser',
// },
],
},
{
@@ -43,12 +45,8 @@ export const TechDocsHome = () => {
title: 'Owned documents',
description: 'Access your documentation.',
panelType: 'DocsTable' as PanelType,
filterPredicate: (entity: Entity) => {
if (!user) {
return false;
}
return isOwnerOf(user, entity);
},
// ownedByUser filters out entities owned by signed in user
filterPredicate: 'ownedByUser',
},
],
},
-17
View File
@@ -1,17 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { useOwnUser } from './useOwnUser';
@@ -1,41 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { UserEntity } from '@backstage/catalog-model';
import { identityApiRef, useApi } from '@backstage/core';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { useAsync } from 'react-use';
import { AsyncState } from 'react-use/lib/useAsync';
/**
* Get the catalog User entity (if any) that matches the logged-in user.
*/
export function useOwnUser(): AsyncState<UserEntity | undefined> {
const catalogApi = useApi(catalogApiRef);
const identityApi = useApi(identityApiRef);
// TODO: get the full entity (or at least the full entity name) from the
// identityApi
return useAsync(
() =>
catalogApi.getEntityByName({
kind: 'User',
namespace: 'default',
name: identityApi.getUserId(),
}) as Promise<UserEntity | undefined>,
[catalogApi, identityApi],
);
}