]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21639 Start using new RadioButtonGroup and deprecate legacy RadioButton component
authorJeremy Davis <jeremy.davis@sonarsource.com>
Wed, 13 Mar 2024 08:44:29 +0000 (09:44 +0100)
committersonartech <sonartech@sonarsource.com>
Sat, 16 Mar 2024 20:02:47 +0000 (20:02 +0000)
server/sonar-web/design-system/src/components/DropdownMenu.tsx
server/sonar-web/design-system/src/components/__tests__/DropdownMenu-test.tsx
server/sonar-web/design-system/src/components/input/RadioButton.tsx
server/sonar-web/src/main/js/apps/audit-logs/components/AuditAppRenderer.tsx
server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
server/sonar-web/src/main/js/apps/projectsManagement/ChangeDefaultVisibilityForm.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionModal.tsx
server/sonar-web/src/main/js/apps/system/components/ChangeLogLevelForm.tsx
server/sonar-web/src/main/js/components/common/VisibilitySelector.tsx

index 9072d93ae6555f61aa4fc0271a109c7b4819a01e..d5ba01e248570dd1494ac34035e5bbbc0ecd66f1 100644 (file)
@@ -30,7 +30,6 @@ import NavLink from './NavLink';
 import { Tooltip } from './Tooltip';
 import { ClipboardBase } from './clipboard';
 import { Checkbox } from './input/Checkbox';
-import { RadioButton } from './input/RadioButton';
 
 interface Props extends React.HtmlHTMLAttributes<HTMLMenuElement> {
   children?: React.ReactNode;
@@ -198,30 +197,6 @@ export function ItemCheckbox(props: ItemCheckboxProps) {
   );
 }
 
-interface ItemRadioButtonProps extends ListItemProps {
-  checked: boolean;
-  disabled?: boolean;
-  onCheck: (value: string) => void;
-  value: string;
-}
-
-export function ItemRadioButton(props: ItemRadioButtonProps) {
-  const { checked, children, className, disabled, innerRef, onCheck, value, ...liProps } = props;
-  return (
-    <li ref={innerRef} role="none" {...liProps}>
-      <ItemRadioButtonStyled
-        checked={checked}
-        className={classNames(className, { disabled })}
-        disabled={disabled}
-        onCheck={onCheck}
-        value={value}
-      >
-        {children}
-      </ItemRadioButtonStyled>
-    </li>
-  );
-}
-
 interface ItemCopyProps {
   children?: React.ReactNode;
   className?: string;
@@ -398,8 +373,3 @@ const ItemCheckboxStyled = styled(Checkbox)`
   --color: ${themeContrast('dropdownMenu')};
   ${itemStyle}
 `;
-
-const ItemRadioButtonStyled = styled(RadioButton)`
-  --color: ${themeContrast('dropdownMenu')};
-  ${itemStyle}
-`;
index 087a89bcdb3623eabb3ee51aa34956f77b479c0d..5e2aebceb65226e885d9d776be4570f5cc004cde 100644 (file)
@@ -30,7 +30,6 @@ import {
   ItemHeader,
   ItemLink,
   ItemNavLink,
-  ItemRadioButton,
 } from '../DropdownMenu';
 import { Tooltip } from '../Tooltip';
 import { MenuIcon } from '../icons/MenuIcon';
@@ -74,9 +73,6 @@ function renderDropdownMenu() {
       <ItemCheckbox checked onCheck={noop}>
         Checkbox item
       </ItemCheckbox>
-      <ItemRadioButton checked={false} onCheck={noop} value="radios">
-        Radio item
-      </ItemRadioButton>
     </DropdownMenu>,
   );
 }
index 7ee29e59f894f12aa38a78d31b270d1ca305b2be..f36d7ec762fdbfed5fb8f86c0b630505659c9170 100644 (file)
@@ -39,6 +39,11 @@ type Props =
   | ({ onCheck: (value: string) => void; value: string } & PropsBase)
   | ({ onCheck: () => void; value: never } & PropsBase);
 
