From 490bcf7bdc0b2e14d3b33ff3cfd06acd6850236d Mon Sep 17 00:00:00 2001 From: Revanshu Paliwal Date: Fri, 29 Sep 2023 11:26:46 +0200 Subject: [PATCH] SONAR-20500 Migrating rules list to new design --- .../coding-rules/__tests__/CodingRules-it.ts | 1 - .../components/CodingRulesApp.tsx | 1 + .../components/RuleDetailsMeta.tsx | 33 +--- .../components/RuleDetailsTagsPopup.tsx | 9 +- .../components/RuleInheritanceIcon.tsx | 49 ----- .../coding-rules/components/RuleListItem.tsx | 184 +++++++++--------- .../main/js/apps/coding-rules/utils-tests.tsx | 3 +- .../src/main/js/components/tags/TagsList.tsx | 44 +++-- .../tags/__tests__/TagsList-test.tsx | 32 --- .../tags/__tests__/TagsSelector-test.tsx | 51 ----- .../__snapshots__/TagsList-test.tsx.snap | 42 ---- .../__snapshots__/TagsSelector-test.tsx.snap | 43 ---- .../resources/org/sonar/l10n/core.properties | 1 + 13 files changed, 133 insertions(+), 360 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/coding-rules/components/RuleInheritanceIcon.tsx delete mode 100644 server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.tsx delete mode 100644 server/sonar-web/src/main/js/components/tags/__tests__/TagsSelector-test.tsx delete mode 100644 server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsList-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts index 637c3b1f08c..f4e273180e6 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts +++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts @@ -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(); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx index 1a098454a2d..68fd8b60c8e 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx @@ -677,6 +677,7 @@ export class CodingRulesApp extends React.PureComponent { loadMore={this.fetchMoreRules} ready={!this.state.loading} total={paging.total} + useMIUIButtons /> )} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx index 5aec7b822a5..a3345f1ff20 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx @@ -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 { return (
- {this.props.canWrite ? ( - 0 ? allTags : [translate('coding_rules.no_tags')]} + overlay={ + canWrite ? ( - } - overlayPlacement={PopupPlacement.BottomRight} - > - - 0 ? allTags : [translate('coding_rules.no_tags')]} - /> - - - ) : ( - 0 ? allTags : [translate('coding_rules.no_tags')]} - /> - )} + ) : undefined + } + />
); }; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsTagsPopup.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsTagsPopup.tsx index 78fa36f593f..38d14f6d623 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsTagsPopup.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsTagsPopup.tsx @@ -17,10 +17,12 @@ * 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 - - - ); -} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx index 885fe4a9267..753585d3382 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx @@ -17,11 +17,18 @@ * 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 { } return ( - +
{selectedProfile && selectedProfile.parentName && ( <> @@ -102,10 +108,7 @@ export default class RuleListItem extends React.PureComponent { selectedProfile.parentName, )} > - + )} {activation.inherit === 'INHERITED' && ( @@ -116,15 +119,12 @@ export default class RuleListItem extends React.PureComponent { selectedProfile.parentName, )} > - + )} )} - +
); }; @@ -138,13 +138,13 @@ export default class RuleListItem extends React.PureComponent { const canCopy = selectedProfile.actions?.copy; if (selectedProfile.isBuiltIn && canCopy) { return ( - +
- + - +
); } @@ -155,7 +155,7 @@ export default class RuleListItem extends React.PureComponent { if (activation) { return ( - +
{activation.inherit === 'NONE' || canDeactivateInherited ? ( { onConfirm={this.handleDeactivate} > {({ onClick }) => ( - + )} ) : ( - + )} - +
); } return ( - +
{!rule.isTemplate && ( )} - +
); }; @@ -206,75 +199,72 @@ export default class RuleListItem extends React.PureComponent { const { rule, selected } = this.props; const allTags = [...(rule.tags || []), ...(rule.sysTags || [])]; return ( -
  • - - - +
    +
    + {rule.cleanCodeAttributeCategory !== undefined && ( + + )} +
    +
    +
    {this.renderActivation()} -
    - - - + + )} + {rule.status !== 'READY' && ( + + {translate('rules.status', rule.status)} + + )} + +
    + {rule.langName} + {rule.impacts.map(({ severity, softwareQuality }) => ( + + ))} + {allTags.length > 0 && ( + + )} {this.renderActions()} -
    - -
    -
    - - {rule.name} - - {rule.isTemplate && ( - - - {translate('coding_rules.rule_template')} - - - )} -
    -
    -
    - {rule.cleanCodeAttributeCategory !== undefined && ( - - )} - {rule.status !== 'READY' && ( - - {translate('rules.status', rule.status)} - - )} - - {rule.langName} +
    + + {rule.name} + +
    + {rule.isTemplate && ( + + + {translate('coding_rules.rule_template')} - {rule.impacts.map(({ severity, softwareQuality }) => ( - - ))} - {allTags.length > 0 && ( - - )} -
    -
    -
  • + + + + ); } } + +const ListItemStyled = styled.li<{ selected: boolean }>` + border: ${(props) => + props.selected ? themeBorder('default', 'primary') : themeBorder('default', 'almCardBorder')}; + + border-width: ${(props) => (props.selected ? '2px' : '1px')}; +`; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/utils-tests.tsx b/server/sonar-web/src/main/js/apps/coding-rules/utils-tests.tsx index 2bbe2a9f97e..aae6b059dc4 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/utils-tests.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/utils-tests.tsx @@ -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' }), diff --git a/server/sonar-web/src/main/js/components/tags/TagsList.tsx b/server/sonar-web/src/main/js/components/tags/TagsList.tsx index 8bd5259552e..07318b147fd 100644 --- a/server/sonar-web/src/main/js/components/tags/TagsList.tsx +++ b/server/sonar-web/src/main/js/components/tags/TagsList.tsx @@ -17,31 +17,43 @@ * 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) { + const [open, setOpen] = React.useState(false); + return ( - - - - {tags.join(', ')} - - {allowUpdate && } - + 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 index 8b009764b1c..00000000000 --- a/server/sonar-web/src/main/js/components/tags/__tests__/TagsList-test.tsx +++ /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()).toMatchSnapshot(); -}); - -it('should render with a caret on the right if update is allowed', () => { - expect(shallow()).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 index 08695265c68..00000000000 --- a/server/sonar-web/src/main/js/components/tags/__tests__/TagsSelector-test.tsx +++ /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(); - expect(tagsSelector).toMatchSnapshot(); -}); - -it('should render without tags at all', () => { - expect(shallow()).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 index be6193f7834..00000000000 --- a/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsList-test.tsx.snap +++ /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`] = ` - - - - foo, bar - - - -`; - -exports[`should render with a list of tag 1`] = ` - - - - foo, bar - - -`; 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 index fa38cfaa42d..00000000000 --- a/server/sonar-web/src/main/js/components/tags/__tests__/__snapshots__/TagsSelector-test.tsx.snap +++ /dev/null @@ -1,43 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render with selected tags 1`] = ` - -`; - -exports[`should render without tags at all 1`] = ` - -`; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 5ae816cf09d..aacfb2952c8 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -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. -- 2.39.5