]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17453 Fixed order when changing selection in nulti select ui
authorMathieu Suen <mathieu.suen@sonarsource.com>
Tue, 18 Oct 2022 12:36:44 +0000 (14:36 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 21 Oct 2022 20:03:17 +0000 (20:03 +0000)
server/sonar-web/src/main/js/components/common/MultiSelect.tsx
server/sonar-web/src/main/js/components/common/MultiSelectOption.tsx
server/sonar-web/src/main/js/components/common/__tests__/MultiSelect-test.tsx
server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx [deleted file]
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelect-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap [deleted file]

index ff01303af8009018096d53859970f2efab58e5cb..efffa6b48513b0865df53c36faf44d9919b0d372 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import classNames from 'classnames';
-import { difference } from 'lodash';
+import { isEmpty, remove, xor } from 'lodash';
 import * as React from 'react';
 import SearchBox from '../../components/controls/SearchBox';
-import { isShortcut } from '../../helpers/keyboardEventHelpers';
-import { KeyboardKeys } from '../../helpers/keycodes';
 import { translateWithParameters } from '../../helpers/l10n';
-import MultiSelectOption from './MultiSelectOption';
+import MultiSelectOption, { Element } from './MultiSelectOption';
 
 export interface MultiSelectProps {
   allowNewElements?: boolean;
   allowSelection?: boolean;
   legend: string;
   elements: string[];
-  // eslint-disable-next-line react/no-unused-prop-types
   filterSelected?: (query: string, selectedElements: string[]) => string[];
   footerNode?: React.ReactNode;
   listSize?: number;
@@ -45,11 +42,9 @@ export interface MultiSelectProps {
 }
 
 interface State {
-  activeIdx: number;
   loading: boolean;
   query: string;
-  selectedElements: string[];
-  unselectedElements: string[];
+  elements: Element[];
 }
 
 interface DefaultProps {
@@ -63,8 +58,6 @@ interface DefaultProps {
 type PropsWithDefault = MultiSelectProps & DefaultProps;
 
 export default class MultiSelect extends React.PureComponent<PropsWithDefault, State> {
-  container?: HTMLDivElement | null;
-  searchInput?: HTMLInputElement | null;
   mounted = false;
 
   static defaultProps: DefaultProps = {
@@ -78,51 +71,79 @@ export default class MultiSelect extends React.PureComponent<PropsWithDefault, S
   constructor(props: PropsWithDefault) {
     super(props);
     this.state = {
-      activeIdx: 0,
-      loading: true,
+      loading: false,
       query: '',
-      selectedElements: [],
-      unselectedElements: []
+      elements: []
     };
   }
 
   componentDidMount() {
     this.mounted = true;
     this.onSearchQuery('');
-    this.updateSelectedElements(this.props as PropsWithDefault);
-    this.updateUnselectedElements(this.props as PropsWithDefault);
-    if (this.container) {
-      this.container.addEventListener('keydown', this.handleKeyboard, true);
-    }
+    this.computeElements();
   }
 
-  componentDidUpdate(prevProps: PropsWithDefault) {
+  componentDidUpdate(prevProps: PropsWithDefault, prevState: State) {
     if (
-      prevProps.elements !== this.props.elements ||
-      prevProps.selectedElements !== this.props.selectedElements
+      !isEmpty(
+        xor(
+          [...prevProps.selectedElements, ...prevProps.elements],
+          [...this.props.selectedElements, ...this.props.elements]
+        )
+      )
     ) {
-      this.updateSelectedElements(this.props);
-      this.updateUnselectedElements(this.props);
-
-      const totalElements = this.getAllElements(this.props, this.state).length;
-      if (this.state.activeIdx >= totalElements) {
-        this.setState({ activeIdx: totalElements - 1 });
-      }
+      this.computeElements();
     }
 
-    if (this.searchInput) {
-      this.searchInput.focus();
+    if (prevState.query !== this.state.query) {
+      this.setState(({ query, elements }, props) => {
+        const newElements = [...elements];
+        this.appendCreateElelement(newElements, query, props);
+        return { elements: newElements };
+      });
     }
   }
 
   componentWillUnmount() {
     this.mounted = false;
-    if (this.container) {
-      this.container.removeEventListener('keydown', this.handleKeyboard, true);
+  }
+
+  computeElements() {
+    this.setState(({ query }, props) => {
+      const newStateElement: Element[] = [
+        ...this.props
+          .filterSelected(query, this.props.selectedElements)
+          .map(e => ({ value: e, selected: true })),
+        ...this.props.elements.map(e => ({
+          value: e,
+          selected: false
+        }))
+      ];
+
+      this.appendCreateElelement(newStateElement, query, props);
+      return { elements: newStateElement };
+    });
+  }
+
+  appendCreateElelement(elements: Element[], query: string, props: PropsWithDefault) {
+    const { allowNewElements = true } = props;
+    if (this.isNewElement(query, props) && allowNewElements) {
+      const create = elements.find(e => e.custom);
+      if (create) {
+        create.value = query;
+      } else {
+        elements.push({ value: query, selected: false, custom: true });
+      }
+    } else if (!this.isNewElement(query, props) && allowNewElements) {
+      remove(elements, e => e.custom);
     }
   }
 
   handleSelectChange = (selected: boolean, item: string) => {
+    this.setState(({ elements }) => {
+      const newElements = elements.map(e => (e.value === item ? { value: e.value, selected } : e));
+      return { elements: newElements };
+    });
     if (selected) {
       this.onSelectItem(item);
     } else {
@@ -134,42 +155,16 @@ export default class MultiSelect extends React.PureComponent<PropsWithDefault, S
     this.onSearchQuery((this.props as PropsWithDefault).validateSearchInput(value));
   };
 
-  handleElementHover = (element: string) => {
-    this.setState((prevState, props) => {
-      return { activeIdx: this.getAllElements(props, prevState).indexOf(element) };
-    });
-  };
-
-  handleKeyboard = (event: KeyboardEvent) => {
-    if (isShortcut(event)) {
-      return true;
-    }
-    switch (event.key) {
-      case KeyboardKeys.DownArrow:
-        event.stopPropagation();
-        event.preventDefault();
-        this.setState(this.selectNextElement);
-        break;
-      case KeyboardKeys.UpArrow:
-        event.stopPropagation();
-        event.preventDefault();
-        this.setState(this.selectPreviousElement);
-        break;
-      case KeyboardKeys.LeftArrow:
-      case KeyboardKeys.RightArrow:
-        event.stopPropagation();
-        break;
-      case KeyboardKeys.Enter:
-        if (this.state.activeIdx >= 0) {
-          this.toggleSelect(this.getAllElements(this.props, this.state)[this.state.activeIdx]);
-        }
-        break;
-    }
-  };
-
   onSearchQuery = (query: string) => {
-    this.setState({ activeIdx: 0, loading: true, query });
+    const { allowNewElements = true } = this.props;
+
     this.props.onSearch(query).then(this.stopLoading, this.stopLoading);
+    if (allowNewElements) {
+      this.setState({
+        loading: true,
+        query
+      });
+    }
   };
 
   onSelectItem = (item: string) => {
@@ -182,67 +177,7 @@ export default class MultiSelect extends React.PureComponent<PropsWithDefault, S
   onUnselectItem = (item: string) => this.props.onUnselect(item);
 
   isNewElement = (elem: string, { selectedElements, elements }: PropsWithDefault) =>
-    elem.length > 0 && selectedElements.indexOf(elem) === -1 && elements.indexOf(elem) === -1;
-
-  updateSelectedElements = (props: PropsWithDefault) => {
-    this.setState((state: State) => {
-      if (state.query) {
-        return {
-          selectedElements: props.filterSelected(state.query, props.selectedElements)
-        };
-      } else {
-        return { selectedElements: [...props.selectedElements] };
-      }
-    });
-  };
-
-  updateUnselectedElements = (props: PropsWithDefault) => {
-    this.setState((state: State) => {
-      if (props.listSize === 0) {
-        return { unselectedElements: difference(props.elements, props.selectedElements) };
-      } else if (props.listSize < state.selectedElements.length) {
-        return { unselectedElements: [] };
-      } else {
-        return {
-          unselectedElements: difference(props.elements, props.selectedElements).slice(
-            0,
-            props.listSize - state.selectedElements.length
-          )
-        };
-      }
-    });
-  };
-
-  getAllElements = (props: PropsWithDefault, state: State) => {
-    if (this.isNewElement(state.query, props)) {
-      return [...state.selectedElements, ...state.unselectedElements, state.query];
-    } else {
-      return [...state.selectedElements, ...state.unselectedElements];
-    }
-  };
-
-  setElementActive = (idx: number) => this.setState({ activeIdx: idx });
-
-  selectNextElement = (state: State, props: PropsWithDefault) => {
-    const { activeIdx } = state;
-    const allElements = this.getAllElements(props, state);
-    if (activeIdx < 0 || activeIdx >= allElements.length - 1) {
-      return { activeIdx: 0 };
-    } else {
-      return { activeIdx: activeIdx + 1 };
-    }
-  };
-
-  selectPreviousElement = (state: State, props: PropsWithDefault) => {
-    const { activeIdx } = state;
-    const allElements = this.getAllElements(props, state);
-    if (activeIdx <= 0) {
-      const lastIdx = allElements.length - 1;
-      return { activeIdx: lastIdx };
-    } else {
-      return { activeIdx: activeIdx - 1 };
-    }
-  };
+    !isEmpty(elem) && selectedElements.indexOf(elem) === -1 && elements.indexOf(elem) === -1;
 
   stopLoading = () => {
     if (this.mounted) {
@@ -250,20 +185,10 @@ export default class MultiSelect extends React.PureComponent<PropsWithDefault, S
     }
   };
 
-  toggleSelect = (item: string) => {
-    if (this.props.selectedElements.indexOf(item) === -1) {
-      this.onSelectItem(item);
-    } else {
-      this.onUnselectItem(item);
-    }
-  };
-
   render() {
-    const { legend, allowSelection = true, allowNewElements = true, footerNode = '' } = this.props;
+    const { legend, allowSelection = true, footerNode = '' } = this.props;
     const { renderLabel } = this.props as PropsWithDefault;
-    const { query, activeIdx, selectedElements, unselectedElements } = this.state;
-    const activeElement = this.getAllElements(this.props, this.state)[activeIdx];
-    const showNewElement = allowNewElements && this.isNewElement(query, this.props);
+    const { query, elements } = this.state;
     const infiniteList = this.props.listSize === 0;
     const listClasses = classNames('menu', {
       'menu-vertically-limited': infiniteList,
@@ -273,7 +198,7 @@ export default class MultiSelect extends React.PureComponent<PropsWithDefault, S
     });
 
     return (
-      <div className="multi-select" ref={div => (this.container = div)}>
+      <div className="multi-select">
         <div className="menu-search">
           <SearchBox
             autoFocus={true}
@@ -286,42 +211,16 @@ export default class MultiSelect extends React.PureComponent<PropsWithDefault, S
         </div>
         <fieldset aria-label={legend}>
           <ul className={listClasses}>
-            {selectedElements.length > 0 &&
-              selectedElements.map(element => (
-                <MultiSelectOption
-                  active={activeElement === element}
-                  element={element}
-                  key={element}
-                  onHover={this.handleElementHover}
-                  onSelectChange={this.handleSelectChange}
-                  renderLabel={renderLabel}
-                  selected={true}
-                />
-              ))}
-            {unselectedElements.length > 0 &&
-              unselectedElements.map(element => (
-                <MultiSelectOption
-                  active={activeElement === element}
-                  disabled={!allowSelection}
-                  element={element}
-                  key={element}
-                  onHover={this.handleElementHover}
-                  onSelectChange={this.handleSelectChange}
-                  renderLabel={renderLabel}
-                />
-              ))}
-            {showNewElement && (
+            {elements.map(e => (
               <MultiSelectOption
-                active={activeElement === query}
-                custom={true}
-                element={query}
-                key={query}
-                onHover={this.handleElementHover}
+                element={e}
+                disabled={!allowSelection && !e.selected}
+                key={e.value}
                 onSelectChange={this.handleSelectChange}
                 renderLabel={renderLabel}
               />
-            )}
-            {!showNewElement && selectedElements.length < 1 && unselectedElements.length < 1 && (
+            ))}
+            {isEmpty(elements) && (
               <li className="spacer-left">{translateWithParameters('no_results_for_x', query)}</li>
             )}
           </ul>
index f3da85c78e8ca12edfa1ed545a3bd45d5d5292f2..70063767fd3f3bac11c5650f9f92ff89fd764bc4 100644 (file)
@@ -22,32 +22,38 @@ import * as React from 'react';
 import Checkbox from '../../components/controls/Checkbox';
 import { translate } from '../../helpers/l10n';
 
-export interface MultiSelectOptionProps {
-  active?: boolean;
+export interface Element {
+  value: string;
+  selected: boolean;
   custom?: boolean;
+}
+
+export interface MultiSelectOptionProps {
   disabled?: boolean;
-  element: string;
-  onHover: (element: string) => void;
+  element: Element;
   onSelectChange: (selected: boolean, element: string) => void;
   renderLabel: (element: string) => React.ReactNode;
-  selected?: boolean;
 }
 
 export default function MultiSelectOption(props: MultiSelectOptionProps) {
-  const { active, custom, disabled, element, selected } = props;
-  const onHover = () => props.onHover(element);
+  const { disabled, element } = props;
+  const [active, setActive] = React.useState(false);
   const className = classNames({ active, disabled });
-  const label = props.renderLabel(element);
+  const label = props.renderLabel(element.value);
 
   return (
-    <li onFocus={onHover} onMouseOver={onHover}>
+    <li
+      onFocus={() => setActive(true)}
+      onBlur={() => setActive(false)}
+      onMouseLeave={() => setActive(false)}
+      onMouseOver={() => setActive(true)}>
       <Checkbox
-        checked={Boolean(selected)}
+        checked={element.selected}
         className={className}
         disabled={disabled}
-        id={element}
+        id={element.value}
         onCheck={props.onSelectChange}>
-        {custom ? (
+        {element.custom ? (
           <span
             aria-label={`${translate('create_new_element')}: ${label}`}
             className="little-spacer-left">
index a2e5c946cbdc32aab02549783dc4ec597887cc26..bc8fd239ee012b8d13a14f8bee47c168ac94a034 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 { mount, shallow } from 'enzyme';
+import { render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
 import * as React from 'react';
-import { KeyboardKeys } from '../../../helpers/keycodes';
-import { mockEvent } from '../../../helpers/testUtils';
-import MultiSelect, { MultiSelectProps } from '../MultiSelect';
+import { byRole, byText } from 'testing-library-selector';
+import MultiSelect from '../MultiSelect';
 
-const props = {
-  selectedElements: ['bar'],
-  elements: [],
-  onSearch: () => Promise.resolve(),
-  onSelect: () => {},
-  onUnselect: () => {},
-  renderLabel: (element: string) => element,
-  placeholder: ''
+const ui = {
+  checkbox: (name: string) => byRole('checkbox', { name }),
+  search: byRole('searchbox', { name: 'search_verb' }),
+  noResult: byText('no_results_for_x.notfound')
 };
 
-const elements = ['foo', 'bar', 'baz'];
+it('should handle selection', async () => {
+  const user = userEvent.setup();
+  const rerender = renderMultiSelect();
+  expect(ui.checkbox('az').get()).toBeChecked();
+  await user.click(ui.checkbox('az').get());
+  expect(ui.checkbox('az').get()).not.toBeChecked();
 
-it('should render multiselect with selected elements', () => {
-  const multiselect = shallowRender();
-  // Will not only the selected element
-  expect(multiselect).toMatchSnapshot();
+  await user.type(ui.search.get(), 'create');
+  await user.click(ui.checkbox('create_new_element: create').get());
+  expect(ui.checkbox('create').get()).toBeChecked();
 
-  multiselect.setProps({ elements });
-  expect(multiselect).toMatchSnapshot();
-  multiselect.setState({ activeIdx: 2 });
-  expect(multiselect).toMatchSnapshot();
-  multiselect.setState({ query: 'test' });
-  expect(multiselect).toMatchSnapshot();
+  // Custom label
+  rerender({ renderLabel: label => `prefxed-${label}` });
+  expect(ui.checkbox('prefxed-create').get()).toBeChecked();
 });
 
-it('should render with the focus inside the search input', () => {
-  /*
-   * Need to attach to document body to have it set to `document.activeElement`
-   * See: https://github.com/jsdom/jsdom/issues/2723#issuecomment-580163361
-   */
-  const container = document.createElement('div');
-  document.body.appendChild(container);
-  const multiselect = mount(<MultiSelect legend="multi select" {...props} />, {
-    attachTo: container
-  });
-
-  expect(multiselect.find('input').getDOMNode()).toBe(document.activeElement);
-
-  multiselect.unmount();
-});
-
-it.each([
-  [KeyboardKeys.DownArrow, 1, 1],
-  [KeyboardKeys.UpArrow, 1, 1],
-  [KeyboardKeys.LeftArrow, 1, 0]
-])('should handle keyboard event: %s', (key, stopPropagationCalls, preventDefaultCalls) => {
-  const wrapper = shallowRender();
-
-  const stopPropagation = jest.fn();
-  const preventDefault = jest.fn();
-  const event = mockEvent({ preventDefault, stopPropagation, key });
-
-  wrapper.instance().handleKeyboard(event);
-
-  expect(stopPropagation).toBeCalledTimes(stopPropagationCalls);
-  expect(preventDefault).toBeCalledTimes(preventDefaultCalls);
+it('should handle disable selection', () => {
+  renderMultiSelect({ allowSelection: false });
+  expect(ui.checkbox('az').get()).toBeChecked();
+  expect(ui.checkbox('cx').get()).toHaveAttribute('aria-disabled', 'true');
 });
 
-it('should handle keyboard event: enter', () => {
-  const wrapper = shallowRender();
-
-  wrapper.instance().toggleSelect = jest.fn();
-
-  wrapper.instance().handleKeyboard(mockEvent({ key: KeyboardKeys.Enter }));
-
-  expect(wrapper.instance().toggleSelect).toBeCalled();
+it('should handle search', async () => {
+  const user = userEvent.setup();
+  const rerender = renderMultiSelect();
+  expect(ui.checkbox('cx').get()).toBeInTheDocument();
+  await user.type(ui.search.get(), 'az');
+  expect(ui.checkbox('cx').query()).not.toBeInTheDocument();
+  expect(ui.checkbox('az').get()).toBeInTheDocument();
+  expect(ui.checkbox('az-new').get()).toBeInTheDocument();
+
+  await user.clear(ui.search.get());
+  await user.type(ui.search.get(), 'notfound');
+  expect(ui.checkbox('create_new_element: notfound').get()).toBeInTheDocument();
+
+  rerender({ allowNewElements: false });
+  await user.clear(ui.search.get());
+  await user.type(ui.search.get(), 'notfound');
+  expect(ui.noResult.get()).toBeInTheDocument();
 });
 
-function shallowRender(overrides: Partial<MultiSelectProps> = {}) {
-  return shallow<MultiSelect>(
-    <MultiSelect
-      selectedElements={['bar']}
-      elements={[]}
-      legend="multi select"
-      onSearch={() => Promise.resolve()}
-      onSelect={jest.fn()}
-      onUnselect={jest.fn()}
-      renderLabel={(element: string) => element}
-      placeholder=""
-      {...overrides}
-    />
-  );
+function renderMultiSelect(override?: Partial<MultiSelect['props']>) {
+  const initial = ['cx', 'dw', 'ev', 'fu', 'gt', 'hs'];
+  const initialSelected = ['az', 'by'];
+
+  function Parent(props?: Partial<MultiSelect['props']>) {
+    const [elements, setElements] = React.useState(initial);
+    const [selected, setSelected] = React.useState(['az', 'by']);
+    const onSearch = (query: string) => {
+      if (query === 'notfound') {
+        setElements([]);
+        setSelected([]);
+      } else if (query === '') {
+        setElements(initial);
+        setSelected(initialSelected);
+      } else {
+        setElements([...elements.filter(e => e.indexOf(query) !== -1), `${query}-new`]);
+        setSelected(selected.filter(e => e.indexOf(query) !== -1));
+      }
+      return Promise.resolve();
+    };
+
+    const onSelect = (element: string) => {
+      setSelected([...selected, element]);
+      setElements(elements.filter(e => e !== element));
+    };
+
+    const onUnselect = (element: string) => {
+      setElements([...elements, element]);
+      setSelected(selected.filter(e => e !== element));
+    };
+    return (
+      <MultiSelect
+        selectedElements={selected}
+        elements={elements}
+        legend="multi select"
+        onSearch={onSearch}
+        onSelect={onSelect}
+        onUnselect={onUnselect}
+        placeholder=""
+        {...props}
+      />
+    );
+  }
+
+  const { rerender } = render(<Parent {...override} />);
+  return function(reoverride?: Partial<MultiSelect['props']>) {
+    rerender(<Parent {...override} {...reoverride} />);
+  };
 }
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/MultiSelectOption-test.tsx
deleted file mode 100644 (file)
index 28e09b6..0000000
+++ /dev/null
@@ -1,42 +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 MultiSelectOption, { MultiSelectOptionProps } from '../MultiSelectOption';
-
-it('should render standard element', () => {
-  expect(shallowRender()).toMatchSnapshot('default element');
-  expect(shallowRender({ selected: true })).toMatchSnapshot('selected element');
-  expect(shallowRender({ custom: true })).toMatchSnapshot('custom element');
-  expect(shallowRender({ active: true, selected: true })).toMatchSnapshot('active element');
-  expect(shallowRender({ disabled: true })).toMatchSnapshot('disabled element');
-});
-
-function shallowRender(props: Partial<MultiSelectOptionProps> = {}) {
-  return shallow(
-    <MultiSelectOption
-      element="mytag"
-      onHover={jest.fn()}
-      onSelectChange={jest.fn()}
-      renderLabel={(element: string) => element}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelect-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelect-test.tsx.snap
deleted file mode 100644 (file)
index 2ec8d12..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render multiselect with selected elements 1`] = `
-<div
-  className="multi-select"
->
-  <div
-    className="menu-search"
-  >
-    <SearchBox
-      autoFocus={true}
-      className="little-spacer-top"
-      loading={true}
-      onChange={[Function]}
-      placeholder=""
-      value=""
-    />
-  </div>
-  <fieldset
-    aria-label="multi select"
-  >
-    <ul
-      className="menu menu-vertically-limited spacer-top with-top-separator"
-    >
-      <MultiSelectOption
-        active={true}
-        element="bar"
-        key="bar"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-        selected={true}
-      />
-    </ul>
-  </fieldset>
-</div>
-`;
-
-exports[`should render multiselect with selected elements 2`] = `
-<div
-  className="multi-select"
->
-  <div
-    className="menu-search"
-  >
-    <SearchBox
-      autoFocus={true}
-      className="little-spacer-top"
-      loading={true}
-      onChange={[Function]}
-      placeholder=""
-      value=""
-    />
-  </div>
-  <fieldset
-    aria-label="multi select"
-  >
-    <ul
-      className="menu menu-vertically-limited spacer-top with-top-separator"
-    >
-      <MultiSelectOption
-        active={true}
-        element="bar"
-        key="bar"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-        selected={true}
-      />
-      <MultiSelectOption
-        active={false}
-        disabled={false}
-        element="foo"
-        key="foo"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-      />
-      <MultiSelectOption
-        active={false}
-        disabled={false}
-        element="baz"
-        key="baz"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-      />
-    </ul>
-  </fieldset>
-</div>
-`;
-
-exports[`should render multiselect with selected elements 3`] = `
-<div
-  className="multi-select"
->
-  <div
-    className="menu-search"
-  >
-    <SearchBox
-      autoFocus={true}
-      className="little-spacer-top"
-      loading={true}
-      onChange={[Function]}
-      placeholder=""
-      value=""
-    />
-  </div>
-  <fieldset
-    aria-label="multi select"
-  >
-    <ul
-      className="menu menu-vertically-limited spacer-top with-top-separator"
-    >
-      <MultiSelectOption
-        active={false}
-        element="bar"
-        key="bar"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-        selected={true}
-      />
-      <MultiSelectOption
-        active={false}
-        disabled={false}
-        element="foo"
-        key="foo"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-      />
-      <MultiSelectOption
-        active={true}
-        disabled={false}
-        element="baz"
-        key="baz"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-      />
-    </ul>
-  </fieldset>
-</div>
-`;
-
-exports[`should render multiselect with selected elements 4`] = `
-<div
-  className="multi-select"
->
-  <div
-    className="menu-search"
-  >
-    <SearchBox
-      autoFocus={true}
-      className="little-spacer-top"
-      loading={true}
-      onChange={[Function]}
-      placeholder=""
-      value="test"
-    />
-  </div>
-  <fieldset
-    aria-label="multi select"
-  >
-    <ul
-      className="menu menu-vertically-limited spacer-top with-top-separator"
-    >
-      <MultiSelectOption
-        active={false}
-        element="bar"
-        key="bar"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-        selected={true}
-      />
-      <MultiSelectOption
-        active={false}
-        disabled={false}
-        element="foo"
-        key="foo"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-      />
-      <MultiSelectOption
-        active={true}
-        disabled={false}
-        element="baz"
-        key="baz"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-      />
-      <MultiSelectOption
-        active={false}
-        custom={true}
-        element="test"
-        key="test"
-        onHover={[Function]}
-        onSelectChange={[Function]}
-        renderLabel={[Function]}
-      />
-    </ul>
-  </fieldset>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/MultiSelectOption-test.tsx.snap
deleted file mode 100644 (file)
index ee6ce1b..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render standard element: active element 1`] = `
-<li
-  onFocus={[Function]}
-  onMouseOver={[Function]}
->
-  <Checkbox
-    checked={true}
-    className="active"
-    id="mytag"
-    onCheck={[MockFunction]}
-    thirdState={false}
-  >
-    <span
-      className="little-spacer-left"
-    >
-      mytag
-    </span>
-  </Checkbox>
-</li>
-`;
-
-exports[`should render standard element: custom element 1`] = `
-<li
-  onFocus={[Function]}
-  onMouseOver={[Function]}
->
-  <Checkbox
-    checked={false}
-    className=""
-    id="mytag"
-    onCheck={[MockFunction]}
-    thirdState={false}
-  >
-    <span
-      aria-label="create_new_element: mytag"
-      className="little-spacer-left"
-    >
-      <span
-        aria-hidden={true}
-        className="little-spacer-right"
-      >
-        +
-      </span>
-      mytag
-    </span>
-  </Checkbox>
-</li>
-`;
-
-exports[`should render standard element: default element 1`] = `
-<li
-  onFocus={[Function]}
-  onMouseOver={[Function]}
->
-  <Checkbox
-    checked={false}
-    className=""
-    id="mytag"
-    onCheck={[MockFunction]}
-    thirdState={false}
-  >
-    <span
-      className="little-spacer-left"
-    >
-      mytag
-    </span>
-  </Checkbox>
-</li>
-`;
-
-exports[`should render standard element: disabled element 1`] = `
-<li
-  onFocus={[Function]}
-  onMouseOver={[Function]}
->
-  <Checkbox
-    checked={false}
-    className="disabled"
-    disabled={true}
-    id="mytag"
-    onCheck={[MockFunction]}
-    thirdState={false}
-  >
-    <span
-      className="little-spacer-left"
-    >
-      mytag
-    </span>
-  </Checkbox>
-</li>
-`;
-
-exports[`should render standard element: selected element 1`] = `
-<li
-  onFocus={[Function]}
-  onMouseOver={[Function]}
->
-  <Checkbox
-    checked={true}
-    className=""
-    id="mytag"
-    onCheck={[MockFunction]}
-    thirdState={false}
-  >
-    <span
-      className="little-spacer-left"
-    >
-      mytag
-    </span>
-  </Checkbox>
-</li>
-`;