aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorViktor Vorona <viktor.vorona@sonarsource.com>2023-08-04 16:48:35 +0200
committersonartech <sonartech@sonarsource.com>2023-08-18 20:02:48 +0000
commitf25c6ec28647f3359ad72460039632eb8e2a8f2a (patch)
treebfda79da8128795a887356d0c5c2622a13acafea /server/sonar-web
parentd9cedb7e0e1bd180cdefdb8ec55325795dd9670c (diff)
downloadsonarqube-f25c6ec28647f3359ad72460039632eb8e2a8f2a.tar.gz
sonarqube-f25c6ec28647f3359ad72460039632eb8e2a8f2a.zip
SONAR-20023 Issue details guide
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/design-system/src/components/buttons/BareButtons.tsx2
-rw-r--r--server/sonar-web/design-system/src/components/buttons/ButtonLink.tsx39
-rw-r--r--server/sonar-web/design-system/src/components/buttons/__tests__/ButtonLink-test.tsx32
-rw-r--r--server/sonar-web/design-system/src/components/buttons/index.ts20
-rw-r--r--server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssueGuide.tsx (renamed from server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx)15
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/common/Guide.tsx8
-rw-r--r--server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx2
13 files changed, 134 insertions, 41 deletions
diff --git a/server/sonar-web/design-system/src/components/buttons/BareButtons.tsx b/server/sonar-web/design-system/src/components/buttons/BareButtons.tsx
index 2d3842daa3b..4ace6ccd48e 100644
--- a/server/sonar-web/design-system/src/components/buttons/BareButtons.tsx
+++ b/server/sonar-web/design-system/src/components/buttons/BareButtons.tsx
@@ -83,7 +83,7 @@ export const LineSCMStyled = styled(BareButton)`
${tw`sw-cursor-pointer`}
${tw`sw-w-full sw-h-full`}
-&:hover {
+ &:hover {
color: ${themeColor('codeLineMetaHover')};
}
`;
diff --git a/server/sonar-web/design-system/src/components/buttons/ButtonLink.tsx b/server/sonar-web/design-system/src/components/buttons/ButtonLink.tsx
new file mode 100644
index 00000000000..24aab4221f9
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/buttons/ButtonLink.tsx
@@ -0,0 +1,39 @@
+/*
+ * 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 styled from '@emotion/styled';
+import tw from 'twin.macro';
+import { themeBorder, themeColor } from '../../helpers';
+import { BareButton } from './BareButtons';
+
+export const ButtonLink = styled(BareButton)`
+ color: ${themeColor('linkDefault')};
+ border-bottom: ${themeBorder('default', 'linkDefault')};
+
+ ${tw`sw-font-semibold`}
+ ${tw`sw-no-underline`}
+
+ &:hover,
+ &:focus,
+ &:active {
+ color: ${themeColor('linkActive')};
+ border-bottom: ${themeBorder('default', 'linkDefault')};
+ }
+`;
diff --git a/server/sonar-web/design-system/src/components/buttons/__tests__/ButtonLink-test.tsx b/server/sonar-web/design-system/src/components/buttons/__tests__/ButtonLink-test.tsx
new file mode 100644
index 00000000000..32e38e044a3
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/buttons/__tests__/ButtonLink-test.tsx
@@ -0,0 +1,32 @@
+/*
+ * 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 { render, screen } from '@testing-library/react';
+import { ButtonLink } from '../ButtonLink';
+
+it('renders ButtonLink correctly', () => {
+ render(<ButtonLink>Hello</ButtonLink>);
+ const content = screen.getByRole('button', { name: 'Hello' });
+ expect(content).toHaveStyle({
+ all: 'unset',
+ color: 'rgb(93,108,208)',
+ 'border-bottom': '1px solid rgb(93,108,208)',
+ });
+});
diff --git a/server/sonar-web/design-system/src/components/buttons/index.ts b/server/sonar-web/design-system/src/components/buttons/index.ts
index a813c49afc8..2744f58adda 100644
--- a/server/sonar-web/design-system/src/components/buttons/index.ts
+++ b/server/sonar-web/design-system/src/components/buttons/index.ts
@@ -17,27 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-/*
- * 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.
- */
export * from './BareButtons';
export * from './Button';
+export * from './ButtonLink';
export * from './ButtonPrimary';
export * from './ButtonSecondary';
export * from './DangerButtonPrimary';
diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
index b9b53aeb4e5..79845e3c7d3 100644
--- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
@@ -809,4 +809,38 @@ describe('guide', () => {
expect(ui.loading.query()).not.toBeInTheDocument();
expect(ui.guidePopup.query()).not.toBeInTheDocument();
});
+
+ it('should show guide on issue page', async () => {
+ const user = userEvent.setup();
+ renderProjectIssuesApp(
+ 'project/issues?issues=issue11&open=issue11&id=myproject',
+ undefined,
+ mockCurrentUser({ isLoggedIn: true })
+ );
+
+ expect(await ui.guidePopup.find()).toBeInTheDocument();
+ expect(ui.guidePopup.get()).toHaveTextContent('guiding.step_x_of_y.1.5');
+
+ await user.click(ui.guidePopup.byRole('button', { name: 'next' }).get());
+
+ expect(ui.guidePopup.get()).toHaveTextContent('guiding.step_x_of_y.2.5');
+
+ await user.click(ui.guidePopup.byRole('button', { name: 'next' }).get());
+
+ expect(ui.guidePopup.get()).toHaveTextContent('guiding.step_x_of_y.3.5');
+
+ await user.click(ui.guidePopup.byRole('button', { name: 'next' }).get());
+
+ expect(ui.guidePopup.get()).toHaveTextContent('guiding.step_x_of_y.4.5');
+
+ await user.click(ui.guidePopup.byRole('button', { name: 'next' }).get());
+
+ expect(ui.guidePopup.get()).toHaveTextContent('guiding.step_x_of_y.5.5');
+
+ expect(ui.guidePopup.byRole('button', { name: 'Next' }).query()).not.toBeInTheDocument();
+
+ await user.click(ui.guidePopup.byRole('button', { name: 'close' }).get());
+
+ expect(ui.guidePopup.query()).not.toBeInTheDocument();
+ });
});
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueGuide.tsx
index 5b9014e1486..438394a24fe 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssueGuide.tsx
@@ -28,9 +28,10 @@ import { NoticeType } from '../../../types/users';
interface Props {
run?: boolean;
+ bottom?: boolean;
}
-export default function IssueListGuide({ run }: Props) {
+export default function IssueGuide({ run, bottom }: Props) {
const { currentUser, updateDismissedNotices } = React.useContext(CurrentUserContext);
if (!currentUser.isLoggedIn || currentUser.dismissedNotices[NoticeType.ISSUE_GUIDE]) {
@@ -73,27 +74,27 @@ export default function IssueListGuide({ run }: Props) {
const steps = [
{
- target: '[data-guiding-id="issuelist-1"]',
+ target: '[data-guiding-id="issue-1"]',
content: constructContent('guiding.issue_list.1.content.1', 'guiding.issue_list.1.content.2'),
title: translate('guiding.issue_list.1.title'),
- placement: 'right' as const,
+ placement: bottom ? ('bottom' as const) : ('right' as const),
...commonStepProps,
},
{
- target: '[data-guiding-id="issuelist-2"]',
+ target: '[data-guiding-id="issue-2"]',
content: constructContent('guiding.issue_list.2.content.1', 'guiding.issue_list.2.content.2'),
title: translate('guiding.issue_list.2.title'),
- placement: 'right' as const,
+ placement: bottom ? ('bottom' as const) : ('right' as const),
...commonStepProps,
},
{
- target: '[data-guiding-id="issuelist-3"]',
+ target: '[data-guiding-id="issue-3"]',
content: constructContent('guiding.issue_list.3.content.1', 'guiding.issue_list.3.content.2'),
title: translate('guiding.issue_list.3.title'),
...commonStepProps,
},
{
- target: '[data-guiding-id="issuelist-4"]',
+ target: '[data-guiding-id="issue-4"]',
content: constructContent(
'guiding.issue_list.4.content.1',
'guiding.issue_list.4.content.2',
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
index eaa850eb853..ec36bbd01a8 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
@@ -170,10 +170,13 @@ export default class IssueHeader extends React.PureComponent<Props, State> {
return (
<header className="sw-flex sw-mb-6">
<div className="sw-mr-8 sw-flex-1">
- <CleanCodeAttributePill
- cleanCodeAttributeCategory={issue.cleanCodeAttributeCategory}
- cleanCodeAttribute={issue.cleanCodeAttribute}
- />
+ <div data-guiding-id="issue-1" className="sw-w-fit">
+ <CleanCodeAttributePill
+ cleanCodeAttributeCategory={issue.cleanCodeAttributeCategory}
+ cleanCodeAttribute={issue.cleanCodeAttribute}
+ />
+ </div>
+
<div className="sw-flex sw-items-center sw-my-2">
<PageContentFontWrapper className="sw-body-md-highlight" as="h1">
<IssueMessageHighlighting
@@ -194,7 +197,7 @@ export default class IssueHeader extends React.PureComponent<Props, State> {
</div>
<div className="sw-flex sw-items-center">
<Note>{translate('issue.software_qualities.label')}</Note>
- <ul className="sw-ml-1 sw-flex sw-gap-2">
+ <ul className="sw-ml-1 sw-flex sw-gap-2" data-guiding-id="issue-2">
{issue.impacts.map(({ severity, softwareQuality }) => (
<li key={softwareQuality}>
<SoftwareImpactPill severity={severity} quality={softwareQuality} />
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
index 8c2c4279915..f600c62e668 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
@@ -100,7 +100,7 @@ import {
shouldOpenStandardsFacet,
} from '../utils';
import BulkChangeModal, { MAX_PAGE_SIZE } from './BulkChangeModal';
-import IssueListGuide from './IssueListGuide';
+import IssueGuide from './IssueGuide';
import IssueReviewHistoryAndComments from './IssueReviewHistoryAndComments';
import IssuesList from './IssuesList';
import IssuesSourceViewer from './IssuesSourceViewer';
@@ -1313,7 +1313,7 @@ export class App extends React.PureComponent<Props, State> {
<PageContentFontWrapper className="sw-body-sm">
<div className="sw-w-full sw-flex" id="issues-page">
<Suggestions suggestions="issues" />
- <IssueListGuide run={!open && !component?.needIssueSync && issues.length > 0} />
+ <IssueGuide run={!open && !component?.needIssueSync && issues.length > 0} />
{openIssue ? (
<Helmet
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx
index 48ed584f7c6..e9a92e2469a 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx
@@ -32,7 +32,7 @@ export function AttributeCategoryFacet(props: Props) {
const { categories = [], ...rest } = props;
return (
- <div data-guiding-id="issuelist-1">
+ <div data-guiding-id="issue-1">
<SimpleListStyleFacet
property="cleanCodeAttributeCategory"
itemNamePrefix="issue.clean_code_attribute_category"
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx
index d0dec615114..92e47f10a79 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx
@@ -32,7 +32,7 @@ export function SoftwareQualityFacet(props: Props) {
const { qualities = [], ...rest } = props;
return (
- <div data-guiding-id="issuelist-2">
+ <div data-guiding-id="issue-2">
<SimpleListStyleFacet
property="impactSoftwareQuality"
itemNamePrefix="issue.software_quality"
diff --git a/server/sonar-web/src/main/js/components/common/Guide.tsx b/server/sonar-web/src/main/js/components/common/Guide.tsx
index cf199c55485..08a8490920c 100644
--- a/server/sonar-web/src/main/js/components/common/Guide.tsx
+++ b/server/sonar-web/src/main/js/components/common/Guide.tsx
@@ -18,8 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import {
+ ButtonLink,
ButtonPrimary,
- ButtonSecondary,
CloseIcon,
PopupWrapper,
PopupZLevel,
@@ -66,9 +66,9 @@ function TooltipComponent({
<b>{translateWithParameters('guiding.step_x_of_y', index + 1, size)}</b>
<div>
{index > 0 && (
- <ButtonSecondary className="sw-mr-2" {...backProps}>
+ <ButtonLink className="sw-mr-4" {...backProps}>
{backProps.title}
- </ButtonSecondary>
+ </ButtonLink>
)}
{continuous && !isLastStep && (
<ButtonPrimary {...primaryProps}>{primaryProps.title}</ButtonPrimary>
@@ -90,7 +90,7 @@ export function Guide(props: Props) {
tooltipComponent={TooltipComponent}
locale={{
skip: translate('skip'),
- back: translate('back'),
+ back: translate('go_back'),
close: translate('close'),
next: translate('next'),
}}
diff --git a/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx b/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx
index 20c4ca4ef4e..a75281558c8 100644
--- a/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx
+++ b/server/sonar-web/src/main/js/components/rules/IssueTabViewer.tsx
@@ -27,6 +27,7 @@ import { dismissNotice } from '../../api/users';
import { CurrentUserContextInterface } from '../../app/components/current-user/CurrentUserContext';
import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
import { RuleDescriptionSections } from '../../apps/coding-rules/rule';
+import IssueGuide from '../../apps/issues/components/IssueGuide';
import IssueHeader from '../../apps/issues/components/IssueHeader';
import StyledHeader from '../../apps/issues/components/StyledHeader';
import { fillBranchLike } from '../../helpers/branch-like';
@@ -353,6 +354,7 @@ export class IssueTabViewer extends React.PureComponent<IssueTabViewerProps, Sta
}}
className="sw-overflow-y-auto"
>
+ <IssueGuide run bottom />
<StyledHeader headerHeight={this.headerNode?.clientHeight ?? 0} className="sw-z-normal">
<div className="sw-p-6 sw-pb-4" ref={(node) => (this.headerNode = node)}>
<IssueHeader
diff --git a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx
index 1a2d1f31cf5..1f2ea70fba9 100644
--- a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx
+++ b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx
@@ -62,7 +62,7 @@ export default function SoftwareImpactPill(props: Props) {
>
<Pill className={classNames('sw-flex sw-gap-1 sw-items-center', className)} variant={variant}>
{translate('issue.software_quality', quality)}
- <SoftwareImpactSeverityIcon severity={severity} />
+ <SoftwareImpactSeverityIcon severity={severity} data-guiding-id="issue-3" />
</Pill>
</DocumentationTooltip>
);