Merge pull request #17441 from AmericanAirlines/labels-for-pr

feat(plugin-github-pull-requests-board): add ability to view the labels/tags added to each PR
This commit is contained in:
Fredrik Adelöw
2023-04-25 10:25:58 +02:00
committed by GitHub
12 changed files with 151 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-github-pull-requests-board': patch
---
The `EntityTeamPullRequestsContent` and `EntityTeamPullRequestsCard` support the ability to view the labels/tags added to each PR
@@ -60,6 +60,12 @@ export const useGetPullRequestDetails = () => {
mergeable
state
reviewDecision
labels(first: 10) {
nodes {
id
name
}
}
isDraft
createdAt
author {
@@ -16,6 +16,7 @@
import React, { PropsWithChildren, FunctionComponent } from 'react';
import { Box, Paper, CardActionArea } from '@material-ui/core';
import CardHeader from './CardHeader';
import { Label } from '../../utils/types';
type Props = {
title: string;
@@ -27,6 +28,7 @@ type Props = {
repositoryName: string;
isDraft: boolean;
repositoryIsArchived: boolean;
labels?: Label[];
};
const Card: FunctionComponent<Props> = (props: PropsWithChildren<Props>) => {
@@ -40,6 +42,7 @@ const Card: FunctionComponent<Props> = (props: PropsWithChildren<Props>) => {
repositoryName,
isDraft,
repositoryIsArchived,
labels,
children,
} = props;
@@ -57,6 +60,7 @@ const Card: FunctionComponent<Props> = (props: PropsWithChildren<Props>) => {
repositoryName={repositoryName}
isDraft={isDraft}
repositoryIsArchived={repositoryIsArchived}
labels={labels}
/>
{children}
</Box>
@@ -0,0 +1,57 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 React from 'react';
import { screen } from '@testing-library/react';
import { renderInTestApp } from '@backstage/test-utils';
import CardHeader from './CardHeader';
const props = {
title: 'Fix problem',
createdAt: 'createdAt',
updatedAt: 'updatedAt',
authorName: 'user1246',
authorAvatar: 'authorAvatar',
repositoryName: 'NewRepository',
repositoryIsArchived: false,
isDraft: false,
labels: [
{
id: '01h82',
name: 'bug',
},
{
id: 'id2904',
name: 'documentation',
},
],
};
describe('<CardHeader/>', () => {
it('finds labels in PR Card Header when PR includes labels', async () => {
await renderInTestApp(<CardHeader {...props} />);
expect(screen.getByText('bug')).toBeInTheDocument();
expect(screen.getByText('documentation')).toBeInTheDocument();
});
it('does not find labels in PR Card Header when PR does not include labels', async () => {
const propsWithNoLabels = {
...props,
labels: [],
};
await renderInTestApp(<CardHeader {...propsWithNoLabels} />);
expect(screen.queryByRole('listitem')).not.toBeInTheDocument();
});
});
@@ -14,11 +14,13 @@
* limitations under the License.
*/
import React, { FunctionComponent } from 'react';
import { Typography, Box, Tooltip } from '@material-ui/core';
import { Typography, Box, Tooltip, Chip } from '@material-ui/core';
import { getElapsedTime } from '../../utils/functions';
import { UserHeader } from '../UserHeader';
import { DraftPrIcon } from '../icons/DraftPr';
import UnarchiveIcon from '@material-ui/icons/Unarchive';
import { Label } from '../../utils/types';
import { useFormClasses } from './styles';
type Props = {
title: string;
@@ -29,9 +31,12 @@ type Props = {
repositoryName: string;
isDraft: boolean;
repositoryIsArchived: boolean;
labels?: Label[];
};
const CardHeader: FunctionComponent<Props> = (props: Props) => {
const classes = useFormClasses();
const {
title,
createdAt,
@@ -41,6 +46,7 @@ const CardHeader: FunctionComponent<Props> = (props: Props) => {
repositoryName,
isDraft,
repositoryIsArchived,
labels,
} = props;
return (
@@ -80,6 +86,17 @@ const CardHeader: FunctionComponent<Props> = (props: Props) => {
</Typography>
)}
</Box>
{labels && (
<Box display="flex" alignItems="center" flexWrap="wrap" paddingTop={1}>
{labels.map(data => {
return (
<li key={data.id} className={classes.labelItem}>
<Chip color="primary" label={data.name} size="small" />
</li>
);
})}
</Box>
)}
</>
);
};
@@ -0,0 +1,39 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { makeStyles } from '@material-ui/core';
export const useFormClasses = makeStyles(() => ({
labelItem: {
display: 'flex',
position: 'relative',
boxSizing: 'border-box',
textAlign: 'left',
alignItems: 'center',
paddingTop: 2,
paddingBottom: 2,
justifyContent: 'flex-start',
textDecoration: 'none',
paddingLeft: 2,
paddingRight: 2,
},
tagBox: {
display: 'flex',
flexWrap: 'wrap',
alignItems: 'center',
maxWidth: 750,
},
}));
@@ -59,6 +59,9 @@ jest.mock('../../hooks/usePullRequestsByTeam', () => {
},
isArchived: isArchived,
},
labels: {
nodes: [],
},
isDraft: isDraft,
author: {
login: authorLogin,
@@ -113,6 +113,7 @@ const EntityTeamPullRequestsCard = (props: EntityTeamPullRequestsCardProps) => {
latestReviews,
repository,
isDraft,
labels,
},
index,
) =>
@@ -135,6 +136,7 @@ const EntityTeamPullRequestsCard = (props: EntityTeamPullRequestsCardProps) => {
repositoryName={repository.name}
repositoryIsArchived={repository.isArchived}
isDraft={isDraft}
labels={labels.nodes}
/>
),
)}
@@ -59,6 +59,9 @@ jest.mock('../../hooks/usePullRequestsByTeam', () => {
},
isArchived: isArchived,
},
labels: {
nodes: [],
},
isDraft: isDraft,
author: {
login: authorLogin,
@@ -105,6 +105,7 @@ const EntityTeamPullRequestsContent = (
latestReviews,
repository,
isDraft,
labels,
},
index,
) =>
@@ -127,6 +128,7 @@ const EntityTeamPullRequestsContent = (
repositoryName={repository.name}
repositoryIsArchived={repository.isArchived}
isDraft={isDraft}
labels={labels.nodes}
/>
),
)}
@@ -19,7 +19,7 @@ import {
getChangeRequests,
getCommentedReviews,
} from '../../utils/functions';
import { Reviews, Author } from '../../utils/types';
import { Reviews, Author, Label } from '../../utils/types';
import { Card } from '../Card';
import { UserHeaderList } from '../UserHeaderList';
@@ -33,6 +33,7 @@ type Props = {
repositoryName: string;
repositoryIsArchived: boolean;
isDraft: boolean;
labels?: Label[];
};
const PullRequestCard: FunctionComponent<Props> = (props: Props) => {
@@ -46,6 +47,7 @@ const PullRequestCard: FunctionComponent<Props> = (props: Props) => {
repositoryName,
repositoryIsArchived,
isDraft,
labels,
} = props;
const approvedReviews = getApprovedReviews(reviews);
@@ -63,6 +65,7 @@ const PullRequestCard: FunctionComponent<Props> = (props: Props) => {
prUrl={url}
isDraft={isDraft}
repositoryIsArchived={repositoryIsArchived}
labels={labels}
>
{!!approvedReviews.length && (
<UserHeaderList
@@ -76,6 +76,11 @@ export type Repository = {
isArchived: boolean;
};
export type Label = {
id: string;
name: string;
};
export type PullRequest = {
id: string;
repository: Repository;
@@ -89,6 +94,9 @@ export type PullRequest = {
state: string;
reviewDecision: ReviewDecision | null;
isDraft: boolean;
labels: {
nodes: Label[];
};
createdAt: string;
author: Author;
};