diff options
author | 7PH <benjamin.raymond@sonarsource.com> | 2023-02-09 10:34:39 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-02-10 20:02:45 +0000 |
commit | c63c05b51162c9a0d83cdedbf0705347e44184ef (patch) | |
tree | 752adf7cdf5f58f0351bdae97e7750677cb98027 /server/sonar-web/src | |
parent | 2549a33517216e6b0d15fe8b66db7b0f0d953231 (diff) | |
download | sonarqube-c63c05b51162c9a0d83cdedbf0705347e44184ef.tar.gz sonarqube-c63c05b51162c9a0d83cdedbf0705347e44184ef.zip |
SONAR-18361 Improve accessibility of the quality profile compare page
Diffstat (limited to 'server/sonar-web/src')
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> </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')}> </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> </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')}> </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); |