]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16091 Update SelectLegacy
authorPhilippe Perrin <philippe.perrin@sonarsource.com>
Tue, 29 Mar 2022 13:41:18 +0000 (15:41 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 30 Mar 2022 20:03:10 +0000 (20:03 +0000)
15 files changed:
server/sonar-web/src/main/js/apps/security-hotspots/components/FilterBar.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/FilterBar-test.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/Languages.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/Languages-test.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/Languages-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/inputs/InputForSingleSelectList.tsx
server/sonar-web/src/main/js/apps/settings/components/inputs/__tests__/InputForSingleSelectList-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/users/components/UsersSelectSearch.tsx [deleted file]
server/sonar-web/src/main/js/apps/users/components/__tests__/UsersSelectSearch-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap [deleted file]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index ccbb2979f252d8ac1aeb85981be4916d847a1bf5..3a7e2733616cf9e1364a4e4783bb08ecf1920f5c 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext';
 import HelpTooltip from '../../../components/controls/HelpTooltip';
 import RadioToggle from '../../../components/controls/RadioToggle';
-import SelectLegacy from '../../../components/controls/SelectLegacy';
+import Select from '../../../components/controls/Select';
 import Measure from '../../../components/measure/Measure';
 import CoverageRating from '../../../components/ui/CoverageRating';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
@@ -43,7 +43,7 @@ export interface FilterBarProps {
   onShowAllHotspots: () => void;
 }
 
-const statusOptions: Array<{ label: string; value: string }> = [
+const statusOptions: Array<{ label: string; value: HotspotStatusFilter }> = [
   { value: HotspotStatusFilter.TO_REVIEW, label: translate('hotspot.filters.status.to_review') },
   {
     value: HotspotStatusFilter.ACKNOWLEDGED,
@@ -111,28 +111,28 @@ export function FilterBar(props: FilterBarProps) {
                   />
                 )}
 
-                <span className="spacer-right">{translate('status')}</span>
-                <SelectLegacy
+                <span className="spacer-right"> {translate('status')} </span>
+                <Select
                   className="input-medium big-spacer-right"
-                  clearable={false}
+                  aria-label={translate('hotspot.filters.status')}
                   onChange={(option: { value: HotspotStatusFilter }) =>
                     props.onChangeFilters({ status: option.value })
                   }
                   options={statusOptions}
-                  searchable={false}
-                  value={filters.status}
+                  isSearchable={false}
+                  value={statusOptions.find(status => status.value === filters.status)}
                 />
 
                 {onBranch && (
-                  <SelectLegacy
+                  <Select
                     className="input-medium big-spacer-right"
-                    clearable={false}
+                    aria-label={translate('hotspot.filters.period')}
                     onChange={(option: { value: boolean }) =>
                       props.onChangeFilters({ sinceLeakPeriod: option.value })
                     }
                     options={periodOptions}
-                    searchable={false}
-                    value={filters.sinceLeakPeriod}
+                    isSearchable={false}
+                    value={periodOptions.find(period => period.value === filters.sinceLeakPeriod)}
                   />
                 )}
               </div>
index 91a81f57ebcb7943f6ee4262cb6517f1b67fcea8..1cb08607edc0232b28646a6547e34d7420a7819e 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import RadioToggle from '../../../../components/controls/RadioToggle';
-import SelectLegacy from '../../../../components/controls/SelectLegacy';
+import Select from '../../../../components/controls/Select';
 import { mockComponent } from '../../../../helpers/mocks/component';
 import { mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
 import { ComponentQualifier } from '../../../../types/component';
@@ -60,11 +60,11 @@ it('should trigger onChange for status', () => {
   const wrapper = shallowRender({ onChangeFilters });
 
   const { onChange } = wrapper
-    .find(SelectLegacy)
+    .find(Select)
     .at(0)
     .props();
 
-  onChange!({ value: HotspotStatusFilter.SAFE });
+  onChange({ value: HotspotStatusFilter.SAFE });
   expect(onChangeFilters).toBeCalledWith({ status: HotspotStatusFilter.SAFE });
 });
 
@@ -74,7 +74,7 @@ it('should trigger onChange for self-assigned toggle', () => {
 
   const { onCheck } = wrapper.find(RadioToggle).props();
 
-  onCheck!(AssigneeFilterOption.ALL);
+  onCheck(AssigneeFilterOption.ALL);
   expect(onChangeFilters).toBeCalledWith({ assignedToMe: false });
 });
 
@@ -83,11 +83,11 @@ it('should trigger onChange for leak period', () => {
   const wrapper = shallowRender({ onChangeFilters });
 
   const { onChange } = wrapper
-    .find(SelectLegacy)
+    .find(Select)
     .at(1)
     .props();
 
-  onChange!({ value: true });
+  onChange({ value: true });
   expect(onChangeFilters).toBeCalledWith({ sinceLeakPeriod: true });
 });
 
index 89dd27708424cfdacb5db2af649ddc3515ef1ef3..9c037b787b2894f32581024270b1467263284732 100644 (file)
@@ -47,11 +47,14 @@ exports[`should render correctly: anonymous 1`] = `
           <span
             className="spacer-right"
           >
+             
             status
+             
           </span>
-          <SelectLegacy
+          <Select
+            aria-label="hotspot.filters.status"
             className="input-medium big-spacer-right"
-            clearable={false}
+            isSearchable={false}
             onChange={[Function]}
             options={
               Array [
@@ -73,12 +76,17 @@ exports[`should render correctly: anonymous 1`] = `
                 },
               ]
             }
-            searchable={false}
-            value="TO_REVIEW"
+            value={
+              Object {
+                "label": "hotspot.filters.status.to_review",
+                "value": "TO_REVIEW",
+              }
+            }
           />
-          <SelectLegacy
+          <Select
+            aria-label="hotspot.filters.period"
             className="input-medium big-spacer-right"
-            clearable={false}
+            isSearchable={false}
             onChange={[Function]}
             options={
               Array [
@@ -92,8 +100,12 @@ exports[`should render correctly: anonymous 1`] = `
                 },
               ]
             }
-            searchable={false}
-            value={false}
+            value={
+              Object {
+                "label": "hotspot.filters.period.overall",
+                "value": false,
+              }
+            }
           />
         </div>
         <div
@@ -167,11 +179,14 @@ exports[`should render correctly: logged-in 1`] = `
           <span
             className="spacer-right"
           >
+             
             status
+             
           </span>
-          <SelectLegacy
+          <Select
+            aria-label="hotspot.filters.status"
             className="input-medium big-spacer-right"
-            clearable={false}
+            isSearchable={false}
             onChange={[Function]}
             options={
               Array [
@@ -193,12 +208,17 @@ exports[`should render correctly: logged-in 1`] = `
                 },
               ]
             }
-            searchable={false}
-            value="TO_REVIEW"
+            value={
+              Object {
+                "label": "hotspot.filters.status.to_review",
+                "value": "TO_REVIEW",
+              }
+            }
           />
-          <SelectLegacy
+          <Select
+            aria-label="hotspot.filters.period"
             className="input-medium big-spacer-right"
-            clearable={false}
+            isSearchable={false}
             onChange={[Function]}
             options={
               Array [
@@ -212,8 +232,12 @@ exports[`should render correctly: logged-in 1`] = `
                 },
               ]
             }
-            searchable={false}
-            value={false}
+            value={
+              Object {
+                "label": "hotspot.filters.period.overall",
+                "value": false,
+              }
+            }
           />
         </div>
         <div
@@ -287,11 +311,14 @@ exports[`should render correctly: non-project 1`] = `
           <span
             className="spacer-right"
           >
+             
             status
+             
           </span>
-          <SelectLegacy
+          <Select
+            aria-label="hotspot.filters.status"
             className="input-medium big-spacer-right"
-            clearable={false}
+            isSearchable={false}
             onChange={[Function]}
             options={
               Array [
@@ -313,12 +340,17 @@ exports[`should render correctly: non-project 1`] = `
                 },
               ]
             }
