expect(screen.getByText('issues.not_all_issue_show')).toBeInTheDocument();
});
+
+ it('should show fixed issues message', async () => {
+ renderProjectIssuesApp('project/issues?id=my-project&fixedInPullRequest=01');
+
+ expect(await ui.fixedIssuesHeading.find()).toBeInTheDocument();
+ });
});
describe('navigation', () => {
issueStatuses: [IssueStatus.Accepted, IssueStatus.Confirmed],
tags: ['a', 'b'],
types: ['a', 'b'],
+ fixedInPullRequest: '',
}),
).toStrictEqual({
assignees: 'a,b',
issueStatuses: [],
tags: [],
types: [],
+ fixedInPullRequest: '',
});
});
import '../../../components/search-navigator.css';
import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
import Spinner from '../../../components/ui/Spinner';
-import { fillBranchLike, getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like';
+import {
+ fillBranchLike,
+ getBranchLikeQuery,
+ isPullRequest,
+ isSameBranchLike,
+} from '../../../helpers/branch-like';
import handleRequiredAuthentication from '../../../helpers/handleRequiredAuthentication';
import { parseIssueFromResponse } from '../../../helpers/issues';
import { isInput, isShortcut } from '../../../helpers/keyboardEventHelpers';
import IssueNewStatusAndTransitionGuide from './IssueNewStatusAndTransitionGuide';
import IssueReviewHistoryAndComments from './IssueReviewHistoryAndComments';
import IssuesList from './IssuesList';
+import IssuesListTitle from './IssuesListTitle';
import IssuesSourceViewer from './IssuesSourceViewer';
import NoIssues from './NoIssues';
import NoMyIssues from './NoMyIssues';
interface Props extends WithIndexationContextProps {
branchLike?: BranchLike;
+ branchLikes?: BranchLike[];
component?: Component;
currentUser: CurrentUser;
isFetchingBranch?: boolean;
}
renderList() {
- const { branchLike, component, currentUser } = this.props;
- const { issues, loading, loadingMore, openIssue, paging } = this.state;
+ const { branchLike, component, currentUser, branchLikes } = this.props;
+ const { issues, loading, loadingMore, openIssue, paging, query } = this.state;
const selectedIndex = this.getSelectedIndex();
const selectedIssue = selectedIndex !== undefined ? issues[selectedIndex] : undefined;
return (
<div>
- <h2 className="sw-sr-only">{translate('list_of_issues')}</h2>
+ <IssuesListTitle
+ fixedInPullRequest={query.fixedInPullRequest}
+ pullRequests={branchLikes?.filter(isPullRequest) ?? []}
+ component={component}
+ />
{issues.length > 0 && (
<IssuesList
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { DiscreetLink, LightPrimary, PullRequestIcon, SubTitle } from 'design-system';
+import React from 'react';
+import { useIntl } from 'react-intl';
+import { getPullRequestUrl } from '../../../helpers/urls';
+import { PullRequest } from '../../../types/branch-like';
+import { Component } from '../../../types/types';
+
+interface IssuesListTitleProps {
+ component?: Component;
+ fixedInPullRequest: string;
+ pullRequests: PullRequest[];
+}
+
+export default function IssuesListTitle({
+ fixedInPullRequest,
+ pullRequests,
+ component,
+}: Readonly<IssuesListTitleProps>) {
+ const intl = useIntl();
+ const pullRequest = pullRequests.find((pr) => pr.key === fixedInPullRequest);
+ const prSummaryUrl = getPullRequestUrl(component?.key ?? '', fixedInPullRequest);
+
+ return pullRequest && !component?.needIssueSync ? (
+ <>
+ <SubTitle className="sw-mt-6 sw-mb-2">
+ {intl.formatMessage({ id: 'issues.fixed_issues' })}
+ </SubTitle>
+ <LightPrimary className="sw-flex sw-items-center sw-gap-1 sw-mb-2">
+ {intl.formatMessage(
+ { id: 'issues.fixed_issues.description' },
+ {
+ pullRequest: (
+ <>
+ <PullRequestIcon />
+ <DiscreetLink to={prSummaryUrl} className="sw-mt-[3px]">
+ {pullRequest.title}
+ </DiscreetLink>
+ </>
+ ),
+ },
+ )}
+ </LightPrimary>
+ </>
+ ) : (
+ <h2 className="sw-sr-only">{intl.formatMessage({ id: 'list_of_issues' })}</h2>
+ );
+}
SoftwareImpactSeverity,
SoftwareQuality,
} from '../../types/clean-code-taxonomy';
+import { Feature } from '../../types/features';
import { Component } from '../../types/types';
import { NoticeType } from '../../types/users';
import IssuesApp from './components/IssuesApp';
issueItems: byRole('region'),
+ fixedIssuesHeading: byRole('heading', { level: 2, name: 'issues.fixed_issues' }),
+
issueItem1: byRole('region', { name: 'Issue with no location message' }),
issueItem2: byRole('region', { name: 'FlowIssue' }),
issueItem3: byRole('region', { name: 'Issue on file' }),
{projectIssuesRoutes()}
</Route>
),
- { navigateTo, currentUser },
+ { navigateTo, currentUser, featureList: [Feature.BranchSupport] },
{ component: mockComponent(overrides) },
);
}
cwe: string[];
directories: string[];
files: string[];
+ fixedInPullRequest: string;
impactSeverities: SoftwareImpactSeverity[];
impactSoftwareQualities: SoftwareQuality[];
issues: string[];
tags: parseAsArray(query.tags, parseAsString),
types: parseAsArray(query.types, parseAsString),
codeVariants: parseAsArray(query.codeVariants, parseAsString),
+ fixedInPullRequest: parseAsString(query.fixedInPullRequest),
};
}
cwe: serializeStringArray(query.cwe),
directories: serializeStringArray(query.directories),
files: serializeStringArray(query.files),
+ fixedInPullRequest: serializeString(query.fixedInPullRequest),
issues: serializeStringArray(query.issues),
languages: serializeStringArray(query.languages),
owaspTop10: serializeStringArray(query.owaspTop10),
});
const fixedUrl = getComponentIssuesUrl(component.key, {
branch: pullRequest.target,
- fixedInPR: pullRequest.key,
+ fixedInPullRequest: pullRequest.key,
});
const acceptedUrl = getComponentIssuesUrl(component.key, {
...getBranchLikeQuery(pullRequest),
cwe: [],
directories: [],
files: [],
+ fixedInPullRequest: '',
issues: [],
languages: [],
owaspTop10: [],
export function useBranchesQuery(component?: LightComponent, refetchInterval?: number) {
const features = useContext(AvailableFeaturesContext);
const key = useBranchesQueryKey(InnerState.Details, component?.key);
+
return useQuery({
queryKey: key,
queryFn: async ({ queryKey: [, key, prOrBranch, name] }) => {
issues.my_issues=My Issues
issues.no_my_issues=There are no issues assigned to you.
issues.no_issues=No Issues. Hooray!
+issues.fixed_issues=Fixed issues
+issues.fixed_issues.description=List of issues that will be fixed by {pullRequest}
issues.x_more_locations=+ {0} more locations
issues.not_all_issue_show=Not all issues are included
issues.not_all_issue_show_why=You do not have access to all projects in this portfolio