aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2024-07-29 18:38:22 +0200
committersonartech <sonartech@sonarsource.com>2024-07-30 20:02:34 +0000
commit6af98df51f5f85bb47e3661a471637133cdf97f2 (patch)
treeca704d4d69dbf7f055610d39562546d3e32bc6c7 /server
parentb6c22b44e6cdb452270bc52bf04b854b89df55e9 (diff)
downloadsonarqube-6af98df51f5f85bb47e3661a471637133cdf97f2.tar.gz
sonarqube-6af98df51f5f85bb47e3661a471637133cdf97f2.zip
SONAR-22306 Fix a11y of subnavigation components
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/design-system/src/components/subnavigation/SubnavigationItem.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx31
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx22
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx6
5 files changed, 41 insertions, 36 deletions
diff --git a/server/sonar-web/design-system/src/components/subnavigation/SubnavigationItem.tsx b/server/sonar-web/design-system/src/components/subnavigation/SubnavigationItem.tsx
index 0d03117246a..1059fda1364 100644
--- a/server/sonar-web/design-system/src/components/subnavigation/SubnavigationItem.tsx
+++ b/server/sonar-web/design-system/src/components/subnavigation/SubnavigationItem.tsx
@@ -28,15 +28,17 @@ import NavLink, { NavLinkProps } from '../NavLink';
interface Props {
active?: boolean;
+ ariaCurrent?: boolean;
children: ReactNode;
className?: string;
+ id?: string;
innerRef?: (node: HTMLAnchorElement) => void;
onClick: (value?: string) => void;
value?: string;
}
export function SubnavigationItem(props: Readonly<Props>) {
- const { active, className, children, innerRef, onClick, value } = props;
+ const { active, ariaCurrent, className, children, id, innerRef, onClick, value } = props;
const handleClick = useCallback(
(e: SyntheticEvent<HTMLAnchorElement>) => {
e.preventDefault();
@@ -46,9 +48,11 @@ export function SubnavigationItem(props: Readonly<Props>) {
);
return (
<StyledSubnavigationItem
+ aria-current={ariaCurrent}
className={classNames({ active }, className)}
data-testid="js-subnavigation-item"
href="#"
+ id={id}
onClick={handleClick}
ref={innerRef}
>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx b/server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx
index 2329586d95b..6cc9c75fe76 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx
@@ -69,7 +69,7 @@ describe('rendering', () => {
// Overview.
expect(ui.seeDataAsListLink.get()).toBeInTheDocument();
- expect(ui.overviewDomainBtn.get()).toHaveAttribute('aria-current', 'true');
+ expect(ui.overviewDomainLink.get()).toHaveAttribute('aria-current', 'true');
expect(ui.bubbleChart.get()).toBeInTheDocument();
expect(within(ui.bubbleChart.get()).getAllByRole('link')).toHaveLength(8);
expect(ui.newCodePeriodTxt.get()).toBeInTheDocument();
@@ -98,7 +98,7 @@ describe('rendering', () => {
'Maintainability Rating metric.has_rating_X.E',
'Effort to Reach Maintainability Rating A work_duration.x_minutes.1',
].forEach((measure) => {
- expect(ui.measureBtn(measure).get()).toBeInTheDocument();
+ expect(ui.measureLink(measure).get()).toBeInTheDocument();
});
});
@@ -123,7 +123,7 @@ describe('rendering', () => {
'Maintainability Rating metric.has_rating_X.E',
'Effort to Reach Maintainability Rating A work_duration.x_minutes.1',
].forEach((measure) => {
- expect(ui.measureBtn(measure).get()).toBeInTheDocument();
+ expect(ui.measureLink(measure).get()).toBeInTheDocument();
});
expect(screen.getByText('overview.missing_project_dataTRK')).toBeInTheDocument();
});
@@ -207,7 +207,7 @@ describe('rendering', () => {
'component_measures.metric.new_accepted_issues.name 1',
'component_measures.metric.false_positive_issues.name 1',
].forEach((measure) => {
- expect(ui.measureBtn(measure).get()).toBeInTheDocument();
+ expect(ui.measureLink(measure).get()).toBeInTheDocument();
});
});
@@ -308,7 +308,7 @@ describe('rendering', () => {
expect(ui.goToActivityLink.query()).not.toBeInTheDocument();
await user.click(
- ui.measureBtn('component_measures.metric.maintainability_issues.name 2').get(),
+ ui.measureLink('component_measures.metric.maintainability_issues.name 2').get(),
);
expect(ui.goToActivityLink.get()).toHaveAttribute(
'href',
@@ -344,7 +344,7 @@ describe('navigation', () => {
await user.click(ui.maintainabilityDomainBtn.get());
await user.click(
- ui.measureBtn('component_measures.metric.maintainability_issues.name 2').get(),
+ ui.measureLink('component_measures.metric.maintainability_issues.name 2').get(),
);
expect(
within(ui.measuresRow('folderA').get()).getByRole('cell', { name: '2' }),
@@ -377,7 +377,7 @@ describe('navigation', () => {
await user.click(ui.maintainabilityDomainBtn.get());
await user.click(
- ui.measureBtn('component_measures.metric.maintainability_issues.name 2').get(),
+ ui.measureLink('component_measures.metric.maintainability_issues.name 2').get(),
);
// Click list option in view select
@@ -401,7 +401,7 @@ describe('navigation', () => {
await ui.appLoaded();
await user.click(ui.maintainabilityDomainBtn.get());
- await user.click(ui.measureBtn('Maintainability Rating metric.has_rating_X.E').get());
+ await user.click(ui.measureLink('Maintainability Rating metric.has_rating_X.E').get());
// Click treemap option in view select
await user.click(ui.viewSelect.get());
@@ -427,7 +427,7 @@ describe('navigation', () => {
// Drilldown to the file level.
await user.click(ui.maintainabilityDomainBtn.get());
await user.click(
- ui.measureBtn('component_measures.metric.maintainability_issues.name 2').get(),
+ ui.measureLink('component_measures.metric.maintainability_issues.name 2').get(),
);
await ui.arrowDown(); // Select the 1st element ("folderA")
@@ -472,7 +472,7 @@ describe('redirects', () => {
const { ui } = getPageObject();
renderMeasuresApp('component_measures/metric/bugs?id=foo');
await ui.appLoaded();
- expect(ui.measureBtn('component_measures.metric.bugs.name 0').get()).toHaveAttribute(
+ expect(ui.measureLink('component_measures.metric.bugs.name 0').get()).toHaveAttribute(
'aria-current',
'true',
);
@@ -482,10 +482,9 @@ describe('redirects', () => {
const { ui } = getPageObject();
renderMeasuresApp('component_measures/metric/security_issues?id=foo');
await ui.appLoaded();
- expect(ui.measureBtn('component_measures.metric.security_issues.name 1').get()).toHaveAttribute(
- 'aria-current',
- 'true',
- );
+ expect(
+ ui.measureLink('component_measures.metric.security_issues.name 1').get(),
+ ).toHaveAttribute('aria-current', 'true');
});
it('should redirect old domain route', async () => {
@@ -547,7 +546,7 @@ function getPageObject() {
newCodePeriodTxt: byText('component_measures.leak_legend.new_code'),
// Navigation
- overviewDomainBtn: byRole('button', {
+ overviewDomainLink: byRole('link', {
name: 'component_measures.overview.project_overview.subnavigation',
}),
releasabilityDomainBtn: byRole('button', {
@@ -580,7 +579,7 @@ function getPageObject() {
issuesDomainBtn: byRole('button', {
name: 'Issues component_measures.domain_subnavigation.Issues.help',
}),
- measureBtn: (name: string) => byRole('button', { name }),
+ measureLink: (name: string) => byRole('link', { name }),
// Measure content
measuresTable: byRole('table'),
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx
index a90b976c8d5..ae2fd362713 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import {
- BareButton,
HelperHintIcon,
SubnavigationAccordion,
SubnavigationItem,
@@ -87,10 +86,13 @@ export default function DomainSubnavigation(props: Readonly<Props>) {
id={`measure-${domain.name}`}
>
{hasOverview(domain.name) && (
- <SubnavigationItem active={domain.name === selected} onClick={onChange} value={domain.name}>
- <BareButton aria-current={domain.name === selected}>
- {translate('component_measures.domain_overview')}
- </BareButton>
+ <SubnavigationItem
+ active={domain.name === selected}
+ ariaCurrent={domain.name === selected}
+ onClick={onChange}
+ value={domain.name}
+ >
+ {translate('component_measures.domain_overview')}
</SubnavigationItem>
)}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx
index 7bd33db86cd..791b6025f32 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { BareButton, SubnavigationItem } from 'design-system';
+import { SubnavigationItem } from 'design-system';
import React from 'react';
import { MeasureEnhanced } from '../../../types/types';
import SubnavigationMeasureValue from './SubnavigationMeasureValue';
@@ -37,15 +37,17 @@ export default function DomainSubnavigationItem({
}: Readonly<Props>) {
const { key } = measure.metric;
return (
- <SubnavigationItem active={key === selected} key={key} onClick={onChange} value={key}>
- <BareButton
- aria-current={key === selected}
- className="sw-ml-2 sw-w-full sw-flex sw-justify-between"
- id={`measure-${key}-name`}
- >
- {name}
- <SubnavigationMeasureValue measure={measure} />
- </BareButton>
+ <SubnavigationItem
+ active={key === selected}
+ ariaCurrent={key === selected}
+ key={key}
+ onClick={onChange}
+ value={key}
+ className="sw-pl-2 sw-w-full sw-flex sw-justify-between"
+ id={`measure-${key}-name`}
+ >
+ {name}
+ <SubnavigationMeasureValue measure={measure} />
</SubnavigationItem>
);
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx
index 103d5ce0287..2370040305b 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx
@@ -20,7 +20,6 @@
import { withTheme } from '@emotion/react';
import styled from '@emotion/styled';
import {
- BareButton,
LAYOUT_FOOTER_HEIGHT,
LAYOUT_GLOBAL_NAV_HEIGHT,
LAYOUT_PROJECT_NAV_HEIGHT,
@@ -91,11 +90,10 @@ export default function Sidebar(props: Readonly<Props>) {
<SubnavigationGroup>
<SubnavigationItem
active={isProjectOverview(selectedMetric)}
+ ariaCurrent={isProjectOverview(selectedMetric)}
onClick={handleProjectOverviewClick}
>
- <BareButton aria-current={isProjectOverview(selectedMetric)}>
- {translate('component_measures.overview', PROJECT_OVERVEW, 'subnavigation')}
- </BareButton>
+ {translate('component_measures.overview', PROJECT_OVERVEW, 'subnavigation')}
</SubnavigationItem>
</SubnavigationGroup>