diff options
Diffstat (limited to 'server/sonar-web')
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> ); |