From f25c6ec28647f3359ad72460039632eb8e2a8f2a Mon Sep 17 00:00:00 2001 From: Viktor Vorona Date: Fri, 4 Aug 2023 16:48:35 +0200 Subject: [PATCH] SONAR-20023 Issue details guide --- .../src/components/buttons/BareButtons.tsx | 2 +- .../src/components/buttons/ButtonLink.tsx | 39 +++++++++++++++++++ .../buttons/__tests__/ButtonLink-test.tsx | 32 +++++++++++++++ .../src/components/buttons/index.ts | 20 +--------- .../js/apps/issues/__tests__/IssuesApp-it.tsx | 34 ++++++++++++++++ .../{IssueListGuide.tsx => IssueGuide.tsx} | 15 +++---- .../js/apps/issues/components/IssueHeader.tsx | 13 ++++--- .../js/apps/issues/components/IssuesApp.tsx | 4 +- .../issues/sidebar/AttributeCategoryFacet.tsx | 2 +- .../issues/sidebar/SoftwareQualityFacet.tsx | 2 +- .../src/main/js/components/common/Guide.tsx | 8 ++-- .../js/components/rules/IssueTabViewer.tsx | 2 + .../components/shared/SoftwareImpactPill.tsx | 2 +- .../resources/org/sonar/l10n/core.properties | 1 + 14 files changed, 135 insertions(+), 41 deletions(-) create mode 100644 server/sonar-web/design-system/src/components/buttons/ButtonLink.tsx create mode 100644 server/sonar-web/design-system/src/components/buttons/__tests__/ButtonLink-test.tsx rename server/sonar-web/src/main/js/apps/issues/components/{IssueListGuide.tsx => IssueGuide.tsx} (90%) 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(Hello); + 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 @@ -1,22 +1,3 @@ -/* - * 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. - */ /* * SonarQube * Copyright (C) 2009-2023 SonarSource SA @@ -38,6 +19,7 @@ */ 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 similarity index 90% rename from server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx rename to 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 { return (
- +
+ +
+
{
{translate('issue.software_qualities.label')} -
    +
      {issue.impacts.map(({ severity, 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 {
      - 0} /> + 0} /> {openIssue ? ( +
      +
      {translateWithParameters('guiding.step_x_of_y', index + 1, size)}
      {index > 0 && ( - + {backProps.title} - + )} {continuous && !isLastStep && ( {primaryProps.title} @@ -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 +
      (this.headerNode = node)}> {translate('issue.software_quality', quality)} - + ); diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index b55fe4bb312..52c313e359a 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -96,6 +96,7 @@ format=Format from=From global=Global github=GitHub +go_back=Go back help=Help here=here hide=Hide -- 2.39.5