]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18885 Improve groups administration page accessibility
authorMathieu Suen <mathieu.suen@sonarsource.com>
Thu, 23 Mar 2023 14:01:11 +0000 (15:01 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 28 Mar 2023 20:04:04 +0000 (20:04 +0000)
server/sonar-web/src/main/js/apps/groups/components/App.tsx
server/sonar-web/src/main/js/apps/groups/components/EditMembers.tsx
server/sonar-web/src/main/js/apps/groups/components/List.tsx
server/sonar-web/src/main/js/apps/groups/components/ListItem.tsx
server/sonar-web/src/main/js/apps/groups/components/__tests__/List-test.tsx
server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/EditMembers-test.tsx.snap
server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/List-test.tsx.snap
server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/ListItem-test.tsx.snap
server/sonar-web/src/main/js/apps/groups/groups.css [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 7291eb2974f4eacbc45801cbe48f0a9d40a95617..4652527c324acd56130766dcefcc91c964b364c3 100644 (file)
@@ -29,6 +29,7 @@ import Suggestions from '../../../components/embed-docs-modal/Suggestions';
 import { translate } from '../../../helpers/l10n';
 import { omitNil } from '../../../helpers/request';
 import { Group, Paging, SysInfoCluster } from '../../../types/types';
+import '../groups.css';
 import DeleteForm from './DeleteForm';
 import Form from './Form';
 import Header from './Header';
index ca86ea9fa526ac80a2e6db87ac93137e50c7566c..05fe264ad5be99248d4a1604494a62e55a824ef6 100644 (file)
@@ -62,7 +62,7 @@ export default class EditMembers extends React.PureComponent<Props, State> {
       <>
         <ButtonIcon
           aria-label={translateWithParameters('groups.users.edit', this.props.group.name)}
-          className="button-small"
+          className="button-small little-spacer-left little-padded"
           onClick={this.handleMembersClick}
           title={translateWithParameters('groups.users.edit', this.props.group.name)}
         >
index ed5c8eaff2dddedfd234992b3af9478e659bd3ec..2a98562c17ef938753f0ecd7acd613bd8f4cb0d5 100644 (file)
@@ -39,12 +39,14 @@ export default function List(props: Props) {
       <table className="data zebra zebra-hover" id="groups-list">
         <thead>
           <tr>
-            <th />
-            <th className="nowrap width-10" colSpan={2}>
+            <th id="list-group-name">{translate('user_groups.page.group_header')}</th>
+            <th id="list-group-member" className="nowrap width-10">
               {translate('members')}
             </th>
-            <th className="nowrap">{translate('description')}</th>
-            <th />
+            <th id="list-group-description" className="nowrap">
+              {translate('description')}
+            </th>
+            <th id="list-group-actions">{translate('actions')}</th>
           </tr>
         </thead>
         <tbody>
index c631103f75a1c2da2f0b7b4c4d644e29ac0fda50..58a9d324e81cb807e34cbfe257a68a9997ce5396 100644 (file)
@@ -17,6 +17,7 @@
  * 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 ActionsDropdown, {
   ActionsDropdownDivider,
@@ -48,24 +49,28 @@ export default function ListItem(props: ListItemProps) {
 
   return (
     <tr data-id={name}>
-      <td className="width-20">
-        <strong className="js-group-name">{name}</strong>
+      <td className="width-20" headers="list-group-name">
+        <strong>{name}</strong>
         {group.default && <span className="little-spacer-left">({translate('default')})</span>}
         {isGroupLocal() && <span className="little-spacer-left badge">{translate('local')}</span>}
       </td>
 
-      <td className="thin text-middle text-right little-padded-right">{membersCount}</td>
-      <td className="little-padded-left">
+      <td className="group-members display-flex-justify-end" headers="list-group-member">
+        <span
+          className={classNames({ 'big-padded-right spacer-right': group.default && !isManaged() })}
+        >
+          {membersCount}
+        </span>
         {!group.default && !isManaged() && (
           <EditMembers group={group} onEdit={props.onEditMembers} />
         )}
       </td>
 
-      <td className="width-40">
+      <td className="width-40" headers="list-group-description">
         <span className="js-group-description">{description}</span>
       </td>
 
-      <td className="thin nowrap text-right">
+      <td className="thin nowrap text-right" headers="list-group-actions">
         {!group.default && (!isManaged() || isGroupLocal()) && (
           <ActionsDropdown label={translateWithParameters('groups.edit', group.name)}>
             {!isManaged() && (
index a003332f77bcf4a2d51e3d1b43e1e304cbf5c3d2..44607b3ab11993d08119b287af5b17c33e8f8c4e 100644 (file)
@@ -26,11 +26,7 @@ it('should render', () => {
   expect(shallowRender()).toMatchSnapshot();
 });
 
-it('should not render "Anyone"', () => {
-  expect(shallowRender(false).find('.js-anyone').exists()).toBe(false);
-});
-
-function shallowRender(showAnyone = true) {
+function shallowRender() {
   const groups = [
     mockGroup({ name: 'sonar-users', description: '', membersCount: 55, default: true }),
     mockGroup({ name: 'foo', description: 'foobar', membersCount: 0, default: false }),
@@ -42,7 +38,6 @@ function shallowRender(showAnyone = true) {
       onDelete={jest.fn()}
       onEdit={jest.fn()}
       onEditMembers={jest.fn()}
-      showAnyone={showAnyone}
       manageProvider={undefined}
     />
   );
index 044ea201e66d6a9c999babbac06e097009376d23..6378ecc8307709f4347d02e833bb13ac623b5dfb 100644 (file)
@@ -4,7 +4,7 @@ exports[`should edit members 1`] = `
 <Fragment>
   <ButtonIcon
     aria-label="groups.users.edit.Foo"
-    className="button-small"
+    className="button-small little-spacer-left little-padded"
     onClick={[Function]}
     title="groups.users.edit.Foo"
   >
@@ -17,7 +17,7 @@ exports[`should edit members 2`] = `
 <Fragment>
   <ButtonIcon
     aria-label="groups.users.edit.Foo"
-    className="button-small"
+    className="button-small little-spacer-left little-padded"
     onClick={[Function]}
     title="groups.users.edit.Foo"
   >
@@ -40,7 +40,7 @@ exports[`should edit members 3`] = `
 <Fragment>
   <ButtonIcon
     aria-label="groups.users.edit.Foo"
-    className="button-small"
+    className="button-small little-spacer-left little-padded"
     onClick={[Function]}
     title="groups.users.edit.Foo"
   >
index 0c4e32f3d61dab38715a51128d96bd24b79708fe..f9d93c9e5910d2bebe515d09082bd4eb70a05982 100644 (file)
@@ -10,19 +10,28 @@ exports[`should render 1`] = `
   >
     <thead>
       <tr>
-        <th />
+        <th
+          id="list-group-name"
+        >
+          user_groups.page.group_header
+        </th>
         <th
           className="nowrap width-10"
-          colSpan={2}
+          id="list-group-member"
         >
           members
         </th>
         <th
           className="nowrap"
+          id="list-group-description"
         >
           description
         </th>
-        <th />
+        <th
+          id="list-group-actions"
+        >
+          actions
+        </th>
       </tr>
     </thead>
     <tbody>
index ca99c67a45f71dee5aaf62c4a32ed2cbca0e67dd..5e4a0009aadb096f32bdf6f5190a070d00f0900a 100644 (file)
@@ -6,21 +6,19 @@ exports[`should render correctly 1`] = `
 >
   <td
     className="width-20"
+    headers="list-group-name"
   >
-    <strong
-      className="js-group-name"
-    >
+    <strong>
       Foo
     </strong>
   </td>
   <td
-    className="thin text-middle text-right little-padded-right"
-  >
-    1
-  </td>
-  <td
-    className="little-padded-left"
+    className="group-members display-flex-justify-end"
+    headers="list-group-member"
   >
+    <span>
+      1
+    </span>
     <EditMembers
       group={
         {
@@ -34,6 +32,7 @@ exports[`should render correctly 1`] = `
   </td>
   <td
     className="width-40"
+    headers="list-group-description"
   >
     <span
       className="js-group-description"
@@ -41,6 +40,7 @@ exports[`should render correctly 1`] = `
   </td>
   <td
     className="thin nowrap text-right"
+    headers="list-group-actions"
   >
     <ActionsDropdown
       label="groups.edit.Foo"
@@ -70,10 +70,9 @@ exports[`should render correctly: default group 1`] = `
 >
   <td
     className="width-20"
+    headers="list-group-name"
   >
-    <strong
-      className="js-group-name"
-    >
+    <strong>
       Foo
     </strong>
     <span
@@ -85,15 +84,18 @@ exports[`should render correctly: default group 1`] = `
     </span>
   </td>
   <td
-    className="thin text-middle text-right little-padded-right"
+    className="group-members display-flex-justify-end"
+    headers="list-group-member"
   >
-    1
+    <span
+      className="big-padded-right spacer-right"
+    >
+      1
+    </span>
   </td>
-  <td
-    className="little-padded-left"
-  />
   <td
     className="width-40"
+    headers="list-group-description"
   >
     <span
       className="js-group-description"
@@ -101,6 +103,7 @@ exports[`should render correctly: default group 1`] = `
   </td>
   <td
     className="thin nowrap text-right"
+    headers="list-group-actions"
   />
 </tr>
 `;
diff --git a/server/sonar-web/src/main/js/apps/groups/groups.css b/server/sonar-web/src/main/js/apps/groups/groups.css
new file mode 100644 (file)
index 0000000..1bec592
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#groups-page .group-members {
+  padding-right: 50%;
+}
index 451060ef5cdd75bfba0bce0ce1f93ed45d232996..c1775abd1a3d5599d0ec408757388771eb188095 100644 (file)
@@ -4436,6 +4436,7 @@ users.change_admin_password.form.continue_to_app=Continue to SonarQube
 #------------------------------------------------------------------------------
 user_groups.page=Groups
 user_groups.page.description=Create and administer groups of users.
+user_groups.page.group_header=Name
 user_groups.page.managed_description=Your instance is managed by {provider}. No modification is allowed. You can still delete local groups. All other operations should be done on your identity provider. See {link} for help managing groups. 
 user_groups.anyone.description=Anybody who browses the application belongs to this group. If authentication is not enforced, assigned permissions also apply to non-authenticated users.
 groups.delete_group=Delete Group