]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19526 Improve accessibility of FlagMessage
authorMathieu Suen <mathieu.suen@sonarsource.com>
Tue, 13 Jun 2023 11:38:13 +0000 (13:38 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 14 Jun 2023 09:51:07 +0000 (09:51 +0000)
17 files changed:
server/sonar-web/design-system/src/components/FlagMessage.tsx
server/sonar-web/design-system/src/components/__tests__/FlagMessage-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/AnalysisStatus.tsx
server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx
server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/FilesView.tsx
server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx
server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/ListStyleFacet.tsx
server/sonar-web/src/main/js/apps/overview/branches/ApplicationNonCaycProjectWarning.tsx
server/sonar-web/src/main/js/apps/overview/branches/CleanAsYouCodeWarning.tsx
server/sonar-web/src/main/js/apps/overview/branches/NoCodeWarning.tsx
server/sonar-web/src/main/js/apps/overview/components/IgnoredConditionWarning.tsx
server/sonar-web/src/main/js/components/SourceViewer/components/DuplicationPopup.tsx
server/sonar-web/src/main/js/components/rules/RuleDescription.tsx

index 72ef3a9808ffa78ec60accd44fe88ae910f5d173..5823faae568195a5d3c079d760e239026531cd05 100644 (file)
@@ -28,64 +28,51 @@ import { FlagErrorIcon, FlagInfoIcon, FlagSuccessIcon, FlagWarningIcon } from '.
 export type Variant = 'error' | 'warning' | 'success' | 'info';
 
 interface Props {
-  ariaLabel: string;
   variant: Variant;
 }
 
-interface VariantInformation {
-  backGroundColor: ThemeColors;
-  borderColor: ThemeColors;
-  icon: JSX.Element;
-  role: string;
-}
-
-function getVariantInfo(variant: Variant): VariantInformation {
-  const variantList: Record<Variant, VariantInformation> = {
+function getVariantInfo(variant: Variant) {
+  const variantList = {
     error: {
       icon: <FlagErrorIcon />,
       borderColor: 'errorBorder',
       backGroundColor: 'errorBackground',
-      role: 'alert',
     },
     warning: {
       icon: <FlagWarningIcon />,
       borderColor: 'warningBorder',
       backGroundColor: 'warningBackground',
-      role: 'alert',
     },
     success: {
       icon: <FlagSuccessIcon />,
       borderColor: 'successBorder',
       backGroundColor: 'successBackground',
-      role: 'status',
     },
     info: {
       icon: <FlagInfoIcon />,
       borderColor: 'infoBorder',
       backGroundColor: 'infoBackground',
-      role: 'status',
     },
-  };
+  } as const;
 
   return variantList[variant];
 }
 
 export function FlagMessage(props: Props & React.HTMLAttributes<HTMLDivElement>) {
-  const { ariaLabel, className, variant, ...domProps } = props;
+  const { className, variant, ...domProps } = props;
   const variantInfo = getVariantInfo(variant);
 
   return (
     <StyledFlag
-      aria-label={ariaLabel}
+      backGroundColor={variantInfo.backGroundColor}
+      borderColor={variantInfo.borderColor}
       className={classNames('alert', className)}
-      role={variantInfo.role}
-      variantInfo={variantInfo}
       {...domProps}
     >
-      <StyledFlagInner>
-        <StyledFlagIcon variantInfo={variantInfo}>{variantInfo.icon}</StyledFlagIcon>
-        <StyledFlagContent>{props.children}</StyledFlagContent>
-      </StyledFlagInner>
+      <div className="flag-inner">
+        <div className="flag-icon">{variantInfo.icon}</div>
+        <div className="flag-content">{props.children}</div>
+      </div>
     </StyledFlag>
   );
 }
@@ -93,32 +80,33 @@ export function FlagMessage(props: Props & React.HTMLAttributes<HTMLDivElement>)
 FlagMessage.displayName = 'FlagMessage'; // so that tests don't see the obfuscated production name
 
 export const StyledFlag = styled.div<{
-  variantInfo: VariantInformation;
+  backGroundColor: ThemeColors;
+  borderColor: ThemeColors;
 }>`
   ${tw`sw-inline-flex`}
   ${tw`sw-min-h-10`}
   ${tw`sw-rounded-1`}
-  border: ${({ variantInfo }) => themeBorder('default', variantInfo.borderColor)};
+  border: ${({ borderColor }) => themeBorder('default', borderColor)};
   background-color: ${themeColor('flagMessageBackground')};
-`;
 
-const StyledFlagInner = styled.div`
-  ${tw`sw-flex sw-items-stretch`}
-  ${tw`sw-box-border`}
-`;
+  & > .flag-inner {
+    ${tw`sw-flex sw-items-stretch`}
+    ${tw`sw-box-border`}
+  }
 
-const StyledFlagIcon = styled.div<{ variantInfo: VariantInformation }>`
-  ${tw`sw-flex sw-justify-center sw-items-center`}
-  ${tw`sw-rounded-l-1`}
-  ${tw`sw-px-3`}
-  background-color: ${({ variantInfo }) => themeColor(variantInfo.backGroundColor)};
-`;
+  & .flag-icon {
+    ${tw`sw-flex sw-justify-center sw-items-center`}
+    ${tw`sw-rounded-l-1`}
+    ${tw`sw-px-3`}
+    background-color: ${({ backGroundColor }) => themeColor(backGroundColor)};
+  }
 
-const StyledFlagContent = styled.div`
-  ${tw`sw-flex sw-flex-auto sw-items-center`}
-  ${tw`sw-overflow-auto`}
-  ${tw`sw-text-left`}
-  ${tw`sw-mx-3 sw-my-2`}
-  ${tw`sw-body-sm`}
-  color: ${themeContrast('flagMessageBackground')};
+  & .flag-content {
+    ${tw`sw-flex sw-flex-auto sw-items-center`}
+    ${tw`sw-overflow-auto`}
+    ${tw`sw-text-left`}
+    ${tw`sw-mx-3 sw-my-2`}
+    ${tw`sw-body-sm`}
+    color: ${themeContrast('flagMessageBackground')};
+  }
 `;
index b51dddcad2a062d58b871e9e0d066314f4e711b3..0d1e0dadedef271717ac608ecec190f34bc9cda8 100644 (file)
@@ -23,21 +23,21 @@ import { FCProps } from '../../types/misc';
 import { FlagMessage, Variant } from '../FlagMessage';
 
 it.each([
-  ['error', 'alert', '1px solid rgb(249,112,102)'],
-  ['warning', 'alert', '1px solid rgb(248,205,92)'],
-  ['success', 'status', '1px solid rgb(50,213,131)'],
-  ['info', 'status', '1px solid rgb(110,185,228)'],
-])('should render properly for "%s" variant', (variant: Variant, expectedRole, color) => {
+  ['error', '1px solid rgb(249,112,102)'],
+  ['warning', '1px solid rgb(248,205,92)'],
+  ['success', '1px solid rgb(50,213,131)'],
+  ['info', '1px solid rgb(110,185,228)'],
+])('should render properly for "%s" variant', (variant: Variant, color) => {
   renderFlagMessage({ variant });
 
-  const item = screen.getByRole(expectedRole);
+  const item = screen.getByRole('status');
   expect(item).toBeInTheDocument();
   expect(item).toHaveStyle({ border: color });
 });
 
 function renderFlagMessage(props: Partial<FCProps<typeof FlagMessage>> = {}) {
   return render(
-    <FlagMessage ariaLabel="label" variant="error" {...props}>
+    <FlagMessage role="status" variant="error" {...props}>
       This is an error!
     </FlagMessage>
   );
index 069ed2dffb3ed811f8fb054f86e686a836c14b56..4d8953398fadef1f7bcabb58b8da4a704a551a51 100644 (file)
@@ -63,7 +63,7 @@ export function AnalysisStatus(props: HeaderMetaProps) {
   if (currentTask?.status === TaskStatuses.Failed) {
     return (
       <>
-        <FlagMessage ariaLabel={translate('alert.tooltip.error')} variant="error">
+        <FlagMessage variant="error">
           <span>{translate('project_navigation.analysis_status.failed')}</span>
           <Link className="sw-ml-1" blurAfterClick onClick={openModal} preventDefault to={{}}>
             {translate('project_navigation.analysis_status.details_link')}
@@ -84,7 +84,7 @@ export function AnalysisStatus(props: HeaderMetaProps) {
   if (warnings.length > 0) {
     return (
       <>
-        <FlagMessage ariaLabel={translate('alert.tooltip.warning')} variant="warning">
+        <FlagMessage variant="warning">
           <span>{translate('project_navigation.analysis_status.warnings')}</span>
           <Link className="sw-ml-1" blurAfterClick onClick={openModal} preventDefault to={{}}>
             {translate('project_navigation.analysis_status.details_link')}
index d790deab426a0bf911d69470adf2aec748f32b68..67fc4ee4697e0d5c758398f56f7907e003117060 100644 (file)
@@ -120,11 +120,7 @@ export default function CodeAppRenderer(props: Props) {
       <A11ySkipTarget anchor="code_main" />
 
       {!canBrowseAllChildProjects && isPortfolio && (
-        <FlagMessage
-          ariaLabel={translate('code_viewer.not_all_measures_are_shown')}
-          variant="warning"
-          className="it__portfolio_warning sw-mb-4"
-        >
+        <FlagMessage variant="warning" className="it__portfolio_warning sw-mb-4">
           {translate('code_viewer.not_all_measures_are_shown')}
           <HelpTooltip
             className="sw-ml-2"
index 53ef62d652a8214a0d1b59a48872b7cfc7ab0f76..afa8beb7fd168f366465fbc81eadf9867c420a62 100644 (file)
@@ -475,7 +475,7 @@ function getPageObject() {
     viewSelect: byLabelText('component_measures.view_as'),
     emptyText: byText('component_measures.empty'),
     detailsUnavailableText: byText('component_measures.details_are_not_available'),
-    noAccessWarning: byRole('alert'),
+    noAccessWarning: byText('component_measures.not_all_measures_are_shown'),
     showingOutOfTxt: (x: string, y: string) => byText(`x_of_y_shown.${x}.${y}`),
     showAllBtn: byRole('button', {
       name: 'component_measures.hidden_best_score_metrics_show_label',
index 5c98cb047f43d6ccdcb50d868ef23f877284bf51..69a0dda1d3502e33ad7b9f8084a8b97ffc449e79 100644 (file)
@@ -181,18 +181,7 @@ export default class FilesView extends React.PureComponent<Props, State> {
           view={this.props.view}
         />
         {hidingBestMeasures && this.props.paging && (
-          <FlagMessage
-            ariaLabel={translateWithParameters(
-              'component_measures.hidden_best_score_metrics',
-              formatMeasure(
-                this.props.paging.total - filteredComponents.length,
-                MetricType.Integer
-              ),
-              formatMeasure(this.props.metric.bestValue, this.props.metric.type)
-            )}
-            variant="info"
-            className="sw-mt-4"
-          >
+          <FlagMessage variant="info" className="sw-mt-4">
             {translateWithParameters(
               'component_measures.hidden_best_score_metrics',
               formatMeasure(
index 67075eabf84919c1edfd43b760c89fec02f760d7..f8d68f61eb50f4e587d811d87e58d1fc2829db2d 100644 (file)
@@ -90,11 +90,7 @@ export default function Sidebar(props: Props) {
       }}
     >
       {!canBrowseAllChildProjects && isPortfolioLike(qualifier) && (
-        <FlagMessage
-          ariaLabel={translate('component_measures.not_all_measures_are_shown')}
-          className="sw-mt-4 it__portfolio_warning"
-          variant="warning"
-        >
+        <FlagMessage className="sw-mt-4 it__portfolio_warning" variant="warning">
           {translate('component_measures.not_all_measures_are_shown')}
           <HelpTooltip
             className="spacer-left"
index 9ca54897266520fa8b01a86abd0c0d01da9fbbcb..e3e6645d3d8700d805ab29da20937ae105ade349 100644 (file)
@@ -437,11 +437,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
       <DeferredSpinner loading={loading}>
         <form id="bulk-change-form" onSubmit={this.handleSubmit}>
           {limitReached && (
-            <FlagMessage
-              ariaLabel={translate('alert.tooltip.warning')}
-              className="sw-mb-4"
-              variant="warning"
-            >
+            <FlagMessage className="sw-mb-4" variant="warning">
               <span>
                 <FormattedMessage
                   defaultMessage={translate('issue_bulk_change.max_issues_reached')}
@@ -461,9 +457,7 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
           {this.renderCommentField()}
           {issues.length > 0 && this.renderNotificationsField()}
           {issues.length === 0 && (
-            <FlagMessage ariaLabel={translate('alert.tooltip.warning')} variant="warning">
-              {translate('issue_bulk_change.no_match')}
-            </FlagMessage>
+            <FlagMessage variant="warning">{translate('issue_bulk_change.no_match')}</FlagMessage>
           )}
         </form>
       </DeferredSpinner>
index f281d730592f84c374bd2619c18b33747010bcaa..6843a7d2b93c80ed5f5bad1da3ec58643e2b07ec 100644 (file)
@@ -1031,7 +1031,6 @@ export class App extends React.PureComponent<Props, State> {
 
     const warning = !canBrowseAllChildProjects && isPortfolioLike(qualifier) && (
       <FlagMessage
-        ariaLabel={translate('issues.not_all_issue_show')}
         className="it__portfolio_warning sw-flex"
         title={translate('issues.not_all_issue_show_why')}
         variant="warning"
@@ -1236,10 +1235,7 @@ export class App extends React.PureComponent<Props, State> {
               ) : (
                 <DeferredSpinner loading={loading} ariaLabel={translate('issues.loading_issues')}>
                   {checkAll && paging && paging.total > MAX_PAGE_SIZE && (
-                    <FlagMessage
-                      ariaLabel={translate('issue_bulk_change.max_issues_reached')}
-                      variant="warning"
-                    >
+                    <FlagMessage variant="warning">
                       <span>
                         <FormattedMessage
                           defaultMessage={translate('issue_bulk_change.max_issues_reached')}
@@ -1251,14 +1247,7 @@ export class App extends React.PureComponent<Props, State> {
                   )}
 
                   {cannotShowOpenIssue && (!paging || paging.total > 0) && (
-                    <FlagMessage
-                      ariaLabel={translateWithParameters(
-                        'issues.cannot_open_issue_max_initial_X_fetched',
-                        MAX_INITAL_FETCH
-                      )}
-                      className="sw-mb-4"
-                      variant="warning"
-                    >
+                    <FlagMessage className="sw-mb-4" variant="warning">
                       {translateWithParameters(
                         'issues.cannot_open_issue_max_initial_X_fetched',
                         MAX_INITAL_FETCH
index 413b72c56893d268b9819ab9c32605e63643f39f..47d82f53de20ab51c6ab449e51bb62f3eaa22460 100644 (file)
@@ -281,11 +281,7 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
     return (
       <>
         {issueIsClosed && (
-          <FlagMessage
-            className="sw-mb-2 sw-flex"
-            variant="success"
-            ariaLabel={translate(closedIssueMessageKey)}
-          >
+          <FlagMessage className="sw-mb-2 sw-flex" variant="success">
             <div className="sw-block">
               <FormattedMessage
                 id={closedIssueMessageKey}
index 52c90cc0b23f158f7f04560c88f21719c1ebf5ea..dce39a06677cb72ce58afd4db5c2029416493b40 100644 (file)
@@ -335,11 +335,7 @@ export class ListStyleFacet<S> extends React.Component<Props<S>, State<S>> {
         />
 
         {mightHaveMoreResults && this.state.showFullList && (
-          <FlagMessage
-            ariaLabel={translate('facet_might_have_more_results')}
-            className="sw-flex sw-my-4"
-            variant="warning"
-          >
+          <FlagMessage className="sw-flex sw-my-4" variant="warning">
             {translate('facet_might_have_more_results')}
           </FlagMessage>
         )}
@@ -382,11 +378,7 @@ export class ListStyleFacet<S> extends React.Component<Props<S>, State<S>> {
         </FacetItemsList>
 
         {searchMaxResults && (
-          <FlagMessage
-            ariaLabel={translate('facet_might_have_more_results')}
-            className="sw-flex sw-my-4"
-            variant="warning"
-          >
+          <FlagMessage className="sw-flex sw-my-4" variant="warning">
             {translate('facet_might_have_more_results')}
           </FlagMessage>
         )}
index 0cbadd8fc016b818b27fc2f5d1f52a392491b960..8e7d0139efbabd929b5943cab57e48e94eb1c489 100644 (file)
@@ -38,13 +38,7 @@ export default function ApplicationNonCaycProjectWarning({ projects, caycStatus
   return (
     <Card className="sw-mt-4 sw-body-sm">
       {caycStatus === CaycStatus.NonCompliant ? (
-        <FlagMessage
-          ariaLabel={translateWithParameters(
-            'overview.quality_gate.application.non_cayc.projects_x',
-            projects.length
-          )}
-          variant="warning"
-        >
+        <FlagMessage variant="warning">
           {translateWithParameters(
             'overview.quality_gate.application.non_cayc.projects_x',
             projects.length
index d54f177c953a4effb53cf8bb928fb4d2e8e72bc2..a0bb6333134e064b74d37aec0382d805b95da7cf 100644 (file)
@@ -34,10 +34,7 @@ export default function CleanAsYouCodeWarning({ component }: Props) {
 
   return (
     <>
-      <FlagMessage
-        ariaLabel={translate('overview.quality_gate.conditions.cayc.warning')}
-        variant="warning"
-      >
+      <FlagMessage variant="warning">
         {translate('overview.quality_gate.conditions.cayc.warning')}
       </FlagMessage>
       {component.qualityGate ? (
index 19a939a74c4b3e2380150f38c98b0208d674d348..07ad44e7ed0372bbe2f9c8643161c6a8db43371c 100644 (file)
@@ -83,11 +83,7 @@ export function NoCodeWarning({ branchLike, component, measures }: Props) {
   }
   /* eslint-enable no-lonely-if */
 
-  return (
-    <FlagMessage ariaLabel={title} variant="warning">
-      {title}
-    </FlagMessage>
-  );
+  return <FlagMessage variant="warning">{title}</FlagMessage>;
 }
 
 export default React.memo(NoCodeWarning);
index a3055cf6e9fcc755b6b1a294f4bd296c8e6938ff..21d076bd71b68f7b8e41ea97380e673377309b5f 100644 (file)
@@ -25,11 +25,7 @@ import { translate } from '../../../helpers/l10n';
 
 export default function IgnoredConditionWarning() {
   return (
-    <FlagMessage
-      ariaLabel={translate('overview.quality_gate.ignored_conditions')}
-      className="sw-mb-4"
-      variant="info"
-    >
+    <FlagMessage className="sw-mb-4" variant="info">
       <span>{translate('overview.quality_gate.ignored_conditions')}</span>
       <HelpTooltip
         className="sw-ml-2"
index 8c0e256502cdb6ee3d2ae6ffafbb678da7c1ae68..0c821a572f36dec70c7d442217de8170e953b736 100644 (file)
@@ -112,10 +112,7 @@ export default class DuplicationPopup extends PureComponent<Props> {
     return (
       <div className="sw-w-abs-400">
         {inRemovedComponent && (
-          <FlagMessage
-            ariaLabel={translate('duplications.dups_found_on_deleted_resource')}
-            variant="warning"
-          >
+          <FlagMessage variant="warning">
             {translate('duplications.dups_found_on_deleted_resource')}
           </FlagMessage>
         )}
index 582a72514036980c528579fb5f20d0641bf59442..9f8a6d41a41bf7a9e75cb2618958fed479dd76bc 100644 (file)
@@ -128,14 +128,7 @@ export default class RuleDescription extends React.PureComponent<Props, State> {
             {translate('coding_rules.description_context.title')}
           </h2>
           {defaultContext && (
-            <FlagMessage
-              variant="info"
-              className="sw-mb-4"
-              ariaLabel={translateWithParameters(
-                'coding_rules.description_context.default_information',
-                defaultContext.displayName
-              )}
-            >
+            <FlagMessage variant="info" className="sw-mb-4">
               {translateWithParameters(
                 'coding_rules.description_context.default_information',
                 defaultContext.displayName