]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19604 Layout correction for miui activity page
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>
Thu, 22 Jun 2023 13:58:49 +0000 (15:58 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 26 Jun 2023 20:03:54 +0000 (20:03 +0000)
22 files changed:
server/sonar-web/design-system/src/components/DeferredSpinner.tsx
server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
server/sonar-web/src/main/js/apps/overview/styles.css
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppRenderer.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css
server/sonar-web/src/main/js/components/activity-graph/DataTableModal.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsHeader.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsLegendCustom.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsLegendStatic.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsTooltips.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsTooltipsContent.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsTooltipsContentCoverage.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsTooltipsContentDuplication.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsTooltipsContentEvents.tsx
server/sonar-web/src/main/js/components/activity-graph/RichQualityGateEventInner.tsx
server/sonar-web/src/main/js/components/activity-graph/__tests__/ActivityGraph-it.tsx
server/sonar-web/src/main/js/components/activity-graph/styles.css [deleted file]

index 09c432d68734687c9bb48970a574f9074838e326..b5fcd1067f1ce601bf824a71ae2fcc2a0dd5c8eb 100644 (file)
@@ -25,6 +25,7 @@ import { translate } from '../helpers/l10n';
 import { themeColor } from '../helpers/theme';
 
 interface Props {
+  ariaLabel?: string;
   children?: React.ReactNode;
   className?: string;
   customSpinner?: JSX.Element;
@@ -77,12 +78,12 @@ export class DeferredSpinner extends React.PureComponent<Props, State> {
 
   render() {
     const { showSpinner } = this.state;
-    const { customSpinner, className, children, placeholder } = this.props;
+    const { customSpinner, className, children, placeholder, ariaLabel } = this.props;
     if (showSpinner) {
       if (customSpinner) {
         return customSpinner;
       }
-      return <Spinner className={className} role="status" />;
+      return <Spinner aria-label={ariaLabel} className={className} role="status" />;
     }
     if (children) {
       return children;
index 4a425746158b70fdaee603df750bdb8ca1692acf..2a3d7ec742b536117382c313033f4b57986b8e58 100644 (file)
@@ -43,6 +43,7 @@ const TEMP_PAGELIST_WITH_NEW_BACKGROUND = [
   '/security_hotspots',
   '/component_measures',
   '/project/issues',
+  '/project/activity',
   '/code',
 ];
 
index 5c2e5194326f067b338205da8b3b2581e9f5455c..554983b0a0c4de26c750a15490a5203aecde573d 100644 (file)
  */
 
 .overview-panel .activity-graph-legends {
-  justify-content: right;
+  justify-content: right !important;
   margin-top: -38px;
 }
 
 .overview-analysis-event + .overview-analysis-event {
   margin-top: 4px;
 }
-
-.activity-graph-container {
-  height: 300px;
-  padding-bottom: 0;
-}
index c88c67e0dbb6d8e4e7e990f13ce56b9deb069b4a..7d043a09ea0c597a60f2ebbd4cec58821b55e916 100644 (file)
@@ -20,7 +20,7 @@
 import styled from '@emotion/styled';
 import classNames from 'classnames';
 import { isEqual } from 'date-fns';
-import { Badge, DeferredSpinner, themeColor } from 'design-system';
+import { Badge, DeferredSpinner, LightLabel, themeColor } from 'design-system';
 import * as React from 'react';
 import Tooltip from '../../../components/controls/Tooltip';
 import DateFormatter from '../../../components/intl/DateFormatter';
@@ -108,7 +108,9 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
               <DeferredSpinner />
             </div>
           ) : (
-            <div className="sw-p-4 sw-body-sm">{translate('no_results')}</div>
+            <div className="sw-p-4 sw-body-sm">
+              <LightLabel>{translate('no_results')}</LightLabel>
+            </div>
           )}
         </div>
       );
@@ -116,9 +118,10 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
 
     return (
       <ul
-        className="it__project-activity-versions-list sw-box-border sw-max-w-abs-400 sw-overflow-auto sw-grow sw-shrink-0 sw-py-0 sw-px-4"
+        className="it__project-activity-versions-list sw-box-border sw-overflow-auto sw-grow sw-shrink-0 sw-py-0 sw-px-4"
         ref={(element) => (this.scrollContainer = element)}
         style={{
+          height: 'calc(100vh - 250px)',
           marginTop:
             this.props.project.qualifier === ComponentQualifier.Project
               ? LIST_MARGIN_TOP
@@ -136,7 +139,7 @@ export default class ProjectActivityAnalysesList extends React.PureComponent<Pro
               {version.version && (
                 <VersionTagStyled
                   className={classNames(
-                    'sw-sticky sw-top-0 sw-left-0 sw-pb-1 -sw-ml-4 sw-pt-3 sw-z-normal',
+                    'sw-sticky sw-top-0 sw-left-0 sw-pb-1 -sw-ml-4 sw-z-normal',
                     {
                       'sw-top-0 sw-pt-0': idx === 0,
                     }
index 913c0863f6ece7a25416c14451bfe836dcad9bc7..ec4085ef343971e4796a56b0b118553123e33011 100644 (file)
@@ -17,6 +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 { LargeCenteredLayout, themeBorder, themeColor } from 'design-system';
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget';
@@ -58,56 +60,62 @@ export default function ProjectActivityAppRenderer(props: Props) {
     (configuration ? configuration.showHistory : false);
   const canDeleteAnalyses = configuration ? configuration.showHistory : false;
   return (
-    <main className="page page-limited" id="project-activity">
+    <main className="sw-p-5" id="project-activity">
       <Suggestions suggestions="project_activity" />
       <Helmet defer={false} title={translate('project_activity.page')} />
 
       <A11ySkipTarget anchor="activity_main" />
+      <LargeCenteredLayout>
+        <ProjectActivityPageFilters
+          category={query.category}
+          from={query.from}
+          project={props.project}
+          to={query.to}
+          updateQuery={props.onUpdateQuery}
+        />
 
-      <ProjectActivityPageFilters
-        category={query.category}
-        from={query.from}
-        project={props.project}
-        to={query.to}
-        updateQuery={props.onUpdateQuery}
-      />
-
-      <div className="layout-page project-activity-page">
-        <div className="layout-page-side-outer project-activity-page-side-outer boxed-group">
-          <ProjectActivityAnalysesList
-            onAddCustomEvent={props.onAddCustomEvent}
-            onAddVersion={props.onAddVersion}
-            analyses={analyses}
-            analysesLoading={props.analysesLoading}
-            canAdmin={canAdmin}
-            canDeleteAnalyses={canDeleteAnalyses}
-            onChangeEvent={props.onChangeEvent}
-            onDeleteAnalysis={props.onDeleteAnalysis}
-            onDeleteEvent={props.onDeleteEvent}
-            initializing={props.initializing}
-            leakPeriodDate={
-              props.project.leakPeriodDate ? parseDate(props.project.leakPeriodDate) : undefined
-            }
-            project={props.project}
-            query={query}
-            onUpdateQuery={props.onUpdateQuery}
-          />
-        </div>
-        <div className="project-activity-layout-page-main">
-          <ProjectActivityGraphs
-            analyses={analyses}
-            leakPeriodDate={
-              props.project.leakPeriodDate ? parseDate(props.project.leakPeriodDate) : undefined
-            }
-            loading={props.graphLoading}
-            measuresHistory={measuresHistory}
-            metrics={props.metrics}
-            project={props.project.key}
-            query={query}
-            updateQuery={props.onUpdateQuery}
-          />
+        <div className="sw-grid sw-grid-cols-12 sw-gap-x-12">
+          <StyledWrapper className="sw-col-span-4 sw-rounded-1">
+            <ProjectActivityAnalysesList
+              onAddCustomEvent={props.onAddCustomEvent}
+              onAddVersion={props.onAddVersion}
+              analyses={analyses}
+              analysesLoading={props.analysesLoading}
+              canAdmin={canAdmin}
+              canDeleteAnalyses={canDeleteAnalyses}
+              onChangeEvent={props.onChangeEvent}
+              onDeleteAnalysis={props.onDeleteAnalysis}
+              onDeleteEvent={props.onDeleteEvent}
+              initializing={props.initializing}
+              leakPeriodDate={
+                props.project.leakPeriodDate ? parseDate(props.project.leakPeriodDate) : undefined
+              }
+              project={props.project}
+              query={query}
+              onUpdateQuery={props.onUpdateQuery}
+            />
+          </StyledWrapper>
+          <StyledWrapper className="sw-col-span-8 sw-rounded-1">
+            <ProjectActivityGraphs
+              analyses={analyses}
+              leakPeriodDate={
+                props.project.leakPeriodDate ? parseDate(props.project.leakPeriodDate) : undefined
+              }
+              loading={props.graphLoading}
+              measuresHistory={measuresHistory}
+              metrics={props.metrics}
+              project={props.project.key}
+              query={query}
+              updateQuery={props.onUpdateQuery}
+            />
+          </StyledWrapper>
         </div>
-      </div>
+      </LargeCenteredLayout>
     </main>
   );
 }
+
+const StyledWrapper = styled.div`
+  border: ${themeBorder('default', 'filterbarBorder')};
+  background-color: ${themeColor('backgroundSecondary')};
+`;
index 3acdcfb0e13eddb5709647b5fd5aa85b6fb3b2ca..9a424a2365f79d887d57d4c8af56c6bd99efbe36 100644 (file)
@@ -203,10 +203,10 @@ export default class ProjectActivityGraphs extends React.PureComponent<Props, St
     const { graphEndDate, graphStartDate, series } = this.state;
 
     return (
-      <div className="project-activity-layout-page-main-inner boxed-group boxed-group-inner">
+      <div className="sw-px-5 sw-py-4 sw-h-full sw-flex sw-flex-col sw-box-border">
         <GraphsHeader
           onAddCustomMetric={this.handleAddCustomMetric}
-          className="big-spacer-bottom"
+          className="sw-mb-4"
           graph={query.graph}
           metrics={metrics}
           metricsTypeFilter={this.getMetricsTypeFilter()}
index 9ac37d471371462f5fabe703dc53b6e1ec8859bb..e96e601475ab9d4a7a15d37a87868b3702e8a6fd 100644 (file)
@@ -24,7 +24,6 @@ import { keyBy, times } from 'lodash';
 import React from 'react';
 import { act } from 'react-dom/test-utils';
 import { Route } from 'react-router-dom';
-import selectEvent from 'react-select-event';
 import { ProjectActivityServiceMock } from '../../../../api/mocks/ProjectActivityServiceMock';
 import { TimeMachineServiceMock } from '../../../../api/mocks/TimeMachineServiceMock';
 import { parseDate } from '../../../../helpers/dates';
index fabdaf7335bcace53e8b9185c762864281b04359..d1dfa13d5c17141af2952c4e5fe8f87cb8c619e5 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.
  */
-.project-activity-page {
-  min-height: 600px;
-  height: calc(100vh - 250px);
-}
-
-.project-activity-page-side-outer {
-  position: relative;
-  width: 400px;
-  margin-bottom: 0;
-  display: flex;
-  flex-direction: row;
-  align-items: stretch;
-  background-color: #fff !important;
-}
-
-.project-activity-layout-page-main {
-  flex-grow: 1;
-  min-width: 640px;
-  padding-left: 20px;
-  display: flex;
-}
-
-.project-activity-layout-page-main-inner {
-  min-width: 640px;
-  max-width: 880px;
-  margin-bottom: 0px;
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  align-items: stretch;
-}
-
-.project-activity-versions-list {
-  max-width: 400px;
-  box-sizing: border-box;
-  overflow: auto;
-  flex-grow: 1;
-  flex-shrink: 0;
-  padding: 0 calc(2 * var(--gridSize)) calc(2 * var(--gridSize)) calc(1.5 * var(--gridSize));
-}
-
-.project-activity-day {
-  margin-top: 8px;
-  margin-bottom: 24px;
-}
-
-.project-activity-day:last-child {
-  margin-bottom: 10px;
-}
-
-.project-activity-analysis {
-  position: relative;
-  min-height: var(--smallControlHeight);
-  padding: calc(2 * var(--gridSize));
-  cursor: pointer;
-}
-
-.project-activity-analysis.selected {
-  cursor: default;
-  background-color: var(--rowHoverHighlight);
-}
-
-.project-activity-analysis:focus {
-  outline: none;
-}
-
-.project-activity-analysis:hover {
-  background-color: var(--rowHoverHighlight);
-}
-
-.project-activity-analysis + .project-activity-analysis {
-  border-top: none;
-}
-
-.project-activity-analysis-actions {
-  height: var(--smallControlHeight);
-}
-
-.project-activity-time {
-  height: var(--smallControlHeight);
-  line-height: var(--smallControlHeight);
-}
-
-.project-activity-event {
-  line-height: var(--smallControlHeight);
-  text-indent: -20px;
-  padding-left: 20px;
-}
-
-.project-activity-event * {
-  text-indent: 0;
-}
-
-.project-activity-event + .project-activity-event {
-  margin-top: var(--gridSize);
-}
-
-.project-activity-event-inner-more-link {
-  line-height: 16px;
-}
-
-.project-activity-event-icon.VERSION {
-  color: var(--blue);
-}
-
-.project-activity-event-icon.QUALITY_GATE {
-  color: var(--purple);
-}
-
-.project-activity-event-icon.QUALITY_PROFILE {
-  color: #cccccc;
-}
-
-.project-activity-event-icon.DEFINITION_CHANGE {
-  color: #33a759;
-}
-
-.project-activity-event-icon.OTHER {
-  color: #442d1b;
-}
-
-.project-activity-version-badge {
-  position: sticky;
-  top: calc(-3 * var(--gridSize));
-  left: calc(1.5 * var(--gridSize));
-  right: calc(2 * var(--gridSize));
-  margin-left: calc(-1.5 * var(--gridSize));
-  background-color: white;
-  padding-top: calc(3 * var(--gridSize));
-  padding-bottom: var(--gridSize);
-  z-index: var(--belowNormalZIndex);
-}
-
-.project-activity-version-badge.first {
-  top: 0;
-  padding-top: 0;
-}
-
-.project-activity-version-badge .analysis-version {
-  max-width: 385px;
-  border-radius: 0 2px 2px 0;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
-.Select .project-activity-event-icon,
-.activity-graph-tooltip-line .project-activity-event-icon {
-  margin-top: 1px;
-}
-
 .baseline-marker {
   position: absolute;
   top: -10px;
index eee2bdbfca39315e691c3e4626574012f9785f93..b885681c1ecf8bb3ddb6e92d2a6c8e665b29c349 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 styled from '@emotion/styled';
+import { FlagMessage, Modal } from 'design-system';
 import { filter, slice, sortBy } from 'lodash';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import { formatMeasure } from '../../helpers/measures';
 import { ParsedAnalysis, Serie } from '../../types/project-activity';
-import Modal from '../controls/Modal';
-import { Button } from '../controls/buttons';
 import DateFormatter from '../intl/DateFormatter';
 import TimeFormatter from '../intl/TimeFormatter';
-import { Alert } from '../ui/Alert';
 import EventInner from './EventInner';
 import { getAnalysisEventsForDate } from './utils';
 
@@ -49,9 +48,9 @@ export default function DataTableModal(props: DataTableModalProps) {
   if (series.length === 0) {
     return renderModal(
       props,
-      <Alert variant="info">
+      <FlagMessage variant="warning">
         {translate('project_activity.graphs.data_table.no_data_warning')}
-      </Alert>
+      </FlagMessage>
     );
   }
 
@@ -93,21 +92,21 @@ export default function DataTableModal(props: DataTableModalProps) {
     MAX_DATA_TABLE_ROWS
   ).map(({ date, ...values }) => (
     <tr key={date.getTime()}>
-      <td className="nowrap">
+      <td className="sw-whitespace-nowrap">
         <DateFormatter long date={date} />
-        <div className="small note">
+        <div className="sw-text-xs">
           <TimeFormatter date={date} />
         </div>
       </td>
       {metrics.map((metric) => (
-        <td key={metric} className="thin nowrap">
+        <td key={metric} className="sw-whitespace-nowrap sw-w-20">
           {values[metric] ?? '-'}
         </td>
       ))}
       <td>
         <ul>
           {getAnalysisEventsForDate(analyses, date).map((event) => (
-            <li className="little-spacer-bottom" key={event.key}>
+            <li className="sw-mb-1" key={event.key}>
               <EventInner event={event} readonly />
             </li>
           ))}
@@ -131,7 +130,7 @@ export default function DataTableModal(props: DataTableModalProps) {
     }
     return renderModal(
       props,
-      <Alert variant="info">
+      <FlagMessage variant="warning">
         <FormattedMessage
           defaultMessage={translate(
             `project_activity.graphs.data_table.no_data_warning_check_dates${suffix}`
@@ -139,7 +138,7 @@ export default function DataTableModal(props: DataTableModalProps) {
           id={`project_activity.graphs.data_table.no_data_warning_check_dates${suffix}`}
           values={{ start, end }}
         />
-      </Alert>
+      </FlagMessage>
     );
   }
 
@@ -147,19 +146,19 @@ export default function DataTableModal(props: DataTableModalProps) {
     props,
     <>
       {rowCount === MAX_DATA_TABLE_ROWS && (
-        <Alert variant="info">
+        <FlagMessage variant="warning">
           {translateWithParameters(
             'project_activity.graphs.data_table.max_lines_warning',
             MAX_DATA_TABLE_ROWS
           )}
-        </Alert>
+        </FlagMessage>
       )}
-      <table className="spacer-top data zebra">
+      <StyledTable className="sw-mt-2">
         <thead>
           <tr>
             <th>{translate('date')}</th>
             {series.map((serie) => (
-              <th key={serie.name} className="thin nowrap">
+              <th key={serie.name} className="sw-whitespace-nowrap sw-w-20">
                 {serie.translatedName}
               </th>
             ))}
@@ -167,7 +166,7 @@ export default function DataTableModal(props: DataTableModalProps) {
           </tr>
         </thead>
         <tbody>{rows}</tbody>
-      </table>
+      </StyledTable>
     </>
   );
 }
@@ -175,14 +174,47 @@ export default function DataTableModal(props: DataTableModalProps) {
 function renderModal(props: DataTableModalProps, children: React.ReactNode) {
   const heading = translate('project_activity.graphs.data_table.title');
   return (
-    <Modal onRequestClose={props.onClose} contentLabel={heading} size="medium">
-      <div className="modal-head">
-        <h2>{heading}</h2>
-      </div>
-      <div className="modal-body modal-container">{children}</div>
-      <div className="modal-foot">
-        <Button onClick={props.onClose}>{translate('close')}</Button>
-      </div>
-    </Modal>
+    <Modal
+      headerTitle={heading}
+      isLarge
+      onClose={props.onClose}
+      body={children}
+      primaryButton={null}
+      secondaryButtonLabel={translate('close')}
+    />
   );
 }
+
+const StyledTable = styled.table`
+  width: 100%;
+  & > thead > tr > th {
+    position: relative;
+    vertical-align: top;
+    line-height: 18px;
+    padding: 8px 10px;
+    border-bottom: 1px solid var(--barBorderColor);
+    font-weight: 600;
+  }
+
+  & > thead > tr > th > .small {
+    display: block;
+    line-height: 1.4;
+    font-weight: 400;
+  }
+
+  & > tfoot > tr > td {
+    font-size: 93%;
+    color: var(--secondFontColor);
+    padding: 5px;
+  }
+
+  & > tbody > tr > td {
+    position: relative;
+    padding: 8px 10px;
+    line-height: 16px;
+  }
+
+  & > tbody > tr > td.text-middle {
+    vertical-align: middle;
+  }
+`;
index eee04c2270d8790d7ab1b31e6b9d89878bde35f1..7f5c8b1c5f40b4a346589b3bf62d035adb6256b1 100644 (file)
@@ -17,7 +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 { ButtonSecondary } from 'design-system';
 import * as React from 'react';
 import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer';
 import { AdvancedTimeline } from '../../components/charts/AdvancedTimeline';
@@ -25,7 +26,6 @@ import { translate } from '../../helpers/l10n';
 import { formatMeasure, getShortType } from '../../helpers/measures';
 import { MeasureHistory, ParsedAnalysis, Serie } from '../../types/project-activity';
 import ModalButton from '../controls/ModalButton';
-import { Button } from '../controls/buttons';
 import DataTableModal from './DataTableModal';
 import GraphsLegendCustom from './GraphsLegendCustom';
 import GraphsLegendStatic from './GraphsLegendStatic';
@@ -104,19 +104,14 @@ export default class GraphHistory extends React.PureComponent<Props, State> {
     const events = getAnalysisEventsForDate(analyses, selectedDate);
 
     return (
-      <div
-        className={
-          'activity-graph-container flex-grow display-flex-column display-flex-stretch ' +
-          'display-flex-justify-center'
-        }
-      >
+      <StyledGraphContainer className="sw-flex sw-flex-col sw-justify-center sw-items-stretch sw-grow sw-py-2">
         {isCustom && this.props.removeCustomMetric ? (
           <GraphsLegendCustom removeMetric={this.props.removeCustomMetric} series={series} />
         ) : (
           <GraphsLegendStatic series={series} />
         )}
 
-        <div className="flex-1">
+        <div className="sw-flex-1">
           <AutoSizer>
             {({ height, width }) => (
               <div>
@@ -158,13 +153,17 @@ export default class GraphHistory extends React.PureComponent<Props, State> {
         {canShowDataAsTable && (
           <ModalButton modal={modalProp}>
             {({ onClick }) => (
-              <Button className="a11y-hidden" onClick={onClick}>
+              <ButtonSecondary className="a11y-hidden" onClick={onClick}>
                 {translate('project_activity.graphs.open_in_table')}
-              </Button>
+              </ButtonSecondary>
             )}
           </ModalButton>
         )}
-      </div>
+      </StyledGraphContainer>
     );
   }
 }
+
+const StyledGraphContainer = styled.div`
+  height: 300px;
+`;
index e2165221004baa38ed10bfed0834da011ee73a03..fb4f216d370b7458a3290dc7a50b3b0d11227ea3 100644 (file)
@@ -32,7 +32,6 @@ import { translate } from '../../helpers/l10n';
 import { GraphType } from '../../types/project-activity';
 import { Metric } from '../../types/types';
 import AddGraphMetric from './AddGraphMetric';
-import './styles.css';
 import { getGraphTypes, isCustomGraph } from './utils';
 
 interface Props {
index b9790af566d53846052c3a3bdf0c48ca707cc177..fe17ca2251c38724f15b583f440184875255c55a 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 { DeferredSpinner, LightLabel } from 'design-system';
 import { isEqual, uniqBy } from 'lodash';
 import * as React from 'react';
-import DeferredSpinner from '../../components/ui/DeferredSpinner';
 import { translate, translateWithParameters } from '../../helpers/l10n';
-import { getBaseUrl } from '../../helpers/system';
 import { GraphType, MeasureHistory, ParsedAnalysis, Serie } from '../../types/project-activity';
 import GraphHistory from './GraphHistory';
-import './styles.css';
 import { getSeriesMetricType, hasHistoryData, isCustomGraph } from './utils';
 
 interface Props {
@@ -73,8 +71,8 @@ export default class GraphsHistory extends React.PureComponent<Props, State> {
 
     if (loading) {
       return (
-        <div className="activity-graph-container flex-grow display-flex-column display-flex-stretch display-flex-justify-center">
-          <div className="text-center">
+        <div className="sw-flex sw-justify-center sw-flex-col sw-items-stretch sw-grow">
+          <div className="sw-text-center">
             <DeferredSpinner ariaLabel={translate('loading')} loading={loading} />
           </div>
         </div>
@@ -83,28 +81,20 @@ export default class GraphsHistory extends React.PureComponent<Props, State> {
 
     if (!hasHistoryData(series)) {
       return (
-        <div className="activity-graph-container flex-grow display-flex-column display-flex-stretch display-flex-justify-center">
-          <div className="display-flex-center display-flex-justify-center">
-            <img
-              alt="" /* Make screen readers ignore this image; it's purely eye candy. */
-              className="spacer-right"
-              height={52}
-              src={`${getBaseUrl()}/images/activity-chart.svg`}
-            />
-            <div className="big-spacer-left big text-muted" style={{ maxWidth: 300 }}>
-              {translate(
-                isCustom
-                  ? 'project_activity.graphs.custom.no_history'
-                  : 'component_measures.no_history'
-              )}
-            </div>
-          </div>
+        <div className="sw-flex sw-items-center sw-justify-center sw-h-full">
+          <LightLabel className="sw-body-sm">
+            {translate(
+              isCustom
+                ? 'project_activity.graphs.custom.no_history'
+                : 'component_measures.no_history'
+            )}
+          </LightLabel>
         </div>
       );
     }
     const showAreas = [GraphType.coverage, GraphType.duplications].includes(graph);
     return (
-      <div className="display-flex-justify-center display-flex-column display-flex-stretch flex-grow">
+      <div className="sw-flex sw-justify-center sw-flex-col sw-items-stretch sw-grow">
         {this.props.graphs.map((graphSeries, idx) => {
           return (
             <GraphHistory
index be10615dd13bdc0a494baec66beeee84faa1a210..8f4d13f3456943bbb240dab058e4466c16883a17 100644 (file)
@@ -35,7 +35,7 @@ export default function GraphsLegendCustom(props: GraphsLegendCustomProps) {
   const { series } = props;
 
   return (
-    <ul className="activity-graph-legends">
+    <ul className="activity-graph-legends sw-flex sw-justify-center sw-items-center sw-pb-4">
       {series.map((serie, idx) => {
         const hasData = hasDataValues(serie);
 
index 9f62fbcb55baa866ea90027e05057c2df4dddb93..ca4af98ba1f598618467818dc9b8b375e3d43b17 100644 (file)
@@ -30,7 +30,7 @@ export interface GraphsLegendStaticProps {
 
 export default function GraphsLegendStatic({ series }: GraphsLegendStaticProps) {
   return (
-    <ul className="activity-graph-legends">
+    <ul className="activity-graph-legends sw-flex sw-justify-center sw-items-center sw-pb-4">
       {series.map((serie, idx) => (
         <li key={serie.name}>
           <GraphsLegendItem
index 5f90bd2464da4952b99375e31f594cfa63f32dc7..0e7fca5c409f891910f65cd3e8a35a9be4dd51c3 100644 (file)
@@ -112,12 +112,12 @@ export class GraphsTooltipsClass extends React.PureComponent<Props> {
 
     return (
       <Popup
-        className="disabled-pointer-events"
+        className="sw-pointer-events-none"
         noArrow
         placement={placement}
         style={{ top, left, width: TOOLTIP_WIDTH }}
       >
-        <div className="activity-graph-tooltip">
+        <div className="sw-p-2">
           <div
             className="sw-body-md-highlight sw-whitespace-nowrap"
             style={{ color: themeColor('selectionCardHeader')({ theme }) }}
@@ -131,7 +131,7 @@ export class GraphsTooltipsClass extends React.PureComponent<Props> {
             <tbody>
               {addSeparator && (
                 <tr>
-                  <td className="activity-graph-tooltip-separator" colSpan={3}>
+                  <td colSpan={3}>
                     <hr />
                   </td>
                 </tr>
index 345c16d7bf6035ceb48e38522885b81360821e24..27237b22a914fec5626fa295f505cfafba15aa49 100644 (file)
@@ -30,11 +30,11 @@ interface Props {
 
 export default function GraphsTooltipsContent({ name, index, translatedName, value }: Props) {
   return (
-    <tr className="activity-graph-tooltip-line" key={name}>
+    <tr className="sw-h-8" key={name}>
       <td className="thin">
         <ChartLegendIcon className="spacer-right" index={index} />
       </td>
-      <td className="activity-graph-tooltip-value text-right spacer-right thin">{value}</td>
+      <td className="sw-font-bold sw-text-right sw-pr-2 thin">{value}</td>
       <td>{translatedName}</td>
     </tr>
   );
index 2212f49e4e04d412d706d505bff1c4ceb50d2232..153b0ddae3f28ea72727650e59d42038ea73614a 100644 (file)
@@ -42,22 +42,22 @@ export default function GraphsTooltipsContentCoverage(props: GraphsTooltipsConte
     <>
       {addSeparator && (
         <tr>
-          <td className="activity-graph-tooltip-separator" colSpan={3}>
+          <td colSpan={3}>
             <hr />
           </td>
         </tr>
       )}
       {uncoveredValue && (
-        <tr className="activity-graph-tooltip-line">
-          <td className="activity-graph-tooltip-value text-right spacer-right thin" colSpan={2}>
+        <tr className="sw-h-8">
+          <td className="sw-font-bold sw-text-right sw-pr-2 thin" colSpan={2}>
             {formatMeasure(uncoveredValue, MetricType.ShortInteger)}
           </td>
           <td>{translate('metric.uncovered_lines.name')}</td>
         </tr>
       )}
       {coverageValue && (
-        <tr className="activity-graph-tooltip-line">
-          <td className="activity-graph-tooltip-value text-right spacer-right thin" colSpan={2}>
+        <tr className="sw-h-8">
+          <td className="sw-font-bold sw-text-right sw-pr-2 thin" colSpan={2}>
             {formatMeasure(coverageValue, MetricType.Percent)}
           </td>
           <td>{translate('metric.coverage.name')}</td>
index 55677538a5a0db880a38176c4bf45b56897e5c5d..e590e2775acc153497165bec672d45d2b158e1fa 100644 (file)
@@ -47,13 +47,13 @@ export default function GraphsTooltipsContentDuplication(
     <>
       {addSeparator && (
         <tr>
-          <td className="activity-graph-tooltip-separator" colSpan={3}>
+          <td colSpan={3}>
             <hr />
           </td>
         </tr>
       )}
-      <tr className="activity-graph-tooltip-line">
-        <td className="activity-graph-tooltip-value text-right spacer-right thin" colSpan={2}>
+      <tr className="sw-h-8">
+        <td className="sw-font-bold sw-text-right sw-pr-2 thin" colSpan={2}>
           {formatMeasure(duplicationDensityValue, MetricType.Percent)}
         </td>
         <td>{translate('metric.duplicated_lines_density.name')}</td>
index b05222c4a9279a60879686c170c31d4ffac0d3c5..c867a5fdb9e83172806b31aca692289a76bbc55d 100644 (file)
@@ -29,10 +29,10 @@ interface Props {
 export default function GraphsTooltipsContentEvents({ addSeparator, events }: Props) {
   return (
     <>
-      <tr className="activity-graph-tooltip-line">
+      <tr className="sw-h-8">
         <td colSpan={3}>
           {events.map((event) => (
-            <div className="little-spacer-bottom" key={event.key}>
+            <div key={event.key}>
               <EventInner event={event} readonly />
             </div>
           ))}
@@ -40,7 +40,7 @@ export default function GraphsTooltipsContentEvents({ addSeparator, events }: Pr
       </tr>
       {addSeparator && (
         <tr>
-          <td className="activity-graph-tooltip-separator" colSpan={3}>
+          <td colSpan={3}>
             <hr />
           </td>
         </tr>
index dd00000e8c98f0211a05e9ea2ee8785a77e200a4..500a4bd9faa6b718e4a03e606eff9a69af9919df 100644 (file)
@@ -97,7 +97,7 @@ export class RichQualityGateEventInner extends React.PureComponent<Props, State>
                     </StandoutLink>
                   </ClickEventBoundary>
                 </div>
-                <div className="sw-shrink sw-flex">
+                <div className="sw-shrink sw-flex sw-ml-2">
                   <div className="sw-items-top">
                     <QualityGateIndicator status={event.qualityGate.status} size="sm" />
                   </div>
index 7292dba8360564d5860bdf70d9adc1f16149d89a..79cdb85ca3bc7a07812b091220e1094820a300e5 100644 (file)
@@ -42,7 +42,7 @@ const START_DATE = '2016-01-01T00:00:00+0200';
 describe('rendering', () => {
   it('should render correctly when loading', async () => {
     renderActivityGraph({ loading: true });
-    expect(await screen.findByText('loading')).toBeInTheDocument();
+    expect(await screen.findByLabelText('loading')).toBeInTheDocument();
   });
 
   it('should show the correct legend items', async () => {
diff --git a/server/sonar-web/src/main/js/components/activity-graph/styles.css b/server/sonar-web/src/main/js/components/activity-graph/styles.css
deleted file mode 100644 (file)
index 2ce6982..0000000
+++ /dev/null
@@ -1,65 +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.
- */
-
-.activity-graph-container {
-  padding: 10px 0;
-}
-
-.activity-graph-tooltip {
-  padding: var(--gridSize);
-}
-
-.activity-graph-tooltip-line {
-  height: 20px;
-}
-
-.activity-graph-tooltip-line + .activity-graph-tooltip-line {
-  padding-top: calc(var(--gridSize) / 2);
-}
-
-.activity-graph-tooltip-separator {
-  padding-left: calc(2 * var(--gridSize));
-  padding-right: calc(2 * var(--gridSize));
-}
-
-.activity-graph-tooltip-separator hr {
-  margin-top: var(--gridSize);
-  margin-bottom: var(--gridSize);
-}
-
-.activity-graph-tooltip-value {
-  font-weight: bold;
-}
-
-.activity-graph-legends {
-  align-items: center;
-  display: flex;
-  justify-content: center;
-  padding-bottom: calc(2 * var(--gridSize));
-}
-
-.activity-graph-legend-actionable {
-  display: inline-block;
-  padding: calc(var(--gridSize) / 2) var(--gridSize) calc(var(--gridSize) / 2)
-    calc(1.5 * var(--gridSize));
-  border-width: 1px;
-  border-style: solid;
-  border-radius: calc(1.5 * var(--gridSize));
-}