]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19709 Fixing placeholder issue in assignee search
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>
Thu, 17 Aug 2023 09:42:52 +0000 (11:42 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 18 Aug 2023 20:02:50 +0000 (20:02 +0000)
30 files changed:
server/sonar-web/design-system/src/components/TagsSelector.tsx
server/sonar-web/design-system/src/components/__tests__/Tags-test.tsx
server/sonar-web/design-system/src/components/input/InputSearch.tsx
server/sonar-web/design-system/src/components/input/MultiSelectMenu.tsx
server/sonar-web/design-system/src/components/input/SearchSelect.tsx
server/sonar-web/design-system/src/components/input/SearchSelectControlledInput.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/input/SearchSelectDropdown.tsx
server/sonar-web/design-system/src/components/input/__tests__/InputSearch-test.tsx
server/sonar-web/design-system/src/components/input/__tests__/MultiSelectMenu-test.tsx
server/sonar-web/design-system/src/components/input/__tests__/SearchSelectControlledInput-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/global-search/GlobalSearch.tsx
server/sonar-web/src/main/js/app/components/global-search/__tests__/GlobalSearch-it.tsx
server/sonar-web/src/main/js/app/components/nav/component/branch-like/Menu.tsx
server/sonar-web/src/main/js/apps/code/components/Search.tsx
server/sonar-web/src/main/js/apps/create/project/Azure/AzureProjectCreateRenderer.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketCloud/BitbucketCloudSearchForm.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketServer/BitbucketImportRepositoryForm.tsx
server/sonar-web/src/main/js/apps/create/project/Github/GitHubProjectCreateRenderer.tsx
server/sonar-web/src/main/js/apps/create/project/Gitlab/GitlabProjectSelectionForm.tsx
server/sonar-web/src/main/js/apps/issues/components/AssigneeSelect.tsx
server/sonar-web/src/main/js/apps/issues/components/TagsSelect.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/ListStyleFacet.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/ListStyleFacet-test.tsx.snap
server/sonar-web/src/main/js/apps/projectInformation/about/components/MetaTags.tsx
server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/Assignee.tsx
server/sonar-web/src/main/js/apps/web-api-v2/components/ApiSidebar.tsx
server/sonar-web/src/main/js/components/activity-graph/AddGraphMetricPopup.tsx
server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx
server/sonar-web/src/main/js/components/issue/popups/IssueTagsPopup.tsx

index 4e1f7b47f70077b50c50e87107b5e3524d3aa9d5..55eaac13c0430dce8b620a809cf434aec74b1adf 100644 (file)
@@ -21,7 +21,6 @@ import { MultiSelectMenu } from './input/MultiSelectMenu';
 
 interface Props {
   allowNewElements?: boolean;
-  clearIconAriaLabel: string;
   createElementLabel: string;
   headerLabel: string;
   noResultsLabel: string;
@@ -38,7 +37,6 @@ const LIST_SIZE = 10;
 export function TagsSelector(props: Props) {
   const {
     allowNewElements,
-    clearIconAriaLabel,
     createElementLabel,
     headerLabel,
     noResultsLabel,
@@ -50,7 +48,6 @@ export function TagsSelector(props: Props) {
   return (
     <MultiSelectMenu
       allowNewElements={allowNewElements}
-      clearIconAriaLabel={clearIconAriaLabel}
       createElementLabel={createElementLabel}
       elements={tags}
       headerNode={<div className="sw-mt-4 sw-font-semibold">{headerLabel}</div>}
index 2974d4898030b588d3645cd28c698972ee99a2dd..73a623cee341eea92fe8c1dbfa447d384d8e7f97 100644 (file)
@@ -84,7 +84,6 @@ function Wrapper(overrides: Partial<FCProps<typeof Tags>> = {}) {
 
   const overlay = (
     <TagsSelector
-      clearIconAriaLabel="clear"
       createElementLabel="create new tag"
       headerLabel="edit tags"
       noResultsLabel="no results"
index b4f72d2f8deb844f32367c7c2d218da8d9bda731..1f7beabf84078ea7421f3c5ae42b9f874aa44032 100644 (file)
@@ -22,6 +22,7 @@ import styled from '@emotion/styled';
 import classNames from 'classnames';
 import { debounce } from 'lodash';
 import React, { PropsWithChildren, useEffect, useMemo, useRef, useState } from 'react';
+import { useIntl } from 'react-intl';
 import tw, { theme } from 'twin.macro';
 import { DEBOUNCE_DELAY, INPUT_SIZES } from '../../helpers/constants';
 import { Key } from '../../helpers/keyboard';
@@ -36,7 +37,6 @@ import { SearchIcon } from '../icons/SearchIcon';
 interface Props {
   autoFocus?: boolean;
   className?: string;
-  clearIconAriaLabel: string;
   id?: string;
   innerRef?: React.RefCallback<HTMLInputElement>;
   loading?: boolean;
@@ -50,7 +50,6 @@ interface Props {
   placeholder?: string;
   searchInputAriaLabel?: string;
   size?: InputSizeKeys;
-  tooShortText?: string;
   value?: string;
 }
 
@@ -72,14 +71,20 @@ export function InputSearch({
   maxLength = DEFAULT_MAX_LENGTH,
   size = 'medium',
   value: parentValue,
-  tooShortText,
   searchInputAriaLabel,
-  clearIconAriaLabel,
-  children,
 }: PropsWithChildren<Props>) {
+  const intl = useIntl();
   const input = useRef<null | HTMLElement>(null);
   const [value, setValue] = useState(parentValue ?? '');
-  const debouncedOnChange = useMemo(() => debounce(onChange, DEBOUNCE_DELAY), [onChange]);
+  const [dirty, setDirty] = useState(false);
+  const debouncedOnChange = useMemo(
+    () =>
+      debounce((val: string) => {
+        onChange(val);
+        setDirty(false);
+      }, DEBOUNCE_DELAY),
+    [onChange]
+  );
 
   const tooShort = isDefined(minLength) && value.length > 0 && value.length < minLength;
   const inputClassName = classNames('js-input-search', {
@@ -87,13 +92,8 @@ export function InputSearch({
     'sw-pr-10': value.length > 0,
   });
 
-  /*
-   * ParentValue is useful as an initial value for a page load
-   * And when the parent component wants to empty the search (facet search)
-   * After that the input value is controlled by this component
-   */
   useEffect(() => {
-    if (parentValue === '' || (parentValue !== undefined && value === '')) {
+    if (parentValue !== undefined && !dirty) {
       setValue(parentValue);
     }
   }, [parentValue]); // eslint-disable-line
@@ -113,6 +113,7 @@ export function InputSearch({
   const handleInputChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
     const eventValue = event.currentTarget.value;
     setValue(eventValue);
+    setDirty(true);
     changeValue(eventValue);
   };
 
@@ -140,42 +141,44 @@ export function InputSearch({
       id={id}
       onMouseDown={onMouseDown}
       style={{ '--inputSize': INPUT_SIZES[size] }}
-      title={tooShort && tooShortText && isDefined(minLength) ? tooShortText : ''}
+      title={
+        tooShort && isDefined(minLength)
+          ? intl.formatMessage({ id: 'select2.tooShort' }, { 0: minLength })
+          : ''
+      }
     >
       <StyledInputWrapper className="sw-flex sw-items-center">
-        {children ?? (
-          <input
-            aria-label={searchInputAriaLabel ?? placeholder}
-            autoComplete="off"
-            className={inputClassName}
-            maxLength={maxLength}
-            onBlur={onBlur}
-            onChange={handleInputChange}
-            onFocus={onFocus}
-            onKeyDown={handleInputKeyDown}
-            placeholder={placeholder}
-            ref={ref}
-            role="searchbox"
-            type="search"
-            value={value}
-          />
-        )}
+        <input
+          aria-label={searchInputAriaLabel ?? placeholder}
+          autoComplete="off"
+          className={inputClassName}
+          maxLength={maxLength}
+          onBlur={onBlur}
+          onChange={handleInputChange}
+          onFocus={onFocus}
+          onKeyDown={handleInputKeyDown}
+          placeholder={placeholder}
+          ref={ref}
+          role="searchbox"
+          type="search"
+          value={value}
+        />
         <Spinner className="sw-z-normal" loading={loading ?? false}>
           <StyledSearchIcon />
         </Spinner>
         {value && (
           <StyledInteractiveIcon
             Icon={CloseIcon}
-            aria-label={clearIconAriaLabel}
+            aria-label={intl.formatMessage({ id: 'clear' })}
             className="it__search-box-clear"
             onClick={handleClearClick}
             size="small"
           />
         )}
 
-        {tooShort && tooShortText && isDefined(minLength) && (
+        {tooShort && isDefined(minLength) && (
           <StyledNote className="sw-ml-1" role="note">
-            {tooShortText}
+            {intl.formatMessage({ id: 'select2.tooShort' }, { 0: minLength })}
           </StyledNote>
         )}
       </StyledInputWrapper>
@@ -237,7 +240,7 @@ export const StyledInputWrapper = styled.div`
   }
 `;
 
-const StyledSearchIcon = styled(SearchIcon)`
+export const StyledSearchIcon = styled(SearchIcon)`
   color: ${themeColor('inputBorder')};
   top: calc((${theme('height.control')} - ${theme('spacing.4')}) / 2);
 
@@ -251,7 +254,7 @@ export const StyledInteractiveIcon = styled(InteractiveIcon)`
   ${tw`sw-right-2`}
 `;
 
-const StyledNote = styled.span`
+export const StyledNote = styled.span`
   color: ${themeColor('inputPlaceholder')};
   top: calc(1px + ${theme('inset.2')});
 
index 191de5d27f8528629c6e7b4905f08892f3696220..bd73d4672ec27e96533e60b318e4be84176f5fc7 100644 (file)
@@ -28,7 +28,6 @@ import { MultiSelectMenuOption } from './MultiSelectMenuOption';
 interface Props {
   allowNewElements?: boolean;
   allowSelection?: boolean;
-  clearIconAriaLabel: string;
   createElementLabel: string;
   elements: string[];
   footerNode?: React.ReactNode;
@@ -262,7 +261,6 @@ export class MultiSelectMenu extends PureComponent<Props, State> {
       headerNode = '',
       footerNode = '',
       inputId,
-      clearIconAriaLabel,
       noResultsLabel,
       searchInputAriaLabel,
     } = this.props;
@@ -280,7 +278,6 @@ export class MultiSelectMenu extends PureComponent<Props, State> {
           <InputSearch
             autoFocus
             className="sw-mt-1"
-            clearIconAriaLabel={clearIconAriaLabel}
             id={inputId}
             loading={this.state.loading}
             onChange={this.handleSearchChange}
index 91c14aa81da7a98e7ae5f5cc3e50e1ea9bb170f5..7ed1c6fd88a9d50ec179d2b051172adb96febcaa 100644 (file)
 import classNames from 'classnames';
 import { omit } from 'lodash';
 import React, { RefObject } from 'react';
-import { useIntl } from 'react-intl';
 import { GroupBase, InputProps, components } from 'react-select';
 import AsyncSelect, { AsyncProps } from 'react-select/async';
 import Select from 'react-select/dist/declarations/src/Select';
 import { INPUT_SIZES } from '../../helpers';
 import { Key } from '../../helpers/keyboard';
-import { InputSearch } from './InputSearch';
 import { LabelValueSelectOption, SelectProps, selectStyle } from './InputSelect';
+import { SearchSelectControlledInput } from './SearchSelectControlledInput';
 
 type SearchSelectProps<
   V,
@@ -89,11 +88,9 @@ export function SearchSelectInput<
   Group extends GroupBase<Option> = GroupBase<Option>
 >(props: InputProps<Option, IsMulti, Group>) {
   const {
-    selectProps: { clearIconLabel, placeholder, isLoading, inputValue, minLength, tooShortText },
+    selectProps: { placeholder, isLoading, inputValue, minLength },
   } = props;
 
-  const intl = useIntl();
-
   const onChange = (v: string, prevValue = '') => {
     props.selectProps.onInputChange(v, { action: 'input-change', prevInputValue: prevValue });
   };
@@ -108,13 +105,11 @@ export function SearchSelectInput<
   };
 
   return (
-    <InputSearch
-      clearIconAriaLabel={clearIconLabel ?? intl.formatMessage({ id: 'clear' })}
+    <SearchSelectControlledInput
       loading={isLoading && inputValue.length >= (minLength ?? 0)}
       minLength={minLength}
       onChange={onChange}
       size="full"
-      tooShortText={tooShortText}
       value={inputValue}
     >
       <components.Input
@@ -123,6 +118,6 @@ export function SearchSelectInput<
         placeholder={placeholder as string}
         style={{}}
       />
-    </InputSearch>
+    </SearchSelectControlledInput>
   );
 }
diff --git a/server/sonar-web/design-system/src/components/input/SearchSelectControlledInput.tsx b/server/sonar-web/design-system/src/components/input/SearchSelectControlledInput.tsx
new file mode 100644 (file)
index 0000000..f737ce0
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+import React, { PropsWithChildren } from 'react';
+import { useIntl } from 'react-intl';
+import { INPUT_SIZES } from '../../helpers/constants';
+import { isDefined } from '../../helpers/types';
+import { InputSizeKeys } from '../../types/theme';
+import { Spinner } from '../Spinner';
+import { CloseIcon } from '../icons/CloseIcon';
+import {
+  InputSearchWrapper,
+  StyledInputWrapper,
+  StyledInteractiveIcon,
+  StyledNote,
+  StyledSearchIcon,
+} from './InputSearch';
+
+interface Props {
+  className?: string;
+  id?: string;
+  loading?: boolean;
+  minLength?: number;
+  onChange: (value: string) => void;
+  onMouseDown?: React.MouseEventHandler<HTMLInputElement>;
+  size?: InputSizeKeys;
+  value: string;
+}
+
+export function SearchSelectControlledInput({
+  id,
+  className,
+  onChange,
+  onMouseDown,
+  loading,
+  minLength,
+  size = 'medium',
+  value,
+  children,
+}: PropsWithChildren<Props>) {
+  const intl = useIntl();
+  const tooShort = isDefined(minLength) && value.length > 0 && value.length < minLength;
+
+  return (
+    <InputSearchWrapper
+      className={className}
+      id={id}
+      onMouseDown={onMouseDown}
+      style={{ '--inputSize': INPUT_SIZES[size] }}
+      title={
+        tooShort && isDefined(minLength)
+          ? intl.formatMessage({ id: 'select2.tooShort' }, { 0: minLength })
+          : ''
+      }
+    >
+      <StyledInputWrapper className="sw-flex sw-items-center">
+        {children}
+        <Spinner className="sw-z-normal" loading={loading ?? false}>
+          <StyledSearchIcon />
+        </Spinner>
+        {value !== '' && (
+          <StyledInteractiveIcon
+            Icon={CloseIcon}
+            aria-label={intl.formatMessage({ id: 'clear' })}
+            className="it__search-box-clear"
+            onClick={() => {
+              onChange('');
+            }}
+            size="small"
+          />
+        )}
+
+        {tooShort && isDefined(minLength) && (
+          <StyledNote className="sw-ml-1" role="note">
+            {intl.formatMessage({ id: 'select2.tooShort' }, { 0: minLength })}
+          </StyledNote>
+        )}
+      </StyledInputWrapper>
+    </InputSearchWrapper>
+  );
+}
+
+SearchSelectControlledInput.displayName = 'SearchSelectControlledInput'; // so that tests don't see the obfuscated production name
index 2e105e2365fdf56197a2e95a18b93b4bff93a10e..d6f0fe887d63e9ff23d05700227d97067de17c87 100644 (file)
@@ -40,9 +40,7 @@ import { SearchSelectDropdownControl } from './SearchSelectDropdownControl';
 
 declare module 'react-select/dist/declarations/src/Select' {
   export interface Props<Option, IsMulti extends boolean, Group extends GroupBase<Option>> {
-    clearIconLabel?: string;
     minLength?: number;
-    tooShortText?: string;
   }
 }
 
index 55901ed7cecaeae2aa8caf6864d29aed3b438b27..79339d6a0d8c735611f51ff5a4767382b5eccc2e 100644 (file)
@@ -76,13 +76,11 @@ it('should clear input using escape', async () => {
 function setupWithProps(props: Partial<FCProps<typeof InputSearch>> = {}) {
   return renderWithContext(
     <InputSearch
-      clearIconAriaLabel=""
       maxLength={150}
       minLength={2}
       onChange={jest.fn()}
       placeholder="placeholder"
       searchInputAriaLabel=""
-      tooShortText="too short"
       value="foo"
       {...props}
     />
index 7035ce401f256951fe09c0a5fcf2d9aa841e5fea..9c2a7520aa9ea529a7c86bb9f62dd5dcee4e7189 100644 (file)
@@ -97,7 +97,6 @@ it('should show no results', () => {
 function renderMultiselect(props: Partial<MultiSelectMenu['props']> = {}) {
   return renderWithContext(
     <MultiSelectMenu
-      clearIconAriaLabel="clear"
       createElementLabel="create thing"
       elements={[]}
       filterSelected={jest.fn()}
diff --git a/server/sonar-web/design-system/src/components/input/__tests__/SearchSelectControlledInput-test.tsx b/server/sonar-web/design-system/src/components/input/__tests__/SearchSelectControlledInput-test.tsx
new file mode 100644 (file)
index 0000000..86ac616
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { screen } from '@testing-library/react';
+import { renderWithContext } from '../../../helpers/testUtils';
+import { FCProps } from '../../../types/misc';
+import { InputSearch } from '../InputSearch';
+import { SearchSelectControlledInput } from '../SearchSelectControlledInput';
+
+it('should work properly when input is passed as a children', async () => {
+  const onChange = jest.fn();
+  const { user } = setupWithProps({
+    onChange,
+    value: 'foo',
+    children: <input onChange={onChange} />,
+  });
+  await user.click(screen.getByLabelText('clear'));
+  expect(onChange).toHaveBeenCalledWith('');
+});
+
+it('should warn when input is too short', () => {
+  setupWithProps({
+    value: 'f',
+    children: <input />,
+  });
+  expect(screen.getByRole('note')).toBeInTheDocument();
+});
+
+function setupWithProps(props: Partial<FCProps<typeof InputSearch>> = {}) {
+  return renderWithContext(
+    <SearchSelectControlledInput
+      maxLength={150}
+      minLength={2}
+      onChange={jest.fn()}
+      placeholder="placeholder"
+      searchInputAriaLabel=""
+      value="foo"
+      {...props}
+    />
+  );
+}
index 1bf0c8c2421ea5a5227f6739bfcd49d3f0e71975..1554fc928ba129ab2bd11c4a4982d97a144530db 100644 (file)
@@ -411,9 +411,7 @@ export class GlobalSearch extends React.PureComponent<Props, State> {
             placeholder={translate('search.search_for_projects')}
             size="auto"
             value={query}
-            tooShortText={translateWithParameters('select2.tooShort', MIN_SEARCH_QUERY_LENGTH)}
             searchInputAriaLabel={translate('search_verb')}
-            clearIconAriaLabel={translate('clear')}
           />
         </Popup>
       </div>
index ae3197eb2a7c21eecdede7a2aff5a2eb6a229a3d..92885b62591b744d215937e67ef3d78461fde119 100644 (file)
@@ -60,7 +60,7 @@ const ui = {
   searchItemListWrapper: byRole('menu'),
   searchItem: byRole('menuitem'),
   showMoreButton: byRole('menuitem', { name: 'show_more' }),
-  tooShortWarning: byText('select2.tooShort.2'),
+  tooShortWarning: byText('select2.tooShort'),
   noResultTextABCD: byText('no_results_for_x.abcd'),
 };
 
index 777f2d999fd8648cdff79f6a3e22e366be2c3f0a..6b8f94cf2aa2a2e4cc9b5acb13a144de1ed51c34 100644 (file)
@@ -172,7 +172,6 @@ export class Menu extends React.PureComponent<Props, State> {
           size="auto"
           value={query}
           searchInputAriaLabel={translate('search_verb')}
-          clearIconAriaLabel={translate('clear')}
         />
         <MenuItemList
           branchLikeTree={branchLikesToDisplayTree}
index f7d8252248a7f8e25da1995dfd0944c7360f3dec..1813508e0ae3f1a7c5412fff15cdba070b8c6d2a 100644 (file)
@@ -167,7 +167,6 @@ class Search extends React.PureComponent<Props, State> {
           </div>
         )}
         <InputSearch
-          clearIconAriaLabel={translate('clear')}
           searchInputAriaLabel={searchPlaceholder}
           minLength={3}
           onChange={this.handleQueryChange}
index 0dfb6b79fd8bb4f777b5cae01ead1af206851c46..0e9a3cd513095627f5f59690cca5b54aea83daec 100644 (file)
@@ -136,7 +136,6 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
           <>
             <div className="sw-mb-10 sw-w-abs-400">
               <InputSearch
-                clearIconAriaLabel={translate('clear')}
                 onChange={props.onSearch}
                 placeholder={translate('onboarding.create_project.search_projects_repositories')}
                 size="full"
index 9f9994d82a093fce6650b334412b821479b2bf03..727b24720c7cea5d251a6037f380665fee7af8d3 100644 (file)
@@ -76,7 +76,6 @@ export default function BitbucketCloudSearchForm(props: BitbucketCloudSearchForm
     <div>
       <div className="sw-flex sw-items-center sw-mb-6 sw-w-abs-400">
         <InputSearch
-          clearIconAriaLabel={translate('clear')}
           loading={searching}
           minLength={3}
           onChange={props.onSearch}
index e7a05eb1c15dec28f773751e0f2e2b5796333273..425373e435d8a887c7358294f11f302a0494a281 100644 (file)
@@ -76,7 +76,6 @@ export default function BitbucketImportRepositoryForm(props: BitbucketImportRepo
       <div className="sw-mb-10 sw-w-abs-400">
         <InputSearch
           searchInputAriaLabel={translate('onboarding.create_project.search_repositories_by_name')}
-          clearIconAriaLabel={translate('clear')}
           onChange={props.onSearch}
           placeholder={translate('onboarding.create_project.search_repositories_by_name')}
           size="full"
index 97ffd1f2755f5b076605cc64f3a3fd55ecfdcaec..722a20276974dee81e764914245fac87f252be19 100644 (file)
@@ -80,7 +80,6 @@ function renderRepositoryList(props: GitHubProjectCreateRendererProps) {
             onChange={props.onSearch}
             placeholder={translate('onboarding.create_project.search_repositories')}
             value={searchQuery}
-            clearIconAriaLabel={translate('clear')}
           />
         </div>
 
index 3ae79b1ec6001c1fe0f99303885314ddeee624af..953f4e8d9210fcde4a06d903550e3a297253d99e 100644 (file)
@@ -77,7 +77,8 @@ export default function GitlabProjectSelectionForm(props: GitlabProjectSelection
         onChange={props.onSearch}
         placeholder={translate('onboarding.create_project.search_repositories')}
         value={searchQuery}
-        clearIconAriaLabel={translate('clear')}
+        minLength={3}
+        loading={searching}
       />
 
       {projects.length === 0 ? (
index 111f4561a85ababfe9ebe5f12df882dccf9287ce..0de539ae33397f21eda27a832ca74b98eef1115f 100644 (file)
@@ -100,7 +100,6 @@ export default function AssigneeSelect(props: AssigneeSelectProps) {
           ? translateWithParameters('select2.tooShort', MIN_QUERY_LENGTH)
           : translate('select2.noMatches')
       }
-      tooShortText={translateWithParameters('search.tooShort', MIN_QUERY_LENGTH)}
       placeholder={translate('search.search_for_users')}
       controlLabel={controlLabel}
       controlAriaLabel={translate('issue_bulk_change.assignee.change')}
index 7956a5947aeda178755309e1b1c8980530a9e24b..6b52f0e53194981864c2667ec9a046c8ea42ba2d 100644 (file)
@@ -72,7 +72,6 @@ export default function TagsSelect(props: Props) {
           <TagsSelector
             allowNewElements={allowCreation}
             createElementLabel={translateWithParameters('issue.create_tag')}
-            clearIconAriaLabel={translate('clear')}
             headerLabel={translate('issue_bulk_change.select_tags')}
             noResultsLabel={translate('no_results')}
             onSelect={onSelect}
index 106ea55769073e362b610a7f930b97cef92fe139..8ea575ab347f0e41351fc3027036127b49c47c09 100644 (file)
@@ -380,9 +380,7 @@ export class ListStyleFacet<S> extends React.Component<Props<S>, State<S>> {
         size="auto"
         value={this.state.query}
         searchInputAriaLabel={translate('search_verb')}
-        clearIconAriaLabel={translate('clear')}
         minLength={minSearchLength}
-        tooShortText={translateWithParameters('select2.tooShort', minSearchLength)}
       />
     );
   }
index f3f1bf306ffe7362005973752b92ef81dbaf4c2f..b044ec5f7779adc3e8cc63716fab23c421162738 100644 (file)
@@ -36,13 +36,11 @@ exports[`should display all selected items 1`] = `
     <InputSearch
       autoFocus={false}
       className="it__search-box-input sw-mb-4 sw-w-full"
-      clearIconAriaLabel="clear"
       minLength={2}
       onChange={[Function]}
       placeholder="search for foo..."
       searchInputAriaLabel="search_verb"
       size="auto"
-      tooShortText="select2.tooShort.2"
       value=""
     />
     <FacetItemsList
@@ -121,13 +119,11 @@ exports[`should render 1`] = `
     <InputSearch
       autoFocus={false}
       className="it__search-box-input sw-mb-4 sw-w-full"
-      clearIconAriaLabel="clear"
       minLength={2}
       onChange={[Function]}
       placeholder="search for foo..."
       searchInputAriaLabel="search_verb"
       size="auto"
-      tooShortText="select2.tooShort.2"
       value=""
     />
     <FacetItemsList
@@ -197,13 +193,11 @@ exports[`should search 1`] = `
     <InputSearch
       autoFocus={false}
       className="it__search-box-input sw-mb-4 sw-w-full"
-      clearIconAriaLabel="clear"
       minLength={2}
       onChange={[Function]}
       placeholder="search for foo..."
       searchInputAriaLabel="search_verb"
       size="auto"
-      tooShortText="select2.tooShort.2"
       value="query"
     />
     <FacetItemsList
@@ -266,13 +260,11 @@ exports[`should search 2`] = `
     <InputSearch
       autoFocus={false}
       className="it__search-box-input sw-mb-4 sw-w-full"
-      clearIconAriaLabel="clear"
       minLength={2}
       onChange={[Function]}
       placeholder="search for foo..."
       searchInputAriaLabel="search_verb"
       size="auto"
-      tooShortText="select2.tooShort.2"
       value="query"
     />
     <FacetItemsList
@@ -345,13 +337,11 @@ exports[`should search 3`] = `
     <InputSearch
       autoFocus={false}
       className="it__search-box-input sw-mb-4 sw-w-full"
-      clearIconAriaLabel="clear"
       minLength={2}
       onChange={[Function]}
       placeholder="search for foo..."
       searchInputAriaLabel="search_verb"
       size="auto"
-      tooShortText="select2.tooShort.2"
       value=""
     />
     <FacetItemsList
@@ -421,13 +411,11 @@ exports[`should search 4`] = `
     <InputSearch
       autoFocus={false}
       className="it__search-box-input sw-mb-4 sw-w-full"
-      clearIconAriaLabel="clear"
       minLength={2}
       onChange={[Function]}
       placeholder="search for foo..."
       searchInputAriaLabel="search_verb"
       size="auto"
-      tooShortText="select2.tooShort.2"
       value="blabla"
     />
     <div
@@ -463,13 +451,11 @@ exports[`should search 5`] = `
     <InputSearch
       autoFocus={false}
       className="it__search-box-input sw-mb-4 sw-w-full"
-      clearIconAriaLabel="clear"
       minLength={2}
       onChange={[Function]}
       placeholder="search for foo..."
       searchInputAriaLabel="search_verb"
       size="auto"
-      tooShortText="select2.tooShort.2"
       value="blabla"
     />
     <div
index 65a075d8e25e8676a271ea8b316122299fc4ddb6..fd7c98b51f87722fd7a042af4dbad71d97931ca3 100644 (file)
@@ -119,7 +119,6 @@ function MetaTagsSelector({ selectedTags, setProjectTags }: MetaTagsSelectorProp
     <TagsSelector
       headerLabel={translate('tags')}
       searchInputAriaLabel={translate('search.search_for_tags')}
-      clearIconAriaLabel={translate('clear')}
       createElementLabel={translate('issue.create_tag')}
       noResultsLabel={translate('no_results')}
       onSearch={onSearch}
index 40e5b836998ca1f8065c05473661e1a8a1de56bf..e4ca4d1c71909bc4630e2c6bf4786136986ee153 100644 (file)
@@ -21,7 +21,7 @@ import { InputSearch, LightLabel, LightPrimary } from 'design-system';
 import * as React from 'react';
 import HomePageSelect from '../../../components/controls/HomePageSelect';
 import Tooltip from '../../../components/controls/Tooltip';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
 import { RawQuery } from '../../../types/types';
 import { CurrentUser, isLoggedIn } from '../../../types/users';
 import ApplicationCreation from './ApplicationCreation';
@@ -66,9 +66,7 @@ export default function PageHeader(props: Props) {
               size="auto"
               placeholder={translate('search.search_for_projects')}
               value={query.search ?? ''}
-              tooShortText={translateWithParameters('select2.tooShort', MIN_SEARCH_QUERY_LENGTH)}
               searchInputAriaLabel={translate('search_verb')}
-              clearIconAriaLabel={translate('clear')}
             />
           </Tooltip>
           <PerspectiveSelect onChange={props.onPerspectiveChange} view={view} />
index c732c7b7e2d04c94db221e882ebc7380e6404d37..7278b51fccdf390001028a1117b795585acd4b53 100644 (file)
@@ -122,7 +122,6 @@ export default function Assignee(props: Props) {
       minLength={minSearchLength}
       isDiscreet
       controlLabel={controlLabel}
-      tooShortText={translateWithParameters('search.tooShort', String(minSearchLength))}
       placeholder={translate('search.search_for_users')}
       aria-label={translate('search.search_for_users')}
     />
index 8a3cc3e070204d42619a9bc226c9e438fddf8f9f..5d20aca8cc80bcf91d07124e891d2e476c08708a 100644 (file)
@@ -79,7 +79,6 @@ export default function ApiSidebar({ apisList, docInfo }: Props) {
         className="sw-w-full"
         placeholder={translate('api_documentation.v2.search')}
         onChange={setSearch}
-        clearIconAriaLabel={translate('clear')}
         value={search}
       />
 
index f762e83d8554a2598d690136a871c6d4b118c6bf..a704df6ce52fcad881b119ed344c8a3f8b74ebea 100644 (file)
@@ -62,7 +62,6 @@ export default function AddGraphMetricPopup({
 
   return (
     <MultiSelectMenu
-      clearIconAriaLabel={translate('clear_verb')}
       createElementLabel=""
       searchInputAriaLabel={translate('project_activity.graphs.custom.select_metric')}
       allowNewElements={false}
index fa0e959ff83eaed280a0749343926ade3400d9be..752163d452c672ce6a88c1a76d8027b367ef6b6d 100644 (file)
@@ -150,7 +150,6 @@ export default function IssueAssignee(props: Props) {
         onMenuClose={handleClose}
         isDiscreet
         controlLabel={controlLabel}
-        tooShortText={translateWithParameters('search.tooShort', String(minSearchLength))}
         placeholder={translate('search.search_for_users')}
         aria-label={translate('search.search_for_users')}
         zLevel={PopupZLevel.Absolute}
index d238b18829ed46393683fd8582eb4d946b5caed1..2e37c4848402342ab557e81bfc4e053bf46e4c0f 100644 (file)
@@ -56,7 +56,6 @@ function IssueTagsPopup({ selectedTags, setTags }: IssueTagsPopupProps) {
     <TagsSelector
       headerLabel={translate('issue.tags')}
       searchInputAriaLabel={translate('search.search_for_tags')}
-      clearIconAriaLabel={translate('clear')}
       createElementLabel={translate('issue.create_tag')}
       noResultsLabel={translate('no_results')}
       onSearch={onSearch}