]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19472 Apply new design to code page header
authorJeremy Davis <jeremy.davis@sonarsource.com>
Mon, 5 Jun 2023 13:17:34 +0000 (15:17 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 9 Jun 2023 20:03:10 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/code/__tests__/Code-it.ts
server/sonar-web/src/main/js/apps/code/code.css
server/sonar-web/src/main/js/apps/code/components/Breadcrumbs.tsx [deleted file]
server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx
server/sonar-web/src/main/js/apps/code/components/CodeBreadcrumbs.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx
server/sonar-web/src/main/js/apps/code/components/Search.tsx

index 2cd68c56004f511f31e7cbb6f2bba055d1b2e08b..bb2b888e1aaf3298a09027522cd322de702c2ff6 100644 (file)
@@ -381,8 +381,8 @@ function getPageObject(user: UserEvent) {
     sourceCode: byText('function Test() {}'),
     notAccessToAllChildrenTxt: byText('code_viewer.not_all_measures_are_shown'),
     showingOutOfTxt: (x: number, y: number) => byText(`x_of_y_shown.${x}.${y}`),
-    newCodeBtn: byRole('button', { name: 'projects.view.new_code' }),
-    overallCodeBtn: byRole('button', { name: 'projects.view.overall_code' }),
+    newCodeBtn: byRole('radio', { name: 'projects.view.new_code' }),
+    overallCodeBtn: byRole('radio', { name: 'projects.view.overall_code' }),
     measureRow: (name: string | RegExp) => byRole('row', { name, exact: false }),
     measureValueCell: (row: HTMLElement, name: string, value: string) => {
       const i = Array.from(screen.getAllByRole('columnheader')).findIndex((c) =>
index 8c9864aaf9611d25149e102169cd009031dd9dd4..056e0e56fd10409a627b909dcb0e93694db4775d 100644 (file)
   }
 }
 
-.code-search {
-  margin-bottom: 10px;
-  display: flex;
-}
-
 .code-components-header {
   position: sticky;
   top: 105px;
diff --git a/server/sonar-web/src/main/js/apps/code/components/Breadcrumbs.tsx b/server/sonar-web/src/main/js/apps/code/components/Breadcrumbs.tsx
deleted file mode 100644 (file)
index b9ce55d..0000000
+++ /dev/null
@@ -1,47 +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 * as React from 'react';
-import { BranchLike } from '../../../types/branch-like';
-import { Breadcrumb, ComponentMeasure } from '../../../types/types';
-import ComponentName from './ComponentName';
-
-interface Props {
-  branchLike?: BranchLike;
-  breadcrumbs: Breadcrumb[];
-  rootComponent: ComponentMeasure;
-}
-
-export default function Breadcrumbs({ branchLike, breadcrumbs, rootComponent }: Props) {
-  return (
-    <ul className="code-breadcrumbs">
-      {breadcrumbs.map((component, index) => (
-        <li key={component.key}>
-          <ComponentName
-            branchLike={branchLike}
-            canBrowse={index < breadcrumbs.length - 1}
-            component={component}
-            rootComponent={rootComponent}
-            unclickable
-          />
-        </li>
-      ))}
-    </ul>
-  );
-}
index 919958d1653522b353908c7e85262283f30ba538..f440a027ece9e9e56a4d42ac1b146fd8869e7b0a 100644 (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 styled from '@emotion/styled';
 import classNames from 'classnames';
-import { LargeCenteredLayout } from 'design-system';
+import { FlagMessage, HelperHintIcon, LargeCenteredLayout } from 'design-system';
 import { intersection } from 'lodash';
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
@@ -28,14 +27,13 @@ import HelpTooltip from '../../../components/controls/HelpTooltip';
 import ListFooter from '../../../components/controls/ListFooter';
 import Suggestions from '../../../components/embed-docs-modal/Suggestions';
 import { Location } from '../../../components/hoc/withRouter';
-import { Alert } from '../../../components/ui/Alert';
 import { translate } from '../../../helpers/l10n';
 import { BranchLike } from '../../../types/branch-like';
 import { isApplication, isPortfolioLike } from '../../../types/component';
 import { Breadcrumb, Component, ComponentMeasure, Dict, Issue, Metric } from '../../../types/types';
 import '../code.css';
 import { getCodeMetrics } from '../utils';
-import Breadcrumbs from './Breadcrumbs';
+import CodeBreadcrumbs from './CodeBreadcrumbs';
 import Components from './Components';
 import Search from './Search';
 import SearchResults from './SearchResults';
@@ -114,27 +112,31 @@ export default function CodeAppRenderer(props: Props) {
 
   return (
     <LargeCenteredLayout className="sw-py-8 sw-body-md">
+      <Suggestions suggestions="code" />
+      <Helmet defer={false} title={sourceViewer !== undefined ? sourceViewer.name : defaultTitle} />
+
       <A11ySkipTarget anchor="code_main" />
 
       {!canBrowseAllChildProjects && isPortfolio && (
-        <StyledAlert variant="warning" className="it__portfolio_warning">
-          <AlertContent>
-            {translate('code_viewer.not_all_measures_are_shown')}
-            <HelpTooltip
-              className="spacer-left"
-              overlay={translate('code_viewer.not_all_measures_are_shown.help')}
-            />
-          </AlertContent>
-        </StyledAlert>
+        <FlagMessage
+          ariaLabel={translate('code_viewer.not_all_measures_are_shown')}
+          variant="warning"
+          className="it__portfolio_warning sw-mb-4"
+        >
+          {translate('code_viewer.not_all_measures_are_shown')}
+          <HelpTooltip
+            className="sw-ml-2"
+            overlay={translate('code_viewer.not_all_measures_are_shown.help')}
+          >
+            <HelperHintIcon />
+          </HelpTooltip>
+        </FlagMessage>
       )}
 
-      <Suggestions suggestions="code" />
-
-      <Helmet defer={false} title={sourceViewer !== undefined ? sourceViewer.name : defaultTitle} />
-
       {hasComponents && (
         <Search
           branchLike={branchLike}
+          className="sw-mb-4"
           component={component}
           newCodeSelected={newCodeSelected}
           onNewCodeToggle={props.handleSelectNewCode}
@@ -156,7 +158,7 @@ export default function CodeAppRenderer(props: Props) {
         )}
 
         {showBreadcrumbs && (
-          <Breadcrumbs
+          <CodeBreadcrumbs
             branchLike={branchLike}
             breadcrumbs={breadcrumbs}
             rootComponent={component}
@@ -219,13 +221,3 @@ export default function CodeAppRenderer(props: Props) {
     </LargeCenteredLayout>
   );
 }
-
-const StyledAlert = styled(Alert)`
-  display: inline-flex;
-  margin-bottom: 15px;
-`;
-
-const AlertContent = styled.div`
-  display: flex;
-  align-items: center;
-`;
diff --git a/server/sonar-web/src/main/js/apps/code/components/CodeBreadcrumbs.tsx b/server/sonar-web/src/main/js/apps/code/components/CodeBreadcrumbs.tsx
new file mode 100644 (file)
index 0000000..55c2f8e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 { Breadcrumbs } from 'design-system';
+import * as React from 'react';
+import { BranchLike } from '../../../types/branch-like';
+import { Breadcrumb, ComponentMeasure } from '../../../types/types';
+import ComponentName from './ComponentName';
+
+interface Props {
+  branchLike?: BranchLike;
+  breadcrumbs: Breadcrumb[];
+  className?: string;
+  rootComponent: ComponentMeasure;
+}
+
+export default function CodeBreadcrumbs(props: Props) {
+  const { branchLike, breadcrumbs, className, rootComponent } = props;
+
+  return (
+    <Breadcrumbs className={className}>
+      {breadcrumbs.map((component, index) => (
+        <ComponentName
+          branchLike={branchLike}
+          canBrowse={index < breadcrumbs.length - 1}
+          component={component}
+          key={component.key}
+          rootComponent={rootComponent}
+          unclickable
+        />
+      ))}
+    </Breadcrumbs>
+  );
+}
index da5c4be773885114dfcb77721fdb94e0b8202757..dbe0000d59d6b4fec3914941cd508f08c80308eb 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { HoverLink, LightLabel, QualifierIcon } from 'design-system';
 import * as React from 'react';
-import { colors } from '../../../app/theme';
-import Link from '../../../components/common/Link';
 import BranchIcon from '../../../components/icons/BranchIcon';
-import QualifierIcon from '../../../components/icons/QualifierIcon';
 import { getBranchLikeQuery } from '../../../helpers/branch-like';
 import { translate } from '../../../helpers/l10n';
 import { CodeScope, getComponentOverviewUrl, queryToSearch } from '../../../helpers/urls';
@@ -36,7 +34,9 @@ import { ComponentMeasure } from '../../../types/types';
 import { mostCommonPrefix } from '../utils';
 
 export function getTooltip(component: ComponentMeasure) {
-  const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS';
+  const isFile =
+    component.qualifier === ComponentQualifier.File ||
+    component.qualifier === ComponentQualifier.TestFile;
 
   if (isFile && component.path) {
     return component.path + '\n\n' + component.key;
@@ -89,7 +89,7 @@ export default function ComponentName({
         </span>
         {component.branch ? (
           <span className="text-ellipsis spacer-left">
-            <BranchIcon className="little-spacer-right" />
+            <BranchIcon className="sw-mr-1" />
             <span className="note">{component.branch}</span>
           </span>
         ) : (
@@ -99,11 +99,7 @@ export default function ComponentName({
     );
   }
   return (
-    <span
-      className="max-width-100 text-ellipsis"
-      title={getTooltip(component)}
-      aria-label={ariaLabel}
-    >
+    <span title={getTooltip(component)} aria-label={ariaLabel}>
       {renderNameWithIcon(branchLike, component, previous, rootComponent, unclickable, canBrowse)}
     </span>
   );
@@ -132,22 +128,17 @@ function renderNameWithIcon(
       ? component.branch
       : undefined;
     return (
-      <Link
-        className="display-inline-flex-center link-no-underline"
+      <HoverLink
         to={getComponentOverviewUrl(
-          component.refKey || component.key,
+          component.refKey ?? component.key,
           component.qualifier,
           { branch },
           codeType
         )}
       >
-        <QualifierIcon
-          className="little-spacer-right"
-          qualifier={component.qualifier}
-          fill={colors.primary}
-        />
+        <QualifierIcon className="sw-mr-1" qualifier={component.qualifier} />
         <span>{name}</span>
-      </Link>
+      </HoverLink>
     );
   } else if (canBrowse) {
     const query = { id: rootComponent.key, ...getBranchLikeQuery(branchLike) };
@@ -155,41 +146,32 @@ function renderNameWithIcon(
       Object.assign(query, { selected: component.key });
     }
     return (
-      <Link
-        className="display-inline-flex-center link-no-underline"
-        to={{ pathname: '/code', search: queryToSearch(query) }}
-      >
-        <QualifierIcon
-          className="little-spacer-right"
-          qualifier={component.qualifier}
-          fill={colors.primary}
-        />
+      <HoverLink to={{ pathname: '/code', search: queryToSearch(query) }}>
+        <QualifierIcon className="sw-mr-1" qualifier={component.qualifier} />
         <span>{name}</span>
-      </Link>
+      </HoverLink>
     );
   }
   return (
-    <span>
-      <QualifierIcon
-        qualifier={component.qualifier}
-        fill={
-          component.qualifier === ComponentQualifier.Directory ? colors.orange : colors.neutral800
-        }
-      />{' '}
+    <span className="sw-flex sw-items-center">
+      <QualifierIcon className="sw-mr-1" qualifier={component.qualifier} />
       {name}
     </span>
   );
 }
 
 function renderName(component: ComponentMeasure, previous: ComponentMeasure | undefined) {
-  const areBothDirs = component.qualifier === 'DIR' && previous && previous.qualifier === 'DIR';
+  const areBothDirs =
+    component.qualifier === ComponentQualifier.Directory &&
+    previous &&
+    previous.qualifier === ComponentQualifier.Directory;
   const prefix =
     areBothDirs && previous !== undefined
       ? mostCommonPrefix([component.name + '/', previous.name + '/'])
       : '';
   return prefix ? (
     <span>
-      <span style={{ color: colors.secondFontColor }}>{prefix}</span>
+      <LightLabel>{prefix}</LightLabel>
       <span>{component.name.slice(prefix.length)}</span>
     </span>
   ) : (
index 089de348926fbc3a600c6199032cfbe1ca5e5177..69980d82774f8546d97c66da9c47851867909552 100644 (file)
  * 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 { InputSearch, ToggleButton } from 'design-system';
 import { isEmpty, omit } from 'lodash';
 import * as React from 'react';
 import { getTree } from '../../../api/components';
-import ButtonToggle from '../../../components/controls/ButtonToggle';
-import SearchBox from '../../../components/controls/SearchBox';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { getBranchLikeQuery } from '../../../helpers/branch-like';
@@ -33,6 +33,7 @@ import { ComponentMeasure } from '../../../types/types';
 
 interface Props {
   branchLike?: BranchLike;
+  className?: string;
   component: ComponentMeasure;
   location: Location;
   newCodeSelected: boolean;
@@ -138,15 +139,15 @@ class Search extends React.PureComponent<Props, State> {
   };
 
   render() {
-    const { component, newCodeSelected } = this.props;
+    const { className, component, newCodeSelected } = this.props;
     const { loading, query } = this.state;
     const isViewLike = isView(component.qualifier);
 
     return (
-      <div className="code-search" id="code-search">
+      <div className={classNames('sw-flex sw-items-center', className)} id="code-search">
         {isViewLike && (
-          <span className="big-spacer-right">
-            <ButtonToggle
+          <div className="sw-mr-4">
+            <ToggleButton
               disabled={!isEmpty(query)}
               options={[
                 {
@@ -159,20 +160,25 @@ class Search extends React.PureComponent<Props, State> {
                 },
               ]}
               value={newCodeSelected}
-              onCheck={this.props.onNewCodeToggle}
+              onChange={this.props.onNewCodeToggle}
             />
-          </span>
+          </div>
         )}
-        <SearchBox
+        <InputSearch
+          clearIconAriaLabel={translate('clear')}
+          searchInputAriaLabel={translate(
+            isViewLike ? 'code.search_placeholder.portfolio' : 'code.search_placeholder'
+          )}
           minLength={3}
           onChange={this.handleQueryChange}
           onKeyDown={this.handleKeyDown}
           placeholder={translate(
             isViewLike ? 'code.search_placeholder.portfolio' : 'code.search_placeholder'
           )}
+          size="large"
           value={this.state.query}
         />
-        <DeferredSpinner className="spacer-left" loading={loading} />
+        <DeferredSpinner className="sw-ml-2" loading={loading} />
       </div>
     );
   }