aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src
diff options
context:
space:
mode:
author7PH <benjamin.raymond@sonarsource.com>2023-02-09 10:34:39 +0100
committersonartech <sonartech@sonarsource.com>2023-02-10 20:02:45 +0000
commitc63c05b51162c9a0d83cdedbf0705347e44184ef (patch)
tree752adf7cdf5f58f0351bdae97e7750677cb98027 /server/sonar-web/src
parent2549a33517216e6b0d15fe8b66db7b0f0d953231 (diff)
downloadsonarqube-c63c05b51162c9a0d83cdedbf0705347e44184ef.tar.gz
sonarqube-c63c05b51162c9a0d83cdedbf0705347e44184ef.zip
SONAR-18361 Improve accessibility of the quality profile compare page
Diffstat (limited to 'server/sonar-web/src')
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/tables.css6
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx189
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/styles.css10
5 files changed, 118 insertions, 98 deletions
diff --git a/server/sonar-web/src/main/js/app/styles/init/tables.css b/server/sonar-web/src/main/js/app/styles/init/tables.css
index df2e1665f3e..a6317d0229e 100644
--- a/server/sonar-web/src/main/js/app/styles/init/tables.css
+++ b/server/sonar-web/src/main/js/app/styles/init/tables.css
@@ -162,6 +162,12 @@ table.data th.small {
white-space: nowrap;
}
+table.data > caption {
+ padding: 8px 10px;
+ text-align: center;
+ font-weight: bold;
+}
+
table.data > thead > tr > th {
position: relative;
vertical-align: top;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx
index 7d5413dbbff..00c6ac5aba2 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx
@@ -83,9 +83,11 @@ const ui = {
}),
nameCreatePopupInput: byRole('textbox', { name: 'name field_required' }),
comparisonDiffTableHeading: (rulesQuantity: number, profileName: string) =>
- byRole('heading', { name: `quality_profiles.x_rules_only_in.${rulesQuantity} ${profileName}` }),
+ byRole('columnheader', {
+ name: `quality_profiles.x_rules_only_in.${rulesQuantity} ${profileName}`,
+ }),
comparisonModifiedTableHeading: (rulesQuantity: number) =>
- byRole('heading', {
+ byRole('table', {
name: `quality_profiles.x_rules_have_different_configuration.${rulesQuantity}`,
}),
newRuleLink: byRole('link', { name: 'Recently Added Rule' }),
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
index 51b987e0293..333e6aeff30 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
@@ -77,11 +77,6 @@ export default class ComparisonResultActivation extends React.PureComponent<Prop
render() {
const { profile } = this.props;
- const canActivate = !profile.isBuiltIn && profile.actions && profile.actions.edit;
- if (!canActivate) {
- return null;
- }
-
return (
<DeferredSpinner loading={this.state.state === 'opening'}>
<Tooltip
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx
index ad320dec6e3..dde41ffd05f 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx
@@ -17,13 +17,14 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import classNames from 'classnames';
import * as React from 'react';
import { CompareResponse, Profile } from '../../../api/quality-profiles';
import Link from '../../../components/common/Link';
import ChevronLeftIcon from '../../../components/icons/ChevronLeftIcon';
import ChevronRightIcon from '../../../components/icons/ChevronRightIcon';
import SeverityIcon from '../../../components/icons/SeverityIcon';
-import { translateWithParameters } from '../../../helpers/l10n';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getRulesUrl } from '../../../helpers/urls';
import { Dict } from '../../../types/types';
import ComparisonEmpty from './ComparisonEmpty';
@@ -38,6 +39,10 @@ interface Props extends CompareResponse {
}
export default class ComparisonResults extends React.PureComponent<Props> {
+ canActivate(profile: Profile) {
+ return !profile.isBuiltIn && profile.actions && profile.actions.edit;
+ }
+
renderRule(rule: { key: string; name: string }, severity: string) {
return (
<div>
@@ -70,38 +75,43 @@ export default class ComparisonResults extends React.PureComponent<Props> {
if (this.props.inLeft.length === 0) {
return null;
}
+
+ const renderSecondColumn = this.props.rightProfile && this.canActivate(this.props.rightProfile);
+
return (
- <>
- <tr>
- <td>
- <h6>
+ <table className="data fixed zebra">
+ <thead>
+ <tr>
+ <th>
{translateWithParameters(
'quality_profiles.x_rules_only_in',
this.props.inLeft.length
)}{' '}
{this.props.left.name}
- </h6>
- </td>
- <td>&nbsp;</td>
- </tr>
- {this.props.inLeft.map((rule) => (
- <tr className="js-comparison-in-left" key={`left-${rule.key}`}>
- <td>{this.renderRule(rule, rule.severity)}</td>
- <td>
- {this.props.rightProfile && (
- <ComparisonResultActivation
- key={rule.key}
- onDone={this.props.refresh}
- profile={this.props.rightProfile}
- ruleKey={rule.key}
- >
- <ChevronRightIcon />
- </ComparisonResultActivation>
- )}
- </td>
+ </th>
+ {renderSecondColumn && <th aria-label={translate('actions')}>&nbsp;</th>}
</tr>
- ))}
- </>
+ </thead>
+ <tbody>
+ {this.props.inLeft.map((rule) => (
+ <tr key={`left-${rule.key}`}>
+ <td>{this.renderRule(rule, rule.severity)}</td>
+ {renderSecondColumn && (
+ <td>
+ <ComparisonResultActivation
+ key={rule.key}
+ onDone={this.props.refresh}
+ profile={this.props.rightProfile as Profile}
+ ruleKey={rule.key}
+ >
+ <ChevronRightIcon />
+ </ComparisonResultActivation>
+ </td>
+ )}
+ </tr>
+ ))}
+ </tbody>
+ </table>
);
}
@@ -109,36 +119,47 @@ export default class ComparisonResults extends React.PureComponent<Props> {
if (this.props.inRight.length === 0) {
return null;
}
+
+ const renderFirstColumn = this.props.leftProfile && this.canActivate(this.props.leftProfile);
+
return (
- <>
- <tr>
- <td>&nbsp;</td>
- <td>
- <h6>
+ <table
+ className={classNames('data fixed zebra quality-profile-compare-right-table', {
+ 'has-first-column': renderFirstColumn,
+ })}
+ >
+ <thead>
+ <tr>
+ {renderFirstColumn && <th aria-label={translate('actions')}>&nbsp;</th>}
+ <th>
{translateWithParameters(
'quality_profiles.x_rules_only_in',
this.props.inRight.length
)}{' '}
{this.props.right.name}
- </h6>
- </td>
- </tr>
- {this.props.inRight.map((rule) => (
- <tr className="js-comparison-in-right" key={`right-${rule.key}`}>
- <td className="text-right">
- <ComparisonResultActivation
- key={rule.key}
- onDone={this.props.refresh}
- profile={this.props.leftProfile}
- ruleKey={rule.key}
- >
- <ChevronLeftIcon />
- </ComparisonResultActivation>
- </td>
- <td>{this.renderRule(rule, rule.severity)}</td>
+ </th>
</tr>
- ))}
- </>
+ </thead>
+ <tbody>
+ {this.props.inRight.map((rule) => (
+ <tr key={`right-${rule.key}`}>
+ {renderFirstColumn && (
+ <td className="text-right">
+ <ComparisonResultActivation
+ key={rule.key}
+ onDone={this.props.refresh}
+ profile={this.props.leftProfile}
+ ruleKey={rule.key}
+ >
+ <ChevronLeftIcon />
+ </ComparisonResultActivation>
+ </td>
+ )}
+ <td>{this.renderRule(rule, rule.severity)}</td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
);
}
@@ -147,38 +168,34 @@ export default class ComparisonResults extends React.PureComponent<Props> {
return null;
}
return (
- <>
- <tr>
- <td className="text-center" colSpan={2}>
- <h6>
- {translateWithParameters(
- 'quality_profiles.x_rules_have_different_configuration',
- this.props.modified.length
- )}
- </h6>
- </td>
- </tr>
- <tr>
- <td>
- <h6>{this.props.left.name}</h6>
- </td>
- <td>
- <h6>{this.props.right.name}</h6>
- </td>
- </tr>
- {this.props.modified.map((rule) => (
- <tr className="js-comparison-modified" key={`modified-${rule.key}`}>
- <td>
- {this.renderRule(rule, rule.left.severity)}
- {this.renderParameters(rule.left.params)}
- </td>
- <td>
- {this.renderRule(rule, rule.right.severity)}
- {this.renderParameters(rule.right.params)}
- </td>
+ <table className="data fixed zebra zebra-inversed">
+ <caption>
+ {translateWithParameters(
+ 'quality_profiles.x_rules_have_different_configuration',
+ this.props.modified.length
+ )}
+ </caption>
+ <thead>
+ <tr>
+ <th>{this.props.left.name}</th>
+ <th>{this.props.right.name}</th>
</tr>
- ))}
- </>
+ </thead>
+ <tbody>
+ {this.props.modified.map((rule) => (
+ <tr key={`modified-${rule.key}`}>
+ <td>
+ {this.renderRule(rule, rule.left.severity)}
+ {this.renderParameters(rule.left.params)}
+ </td>
+ <td>
+ {this.renderRule(rule, rule.right.severity)}
+ {this.renderParameters(rule.right.params)}
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
);
}
@@ -188,13 +205,11 @@ export default class ComparisonResults extends React.PureComponent<Props> {
}
return (
- <table className="data zebra quality-profile-comparison-table">
- <tbody>
- {this.renderLeft()}
- {this.renderRight()}
- {this.renderModified()}
- </tbody>
- </table>
+ <>
+ {this.renderLeft()}
+ {this.renderRight()}
+ {this.renderModified()}
+ </>
);
}
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/styles.css b/server/sonar-web/src/main/js/apps/quality-profiles/styles.css
index 3b7c844023f..4fb6b68c3e7 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/styles.css
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/styles.css
@@ -99,10 +99,6 @@
padding: 0;
}
-.quality-profile-comparison-table {
- table-layout: fixed;
-}
-
.quality-profile-changelog-rule-cell {
line-height: 1.5;
}
@@ -112,6 +108,12 @@
word-break: break-word;
}
+.quality-profile-compare-right-table:not(.has-first-column) td,
+.quality-profile-compare-right-table:not(.has-first-column) th {
+ /* Aligns the first column with the second one (50%) and add usual cell padding */
+ padding-left: calc(50% + 10px);
+}
+
#create-profile-form .radio-card {
width: 245px;
background-color: var(--neutral50);