Browse Source

SONAR-20221 Fix issue message code highlighting

tags/10.2.0.77647
Wouter Admiraal 9 months ago
parent
commit
dc1c42dca4

+ 8
- 2
server/sonar-web/design-system/src/components/IssueMessageHighlighting.tsx View File

@@ -19,6 +19,7 @@
*/
import styled from '@emotion/styled';
import * as React from 'react';
import tw from 'twin.macro';
import { themeColor } from '../helpers';

export interface MessageFormatting {
@@ -39,7 +40,7 @@ export interface IssueMessageHighlightingProps {
export function IssueMessageHighlighting(props: IssueMessageHighlightingProps) {
const { message, messageFormattings } = props;

if (!message) {
if (message === undefined || message === '') {
return null;
}

@@ -96,7 +97,7 @@ export function IssueMessageHighlighting(props: IssueMessageHighlightingProps) {
<React.Fragment key={`${message}-${start}-${end}`}>
{message.slice(beginning, start)}
{type === MessageFormattingType.CODE ? (
<SingleLineSnippet className="sw-code sw-rounded-1 sw-py-1/2 sw-px-1 sw-border sw-border-solid">
<SingleLineSnippet className="sw-code sw-rounded-1 sw-px-1 sw-border sw-border-solid">
{message.slice(start, end)}
</SingleLineSnippet>
) : (
@@ -115,4 +116,9 @@ const SingleLineSnippet = styled.span`
background: ${themeColor('codeSnippetBackground')};
border-color: ${themeColor('codeSnippetBorder')};
color: ${themeColor('codeSnippetInline')};
${tw`sw-py-1/2`}

a & {
${tw`sw-pb-0`}
}
`;

+ 29
- 5
server/sonar-web/design-system/src/components/__tests__/__snapshots__/IssueMessageHighlighting-test.tsx.snap View File

@@ -28,18 +28,24 @@ exports[`should format the string with highlights 5`] = `
background: rgb(252,252,253);
border-color: rgb(225,230,243);
color: rgb(62,67,87);
padding-top: 0.125rem;
padding-bottom: 0.125rem;
}

a .emotion-0 {
padding-bottom: 0;
}

<span>
m
<span
class="sw-code sw-rounded-1 sw-py-1/2 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
class="sw-code sw-rounded-1 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
>
ess
</span>
a
<span
class="sw-code sw-rounded-1 sw-py-1/2 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
class="sw-code sw-rounded-1 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
>
g
</span>
@@ -62,12 +68,18 @@ exports[`should format the string with highlights 7`] = `
background: rgb(252,252,253);
border-color: rgb(225,230,243);
color: rgb(62,67,87);
padding-top: 0.125rem;
padding-bottom: 0.125rem;
}

a .emotion-0 {
padding-bottom: 0;
}

<span>
a somewhat longer message with overlapping range
<span
class="sw-code sw-rounded-1 sw-py-1/2 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
class="sw-code sw-rounded-1 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
>
s
</span>
@@ -89,12 +101,18 @@ exports[`should format the string with highlights 9`] = `
background: rgb(252,252,253);
border-color: rgb(225,230,243);
color: rgb(62,67,87);
padding-top: 0.125rem;
padding-bottom: 0.125rem;
}

a .emotion-0 {
padding-bottom: 0;
}

<span>
a
<span
class="sw-code sw-rounded-1 sw-py-1/2 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
class="sw-code sw-rounded-1 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
>
somewhat longer message
</span>
@@ -109,12 +127,18 @@ exports[`should format the string with highlights 10`] = `
background: rgb(252,252,253);
border-color: rgb(225,230,243);
color: rgb(62,67,87);
padding-top: 0.125rem;
padding-bottom: 0.125rem;
}

a .emotion-0 {
padding-bottom: 0;
}

<span>
a
<span
class="sw-code sw-rounded-1 sw-py-1/2 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
class="sw-code sw-rounded-1 sw-px-1 sw-border sw-border-solid emotion-0 emotion-1"
>
somewhat longer message with
</span>

+ 11
- 2
server/sonar-web/src/main/js/apps/issues/issues-subnavigation/SubnavigationIssue.tsx View File

@@ -18,7 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import styled from '@emotion/styled';
import { BareButton, SubnavigationItem, themeColor, themeContrast } from 'design-system';
import {
BareButton,
IssueMessageHighlighting,
SubnavigationItem,
themeColor,
themeContrast,
} from 'design-system';
import { noop } from 'lodash';
import * as React from 'react';
import { Issue } from '../../../types/types';
@@ -60,7 +66,10 @@ export default function SubnavigationIssue(props: ConciseIssueProps) {
>
<div className="sw-w-full">
<StyledIssueTitle aria-current={selected} className="sw-mb-2">
{issue.message}
<IssueMessageHighlighting
message={issue.message}
messageFormattings={issue.messageFormattings}
/>
</StyledIssueTitle>
<IssueInfo className="sw-flex sw-justify-between sw-gap-2">
<IssueItemLocationsQuantity issue={issue} />

+ 1
- 1
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx View File

@@ -21,6 +21,7 @@ import { withTheme } from '@emotion/react';
import styled from '@emotion/styled';
import {
ClipboardIconButton,
IssueMessageHighlighting,
LAYOUT_GLOBAL_NAV_HEIGHT,
LAYOUT_PROJECT_NAV_HEIGHT,
LightLabel,
@@ -33,7 +34,6 @@ import {
themeShadow,
} from 'design-system';
import React from 'react';
import { IssueMessageHighlighting } from '../../../components/issue/IssueMessageHighlighting';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
import {

+ 1
- 2
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotPrimaryLocationBox.tsx View File

@@ -17,9 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { LineFinding } from 'design-system';
import { IssueMessageHighlighting, LineFinding } from 'design-system';
import * as React from 'react';
import { IssueMessageHighlighting } from '../../../components/issue/IssueMessageHighlighting';
import { Hotspot } from '../../../types/security-hotspots';

const SCROLL_DELAY = 100;

+ 0
- 102
server/sonar-web/src/main/js/components/issue/IssueMessageHighlighting.tsx View File

@@ -1,102 +0,0 @@
/*
* 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 classNames from 'classnames';
import * as React from 'react';
import { MessageFormatting, MessageFormattingType } from '../../types/issues';

export interface IssueMessageHighlightingProps {
message?: string;
messageFormattings?: MessageFormatting[];
}

export function IssueMessageHighlighting(props: IssueMessageHighlightingProps) {
const { message, messageFormattings } = props;

if (!message) {
return null;
}

if (!(messageFormattings && messageFormattings.length > 0)) {
return <>{message}</>;
}

let previousEnd = 0;

const sanitizedFormattings = [...messageFormattings]
.sort((a, b) => a.start - b.start)
.reduce((acc, messageFormatting) => {
const { type } = messageFormatting;

if (type !== MessageFormattingType.CODE) {
return acc;
}

const { start } = messageFormatting;
let { end } = messageFormatting;

end = Math.min(message.length, end);

if (start < 0 || end === start || end < start) {
return acc;
}

if (acc.length > 0) {
const { start: previousStart, end: previousEnd } = acc[acc.length - 1];

if (start <= previousEnd) {
acc[acc.length - 1] = {
start: previousStart,
end: Math.max(previousEnd, end),
type,
};

return acc;
}
}

acc.push({ start, end, type });

return acc;
}, [] as typeof messageFormattings);

return (
<span>
{sanitizedFormattings.map(({ start, end, type }) => {
const beginning = previousEnd;
previousEnd = end;

return (
<React.Fragment key={`${message}-${start}-${end}`}>
{message.slice(beginning, start)}
<span
className={classNames({
'issue-message-highlight-CODE': type === MessageFormattingType.CODE,
})}
>
{message.slice(start, end)}
</span>
</React.Fragment>
);
})}

{message.slice(previousEnd)}
</span>
);
}

+ 0
- 74
server/sonar-web/src/main/js/components/issue/__tests__/IssueMessageHighlighting-test.tsx View File

@@ -1,74 +0,0 @@
/*
* 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 React from 'react';
import { renderComponent } from '../../../helpers/testReactTestingUtils';
import { MessageFormattingType } from '../../../types/issues';
import {
IssueMessageHighlighting,
IssueMessageHighlightingProps,
} from '../IssueMessageHighlighting';

it.each([
[undefined, undefined],
['message', undefined],
['message', []],
['message', [{ start: 1, end: 4, type: 'something else' as MessageFormattingType }]],
[
'message',
[
{ start: 5, end: 6, type: MessageFormattingType.CODE },
{ start: 1, end: 4, type: MessageFormattingType.CODE },
],
],
[
'a somewhat longer message with overlapping ranges',
[{ start: -1, end: 1, type: MessageFormattingType.CODE }],
],
[
'a somewhat longer message with overlapping ranges',
[{ start: 48, end: 70, type: MessageFormattingType.CODE }],
],
[
'a somewhat longer message with overlapping ranges',
[{ start: 0, end: 0, type: MessageFormattingType.CODE }],
],
[
'a somewhat longer message with overlapping ranges',
[
{ start: 11, end: 17, type: MessageFormattingType.CODE },
{ start: 2, end: 25, type: MessageFormattingType.CODE },
{ start: 25, end: 2, type: MessageFormattingType.CODE },
],
],
[
'a somewhat longer message with overlapping ranges',
[
{ start: 18, end: 30, type: MessageFormattingType.CODE },
{ start: 2, end: 25, type: MessageFormattingType.CODE },
],
],
])('should format the string with highlights', (message, messageFormattings) => {
const { asFragment } = renderIssueMessageHighlighting({ message, messageFormattings });
expect(asFragment()).toMatchSnapshot();
});

function renderIssueMessageHighlighting(props: Partial<IssueMessageHighlightingProps> = {}) {
return renderComponent(<IssueMessageHighlighting {...props} />);
}

+ 0
- 100
server/sonar-web/src/main/js/components/issue/__tests__/__snapshots__/IssueMessageHighlighting-test.tsx.snap View File

@@ -1,100 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should format the string with highlights 1`] = `<DocumentFragment />`;

exports[`should format the string with highlights 2`] = `
<DocumentFragment>
message
</DocumentFragment>
`;

exports[`should format the string with highlights 3`] = `
<DocumentFragment>
message
</DocumentFragment>
`;

exports[`should format the string with highlights 4`] = `
<DocumentFragment>
<span>
message
</span>
</DocumentFragment>
`;

exports[`should format the string with highlights 5`] = `
<DocumentFragment>
<span>
m
<span
class="issue-message-highlight-CODE"
>
ess
</span>
a
<span
class="issue-message-highlight-CODE"
>
g
</span>
e
</span>
</DocumentFragment>
`;

exports[`should format the string with highlights 6`] = `
<DocumentFragment>
<span>
a somewhat longer message with overlapping ranges
</span>
</DocumentFragment>
`;

exports[`should format the string with highlights 7`] = `
<DocumentFragment>
<span>
a somewhat longer message with overlapping range
<span
class="issue-message-highlight-CODE"
>
s
</span>
</span>
</DocumentFragment>
`;

exports[`should format the string with highlights 8`] = `
<DocumentFragment>
<span>
a somewhat longer message with overlapping ranges
</span>
</DocumentFragment>
`;

exports[`should format the string with highlights 9`] = `
<DocumentFragment>
<span>
a
<span
class="issue-message-highlight-CODE"
>
somewhat longer message
</span>
with overlapping ranges
</span>
</DocumentFragment>
`;

exports[`should format the string with highlights 10`] = `
<DocumentFragment>
<span>
a
<span
class="issue-message-highlight-CODE"
>
somewhat longer message with
</span>
overlapping ranges
</span>
</DocumentFragment>
`;

+ 1
- 2
server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx View File

@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { StandoutLink } from 'design-system';
import { IssueMessageHighlighting, StandoutLink } from 'design-system';
import * as React from 'react';
import { ComponentContext } from '../../../app/components/componentContext/ComponentContext';
import { areMyIssuesSelected, parseQuery, serializeQuery } from '../../../apps/issues/utils';
@@ -27,7 +27,6 @@ import { getComponentIssuesUrl, getIssuesUrl } from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
import { Issue } from '../../../types/types';
import { useLocation } from '../../hoc/withRouter';
import { IssueMessageHighlighting } from '../IssueMessageHighlighting';

export interface IssueMessageProps {
issue: Issue;

+ 1
- 2
server/sonar-web/src/main/js/components/locations/SingleFileLocationNavigator.tsx View File

@@ -19,12 +19,11 @@
*/
import styled from '@emotion/styled';
import classNames from 'classnames';
import { LocationMarker, StyledMarker, themeColor } from 'design-system';
import { IssueMessageHighlighting, LocationMarker, StyledMarker, themeColor } from 'design-system';
import * as React from 'react';
import { translateWithParameters } from '../../helpers/l10n';
import { MessageFormatting } from '../../types/issues';
import LocationMessage from '../common/LocationMessage';
import { IssueMessageHighlighting } from '../issue/IssueMessageHighlighting';
import './SingleFileLocationNavigator.css';

interface Props {

Loading…
Cancel
Save