]> source.dussan.org Git - sonarqube.git/commitdiff
NO-JIRA Add keyboard handler for RadioCard
authorstanislavh <stanislav.honcharov@sonarsource.com>
Mon, 23 Oct 2023 11:39:48 +0000 (13:39 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 23 Oct 2023 20:02:43 +0000 (20:02 +0000)
server/sonar-web/src/main/js/components/controls/RadioCard.tsx
server/sonar-web/src/main/js/components/controls/__tests__/RadioCard-test.tsx [new file with mode: 0644]

index edff2a73e18e0689e3db0cc9d752e78cdf573f2f..a719c21b0c1c26f576b5771cef5b25226abf4749 100644 (file)
@@ -20,6 +20,7 @@
 import classNames from 'classnames';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
+import { KeyboardKeys } from '../../helpers/keycodes';
 import { translate } from '../../helpers/l10n';
 import RecommendedIcon from '../icons/RecommendedIcon';
 import './Radio.css';
@@ -55,6 +56,14 @@ export default function RadioCard(props: Props) {
     noRadio = false,
   } = props;
   const isActionable = Boolean(onClick);
+  const clickHandler = isActionable && !disabled && !selected ? onClick : undefined;
+
+  const keyPressHandler = (event: React.KeyboardEvent<HTMLDivElement>) => {
+    if (event.code === KeyboardKeys.Enter) {
+      clickHandler?.();
+    }
+  };
+
   return (
     <div
       aria-checked={selected}
@@ -68,7 +77,8 @@ export default function RadioCard(props: Props) {
         },
         className,
       )}
-      onClick={isActionable && !disabled && !selected ? onClick : undefined}
+      onClick={clickHandler}
+      onKeyPress={keyPressHandler}
       role="radio"
       aria-label={label}
       aria-disabled={disabled}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/RadioCard-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/RadioCard-test.tsx
new file mode 100644 (file)
index 0000000..622b59c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+import userEvent from '@testing-library/user-event';
+import * as React from 'react';
+import { renderComponent } from '../../../helpers/testReactTestingUtils';
+import { byRole } from '../../../helpers/testSelector';
+import { FCProps } from '../../../types/misc';
+import RadioCard from '../RadioCard';
+
+describe('RadioCard component', () => {
+  it('renders & handles selection', async () => {
+    const user = userEvent.setup();
+    const onClick = jest.fn();
+    renderRadioCard({ onClick });
+
+    const card = byRole('radio').get();
+
+    expect(card).toBeInTheDocument();
+    expect(byRole('heading', { name: 'body' }).get()).toBeInTheDocument();
+
+    // Keyboard selection
+    await user.keyboard('{Tab}');
+    await user.keyboard('{Enter}');
+    expect(onClick).toHaveBeenCalledTimes(1);
+
+    // Mouse selection
+    await user.click(card);
+    expect(onClick).toHaveBeenCalledTimes(2);
+  });
+});
+
+function renderRadioCard(overrides: Partial<FCProps<typeof RadioCard>>) {
+  return renderComponent(
+    <RadioCard title="Radio Card" {...overrides}>
+      <h3>body</h3>
+    </RadioCard>,
+  );
+}