]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22287 Fix a11y issues on Background Tasks page
author7PH <benjamin.raymond@sonarsource.com>
Thu, 15 Aug 2024 08:43:56 +0000 (10:43 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 20 Aug 2024 20:02:56 +0000 (20:02 +0000)
server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-it.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/AnalysisWarningsModal.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/Task.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index c984c2a9b74ce29993391b00fdf7990eef0115de..338205bfae1782b4028fb7a3e587ff511eead713 100644 (file)
@@ -410,7 +410,11 @@ function getPageObject() {
     async clickOnTaskAction(rowIndex: number, label: string) {
       const row = ui.getAllRows()[rowIndex];
       expect(row).toBeVisible();
-      await user.click(within(row).getByRole('button', { name: 'background_tasks.show_actions' }));
+      await user.click(
+        within(row).getByRole('button', {
+          name: `background_tasks.show_actions_for_task_x_in_list.${rowIndex}`,
+        }),
+      );
       await user.click(within(row).getByRole('menuitem', { name: label }));
     },
   };
index 31295c6c1ad72882f0c798a48469fb223595a793..3671ee2f0f8cd5a0a07b21310fc483dd43cd910b 100644 (file)
@@ -112,39 +112,41 @@ export class AnalysisWarningsModal extends React.PureComponent<Props, State> {
 
     const body = (
       <Spinner loading={loading}>
-        {warnings.map(({ dismissable, key, message }) => (
-          <React.Fragment key={key}>
-            <div className="sw-flex sw-items-center sw-mt-2">
-              <FlagMessage variant="warning">
-                <HtmlFormatter>
-                  <span
-                    // eslint-disable-next-line react/no-danger
-                    dangerouslySetInnerHTML={{
-                      __html: sanitizeStringRestricted(message.trim().replace(/\n/g, '<br />')),
-                    }}
-                  />
-                </HtmlFormatter>
-              </FlagMessage>
-            </div>
-            <div>
-              {dismissable && currentUser.isLoggedIn && (
-                <div className="sw-mt-4">
-                  <Button
-                    isDisabled={Boolean(dismissedWarning)}
-                    onClick={() => {
-                      this.handleDismissMessage(key);
-                    }}
-                    variety={ButtonVariety.DangerOutline}
-                  >
-                    {translate('dismiss_permanently')}
-                  </Button>
-
-                  <Spinner className="sw-ml-2" loading={dismissedWarning === key} />
-                </div>
-              )}
-            </div>
-          </React.Fragment>
-        ))}
+        <ul>
+          {warnings.map(({ dismissable, key, message }) => (
+            <li key={key}>
+              <div className="sw-flex sw-items-center sw-mt-2">
+                <FlagMessage variant="warning">
+                  <HtmlFormatter>
+                    <span
+                      // eslint-disable-next-line react/no-danger
+                      dangerouslySetInnerHTML={{
+                        __html: sanitizeStringRestricted(message.trim().replace(/\n/g, '<br />')),
+                      }}
+                    />
+                  </HtmlFormatter>
+                </FlagMessage>
+              </div>
+              <div>
+                {dismissable && currentUser.isLoggedIn && (
+                  <div className="sw-mt-4">
+                    <Button
+                      isDisabled={Boolean(dismissedWarning)}
+                      onClick={() => {
+                        this.handleDismissMessage(key);
+                      }}
+                      variety={ButtonVariety.DangerOutline}
+                    >
+                      {translate('dismiss_permanently')}
+                    </Button>
+
+                    <Spinner className="sw-ml-2" loading={dismissedWarning === key} />
+                  </div>
+                )}
+              </div>
+            </li>
+          ))}
+        </ul>
       </Spinner>
     );
 
index ab722b4f0f6a7a83b39c16f551b838c88a1e003d..dbb564cb9c560f4a0651892a713d1cef64ee9d59 100644 (file)
@@ -35,10 +35,11 @@ interface Props {
   onCancelTask: (task: ITask) => Promise<void>;
   onFilterTask: (task: ITask) => void;
   task: ITask;
+  taskIndex: number;
 }
 
 export default function Task(props: Readonly<Props>) {
-  const { task, component, onCancelTask, onFilterTask } = props;
+  const { task, component, taskIndex, onCancelTask, onFilterTask } = props;
 
   const appState = React.useContext(AppStateContext);
   const isDataCenter = appState.edition === EditionKey.datacenter;
@@ -54,6 +55,7 @@ export default function Task(props: Readonly<Props>) {
       <TaskExecutionTime ms={task.executionTimeMs} />
       <TaskActions
         component={component}
+        taskIndex={taskIndex}
         onCancelTask={onCancelTask}
         onFilterTask={onFilterTask}
         task={task}
index c42b915f98ac7ab4bf8f342b34778c1f32a073c5..1a91d5e740adc9d00029e8fad1d692adec854339 100644 (file)
@@ -31,6 +31,7 @@ interface Props {
   onCancelTask: (task: Task) => Promise<void>;
   onFilterTask: (task: Task) => void;
   task: Task;
+  taskIndex: number;
 }
 
 interface State {
@@ -89,7 +90,7 @@ export default class TaskActions extends React.PureComponent<Props, State> {
   };
 
   render() {
-    const { component, task } = this.props;
+    const { component, task, taskIndex } = this.props;
 
     const canFilter = component === undefined && task.componentName;
     const canCancel = task.status === TaskStatuses.Pending;
@@ -106,7 +107,10 @@ export default class TaskActions extends React.PureComponent<Props, State> {
       <ActionCell>
         <ActionsDropdown
           id={`task-${task.id}-actions`}
-          ariaLabel={translate('background_tasks.show_actions')}
+          ariaLabel={translateWithParameters(
+            'background_tasks.show_actions_for_task_x_in_list',
+            taskIndex,
+          )}
           className="js-task-action"
         >
           {canFilter && task.componentName && (
index 274803fa07eaa2bae0f3ce1d1b2b54d90b5b5205..2c0171ec3dedebdb21ee1808c8512b667aabf3d4 100644 (file)
@@ -59,10 +59,11 @@ export function Tasks({ tasks, component, onCancelTask, onFilterTask }: Readonly
         </TableRow>
       }
     >
-      {tasks.map((task) => (
+      {tasks.map((task, index) => (
         <Task
           component={component}
           key={task.id}
+          taskIndex={index}
           onCancelTask={onCancelTask}
           onFilterTask={onFilterTask}
           task={task}
index 7a7573f8bbde567ca1a807709680709ee50ae82a..d5961c4c853953b2063c0728b6434ed4892e8960 100644 (file)
@@ -3835,7 +3835,7 @@ background_tasks.cancel_all_tasks.text=Are you sure you want to cancel all pendi
 background_tasks.cancel_all_tasks.submit=Cancel All
 background_tasks.scanner_context=Scanner Context
 background_tasks.show_scanner_context=Show Scanner Context
-background_tasks.show_actions=Show actions
+background_tasks.show_actions_for_task_x_in_list=Show actions for task in row {0}
 background_tasks.show_stacktrace=Show Error Details
 background_tasks.show_warnings=Show Warnings
 background_tasks.error_message=Error Message