]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20500 Migrating rules list to new design
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>
Fri, 29 Sep 2023 09:26:46 +0000 (11:26 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 5 Oct 2023 20:02:47 +0000 (20:02 +0000)
13 files changed:
server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsTagsPopup.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleInheritanceIcon.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx
server/sonar-web/src/main/js/apps/coding-rules/utils-tests.tsx
server/sonar-web/src/main/js/components/tags/TagsList.tsx
server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tags/__tests__/TagsSelector-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsList-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.tsx.snap [deleted file]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 637c3b1f08c5499dcea88235892886169aa68684..f4e273180e63164de9abd289403b80e7cb6d7ad1 100644 (file)
@@ -660,7 +660,6 @@ describe('Rule app details', () => {
     await ui.appLoaded();
 
     await user.click(ui.tagsDropdown.get());
-    expect(ui.tagsMenu.get()).toBeInTheDocument();
 
     RULE_TAGS_MOCK.forEach((tag) => {
       expect(ui.tagCheckbox(tag).get()).toBeInTheDocument();
index 1a098454a2dcfb47fb908d986724dba3b2563de4..68fd8b60c8e7425019f4f12cd817c927784a37a4 100644 (file)
@@ -677,6 +677,7 @@ export class CodingRulesApp extends React.PureComponent<Props, State> {
                       loadMore={this.fetchMoreRules}
                       ready={!this.state.loading}
                       total={paging.total}
+                      useMIUIButtons
                     />
                   )}
                 </>
index 5aec7b822a514b997c1abb4aabe117647aaf1cfe..a3345f1ff203732e1a0a24f36480815feeee84c2 100644 (file)
@@ -22,10 +22,8 @@ import * as React from 'react';
 import { colors } from '../../../app/theme';
 import DocumentationTooltip from '../../../components/common/DocumentationTooltip';
 import Link from '../../../components/common/Link';
-import Dropdown from '../../../components/controls/Dropdown';
 import HelpTooltip from '../../../components/controls/HelpTooltip';
 import Tooltip from '../../../components/controls/Tooltip';
-import { ButtonLink } from '../../../components/controls/buttons';
 import IssueTypeIcon from '../../../components/icons/IssueTypeIcon';
 import LinkIcon from '../../../components/icons/LinkIcon';
 import DateFormatter from '../../../components/intl/DateFormatter';
@@ -33,7 +31,6 @@ import { CleanCodeAttributePill } from '../../../components/shared/CleanCodeAttr
 import SeverityHelper from '../../../components/shared/SeverityHelper';
 import SoftwareImpactPill from '../../../components/shared/SoftwareImpactPill';
 import TagsList from '../../../components/tags/TagsList';
-import { PopupPlacement } from '../../../components/ui/popups';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { getRuleUrl } from '../../../helpers/urls';
 import { Dict, RuleDetails } from '../../../types/types';
@@ -120,33 +117,19 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
 
     return (
       <div className="coding-rules-detail-property null-spacer-bottom" data-meta="tags">
-        {this.props.canWrite ? (
-          <Dropdown
-            closeOnClick={false}
-            closeOnClickOutside
-            overlay={
+        <TagsList
+          allowUpdate={canWrite}
+          tags={allTags.length > 0 ? allTags : [translate('coding_rules.no_tags')]}
+          overlay={
+            canWrite ? (
               <RuleDetailsTagsPopup
                 setTags={this.props.onTagsChange}
                 sysTags={sysTags}
                 tags={tags}
               />
-            }
-            overlayPlacement={PopupPlacement.BottomRight}
-          >
-            <ButtonLink>
-              <TagsList
-                allowUpdate={canWrite}
-                tags={allTags.length > 0 ? allTags : [translate('coding_rules.no_tags')]}
-              />
-            </ButtonLink>
-          </Dropdown>
-        ) : (
-          <TagsList
-            allowUpdate={canWrite}
-            className="display-flex-center"
-            tags={allTags.length > 0 ? allTags : [translate('coding_rules.no_tags')]}
-          />
-        )}
+            ) : undefined
+          }
+        />
       </div>
     );
   };
index 78fa36f593fcb19cd3619341339365357b3f913c..38d14f6d623bd23eb84dba785bbe70fdd4c19496 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 { TagsSelector } from 'design-system';
 import { difference, uniq, without } from 'lodash';
 import * as React from 'react';
 import { getRuleTags } from '../../../api/rules';
-import TagsSelector from '../../../components/tags/TagsSelector';
+import { translate } from '../../../helpers/l10n';
 
 export interface Props {
   setTags: (tags: string[]) => void;
@@ -73,7 +75,10 @@ export default class RuleDetailsTagsPopup extends React.PureComponent<Props, Sta
     const availableTags = difference(this.state.searchResult, this.props.tags);
     return (
       <TagsSelector
-        listSize={LIST_SIZE}
+        createElementLabel={translate('coding_rules.create_tag')}
+        headerLabel={translate('tags')}
+        searchInputAriaLabel={translate('search.search_for_tags')}
+        noResultsLabel={translate('no_results')}
         onSearch={this.onSearch}
         onSelect={this.onSelect}
         onUnselect={this.onUnselect}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleInheritanceIcon.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleInheritanceIcon.tsx
deleted file mode 100644 (file)
index 1944c82..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 * as React from 'react';
-import { colors } from '../../../app/theme';
-import { RuleInheritance } from '../../../types/types';
-
-interface Props {
-  className?: string;
-  inheritance: RuleInheritance;
-}
-
-export default function RuleInheritanceIcon({ className, inheritance, ...other }: Props) {
-  const fill = inheritance === 'OVERRIDES' ? colors.red : colors.baseFontColor;
-
-  return (
-    <svg
-      className={className}
-      height={16}
-      version="1.1"
-      viewBox="0 0 16 16"
-      width={16}
-      xmlSpace="preserve"
-      xmlnsXlink="https://www.w3.org/1999/xlink"
-      {...other}
-    >
-      <path
-        d="M6.25 12.5a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0zm0-9a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0zm5 1a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0zm.75 0a1.5 1.5 0 0 1-.75 1.297c-.023 2.82-2.023 3.445-3.352 3.867-1.242.39-1.648.578-1.648 1.336v.203A1.5 1.5 0 1 1 4 12.5a1.5 1.5 0 0 1 .75-1.297V4.797A1.5 1.5 0 1 1 7 3.5a1.5 1.5 0 0 1-.75 1.297V8.68c.398-.196.82-.328 1.203-.446 1.453-.46 2.281-.804 2.297-2.437A1.5 1.5 0 1 1 12 4.5z"
-        style={{ fill, fillRule: 'nonzero' }}
-      />
-    </svg>
-  );
-}
index 885fe4a9267ed08815f2eeb8c75e41be11b32d6b..753585d33829690bc94f2c6d978e75702b84cb31 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 classNames from 'classnames';
+
+import styled from '@emotion/styled';
+import {
+  Badge,
+  DangerButtonSecondary,
+  InheritanceIcon,
+  Link,
+  Note,
+  themeBorder,
+} from 'design-system';
 import * as React from 'react';
-import { deactivateRule, Profile } from '../../../api/quality-profiles';
-import Link from '../../../components/common/Link';
-import { Button } from '../../../components/controls/buttons';
+import { Profile, deactivateRule } from '../../../api/quality-profiles';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
 import Tooltip from '../../../components/controls/Tooltip';
 import SeverityIcon from '../../../components/icons/SeverityIcon';
@@ -33,7 +40,6 @@ import { getRuleUrl } from '../../../helpers/urls';
 import { Rule } from '../../../types/types';
 import { Activation } from '../query';
 import ActivationButton from './ActivationButton';
-import RuleInheritanceIcon from './RuleInheritanceIcon';
 
 interface Props {
   activation?: Activation;
@@ -90,7 +96,7 @@ export default class RuleListItem extends React.PureComponent<Props> {
     }
 
     return (
-      <td className="coding-rule-table-meta-cell coding-rule-activation">
+      <div className="sw-mr-2">
         <SeverityIcon severity={activation.severity} />
         {selectedProfile && selectedProfile.parentName && (
           <>
@@ -102,10 +108,7 @@ export default class RuleListItem extends React.PureComponent<Props> {
                   selectedProfile.parentName,
                 )}
               >
-                <RuleInheritanceIcon
-                  className="little-spacer-left"
-                  inheritance={activation.inherit}
-                />
+                <InheritanceIcon className="sw-ml-1" fill="destructiveIconFocus" />
               </Tooltip>
             )}
             {activation.inherit === 'INHERITED' && (
@@ -116,15 +119,12 @@ export default class RuleListItem extends React.PureComponent<Props> {
                   selectedProfile.parentName,
                 )}
               >
-                <RuleInheritanceIcon
-                  className="little-spacer-left"
-                  inheritance={activation.inherit}
-                />
+                <InheritanceIcon className="sw-ml-1" fill="currentColor" />
               </Tooltip>
             )}
           </>
         )}
-      </td>
+      </div>
     );
   };
 
@@ -138,13 +138,13 @@ export default class RuleListItem extends React.PureComponent<Props> {
     const canCopy = selectedProfile.actions?.copy;
     if (selectedProfile.isBuiltIn && canCopy) {
       return (
-        <td className="coding-rule-table-meta-cell coding-rule-activation-actions">
+        <div className="sw-ml-4">
           <Tooltip overlay={translate('coding_rules.need_extend_or_copy')}>
-            <Button className="coding-rules-detail-quality-profile-deactivate button-red disabled">
+            <DangerButtonSecondary disabled>
               {translate('coding_rules', activation ? 'deactivate' : 'activate')}
-            </Button>
+            </DangerButtonSecondary>
           </Tooltip>
-        </td>
+        </div>
       );
     }
 
@@ -155,7 +155,7 @@ export default class RuleListItem extends React.PureComponent<Props> {
 
     if (activation) {
       return (
-        <td className="coding-rule-table-meta-cell coding-rule-activation-actions">
+        <div className="sw-ml-4">
           {activation.inherit === 'NONE' || canDeactivateInherited ? (
             <ConfirmButton
               confirmButtonText={translate('yes')}
@@ -164,41 +164,34 @@ export default class RuleListItem extends React.PureComponent<Props> {
               onConfirm={this.handleDeactivate}
             >
               {({ onClick }) => (
-                <Button
-                  className="coding-rules-detail-quality-profile-deactivate button-red"
-                  onClick={onClick}
-                >
+                <DangerButtonSecondary onClick={onClick}>
                   {translate('coding_rules.deactivate')}
-                </Button>
+                </DangerButtonSecondary>
               )}
             </ConfirmButton>
           ) : (
             <Tooltip overlay={translate('coding_rules.can_not_deactivate')}>
-              <Button
-                className="coding-rules-detail-quality-profile-deactivate button-red"
-                disabled
-              >
+              <DangerButtonSecondary disabled>
                 {translate('coding_rules.deactivate')}
-              </Button>
+              </DangerButtonSecondary>
             </Tooltip>
           )}
-        </td>
+        </div>
       );
     }
 
     return (
-      <td className="coding-rule-table-meta-cell coding-rule-activation-actions">
+      <div className="sw-ml-4">
         {!rule.isTemplate && (
           <ActivationButton
             buttonText={translate('coding_rules.activate')}
-            className="coding-rules-detail-quality-profile-activate"
             modalHeader={translate('coding_rules.activate_in_quality_profile')}
             onDone={this.handleActivate}
             profiles={[selectedProfile]}
             rule={rule}
           />
         )}
-      </td>
+      </div>
     );
   };
 
@@ -206,75 +199,72 @@ export default class RuleListItem extends React.PureComponent<Props> {
     const { rule, selected } = this.props;
     const allTags = [...(rule.tags || []), ...(rule.sysTags || [])];
     return (
-      <li
-        className={classNames('coding-rule', { selected })}
+      <ListItemStyled
+        selected={selected}
+        className="it__coding-rule sw-p-3 sw-mb-4 sw-rounded-1"
         aria-current={selected}
         data-rule={rule.key}
       >
-        <table className="coding-rule-table">
-          <tbody>
-            <tr>
+        <div className="sw-flex sw-flex-col">
+          <div className="sw-mb-4">
+            {rule.cleanCodeAttributeCategory !== undefined && (
+              <CleanCodeAttributePill
+                cleanCodeAttributeCategory={rule.cleanCodeAttributeCategory}
+                type="rule"
+              />
+            )}
+          </div>
+          <div className="sw-flex sw-justify-between sw-items-center">
+            <div className="sw-flex sw-items-center">
               {this.renderActivation()}
-              <td>
-                <div className="coding-rule-title">
-                  <Link
-                    className="link-no-underline"
-                    onClick={this.handleNameClick}
-                    to={getRuleUrl(rule.key)}
-                  >
-                    {rule.name}
-                  </Link>
-                  {rule.isTemplate && (
-                    <Tooltip overlay={translate('coding_rules.rule_template.title')}>
-                      <span className="badge spacer-left">
-                        {translate('coding_rules.rule_template')}
-                      </span>
-                    </Tooltip>
-                  )}
-                </div>
-              </td>
-
-              <td className="coding-rule-table-meta-cell">
-                <div className="display-flex-center coding-rule-meta">
-                  {rule.cleanCodeAttributeCategory !== undefined && (
-                    <CleanCodeAttributePill
-                      className="spacer-left"
-                      cleanCodeAttributeCategory={rule.cleanCodeAttributeCategory}
-                      type="rule"
-                    />
-                  )}
-                  {rule.status !== 'READY' && (
-                    <span className="spacer-left badge badge-error">
-                      {translate('rules.status', rule.status)}
-                    </span>
-                  )}
-                  <span className="display-inline-flex-center spacer-left note">
-                    {rule.langName}
+              <div>
+                <Link
+                  className="sw-body-sm-highlight"
+                  onClick={this.handleNameClick}
+                  to={getRuleUrl(rule.key)}
+                >
+                  {rule.name}
+                </Link>
+              </div>
+              {rule.isTemplate && (
+                <Tooltip overlay={translate('coding_rules.rule_template.title')}>
+                  <span>
+                    <Badge className="sw-ml-2">{translate('coding_rules.rule_template')}</Badge>
                   </span>
-                  {rule.impacts.map(({ severity, softwareQuality }) => (
-                    <SoftwareImpactPill
-                      className="spacer-left"
-                      key={softwareQuality}
-                      severity={severity}
-                      quality={softwareQuality}
-                      type="rule"
-                    />
-                  ))}
-                  {allTags.length > 0 && (
-                    <TagsList
-                      allowUpdate={false}
-                      className="display-inline-flex-center note spacer-left"
-                      tags={allTags}
-                    />
-                  )}
-                </div>
-              </td>
-
+                </Tooltip>
+              )}
+              {rule.status !== 'READY' && (
+                <Badge variant="deleted" className="sw-ml-2">
+                  {translate('rules.status', rule.status)}
+                </Badge>
+              )}
+            </div>
+            <div className="sw-flex sw-items-center sw-ml-2">
+              <Note>{rule.langName}</Note>
+              {rule.impacts.map(({ severity, softwareQuality }) => (
+                <SoftwareImpactPill
+                  className="sw-ml-3"
+                  key={softwareQuality}
+                  severity={severity}
+                  quality={softwareQuality}
+                  type="rule"
+                />
+              ))}
+              {allTags.length > 0 && (
+                <TagsList allowUpdate={false} className="sw-ml-3" tags={allTags} />
+              )}
               {this.renderActions()}
-            </tr>
-          </tbody>
-        </table>
-      </li>
+            </div>
+          </div>
+        </div>
+      </ListItemStyled>
     );
   }
 }
+
+const ListItemStyled = styled.li<{ selected: boolean }>`
+  border: ${(props) =>
+    props.selected ? themeBorder('default', 'primary') : themeBorder('default', 'almCardBorder')};
+
+  border-width: ${(props) => (props.selected ? '2px' : '1px')};
+`;
index 2bbe2a9f97ec0819a620807fca9b968db0b4ae97..aae6b059dc4d9fd75e013525bf09ef339a880d21 100644 (file)
@@ -116,8 +116,7 @@ const selectors = {
   ruleSoftwareQuality: (quality: SoftwareQuality) => byText(`issue.software_quality.${quality}`),
 
   // Rule tags
-  tagsDropdown: byRole('button', { name: /tags_list_x/ }),
-  tagsMenu: byRole('group', { name: 'select_tags' }),
+  tagsDropdown: byLabelText(/tags_list_x/).byRole('button'),
   tagCheckbox: (tag: string) => byRole('checkbox', { name: tag }),
   tagSearch: byRole('searchbox', { name: 'search.search_for_tags' }),
 
index 8bd5259552ec13d5d8dfa7edf737470f5b626151..07318b147fd238e6eab69b957a39a771c9e74176 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 classNames from 'classnames';
+
+import { PopupPlacement, Tags, Tooltip } from 'design-system';
 import * as React from 'react';
-import DropdownIcon from '../../components/icons/DropdownIcon';
-import TagsIcon from '../../components/icons/TagsIcon';
-import { translateWithParameters } from '../../helpers/l10n';
+import { translate, translateWithParameters } from '../../helpers/l10n';
 import './TagsList.css';
 
 interface Props {
   allowUpdate?: boolean;
   className?: string;
   tags: string[];
+  overlay?: React.ReactNode;
 }
 
-export default function TagsList({ allowUpdate = false, className, tags }: Props) {
+const TAGS_TO_DISPLAY = 2;
+
+export default function TagsList({
+  allowUpdate = false,
+  className,
+  tags,
+  overlay,
+}: Readonly<Props>) {
+  const [open, setOpen] = React.useState(false);
+
   return (
-    <span
-      aria-label={translateWithParameters('tags_list_x', tags.join(', '))}
-      role="note"
-      className={classNames('tags-list', className)}
-    >
-      <TagsIcon className="text-middle" />
-      <span aria-hidden className="text-ellipsis text-middle" title={tags.join(', ')}>
-        {tags.join(', ')}
-      </span>
-      {allowUpdate && <DropdownIcon className="text-middle" />}
-    </span>
+    <Tags
+      allowUpdate={allowUpdate}
+      ariaTagsListLabel={translateWithParameters('tags_list_x', tags.join(', '))}
+      className={className}
+      emptyText={translate('no_tags')}
+      menuId="rule-tags-menu"
+      onClose={() => setOpen(false)}
+      open={open}
+      overlay={overlay}
+      popupPlacement={PopupPlacement.Bottom}
+      tags={tags}
+      tagsToDisplay={TAGS_TO_DISPLAY}
+      tooltip={Tooltip}
+    />
   );
 }
diff --git a/server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.tsx b/server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.tsx
deleted file mode 100644 (file)
index 8b00976..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 { shallow } from 'enzyme';
-import * as React from 'react';
-import TagsList from '../TagsList';
-
-const tags = ['foo', 'bar'];
-
-it('should render with a list of tag', () => {
-  expect(shallow(<TagsList tags={tags} />)).toMatchSnapshot();
-});
-
-it('should render with a caret on the right if update is allowed', () => {
-  expect(shallow(<TagsList allowUpdate tags={tags} />)).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/components/tags/__tests__/TagsSelector-test.tsx b/server/sonar-web/src/main/js/components/tags/__tests__/TagsSelector-test.tsx
deleted file mode 100644 (file)
index 0869526..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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 { shallow } from 'enzyme';
-import * as React from 'react';
-import TagsSelector, { validateTag } from '../TagsSelector';
-
-const props = {
-  listSize: 10,
-  onSearch: () => Promise.resolve(),
-  onSelect: () => {},
-  onUnselect: () => {},
-  renderLabel: (element: string) => element,
-  position: { right: 0, top: 0 },
-  selectedTags: ['bar'],
-  tags: ['foo', 'bar', 'baz'],
-};
-
-it('should render with selected tags', () => {
-  const tagsSelector = shallow(<TagsSelector {...props} />);
-  expect(tagsSelector).toMatchSnapshot();
-});
-
-it('should render without tags at all', () => {
-  expect(shallow(<TagsSelector {...props} selectedTags={[]} tags={[]} />)).toMatchSnapshot();
-});
-
-it('should validate tags correctly', () => {
-  const validChars = 'abcdefghijklmnopqrstuvwxyz0123456789+-#.';
-  expect(validateTag('test')).toBe('test');
-  expect(validateTag(validChars)).toBe(validChars);
-  expect(validateTag(validChars.toUpperCase())).toBe(validChars);
-  expect(validateTag('T E$ST')).toBe('test');
-  expect(validateTag('T E$st!^àéèing1')).toBe('testing1');
-});
diff --git a/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsList-test.tsx.snap b/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsList-test.tsx.snap
deleted file mode 100644 (file)
index be6193f..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render with a caret on the right if update is allowed 1`] = `
-<span
-  aria-label="tags_list_x.foo, bar"
-  className="tags-list"
-  role="note"
->
-  <TagsIcon
-    className="text-middle"
-  />
-  <span
-    aria-hidden={true}
-    className="text-ellipsis text-middle"
-    title="foo, bar"
-  >
-    foo, bar
-  </span>
-  <DropdownIcon
-    className="text-middle"
-  />
-</span>
-`;
-
-exports[`should render with a list of tag 1`] = `
-<span
-  aria-label="tags_list_x.foo, bar"
-  className="tags-list"
-  role="note"
->
-  <TagsIcon
-    className="text-middle"
-  />
-  <span
-    aria-hidden={true}
-    className="text-ellipsis text-middle"
-    title="foo, bar"
-  >
-    foo, bar
-  </span>
-</span>
-`;
diff --git a/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.tsx.snap b/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.tsx.snap
deleted file mode 100644 (file)
index fa38cfa..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render with selected tags 1`] = `
-<MultiSelect
-  elements={
-    [
-      "foo",
-      "bar",
-      "baz",
-    ]
-  }
-  filterSelected={[Function]}
-  legend="select_tags"
-  listSize={10}
-  onSearch={[Function]}
-  onSelect={[Function]}
-  onUnselect={[Function]}
-  placeholder="search.search_for_tags"
-  renderLabel={[Function]}
-  selectedElements={
-    [
-      "bar",
-    ]
-  }
-  validateSearchInput={[Function]}
-/>
-`;
-
-exports[`should render without tags at all 1`] = `
-<MultiSelect
-  elements={[]}
-  filterSelected={[Function]}
-  legend="select_tags"
-  listSize={10}
-  onSearch={[Function]}
-  onSelect={[Function]}
-  onUnselect={[Function]}
-  placeholder="search.search_for_tags"
-  renderLabel={[Function]}
-  selectedElements={[]}
-  validateSearchInput={[Function]}
-/>
-`;
index 5ae816cf09d4e93495143e8747c0bfe84b855f5f..aacfb2952c8b63937ab7663bec292958b1a8e434 100644 (file)
@@ -2405,6 +2405,7 @@ coding_rules.more_info.notification_message=We've added new information about Cl
 coding_rules.more_info.scroll_message=Scroll down to Code Quality principles
 
 coding_rules.detail.extend_description.form=Extend this rule's description
+coding_rules.create_tag=Create Tag
 
 rule.impact.severity.tooltip=Issues found for this rule will have a {severity} impact on the {quality} of your software.