]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19613 Use new CodeSyntaxHighlighter
authorViktor Vorona <viktor.vorona@sonarsource.com>
Mon, 26 Jun 2023 08:35:18 +0000 (10:35 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 26 Jun 2023 20:03:55 +0000 (20:03 +0000)
server/sonar-web/design-system/src/components/CodeSnippet.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/CodeSyntaxHighlighter.tsx
server/sonar-web/design-system/src/components/__tests__/CodeSnippet-test.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap [new file with mode: 0644]
server/sonar-web/design-system/src/components/index.ts
server/sonar-web/src/main/js/apps/projectInformation/ProjectInformationApp.tsx
server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaKey.tsx
server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx

diff --git a/server/sonar-web/design-system/src/components/CodeSnippet.tsx b/server/sonar-web/design-system/src/components/CodeSnippet.tsx
new file mode 100644 (file)
index 0000000..363ccca
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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 classNames from 'classnames';
+import tw from 'twin.macro';
+import { themeBorder, themeColor } from '../helpers/theme';
+import { isDefined } from '../helpers/types';
+import { CodeSyntaxHighlighter } from './CodeSyntaxHighlighter';
+import { ClipboardButton } from './clipboard';
+
+interface Props {
+  className?: string;
+  isOneLine?: boolean;
+  join?: string;
+  language?: string;
+  noCopy?: boolean;
+  render?: string;
+  snippet: string | Array<string | undefined>;
+  wrap?: boolean;
+}
+
+// keep this "useless" concatenation for the readability reason
+// eslint-disable-next-line no-useless-concat
+const s = ' \\' + '\n  ';
+
+export function CodeSnippet(props: Props) {
+  const { className, isOneLine, join = s, language, noCopy, render, snippet, wrap } = props;
+  const snippetArray = Array.isArray(snippet) ? snippet.filter(isDefined) : [snippet];
+  const finalSnippet = isOneLine ? snippetArray.join(' ') : snippetArray.join(join);
+
+  const isSimpleOneLine = isOneLine && noCopy;
+
+  const copyButton = isOneLine ? (
+    <StyledSingleLineClipboardButton copyValue={finalSnippet} />
+  ) : (
+    <StyledClipboardButton copyValue={finalSnippet} />
+  );
+
+  return (
+    <Wrapper
+      className={classNames(
+        {
+          'code-snippet-highlighted-oneline': isOneLine,
+          'code-snippet-simple-oneline': isSimpleOneLine,
+        },
+        className,
+        'fs-mask'
+      )}
+    >
+      {!noCopy && copyButton}
+      <CodeSyntaxHighlighter
+        className={classNames({ 'sw-pr-24': !noCopy, 'sw-flex': !noCopy })}
+        htmlAsString={render ?? finalSnippet}
+        language={language}
+        wrap={wrap}
+      />
+    </Wrapper>
+  );
+}
+
+const Wrapper = styled.div`
+  background-color: ${themeColor('codeSnippetBackground')};
+  border: ${themeBorder('default', 'codeSnippetBorder')};
+
+  ${tw`sw-rounded-2`}
+  ${tw`sw-relative`}
+  ${tw`sw-my-2`}
+
+  &.code-snippet-simple-oneline {
+    ${tw`sw-my-0`}
+    ${tw`sw-rounded-1`}
+  }
+`;
+
+const StyledClipboardButton = styled(ClipboardButton)`
+  ${tw`sw-select-none`}
+  ${tw`sw-body-sm`}
+  ${tw`sw-top-6 sw-right-6`}
+  ${tw`sw-absolute`}
+
+  .code-snippet-highlighted-oneline & {
+    ${tw`sw-bottom-2`}
+  }
+`;
+
+const StyledSingleLineClipboardButton = styled(StyledClipboardButton)`
+  ${tw`sw-top-6 sw-bottom-6`}
+`;
index 6243efc4e621d508001e93dd1f6036eeaed7da63..9ae139c9f8d75452f7051d2d137e4a89c84d1e58 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 import styled from '@emotion/styled';
+import classNames from 'classnames';
 import hljs from 'highlight.js';
 import apex from 'highlightjs-apex';
 import abap from 'highlightjs-sap-abap';
@@ -40,6 +41,7 @@ interface Props {
   className?: string;
   htmlAsString: string;
   language?: string;
+  wrap?: boolean;
 }
 
 const CODE_REGEXP = '<(code|pre)\\b([^>]*?)>(.+?)<\\/\\1>';
@@ -52,7 +54,8 @@ const htmlDecode = (escapedCode: string) => {
   return doc.documentElement.textContent ?? '';
 };
 
-export function CodeSyntaxHighlighter({ className, htmlAsString, language }: Props) {
+export function CodeSyntaxHighlighter(props: Props) {
+  const { className, htmlAsString, language, wrap } = props;
   let highlightedHtmlAsString = htmlAsString;
 
   htmlAsString.match(GLOBAL_REGEXP)?.forEach((codeBlock) => {
@@ -83,7 +86,7 @@ export function CodeSyntaxHighlighter({ className, htmlAsString, language }: Pro
 
   return (
     <StyledSpan
-      className={`hljs ${className ?? ''}`}
+      className={classNames(`hljs ${className ?? ''}`, { 'code-wrap': wrap })}
       // Safe: value is escaped by highlight.js
       // eslint-disable-next-line react/no-danger
       dangerouslySetInnerHTML={{ __html: highlightedHtmlAsString }}
@@ -140,6 +143,11 @@ const StyledSpan = styled.span`
     color: ${themeColor('codeSnippetPreprocessingDirective')};
   }
 
+  &.code-wrap {
+    ${tw`sw-whitespace-pre-wrap`}
+    ${tw`sw-break-all`}
+  }
+
   mark {
     ${tw`sw-font-regular`}
     ${tw`sw-p-1`}
diff --git a/server/sonar-web/design-system/src/components/__tests__/CodeSnippet-test.tsx b/server/sonar-web/design-system/src/components/__tests__/CodeSnippet-test.tsx
new file mode 100644 (file)
index 0000000..87bca8a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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 { HelmetProvider } from 'react-helmet-async';
+import { renderWithContext } from '../../helpers/testUtils';
+import { FCProps } from '../../types/misc';
+import { CodeSnippet } from '../CodeSnippet';
+
+it('should show full size when multiline with no editting', () => {
+  const { container } = setupWithProps();
+  const copyButton = screen.getByRole('button', { name: 'Copy' });
+  expect(copyButton).toHaveStyle('top: 1.5rem');
+  expect(container).toMatchSnapshot();
+});
+
+it('should show reduced size when single line with no editting', () => {
+  const { container } = setupWithProps({ isOneLine: true, snippet: 'foobar' });
+  const copyButton = screen.getByRole('button', { name: 'Copy' });
+  expect(copyButton).toHaveStyle('top: 1.5rem');
+  expect(container).toMatchSnapshot();
+});
+
+function setupWithProps(props: Partial<FCProps<typeof CodeSnippet>> = {}) {
+  return renderWithContext(
+    <HelmetProvider>
+      <CodeSnippet snippet={'foo\nbar'} {...props} />
+    </HelmetProvider>
+  );
+}
diff --git a/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/CodeSnippet-test.tsx.snap
new file mode 100644 (file)
index 0000000..589692f
--- /dev/null
@@ -0,0 +1,401 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should show full size when multiline with no editting 1`] = `
+.emotion-0 {
+  background-color: rgb(252,252,253);
+  border: 1px solid rgb(225,230,243);
+  border-radius: 0.5rem;
+  position: relative;
+  margin-top: 0.5rem;
+  margin-bottom: 0.5rem;
+}
+
+.emotion-0.code-snippet-simple-oneline {
+  margin-top: 0;
+  margin-bottom: 0;
+  border-radius: 0.25rem;
+}
+
+.emotion-4 {
+  box-sizing: border-box;
+  -webkit-text-decoration: none;
+  text-decoration: none;
+  outline: none;
+  border: var(--border);
+  color: var(--color);
+  background-color: var(--background);
+  -webkit-transition: background-color 0.2s ease,outline 0.2s ease;
+  transition: background-color 0.2s ease,outline 0.2s ease;
+  display: -webkit-inline-box;
+  display: -webkit-inline-flex;
+  display: -ms-inline-flexbox;
+  display: inline-flex;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  height: 2.25rem;
+  font-family: Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+  font-size: 0.875rem;
+  line-height: 1.25rem;
+  font-weight: 600;
+  padding-left: 1rem;
+  padding-right: 1rem;
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+  border-radius: 0.5rem;
+  cursor: pointer;
+  --background: rgb(255,255,255);
+  --backgroundHover: rgb(239,242,249);
+  --color: rgb(62,67,87);
+  --focus: rgba(197,205,223,0.2);
+  --border: 1px solid rgb(197,205,223);
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  font-family: Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+  font-size: 0.875rem;
+  line-height: 1.25rem;
+  font-weight: 400;
+  right: 1.5rem;
+  top: 1.5rem;
+  position: absolute;
+}
+
+.emotion-4:hover {
+  color: var(--color);
+  background-color: var(--backgroundHover);
+}
+
+.emotion-4:focus,
+.emotion-4:active {
+  color: var(--color);
+  outline: 4px solid var(--focus);
+}
+
+.emotion-4:disabled,
+.emotion-4:disabled:hover {
+  color: rgb(166,173,194);
+  background-color: rgb(239,242,249);
+  border: 1px solid rgb(197,205,223);
+  cursor: not-allowed;
+}
+
+.emotion-4>svg {
+  margin-right: 0.25rem;
+}
+
+.emotion-4 [disabled] {
+  pointer-events: none;
+}
+
+.code-snippet-highlighted-oneline .emotion-4 {
+  bottom: 0.5rem;
+}
+
+.emotion-6 code {
+  font-family: Ubuntu Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
+  font-size: 0.875rem;
+  line-height: 1.125rem;
+  font-weight: 400;
+  background: rgb(252,252,253);
+  color: rgb(51,53,60);
+}
+
+.emotion-6 code.hljs {
+  padding: unset;
+}
+
+.emotion-6 .hljs-meta,
+.emotion-6 .hljs-variable {
+  color: rgb(51,53,60);
+}
+
+.emotion-6 .hljs-doctag,
+.emotion-6 .hljs-title,
+.emotion-6 .hljs-title.class_,
+.emotion-6 .hljs-title.function_ {
+  color: rgb(34,84,192);
+}
+
+.emotion-6 .hljs-comment {
+  font-family: Ubuntu Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
+  font-size: 0.875rem;
+  line-height: 1.125rem;
+  font-style: italic;
+  color: rgb(109,111,119);
+}
+
+.emotion-6 .hljs-keyword,
+.emotion-6 .hljs-tag,
+.emotion-6 .hljs-type {
+  color: rgb(152,29,150);
+}
+
+.emotion-6 .hljs-literal,
+.emotion-6 .hljs-number {
+  color: rgb(126,83,5);
+}
+
+.emotion-6 .hljs-string {
+  color: rgb(32,105,31);
+}
+
+.emotion-6 .hljs-meta .hljs-keyword {
+  color: rgb(47,103,48);
+}
+
+.emotion-6.code-wrap {
+  white-space: pre-wrap;
+  word-break: break-all;
+}
+
+.emotion-6 mark {
+  font-weight: 400;
+  padding: 0.25rem;
+  border-radius: 0.25rem;
+  background-color: rgb(197,205,223);
+  color: rgb(217,45,32);
+}
+
+<div>
+  <div
+    class="fs-mask emotion-0 emotion-1"
+  >
+    <button
+      aria-describedby="tooltip-1"
+      class="sw-select-none emotion-2 emotion-3 emotion-4 emotion-5"
+      data-clipboard-text="foo
+bar"
+      type="button"
+    >
+      <svg
+        aria-hidden="true"
+        class="octicon octicon-copy"
+        fill="currentColor"
+        focusable="false"
+        height="16"
+        role="img"
+        style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+        viewBox="0 0 16 16"
+        width="16"
+      >
+        <path
+          d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"
+        />
+        <path
+          d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"
+        />
+      </svg>
+      Copy
+    </button>
+    <span
+      class="hljs sw-pr-24 sw-flex emotion-6 emotion-7"
+    >
+      foo
+bar
+    </span>
+  </div>
+</div>
+`;
+
+exports[`should show reduced size when single line with no editting 1`] = `
+.emotion-0 {
+  background-color: rgb(252,252,253);
+  border: 1px solid rgb(225,230,243);
+  border-radius: 0.5rem;
+  position: relative;
+  margin-top: 0.5rem;
+  margin-bottom: 0.5rem;
+}
+
+.emotion-0.code-snippet-simple-oneline {
+  margin-top: 0;
+  margin-bottom: 0;
+  border-radius: 0.25rem;
+}
+
+.emotion-4 {
+  box-sizing: border-box;
+  -webkit-text-decoration: none;
+  text-decoration: none;
+  outline: none;
+  border: var(--border);
+  color: var(--color);
+  background-color: var(--background);
+  -webkit-transition: background-color 0.2s ease,outline 0.2s ease;
+  transition: background-color 0.2s ease,outline 0.2s ease;
+  display: -webkit-inline-box;
+  display: -webkit-inline-flex;
+  display: -ms-inline-flexbox;
+  display: inline-flex;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  height: 2.25rem;
+  font-family: Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+  font-size: 0.875rem;
+  line-height: 1.25rem;
+  font-weight: 600;
+  padding-left: 1rem;
+  padding-right: 1rem;
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+  border-radius: 0.5rem;
+  cursor: pointer;
+  --background: rgb(255,255,255);
+  --backgroundHover: rgb(239,242,249);
+  --color: rgb(62,67,87);
+  --focus: rgba(197,205,223,0.2);
+  --border: 1px solid rgb(197,205,223);
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  font-family: Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
+  font-size: 0.875rem;
+  line-height: 1.25rem;
+  font-weight: 400;
+  right: 1.5rem;
+  top: 1.5rem;
+  position: absolute;
+  bottom: 1.5rem;
+  top: 1.5rem;
+}
+
+.emotion-4:hover {
+  color: var(--color);
+  background-color: var(--backgroundHover);
+}
+
+.emotion-4:focus,
+.emotion-4:active {
+  color: var(--color);
+  outline: 4px solid var(--focus);
+}
+
+.emotion-4:disabled,
+.emotion-4:disabled:hover {
+  color: rgb(166,173,194);
+  background-color: rgb(239,242,249);
+  border: 1px solid rgb(197,205,223);
+  cursor: not-allowed;
+}
+
+.emotion-4>svg {
+  margin-right: 0.25rem;
+}
+
+.emotion-4 [disabled] {
+  pointer-events: none;
+}
+
+.code-snippet-highlighted-oneline .emotion-4 {
+  bottom: 0.5rem;
+}
+
+.emotion-6 code {
+  font-family: Ubuntu Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
+  font-size: 0.875rem;
+  line-height: 1.125rem;
+  font-weight: 400;
+  background: rgb(252,252,253);
+  color: rgb(51,53,60);
+}
+
+.emotion-6 code.hljs {
+  padding: unset;
+}
+
+.emotion-6 .hljs-meta,
+.emotion-6 .hljs-variable {
+  color: rgb(51,53,60);
+}
+
+.emotion-6 .hljs-doctag,
+.emotion-6 .hljs-title,
+.emotion-6 .hljs-title.class_,
+.emotion-6 .hljs-title.function_ {
+  color: rgb(34,84,192);
+}
+
+.emotion-6 .hljs-comment {
+  font-family: Ubuntu Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
+  font-size: 0.875rem;
+  line-height: 1.125rem;
+  font-style: italic;
+  color: rgb(109,111,119);
+}
+
+.emotion-6 .hljs-keyword,
+.emotion-6 .hljs-tag,
+.emotion-6 .hljs-type {
+  color: rgb(152,29,150);
+}
+
+.emotion-6 .hljs-literal,
+.emotion-6 .hljs-number {
+  color: rgb(126,83,5);
+}
+
+.emotion-6 .hljs-string {
+  color: rgb(32,105,31);
+}
+
+.emotion-6 .hljs-meta .hljs-keyword {
+  color: rgb(47,103,48);
+}
+
+.emotion-6.code-wrap {
+  white-space: pre-wrap;
+  word-break: break-all;
+}
+
+.emotion-6 mark {
+  font-weight: 400;
+  padding: 0.25rem;
+  border-radius: 0.25rem;
+  background-color: rgb(197,205,223);
+  color: rgb(217,45,32);
+}
+
+<div>
+  <div
+    class="code-snippet-highlighted-oneline fs-mask emotion-0 emotion-1"
+  >
+    <button
+      aria-describedby="tooltip-2"
+      class="sw-select-none emotion-2 emotion-3 emotion-4 emotion-5"
+      data-clipboard-text="foobar"
+      type="button"
+    >
+      <svg
+        aria-hidden="true"
+        class="octicon octicon-copy"
+        fill="currentColor"
+        focusable="false"
+        height="16"
+        role="img"
+        style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+        viewBox="0 0 16 16"
+        width="16"
+      >
+        <path
+          d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"
+        />
+        <path
+          d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"
+        />
+      </svg>
+      Copy
+    </button>
+    <span
+      class="hljs sw-pr-24 sw-flex emotion-6 emotion-7"
+    >
+      foobar
+    </span>
+  </div>
+</div>
+`;
index 89f9f5024d39eb3086f7980c8dc3674db351b7d5..1a1f1db8ea6072e0a13d17bad0f5a690426c681e 100644 (file)
@@ -26,6 +26,7 @@ export { Breadcrumbs } from './Breadcrumbs';
 export * from './BubbleChart';
 export * from './Card';
 export * from './Checkbox';
+export * from './CodeSnippet';
 export * from './CodeSyntaxHighlighter';
 export * from './ColorsLegend';
 export * from './CoverageIndicator';
index 191de33ff204b8c952f3519a4ecbb5b3a8d9963a..d1ef52932d34b2f371fb6f6dc7ddd028be303499 100644 (file)
@@ -28,7 +28,7 @@ import withCurrentUserContext from '../../app/components/current-user/withCurren
 import withMetricsContext from '../../app/components/metrics/withMetricsContext';
 import { translate } from '../../helpers/l10n';
 import { BranchLike } from '../../types/branch-like';
-import { ComponentQualifier, isApplication, isProject } from '../../types/component';
+import { isApplication, isProject } from '../../types/component';
 import { Feature } from '../../types/features';
 import { MetricKey } from '../../types/metrics';
 import { Component, Dict, Measure, Metric } from '../../types/types';
@@ -99,12 +99,11 @@ function ProjectInformationApp(props: Props) {
                   <ProjectBadges branchLike={branchLike} component={component} />
                 </Card>
               )}
-              {component.qualifier === ComponentQualifier.Project &&
-                regulatoryReportFeatureEnabled && (
-                  <Card>
-                    <RegulatoryReport component={component} branchLike={branchLike} />
-                  </Card>
-                )}
+              {isProject(component.qualifier) && regulatoryReportFeatureEnabled && (
+                <Card>
+                  <RegulatoryReport component={component} branchLike={branchLike} />
+                </Card>
+              )}
             </div>
           </div>
         </PageContentFontWrapper>
index f5b023c180abe3367bd7c459966921db26bb1444..7a5b041262b814ec97f2da643fba0af3bd76d426 100644 (file)
@@ -45,13 +45,7 @@ export default function MetaKey({ componentKey, qualifier }: MetaKeyProps) {
       </div>
       <div className="sw-w-full">
         <div className="sw-flex sw-gap-2 sw-items-center sw-min-w-0">
-          <CodeSnippet
-            className="sw-min-w-0"
-            isOneLine
-            noCopy
-            highlight={false}
-            snippet={componentKey}
-          />
+          <CodeSnippet className="sw-min-w-0" isOneLine noCopy snippet={componentKey} />
           <ClipboardIconButton copyValue={componentKey} />
         </div>
       </div>
index 165fe8eccb7930fdce0b696d1c511cc888e5e4bd..4fec52c49a8fc56cda852c99e38b41a6d54bcbb3 100644 (file)
@@ -165,10 +165,10 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
       <DeferredSpinner className="spacer-top spacer-bottom" loading={isFetchingToken || isRenewing}>
         {!isLoading && (
           <CodeSnippet
-            wrap
-            className="sw-p-6 it__code-snippet"
             language="plaintext"
+            className="sw-p-6 it__code-snippet"
             snippet={getBadgeSnippet(selectedType, fullBadgeOptions, token)}
+            wrap
           />
         )}
       </DeferredSpinner>
@@ -177,7 +177,7 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
         <p>
           {translate('overview.badges.leak_warning')}
           {canRenew && (
-            <div className="sw-flex sw-flex-col">
+            <span className="sw-flex sw-flex-col">
               {translate('overview.badges.renew.description')}{' '}
               <ButtonSecondary
                 disabled={isLoading}
@@ -188,7 +188,7 @@ export default function ProjectBadges(props: ProjectBadgesProps) {
               >
                 {translate('overview.badges.renew')}
               </ButtonSecondary>
-            </div>
+            </span>
           )}
         </p>
       </FlagMessage>