From: Jeremy Davis Date: Mon, 19 Jun 2023 09:17:47 +0000 (+0200) Subject: SONAR-19604 New UI for the Activity Graph header X-Git-Tag: 10.2.0.77647~535 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=3b8f363175f8ef01fe4e888bccfd693bb9953852;p=sonarqube.git SONAR-19604 New UI for the Activity Graph header --- diff --git a/server/sonar-web/design-system/src/components/MultiSelectMenu.tsx b/server/sonar-web/design-system/src/components/MultiSelectMenu.tsx index b0c77bfcd74..ecfaa459512 100644 --- a/server/sonar-web/design-system/src/components/MultiSelectMenu.tsx +++ b/server/sonar-web/design-system/src/components/MultiSelectMenu.tsx @@ -264,6 +264,8 @@ export class MultiSelectMenu extends PureComponent { noResultsLabel, searchInputAriaLabel, } = this.props; + const { renderLabel } = this.props as PropsWithDefault; + const { query, activeIdx, selectedElements, unselectedElements } = this.state; const activeElement = this.getAllElements(this.props, this.state)[activeIdx]; const showNewElement = allowNewElements && this.isNewElement(query, this.props); @@ -301,6 +303,7 @@ export class MultiSelectMenu extends PureComponent { key={element} onHover={this.handleElementHover} onSelectChange={this.handleSelectChange} + renderLabel={renderLabel} selected /> ))} @@ -314,6 +317,7 @@ export class MultiSelectMenu extends PureComponent { key={element} onHover={this.handleElementHover} onSelectChange={this.handleSelectChange} + renderLabel={renderLabel} /> ))} {showNewElement && ( diff --git a/server/sonar-web/design-system/src/components/MultiSelectMenuOption.tsx b/server/sonar-web/design-system/src/components/MultiSelectMenuOption.tsx index 27147703f96..e68dd7c3687 100644 --- a/server/sonar-web/design-system/src/components/MultiSelectMenuOption.tsx +++ b/server/sonar-web/design-system/src/components/MultiSelectMenuOption.tsx @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import classNames from 'classnames'; +import { identity } from 'lodash'; import { ItemCheckbox } from './DropdownMenu'; export interface MultiSelectOptionProps { @@ -28,12 +29,27 @@ export interface MultiSelectOptionProps { element: string; onHover: (element: string) => void; onSelectChange: (selected: boolean, element: string) => void; + renderLabel?: (element: string) => React.ReactNode; selected?: boolean; } export function MultiSelectMenuOption(props: MultiSelectOptionProps) { - const { active, createElementLabel, custom, disabled, element, onSelectChange, selected } = props; - const onHover = () => props.onHover(element); + const { + active, + createElementLabel, + custom, + disabled, + element, + onSelectChange, + selected, + renderLabel = identity, + } = props; + + const onHover = () => { + props.onHover(element); + }; + + const label = renderLabel(element); return ( ) : ( - {element} + {label} )} ); diff --git a/server/sonar-web/design-system/src/theme/light.ts b/server/sonar-web/design-system/src/theme/light.ts index ac7d1fdd012..b7acbc729b2 100644 --- a/server/sonar-web/design-system/src/theme/light.ts +++ b/server/sonar-web/design-system/src/theme/light.ts @@ -460,6 +460,7 @@ export const lightTheme = { graphZoomBackgroundColor: COLORS.blueGrey[25], graphZoomBorderColor: COLORS.blueGrey[100], graphZoomHandleColor: COLORS.blueGrey[400], + graphLegendBorder: secondary.darker, // page pageTitle: COLORS.blueGrey[700], diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx index 3f54362aef6..3acdcfb0e13 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx @@ -39,7 +39,7 @@ import { Serie, } from '../../../types/project-activity'; import { Metric } from '../../../types/types'; -import { datesQueryChanged, historyQueryChanged, Query } from '../utils'; +import { Query, datesQueryChanged, historyQueryChanged } from '../utils'; import { PROJECT_ACTIVITY_GRAPH } from './ProjectActivityApp'; interface Props { @@ -132,8 +132,8 @@ export default class ProjectActivityGraphs extends React.PureComponent { if ( metric.hidden || isDiffMetric(metric.key) || - ['DATA', 'DISTRIB'].includes(metric.type) || + [MetricType.Data, MetricType.Distribution].includes(metric.type as MetricType) || selectedMetrics.includes(metric.key) || !getLocalizedMetricName(metric).toLowerCase().includes(query.toLowerCase()) ) { @@ -120,11 +119,13 @@ export default class AddGraphMetric extends React.PureComponent { this.props.metrics, this.props.selectedMetrics ); + return ( { /> } > - + + + + ); } diff --git a/server/sonar-web/src/main/js/components/activity-graph/AddGraphMetricPopup.tsx b/server/sonar-web/src/main/js/components/activity-graph/AddGraphMetricPopup.tsx index 583b849a2f4..f762e83d855 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/AddGraphMetricPopup.tsx +++ b/server/sonar-web/src/main/js/components/activity-graph/AddGraphMetricPopup.tsx @@ -17,10 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { FlagMessage, MultiSelectMenu } from 'design-system'; import * as React from 'react'; -import { Alert } from '../../components/ui/Alert'; import { translate, translateWithParameters } from '../../helpers/l10n'; -import MultiSelect from '../common/MultiSelect'; export interface AddGraphMetricPopupProps { elements: string[]; @@ -43,13 +42,13 @@ export default function AddGraphMetricPopup({ if (props.selectedElements.length >= 6) { footerNode = ( - + {translate('project_activity.graphs.custom.add_metric_info')} - + ); } else if (metricsTypeFilter && metricsTypeFilter.length > 0) { footerNode = ( - + {translateWithParameters( 'project_activity.graphs.custom.type_x_message', metricsTypeFilter @@ -57,26 +56,28 @@ export default function AddGraphMetricPopup({ .sort((a, b) => a.localeCompare(b)) .join(', ') )} - + ); } return ( -
- elements.includes(item) && props.onSelect(item)} - onUnselect={props.onUnselect} - placeholder={translate('search.search_for_metrics')} - renderLabel={props.renderLabel} - selectedElements={props.selectedElements} - /> -
+ elements.includes(item) && props.onSelect(item)} + onUnselect={props.onUnselect} + placeholder={translate('search.search_for_metrics')} + renderLabel={props.renderLabel} + selectedElements={props.selectedElements} + listSize={0} + /> ); } diff --git a/server/sonar-web/src/main/js/components/activity-graph/GraphsHeader.tsx b/server/sonar-web/src/main/js/components/activity-graph/GraphsHeader.tsx index 971dbd53157..e2165221004 100644 --- a/server/sonar-web/src/main/js/components/activity-graph/GraphsHeader.tsx +++ b/server/sonar-web/src/main/js/components/activity-graph/GraphsHeader.tsx @@ -31,7 +31,6 @@ import * as React from 'react'; import { translate } from '../../helpers/l10n'; import { GraphType } from '../../types/project-activity'; import { Metric } from '../../types/types'; -import Select from '../controls/Select'; import AddGraphMetric from './AddGraphMetric'; import './styles.css'; import { getGraphTypes, isCustomGraph } from './utils'; @@ -47,90 +46,76 @@ interface Props { onUpdateGraph: (graphType: string) => void; } -export default class GraphsHeader extends React.PureComponent { - handleGraphChange = (option: { value: string }) => { - if (option.value !== this.props.graph) { - this.props.onUpdateGraph(option.value); - } - }; +export default function GraphsHeader(props: Props) { + const { + className, + graph, + metrics, + metricsTypeFilter, + onUpdateGraph, + selectedMetrics = [], + } = props; - render() { - const { className, graph, metrics, metricsTypeFilter, selectedMetrics = [] } = this.props; + const handleGraphChange = React.useCallback( + (value: GraphType) => { + if (value !== graph) { + onUpdateGraph(value); + } + }, + [graph, onUpdateGraph] + ); - const noCustomGraph = - this.props.onAddCustomMetric === undefined || this.props.onRemoveCustomMetric === undefined; + const noCustomGraph = + props.onAddCustomMetric === undefined || props.onRemoveCustomMetric === undefined; + const options = React.useMemo(() => { const types = getGraphTypes(noCustomGraph); - const overlayItems: JSX.Element[] = []; - - const selectOptions: Array<{ - label: string; - value: GraphType; - }> = []; - - types.forEach((type) => { + return types.map((type) => { const label = translate('project_activity.graphs', type); - selectOptions.push({ label, value: type }); - - overlayItems.push( - this.handleGraphChange({ value: type })}> + return ( + handleGraphChange(type)}> {label} ); }); + }, [noCustomGraph, handleGraphChange]); - const selectedOption = selectOptions.find((option) => option.value === graph); - const selectedLabel = selectedOption?.label ?? ''; + return ( +
+
+ + + + + + - return ( -
-
-
- {noCustomGraph ? ( - - - - - - - ) : ( -