From: Wouter Admiraal Date: Fri, 26 Aug 2022 13:03:06 +0000 (+0200) Subject: SONAR-16855 [893325] Information or relationship only presented visually X-Git-Tag: 9.7.0.61563~307 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=3d50c83a073845113f2621ce92695a9bc297d7bf;p=sonarqube.git SONAR-16855 [893325] Information or relationship only presented visually --- diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts index 32221bf8792..af7cf936e55 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts +++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts @@ -87,23 +87,23 @@ it('should show hotspot rule section', async () => { expect(await screen.findByRole('heading', { level: 3, name: 'Hot hotspot' })).toBeInTheDocument(); expect(screen.getByText('Introduction to this rule')).toBeInTheDocument(); expect( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.root_cause.SECURITY_HOTSPOT' }) ).toBeInTheDocument(); expect( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.assess_the_problem' }) ).toBeInTheDocument(); expect( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ).toBeInTheDocument(); // Check that we render plain html await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ); @@ -118,18 +118,18 @@ it('should show rule advanced section', async () => { ).toBeInTheDocument(); expect(screen.getByText('Introduction to this rule')).toBeInTheDocument(); expect( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.how_to_fix' }) ).toBeInTheDocument(); expect( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ).toBeInTheDocument(); // Check that we render plain html await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ); @@ -143,13 +143,13 @@ it('should show rule advanced section with context', async () => { await screen.findByRole('heading', { level: 3, name: 'Python rule with context' }) ).toBeInTheDocument(); expect( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.how_to_fix' }) ).toBeInTheDocument(); await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.how_to_fix' }) ); @@ -384,13 +384,13 @@ it('should show notification for rule advanced section and remove it after user name: 'Awesome Python rule with education principles' }); expect( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ).toBeInTheDocument(); await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ); @@ -408,12 +408,12 @@ it('should show notification for rule advanced section and remove it after user ); // navigate away and come back await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.how_to_fix' }) ); await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ); @@ -429,31 +429,31 @@ it('should show notification for rule advanced section and removes it when user name: 'Awesome Python rule with education principles' }); expect( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ).toBeInTheDocument(); // navigate away and come back await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.how_to_fix' }) ); await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ); expect( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ).toBeInTheDocument(); await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ); @@ -469,12 +469,12 @@ it('should show notification for rule advanced section and removes it when user // navigate away and come back await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.how_to_fix' }) ); await user.click( - screen.getByRole('button', { + screen.getByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ); @@ -486,7 +486,7 @@ it('should not show notification for anonymous users', async () => { renderCodingRulesApp(mockCurrentUser(), 'coding_rules?open=rule8'); await user.click( - await screen.findByRole('button', { + await screen.findByRole('tab', { name: 'coding_rules.description_section.title.more_info' }) ); 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 0d3a43a18b8..2774e105574 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 @@ -46,7 +46,7 @@ it('should show education principles', async () => { const user = userEvent.setup(); renderProjectIssuesApp('project/issues?issues=issue2&open=issue2&id=myproject'); await user.click( - await screen.findByRole('button', { name: `coding_rules.description_section.title.more_info` }) + await screen.findByRole('tab', { name: `coding_rules.description_section.title.more_info` }) ); expect(screen.getByRole('heading', { name: 'Defense-In-Depth', level: 3 })).toBeInTheDocument(); }); @@ -59,7 +59,7 @@ it('should open issue and navigate', async () => { // Select an issue with an advanced rule expect(await screen.findByRole('region', { name: 'Fix that' })).toBeInTheDocument(); await user.click(screen.getByRole('region', { name: 'Fix that' })); - expect(screen.getByRole('button', { name: 'issue.tabs.code' })).toBeInTheDocument(); + expect(screen.getByRole('tab', { name: 'issue.tabs.code' })).toBeInTheDocument(); // Are rule headers present? expect(screen.getByRole('heading', { level: 1, name: 'Fix that' })).toBeInTheDocument(); @@ -67,19 +67,19 @@ it('should open issue and navigate', async () => { // Select the "why is this an issue" tab and check its content expect( - screen.getByRole('button', { name: `coding_rules.description_section.title.root_cause` }) + screen.getByRole('tab', { name: `coding_rules.description_section.title.root_cause` }) ).toBeInTheDocument(); await user.click( - screen.getByRole('button', { name: `coding_rules.description_section.title.root_cause` }) + screen.getByRole('tab', { name: `coding_rules.description_section.title.root_cause` }) ); expect(screen.getByRole('heading', { name: 'Because' })).toBeInTheDocument(); // Select the "how to fix it" tab expect( - screen.getByRole('button', { name: `coding_rules.description_section.title.how_to_fix` }) + screen.getByRole('tab', { name: `coding_rules.description_section.title.how_to_fix` }) ).toBeInTheDocument(); await user.click( - screen.getByRole('button', { name: `coding_rules.description_section.title.how_to_fix` }) + screen.getByRole('tab', { name: `coding_rules.description_section.title.how_to_fix` }) ); // Is the context selector present with the expected values and default selection? @@ -103,10 +103,10 @@ it('should open issue and navigate', async () => { // Select the main info tab and check its content expect( - screen.getByRole('button', { name: `coding_rules.description_section.title.more_info` }) + screen.getByRole('tab', { name: `coding_rules.description_section.title.more_info` }) ).toBeInTheDocument(); await user.click( - screen.getByRole('button', { name: `coding_rules.description_section.title.more_info` }) + screen.getByRole('tab', { name: `coding_rules.description_section.title.more_info` }) ); expect(screen.getByRole('heading', { name: 'Link' })).toBeInTheDocument(); @@ -123,10 +123,10 @@ it('should open issue and navigate', async () => { // Select the "why is this an issue tab" and check its content expect( - screen.getByRole('button', { name: `coding_rules.description_section.title.root_cause` }) + screen.getByRole('tab', { name: `coding_rules.description_section.title.root_cause` }) ).toBeInTheDocument(); await user.click( - screen.getByRole('button', { name: `coding_rules.description_section.title.root_cause` }) + screen.getByRole('tab', { name: `coding_rules.description_section.title.root_cause` }) ); expect(screen.getByRole('heading', { name: 'Default' })).toBeInTheDocument(); @@ -413,7 +413,7 @@ it('should show code tabs when any secondary location is selected', async () => // Select the "why is this an issue" tab await user.click( - screen.getByRole('button', { name: 'coding_rules.description_section.title.root_cause' }) + screen.getByRole('tab', { name: 'coding_rules.description_section.title.root_cause' }) ); expect( screen.queryByRole('row', { @@ -430,7 +430,7 @@ it('should show code tabs when any secondary location is selected', async () => // selecting the same selected hotspot location should also navigate back to code page await user.click( - screen.getByRole('button', { name: 'coding_rules.description_section.title.root_cause' }) + screen.getByRole('tab', { name: 'coding_rules.description_section.title.root_cause' }) ); expect( screen.queryByRole('row', { diff --git a/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanel.tsx index 0f1e50315e5..e3be38745b2 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanel.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/MeasuresPanel.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; import { rawSizes } from '../../../app/theme'; -import BoxedTabs from '../../../components/controls/BoxedTabs'; +import BoxedTabs, { getTabId, getTabPanelId } from '../../../components/controls/BoxedTabs'; import ComponentReportActions from '../../../components/controls/ComponentReportActions'; import { Location, withRouter } from '../../../components/hoc/withRouter'; import DeferredSpinner from '../../../components/ui/DeferredSpinner'; @@ -50,8 +50,8 @@ export interface MeasuresPanelProps { } export enum MeasuresPanelTabs { - New, - Overall + New = 'new', + Overall = 'overall' } export function MeasuresPanel(props: MeasuresPanelProps) { @@ -115,9 +115,13 @@ export function MeasuresPanel(props: MeasuresPanelProps) { ) : ( <> - + selectTab(key)} selected={tab} tabs={tabs} /> -
+
{!hasDiffMeasures && isNewCodeTab ? ( ) : ( diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/MeasuresPanel-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/MeasuresPanel-test.tsx.snap index 97d53169603..6936ea45634 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/MeasuresPanel-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/__snapshots__/MeasuresPanel-test.tsx.snap @@ -48,11 +48,11 @@ exports[`should render correctly for applications: default 1`] = `
@@ -64,7 +64,7 @@ exports[`should render correctly for applications: default 1`] = `
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -85,7 +85,10 @@ exports[`should render correctly for applications: default 1`] = ` } />
@@ -786,7 +789,7 @@ exports[`should render correctly for applications: overall 1`] = `
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -807,7 +810,10 @@ exports[`should render correctly for applications: overall 1`] = ` } />
@@ -1718,7 +1724,7 @@ exports[`should render correctly for projects: default 1`] = `
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -1739,7 +1745,10 @@ exports[`should render correctly for projects: default 1`] = ` } />
@@ -2440,7 +2449,7 @@ exports[`should render correctly for projects: overall 1`] = `
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -2461,7 +2470,10 @@ exports[`should render correctly for projects: overall 1`] = ` } />
@@ -3382,7 +3394,7 @@ exports[`should render correctly if branch is misconfigured: hide settings 1`] =
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -3403,7 +3415,10 @@ exports[`should render correctly if branch is misconfigured: hide settings 1`] = } />
@@ -3526,7 +3541,7 @@ exports[`should render correctly if branch is misconfigured: show settings 1`] =
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -3547,7 +3562,10 @@ exports[`should render correctly if branch is misconfigured: show settings 1`] = } />
@@ -3716,7 +3734,7 @@ exports[`should render correctly if there is no coverage 1`] = `
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -3737,7 +3755,10 @@ exports[`should render correctly if there is no coverage 1`] = ` } />
@@ -4170,7 +4191,7 @@ exports[`should render correctly if there is no new code measures 1`] = `
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -4191,7 +4212,10 @@ exports[`should render correctly if there is no new code measures 1`] = ` } />
@@ -4293,7 +4317,7 @@ exports[`should render correctly when code scope is new code 1`] = `
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -4314,7 +4338,10 @@ exports[`should render correctly when code scope is new code 1`] = ` } />
@@ -5015,7 +5042,7 @@ exports[`should render correctly when code scope is overall code 1`] = `
, }, Object { - "key": 1, + "key": "overall", "label":
@@ -5036,7 +5063,10 @@ exports[`should render correctly when code scope is overall code 1`] = ` } />
{ tabs={TABS} /> - +
+ +
{deleting && ( - + + displayPurgeSetting={true} + onDelete={[Function]} + onRename={[Function]} + onUpdatePurgeSetting={[Function]} + title="project_branch_pull_request.table.branch" + /> +
`; @@ -138,64 +144,70 @@ exports[`should render all tabs correctly 2`] = ` ] } /> - + + displayPurgeSetting={false} + onDelete={[Function]} + onRename={[Function]} + onUpdatePurgeSetting={[Function]} + title="project_branch_pull_request.table.pull_request" + /> +
`; diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx index 294e89be5c9..5b0bd74fb42 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx @@ -19,7 +19,7 @@ */ import { groupBy } from 'lodash'; import * as React from 'react'; -import BoxedTabs from '../../../components/controls/BoxedTabs'; +import BoxedTabs, { getTabId, getTabPanelId } from '../../../components/controls/BoxedTabs'; import RuleDescription from '../../../components/rules/RuleDescription'; import { isInput, isShortcut } from '../../../helpers/keyboardEventHelpers'; import { KeyboardKeys } from '../../../helpers/keycodes'; @@ -187,7 +187,13 @@ export default class HotspotViewerTabs extends React.PureComponent return ( <> -
{currentTab.content}
+
+ {currentTab.content} +
); } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerTabs-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerTabs-test.tsx.snap index 90ef112adf5..8f26bcf4641 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerTabs-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerTabs-test.tsx.snap @@ -70,7 +70,10 @@ exports[`should render correctly: fix 1`] = ` } />
0); return ( -
+
{definitions.length === 0 && ( diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap index f6ea40b3c1c..895747d0970 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap @@ -2,7 +2,10 @@ exports[`should render correctly for multi-ALM binding: editing a definition 1`] = `
+ key={currentTab} + role="tabpanel" + aria-labelledby={getTabId(currentTab)} + id={getTabPanelId(currentTab)}>
{ const user = userEvent.setup(); renderAuthentication(); - expect(screen.getAllByRole('button')).toHaveLength(4); + expect(screen.getAllByRole('tab')).toHaveLength(4); - expect(screen.getByRole('button', { name: 'SAML' })).toHaveAttribute('aria-current', 'true'); + expect(screen.getByRole('tab', { name: 'SAML' })).toHaveAttribute('aria-selected', 'true'); - await user.click(screen.getByRole('button', { name: 'github GitHub' })); + await user.click(screen.getByRole('tab', { name: 'github GitHub' })); - expect(screen.getByRole('button', { name: 'SAML' })).toHaveAttribute('aria-current', 'false'); - expect(screen.getByRole('button', { name: 'github GitHub' })).toHaveAttribute( - 'aria-current', + expect(screen.getByRole('tab', { name: 'SAML' })).toHaveAttribute('aria-selected', 'false'); + expect(screen.getByRole('tab', { name: 'github GitHub' })).toHaveAttribute( + 'aria-selected', 'true' ); }); diff --git a/server/sonar-web/src/main/js/components/controls/BoxedTabs.tsx b/server/sonar-web/src/main/js/components/controls/BoxedTabs.tsx index 53682deec15..1c5a028558c 100644 --- a/server/sonar-web/src/main/js/components/controls/BoxedTabs.tsx +++ b/server/sonar-web/src/main/js/components/controls/BoxedTabs.tsx @@ -21,7 +21,7 @@ import styled from '@emotion/styled'; import * as React from 'react'; import { colors, sizes } from '../../app/theme'; -export interface BoxedTabsProps { +export interface BoxedTabsProps { className?: string; onSelect: (key: K) => void; selected?: K; @@ -76,19 +76,21 @@ const ActiveBorder = styled.div<{ active: boolean }>` top: -1px; `; -export default function BoxedTabs(props: BoxedTabsProps) { +export default function BoxedTabs(props: BoxedTabsProps) { const { className, tabs, selected } = props; return ( - + {tabs.map(({ key, label }, i) => ( selected !== key && props.onSelect(key)} - type="button"> + role="tab"> {label} @@ -96,3 +98,11 @@ export default function BoxedTabs(props: BoxedTabsProps) { ); } + +export function getTabPanelId(key: string | number) { + return `tabpanel-${key}`; +} + +export function getTabId(key: string | number) { + return `tab-${key}`; +} diff --git a/server/sonar-web/src/main/js/components/controls/Tooltip.tsx b/server/sonar-web/src/main/js/components/controls/Tooltip.tsx index 57452118cb6..3103862c93c 100644 --- a/server/sonar-web/src/main/js/components/controls/Tooltip.tsx +++ b/server/sonar-web/src/main/js/components/controls/Tooltip.tsx @@ -286,7 +286,10 @@ export class TooltipInner extends React.Component { }; handleBlur = () => { - this.setState({ visible: false }); + if (this.mounted) { + this.setState({ visible: false }); + } + if (this.props.onHide) { this.props.onHide(); } diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/BoxedTabs-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/BoxedTabs-test.tsx index 746aeb63098..a2cab636bb4 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/BoxedTabs-test.tsx +++ b/server/sonar-web/src/main/js/components/controls/__tests__/BoxedTabs-test.tsx @@ -45,7 +45,7 @@ function mountRender(overrides: Partial> = {}) { return mount(dom(overrides)); } -function dom(overrides: Partial>) { +function dom(overrides: Partial>) { return (