aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>2023-05-03 15:13:21 +0200
committersonartech <sonartech@sonarsource.com>2023-06-01 20:02:58 +0000
commitcf7651008dc6326cd43624d92c45ed9c9b559283 (patch)
tree9a2fe3bbd3f6e90d0ac830e590b217e351a269d8 /server
parent3bcee0f3a74e7a5b144ed438f50b4239ee7339f7 (diff)
downloadsonarqube-cf7651008dc6326cd43624d92c45ed9c9b559283.tar.gz
sonarqube-cf7651008dc6326cd43624d92c45ed9c9b559283.zip
SONAR-19174 Migrating issue code viewer header and code expander section to MIUI
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/design-system/src/components/__tests__/buttons-test.tsx41
-rw-r--r--server/sonar-web/design-system/src/components/buttons.tsx26
-rw-r--r--server/sonar-web/design-system/src/components/icons/index.ts1
-rw-r--r--server/sonar-web/design-system/src/theme/light.ts25
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx38
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx132
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx74
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/IssueSourceViewerHeader-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx4
9 files changed, 245 insertions, 98 deletions
diff --git a/server/sonar-web/design-system/src/components/__tests__/buttons-test.tsx b/server/sonar-web/design-system/src/components/__tests__/buttons-test.tsx
new file mode 100644
index 00000000000..f2a0886ea84
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/__tests__/buttons-test.tsx
@@ -0,0 +1,41 @@
+/*
+ * 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 { screen } from '@testing-library/react';
+import { render } from '../../helpers/testUtils';
+import { CodeViewerExpander } from '../buttons';
+
+it('renders CodeViewerExpander correctly when direction is UP', () => {
+ render(<CodeViewerExpander direction="UP">Hello</CodeViewerExpander>);
+ const content = screen.getByText('Hello');
+ expect(content).toHaveStyle({
+ 'border-top': 'none',
+ 'border-bottom': '1px solid rgb(221,221,221)',
+ });
+});
+
+it('renders CodeViewerExpander correctly when direction is DOWN', () => {
+ render(<CodeViewerExpander direction="DOWN">Hello</CodeViewerExpander>);
+ const content = screen.getByText('Hello');
+ expect(content).toHaveStyle({
+ 'border-bottom': 'none',
+ 'border-top': '1px solid rgb(221,221,221)',
+ });
+});
diff --git a/server/sonar-web/design-system/src/components/buttons.tsx b/server/sonar-web/design-system/src/components/buttons.tsx
index c6d2ccd3ac8..6e955a2bb04 100644
--- a/server/sonar-web/design-system/src/components/buttons.tsx
+++ b/server/sonar-web/design-system/src/components/buttons.tsx
@@ -229,3 +229,29 @@ export const BareButton = styled.button`
background-color: ${themeColor('dropdownMenuHover')};
}
`;
+
+interface CodeViewerExpanderProps {
+ direction: 'UP' | 'DOWN';
+}
+
+export const CodeViewerExpander = styled(BareButton)<CodeViewerExpanderProps>`
+ ${tw`sw-flex sw-items-center sw-gap-2`}
+ ${tw`sw-px-2 sw-py-1`}
+ ${tw`sw-code`}
+ ${tw`sw-w-full`}
+ ${tw`sw-box-border`}
+
+ color: ${themeContrast('codeLineEllipsis')};
+ background-color: ${themeColor('codeLineEllipsis')};
+
+ &:hover {
+ color: ${themeContrast('codeLineEllipsisHover')};
+ background-color: ${themeColor('codeLineEllipsisHover')};
+ }
+
+ border-top: ${(props) =>
+ props.direction === 'DOWN' ? themeBorder('default', 'codeLineBorder') : 'none'};
+
+ border-bottom: ${(props) =>
+ props.direction === 'UP' ? themeBorder('default', 'codeLineBorder') : 'none'};
+`;
diff --git a/server/sonar-web/design-system/src/components/icons/index.ts b/server/sonar-web/design-system/src/components/icons/index.ts
index 27e7dc2d9f6..492863fb778 100644
--- a/server/sonar-web/design-system/src/components/icons/index.ts
+++ b/server/sonar-web/design-system/src/components/icons/index.ts
@@ -27,6 +27,7 @@ export { ChevronRightIcon } from './ChevronRightIcon';
export { ClockIcon } from './ClockIcon';
export { CodeSmellIcon } from './CodeSmellIcon';
export { CommentIcon } from './CommentIcon';
+export { CopyIcon } from './CopyIcon';
export { DirectoryIcon } from './DirectoryIcon';
export { ExecutionFlowIcon } from './ExecutionFlowIcon';
export { FileIcon } from './FileIcon';
diff --git a/server/sonar-web/design-system/src/theme/light.ts b/server/sonar-web/design-system/src/theme/light.ts
index 084037eb643..fb4ddc7a1d0 100644
--- a/server/sonar-web/design-system/src/theme/light.ts
+++ b/server/sonar-web/design-system/src/theme/light.ts
@@ -201,6 +201,25 @@ export const lightTheme = {
codeLineCoveredUnderline: [...COLORS.green[500], 0.15],
codeLineUncoveredUnderline: [...COLORS.red[500], 0.15],
+ codeLineHover: secondary.light,
+ codeLineHighlighted: COLORS.blueGrey[100],
+ codeLineNewCodeUnderline: [...COLORS.indigo[300], 0.15],
+ codeLineMeta: COLORS.blueGrey[300],
+ codeLineMetaHover: secondary.dark,
+ codeLineDuplication: secondary.default,
+ codeLineCovered: COLORS.green[300],
+ codeLineUncovered: danger.default,
+ codeLinePartiallyCoveredA: danger.default,
+ codeLinePartiallyCoveredB: COLORS.white,
+ codeLineIssueSquiggle: danger.lighter,
+ codeLineIssuePointerBorder: COLORS.white,
+ codeLineLocationHighlighted: [...COLORS.blueGrey[200], 0.6],
+ codeLineEllipsis: COLORS.white,
+ codeLineEllipsisHover: secondary.light,
+ codeLineIssueLocation: [...danger.lighter, 0.15],
+ codeLineIssueLocationSelected: [...danger.lighter, 0.5],
+ codeLineIssueMessageTooltip: secondary.darker,
+
// checkbox
checkboxHover: COLORS.indigo[50],
checkboxCheckedHover: primary.light,
@@ -562,8 +581,14 @@ export const lightTheme = {
toggleHover: secondary.darker,
// code viewer
+ codeLineNewCodeUnderline: COLORS.indigo[500],
+ codeLineCoveredUnderline: COLORS.green[700],
+ codeLineUncoveredUnderline: COLORS.red[700],
+ codeLineEllipsis: COLORS.blueGrey[300],
+ codeLineEllipsisHover: secondary.dark,
codeLineLocationMarker: COLORS.red[900],
codeLineLocationMarkerSelected: COLORS.red[900],
+ codeLineIssueMessageTooltip: COLORS.blueGrey[25],
// code snippet
codeSnippetHighlight: danger.default,
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx
index 194cdc3f7d1..93e16726fbc 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx
@@ -17,13 +17,14 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import classNames from 'classnames';
+import { FlagMessage } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { getSources } from '../../../api/components';
import getCoverageStatus from '../../../components/SourceViewer/helpers/getCoverageStatus';
import { locationsByLine } from '../../../components/SourceViewer/helpers/indexing';
import IssueMessageBox from '../../../components/issue/IssueMessageBox';
-import { Alert } from '../../../components/ui/Alert';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
import { BranchLike } from '../../../types/branch-like';
@@ -273,20 +274,26 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
return (
<>
{issueIsClosed && (
- <Alert variant="success">
- <FormattedMessage
- id={closedIssueMessageKey}
- defaultMessage={translate(closedIssueMessageKey)}
- values={{
- status: (
- <strong>
- {translate('issue.status', issue.status)} (
- {issue.resolution ? translate('issue.resolution', issue.resolution) : '-'})
- </strong>
- ),
- }}
- />
- </Alert>
+ <FlagMessage
+ className="sw-mb-2 sw-flex"
+ variant="success"
+ ariaLabel={translate(closedIssueMessageKey)}
+ >
+ <div className="sw-block">
+ <FormattedMessage
+ id={closedIssueMessageKey}
+ defaultMessage={translate(closedIssueMessageKey)}
+ values={{
+ status: (
+ <strong>
+ {translate('issue.status', issue.status)} (
+ {issue.resolution ? translate('issue.resolution', issue.resolution) : '-'})
+ </strong>
+ ),
+ }}
+ />
+ </div>
+ </FlagMessage>
)}
<IssueSourceViewerHeader
@@ -337,6 +344,7 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
onLocationSelect={this.props.onLocationSelect}
renderDuplicationPopup={this.renderDuplicationPopup}
snippet={snippet}
+ className={classNames({ 'sw-mt-2': index !== 0 })}
/>
))}
</>
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
index cf4dbc06364..e51e0e58fb2 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
@@ -17,14 +17,24 @@
* 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 classNames from 'classnames';
+import {
+ ChevronRightIcon,
+ CopyIcon,
+ DeferredSpinner,
+ HoverLink,
+ InteractiveIcon,
+ LightLabel,
+ Link,
+ ThemeProp,
+ UnfoldIcon,
+ themeColor,
+ withTheme,
+} from 'design-system';
import * as React from 'react';
-import Link from '../../../components/common/Link';
-import { ButtonIcon } from '../../../components/controls/buttons';
-import { ClipboardIconButton } from '../../../components/controls/clipboard';
-import ExpandSnippetIcon from '../../../components/icons/ExpandSnippetIcon';
-import QualifierIcon from '../../../components/icons/QualifierIcon';
-import DeferredSpinner from '../../../components/ui/DeferredSpinner';
+import Tooltip from '../../../components/controls/Tooltip';
+import { ClipboardBase } from '../../../components/controls/clipboard';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
import { collapsedDirFromPath, fileFromPath } from '../../../helpers/path';
@@ -34,6 +44,8 @@ import { ComponentQualifier } from '../../../types/component';
import { SourceViewerFile } from '../../../types/types';
import './IssueSourceViewerHeader.css';
+export const INTERACTIVE_TOOLTIP_DELAY = 0.5;
+
export interface Props {
branchLike: BranchLike | undefined;
className?: string;
@@ -45,7 +57,7 @@ export interface Props {
sourceViewerFile: SourceViewerFile;
}
-export default function IssueSourceViewerHeader(props: Props) {
+function IssueSourceViewerHeader(props: Props & ThemeProp) {
const {
branchLike,
className,
@@ -55,64 +67,83 @@ export default function IssueSourceViewerHeader(props: Props) {
loading,
onExpand,
sourceViewerFile,
+ theme,
} = props;
const { measures, path, project, projectName, q } = sourceViewerFile;
- const projectNameLabel = (
- <>
- <QualifierIcon qualifier={ComponentQualifier.Project} /> <span>{projectName}</span>
- </>
- );
-
const isProjectRoot = q === ComponentQualifier.Project;
+ const borderColor = themeColor('codeLineBorder')({ theme });
+
+ const IssueSourceViewerStyle = styled.div`
+ border: 1px solid ${borderColor};
+ border-bottom: none;
+ `;
+
return (
- <div
+ <IssueSourceViewerStyle
className={classNames(
- 'issue-source-viewer-header display-flex-row display-flex-space-between',
+ 'sw-flex sw-justify-space-between sw-items-center sw-px-4 sw-py-3 sw-text-sm',
className
)}
role="separator"
aria-label={sourceViewerFile.path}
>
- <div className="display-flex-center flex-1">
+ <div className="sw-flex-1">
{displayProjectName && (
- <div className="spacer-right">
+ <>
{linkToProject ? (
- <a
- className="link-no-underline"
- href={getPathUrlAsString(getBranchLikeUrl(project, branchLike))}
+ <HoverLink
+ to={getPathUrlAsString(getBranchLikeUrl(project, branchLike))}
+ className="sw-mr-2"
>
- {projectNameLabel}
- </a>
+ <LightLabel>{projectName}</LightLabel>
+ </HoverLink>
) : (
- projectNameLabel
+ <LightLabel className="sw-ml-1 sw-mr-2">{projectName}</LightLabel>
)}
- </div>
+ </>
)}
{!isProjectRoot && (
<>
- <div className="spacer-right">
- <QualifierIcon qualifier={q} /> <span>{collapsedDirFromPath(path)}</span>
- <span className="component-name-file">{fileFromPath(path)}</span>
- </div>
+ {displayProjectName && <ChevronRightIcon className="sw-mr-2" />}
+ <LightLabel>
+ {collapsedDirFromPath(path)}
+ {fileFromPath(path)}
+ </LightLabel>
- <div className="spacer-right">
- <ClipboardIconButton
- className="button-link link-no-underline"
- copyValue={path}
- aria-label={translate('source_viewer.click_to_copy_filepath')}
- />
- </div>
+ <ClipboardBase>
+ {({ setCopyButton, copySuccess }) => {
+ return (
+ <Tooltip
+ mouseEnterDelay={INTERACTIVE_TOOLTIP_DELAY}
+ overlay={
+ <div className="sw-w-abs-150 sw-text-center">
+ {translate(copySuccess ? 'copied_action' : 'copy_to_clipboard')}
+ </div>
+ }
+ {...(copySuccess ? { visible: copySuccess } : undefined)}
+ >
+ <InteractiveIcon
+ Icon={CopyIcon}
+ aria-label={translate('source_viewer.click_to_copy_filepath')}
+ data-clipboard-text={path}
+ className="sw-h-6 sw-mr-2"
+ innerRef={setCopyButton}
+ />
+ </Tooltip>
+ );
+ }}
+ </ClipboardBase>
</>
)}
</div>
{!isProjectRoot && measures.issues !== undefined && (
<div
- className={classNames('flex-0 big-spacer-left', {
- 'little-spacer-right': !expandable || loading,
+ className={classNames('sw-ml-4', {
+ 'sw-mr-1': !expandable || loading,
})}
>
<Link
@@ -127,19 +158,20 @@ export default function IssueSourceViewerHeader(props: Props) {
</div>
)}
- {expandable && (
- <DeferredSpinner className="little-spacer-right" loading={loading}>
- <div className="flex-0 big-spacer-left">
- <ButtonIcon
- aria-label={translate('source_viewer.expand_all_lines')}
- className="js-actions"
- onClick={onExpand}
- >
- <ExpandSnippetIcon />
- </ButtonIcon>
- </div>
- </DeferredSpinner>
+ <DeferredSpinner className="sw-mr-1" loading={loading} />
+
+ {expandable && !loading && (
+ <div className="sw-ml-4">
+ <InteractiveIcon
+ Icon={UnfoldIcon}
+ aria-label={translate('source_viewer.expand_all_lines')}
+ className="sw-h-6"
+ onClick={onExpand}
+ />
+ </div>
)}
- </div>
+ </IssueSourceViewerStyle>
);
}
+
+export default withTheme(IssueSourceViewerHeader);
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx
index 2180aa4bf6a..7a0becf21bb 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx
@@ -18,6 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import classNames from 'classnames';
+import {
+ CodeViewerExpander,
+ ThemeProp,
+ UnfoldDownIcon,
+ UnfoldUpIcon,
+ themeColor,
+ withTheme,
+} from 'design-system';
import * as React from 'react';
import Line from '../../../components/SourceViewer/components/Line';
import { symbolsByLine } from '../../../components/SourceViewer/helpers/indexing';
@@ -26,7 +34,6 @@ import {
optimizeHighlightedSymbols,
optimizeLocationMessage,
} from '../../../components/SourceViewer/helpers/lines';
-import ExpandSnippetIcon from '../../../components/icons/ExpandSnippetIcon';
import { translate } from '../../../helpers/l10n';
import {
Duplication,
@@ -40,7 +47,7 @@ import {
import './SnippetViewer.css';
import { LINES_BELOW_ISSUE } from './utils';
-interface Props {
+export interface SnippetViewerProps {
component: SourceViewerFile;
displayLineNumberOptions?: boolean;
displaySCM?: boolean;
@@ -60,9 +67,10 @@ interface Props {
renderAdditionalChildInLine?: (line: SourceLine) => React.ReactNode | undefined;
renderDuplicationPopup: (index: number, line: number) => React.ReactNode;
snippet: SourceLine[];
+ className?: string;
}
-export default class SnippetViewer extends React.PureComponent<Props> {
+class SnippetViewer extends React.PureComponent<SnippetViewerProps & ThemeProp> {
expandBlock = (direction: ExpandDirection) => () =>
this.props.expandBlock(this.props.index, direction);
@@ -136,8 +144,16 @@ export default class SnippetViewer extends React.PureComponent<Props> {
}
render() {
- const { component, displaySCM, issue, lastSnippetOfLastGroup, locationsByLine, snippet } =
- this.props;
+ const {
+ component,
+ displaySCM,
+ issue,
+ lastSnippetOfLastGroup,
+ locationsByLine,
+ snippet,
+ theme,
+ className,
+ } = this.props;
const lastLine =
component.measures && component.measures.lines && parseInt(component.measures.lines, 10);
@@ -154,26 +170,24 @@ export default class SnippetViewer extends React.PureComponent<Props> {
const displayDuplications =
Boolean(this.props.loadDuplications) && snippet.some((s) => !!s.duplicated);
+ const borderColor = themeColor('codeLineBorder')({ theme });
+
return (
- <div className="source-viewer-code snippet">
+ <div
+ className={classNames('source-viewer-code', className)}
+ style={{ border: `1px solid ${borderColor}` }}
+ >
<div>
{snippet[0].line > 1 && (
- <div className="expand-block expand-block-above">
- <button
- aria-label={translate('source_viewer.expand_above')}
- onClick={this.expandBlock('up')}
- type="button"
- >
- <ExpandSnippetIcon />
- </button>
- </div>
+ <CodeViewerExpander
+ direction="UP"
+ className="sw-flex sw-justify-start sw-items-center sw-py-1 sw-px-2"
+ onClick={this.expandBlock('up')}
+ >
+ <UnfoldUpIcon aria-label={translate('source_viewer.expand_above')} />
+ </CodeViewerExpander>
)}
- <table
- className={classNames('source-table', {
- 'expand-up': snippet[0].line > 1,
- 'expand-down': !lastLine || snippet[snippet.length - 1].line < lastLine,
- })}
- >
+ <table>
<tbody>
{snippet.map((line, index) =>
this.renderLine({
@@ -190,18 +204,18 @@ export default class SnippetViewer extends React.PureComponent<Props> {
</tbody>
</table>
{(!lastLine || snippet[snippet.length - 1].line < lastLine) && (
- <div className="expand-block expand-block-below">
- <button
- aria-label={translate('source_viewer.expand_below')}
- onClick={this.expandBlock('down')}
- type="button"
- >
- <ExpandSnippetIcon />
- </button>
- </div>
+ <CodeViewerExpander
+ className="sw-flex sw-justify-start sw-items-center sw-py-1 sw-px-2"
+ onClick={this.expandBlock('down')}
+ direction="DOWN"
+ >
+ <UnfoldDownIcon aria-label={translate('source_viewer.expand_below')} />
+ </CodeViewerExpander>
)}
</div>
</div>
);
}
}
+
+export default withTheme(SnippetViewer);
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/IssueSourceViewerHeader-test.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/IssueSourceViewerHeader-test.tsx
index d95876d1cde..fa075dc4c4c 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/IssueSourceViewerHeader-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/IssueSourceViewerHeader-test.tsx
@@ -26,7 +26,7 @@ import IssueSourceViewerHeader, { Props } from '../IssueSourceViewerHeader';
const ui = {
expandAllLines: byRole('button', { name: 'source_viewer.expand_all_lines' }),
- projectLink: byRole('link', { name: 'qualifier.TRK MyProject' }),
+ projectLink: byRole('link', { name: 'MyProject' }),
projectName: byText('MyProject'),
viewAllIssues: byRole('link', { name: 'source_viewer.view_all_issues' }),
};
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx
index 71915f77d7a..cdbcdb84b88 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx
@@ -24,7 +24,7 @@ import { byRole } from 'testing-library-selector';
import { mockSourceLine, mockSourceViewerFile } from '../../../../helpers/mocks/sources';
import { mockIssue } from '../../../../helpers/testMocks';
import { renderComponent } from '../../../../helpers/testReactTestingUtils';
-import SnippetViewer from '../SnippetViewer';
+import SnippetViewer, { SnippetViewerProps } from '../SnippetViewer';
beforeEach(() => {
jest.clearAllMocks();
@@ -91,7 +91,7 @@ it('should render additional child in line', () => {
expect(screen.getByTestId('additional-child')).toBeInTheDocument();
});
-function renderSnippetViewer(props: Partial<SnippetViewer['props']> = {}) {
+function renderSnippetViewer(props: Partial<SnippetViewerProps> = {}) {
return renderComponent(
<SnippetViewer
component={mockSourceViewerFile()}