+/** @deprecated Use RadioButtonGroup from Echoes instead.
+ *
+ * Individual Radio Buttons can't be used anymore.
+ * Instead, build the list of options and pass it to the RadioButtonGroup component.
+ */
 export function RadioButton({
   checked,
   children,
@@ -88,6 +93,11 @@ const LabelStyled = styled.label<{ disabled?: boolean }>`
   }
 `;
 
+/** @deprecated Use RadioButtonGroup from Echoes instead.
+ *
+ * Individual Radio Buttons can't be used anymore.
+ * Instead, build the list of options and pass it to the RadioButtonGroup component.
+ */
 export const RadioButtonStyled = styled.input`
   appearance: none; //disables native style
   border: ${themeBorder('default', 'radioBorder')};
index 7242e023fd0c9b288a0d7ddde824c59f343053ce..98b3de92e9c10d410c82990e3b3f4e804e973384 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+import { Link, RadioButtonGroup } from '@sonarsource/echoes-react';
 import { subDays } from 'date-fns';
 import {
   DateRangePicker,
   LargeCenteredLayout,
-  Link,
   PageContentFontWrapper,
   PopupZLevel,
-  RadioButton,
   Title,
 } from 'design-system';
 import * as React from 'react';
@@ -75,7 +74,7 @@ const getRangeOptions = (housekeepingPolicy: HousekeepingPolicy) => {
   return rangeOptions;
 };
 
-export default function AuditAppRenderer(props: AuditAppRendererProps) {
+export default function AuditAppRenderer(props: Readonly<AuditAppRendererProps>) {
   const { dateRange, downloadStarted, housekeepingPolicy, selection } = props;
 
   return (
@@ -112,19 +111,15 @@ export default function AuditAppRenderer(props: AuditAppRendererProps) {
         <div className="sw-mb-6">
           <h3 className="sw-mb-4">{translate('audit_logs.download')}</h3>
 
-          <ul>
-            {getRangeOptions(housekeepingPolicy).map((option) => (
-              <li key={option} className="sw-mb-2">
-                <RadioButton
-                  checked={selection === option}
-                  onCheck={props.handleOptionSelection}
-                  value={option}
-                >
-                  {translate('audit_logs.range_option', option)}
-                </RadioButton>
-              </li>
-            ))}
-          </ul>
+          <RadioButtonGroup
+            id="audit-logs-housekeeping-radio"
+            options={getRangeOptions(housekeepingPolicy).map((option) => ({
+              label: translate('audit_logs.range_option', option),
+              value: option,
+            }))}
+            value={selection}
+            onChange={props.handleOptionSelection}
+          />
 
           <DateRangePicker
             className="sw-w-abs-350 sw-mt-4"
index e0cd92c98dfc7897a932907eb2dd0057d641653e..4102f8cf9cc036f317eb5438ce2cec834b7b77e6 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { Checkbox, Spinner } from '@sonarsource/echoes-react';
+import { Checkbox, RadioButtonGroup, Spinner } from '@sonarsource/echoes-react';
 import {
   ButtonPrimary,
   FlagMessage,
@@ -28,7 +28,6 @@ import {
   LabelValueSelectOption,
   LightLabel,
   Modal,
-  RadioButton,
 } from 'design-system';
 import { countBy, flattenDeep, pickBy, sortBy } from 'lodash';
 import * as React from 'react';
@@ -297,23 +296,16 @@ export class BulkChangeModal extends React.PureComponent<Props, State> {
           <Highlight as="legend" className="sw-mb-2">
             {translate('issue.change_status')}
           </Highlight>
-          {transitions.map((transition) => (
-            <div
-              className="sw-mb-1 sw-flex sw-items-center sw-justify-between"
-              key={transition.transition}
-            >
-              <RadioButton
-                checked={this.state.transition === transition.transition}
-                onCheck={this.handleRadioTransitionChange}
-                value={transition.transition}
-              >
-                {translate('issue.transition', transition.transition)}
-              </RadioButton>
-              <LightLabel>
-                ({translateWithParameters('issue_bulk_change.x_issues', transition.count)})
-              </LightLabel>
-            </div>
-          ))}
+
+          <RadioButtonGroup
+            id="bulk-change-transition"
+            options={transitions.map(({ transition, count }) => ({
+              label: translate('issue.transition', transition),
+              value: transition,
+              helpText: translateWithParameters('issue_bulk_change.x_issues', count),
+            }))}
+            onChange={this.handleRadioTransitionChange}
+          />
         </fieldset>
       </div>
     );
@@ -333,7 +325,6 @@ export class BulkChangeModal extends React.PureComponent<Props, State> {
     return (
       <FormField label={translate('issue_bulk_change.resolution_comment')}>
         <InputTextArea
-          autoFocus
           aria-label={translate('issue_bulk_change.resolution_comment')}
           onChange={this.handleCommentChange}
           placeholder={translate(
index 31d075ba7a1d6c1cfbdb6eb8e31f6ccffae48ad7..6f2eca87f864d60837cb89faf2d5b8bea515b0f0 100644 (file)
@@ -17,7 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { ButtonPrimary, FlagMessage, Modal, RadioButton, TextSubdued } from 'design-system';
+import { RadioButtonGroup } from '@sonarsource/echoes-react';
+import { ButtonPrimary, FlagMessage, Modal } from 'design-system';
 import React, { useState } from 'react';
 import { translate } from '../../helpers/l10n';
 import { useGithubProvisioningEnabledQuery } from '../../queries/identity-provider/github';
@@ -31,7 +32,7 @@ export interface Props {
 
 const FORM_ID = 'change-default-visibility-form';
 
-export default function ChangeDefaultVisibilityForm(props: Props) {
+export default function ChangeDefaultVisibilityForm(props: Readonly<Props>) {
   const [visibility, setVisibility] = useState(props.defaultVisibility);
   const { data: githubProbivisioningEnabled } = useGithubProvisioningEnabledQuery();
 
@@ -49,22 +50,17 @@ export default function ChangeDefaultVisibilityForm(props: Props) {
 
   const body = (
     <form id={FORM_ID} onSubmit={handleConfirmClick}>
-      {Object.values(Visibility).map((visibilityValue) => (
-        <div className="sw-mb-4" key={visibilityValue}>
-          <RadioButton
-            value={visibilityValue}
-            checked={visibility === visibilityValue}
-            onCheck={handleVisibilityChange}
-          >
-            <div>
-              {translate('visibility', visibilityValue)}
-              <TextSubdued as="p" className="sw-mt-2">
-                {translate('visibility', visibilityValue, 'description.short')}
-              </TextSubdued>
-            </div>
-          </RadioButton>
-        </div>
-      ))}
+      <RadioButtonGroup
+        id="settings-projects-visibility-radio"
+        options={Object.values(Visibility).map((visibilityValue) => ({
+          label: translate('visibility', visibilityValue),
+          helpText: translate('visibility', visibilityValue, 'description.short'),
+          value: visibilityValue,
+        }))}
+        value={visibility}
+        onChange={handleVisibilityChange}
+      />
+
       <FlagMessage variant="warning">
         {translate(
           `settings.projects.change_visibility_form.warning${
index 61f7ae71dfc7c1ea6db4d0f2fb09b42d863cbcf0..06b60e44600b060052e105f2bc5fb4dafe5ad8c6 100644 (file)
@@ -17,7 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { ButtonPrimary, FormField, Modal, RadioButton } from 'design-system';
+import { RadioButtonGroup } from '@sonarsource/echoes-react';
+import { ButtonPrimary, FormField, Modal } from 'design-system';
 import * as React from 'react';
 import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
 import { isDiffMetric } from '../../../helpers/measures';
@@ -93,18 +94,15 @@ export default function AddConditionModal({ metrics, onClose, qualityGate }: Rea
     return (
       <form onSubmit={handleFormSubmit} id={ADD_CONDITION_MODAL_ID}>
         <FormField label={translate('quality_gates.conditions.where')}>
-          <div className="sw-flex sw-gap-4">
-            <RadioButton checked={scope === 'new'} onCheck={handleScopeChange} value="new">
-              <span data-test="quality-gates__condition-scope-new">
-                {translate('quality_gates.conditions.new_code')}
-              </span>
-            </RadioButton>
-            <RadioButton checked={scope === 'overall'} onCheck={handleScopeChange} value="overall">
-              <span data-test="quality-gates__condition-scope-overall">
-                {translate('quality_gates.conditions.overall_code')}
-              </span>
-            </RadioButton>
-          </div>
+          <RadioButtonGroup
+            id="quality_gates-add-condition-scope-radio"
+            options={[
+              { label: translate('quality_gates.conditions.new_code'), value: 'new' },
+              { label: translate('quality_gates.conditions.overall_code'), value: 'overall' },
+            ]}
+            value={scope}
+            onChange={handleScopeChange}
+          />
         </FormField>
 
         <FormField
index cf638be0286f86e3f0655ec902cc8501bf838259..87e64ff1beda26ff82b7240f2c2d333b81b3122d 100644 (file)
@@ -17,7 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { ButtonPrimary, FlagMessage, Modal, RadioButton } from 'design-system';
+import { RadioButtonGroup } from '@sonarsource/echoes-react';
+import { ButtonPrimary, FlagMessage, Modal } from 'design-system';
 import * as React from 'react';
 import { setLogLevel } from '../../../api/system';
 import { translate } from '../../../helpers/l10n';
@@ -65,21 +66,15 @@ export default class ChangeLogLevelForm extends React.PureComponent<Props, State
         onClose={this.props.onClose}
         body={
           <form id={FORM_ID} onSubmit={this.handleFormSubmit}>
-            {LOGS_LEVELS.map((level) => (
-              <RadioButton
-                key={level}
-                checked={level === newLevel}
-                className="sw-mb-2"
-                id={`loglevel-${level}`}
-                name="system.log_levels"
-                onCheck={this.handleLevelChange}
-                value={level}
-              >
-                <label className="text-middle" htmlFor={`loglevel-${level}`}>
-                  {level}
-                </label>
-              </RadioButton>
-            ))}
+            <div className="sw-mt-1">
+              <RadioButtonGroup
+                id="system-loglevel-radio"
+                options={LOGS_LEVELS.map((level) => ({ label: level, value: level }))}
+                value={newLevel}
+                onChange={this.handleLevelChange}
+              />
+            </div>
+
             <FlagMessage className="sw-mt-2" variant="info">
               {this.props.infoMsg}
             </FlagMessage>
index 51f3effe06924ac4365b8949cd1edfbc7eaf9c14..e3cfc540ab7642857757d7b44082264949836a80 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { RadioButtonGroup } from '@sonarsource/echoes-react';
 import classNames from 'classnames';
-import { RadioButton } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../helpers/l10n';
 import { Visibility } from '../../types/component';
@@ -32,22 +32,21 @@ export interface VisibilitySelectorProps {
   loading?: boolean;
 }
 
-export default function VisibilitySelector(props: VisibilitySelectorProps) {
+export default function VisibilitySelector(props: Readonly<VisibilitySelectorProps>) {
   const { className, canTurnToPrivate, visibility, disabled, loading = false } = props;
   return (
     <div className={classNames(className)}>
-      {Object.values(Visibility).map((v) => (
-        <RadioButton
-          className={`sw-mr-10 it__visibility-${v}`}
-          key={v}
-          value={v}
-          checked={v === visibility}
-          onCheck={props.onChange}
-          disabled={disabled || (v === Visibility.Private && !canTurnToPrivate) || loading}
-        >
-          <div>{translate('visibility', v)}</div>
-        </RadioButton>
-      ))}
+      <RadioButtonGroup
+        id="project-visiblity-radio"
+        isDisabled={disabled}
+        options={Object.values(Visibility).map((v) => ({
+          label: translate('visibility', v),
+          value: v,
+          isDisabled: (v === Visibility.Private && !canTurnToPrivate) || loading,
+        }))}
+        value={visibility}
+        onChange={props.onChange}
+      />
     </div>
   );
 }