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'
})
);
).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'
})
);
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'
})
);
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'
})
);
);
// 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'
})
);
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'
})
);
// 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'
})
);
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'
})
);
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();
});
// 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();
// 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?
// 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();
// 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();
// 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', {
// 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', {
*/
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';
}
export enum MeasuresPanelTabs {
- New,
- Overall
+ New = 'new',
+ Overall = 'overall'
}
export function MeasuresPanel(props: MeasuresPanelProps) {
</div>
) : (
<>
- <BoxedTabs onSelect={selectTab} selected={tab} tabs={tabs} />
+ <BoxedTabs onSelect={key => selectTab(key)} selected={tab} tabs={tabs} />
- <div className="overview-panel-content flex-1 bordered">
+ <div
+ className="overview-panel-content flex-1 bordered"
+ role="tabpanel"
+ id={getTabPanelId(tab)}
+ aria-labelledby={getTabId(tab)}>
{!hasDiffMeasures && isNewCodeTab ? (
<MeasuresPanelNoNewCode branch={branch} component={component} period={period} />
) : (
</div>
<BoxedTabs
onSelect={[Function]}
- selected={0}
+ selected="new"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-new"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-new"
+ role="tabpanel"
>
<MeasuresPanelIssueMeasureRow
branchLike={
</div>
<BoxedTabs
onSelect={[Function]}
- selected={1}
+ selected="overall"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-overall"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-overall"
+ role="tabpanel"
>
<MeasuresPanelIssueMeasureRow
branchLike={
</div>
<BoxedTabs
onSelect={[Function]}
- selected={0}
+ selected="new"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-new"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-new"
+ role="tabpanel"
>
<MeasuresPanelIssueMeasureRow
branchLike={
</div>
<BoxedTabs
onSelect={[Function]}
- selected={1}
+ selected="overall"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-overall"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-overall"
+ role="tabpanel"
>
<MeasuresPanelIssueMeasureRow
branchLike={
</div>
<BoxedTabs
onSelect={[Function]}
- selected={0}
+ selected="new"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-new"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-new"
+ role="tabpanel"
>
<MeasuresPanelNoNewCode
branch={
</div>
<BoxedTabs
onSelect={[Function]}
- selected={0}
+ selected="new"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-new"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-new"
+ role="tabpanel"
>
<MeasuresPanelNoNewCode
branch={
</div>
<BoxedTabs
onSelect={[Function]}
- selected={0}
+ selected="new"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-new"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-new"
+ role="tabpanel"
>
<MeasuresPanelIssueMeasureRow
branchLike={
</div>
<BoxedTabs
onSelect={[Function]}
- selected={0}
+ selected="new"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-new"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-new"
+ role="tabpanel"
>
<MeasuresPanelNoNewCode
branch={
</div>
<BoxedTabs
onSelect={[Function]}
- selected={0}
+ selected="new"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-new"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-new"
+ role="tabpanel"
>
<MeasuresPanelIssueMeasureRow
branchLike={
</div>
<BoxedTabs
onSelect={[Function]}
- selected={1}
+ selected="overall"
tabs={
Array [
Object {
- "key": 0,
+ "key": "new",
"label": <div
className="text-left overview-measures-tab"
>
</div>,
},
Object {
- "key": 1,
+ "key": "overall",
"label": <div
className="text-left overview-measures-tab"
>
}
/>
<div
+ aria-labelledby="tab-overall"
className="overview-panel-content flex-1 bordered"
+ id="tabpanel-overall"
+ role="tabpanel"
>
<MeasuresPanelIssueMeasureRow
branchLike={
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import BoxedTabs from '../../../components/controls/BoxedTabs';
+import BoxedTabs, { getTabId, getTabPanelId } from '../../../components/controls/BoxedTabs';
import BranchIcon from '../../../components/icons/BranchIcon';
import PullRequestIcon from '../../../components/icons/PullRequestIcon';
import {
tabs={TABS}
/>
- <BranchLikeTable
- branchLikes={branchLikesToDisplay}
- component={component}
- displayPurgeSetting={isBranchMode}
- onDelete={this.handleDeleteBranchLike}
- onRename={this.handleRenameBranchLike}
- onUpdatePurgeSetting={this.handleUpdatePurgeSetting}
- title={title}
- />
+ <div role="tabpanel" id={getTabPanelId(currentTab)} aria-labelledby={getTabId(currentTab)}>
+ <BranchLikeTable
+ branchLikes={branchLikesToDisplay}
+ component={component}
+ displayPurgeSetting={isBranchMode}
+ onDelete={this.handleDeleteBranchLike}
+ onRename={this.handleRenameBranchLike}
+ onUpdatePurgeSetting={this.handleUpdatePurgeSetting}
+ title={title}
+ />
+ </div>
{deleting && (
<DeleteBranchModal
]
}
/>
- <Memo(BranchLikeTable)
- branchLikes={
- Array [
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- },
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": false,
- "name": "branch-1",
- },
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": false,
- "name": "branch-11",
- },
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": false,
- "name": "branch-12",
- },
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": false,
- "name": "branch-2",
- },
- Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": false,
- "name": "branch-3",
- },
- ]
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
+ <div
+ aria-labelledby="tab-0"
+ id="tabpanel-0"
+ role="tabpanel"
+ >
+ <Memo(BranchLikeTable)
+ branchLikes={
+ Array [
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": true,
+ "name": "master",
+ },
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": false,
+ "name": "branch-1",
+ },
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": false,
+ "name": "branch-11",
+ },
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": false,
+ "name": "branch-12",
+ },
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": false,
+ "name": "branch-2",
+ },
Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": false,
+ "name": "branch-3",
+ },
+ ]
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
"name": "Sonar way",
},
- ],
- "tags": Array [],
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
}
- }
- displayPurgeSetting={true}
- onDelete={[Function]}
- onRename={[Function]}
- onUpdatePurgeSetting={[Function]}
- title="project_branch_pull_request.table.branch"
- />
+ displayPurgeSetting={true}
+ onDelete={[Function]}
+ onRename={[Function]}
+ onUpdatePurgeSetting={[Function]}
+ title="project_branch_pull_request.table.branch"
+ />
+ </div>
</Fragment>
`;
]
}
/>
- <Memo(BranchLikeTable)
- branchLikes={
- Array [
- Object {
- "analysisDate": "2018-01-01",
- "base": "master",
- "branch": "feature/foo/bar",
- "key": "1",
- "target": "master",
- "title": "PR-1",
- },
- Object {
- "analysisDate": "2018-01-01",
- "base": "master",
- "branch": "feature/foo/bar",
- "key": "2",
- "target": "master",
- "title": "PR-2",
- },
- Object {
- "analysisDate": "2018-01-01",
- "base": "master",
- "branch": "feature/foo/bar",
- "isOrphan": true,
- "key": "2",
- "target": "llb-100",
- "title": "PR-2",
- },
- ]
- }
- component={
- Object {
- "breadcrumbs": Array [],
- "key": "my-project",
- "name": "MyProject",
- "qualifier": "TRK",
- "qualityGate": Object {
- "isDefault": true,
- "key": "30",
- "name": "Sonar way",
- },
- "qualityProfiles": Array [
+ <div
+ aria-labelledby="tab-1"
+ id="tabpanel-1"
+ role="tabpanel"
+ >
+ <Memo(BranchLikeTable)
+ branchLikes={
+ Array [
Object {
- "deleted": false,
- "key": "my-qp",
- "language": "ts",
+ "analysisDate": "2018-01-01",
+ "base": "master",
+ "branch": "feature/foo/bar",
+ "key": "1",
+ "target": "master",
+ "title": "PR-1",
+ },
+ Object {
+ "analysisDate": "2018-01-01",
+ "base": "master",
+ "branch": "feature/foo/bar",
+ "key": "2",
+ "target": "master",
+ "title": "PR-2",
+ },
+ Object {
+ "analysisDate": "2018-01-01",
+ "base": "master",
+ "branch": "feature/foo/bar",
+ "isOrphan": true,
+ "key": "2",
+ "target": "llb-100",
+ "title": "PR-2",
+ },
+ ]
+ }
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
"name": "Sonar way",
},
- ],
- "tags": Array [],
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
}
- }
- displayPurgeSetting={false}
- onDelete={[Function]}
- onRename={[Function]}
- onUpdatePurgeSetting={[Function]}
- title="project_branch_pull_request.table.pull_request"
- />
+ displayPurgeSetting={false}
+ onDelete={[Function]}
+ onRename={[Function]}
+ onUpdatePurgeSetting={[Function]}
+ title="project_branch_pull_request.table.pull_request"
+ />
+ </div>
</Fragment>
`;
*/
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';
return (
<>
<BoxedTabs onSelect={this.handleSelectTabs} selected={currentTab.key} tabs={tabs} />
- <div className="bordered huge-spacer-bottom">{currentTab.content}</div>
+ <div
+ className="bordered huge-spacer-bottom"
+ role="tabpanel"
+ aria-labelledby={getTabId(currentTab.key)}
+ id={getTabPanelId(currentTab.key)}>
+ {currentTab.content}
+ </div>
</>
);
}
}
/>
<div
+ aria-labelledby="tab-fix"
className="bordered huge-spacer-bottom"
+ id="tabpanel-fix"
+ role="tabpanel"
>
<RuleDescription
className="big-padded"
}
/>
<div
+ aria-labelledby="tab-code"
className="bordered huge-spacer-bottom"
+ id="tabpanel-code"
+ role="tabpanel"
>
<div
className="padded"
}
/>
<div
+ aria-labelledby="tab-vulnerability"
className="bordered huge-spacer-bottom"
+ id="tabpanel-vulnerability"
+ role="tabpanel"
>
<RuleDescription
className="big-padded"
}
/>
<div
+ aria-labelledby="tab-code"
className="bordered huge-spacer-bottom"
+ id="tabpanel-code"
+ role="tabpanel"
>
<div
className="padded"
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import Link from '../../../../components/common/Link';
+import { getTabId, getTabPanelId } from '../../../../components/controls/BoxedTabs';
import { Button } from '../../../../components/controls/buttons';
import { Alert } from '../../../../components/ui/Alert';
import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
const preventCreation = loadingProjectCount || (!multipleAlmEnabled && definitions.length > 0);
return (
- <div className="bordered">
+ <div
+ className="bordered"
+ role="tabpanel"
+ id={getTabPanelId(almTab)}
+ aria-labelledby={getTabId(almTab)}>
<div className="big-padded">
<DeferredSpinner loading={loadingAlmDefinitions}>
{definitions.length === 0 && (
exports[`should render correctly for multi-ALM binding: editing a definition 1`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly for multi-ALM binding: loaded 1`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly for multi-ALM binding: loading ALM definitions 1`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly for multi-ALM binding: loading project count 1`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly for single-ALM binding 1`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly for single-ALM binding 2`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly for single-ALM binding 3`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly with validation: create a first 1`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly with validation: create a second 1`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly with validation: default 1`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly with validation: empty 1`] = `
<div
+ aria-labelledby="tab-azure"
className="bordered"
+ id="tabpanel-azure"
+ role="tabpanel"
>
<div
className="big-padded"
exports[`should render correctly with validation: pass the correct key for bitbucket cloud 1`] = `
<div
+ aria-labelledby="tab-bitbucket"
className="bordered"
+ id="tabpanel-bitbucket"
+ role="tabpanel"
>
<div
className="big-padded"
import { useSearchParams } from 'react-router-dom';
import Link from '../../../../components/common/Link';
import ScreenPositionHelper from '../../../../components/common/ScreenPositionHelper';
-import BoxedTabs from '../../../../components/controls/BoxedTabs';
+import BoxedTabs, { getTabId, getTabPanelId } from '../../../../components/controls/BoxedTabs';
import { Alert } from '../../../../components/ui/Alert';
import { translate } from '../../../../helpers/l10n';
import { getBaseUrl } from '../../../../helpers/system';
maxHeight: `calc(100vh - ${top + HEIGHT_ADJUSTMENT}px)`
}}
className="bordered overflow-y-auto tabbed-definitions"
- key={currentTab}>
+ key={currentTab}
+ role="tabpanel"
+ aria-labelledby={getTabId(currentTab)}
+ id={getTabPanelId(currentTab)}>
<div className="big-padded">
<Alert variant="info">
<FormattedMessage
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'
);
});
import * as React from 'react';
import { colors, sizes } from '../../app/theme';
-export interface BoxedTabsProps<K> {
+export interface BoxedTabsProps<K extends string | number> {
className?: string;
onSelect: (key: K) => void;
selected?: K;
top: -1px;
`;
-export default function BoxedTabs<K>(props: BoxedTabsProps<K>) {
+export default function BoxedTabs<K extends string | number>(props: BoxedTabsProps<K>) {
const { className, tabs, selected } = props;
return (
- <TabContainer className={className}>
+ <TabContainer className={className} role="tablist">
{tabs.map(({ key, label }, i) => (
<StyledTab
+ id={getTabId(key)}
active={selected === key}
- aria-current={selected === key}
+ aria-selected={selected === key}
+ aria-controls={getTabPanelId(key)}
// eslint-disable-next-line react/no-array-index-key
key={i}
onClick={() => selected !== key && props.onSelect(key)}
- type="button">
+ role="tab">
<ActiveBorder active={selected === key} />
{label}
</StyledTab>
</TabContainer>
);
}
+
+export function getTabPanelId(key: string | number) {
+ return `tabpanel-${key}`;
+}
+
+export function getTabId(key: string | number) {
+ return `tab-${key}`;
+}
};
handleBlur = () => {
- this.setState({ visible: false });
+ if (this.mounted) {
+ this.setState({ visible: false });
+ }
+
if (this.props.onHide) {
this.props.onHide();
}
return mount(dom(overrides));
}
-function dom<K>(overrides: Partial<BoxedTabsProps<K>>) {
+function dom(overrides: Partial<BoxedTabsProps<string>>) {
return (
<BoxedTabs
className="boxed-tabs"
>
<Styled(div)
className="boxed-tabs"
+ role="tablist"
>
<Insertion
cache={
/>
<div
className="boxed-tabs emotion-6"
+ role="tablist"
>
<Styled(button)
active={true}
- aria-current={true}
+ aria-controls="tabpanel-a"
+ aria-selected={true}
+ id="tab-a"
key="0"
onClick={[Function]}
- type="button"
+ role="tab"
>
<Insertion
cache={
}
/>
<button
- aria-current={true}
+ aria-controls="tabpanel-a"
+ aria-selected={true}
className="emotion-1"
+ id="tab-a"
onClick={[Function]}
- type="button"
+ role="tab"
>
<Styled(div)
active={true}
</Styled(button)>
<Styled(button)
active={false}
- aria-current={false}
+ aria-controls="tabpanel-b"
+ aria-selected={false}
+ id="tab-b"
key="1"
onClick={[Function]}
- type="button"
+ role="tab"
>
<Insertion
cache={
}
/>
<button
- aria-current={false}
+ aria-controls="tabpanel-b"
+ aria-selected={false}
className="emotion-3"
+ id="tab-b"
onClick={[Function]}
- type="button"
+ role="tab"
>
<Styled(div)
active={false}
</Styled(button)>
<Styled(button)
active={false}
- aria-current={false}
+ aria-controls="tabpanel-c"
+ aria-selected={false}
+ id="tab-c"
key="2"
onClick={[Function]}
- type="button"
+ role="tab"
>
<Insertion
cache={
}
/>
<button
- aria-current={false}
+ aria-controls="tabpanel-c"
+ aria-selected={false}
className="emotion-3"
+ id="tab-c"
onClick={[Function]}
- type="button"
+ role="tab"
>
<Styled(div)
active={false}
import { RuleDetails } from '../../types/types';
import { NoticeType } from '../../types/users';
import ScreenPositionHelper from '../common/ScreenPositionHelper';
-import BoxedTabs from '../controls/BoxedTabs';
+import BoxedTabs, { getTabId, getTabPanelId } from '../controls/BoxedTabs';
import MoreInfoRuleDescription from './MoreInfoRuleDescription';
import RuleDescription from './RuleDescription';
import './style.css';
// We substract the footer height with padding (80) and the main layout padding (20)
maxHeight: scrollInTab ? `calc(100vh - ${top + 100}px)` : 'initial'
}}
- className="bordered display-flex-column">
+ className="bordered display-flex-column"
+ role="tabpanel"
+ aria-labelledby={getTabId(selectedTab.key)}
+ id={getTabPanelId(selectedTab.key)}>
{/* Adding a key to force re-rendering of the tab container, so that it resets the scroll position */}
<div className="overflow-y-auto spacer" key={selectedTab.key}>
{tabContent}