From b944a031eaaf02ea9d2250851a6c88fab6c6f30b Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Mon, 25 Sep 2023 10:12:18 +0200 Subject: [PATCH] SONAR-20500 Migrate Rule Details to the new UI --- server/sonar-web/design-system/package.json | 9 +- .../src/components/CodeSyntaxHighlighter.tsx | 9 +- .../src/components/icons/InheritanceIcon.tsx | 58 ++++++ .../src/components/icons/index.ts | 1 + .../components/ActivationButton.tsx | 6 +- .../RemoveExtendedDescriptionModal.tsx | 41 ++--- .../coding-rules/components/RuleDetails.tsx | 9 + .../components/RuleDetailsCustomRules.tsx | 100 ++++++---- .../components/RuleDetailsDescription.tsx | 149 +++++++-------- .../components/RuleDetailsIssues.tsx | 42 +++-- .../components/RuleDetailsParameters.tsx | 31 ++-- .../components/RuleDetailsProfiles.tsx | 173 +++++++++++------- .../main/js/apps/coding-rules/utils-tests.tsx | 2 +- .../js/components/rules/RuleTabViewer.tsx | 114 +++++------- server/sonar-web/yarn.lock | 18 ++ .../resources/org/sonar/l10n/core.properties | 4 +- 16 files changed, 446 insertions(+), 320 deletions(-) create mode 100644 server/sonar-web/design-system/src/components/icons/InheritanceIcon.tsx diff --git a/server/sonar-web/design-system/package.json b/server/sonar-web/design-system/package.json index c9fd9bdaab8..3b34f44e7c2 100644 --- a/server/sonar-web/design-system/package.json +++ b/server/sonar-web/design-system/package.json @@ -39,9 +39,6 @@ "eslint-plugin-import": "2.28.1", "eslint-plugin-local-rules": "2.0.0", "eslint-plugin-typescript-sort-keys": "2.3.0", - "highlight.js": "11.8.0", - "highlightjs-apex": "1.2.0", - "highlightjs-sap-abap": "0.3.0", "history": "5.3.0", "jest": "29.6.4", "postcss": "8.4.29", @@ -85,5 +82,11 @@ "config": "../tailwind.config.js", "preset": "emotion" } + }, + "dependencies": { + "highlight.js": "11.8.0", + "highlightjs-apex": "1.2.0", + "highlightjs-cobol": "0.3.3", + "highlightjs-sap-abap": "0.3.0" } } diff --git a/server/sonar-web/design-system/src/components/CodeSyntaxHighlighter.tsx b/server/sonar-web/design-system/src/components/CodeSyntaxHighlighter.tsx index f6fda7072a1..bf2ca3d5ced 100644 --- a/server/sonar-web/design-system/src/components/CodeSyntaxHighlighter.tsx +++ b/server/sonar-web/design-system/src/components/CodeSyntaxHighlighter.tsx @@ -22,12 +22,14 @@ import styled from '@emotion/styled'; import classNames from 'classnames'; import hljs, { HighlightResult } from 'highlight.js'; import apex from 'highlightjs-apex'; +import cobol from 'highlightjs-cobol'; import abap from 'highlightjs-sap-abap'; import tw from 'twin.macro'; import { themeColor, themeContrast } from '../helpers/theme'; hljs.registerLanguage('abap', abap); hljs.registerLanguage('apex', apex); +hljs.registerLanguage('cobol', cobol); hljs.registerAliases('azureresourcemanager', { languageName: 'json' }); hljs.registerAliases('flex', { languageName: 'actionscript' }); @@ -67,9 +69,12 @@ export function CodeSyntaxHighlighter(props: Props) { let highlightedCode: HighlightResult; try { + const actualLanguage = + language !== undefined && hljs.getLanguage(language) ? language : 'plaintext'; + highlightedCode = hljs.highlight(unescapedCode, { ignoreIllegals: true, - language: language ?? 'plaintext', + language: actualLanguage, }); } catch { highlightedCode = hljs.highlight(unescapedCode, { @@ -82,7 +87,7 @@ export function CodeSyntaxHighlighter(props: Props) { codeBlock, // Use a function to avoid triggering special replacement patterns // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_the_replacement - () => `<${tag}${attributes}>${highlightedCode.value}` + () => `<${tag}${attributes}>${highlightedCode.value}`, ); }); diff --git a/server/sonar-web/design-system/src/components/icons/InheritanceIcon.tsx b/server/sonar-web/design-system/src/components/icons/InheritanceIcon.tsx new file mode 100644 index 00000000000..3c32a4066a7 --- /dev/null +++ b/server/sonar-web/design-system/src/components/icons/InheritanceIcon.tsx @@ -0,0 +1,58 @@ +/* + * 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 { useTheme } from '@emotion/react'; +import { themeColor } from '../../helpers/theme'; +import { CustomIcon, IconProps } from './Icon'; + +export function InheritanceIcon({ fill = 'currentColor', ...iconProps }: Readonly) { + const theme = useTheme(); + const fillColor = themeColor(fill)({ theme }); + return ( + + + + + + + + + + + + ); +} diff --git a/server/sonar-web/design-system/src/components/icons/index.ts b/server/sonar-web/design-system/src/components/icons/index.ts index 9cbd5c26118..5bb8abda23c 100644 --- a/server/sonar-web/design-system/src/components/icons/index.ts +++ b/server/sonar-web/design-system/src/components/icons/index.ts @@ -43,6 +43,7 @@ export { HelperHintIcon } from './HelperHintIcon'; export { HomeFillIcon } from './HomeFillIcon'; export { HomeIcon } from './HomeIcon'; export * from './Icon'; +export { InheritanceIcon } from './InheritanceIcon'; export { IssueLocationIcon } from './IssueLocationIcon'; export { LinkIcon } from './LinkIcon'; export { LockIcon } from './LockIcon'; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx index 6315b33f5b1..24aa404d821 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx @@ -17,9 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { ButtonSecondary } from 'design-system'; import * as React from 'react'; import { Profile as BaseProfile } from '../../../api/quality-profiles'; -import { Button } from '../../../components/controls/buttons'; import { Rule, RuleActivation, RuleDetails } from '../../../types/types'; import ActivationFormModal from './ActivationFormModal'; @@ -40,14 +40,14 @@ export default function ActivationButton(props: Props) { return ( <> - + {modalOpen && ( - {({ onCloseClick, onFormSubmit, submitting }) => ( -
-
-

{header}

-
-
- {translate('coding_rules.remove_extended_description.confirm')} -
+ const handleClick = React.useCallback(() => { + setSubmitting(true); + onSubmit(); + }, [onSubmit]); -
- {submitting && } - - {translate('remove')} - - {translate('cancel')} -
- - )} - + return ( + + {translate('remove')} + + } + loading={submitting} + secondaryButtonLabel={translate('cancel')} + /> ); } diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx index eef92663a2d..feafa53b4da 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx @@ -17,12 +17,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { SubHeadingHighlight } from 'design-system/lib'; import * as React from 'react'; import { Profile } from '../../../api/quality-profiles'; import { deleteRule, getRuleDetails, updateRule } from '../../../api/rules'; import ConfirmButton from '../../../components/controls/ConfirmButton'; import HelpTooltip from '../../../components/controls/HelpTooltip'; import { Button } from '../../../components/controls/buttons'; +import DateFormatter from '../../../components/intl/DateFormatter'; import Spinner from '../../../components/ui/Spinner'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { Dict, RuleActivation, RuleDetails as TypeRuleDetails } from '../../../types/types'; @@ -252,6 +254,13 @@ export default class RuleDetails extends React.PureComponent { {!ruleDetails.isTemplate && ruleDetails.type !== 'SECURITY_HOTSPOT' && ( )} + +
+ + {translate('coding_rules.available_since')} + + +
); diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx index df822427393..d104dd1514b 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx @@ -17,16 +17,25 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { + ButtonSecondary, + ContentCell, + DangerButtonSecondary, + HeadingDark, + Link, + Spinner, + Table, + TableRow, + UnorderedList, +} from 'design-system'; import { sortBy } from 'lodash'; import * as React from 'react'; import { deleteRule, searchRules } from '../../../api/rules'; -import Link from '../../../components/common/Link'; import ConfirmButton from '../../../components/controls/ConfirmButton'; -import { Button } from '../../../components/controls/buttons'; -import SeverityHelper from '../../../components/shared/SeverityHelper'; -import Spinner from '../../../components/ui/Spinner'; +import IssueSeverityIcon from '../../../components/icon-mappers/IssueSeverityIcon'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { getRuleUrl } from '../../../helpers/urls'; +import { IssueSeverity } from '../../../types/issues'; import { Rule, RuleDetails } from '../../../types/types'; import CustomRuleButton from './CustomRuleButton'; @@ -40,6 +49,9 @@ interface State { rules?: Rule[]; } +const COLUMN_COUNT = 3; +const COLUMN_COUNT_WITH_EDIT_PERMISSIONS = 4; + export default class RuleDetailsCustomRules extends React.PureComponent { mounted = false; state: State = { loading: false }; @@ -97,32 +109,36 @@ export default class RuleDetailsCustomRules extends React.PureComponent ( - - + + {rule.name} - - - - - - - - {rule.params && - rule.params - .filter((param) => param.defaultValue) + + + + + {translate('severity', rule.severity)} + + + + + {rule.params + ?.filter((param) => param.defaultValue) .map((param) => ( -
- {param.key} - - - {param.defaultValue} - -
+
  • + {param.key} + + {param.defaultValue} +
  • ))} - +
    +
    {this.props.canChange && ( - + {({ onClick }) => ( - + )} - + )} - +
    ); render() { const { loading, rules = [] } = this.state; return ( -
    -
    -

    {translate('coding_rules.custom_rules')}

    +
    +
    + {translate('coding_rules.custom_rules')} {this.props.canChange && ( {({ onClick }) => ( - + )} )} - + {rules.length > 0 && ( - - {sortBy(rules, (rule) => rule.name).map(this.renderRule)} -
    + + {sortBy(rules, (rule) => rule.name).map(this.renderRule)} +
    )}
    diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx index 62e860d6fab..57a2fa03602 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx @@ -18,11 +18,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { CodeSyntaxHighlighter } from 'design-system'; +import { + ButtonPrimary, + ButtonSecondary, + CodeSyntaxHighlighter, + DangerButtonSecondary, + InputTextArea, + Spinner, +} from 'design-system'; import * as React from 'react'; import { updateRule } from '../../../api/rules'; import FormattingTips from '../../../components/common/FormattingTips'; -import { Button, ResetButtonLink } from '../../../components/controls/buttons'; import RuleTabViewer from '../../../components/rules/RuleTabViewer'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { sanitizeString, sanitizeUserInput } from '../../../helpers/sanitize'; @@ -67,7 +73,8 @@ export default class RuleDetailsDescription extends React.PureComponent { + handleSaveClick = (event: React.SyntheticEvent) => { + event.preventDefault(); this.updateDescription(this.state.description); }; @@ -116,88 +123,82 @@ export default class RuleDetailsDescription extends React.PureComponent {this.props.ruleDetails.htmlNote !== undefined && ( )} - {this.props.canWrite && ( - - )} +
    + {this.props.canWrite && ( + + {translate('coding_rules.extend_description')} + + )} +
    ); renderForm = () => ( -
    - - - -
    -