-            searchable={false}
-            value="TO_REVIEW"
+            value={
+              Object {
+                "label": "hotspot.filters.status.to_review",
+                "value": "TO_REVIEW",
+              }
+            }
           />
-          <SelectLegacy
+          <Select
+            aria-label="hotspot.filters.period"
             className="input-medium big-spacer-right"
-            clearable={false}
+            isSearchable={false}
             onChange={[Function]}
             options={
               Array [
@@ -332,8 +364,12 @@ exports[`should render correctly: non-project 1`] = `
                 },
               ]
             }
-            searchable={false}
-            value={false}
+            value={
+              Object {
+                "label": "hotspot.filters.period.overall",
+                "value": false,
+              }
+            }
           />
         </div>
       </div>
@@ -366,11 +402,14 @@ exports[`should render correctly: on Pull request 1`] = `
           <span
             className="spacer-right"
           >
+             
             status
+             
           </span>
-          <SelectLegacy
+          <Select
+            aria-label="hotspot.filters.status"
             className="input-medium big-spacer-right"
-            clearable={false}
+            isSearchable={false}
             onChange={[Function]}
             options={
               Array [
@@ -392,8 +431,12 @@ exports[`should render correctly: on Pull request 1`] = `
                 },
               ]
             }
-            searchable={false}
-            value="TO_REVIEW"
+            value={
+              Object {
+                "label": "hotspot.filters.status.to_review",
+                "value": "TO_REVIEW",
+              }
+            }
           />
         </div>
         <div
@@ -448,11 +491,14 @@ exports[`should render correctly: with hotspots reviewed measure 1`] = `
           <span
             className="spacer-right"
           >
+             
             status
+             
           </span>
-          <SelectLegacy
+          <Select
+            aria-label="hotspot.filters.status"
             className="input-medium big-spacer-right"
-            clearable={false}
+            isSearchable={false}
             onChange={[Function]}
             options={
               Array [
@@ -474,12 +520,17 @@ exports[`should render correctly: with hotspots reviewed measure 1`] = `
                 },
               ]
             }
-            searchable={false}
-            value="TO_REVIEW"
+            value={
+              Object {
+                "label": "hotspot.filters.status.to_review",
+                "value": "TO_REVIEW",
+              }
+            }
           />
-          <SelectLegacy
+          <Select
+            aria-label="hotspot.filters.period"
             className="input-medium big-spacer-right"
-            clearable={false}
+            isSearchable={false}
             onChange={[Function]}
             options={
               Array [
@@ -493,8 +544,12 @@ exports[`should render correctly: with hotspots reviewed measure 1`] = `
                 },
               ]
             }
-            searchable={false}
-            value={false}
+            value={
+              Object {
+                "label": "hotspot.filters.period.overall",
+                "value": false,
+              }
+            }
           />
         </div>
         <div
index 7ffb1d264151e35a45cc9f6af298d8758dc0b7be..83adcaee6860816102eeb872ee49b9236d770d49 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import SelectLegacy from '../../../components/controls/SelectLegacy';
+import Select from '../../../components/controls/Select';
 import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
 import { translate } from '../../../helpers/l10n';
 import { getCategoryName } from '../utils';
@@ -51,14 +51,17 @@ export function Languages(props: LanguagesProps) {
 
   return (
     <>
-      <h2 className="settings-sub-category-name">{translate('property.category.languages')}</h2>
+      <h2 id="languages-category-title" className="settings-sub-category-name">
+        {translate('property.category.languages')}
+      </h2>
       <div data-test="language-select">
-        <SelectLegacy
-          className="input-large"
+        <Select
+          aria-labelledby="languages-category-title"
+          className="input-large select-settings-language"
           onChange={handleOnChange}
           options={availableLanguages}
           placeholder={translate('settings.languages.select_a_language_placeholder')}
-          value={selectedLanguage}
+          value={availableLanguages.find(language => language.value === selectedLanguage)}
         />
       </div>
       {selectedLanguage && (
index 9e9b03df383ba92224e34f0c9b7471c126c7177a..6e44066cdcbc7f5afaa99b044654d80f176e5958 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import SelectLegacy from '../../../../components/controls/SelectLegacy';
+import Select from '../../../../components/controls/Select';
 import { mockComponent } from '../../../../helpers/mocks/component';
 import { mockLocation, mockRouter } from '../../../../helpers/testMocks';
 import CategoryDefinitionsList from '../CategoryDefinitionsList';
@@ -41,9 +41,9 @@ it('should correctly handle a change of the selected language', () => {
   const wrapper = shallowRender({ router });
   expect(wrapper.find(CategoryDefinitionsList).props().category).toBe('java');
 
-  const { onChange } = wrapper.find(SelectLegacy).props();
+  const { onChange } = wrapper.find(Select).props();
 
-  onChange!({ label: '', originalValue: 'CoBoL', value: 'cobol' });
+  onChange({ label: '', originalValue: 'CoBoL', value: 'cobol' });
   expect(push).toHaveBeenCalledWith(expect.objectContaining({ query: { category: 'CoBoL' } }));
 });
 
index 0659334976a25e6d7adf720c2ec19dd8f2bb6d14..b44e852341a4275dccadd5a18d89aca8def1746d 100644 (file)
@@ -4,14 +4,16 @@ exports[`should render correctly 1`] = `
 <Fragment>
   <h2
     className="settings-sub-category-name"
+    id="languages-category-title"
   >
     property.category.languages
   </h2>
   <div
     data-test="language-select"
   >
-    <SelectLegacy
-      className="input-large"
+    <Select
+      aria-labelledby="languages-category-title"
+      className="input-large select-settings-language"
       onChange={[Function]}
       options={
         Array [
@@ -33,7 +35,13 @@ exports[`should render correctly 1`] = `
         ]
       }
       placeholder="settings.languages.select_a_language_placeholder"
-      value="java"
+      value={
+        Object {
+          "label": "property.category.Java",
+          "originalValue": "Java",
+          "value": "java",
+        }
+      }
     />
   </div>
   <div
@@ -51,14 +59,16 @@ exports[`should render correctly with an unknow language 1`] = `
 <Fragment>
   <h2
     className="settings-sub-category-name"
+    id="languages-category-title"
   >
     property.category.languages
   </h2>
   <div
     data-test="language-select"
   >
-    <SelectLegacy
-      className="input-large"
+    <Select
+      aria-labelledby="languages-category-title"
+      className="input-large select-settings-language"
       onChange={[Function]}
       options={
         Array [
index 19285a1f93523f43de6287f20e0ff980f5e295df..6b41d66209b1e2cce3ccb32e9ff80fa4e7627913 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import SelectLegacy from '../../../../components/controls/SelectLegacy';
+import Select from '../../../../components/controls/Select';
 import { ExtendedSettingDefinition } from '../../../../types/settings';
 import { DefaultSpecializedInputProps } from '../../utils';
 
@@ -30,19 +30,20 @@ export default class InputForSingleSelectList extends React.PureComponent<Props>
   };
 
   render() {
-    const options = this.props.options.map(option => ({
+    const { options: opts, name, value } = this.props;
+
+    const options = opts.map(option => ({
       label: option,
       value: option
     }));
 
     return (
-      <SelectLegacy
+      <Select
         className="settings-large-input"
-        clearable={false}
-        name={this.props.name}
+        name={name}
         onChange={this.handleInputChange}
         options={options}
-        value={this.props.value}
+        value={options.find(option => option.value === value)}
       />
     );
   }
index c6a9972e4cf9da76a6685c96b26f8327654c4fb8..63172dd9079a4b68184035f6d898a87ee21221bd 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import SelectLegacy from '../../../../../components/controls/SelectLegacy';
+import Select from '../../../../../components/controls/Select';
 import { mockSetting } from '../../../../../helpers/mocks/settings';
 import { DefaultSpecializedInputProps } from '../../../utils';
 import InputForSingleSelectList from '../InputForSingleSelectList';
 
 it('should render Select', () => {
   const onChange = jest.fn();
-  const select = shallowRender({ onChange }).find(SelectLegacy);
+  const select = shallowRender({ onChange }).find(Select);
   expect(select.length).toBe(1);
   expect(select.prop('name')).toBe('foo');
-  expect(select.prop('value')).toBe('bar');
+  expect(select.prop('value')).toEqual({ label: 'bar', value: 'bar' });
   expect(select.prop('options')).toEqual([
     { value: 'foo', label: 'foo' },
     { value: 'bar', label: 'bar' },
@@ -40,7 +40,7 @@ it('should render Select', () => {
 
 it('should call onChange', () => {
   const onChange = jest.fn();
-  const select = shallowRender({ onChange }).find(SelectLegacy);
+  const select = shallowRender({ onChange }).find(Select);
   expect(select.length).toBe(1);
   expect(select.prop('onChange')).toBeDefined();
 
index 29162ee65e5b101324d7c91b32f6456c6bc95fd6..b74af2527a5a73e4e4d57ed40e590747b7f1fbf4 100644 (file)
@@ -20,8 +20,9 @@
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { Link } from 'react-router';
+import { components, OptionProps, SingleValueProps } from 'react-select';
 import { Button, SubmitButton } from '../../../../components/controls/buttons';
-import SelectLegacy from '../../../../components/controls/SelectLegacy';
+import Select from '../../../../components/controls/Select';
 import AlertSuccessIcon from '../../../../components/icons/AlertSuccessIcon';
 import { Alert } from '../../../../components/ui/Alert';
 import DeferredSpinner from '../../../../components/ui/DeferredSpinner';
@@ -56,7 +57,15 @@ export interface PRDecorationBindingRendererProps {
   isSysAdmin: boolean;
 }
 
-function optionRenderer(instance: AlmSettingsInstance) {
+function optionRenderer(props: OptionProps<AlmSettingsInstance, false>) {
+  return <components.Option {...props}>{customOptions(props.data)}</components.Option>;
+}
+
+function singleValueRenderer(props: SingleValueProps<AlmSettingsInstance>) {
+  return <components.SingleValue {...props}>{customOptions(props.data)}</components.SingleValue>;
+}
+
+function customOptions(instance: AlmSettingsInstance) {
   return instance.url ? (
     <>
       <span>{instance.key} — </span>
@@ -141,22 +150,18 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe
             </div>
           </div>
           <div className="settings-definition-right">
-            <SelectLegacy
-              autosize={true}
-              className="abs-width-400 big-spacer-top"
-              clearable={false}
-              id="name"
-              menuContainerStyle={{
-                maxWidth: '210%' /* Allow double the width of the select */,
-                width: 'auto'
-              }}
-              onChange={(instance: AlmSettingsInstance) => props.onFieldChange('key', instance.key)}
-              optionRenderer={optionRenderer}
+            <Select
+              inputId="name"
+              className="abs-width-400 big-spacer-top it__configuration-name-select"
+              isClearable={false}
+              isSearchable={false}
               options={instances}
-              searchable={false}
-              value={formData.key}
-              valueKey="key"
-              valueRenderer={optionRenderer}
+              onChange={(instance: AlmSettingsInstance) => props.onFieldChange('key', instance.key)}
+              components={{
+                Option: optionRenderer,
+                SingleValue: singleValueRenderer
+              }}
+              value={instances.filter(instance => instance.key === formData.key)}
             />
           </div>
         </div>
index a4bf1454e6da7e10457cf27b1c0f2b2c5bb3cc55..d61e57e0ea62ba6edba433c8f5120685926c44ed 100644 (file)
@@ -19,8 +19,6 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import SelectLegacy from '../../../../../components/controls/SelectLegacy';
-import { waitAndUpdate } from '../../../../../helpers/testUtils';
 import {
   AlmKeys,
   AlmSettingsInstance,
@@ -124,18 +122,6 @@ it.each([
   }
 );
 
-it('should render select options correctly', async () => {
-  const wrapper = shallowRender({ instances });
-
-  await waitAndUpdate(wrapper);
-
-  const { optionRenderer } = wrapper.find(SelectLegacy).props();
-
-  expect(optionRenderer!(instances[0])).toMatchSnapshot();
-
-  expect(optionRenderer!(instances[1])).toMatchSnapshot();
-});
-
 function shallowRender(props: Partial<PRDecorationBindingRendererProps> = {}) {
   return shallow(
     <PRDecorationBindingRenderer
index f1bf70fdb61a36cbba22e53ded1d2316d709ce65..b72c9f4121e9b8eaac42c5164742a728bf2e7d2b 100644 (file)
@@ -129,19 +129,18 @@ exports[`should render correctly: when there are configuration errors (admin use
       <div
         className="settings-definition-right"
       >
-        <SelectLegacy
-          autosize={true}
-          className="abs-width-400 big-spacer-top"
-          clearable={false}
-          id="name"
-          menuContainerStyle={
+        <Select
+          className="abs-width-400 big-spacer-top it__configuration-name-select"
+          components={
             Object {
-              "maxWidth": "210%",
-              "width": "auto",
+              "Option": [Function],
+              "SingleValue": [Function],
             }
           }
+          inputId="name"
+          isClearable={false}
+          isSearchable={false}
           onChange={[Function]}
-          optionRenderer={[Function]}
           options={
             Array [
               Object {
@@ -165,10 +164,15 @@ exports[`should render correctly: when there are configuration errors (admin use
               },
             ]
           }
-          searchable={false}
-          value="i1"
-          valueKey="key"
-          valueRenderer={[Function]}
+          value={
+            Array [
+              Object {
+                "alm": "github",
+                "key": "i1",
+                "url": "http://github.enterprise.com",
+              },
+            ]
+          }
         />
       </div>
     </div>
@@ -330,19 +334,18 @@ exports[`should render correctly: when there are configuration errors (admin use
       <div
         className="settings-definition-right"
       >
-        <SelectLegacy
-          autosize={true}
-          className="abs-width-400 big-spacer-top"
-          clearable={false}
-          id="name"
-          menuContainerStyle={
+        <Select
+          className="abs-width-400 big-spacer-top it__configuration-name-select"
+          components={
             Object {
-              "maxWidth": "210%",
-              "width": "auto",
+              "Option": [Function],
+              "SingleValue": [Function],
             }
           }
+          inputId="name"
+          isClearable={false}
+          isSearchable={false}
           onChange={[Function]}
-          optionRenderer={[Function]}
           options={
             Array [
               Object {
@@ -366,10 +369,7 @@ exports[`should render correctly: when there are configuration errors (admin use
               },
             ]
           }
-          searchable={false}
-          value=""
-          valueKey="key"
-          valueRenderer={[Function]}
+          value={Array []}
         />
       </div>
     </div>
@@ -472,19 +472,18 @@ exports[`should render correctly: when there are configuration errors (non-admin
       <div
         className="settings-definition-right"
       >
-        <SelectLegacy
-          autosize={true}
-          className="abs-width-400 big-spacer-top"
-          clearable={false}
-          id="name"
-          menuContainerStyle={
+        <Select
+          className="abs-width-400 big-spacer-top it__configuration-name-select"
+          components={
             Object {
-              "maxWidth": "210%",
-              "width": "auto",
+              "Option": [Function],
+              "SingleValue": [Function],
             }
           }
+          inputId="name"
+          isClearable={false}
+          isSearchable={false}
           onChange={[Function]}
-          optionRenderer={[Function]}
           options={
             Array [
               Object {
@@ -508,10 +507,7 @@ exports[`should render correctly: when there are configuration errors (non-admin
               },
             ]
           }
-          searchable={false}
-          value=""
-          valueKey="key"
-          valueRenderer={[Function]}
+          value={Array []}
         />
       </div>
     </div>
@@ -617,19 +613,18 @@ exports[`should render correctly: with a single ALM instance 1`] = `
       <div
         className="settings-definition-right"
       >
-        <SelectLegacy
-          autosize={true}
-          className="abs-width-400 big-spacer-top"
-          clearable={false}
-          id="name"
-          menuContainerStyle={
+        <Select
+          className="abs-width-400 big-spacer-top it__configuration-name-select"
+          components={
             Object {
-              "maxWidth": "210%",
-              "width": "auto",
+              "Option": [Function],
+              "SingleValue": [Function],
             }
           }
+          inputId="name"
+          isClearable={false}
+          isSearchable={false}
           onChange={[Function]}
-          optionRenderer={[Function]}
           options={
             Array [
               Object {
@@ -639,10 +634,7 @@ exports[`should render correctly: with a single ALM instance 1`] = `
               },
             ]
           }
-          searchable={false}
-          value=""
-          valueKey="key"
-          valueRenderer={[Function]}
+          value={Array []}
         />
       </div>
     </div>
@@ -699,19 +691,18 @@ exports[`should render correctly: with a valid and saved form 1`] = `
       <div
         className="settings-definition-right"
       >
-        <SelectLegacy
-          autosize={true}
-          className="abs-width-400 big-spacer-top"
-          clearable={false}
-          id="name"
-          menuContainerStyle={
+        <Select
+          className="abs-width-400 big-spacer-top it__configuration-name-select"
+          components={
             Object {
-              "maxWidth": "210%",
-              "width": "auto",
+              "Option": [Function],
+              "SingleValue": [Function],
             }
           }
+          inputId="name"
+          isClearable={false}
+          isSearchable={false}
           onChange={[Function]}
-          optionRenderer={[Function]}
           options={
             Array [
               Object {
@@ -735,10 +726,15 @@ exports[`should render correctly: with a valid and saved form 1`] = `
               },
             ]
           }
-          searchable={false}
-          value="i1"
-          valueKey="key"
-          valueRenderer={[Function]}
+          value={
+            Array [
+              Object {
+                "alm": "github",
+                "key": "i1",
+                "url": "http://github.enterprise.com",
+              },
+            ]
+          }
         />
       </div>
     </div>
@@ -857,19 +853,18 @@ exports[`should render correctly: with an empty form 1`] = `
       <div
         className="settings-definition-right"
       >
-        <SelectLegacy
-          autosize={true}
-          className="abs-width-400 big-spacer-top"
-          clearable={false}
-          id="name"
-          menuContainerStyle={
+        <Select
+          className="abs-width-400 big-spacer-top it__configuration-name-select"
+          components={
             Object {
-              "maxWidth": "210%",
-              "width": "auto",
+              "Option": [Function],
+              "SingleValue": [Function],
             }
           }
+          inputId="name"
+          isClearable={false}
+          isSearchable={false}
           onChange={[Function]}
-          optionRenderer={[Function]}
           options={
             Array [
               Object {
@@ -893,10 +888,7 @@ exports[`should render correctly: with an empty form 1`] = `
               },
             ]
           }
-          searchable={false}
-          value=""
-          valueKey="key"
-          valueRenderer={[Function]}
+          value={Array []}
         />
       </div>
     </div>
@@ -949,31 +941,3 @@ exports[`should render correctly: with no ALM instances (non-admin user) 1`] = `
   </Alert>
 </div>
 `;
-
-exports[`should render select options correctly 1`] = `
-<React.Fragment>
-  <span>
-    i1
-     — 
-  </span>
-  <span
-    className="text-muted"
-  >
-    http://github.enterprise.com
-  </span>
-</React.Fragment>
-`;
-
-exports[`should render select options correctly 2`] = `
-<React.Fragment>
-  <span>
-    i2
-     — 
-  </span>
-  <span
-    className="text-muted"
-  >
-    http://github.enterprise.com
-  </span>
-</React.Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/users/components/UsersSelectSearch.tsx b/server/sonar-web/src/main/js/apps/users/components/UsersSelectSearch.tsx
deleted file mode 100644 (file)
index 6b6cca1..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { debounce } from 'lodash';
-import * as React from 'react';
-import SelectLegacy from '../../../components/controls/SelectLegacy';
-import Avatar from '../../../components/ui/Avatar';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-
-interface Option {
-  login: string;
-  name: string;
-  avatar?: string;
-}
-
-interface Props {
-  autoFocus?: boolean;
-  excludedUsers: string[];
-  handleValueChange: (option: Option) => void;
-  searchUsers: (query: string, ps: number) => Promise<{ users: Option[] }>;
-  selectedUser?: Option;
-}
-
-interface State {
-  isLoading: boolean;
-  search: string;
-  searchResult: Option[];
-}
-
-const LIST_SIZE = 10;
-const AVATAR_SIZE = 16;
-
-export default class UsersSelectSearch extends React.PureComponent<Props, State> {
-  mounted = false;
-
-  constructor(props: Props) {
-    super(props);
-    this.handleSearch = debounce(this.handleSearch, 250);
-    this.state = { searchResult: [], isLoading: false, search: '' };
-  }
-
-  componentDidMount() {
-    this.mounted = true;
-    this.handleSearch(this.state.search);
-  }
-
-  componentDidUpdate(prevProps: Props) {
-    if (this.props.excludedUsers !== prevProps.excludedUsers) {
-      this.handleSearch(this.state.search);
-    }
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  filterSearchResult = ({ users }: { users: Option[] }) =>
-    users.filter(user => !this.props.excludedUsers.includes(user.login)).slice(0, LIST_SIZE);
-
-  filterOptions = (options: Option[]) => {
-    return options; // We don't filter anything, this is done by the WS
-  };
-
-  handleSearch = (search: string) => {
-    this.props
-      .searchUsers(search, Math.min(this.props.excludedUsers.length + LIST_SIZE, 500))
-      .then(this.filterSearchResult)
-      .then(
-        searchResult => {
-          if (this.mounted) {
-            this.setState({ isLoading: false, searchResult });
-          }
-        },
-        () => {
-          if (this.mounted) {
-            this.setState({ isLoading: false, searchResult: [] });
-          }
-        }
-      );
-  };
-
-  handleInputChange = (search: string) => {
-    if (search == null || search.length === 1) {
-      this.setState({ search });
-    } else {
-      this.setState({ isLoading: true, search });
-      this.handleSearch(search);
-    }
-  };
-
-  render() {
-    const noResult =
-      this.state.search.length === 1
-        ? translateWithParameters('select2.tooShort', 2)
-        : translate('no_results');
-    return (
-      <SelectLegacy
-        autoFocus={this.props.autoFocus}
-        className="Select-big"
-        clearable={false}
-        filterOptions={this.filterOptions}
-        isLoading={this.state.isLoading}
-        labelKey="name"
-        noResultsText={noResult}
-        onChange={this.props.handleValueChange}
-        onInputChange={this.handleInputChange}
-        optionComponent={UsersSelectSearchOption}
-        options={this.state.searchResult}
-        placeholder=""
-        searchable={true}
-        value={this.props.selectedUser}
-        valueComponent={UsersSelectSearchValue}
-        valueKey="login"
-      />
-    );
-  }
-}
-
-interface OptionProps {
-  children?: React.ReactNode;
-  className?: string;
-  isFocused?: boolean;
-  onFocus: (option: Option, evt: React.MouseEvent<HTMLDivElement>) => void;
-  onSelect: (option: Option, evt: React.MouseEvent<HTMLDivElement>) => void;
-  option: Option;
-}
-
-export class UsersSelectSearchOption extends React.PureComponent<OptionProps> {
-  handleMouseDown = (evt: React.MouseEvent<HTMLDivElement>) => {
-    evt.preventDefault();
-    evt.stopPropagation();
-    this.props.onSelect(this.props.option, evt);
-  };
-
-  handleMouseEnter = (evt: React.MouseEvent<HTMLDivElement>) => {
-    this.props.onFocus(this.props.option, evt);
-  };
-
-  handleMouseMove = (evt: React.MouseEvent<HTMLDivElement>) => {
-    if (this.props.isFocused) {
-      return;
-    }
-    this.props.onFocus(this.props.option, evt);
-  };
-
-  render() {
-    const { option } = this.props;
-    return (
-      <div
-        className={this.props.className}
-        onMouseDown={this.handleMouseDown}
-        onMouseEnter={this.handleMouseEnter}
-        onMouseMove={this.handleMouseMove}
-        role="listitem"
-        title={option.name}>
-        <Avatar hash={option.avatar} name={option.name} size={AVATAR_SIZE} />
-        <strong className="spacer-left">{this.props.children}</strong>
-        <span className="note little-spacer-left">{option.login}</span>
-      </div>
-    );
-  }
-}
-
-interface ValueProps {
-  value?: Option;
-  children?: React.ReactNode;
-}
-
-export function UsersSelectSearchValue({ children, value }: ValueProps) {
-  return (
-    <div className="Select-value" title={value ? value.name : ''}>
-      {value && value.login && (
-        <div className="Select-value-label">
-          <Avatar hash={value.avatar} name={value.name} size={AVATAR_SIZE} />
-          <strong className="spacer-left">{children}</strong>
-          <span className="note little-spacer-left">{value.login}</span>
-        </div>
-      )}
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/UsersSelectSearch-test.tsx b/server/sonar-web/src/main/js/apps/users/components/__tests__/UsersSelectSearch-test.tsx
deleted file mode 100644 (file)
index d1d2c35..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { shallow } from 'enzyme';
-import * as React from 'react';
-import UsersSelectSearch, {
-  UsersSelectSearchOption,
-  UsersSelectSearchValue
-} from '../UsersSelectSearch';
-
-const selectedUser = {
-  login: 'admin',
-  name: 'Administrator',
-  avatar: '7daf6c79d4802916d83f6266e24850af'
-};
-const users = [
-  { login: 'admin', name: 'Administrator', email: 'admin@admin.ch' },
-  { login: 'test', name: 'Tester', email: 'tester@testing.ch' },
-  { login: 'foo', name: 'Foo Bar', email: 'foo@bar.ch' }
-];
-const excludedUsers = ['admin'];
-
-describe('UsersSelectSearch', () => {
-  it('should render correctly', () => {
-    const onSearch = jest.fn().mockResolvedValue({ users });
-    const wrapper = shallow(
-      <UsersSelectSearch
-        excludedUsers={excludedUsers}
-        handleValueChange={jest.fn()}
-        searchUsers={onSearch}
-        selectedUser={selectedUser}
-      />
-    );
-    expect(wrapper).toMatchSnapshot();
-    const searchResult = (wrapper.instance() as UsersSelectSearch).filterSearchResult({ users });
-    expect(searchResult).toMatchSnapshot();
-    expect(wrapper.setState({ searchResult })).toMatchSnapshot();
-  });
-});
-
-describe('UsersSelectSearchOption', () => {
-  it('should render correctly without all parameters', () => {
-    const wrapper = shallow(
-      <UsersSelectSearchOption onFocus={jest.fn()} onSelect={jest.fn()} option={selectedUser}>
-        {selectedUser.name}
-      </UsersSelectSearchOption>
-    );
-    expect(wrapper).toMatchSnapshot();
-  });
-
-  it('should render correctly with email instead of hash', () => {
-    const wrapper = shallow(
-      <UsersSelectSearchOption onFocus={jest.fn()} onSelect={jest.fn()} option={users[0]}>
-        {users[0].name}
-      </UsersSelectSearchOption>
-    );
-    expect(wrapper).toMatchSnapshot();
-  });
-});
-
-describe('UsersSelectSearchValue', () => {
-  it('should render correctly with a user', () => {
-    const wrapper = shallow(
-      <UsersSelectSearchValue value={selectedUser}>{selectedUser.name}</UsersSelectSearchValue>
-    );
-    expect(wrapper).toMatchSnapshot();
-  });
-
-  it('should render correctly with email instead of hash', () => {
-    const wrapper = shallow(
-      <UsersSelectSearchValue value={users[0]}>{users[0].name}</UsersSelectSearchValue>
-    );
-    expect(wrapper).toMatchSnapshot();
-  });
-
-  it('should render correctly without value', () => {
-    const wrapper = shallow(<UsersSelectSearchValue />);
-    expect(wrapper).toMatchSnapshot();
-  });
-});
diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap
deleted file mode 100644 (file)
index 0d64938..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`UsersSelectSearch should render correctly 1`] = `
-<SelectLegacy
-  className="Select-big"
-  clearable={false}
-  filterOptions={[Function]}
-  isLoading={false}
-  labelKey="name"
-  noResultsText="no_results"
-  onChange={[MockFunction]}
-  onInputChange={[Function]}
-  optionComponent={[Function]}
-  options={Array []}
-  placeholder=""
-  searchable={true}
-  value={
-    Object {
-      "avatar": "7daf6c79d4802916d83f6266e24850af",
-      "login": "admin",
-      "name": "Administrator",
-    }
-  }
-  valueComponent={[Function]}
-  valueKey="login"
-/>
-`;
-
-exports[`UsersSelectSearch should render correctly 2`] = `
-Array [
-  Object {
-    "email": "tester@testing.ch",
-    "login": "test",
-    "name": "Tester",
-  },
-  Object {
-    "email": "foo@bar.ch",
-    "login": "foo",
-    "name": "Foo Bar",
-  },
-]
-`;
-
-exports[`UsersSelectSearch should render correctly 3`] = `
-<SelectLegacy
-  className="Select-big"
-  clearable={false}
-  filterOptions={[Function]}
-  isLoading={false}
-  labelKey="name"
-  noResultsText="no_results"
-  onChange={[MockFunction]}
-  onInputChange={[Function]}
-  optionComponent={[Function]}
-  options={
-    Array [
-      Object {
-        "email": "tester@testing.ch",
-        "login": "test",
-        "name": "Tester",
-      },
-      Object {
-        "email": "foo@bar.ch",
-        "login": "foo",
-        "name": "Foo Bar",
-      },
-    ]
-  }
-  placeholder=""
-  searchable={true}
-  value={
-    Object {
-      "avatar": "7daf6c79d4802916d83f6266e24850af",
-      "login": "admin",
-      "name": "Administrator",
-    }
-  }
-  valueComponent={[Function]}
-  valueKey="login"
-/>
-`;
-
-exports[`UsersSelectSearchOption should render correctly with email instead of hash 1`] = `
-<div
-  onMouseDown={[Function]}
-  onMouseEnter={[Function]}
-  onMouseMove={[Function]}
-  role="listitem"
-  title="Administrator"
->
-  <withAppStateContext(Avatar)
-    name="Administrator"
-    size={16}
-  />
-  <strong
-    className="spacer-left"
-  >
-    Administrator
-  </strong>
-  <span
-    className="note little-spacer-left"
-  >
-    admin
-  </span>
-</div>
-`;
-
-exports[`UsersSelectSearchOption should render correctly without all parameters 1`] = `
-<div
-  onMouseDown={[Function]}
-  onMouseEnter={[Function]}
-  onMouseMove={[Function]}
-  role="listitem"
-  title="Administrator"
->
-  <withAppStateContext(Avatar)
-    hash="7daf6c79d4802916d83f6266e24850af"
-    name="Administrator"
-    size={16}
-  />
-  <strong
-    className="spacer-left"
-  >
-    Administrator
-  </strong>
-  <span
-    className="note little-spacer-left"
-  >
-    admin
-  </span>
-</div>
-`;
-
-exports[`UsersSelectSearchValue should render correctly with a user 1`] = `
-<div
-  className="Select-value"
-  title="Administrator"
->
-  <div
-    className="Select-value-label"
-  >
-    <withAppStateContext(Avatar)
-      hash="7daf6c79d4802916d83f6266e24850af"
-      name="Administrator"
-      size={16}
-    />
-    <strong
-      className="spacer-left"
-    >
-      Administrator
-    </strong>
-    <span
-      className="note little-spacer-left"
-    >
-      admin
-    </span>
-  </div>
-</div>
-`;
-
-exports[`UsersSelectSearchValue should render correctly with email instead of hash 1`] = `
-<div
-  className="Select-value"
-  title="Administrator"
->
-  <div
-    className="Select-value-label"
-  >
-    <withAppStateContext(Avatar)
-      name="Administrator"
-      size={16}
-    />
-    <strong
-      className="spacer-left"
-    >
-      Administrator
-    </strong>
-    <span
-      className="note little-spacer-left"
-    >
-      admin
-    </span>
-  </div>
-</div>
-`;
-
-exports[`UsersSelectSearchValue should render correctly without value 1`] = `
-<div
-  className="Select-value"
-  title=""
-/>
-`;
index 8da222bc358b5a4390f0916f261a9936be470c36..813e85e41ab6c8355e02cf2bbb2fe07d2e0c6e9b 100644 (file)
@@ -774,9 +774,11 @@ hotspots.continue_to_next_hotspot=Continue Reviewing
 hotspot.filters.title=Filters
 hotspot.filters.assignee.assigned_to_me=Assigned to me
 hotspot.filters.assignee.all=All
+hotspot.filters.status=Status filter
 hotspot.filters.status.to_review=To review
 hotspot.filters.status.acknowledged=Acknowledged
 hotspot.filters.status.fixed=Fixed
+hotspot.filters.period=Period filter
 hotspot.filters.period.since_leak_period=New code
 hotspot.filters.period.overall=Overall code
 hotspot.filters.status.safe=Safe