diff options
author | Ambroise C <ambroise.christea@sonarsource.com> | 2023-12-07 17:09:43 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-12-11 20:02:55 +0000 |
commit | 802e72275499a78b01f57f9ae60bb5df52078ed0 (patch) | |
tree | 68a5fedcb31dff1389d3228be0974918e3b8ba57 /server | |
parent | 6874d8004f4e3b059abdf9ad38e0458397caae67 (diff) | |
download | sonarqube-802e72275499a78b01f57f9ae60bb5df52078ed0.tar.gz sonarqube-802e72275499a78b01f57f9ae60bb5df52078ed0.zip |
SONAR-20982 Align display of issue's data/execution flows with SC
Diffstat (limited to 'server')
4 files changed, 66 insertions, 38 deletions
diff --git a/server/sonar-web/design-system/src/components/ExecutionFlowAccordion.tsx b/server/sonar-web/design-system/src/components/ExecutionFlowAccordion.tsx index 73ca6d46a08..e324b4dd558 100644 --- a/server/sonar-web/design-system/src/components/ExecutionFlowAccordion.tsx +++ b/server/sonar-web/design-system/src/components/ExecutionFlowAccordion.tsx @@ -29,19 +29,21 @@ interface Props { children: ReactNode; expanded?: boolean; header: ReactNode; + hidden?: boolean; id: string; innerRef?: (node: HTMLDivElement) => void; onClick?: () => void; } export function ExecutionFlowAccordion(props: Props) { - const { children, expanded, header, id, innerRef, onClick } = props; + const { children, expanded, header, hidden, id, innerRef, onClick } = props; return ( - <Accordion className={classNames({ expanded })} ref={innerRef}> + <Accordion className={classNames({ expanded, 'sw-hidden': hidden })} ref={innerRef}> <Expander aria-controls={`${id}-flow-accordion`} aria-expanded={expanded} + aria-hidden={hidden} id={`${id}-flow-accordion-button`} onClick={onClick} > diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx index 3b94be4800e..fdc438e8319 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx @@ -93,8 +93,8 @@ describe('issue app', () => { const dataFlowButton = await screen.findByRole('button', { name: 'issue.flow.x_steps.2 Backtracking 1', }); - const exectionFlowButton = screen.getByRole('button', { - name: 'issue.flow.x_steps.3 issue.full_execution_flow', + const exectionFlowButton = screen.getByRole('link', { + name: 'issue.show_full_execution_flow.3', }); let dataLocation1Button = screen.getByLabelText('Data location 1'); diff --git a/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/IssueItemLocationsQuantity.tsx b/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/IssueItemLocationsQuantity.tsx index 112e5c575a6..36e73763ed2 100644 --- a/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/IssueItemLocationsQuantity.tsx +++ b/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/IssueItemLocationsQuantity.tsx @@ -20,7 +20,7 @@ import { ExecutionFlowIcon } from 'design-system'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; -import { Issue } from '../../../types/types'; +import { FlowType, Issue } from '../../../types/types'; interface Props { issue: Pick<Issue, 'flows' | 'flowsWithType' | 'secondaryLocations'>; @@ -50,7 +50,11 @@ function getLocationsText(issue: Props['issue']) { } else if (flows.length > 1) { return { quantity: flows.length, message: translate('issues.execution_flows') }; } else if (flowsWithType.length > 1) { - return { quantity: flowsWithType.length, message: translate('issues.execution_flows') }; + const dataFlows = flowsWithType.filter(({ type }) => type === FlowType.DATA); + return { + quantity: dataFlows.length, + message: translate(dataFlows.length > 1 ? 'issues.data_flows' : 'issues.data_flow'), + }; } else if (secondaryLocations.length === 1) { return { quantity: secondaryLocations.length, message: translate('issues.location') }; } else if (secondaryLocations.length > 1) { diff --git a/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/IssueLocationsNavigator.tsx b/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/IssueLocationsNavigator.tsx index e8fc6b62493..4e9906ffcf7 100644 --- a/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/IssueLocationsNavigator.tsx +++ b/server/sonar-web/src/main/js/apps/issues/issues-subnavigation/IssueLocationsNavigator.tsx @@ -17,8 +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 { ExecutionFlowAccordion, SubnavigationFlowSeparator } from 'design-system'; -import React, { useCallback, useRef } from 'react'; +import { DiscreetLink, ExecutionFlowAccordion, SubnavigationFlowSeparator } from 'design-system'; +import React, { Fragment, useCallback, useRef } from 'react'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { Flow, FlowType, Issue } from '../../../types/types'; import { getLocations } from '../utils'; @@ -98,32 +98,56 @@ export default function IssueLocationsNavigator(props: Props) { <> <div className="sw-flex sw-flex-col sw-gap-4 sw-mt-4"> {flows.map((flow, index) => ( - <ExecutionFlowAccordion - expanded={index === selectedFlowIndex} - header={ - <span> - <strong> - {flow.locations.length > 1 - ? translateWithParameters('issue.flow.x_steps', flow.locations.length) - : translate('issue.flow.1_step')} - </strong>{' '} - {getExecutionFlowLabel(flow, hasFlowsWithType)} - </span> - } - id={`${issue.key}-flow-${index}`} - innerRef={(n) => (accordionElement.current = n)} - key={`${issue.key}-flow-${index}`} - onClick={() => { - handleAccordionClick(index); - }} - > - <IssueLocations - issue={issue} - locations={flow.locations} - onLocationSelect={onLocationSelect} - selectedLocationIndex={selectedLocationIndex} - /> - </ExecutionFlowAccordion> + <Fragment key={`${issue.key}-flow-${index}`}> + <ExecutionFlowAccordion + expanded={index === selectedFlowIndex} + header={ + <span> + <strong> + {flow.locations.length > 1 + ? translateWithParameters('issue.flow.x_steps', flow.locations.length) + : translate('issue.flow.1_step')} + </strong>{' '} + {getExecutionFlowLabel(flow, hasFlowsWithType)} + </span> + } + hidden={ + index !== selectedFlowIndex && + flow.type === FlowType.EXECUTION && + hasFlowsWithType + } + id={`${issue.key}-flow-${index}`} + innerRef={(n) => (accordionElement.current = n)} + onClick={() => { + handleAccordionClick(index); + }} + > + <IssueLocations + issue={issue} + locations={flow.locations} + onLocationSelect={onLocationSelect} + selectedLocationIndex={selectedLocationIndex} + /> + </ExecutionFlowAccordion> + {index !== selectedFlowIndex && + flow.type === FlowType.EXECUTION && + hasFlowsWithType && ( + <div> + <DiscreetLink + onClick={() => { + handleAccordionClick(index); + }} + preventDefault + to="{{}}" + > + {translateWithParameters( + 'issue.show_full_execution_flow', + flow.locations.length, + )} + </DiscreetLink> + </div> + )} + </Fragment> ))} </div> <IssueLocationsNavigatorKeyboardHint showLeftRightHint /> @@ -135,10 +159,8 @@ export default function IssueLocationsNavigator(props: Props) { } function getExecutionFlowLabel(flow: Flow, hasFlowsWithType: boolean) { - if (hasFlowsWithType) { - return flow.type === FlowType.EXECUTION - ? translate('issue.full_execution_flow') - : flow.description; + if (hasFlowsWithType && flow.type !== FlowType.EXECUTION) { + return flow.description; } return translate('issues.execution_flow'); |