]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22418 Use Select from Echoes 0.4.0 in SQ
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Tue, 9 Jul 2024 12:51:45 +0000 (14:51 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 10 Jul 2024 20:02:38 +0000 (20:02 +0000)
server/sonar-web/src/main/js/app/utils/startReactApp.tsx
server/sonar-web/src/main/js/apps/component-measures/__tests__/ComponentMeasures-it.tsx
server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx
server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/Definition-it.tsx
server/sonar-web/src/main/js/apps/settings/components/inputs/InputForSingleSelectList.tsx
server/sonar-web/src/main/js/apps/settings/utils.ts
server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx
server/sonar-web/yarn.lock

index 5116cc33a10f5f6a7b3740a86c92c3a7482b8ee8..df15beb3d0451579cdce61aff768d7cebc519176 100644 (file)
@@ -20,7 +20,7 @@
 
 import { ThemeProvider } from '@emotion/react';
 import styled from '@emotion/styled';
-import { TooltipProvider } from '@sonarsource/echoes-react';
+import { EchoesProvider } from '@sonarsource/echoes-react';
 import { QueryClientProvider } from '@tanstack/react-query';
 import { ToastMessageContainer, lightTheme } from 'design-system';
 import * as React from 'react';
@@ -278,9 +278,9 @@ export default function startReactApp(
                   <ToastMessageContainer />
                   <Helmet titleTemplate={translate('page_title.template.default')} />
                   <StackContext>
-                    <TooltipProvider>
+                    <EchoesProvider>
                       <RouterProvider router={router} />
-                    </TooltipProvider>
+                    </EchoesProvider>
                   </StackContext>
                 </QueryClientProvider>
               </ThemeProvider>
index 85a6568c8aef7fbeb33cc79077f86ed0d07cf477..2329586d95b53c527e9b733f99bf522fc22a23da 100644 (file)
@@ -20,7 +20,6 @@
 import { screen, waitFor, within } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { times } from 'lodash';
-import selectEvent from 'react-select-event';
 import { byLabelText, byRole, byTestId, byText } from '~sonar-aligned/helpers/testSelector';
 import { ComponentQualifier } from '~sonar-aligned/types/component';
 import { MetricKey } from '~sonar-aligned/types/metrics';
@@ -380,7 +379,10 @@ describe('navigation', () => {
     await user.click(
       ui.measureBtn('component_measures.metric.maintainability_issues.name 2').get(),
     );
-    await waitFor(() => ui.changeViewToList());
+
+    // Click list option in view select
+    await user.click(ui.viewSelect.get());
+    await user.click(ui.selectOptions('component_measures.tab.list').get());
 
     expect(
       within(await ui.measuresRow('out.tsx').find()).getByRole('cell', { name: '2' }),
@@ -400,7 +402,10 @@ describe('navigation', () => {
 
     await user.click(ui.maintainabilityDomainBtn.get());
     await user.click(ui.measureBtn('Maintainability Rating metric.has_rating_X.E').get());
-    await waitFor(() => ui.changeViewToTreeMap());
+
+    // Click treemap option in view select
+    await user.click(ui.viewSelect.get());
+    await user.click(ui.selectOptions('component_measures.tab.treemap').get());
 
     expect(await ui.treeMapCell(/folderA/).find()).toBeInTheDocument();
     expect(ui.treeMapCell(/test1\.js/).get()).toBeInTheDocument();
@@ -600,6 +605,7 @@ function getPageObject() {
       name: 'component_measures.hidden_best_score_metrics_show_label',
     }),
     goToActivityLink: byRole('link', { name: 'component_measures.see_metric_history' }),
+    selectOptions: (name: string) => byRole('option', { name }),
   };
 
   const ui = {
@@ -610,12 +616,6 @@ function getPageObject() {
         expect(selectors.loading.query()).not.toBeInTheDocument();
       });
     },
-    async changeViewToList() {
-      await selectEvent.select(ui.viewSelect.get(), 'component_measures.tab.list');
-    },
-    async changeViewToTreeMap() {
-      await selectEvent.select(ui.viewSelect.get(), 'component_measures.tab.treemap');
-    },
     async arrowDown() {
       await user.keyboard('[ArrowDown]');
     },
index 9370dbc13cde6e5c8b71c5fbff1e6528941f9a0a..7a7913f285a5ce3ed71b8fc871d0a918285b1c8e 100644 (file)
@@ -17,7 +17,7 @@
  * 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 } from 'design-system';
+import { Select } from '@sonarsource/echoes-react';
 import * as React from 'react';
 import { translate } from '../../../helpers/l10n';
 import { MeasurePageView } from '../../../types/measures';
@@ -31,47 +31,40 @@ export interface MeasureViewSelectProps {
   view: MeasurePageView;
 }
 
-interface ViewOption {
-  label: string;
-  value: MeasurePageView;
-}
-
-export default function MeasureViewSelect(props: MeasureViewSelectProps) {
-  const { metric, view, className } = props;
-  const options = [];
-  if (hasTree(metric.key)) {
-    options.push({
-      label: translate('component_measures.tab.tree'),
-      value: MeasurePageView.tree,
-    });
-  }
-  if (hasList(metric.key)) {
-    options.push({
-      label: translate('component_measures.tab.list'),
-      value: MeasurePageView.list,
-    });
-  }
-  if (hasTreemap(metric.key, metric.type)) {
-    options.push({
-      label: translate('component_measures.tab.treemap'),
-      value: MeasurePageView.treemap,
-    });
-  }
+export default function MeasureViewSelect(props: Readonly<MeasureViewSelectProps>) {
+  const { metric, view, className, handleViewChange } = props;
 
-  const handleChange = (option: ViewOption) => {
-    return props.handleViewChange(option.value);
-  };
+  const measureViewOptions = React.useMemo(() => {
+    const options = [];
+    if (hasTree(metric.key)) {
+      options.push({
+        label: translate('component_measures.tab.tree'),
+        value: MeasurePageView.tree,
+      });
+    }
+    if (hasList(metric.key)) {
+      options.push({
+        label: translate('component_measures.tab.list'),
+        value: MeasurePageView.list,
+      });
+    }
+    if (hasTreemap(metric.key, metric.type)) {
+      options.push({
+        label: translate('component_measures.tab.treemap'),
+        value: MeasurePageView.treemap,
+      });
+    }
+    return options;
+  }, [metric]);
 
   return (
-    <InputSelect
-      size="small"
-      aria-labelledby="measures-view-selection-label"
-      blurInputOnSelect
+    <Select
+      ariaLabelledBy="measures-view-selection-label"
       className={className}
-      onChange={handleChange}
-      options={options}
-      isSearchable={false}
-      value={options.find((o) => o.value === view)}
+      data={measureViewOptions}
+      isNotClearable
+      onChange={handleViewChange}
+      value={view}
     />
   );
 }
index 693721f51cc0c60f38729eabefccfd45c6a71fdc..61685af0e78e3cc4c4da013d658d9c001cc89eb5 100644 (file)
@@ -17,7 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { InputSelect, LabelValueSelectOption, StyledPageTitle } from 'design-system';
+import { Select } from '@sonarsource/echoes-react';
+import { StyledPageTitle } from 'design-system';
 import * as React from 'react';
 import { translate } from '../../../helpers/l10n';
 import { VIEWS } from '../utils';
@@ -27,43 +28,39 @@ interface Props {
   view: string;
 }
 
-export interface PerspectiveOption {
-  label: string;
-  value: string;
-}
+export default function PerspectiveSelect(props: Readonly<Props>) {
+  const { onChange, view } = props;
+
+  const handleChange = React.useCallback(
+    (value: string) => {
+      onChange({ view: value });
+    },
+    [onChange],
+  );
 
-export default class PerspectiveSelect extends React.PureComponent<Props> {
-  handleChange = (option: PerspectiveOption) => {
-    this.props.onChange({ view: option.value });
-  };
+  const options = React.useMemo(
+    () => VIEWS.map((opt) => ({ value: opt.value, label: translate('projects.view', opt.label) })),
+    [],
+  );
 
-  render() {
-    const { view } = this.props;
-    const options: PerspectiveOption[] = [
-      ...VIEWS.map((opt) => ({
-        value: opt.value,
-        label: translate('projects.view', opt.label),
-      })),
-    ];
-    return (
-      <div className="sw-flex sw-items-center">
-        <StyledPageTitle
-          id="aria-projects-perspective"
-          as="label"
-          className="sw-body-sm-highlight sw-mr-2"
-        >
-          {translate('projects.perspective')}
-        </StyledPageTitle>
-        <InputSelect
-          aria-labelledby="aria-projects-perspective"
-          className="sw-mr-4 sw-body-sm"
-          onChange={(data: LabelValueSelectOption) => this.handleChange(data)}
-          options={options}
-          placeholder={translate('project_activity.filter_events')}
-          size="small"
-          value={options.find((option) => option.value === view)}
-        />
-      </div>
-    );
-  }
+  return (
+    <div className="sw-flex sw-items-center">
+      <StyledPageTitle
+        id="aria-projects-perspective"
+        as="label"
+        className="sw-body-sm-highlight sw-mr-2"
+      >
+        {translate('projects.perspective')}
+      </StyledPageTitle>
+      <Select
+        ariaLabelledBy="aria-projects-perspective"
+        className="sw-mr-4 sw-body-sm"
+        isNotClearable
+        onChange={handleChange}
+        data={options}
+        placeholder={translate('project_activity.filter_events')}
+        value={view}
+      />
+    </div>
+  );
 }
index 3458e060d211e310c460a800cd31bd22c6c75440..f34ef89f780e71e2e66526b60765d06bd6afd705 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 {
-  InputSelect,
-  InteractiveIcon,
-  LabelValueSelectOption,
-  SortAscendIcon,
-  SortDescendIcon,
-  StyledPageTitle,
-} from 'design-system';
-import { omit, sortBy } from 'lodash';
+import { Select, Tooltip } from '@sonarsource/echoes-react';
+import classNames from 'classnames';
+import { InteractiveIcon, SortAscendIcon, SortDescendIcon, StyledPageTitle } from 'design-system';
+import { sortBy } from 'lodash';
 import * as React from 'react';
-import { OptionProps, components } from 'react-select';
-import Tooltip from '../../../components/controls/Tooltip';
 import { translate } from '../../../helpers/l10n';
 import { SORTING_LEAK_METRICS, SORTING_METRICS, parseSorting } from '../utils';
 
 interface Props {
-  className?: string;
   defaultOption: string;
   onChange: (sort: string, desc: boolean) => void;
   selectedSort: string;
@@ -41,8 +33,8 @@ interface Props {
 }
 
 export interface Option {
-  className?: string;
   label: string;
+  optionClass?: string;
   short?: string;
   value: string;
 }
@@ -51,9 +43,7 @@ export default class ProjectsSortingSelect extends React.PureComponent<Props> {
   sortOrderButtonNode: HTMLElement | null = null;
 
   getSorting = () => {
-    const options = this.getOptions();
-    const { sortDesc, sortValue } = parseSorting(this.props.selectedSort);
-    return { sortDesc, value: options.find((o) => o.value === sortValue) };
+    return parseSorting(this.props.selectedSort);
   };
 
   getOptions = () => {
@@ -62,7 +52,7 @@ export default class ProjectsSortingSelect extends React.PureComponent<Props> {
       (option) => ({
         value: option.value,
         label: translate('projects.sorting', option.value),
-        className: option.class,
+        optionClass: option.class,
       }),
     );
   };
@@ -75,24 +65,12 @@ export default class ProjectsSortingSelect extends React.PureComponent<Props> {
     }
   };
 
-  handleSortChange = (option: Option) => {
-    this.props.onChange(option.value, this.getSorting().sortDesc);
-  };
-
-  projectsSortingSelectOption = (props: OptionProps<Option, false>) => {
-    const { data, children } = props;
-    return (
-      <components.Option
-        {...omit(props, ['children'])}
-        className={`it__project-sort-option-${data.value} ${data.className}`}
-      >
-        {data.short ? data.short : children}
-      </components.Option>
-    );
+  handleSortChange = (value: string) => {
+    this.props.onChange(value, this.getSorting().sortDesc);
   };
 
   render() {
-    const { sortDesc, value } = this.getSorting();
+    const { sortDesc, sortValue } = this.getSorting();
 
     return (
       <div className="sw-flex sw-items-center">
@@ -103,20 +81,17 @@ export default class ProjectsSortingSelect extends React.PureComponent<Props> {
         >
           {translate('projects.sort_by')}
         </StyledPageTitle>
-        <InputSelect
-          aria-labelledby="aria-projects-sort"
+        <Select
+          ariaLabelledBy="aria-projects-sort"
           className="sw-body-sm"
-          onChange={(data: LabelValueSelectOption<string>) => this.handleSortChange(data)}
-          options={this.getOptions()}
-          components={{
-            Option: this.projectsSortingSelectOption,
-          }}
+          onChange={this.handleSortChange}
+          data={this.getOptions()}
+          optionComponent={ProjectsSortingSelectItem}
           placeholder={translate('project_activity.filter_events')}
-          size="small"
-          value={value}
+          isNotClearable
+          value={sortValue}
         />
         <Tooltip
-          mouseLeaveDelay={1}
           content={
             sortDesc ? translate('projects.sort_descending') : translate('projects.sort_ascending')
           }
@@ -139,3 +114,19 @@ export default class ProjectsSortingSelect extends React.PureComponent<Props> {
     );
   }
 }
+
+const ProjectsSortingSelectItem = React.forwardRef<HTMLDivElement, Option & { className: string }>(
+  ({ className, label, optionClass, short, value, ...props }, ref) => {
+    return (
+      <div
+        className={classNames(`it__project-sort-option-${value}`, className, optionClass)}
+        ref={ref}
+        {...props}
+      >
+        {short ?? label}
+      </div>
+    );
+  },
+);
+
+ProjectsSortingSelectItem.displayName = 'ProjectsSortingSelectItem';
index 6ed6ce16f3309abb35411bd930a3a8fa978fa84a..ee47f9f3ae20b054e8b2bf65fae16759502c856a 100644 (file)
@@ -21,7 +21,6 @@ import { screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import { last } from 'lodash';
 import React from 'react';
-import selectEvent from 'react-select-event';
 import { byLabelText, byRole, byText } from '~sonar-aligned/helpers/testSelector';
 import SettingsServiceMock, {
   DEFAULT_DEFINITIONS_MOCK,
@@ -57,7 +56,8 @@ const ui = {
   jsonFormatStatus: byText('settings.json.format_error'),
   jsonFormatButton: byRole('button', { name: 'settings.json.format' }),
   toggleButton: byRole('switch'),
-  selectOption: (value: string) => byText(value),
+  selectOption: (name: string) => byRole('option', { name }),
+  selectInput: byRole('searchbox', { name: 'property.test.single.select.list.name' }),
   saveButton: byRole('button', { name: 'save' }),
   cancelButton: byRole('button', { name: 'cancel' }),
   changeButton: byRole('button', { name: 'change_verb' }),
@@ -176,35 +176,40 @@ it('renders definition for SettingType = BOOLEAN and can do operations', async (
 
 it('renders definition for SettingType = SINGLE_SELECT_LIST and can do operations', async () => {
   const user = userEvent.setup();
-  renderDefinition({
+  const definition = {
+    ...DEFAULT_DEFINITIONS_MOCK[0],
+    key: 'test.single.select.list',
     type: SettingType.SINGLE_SELECT_LIST,
-    options: ['first', 'second'],
-  });
+    defaultValue: 'default',
+    options: ['first', 'second', 'default'],
+  };
+  settingsMock.setDefinition(definition);
+  renderDefinition(definition, { key: definition.key, value: 'default' });
 
-  expect(
-    await ui.nameHeading('property.sonar.announcement.message.name').find(),
-  ).toBeInTheDocument();
+  expect(await ui.nameHeading('property.test.single.select.list.name').find()).toBeInTheDocument();
 
   // Can select option
-  expect(ui.selectOption('Select...').get()).toBeInTheDocument();
-  await selectEvent.select(ui.announcementInput.get(), 'first');
-  expect(ui.selectOption('first').get()).toBeInTheDocument();
+  expect(ui.selectInput.get()).toHaveValue('default');
+  await user.click(ui.selectInput.get());
+  await user.click(ui.selectOption('first').get());
+  expect(ui.selectInput.get()).toHaveValue('first');
 
   // Can cancel action
   await user.click(ui.cancelButton.get());
-  expect(ui.selectOption('Select...').get()).toBeInTheDocument();
+  expect(ui.selectInput.get()).toHaveValue('default');
 
   // Can save
-  await selectEvent.select(ui.announcementInput.get(), 'second');
+  await user.click(ui.selectInput.get());
+  await user.click(ui.selectOption('second').get());
   await user.click(ui.saveButton.get());
   expect(ui.savedMsg.get()).toBeInTheDocument();
 
   // Can reset
   await user.click(
-    ui.resetButton('settings.definition.reset.property.sonar.announcement.message.name').get(),
+    ui.resetButton('settings.definition.reset.property.test.single.select.list.name').get(),
   );
   await user.click(ui.resetButton().get());
-  expect(ui.selectOption('Select...').get()).toBeInTheDocument();
+  expect(ui.selectInput.get()).toHaveValue('default');
 });
 
 it('renders definition for SettingType = FORMATTED_TEXT and can do operations', async () => {
index a2f52364899fa9c92092fc8cb170a3aed48fb8da..25ab1b3e4f30c54ef3f749f7e1a47dcad136bf30 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 { InputSelect } from 'design-system';
+import { InputSize, Select } from '@sonarsource/echoes-react';
 import * as React from 'react';
 import { ExtendedSettingDefinition } from '../../../../types/settings';
 import { DefaultSpecializedInputProps, getPropertyName } from '../../utils';
 
 type Props = DefaultSpecializedInputProps & Pick<ExtendedSettingDefinition, 'options'>;
 
-export default class InputForSingleSelectList extends React.PureComponent<Props> {
-  handleInputChange = ({ value }: { label: string; value: string }) => {
-    this.props.onChange(value);
-  };
+export default function InputForSingleSelectList(props: Readonly<Props>) {
+  const { name, options: opts, value, setting } = props;
 
-  render() {
-    const { options: opts, name, value, setting } = this.props;
+  const options = React.useMemo(
+    () => opts.map((option) => ({ label: option, value: option })),
+    [opts],
+  );
 
-    const options = opts.map((option) => ({
-      label: option,
-      value: option,
-    }));
-
-    return (
-      <InputSelect
-        name={name}
-        onChange={this.handleInputChange}
-        aria-label={getPropertyName(setting.definition)}
-        options={options}
-        value={options.find((option) => option.value === value) ?? null}
-      />
-    );
-  }
+  return (
+    <Select
+      ariaLabel={getPropertyName(setting.definition)}
+      data={options}
+      isNotClearable
+      name={name}
+      onChange={props.onChange}
+      size={InputSize.Large}
+      value={value}
+    />
+  );
 }
index 538c0dceae88d1c6e54be3ab9ebe0961cea5e001..81d24b6c2a1a4bc8dae743053b4c0d1f21138c67 100644 (file)
@@ -291,7 +291,7 @@ export const ADDITIONAL_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [
     name: 'Azure DevOps integration',
     description: `azure devops integration configuration
       Configuration name
-      Give your configuration a clear and succinct name. 
+      Give your configuration a clear and succinct name.
       This name will be used at project level to identify the correct configured Azure instance for a project.
       Azure DevOps URL
       For Azure DevOps Server, provide the full collection URL:
@@ -300,8 +300,8 @@ export const ADDITIONAL_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [
       For Azure DevOps Services, provide the full organization URL:
       https://dev.azure.com/your_organization
       Personal Access Token
-      SonarQube needs a Personal Access Token to report the Quality Gate status on Pull Requests in Azure DevOps. 
-      To create this token, we recommend using a dedicated Azure DevOps account with administration permissions. 
+      SonarQube needs a Personal Access Token to report the Quality Gate status on Pull Requests in Azure DevOps.
+      To create this token, we recommend using a dedicated Azure DevOps account with administration permissions.
       The token itself needs Code > Read & Write permission.
     `,
     category: 'almintegration',
@@ -314,25 +314,25 @@ export const ADDITIONAL_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [
     name: 'Bitbucket integration',
     description: `bitbucket server cloud integration configuration
       Configuration name
-      Give your configuration a clear and succinct name. 
+      Give your configuration a clear and succinct name.
       This name will be used at project level to identify the correct configured Bitbucket instance for a project.
       Bitbucket Server URL
       Example: https://bitbucket-server.your-company.com
       Personal Access Token
-      SonarQube needs a Personal Access Token to report the Quality Gate status on Pull Requests in Bitbucket Server. 
-      To create this token, we recommend using a dedicated Bitbucket Server account with administration permissions. 
+      SonarQube needs a Personal Access Token to report the Quality Gate status on Pull Requests in Bitbucket Server.
+      To create this token, we recommend using a dedicated Bitbucket Server account with administration permissions.
       The token itself needs Read permission.
       Workspace ID
       The workspace ID is part of your bitbucket cloud URL https://bitbucket.org/{workspace}/{repository}
-      SonarQube needs you to create an OAuth consumer in your Bitbucket Cloud workspace settings 
-      to report the Quality Gate status on Pull Requests. 
-      It needs to be a private consumer with Pull Requests: Read permission. 
+      SonarQube needs you to create an OAuth consumer in your Bitbucket Cloud workspace settings
+      to report the Quality Gate status on Pull Requests.
+      It needs to be a private consumer with Pull Requests: Read permission.
       An OAuth callback URL is required by Bitbucket Cloud but not used by SonarQube so any URL works.
       OAuth Key
-      Bitbucket automatically creates an OAuth key when you create your OAuth consumer. 
+      Bitbucket automatically creates an OAuth key when you create your OAuth consumer.
       You can find it in your Bitbucket Cloud workspace settings under OAuth consumers.
       OAuth Secret
-      Bitbucket automatically creates an OAuth secret when you create your OAuth consumer. 
+      Bitbucket automatically creates an OAuth secret when you create your OAuth consumer.
       You can find it in your Bitbucket Cloud workspace settings under OAuth consumers.
     `,
     category: 'almintegration',
@@ -345,15 +345,15 @@ export const ADDITIONAL_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [
     name: 'GitHub integration',
     description: `github integration configuration
       Configuration name
-      Give your configuration a clear and succinct name. 
+      Give your configuration a clear and succinct name.
       This name will be used at project level to identify the correct configured GitHub App for a project.
       GitHub API URL
       Example for Github Enterprise:
       https://github.company.com/api/v3
       If using GitHub.com:
       https://api.github.com/
-      You need to install a GitHub App with specific settings and permissions to enable 
-      Pull Request Decoration on your Organization or Repository. 
+      You need to install a GitHub App with specific settings and permissions to enable
+      Pull Request Decoration on your Organization or Repository.
       GitHub App ID
       The App ID is found on your GitHub App's page on GitHub at Settings > Developer Settings > GitHub Apps
       Client ID
@@ -361,8 +361,8 @@ export const ADDITIONAL_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [
       Client Secret
       The Client secret is found on your GitHub App's page.
       Private Key
-      Your GitHub App's private key. You can generate a .pem file from your GitHub App's page under Private keys. 
-      Copy and paste the whole contents of the file here.     
+      Your GitHub App's private key. You can generate a .pem file from your GitHub App's page under Private keys.
+      Copy and paste the whole contents of the file here.
     `,
     category: 'almintegration',
     key: `sonar.almintegration.${AlmKeys.GitHub}`,
@@ -374,14 +374,14 @@ export const ADDITIONAL_SETTING_DEFINITIONS: ExtendedSettingDefinition[] = [
     name: 'Gitlab integration',
     description: `gitlab integration configuration
       Configuration name
-      Give your configuration a clear and succinct name. 
+      Give your configuration a clear and succinct name.
       This name will be used at project level to identify the correct configured GitLab instance for a project.
       GitLab API URL
       Provide the GitLab API URL. For example:
       https://gitlab.com/api/v4
       Personal Access Token
-      SonarQube needs a Personal Access Token to report the Quality Gate status on Merge Requests in GitLab. 
-      To create this token, 
+      SonarQube needs a Personal Access Token to report the Quality Gate status on Merge Requests in GitLab.
+      To create this token,
       we recommend using a dedicated GitLab account with Reporter permission to all target projects.
       The token itself needs the api scope.
     `,
index 0dd27c4218d9d04481af0da246afe140a357a51b..32ce0999cf2ddaca3f63fbb53f305b422598d565 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { TooltipProvider } from '@sonarsource/echoes-react';
+import { EchoesProvider } from '@sonarsource/echoes-react';
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
 import { Matcher, RenderResult, render, screen, within } from '@testing-library/react';
 import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
@@ -127,13 +127,13 @@ export function renderComponent(
             <AvailableFeaturesContext.Provider value={featureList}>
               <CurrentUserContextProvider currentUser={currentUser}>
                 <AppStateContextProvider appState={appState}>
-                  <TooltipProvider delayDuration={0}>
+                  <EchoesProvider tooltipsDelayDuration={0}>
                     <MemoryRouter initialEntries={[pathname]}>
                       <Routes>
                         <Route path="*" element={children} />
                       </Routes>
                     </MemoryRouter>
-                  </TooltipProvider>
+                  </EchoesProvider>
                 </AppStateContextProvider>
               </CurrentUserContextProvider>
             </AvailableFeaturesContext.Provider>
@@ -240,10 +240,9 @@ function renderRoutedApp(
                   <IndexationContextProvider>
                     <QueryClientProvider client={queryClient}>
                       <ToastMessageContainer />
-
-                      <TooltipProvider delayDuration={0}>
+                      <EchoesProvider tooltipsDelayDuration={0}>
                         <RouterProvider router={router} />
-                      </TooltipProvider>
+                      </EchoesProvider>
                     </QueryClientProvider>
                   </IndexationContextProvider>
                 </AppStateContextProvider>
index af84a1cffec8d5f8c8a69a69fefb0e37a63ab869..ee8afbcce1c84910ba60fd1674ead253df45e1da 100644 (file)
@@ -3190,16 +3190,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@floating-ui/core@npm:^1.6.0":
-  version: 1.6.4
-  resolution: "@floating-ui/core@npm:1.6.4"
-  dependencies:
-    "@floating-ui/utils": "npm:^0.2.4"
-  checksum: 10/589430cbff4bac90b9b891e2c94c57dc113d39ac163552f547d9e4c7d21f09997b9d33e82ec717759caee678c47f845f14a3f28df6f029fcfcf3ad803ba4eb7c
-  languageName: node
-  linkType: hard
-
-"@floating-ui/dom@npm:^1.0.0":
+"@floating-ui/dom@npm:^1.0.0, @floating-ui/dom@npm:^1.2.1":
   version: 1.6.5
   resolution: "@floating-ui/dom@npm:1.6.5"
   dependencies:
@@ -3218,16 +3209,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@floating-ui/dom@npm:^1.2.1":
-  version: 1.6.7
-  resolution: "@floating-ui/dom@npm:1.6.7"
-  dependencies:
-    "@floating-ui/core": "npm:^1.6.0"
-    "@floating-ui/utils": "npm:^0.2.4"
-  checksum: 10/a6a42bfd243c311f6040043808a6549c1db45fa36138b81cb1e615170d61fd2daf4f37accc1df3e0189405d97e3d71b12de39879c9d58ccf181c982b69cf6cf9
-  languageName: node
-  linkType: hard
-
 "@floating-ui/react-dom@npm:^1.3.0":
   version: 1.3.0
   resolution: "@floating-ui/react-dom@npm:1.3.0"
@@ -3273,13 +3254,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@floating-ui/utils@npm:^0.2.4":
-  version: 0.2.4
-  resolution: "@floating-ui/utils@npm:0.2.4"
-  checksum: 10/7662d7a4ae39c0287e026f666297a3d28c80e588251c8c59ff66938a0aead47d380bbb9018629bd63a98f399c3919ec689d5448a5c48ffc176d545ddef705df1
-  languageName: node
-  linkType: hard
-
 "@formatjs/ecma402-abstract@npm:2.0.0":
   version: 2.0.0
   resolution: "@formatjs/ecma402-abstract@npm:2.0.0"