]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20337 Updating modals for quality gate's conditions
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>
Mon, 11 Sep 2023 15:46:51 +0000 (17:46 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 19 Sep 2023 20:02:46 +0000 (20:02 +0000)
server/sonar-web/design-system/src/components/modal/Modal.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/ConditionReviewAndUpdateModal.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/MetricSelect.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 01a9163178ddbf58361843f0b0bf0a5e9bec3cf3..4746501fe59e9205d310392e8a9b83703806088f 100644 (file)
@@ -90,7 +90,7 @@ export function Modal({
       <Global styles={globalStyles({ theme })} />
 
       <ReactModal
-        aria={{ labelledby: '#modal_header_title' }}
+        aria={{ labelledby: 'modal_header_title' }}
         className={classNames('design-system-modal-contents modal', { large: isLarge })}
         isOpen={isOpen}
         onRequestClose={onClose}
@@ -147,6 +147,8 @@ const globalStyles = ({ theme }: { theme: Theme }) => css`
     &.large {
       max-width: 1280px;
       min-width: 1040px;
+      transform: translateX(-50%);
+      margin-left: 0px;
     }
   }
 
index 5623df940983dfa62cd6141a9ca03a62d7d0f526..956249dafd2b484fdc16647c601d8837af8c4913 100644 (file)
@@ -21,8 +21,10 @@ import classNames from 'classnames';
 import {
   ActionCell,
   ContentCell,
+  DangerButtonPrimary,
   DestructiveIcon,
   InteractiveIcon,
+  Modal,
   NumericalCell,
   PencilIcon,
   TableRow,
@@ -32,7 +34,6 @@ import {
 import * as React from 'react';
 import { deleteCondition } from '../../../api/quality-gates';
 import withMetricsContext from '../../../app/components/metrics/withMetricsContext';
-import ConfirmModal from '../../../components/controls/ConfirmModal';
 import { getLocalizedMetricName, translate, translateWithParameters } from '../../../helpers/l10n';
 import {
   CaycStatus,
@@ -187,19 +188,24 @@ export class ConditionComponent extends React.PureComponent<Props, State> {
                     size="small"
                   />
                   {this.state.deleteFormOpen && (
-                    <ConfirmModal
-                      confirmButtonText={translate('delete')}
-                      confirmData={condition}
-                      header={translate('quality_gates.delete_condition')}
-                      isDestructive
+                    <Modal
+                      headerTitle={translate('quality_gates.delete_condition')}
                       onClose={this.closeDeleteForm}
-                      onConfirm={this.removeCondition}
-                    >
-                      {translateWithParameters(
+                      body={translateWithParameters(
                         'quality_gates.delete_condition.confirm.message',
                         getLocalizedMetricName(this.props.metric),
                       )}
-                    </ConfirmModal>
+                      primaryButton={
+                        <DangerButtonPrimary
+                          autoFocus
+                          type="submit"
+                          onClick={() => this.removeCondition(condition)}
+                        >
+                          {translate('delete')}
+                        </DangerButtonPrimary>
+                      }
+                      secondaryButtonLabel={translate('close')}
+                    />
                   )}
                 </>
               )}
index 5865e74e5600f9995aa2016a285eb956851e057e..e2bfb2372579c7089a66596923b45eab8f8ff43e 100644 (file)
  * 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, FormField, Modal, RadioButton } from 'design-system';
 import * as React from 'react';
 import { createCondition, updateCondition } from '../../../api/quality-gates';
-import ConfirmModal from '../../../components/controls/ConfirmModal';
-import Radio from '../../../components/controls/Radio';
-import { Alert } from '../../../components/ui/Alert';
 import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
 import { isDiffMetric } from '../../../helpers/measures';
 import { Condition, Metric, QualityGate } from '../../../types/types';
@@ -48,6 +46,8 @@ interface State {
   scope: 'new' | 'overall';
 }
 
+const ADD_CONDITION_MODAL_ID = 'add-condition-modal';
+
 export default class ConditionModal extends React.PureComponent<Props, State> {
   constructor(props: Props) {
     super(props);
@@ -64,7 +64,9 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
     return Array.isArray(operators) ? undefined : operators;
   }
 
-  handleFormSubmit = () => {
+  handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
+    event.preventDefault();
+
     const { condition, qualityGate } = this.props;
     const newCondition: Omit<Condition, 'id'> = {
       metric: this.state.metric!.key,
@@ -74,7 +76,7 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
     const submitPromise = condition
       ? updateCondition({ id: condition.id, ...newCondition })
       : createCondition({ gateName: qualityGate.name, ...newCondition });
-    return submitPromise.then(this.props.onAddCondition);
+    return submitPromise.then(this.props.onAddCondition).then(this.props.onClose);
   };
 
   handleScopeChange = (scope: 'new' | 'overall') => {
@@ -104,44 +106,43 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
     this.setState({ error });
   };
 
-  render() {
-    const { header, metrics, onClose } = this.props;
-    const { op, error, scope, metric } = this.state;
-    return (
-      <ConfirmModal
-        confirmButtonText={header}
-        confirmDisable={metric === undefined}
-        header={header}
-        onClose={onClose}
-        onConfirm={this.handleFormSubmit}
-        size="small"
-      >
-        {this.state.errorMessage && <Alert variant="error">{this.state.errorMessage}</Alert>}
+  renderBody = () => {
+    const { metrics } = this.props;
+    const { op, error, scope, metric, errorMessage } = this.state;
 
+    return (
+      <form id={ADD_CONDITION_MODAL_ID} onSubmit={this.handleFormSubmit}>
+        {errorMessage && (
+          <FlagMessage className="sw-mb-2" variant="error">
+            {errorMessage}
+          </FlagMessage>
+        )}
         {this.props.metric === undefined && (
-          <div className="modal-field display-flex-center">
-            <Radio checked={scope === 'new'} onCheck={this.handleScopeChange} value="new">
-              <span data-test="quality-gates__condition-scope-new">
-                {translate('quality_gates.conditions.new_code')}
-              </span>
-            </Radio>
-            <Radio
-              checked={scope === 'overall'}
-              className="big-spacer-left"
-              onCheck={this.handleScopeChange}
-              value="overall"
-            >
-              <span data-test="quality-gates__condition-scope-overall">
-                {translate('quality_gates.conditions.overall_code')}
-              </span>
-            </Radio>
-          </div>
+          <FormField label={translate('quality_gates.conditions.where')}>
+            <div className="sw-flex sw-gap-4">
+              <RadioButton checked={scope === 'new'} onCheck={this.handleScopeChange} value="new">
+                <span data-test="quality-gates__condition-scope-new">
+                  {translate('quality_gates.conditions.new_code')}
+                </span>
+              </RadioButton>
+              <RadioButton
+                checked={scope === 'overall'}
+                onCheck={this.handleScopeChange}
+                value="overall"
+              >
+                <span data-test="quality-gates__condition-scope-overall">
+                  {translate('quality_gates.conditions.overall_code')}
+                </span>
+              </RadioButton>
+            </div>
+          </FormField>
         )}
 
-        <div className="modal-field">
-          <label htmlFor="condition-metric">
-            {translate('quality_gates.conditions.fails_when')}
-          </label>
+        <FormField
+          description={this.props.metric && getLocalizedMetricName(this.props.metric)}
+          htmlFor="condition-metric"
+          label={translate('quality_gates.conditions.fails_when')}
+        >
           {metrics && (
             <MetricSelect
               metric={metric}
@@ -151,37 +152,61 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
               onMetricChange={this.handleMetricChange}
             />
           )}
-          {this.props.metric && (
-            <span className="note">{getLocalizedMetricName(this.props.metric)}</span>
-          )}
-        </div>
+        </FormField>
 
         {metric && (
-          <>
-            <div className="modal-field display-inline-block">
-              <label id="condition-operator-label">
-                {translate('quality_gates.conditions.operator')}
-              </label>
+          <div className="sw-flex sw-gap-2">
+            <FormField
+              className="sw-mb-0"
+              htmlFor="condition-operator"
+              label={translate('quality_gates.conditions.operator')}
+            >
               <ConditionOperator
                 metric={metric}
                 onOperatorChange={this.handleOperatorChange}
                 op={op}
               />
-            </div>
-            <div className="modal-field display-inline-block spacer-left">
-              <label id="condition-threshold-label">
-                {translate('quality_gates.conditions.value')}
-              </label>
+            </FormField>
+            <FormField
+              htmlFor="condition-threshold"
+              label={translate('quality_gates.conditions.value')}
+            >
               <ThresholdInput
                 metric={metric}
                 name="error"
                 onChange={this.handleErrorChange}
                 value={error}
               />
-            </div>
-          </>
+            </FormField>
+          </div>
         )}
-      </ConfirmModal>
+      </form>
+    );
+  };
+
+  render() {
+    const { header } = this.props;
+    const { metric } = this.state;
+    return (
+      <Modal
+        isScrollable={false}
+        isOverflowVisible
+        headerTitle={header}
+        onClose={this.props.onClose}
+        body={this.renderBody()}
+        primaryButton={
+          <ButtonPrimary
+            autoFocus
+            disabled={metric === undefined}
+            id="add-condition-button"
+            form={ADD_CONDITION_MODAL_ID}
+            type="submit"
+          >
+            {header}
+          </ButtonPrimary>
+        }
+        secondaryButtonLabel={translate('close')}
+      />
     );
   }
 }
index e4153aa49aa416e6ad65ad3fbb205fb201ac4634..102373a259e7e1e92db19c320cba39eeeb708008 100644 (file)
@@ -17,8 +17,9 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+
+import { InputSelect, Note } from 'design-system';
 import * as React from 'react';
-import Select from '../../../components/controls/Select';
 import { translate } from '../../../helpers/l10n';
 import { Metric } from '../../../types/types';
 import { getPossibleOperators } from '../utils';
@@ -50,12 +51,11 @@ export default class ConditionOperator extends React.PureComponent<Props> {
       });
 
       return (
-        <Select
+        <InputSelect
           autoFocus
-          aria-labelledby="condition-operator-label"
-          className="input-medium"
+          size="small"
           isClearable={false}
-          id="condition-operator"
+          inputId="condition-operator"
           name="operator"
           onChange={this.handleChange}
           options={operatorOptions}
@@ -63,12 +63,8 @@ export default class ConditionOperator extends React.PureComponent<Props> {
           value={operatorOptions.filter((o) => o.value === this.props.op)}
         />
       );
-    } else {
-      return (
-        <span className="display-inline-block note abs-width-150">
-          {this.getLabel(operators, this.props.metric)}
-        </span>
-      );
     }
+
+    return <Note className="sw-w-abs-150">{this.getLabel(operators, this.props.metric)}</Note>;
   }
 }
index ed21ba444d94c51a0a3588dbd106db7ebbc6257e..9335287588106445240e4b393f9ce6154c04ff0a 100644 (file)
  * 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, Link, Modal, SubHeading, Title } from 'design-system';
 import { sortBy } from 'lodash';
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { createCondition, updateCondition } from '../../../api/quality-gates';
-import DocLink from '../../../components/common/DocLink';
-import ConfirmModal from '../../../components/controls/ConfirmModal';
+import { useDocUrl } from '../../../helpers/docs';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Condition, Dict, Metric, QualityGate } from '../../../types/types';
 import { getCorrectCaycCondition, getWeakMissingAndNonCaycConditions } from '../utils';
@@ -42,9 +42,31 @@ interface Props {
   qualityGate: QualityGate;
 }
 
-export default class CaycReviewUpdateConditionsModal extends React.PureComponent<Props> {
-  updateCaycQualityGate = () => {
-    const { conditions, qualityGate } = this.props;
+export default function CaycReviewUpdateConditionsModal(props: Props) {
+  const {
+    conditions,
+    qualityGate,
+    metrics,
+    onSaveCondition,
+    onAddCondition,
+    lockEditing,
+    onClose,
+  } = props;
+
+  const { weakConditions, missingConditions } = getWeakMissingAndNonCaycConditions(conditions);
+  const sortedWeakConditions = sortBy(
+    weakConditions,
+    (condition) => metrics[condition.metric] && metrics[condition.metric].name
+  );
+
+  const sortedMissingConditions = sortBy(
+    missingConditions,
+    (condition) => metrics[condition.metric] && metrics[condition.metric].name
+  );
+
+  const getDocUrl = useDocUrl();
+
+  const updateCaycQualityGate = React.useCallback(() => {
     const promiseArr: Promise<Condition | undefined | void>[] = [];
     const { weakConditions, missingConditions } = getWeakMissingAndNonCaycConditions(conditions);
 
@@ -57,10 +79,10 @@ export default class CaycReviewUpdateConditionsModal extends React.PureComponent
           .then((resultCondition) => {
             const currentCondition = conditions.find((con) => con.metric === condition.metric);
             if (currentCondition) {
-              this.props.onSaveCondition(resultCondition, currentCondition);
+              onSaveCondition(resultCondition, currentCondition);
             }
           })
-          .catch(() => undefined),
+          .catch(() => undefined)
       );
     });
 
@@ -70,95 +92,92 @@ export default class CaycReviewUpdateConditionsModal extends React.PureComponent
           ...getCorrectCaycCondition(condition),
           gateName: qualityGate.name,
         })
-          .then((resultCondition) => this.props.onAddCondition(resultCondition))
-          .catch(() => undefined),
+          .then((resultCondition) => onAddCondition(resultCondition))
+          .catch(() => undefined)
       );
     });
 
     return Promise.all(promiseArr).then(() => {
-      this.props.lockEditing();
+      lockEditing();
     });
-  };
-
-  render() {
-    const { conditions, qualityGate, metrics } = this.props;
-
-    const { weakConditions, missingConditions } = getWeakMissingAndNonCaycConditions(conditions);
-    const sortedWeakConditions = sortBy(
-      weakConditions,
-      (condition) => metrics[condition.metric]?.name,
-    );
+  }, [conditions, qualityGate, lockEditing, onAddCondition, onSaveCondition]);
 
-    const sortedMissingConditions = sortBy(
-      missingConditions,
-      (condition) => metrics[condition.metric]?.name,
-    );
+  const body = (
+    <div className="sw-mb-10">
+      <SubHeading as="p" className="sw-body-sm">
+        <FormattedMessage
+          id="quality_gates.cayc.review_update_modal.description1"
+          defaultMessage={translate('quality_gates.cayc.review_update_modal.description1')}
+          values={{
+            cayc_link: (
+              <Link to={getDocUrl('/user-guide/clean-as-you-code/')}>
+                {translate('quality_gates.cayc')}
+              </Link>
+            ),
+          }}
+        />
+      </SubHeading>
 
-    return (
-      <ConfirmModal
-        header={translateWithParameters(
-          'quality_gates.cayc.review_update_modal.header',
-          qualityGate.name,
-        )}
-        confirmButtonText={translate('quality_gates.cayc.review_update_modal.confirm_text')}
-        onClose={this.props.onClose}
-        onConfirm={this.updateCaycQualityGate}
-        size="medium"
-      >
-        <div className="quality-gate-section huge-spacer-bottom">
-          <p>
-            <FormattedMessage
-              id="quality_gates.cayc.review_update_modal.description1"
-              defaultMessage={translate('quality_gates.cayc.review_update_modal.description1')}
-              values={{
-                cayc_link: (
-                  <DocLink to="/user-guide/clean-as-you-code/">
-                    {translate('quality_gates.cayc')}
-                  </DocLink>
-                ),
-              }}
-            />
-          </p>
+      {sortedMissingConditions.length > 0 && (
+        <>
+          <Title as="h4" className="sw-mb-2 sw-mt-4 sw-body-sm-highlight">
+            {translateWithParameters(
+              'quality_gates.cayc.review_update_modal.add_condition.header',
+              sortedMissingConditions.length
+            )}
+          </Title>
+          <ConditionsTable
+            {...props}
+            conditions={sortedMissingConditions}
+            showEdit={false}
+            isCaycModal
+          />
+        </>
+      )}
 
-          {sortedMissingConditions.length > 0 && (
-            <>
-              <h4 className="big-spacer-top spacer-bottom">
-                {translateWithParameters(
-                  'quality_gates.cayc.review_update_modal.add_condition.header',
-                  sortedMissingConditions.length,
-                )}
-              </h4>
-              <ConditionsTable
-                {...this.props}
-                conditions={sortedMissingConditions}
-                showEdit={false}
-                isCaycModal
-              />
-            </>
-          )}
+      {sortedWeakConditions.length > 0 && (
+        <>
+          <Title as="h4" className="sw-mb-2 sw-mt-4 sw-body-sm-highlight">
+            {translateWithParameters(
+              'quality_gates.cayc.review_update_modal.modify_condition.header',
+              sortedWeakConditions.length
+            )}
+          </Title>
+          <ConditionsTable
+            {...props}
+            conditions={sortedWeakConditions}
+            showEdit={false}
+            isCaycModal
+          />
+        </>
+      )}
 
-          {sortedWeakConditions.length > 0 && (
-            <>
-              <h4 className="big-spacer-top spacer-bottom">
-                {translateWithParameters(
-                  'quality_gates.cayc.review_update_modal.modify_condition.header',
-                  sortedWeakConditions.length,
-                )}
-              </h4>
-              <ConditionsTable
-                {...this.props}
-                conditions={sortedWeakConditions}
-                showEdit={false}
-                isCaycModal
-              />
-            </>
-          )}
+      <Title as="h4" className="sw-mb-2 sw-mt-4 sw-body-sm-highlight">
+        {translate('quality_gates.cayc.review_update_modal.description2')}
+      </Title>
+    </div>
+  );
 
-          <h4 className="big-spacer-top spacer-bottom">
-            {translate('quality_gates.cayc.review_update_modal.description2')}
-          </h4>
-        </div>
-      </ConfirmModal>
-    );
-  }
+  return (
+    <Modal
+      isLarge
+      headerTitle={translateWithParameters(
+        'quality_gates.cayc.review_update_modal.header',
+        qualityGate.name
+      )}
+      onClose={onClose}
+      body={body}
+      primaryButton={
+        <ButtonPrimary
+          autoFocus
+          id="fix-quality-gate"
+          type="submit"
+          onClick={updateCaycQualityGate}
+        >
+          {translate('quality_gates.cayc.review_update_modal.confirm_text')}
+        </ButtonPrimary>
+      }
+      secondaryButtonLabel={translate('close')}
+    />
+  );
 }
index 4ec8188e8d73aa393e7f44fb56ea059307a1399e..271bbef4a75af1a7d7ae89486cdad92070a1200e 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { LabelValueSelectOption, SearchSelectDropdown } from 'design-system';
 import { sortBy } from 'lodash';
 import * as React from 'react';
+import { Options } from 'react-select';
 import withMetricsContext from '../../../app/components/metrics/withMetricsContext';
-import Select from '../../../components/controls/Select';
 import { getLocalizedMetricDomain, translate } from '../../../helpers/l10n';
 import { Dict, Metric } from '../../../types/types';
 import { getLocalizedMetricNameNoDiffMetric } from '../utils';
@@ -38,54 +39,62 @@ interface Option {
   value: string;
 }
 
-export class MetricSelect extends React.PureComponent<Props> {
-  handleChange = (option: Option | null) => {
+export function MetricSelect({ metric, metricsArray, metrics, onMetricChange }: Props) {
+  const handleChange = (option: Option | null) => {
     if (option) {
-      const { metricsArray: metrics } = this.props;
-      const selectedMetric = metrics.find((metric) => metric.key === option.value);
+      const selectedMetric = metricsArray.find((metric) => metric.key === option.value);
       if (selectedMetric) {
-        this.props.onMetricChange(selectedMetric);
+        onMetricChange(selectedMetric);
       }
     }
   };
 
-  render() {
-    const { metric, metricsArray, metrics } = this.props;
+  const options: Array<Option & { domain?: string }> = sortBy(
+    metricsArray.map((m) => ({
+      value: m.key,
+      label: getLocalizedMetricNameNoDiffMetric(m, metrics),
+      domain: m.domain,
+    })),
+    'domain'
+  );
 
-    const options: Array<Option & { domain?: string }> = sortBy(
-      metricsArray.map((m) => ({
-        value: m.key,
-        label: getLocalizedMetricNameNoDiffMetric(m, metrics),
-        domain: m.domain,
-      })),
-      'domain',
-    );
+  // Use "disabled" property to emulate optgroups.
+  const optionsWithDomains: Option[] = [];
+  options.forEach((option, index, options) => {
+    const previous = index > 0 ? options[index - 1] : null;
+    if (option.domain && (!previous || previous.domain !== option.domain)) {
+      optionsWithDomains.push({
+        value: '<domain>',
+        label: getLocalizedMetricDomain(option.domain),
+        isDisabled: true,
+      });
+    }
+    optionsWithDomains.push(option);
+  });
 
-    // Use "disabled" property to emulate optgroups.
-    const optionsWithDomains: Option[] = [];
-    options.forEach((option, index, options) => {
-      const previous = index > 0 ? options[index - 1] : null;
-      if (option.domain && (!previous || previous.domain !== option.domain)) {
-        optionsWithDomains.push({
-          value: '<domain>',
-          label: getLocalizedMetricDomain(option.domain),
-          isDisabled: true,
-        });
-      }
-      optionsWithDomains.push(option);
-    });
+  const handleAssigneeSearch = React.useCallback(
+    (query: string, resolve: (options: Options<LabelValueSelectOption<string>>) => void) => {
+      resolve(options.filter((opt) => opt.label.toLowerCase().includes(query.toLowerCase())));
+    },
+    [options]
+  );
 
-    return (
-      <Select
-        className="text-middle quality-gate-metric-select"
-        id="condition-metric"
-        onChange={this.handleChange}
-        options={optionsWithDomains}
-        placeholder={translate('search.search_for_metrics')}
-        value={optionsWithDomains.find((o) => o.value === metric?.key)}
-      />
-    );
-  }
+  return (
+    <SearchSelectDropdown
+      aria-label={translate('search.search_for_metrics')}
+      size="large"
+      controlSize="full"
+      inputId="condition-metric"
+      isClearable
+      defaultOptions={optionsWithDomains}
+      loadOptions={handleAssigneeSearch}
+      onChange={handleChange}
+      placeholder={translate('search.search_for_metrics')}
+      controlLabel={
+        optionsWithDomains.find((o) => o.value === metric?.key)?.label ?? translate('select_verb')
+      }
+    />
+  );
 }
 
 export default withMetricsContext(MetricSelect);
index 9657fb14592c3073f18633c02f6b7238d918b5a0..770468e5e459ae3cd8940219206dc06e3faf6bf5 100644 (file)
@@ -17,8 +17,9 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { InputField, InputSelect } from 'design-system';
 import * as React from 'react';
-import Select from '../../../components/controls/Select';
+import { LabelValueSelectOption } from '../../../components/controls/Select';
 import { Metric } from '../../../types/types';
 
 interface Props {
@@ -33,8 +34,12 @@ export default class ThresholdInput extends React.PureComponent<Props> {
     this.props.onChange(e.currentTarget.value);
   };
 
-  handleSelectChange = (option: { value: string }) => {
-    this.props.onChange(option.value);
+  handleSelectChange = (option: LabelValueSelectOption) => {
+    if (option) {
+      this.props.onChange(option.value);
+    } else {
+      this.props.onChange('');
+    }
   };
 
   renderRatingInput() {
@@ -48,16 +53,14 @@ export default class ThresholdInput extends React.PureComponent<Props> {
     ];
 
     return (
-      <Select
-        className="input-tiny text-middle"
-        aria-labelledby="condition-threshold-label"
-        isClearable={false}
-        id="condition-threshold"
+      <InputSelect
+        className="sw-w-abs-150"
+        inputId="condition-threshold"
         name={name}
         onChange={this.handleSelectChange}
         options={options}
         placeholder=""
-        isSearchable={false}
+        size="small"
         value={options.find((o) => o.value === value)}
       />
     );
@@ -71,9 +74,8 @@ export default class ThresholdInput extends React.PureComponent<Props> {
     }
 
     return (
-      <input
-        className="input-tiny text-middle"
-        aria-labelledby="condition-threshold-label"
+      <InputField
+        size="small"
         data-type={metric.type}
         id="condition-threshold"
         name={name}
index 67560e0cfe06f9804c36df5b018ae56a8d356110..de602a125b0adc5802101e363ad90a03e597a184 100644 (file)
@@ -193,8 +193,8 @@ it('should be able to add a condition', async () => {
   await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.new_code' }));
   await selectEvent.select(dialog.getByRole('combobox'), ['Issues']);
   await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
-  await user.keyboard('12{Enter}');
-
+  await user.keyboard('12');
+  await user.click(dialog.getByRole('button', { name: 'quality_gates.add_condition' }));
   const newConditions = within(await screen.findByTestId('quality-gates__conditions-new'));
   expect(await newConditions.findByRole('cell', { name: 'Issues' })).toBeInTheDocument();
   expect(await newConditions.findByRole('cell', { name: '12' })).toBeInTheDocument();
@@ -209,7 +209,8 @@ it('should be able to add a condition', async () => {
 
   await user.click(dialog.getByText('quality_gates.operator.LT'));
   await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
-  await user.keyboard('42{Enter}');
+  await user.keyboard('42');
+  await user.click(dialog.getByRole('button', { name: 'quality_gates.add_condition' }));
 
   const overallConditions = within(await screen.findByTestId('quality-gates__conditions-overall'));
 
index a38463d3719ac61653ecc10134f00c4e1562d9a6..a92d413d8ce2ef3ab298f8a8368d18b48992ed16 100644 (file)
@@ -2146,6 +2146,7 @@ quality_gates.conditions.operator=Operator
 quality_gates.conditions.warning=Warning
 quality_gates.conditions.warning.tooltip=Warning status is deprecated and will disappear with the next update of the Quality Gate.
 quality_gates.conditions.value=Value
+quality_gates.conditions.where=Where?
 quality_gates.duplicated_conditions=This quality gate has duplicated conditions:
 quality_gates.intro.1=Quality Gate is the set of conditions the project must meet before it can be released into production.
 quality_gates.intro.2=It is possible to set a default Quality Gate, which will be applied to all projects not explicitly assigned to some other gate.